This commit is contained in:
2026-09-18 16:48:35 +08:00
parent e3cca80ff2
commit 1d407620d2
59 changed files with 1879 additions and 1545 deletions
+77 -12
View File
@@ -1,3 +1,4 @@
import { ClipboardList, ReceiptText, Settings, Warehouse } from '@lucide/vue'
import { clientModuleRegistry } from '../modules/registry.ts'
import type { ClientModuleManifest } from '../modules/types.ts'
import {
@@ -15,7 +16,7 @@ export {
type ClientNavigationNode,
} from './tree.ts'
function buildModuleNavigation(module: ClientModuleManifest): ClientNavigationNode[] {
function buildModuleNavigationLeaves(module: ClientModuleManifest): ClientNavigationLeaf[] {
const leaves: ClientNavigationLeaf[] = []
for (const route of module.routes) {
if (route.release !== 'active' || !route.navigation) continue
@@ -27,18 +28,75 @@ function buildModuleNavigation(module: ClientModuleManifest): ClientNavigationNo
permission: route.meta.permission,
})
}
if (!module.navigationGroup) return leaves
if (leaves.length === 0) return []
return [{
key: module.key,
title: module.navigationGroup.title,
icon: module.navigationGroup.icon,
dividerBefore: module.navigationGroup.dividerBefore,
children: leaves as [ClientNavigationNode, ...ClientNavigationNode[]],
}]
return leaves
}
export const clientNavigation: ClientNavigationNode[] = clientModuleRegistry.flatMap(buildModuleNavigation)
const registeredLeaves = clientModuleRegistry.flatMap(buildModuleNavigationLeaves)
function requireLeaf(key: string, title?: string): ClientNavigationLeaf {
const leaf = registeredLeaves.find((item) => item.key === key)
if (!leaf) throw new Error(`Missing client navigation leaf: ${key}`)
return title ? { ...leaf, title } : leaf
}
/**
* The sidebar is organised around customer tasks instead of implementation modules.
* Account and notification routes remain hidden utility entries so breadcrumbs and
* permission-aware fallbacks keep working from the fixed top-header shortcuts.
*/
export const clientNavigation: ClientNavigationNode[] = [
requireLeaf('dashboard', '首页'),
{
key: 'order-center',
title: '订单中心',
icon: ClipboardList,
children: [
requireLeaf('first-leg-orders', '头程订单'),
requireLeaf('first-leg-quick-order', '快速建草稿'),
requireLeaf('cross-border-procurement'),
requireLeaf('cross-border-domestic-trucking'),
requireLeaf('cross-border-pallet-shipping'),
requireLeaf('orders-inbound', '入库订单'),
requireLeaf('orders-delivery', '派送订单'),
],
},
{
key: 'overseas-warehouse-center',
title: '海外仓',
icon: Warehouse,
children: [
requireLeaf('overseas-labeling'),
requireLeaf('overseas-storage-transfer'),
requireLeaf('overseas-cargo-disposal'),
requireLeaf('overseas-return-relabel', '退货预报与签收'),
requireLeaf('overseas-return-inventory'),
requireLeaf('overseas-return-cartons'),
],
},
{
key: 'finance-center',
title: '费用与账单',
icon: ReceiptText,
children: [
requireLeaf('finance-account'),
requireLeaf('finance-bills'),
],
},
{
key: 'company-center',
title: '企业设置',
icon: Settings,
dividerBefore: true,
children: [
requireLeaf('company-profile'),
requireLeaf('company-members'),
requireLeaf('company-roles'),
requireLeaf('company-permissions'),
],
},
{ ...requireLeaf('account-profile'), hidden: true },
{ ...requireLeaf('notifications-list'), hidden: true },
]
export function filterNavigationByPermissions(
nodes: readonly ClientNavigationNode[],
@@ -59,7 +117,11 @@ export function filterNavigationByPermissions(
}
export function getFirstAccessiblePath(permissions: readonly string[]) {
return getNavigationLeaves(filterNavigationByPermissions(clientNavigation, permissions))[0]?.path ?? '/forbidden'
const visiblePath = getNavigationLeaves(filterNavigationByPermissions(clientNavigation, permissions))[0]?.path
if (visiblePath) return visiblePath
const allowed = new Set(permissions)
return getNavigationLeaves(clientNavigation).find((leaf) => allowed.has(leaf.permission))?.path ?? '/forbidden'
}
export interface ClientBreadcrumbItem {
@@ -91,6 +153,9 @@ export function getRouteBreadcrumbs(path: string, currentTitle: string): ClientB
.sort((left, right) => right.path.length - left.path.length)
const parentLeaf = leaves[0]
const trail = parentLeaf ? findNavigationTrail(clientNavigation, parentLeaf.path) ?? [] : []
if (parentLeaf && path === parentLeaf.path && trail.length > 0) {
trail[trail.length - 1] = { ...trail[trail.length - 1], title: currentTitle }
}
const items = [home, ...trail]
if (items.at(-1)?.title !== currentTitle) items.push({ title: currentTitle })
if (items.length > 1) delete items.at(-1)!.path