20260716173254

This commit is contained in:
oneao committed 2026-07-16 17:32:55 +08:00
1 parent 704dda07b5
commit 5cd4e1e1d2
46 files changed
+3569 -528

No files matched your search

@@ -0,0 +1,351 @@
<script setup>
import { computed, h, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import {
Bug,
ChartColumn,
CircleQuestionMark,
Database,
FolderKanban,
House,
Landmark,
PanelLeftClose,
PanelLeftOpen,
Settings,
Wallet,
} from "@lucide/vue";
import { useAppStore } from "../../stores/app";
const route = useRoute();
const router = useRouter();
const appStore = useAppStore();
const menuIcon = (icon) => () => h(icon, { size: 16, strokeWidth: 1.8 });
const menuItems = [
{
key: "/home",
icon: menuIcon(House),
label: "首页",
},
{
key: "business",
icon: menuIcon(FolderKanban),
label: "业务管理",
children: [
{ key: "/business", label: "业务管理" },
{ key: "/business/listcontainer", label: "排车列表" },
],
},
{
key: "finance",
icon: menuIcon(Wallet),
label: "财务管理",
children: [
{
key: "finance-receivable",
label: "应收管理",
children: [
{ key: "/cw/ys/list", label: "应收费用列表" },
{ key: "/cw/ys/listbill", label: "应收账单" },
{ key: "/cw/ys/listinvoice", label: "应收发票" },
{ key: "/cw/ys/listskd", label: "收款管理" },
],
},
{
key: "finance-payable",
label: "应付管理",
children: [
{ key: "/cw/yf/list", label: "应付费用列表" },
{ key: "/cw/yf/listbill", label: "应付账单" },
{ key: "/cw/yf/listinvoice", label: "应付发票" },
{ key: "/cw/yf/listfkd", label: "付款管理" },
],
},
{
key: "finance-daily",
label: "日常费用",
children: [
{ key: "/cw/assistance/daily-expenses", label: "日常费用" },
{ key: "/cw/assistance/payment-account", label: "收付账户" },
],
},
],
},
{
key: "statistics",
icon: menuIcon(ChartColumn),
label: "统计分析",
children: [
{
key: "statistics-receivable",
label: "收款统计分析",
children: [
{ key: "/tj/ystj/tj_nots", label: "应收未收明细" },
{ key: "/tj/ystj/tj_notsbydw", label: "应收未收按单位汇总" },
{ key: "/tj/ystj/tj_notsbysales", label: "未收款按业务员汇总" },
{ key: "/tj/ystj/tj_cwskxz", label: "收款明细统计" },
],
},
{
key: "statistics-payable",
label: "付款统计分析",
children: [
{ key: "/tj/yftj/tj_alone_notf", label: "应付未付明细" },
{ key: "/tj/yftj/tj_notfbydw", label: "应付未付按单位汇总" },
{ key: "/tj/yftj/tj_cwfkxz", label: "付款明细统计" },
],
},
{
key: "statistics-profit",
label: "利润统计分析",
children: [
{ key: "/tj/lrtj/tj_lr_module_id", label: "利润分析单票明细" },
{ key: "/tj/lrtj/tj_lrbysales", label: "利润分析按业务员" },
{ key: "/tj/lrtj/tj_lrbycustomer", label: "利润分析按客户" },
],
},
],
},
{
key: "management",
icon: menuIcon(Landmark),
label: "管理中心",
children: [
{ key: "/shcenter", label: "审批中心" },
{ key: "/partners/khht", label: "合同管理" },
{ key: "/pricelist/list", label: "运费模版" },
{ key: "/system/oa-management/companyAnnouncement", label: "企业公告" },
{ key: "/system/approvalSettings", label: "审批设置" },
{ key: "/system/oa-management/userReminder", label: "提醒设置" },
{ key: "/partners/hl", label: "结算汇率" },
],
},
{
key: "base-data",
icon: menuIcon(Database),
label: "基础数据",
children: [
{ key: "/partners", label: "合作伙伴" },
{ key: "/partners/contact", label: "联系人" },
{ key: "/base/other", label: "其他数据" },
],
},
{
key: "system",
icon: menuIcon(Settings),
label: "系统管理",
children: [
{ key: "/system/permission", label: "权限管理" },
{ key: "/system/field", label: "字段配置" },
{ key: "/system/statisticalSettings", label: "查询设置" },
{ key: "/system/mappingConfig", label: "映射配置" },
{ key: "/system/moduleSettings", label: "模块设置" },
{ key: "/system/autoId", label: "自动编码" },
],
},
{
key: "help",
icon: menuIcon(CircleQuestionMark),
label: "系统帮助",
children: [
{ key: "/help", label: "帮助文档" },
{ key: "/videos", label: "操作视频" },
],
},
{
key: "test",
icon: menuIcon(Bug),
label: "功能测试",
children: [{ key: "/test/test", label: "测试页面" }],
},
];
const selectedKeys = computed(() => [route.meta.menuKey || route.path]);
const openKeys = ref([]);
const parentKeyByRoute = new Map(
menuItems.flatMap((item) =>
item.children?.map((child) => [child.key, item.key]) ?? [],
),
);
watch(
() => route.meta.menuKey || route.path,
(menuKey) => {
const parentKey = parentKeyByRoute.get(menuKey);
if (parentKey && !openKeys.value.includes(parentKey)) {
openKeys.value = [...openKeys.value, parentKey];
}
},
{ immediate: true },
);
function navigate({ key }) {
if (typeof key === "string" && key.startsWith("/")) {
router.push(key);
}
}
function updateOpenKeys(keys) {
openKeys.value = keys;
}
</script>
<template>
<a-layout-sider
class="app-sider"
:collapsed="appStore.sidebarCollapsed"
:trigger="null"
:collapsed-width="56"
collapsible
theme="light"
width="208"
>
<div class="brand" :class="{ 'brand--collapsed': appStore.sidebarCollapsed }">
<div class="brand__mark">F</div>
<div class="brand__copy" :aria-hidden="appStore.sidebarCollapsed">
<strong>FMS 管理系统</strong>
</div>
</div>
<div class="sider-menu">
<a-menu
mode="inline"
:items="menuItems"
:selected-keys="selectedKeys"
:open-keys="openKeys"
@click="navigate"
@open-change="updateOpenKeys"
/>
</div>
<div class="sider-footer">
<a-tooltip :title="appStore.sidebarCollapsed ? '展开侧栏' : '收起侧栏'" placement="right">
<a-button
type="text"
class="sider-collapse"
aria-label="切换侧栏宽度"
@click="appStore.toggleSidebar"
>
<template #icon>
<PanelLeftOpen v-if="appStore.sidebarCollapsed" />
<PanelLeftClose v-else />
</template>
</a-button>
</a-tooltip>
</div>
</a-layout-sider>
</template>
<style lang="scss" scoped>
.app-sider {
position: sticky;
top: 0;
z-index: 20;
height: 100%;
overflow: hidden;
border-right: 1px solid var(--fms-border);
:deep(.ant-layout-sider-children) {
display: flex;
height: 100%;
min-height: 0;
flex-direction: column;
}
:deep(.ant-menu) {
border-inline-end: 0;
}
:deep(.ant-menu-item),
:deep(.ant-menu-submenu-title) {
width: 100%;
height: 36px;
margin: 0;
border-radius: 0;
line-height: 36px;
}
:deep(.ant-menu-root > .ant-menu-item),
:deep(.ant-menu-root > .ant-menu-submenu > .ant-menu-submenu-title) {
padding-inline-start: 20px !important;
}
}
.sider-menu {
flex: 1;
min-height: 0;
overflow-x: hidden;
overflow-y: auto;
}
.sider-footer {
display: flex;
flex: none;
align-items: center;
padding: 4px;
border-top: 1px solid var(--fms-border);
}
.sider-collapse {
width: 100%;
height: 32px;
padding-inline: 0;
border-radius: 4px;
color: var(--fms-text-secondary);
font-size: 17px;
}
.brand {
display: flex;
align-items: center;
min-height: 44px;
gap: 8px;
padding: 6px 12px 6px 15px;
&--collapsed {
justify-content: flex-start;
gap: 0;
padding-inline: 15px;
.brand__copy {
max-width: 0;
opacity: 0;
transform: translateX(-4px);
}
}
&__mark {
display: grid;
flex: none;
width: 26px;
height: 26px;
place-items: center;
border-radius: 6px;
background: #1677ff;
color: #fff;
font-size: 14px;
font-weight: 700;
}
&__copy {
display: flex;
max-width: 140px;
min-width: 0;
overflow: hidden;
opacity: 1;
line-height: 1;
transform: translateX(0);
transition:
max-width 0.18s ease,
opacity 0.12s ease,
transform 0.18s ease;
strong {
color: var(--fms-text);
font-size: 14px;
font-weight: 650;
white-space: nowrap;
}
}
}
</style>