363 lines
8.9 KiB
Vue
363 lines
8.9 KiB
Vue
<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 effectiveCollapsed = computed(() => appStore.sidebarCollapsed);
|
|
|
|
const fallbackMenuItems = [
|
|
{
|
|
key: "/dashboard",
|
|
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/autoId", label: "自动编码" },
|
|
{ key: "/demo/modules", 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 menuItems = ref(fallbackMenuItems);
|
|
|
|
const selectedKeys = computed(() => [route.meta.menuKey || route.path]);
|
|
const openKeys = ref([]);
|
|
const parentKeysByRoute = computed(() => {
|
|
const result = new Map();
|
|
const visit = (items, parents = []) => {
|
|
items.forEach((item) => {
|
|
if (item.children?.length) visit(item.children, [...parents, item.key]);
|
|
else result.set(item.key, parents);
|
|
});
|
|
};
|
|
visit(menuItems.value);
|
|
return result;
|
|
});
|
|
|
|
watch(
|
|
[() => route.meta.menuKey || route.path, menuItems],
|
|
([menuKey]) => {
|
|
const parentKeys = parentKeysByRoute.value.get(menuKey) || [];
|
|
const nextKeys = new Set(openKeys.value);
|
|
parentKeys.forEach((key) => nextKeys.add(key));
|
|
if (nextKeys.size !== openKeys.value.length) {
|
|
openKeys.value = Array.from(nextKeys);
|
|
}
|
|
},
|
|
{ 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="effectiveCollapsed"
|
|
:trigger="null"
|
|
:collapsed-width="56"
|
|
collapsible
|
|
theme="light"
|
|
width="208"
|
|
>
|
|
<div class="brand" :class="{ 'brand--collapsed': effectiveCollapsed }">
|
|
<div class="brand__mark">F</div>
|
|
<div class="brand__copy" :aria-hidden="effectiveCollapsed">
|
|
<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="effectiveCollapsed ? '展开侧栏' : '收起侧栏'" placement="right">
|
|
<a-button
|
|
type="text"
|
|
class="sider-collapse"
|
|
aria-label="切换侧栏宽度"
|
|
@click="appStore.toggleSidebar"
|
|
>
|
|
<template #icon>
|
|
<PanelLeftOpen v-if="effectiveCollapsed" />
|
|
<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>
|