196 lines
5.1 KiB
Vue
196 lines
5.1 KiB
Vue
<script setup>
|
|
import { computed, h, onMounted, ref, watch } from "vue";
|
|
import { useRoute, useRouter } from "vue-router";
|
|
import { Database, House, PanelLeftClose, PanelLeftOpen, Settings } from "@lucide/vue";
|
|
import { useAppStore } from "@/stores/app";
|
|
import { usePermissionStore } from "@/stores/permissions";
|
|
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const appStore = useAppStore();
|
|
const permissionStore = usePermissionStore();
|
|
const menuIcon = (icon) => () => h(icon, { size: 16, strokeWidth: 1.8 });
|
|
const openKeys = ref(["base", "system"]);
|
|
const effectiveCollapsed = computed(() => appStore.sidebarCollapsed);
|
|
function moduleLabel(moduleId, path, fallback) {
|
|
const module = permissionStore.modules.find(
|
|
(item) => item.b_code === moduleId || item.b_route === path,
|
|
);
|
|
return appStore.t(module?.b_i18n, fallback);
|
|
}
|
|
function directoryLabel(fallback) {
|
|
const module = permissionStore.modules.find(
|
|
(item) => item.b_module_type === "directory" && item.b_name === fallback,
|
|
);
|
|
return appStore.t(module?.b_i18n, fallback);
|
|
}
|
|
function canAccessMenu(item) {
|
|
if (item.moduleId) return permissionStore.canAccess(item.moduleId);
|
|
return true;
|
|
}
|
|
const menuItems = computed(() => {
|
|
const basePages = [
|
|
{
|
|
key: "/base/othercompany",
|
|
label: moduleLabel("base_othercompany", "/base/othercompany", "合作伙伴"),
|
|
moduleId: "base_othercompany",
|
|
},
|
|
].filter(canAccessMenu);
|
|
const systemPages = [
|
|
{
|
|
key: "/system/module-management",
|
|
label: moduleLabel("m_metadata", "/system/module-management", "模块管理"),
|
|
moduleId: "m_metadata",
|
|
},
|
|
{
|
|
key: "/system/i18n-type",
|
|
label: moduleLabel("b_i18n_type", "/system/i18n-type", "多语言类型"),
|
|
moduleId: "b_i18n_type",
|
|
},
|
|
{
|
|
key: "/system/login-log",
|
|
label: moduleLabel("d_login_log", "/system/login-log", "登录日志"),
|
|
moduleId: "d_login_log",
|
|
},
|
|
].filter(canAccessMenu);
|
|
const items = [
|
|
{
|
|
key: "/dashboard",
|
|
icon: menuIcon(House),
|
|
label: appStore.t("common.dashboard", "工作台"),
|
|
},
|
|
];
|
|
if (basePages.length)
|
|
items.push({
|
|
key: "base",
|
|
icon: menuIcon(Database),
|
|
label: directoryLabel("基础数据"),
|
|
children: basePages,
|
|
});
|
|
if (systemPages.length)
|
|
items.push({
|
|
key: "system",
|
|
icon: menuIcon(Settings),
|
|
label: directoryLabel("系统管理"),
|
|
children: systemPages,
|
|
});
|
|
return items;
|
|
});
|
|
const selectedKeys = computed(() => [route.meta.menuKey || route.path]);
|
|
watch(
|
|
() => route.path,
|
|
(path) => {
|
|
if (path.startsWith("/base") && !openKeys.value.includes("base"))
|
|
openKeys.value = [...openKeys.value, "base"];
|
|
if (path.startsWith("/system") && !openKeys.value.includes("system"))
|
|
openKeys.value = [...openKeys.value, "system"];
|
|
},
|
|
{ immediate: true },
|
|
);
|
|
function navigate({ key }) {
|
|
if (typeof key === "string" && key.startsWith("/")) router.push(key);
|
|
}
|
|
onMounted(() => permissionStore.load().catch(() => {}));
|
|
</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>
|
|
<strong>{{ appStore.t("common.appName", "FMS 管理系统") }}</strong>
|
|
</div>
|
|
<div class="sider-menu">
|
|
<a-menu
|
|
mode="inline"
|
|
:items="menuItems"
|
|
:selected-keys="selectedKeys"
|
|
:open-keys="openKeys"
|
|
@click="navigate"
|
|
@open-change="openKeys = $event"
|
|
/>
|
|
</div>
|
|
<div class="sider-footer">
|
|
<a-tooltip
|
|
:title="
|
|
effectiveCollapsed
|
|
? appStore.t('common.expandSidebar', '展开侧栏')
|
|
: appStore.t('common.collapseSidebar', '收起侧栏')
|
|
"
|
|
placement="right"
|
|
><a-button type="text" class="sider-collapse" @click="appStore.toggleSidebar"
|
|
><PanelLeftOpen v-if="effectiveCollapsed" /><PanelLeftClose v-else /></a-button
|
|
></a-tooltip>
|
|
</div>
|
|
</a-layout-sider>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.app-sider {
|
|
height: 100%;
|
|
overflow: hidden;
|
|
border-right: 1px solid var(--fms-border);
|
|
:deep(.ant-layout-sider-children) {
|
|
display: flex;
|
|
height: 100%;
|
|
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;
|
|
}
|
|
}
|
|
.brand {
|
|
display: flex;
|
|
min-height: 44px;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 6px 12px 6px 15px;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
}
|
|
.brand__mark {
|
|
display: grid;
|
|
flex: none;
|
|
width: 26px;
|
|
height: 26px;
|
|
place-items: center;
|
|
border-radius: 6px;
|
|
background: #1677ff;
|
|
color: #fff;
|
|
font-weight: 700;
|
|
}
|
|
.brand strong {
|
|
font-size: 14px;
|
|
}
|
|
.brand--collapsed strong {
|
|
display: none;
|
|
}
|
|
.sider-menu {
|
|
flex: 1;
|
|
min-height: 0;
|
|
overflow: auto;
|
|
}
|
|
.sider-footer {
|
|
padding: 4px;
|
|
border-top: 1px solid var(--fms-border);
|
|
}
|
|
.sider-collapse {
|
|
width: 100%;
|
|
}
|
|
</style>
|