20260803225403
This commit is contained in:
1 parent
efd5a2bc93
commit
e17bbe9db3
40 files changed
+5516
-3910
No files matched your search
@@ -1,7 +1,7 @@
|
||||
<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 { Folder, House, PanelLeftClose, PanelLeftOpen } from "@lucide/vue";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import { usePermissionStore } from "@/stores/permissions";
|
||||
|
||||
@@ -9,103 +9,88 @@ 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 openKeys = ref([]);
|
||||
const effectiveCollapsed = computed(() => appStore.sidebarCollapsed);
|
||||
function moduleLabel(moduleId, path, fallback) {
|
||||
const module = permissionStore.modules.find(
|
||||
(item) => item.b_code === moduleId || item.b_route === path,
|
||||
const menuIcon = (icon) => () => h(icon, { size: 16, strokeWidth: 1.8 });
|
||||
|
||||
function buildFunctionMenu() {
|
||||
const nodes = permissionStore.functionNodes.filter(
|
||||
(item) => Number(item.b_canmenu) === 1 && permissionStore.canAccessFunctionNode(item.b_id),
|
||||
);
|
||||
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",
|
||||
},
|
||||
{
|
||||
key: "/base/contact",
|
||||
label: moduleLabel("v_b_contact", "/base/contact", "联系人"),
|
||||
moduleId: "v_b_contact",
|
||||
},
|
||||
{
|
||||
key: "/base/otherdata",
|
||||
label: moduleLabel("base_otherdata", "/base/otherdata", "其他数据"),
|
||||
moduleId: "base_otherdata",
|
||||
},
|
||||
].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",
|
||||
},
|
||||
{
|
||||
key: "/system/user",
|
||||
label: moduleLabel("b_user", "/system/user", "用户管理"),
|
||||
moduleId: "b_user",
|
||||
},
|
||||
].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,
|
||||
const menuById = new Map();
|
||||
const roots = [];
|
||||
|
||||
for (const node of nodes) {
|
||||
if (node.b_node_type === "entry" && !node.b_route) continue;
|
||||
menuById.set(String(node.b_id), {
|
||||
key: node.b_node_type === "directory" ? `function:${node.b_id}` : node.b_route,
|
||||
label: appStore.t(node.b_i18n, node.b_name),
|
||||
icon: node.b_node_type === "directory" ? menuIcon(Folder) : undefined,
|
||||
children: node.b_node_type === "directory" ? [] : undefined,
|
||||
sort: Number(node.b_xh) || 0,
|
||||
nodeId: String(node.b_id),
|
||||
parentId: node.b_parent_id == null ? "" : String(node.b_parent_id),
|
||||
});
|
||||
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"];
|
||||
}
|
||||
|
||||
for (const item of menuById.values()) {
|
||||
const parent = menuById.get(item.parentId);
|
||||
if (parent?.children) parent.children.push(item);
|
||||
else roots.push(item);
|
||||
}
|
||||
const sortItems = (items) => {
|
||||
items.sort((left, right) => left.sort - right.sort || left.nodeId.localeCompare(right.nodeId));
|
||||
for (const item of items) {
|
||||
if (item.children) sortItems(item.children);
|
||||
delete item.sort;
|
||||
delete item.nodeId;
|
||||
delete item.parentId;
|
||||
}
|
||||
};
|
||||
sortItems(roots);
|
||||
return roots;
|
||||
}
|
||||
|
||||
const menuItems = computed(() => [
|
||||
{
|
||||
key: "/dashboard",
|
||||
icon: menuIcon(House),
|
||||
label: appStore.t("common.dashboard", "工作台"),
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
...buildFunctionMenu(),
|
||||
]);
|
||||
const selectedKeys = computed(() => [route.path]);
|
||||
|
||||
function syncOpenKeys(path) {
|
||||
const entry = permissionStore.functionNodes.find(
|
||||
(item) => item.b_node_type === "entry" && String(item.b_route || "") === String(path || ""),
|
||||
);
|
||||
if (!entry) return;
|
||||
const byId = new Map(permissionStore.functionNodes.map((item) => [String(item.b_id), item]));
|
||||
const keys = [];
|
||||
let parentId = entry.b_parent_id == null ? "" : String(entry.b_parent_id);
|
||||
while (parentId) {
|
||||
keys.unshift(`function:${parentId}`);
|
||||
const parent = byId.get(parentId);
|
||||
parentId = parent?.b_parent_id == null ? "" : String(parent.b_parent_id);
|
||||
}
|
||||
openKeys.value = [...new Set([...openKeys.value, ...keys])];
|
||||
}
|
||||
|
||||
watch(() => route.path, syncOpenKeys, { immediate: true });
|
||||
|
||||
function navigate({ key }) {
|
||||
if (typeof key === "string" && key.startsWith("/")) router.push(key);
|
||||
}
|
||||
onMounted(() => permissionStore.load().catch(() => {}));
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await permissionStore.load();
|
||||
syncOpenKeys(route.path);
|
||||
} catch {
|
||||
// The route guard owns authentication and error reporting.
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -140,9 +125,12 @@ onMounted(() => permissionStore.load().catch(() => {}));
|
||||
: 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>
|
||||
>
|
||||
<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>
|
||||
@@ -167,19 +155,6 @@ onMounted(() => permissionStore.load().catch(() => {}));
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
line-height: 36px;
|
||||
transition: padding-inline-start 0.2s ease;
|
||||
}
|
||||
:deep(.ant-menu-inline-collapsed) {
|
||||
.ant-menu-title-content {
|
||||
display: none !important;
|
||||
}
|
||||
.ant-menu-item,
|
||||
.ant-menu-submenu-title {
|
||||
padding-inline-start: 20px !important;
|
||||
}
|
||||
.ant-menu-item-icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
.brand {
|
||||
|
||||
Reference in new issue
Block a user