118 lines
3.8 KiB
TypeScript
118 lines
3.8 KiB
TypeScript
import type { Component } from 'vue'
|
|||
|
|
import {
|
||
|
|
Building2,
|
||
|
|
CircleUserRound,
|
||
|
|
ClipboardList,
|
||
|
|
KeyRound,
|
||
|
|
LayoutGrid,
|
||
|
|
Settings,
|
||
|
|
ShieldCheck,
|
||
|
|
Truck,
|
||
|
|
Users,
|
||
|
|
} from '@lucide/vue'
|
||
|
|
|
||
|
|
export interface ClientNavigationBaseNode {
|
||
|
|
key: string
|
||
|
|
title: string
|
||
|
|
icon: Component
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface ClientNavigationLeaf extends ClientNavigationBaseNode {
|
||
|
|
path: string
|
||
|
|
permission: string
|
||
|
|
children?: never
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface ClientNavigationGroup extends ClientNavigationBaseNode {
|
||
|
|
path?: never
|
||
|
|
permission?: never
|
||
|
|
children: readonly [ClientNavigationNode, ...ClientNavigationNode[]]
|
||
|
|
}
|
||
|
|
|
||
|
|
export type ClientNavigationNode = ClientNavigationLeaf | ClientNavigationGroup
|
||
|
|
|
||
|
|
export const clientNavigation: ClientNavigationNode[] = [
|
||
|
|
{ key: 'dashboard', title: '业务总览', icon: LayoutGrid, path: '/', permission: 'dashboard:view' },
|
||
|
|
{ key: 'account-profile', title: '我的信息', icon: CircleUserRound, path: '/account/profile', permission: 'account:profile:view' },
|
||
|
|
{
|
||
|
|
key: 'first-leg',
|
||
|
|
title: '头程业务',
|
||
|
|
icon: Truck,
|
||
|
|
children: [
|
||
|
|
{ key: 'first-leg-orders', title: '头程订单', icon: ClipboardList, path: '/orders/first-leg', permission: 'order:first-leg:list' },
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
key: 'company',
|
||
|
|
title: '企业管理',
|
||
|
|
icon: Settings,
|
||
|
|
children: [
|
||
|
|
{ key: 'company-profile', title: '企业资料', icon: Building2, path: '/company/profile', permission: 'company:profile:view' },
|
||
|
|
{ key: 'company-members', title: '企业成员', icon: Users, path: '/organization/members', permission: 'company:member:list' },
|
||
|
|
{ key: 'company-roles', title: '角色管理', icon: ShieldCheck, path: '/organization/roles', permission: 'company:role:list' },
|
||
|
|
{ key: 'company-permissions', title: '菜单权限', icon: KeyRound, path: '/organization/permissions', permission: 'company:permission:list' },
|
||
|
|
],
|
||
|
|
},
|
||
|
|
]
|
||
|
|
|
||
|
|
function isNavigationGroup(node: ClientNavigationNode): node is ClientNavigationGroup {
|
||
|
|
return 'children' in node && node.children !== undefined
|
||
|
|
}
|
||
|
|
|
||
|
|
export function getNavigationLeaves(nodes: readonly ClientNavigationNode[]): ClientNavigationLeaf[] {
|
||
|
|
return nodes.flatMap((node) => {
|
||
|
|
if (isNavigationGroup(node)) return getNavigationLeaves(node.children)
|
||
|
|
return [node]
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
export function filterNavigationByPermissions(
|
||
|
|
nodes: readonly ClientNavigationNode[],
|
||
|
|
permissions: readonly string[],
|
||
|
|
): ClientNavigationNode[] {
|
||
|
|
const allowed = new Set(permissions)
|
||
|
|
const result: ClientNavigationNode[] = []
|
||
|
|
for (const node of nodes) {
|
||
|
|
if (!isNavigationGroup(node)) {
|
||
|
|
if (allowed.has(node.permission)) result.push(node)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
const children = filterNavigationByPermissions(node.children, permissions)
|
||
|
|
if (children.length > 0) result.push({ ...node, children: children as [ClientNavigationNode, ...ClientNavigationNode[]] })
|
||
|
|
}
|
||
|
|
return result
|
||
|
|
}
|
||
|
|
|
||
|
|
export function getFirstAccessiblePath(permissions: readonly string[]) {
|
||
|
|
return getNavigationLeaves(filterNavigationByPermissions(clientNavigation, permissions))[0]?.path ?? '/forbidden'
|
||
|
|
}
|
||
|
|
|
||
|
|
function findNavigationParentKeys(
|
||
|
|
nodes: readonly ClientNavigationNode[],
|
||
|
|
path: string,
|
||
|
|
parentKeys: readonly string[],
|
||
|
|
): string[] | undefined {
|
||
|
|
for (const node of nodes) {
|
||
|
|
if (isNavigationGroup(node)) {
|
||
|
|
const result = findNavigationParentKeys(node.children, path, [...parentKeys, node.key])
|
||
|
|
if (result) return result
|
||
|
|
} else if (node.path === path) {
|
||
|
|
return [...parentKeys]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export function getNavigationParentKeys(nodes: readonly ClientNavigationNode[], path: string): string[] {
|
||
|
|
return findNavigationParentKeys(nodes, path, []) ?? []
|
||
|
|
}
|
||
|
|
|
||
|
|
export function openNavigationParentKeys(
|
||
|
|
path: string,
|
||
|
|
nodes: readonly ClientNavigationNode[],
|
||
|
|
open: (key: string) => void,
|
||
|
|
) {
|
||
|
|
for (const key of getNavigationParentKeys(nodes, path)) {
|
||
|
|
open(key)
|
||
|
|
}
|
||
|
|
}
|