2026-07-30 17:59:43 +08:00
|
|
|
import type { Component } from 'vue'
|
|
|
|
|
import {
|
|
|
|
|
Building2,
|
|
|
|
|
ContactRound,
|
|
|
|
|
ClipboardList,
|
2026-08-04 21:01:39 +08:00
|
|
|
CircleDollarSign,
|
2026-07-30 17:59:43 +08:00
|
|
|
Database,
|
2026-08-03 18:15:41 +08:00
|
|
|
Download,
|
2026-08-05 10:46:42 +08:00
|
|
|
FileCheck2,
|
2026-07-30 17:59:43 +08:00
|
|
|
KeyRound,
|
|
|
|
|
Settings,
|
|
|
|
|
ShieldCheck,
|
|
|
|
|
Truck,
|
|
|
|
|
Users,
|
|
|
|
|
Warehouse as WarehouseIcon,
|
|
|
|
|
} from '@lucide/vue'
|
|
|
|
|
|
|
|
|
|
export interface AdminNavigationNode {
|
|
|
|
|
key: string
|
|
|
|
|
title: string
|
|
|
|
|
icon: Component
|
2026-08-03 15:50:24 +08:00
|
|
|
iconColor?: string
|
2026-07-30 17:59:43 +08:00
|
|
|
path?: string
|
|
|
|
|
permission?: string
|
2026-08-03 20:50:18 +08:00
|
|
|
permissionAnyOf?: string[]
|
2026-08-05 10:46:42 +08:00
|
|
|
/** Set to true to remove a registered entry from the sidebar without deleting its route. */
|
2026-08-03 11:45:45 +08:00
|
|
|
hidden?: boolean
|
2026-07-30 17:59:43 +08:00
|
|
|
children?: AdminNavigationNode[]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface AdminNavigationLeaf extends AdminNavigationNode {
|
|
|
|
|
path: string
|
2026-08-03 11:45:45 +08:00
|
|
|
permission?: string
|
2026-07-30 17:59:43 +08:00
|
|
|
children?: never
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const adminNavigation: AdminNavigationNode[] = [
|
2026-08-05 10:46:42 +08:00
|
|
|
{ key: 'dashboard', title: '首页数据看板', icon: ClipboardList, path: '/dashboard', hidden: true },
|
2026-07-30 17:59:43 +08:00
|
|
|
{
|
2026-08-03 11:45:45 +08:00
|
|
|
key: 'customers',
|
|
|
|
|
title: '客户管理',
|
|
|
|
|
icon: Building2,
|
2026-08-03 15:50:24 +08:00
|
|
|
iconColor: '#52c41a',
|
2026-07-30 17:59:43 +08:00
|
|
|
children: [
|
2026-08-03 20:50:18 +08:00
|
|
|
{ key: 'customers-list', title: '客户列表', icon: Building2, path: '/customers', permission: 'platform:customer:list', permissionAnyOf: ['platform:customer:review'] },
|
2026-08-05 10:46:42 +08:00
|
|
|
{ key: 'warehouse-tier-fee', title: '海外仓阶梯费用', icon: Building2, path: '/prototype/customer-management/warehouse-tier-fee', hidden: true },
|
2026-07-30 17:59:43 +08:00
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-08-03 11:45:45 +08:00
|
|
|
key: 'domestic-warehouse',
|
|
|
|
|
title: '国内仓',
|
|
|
|
|
icon: WarehouseIcon,
|
2026-08-03 15:50:24 +08:00
|
|
|
iconColor: '#52c41a',
|
2026-08-03 11:45:45 +08:00
|
|
|
children: [
|
2026-08-05 10:46:42 +08:00
|
|
|
{ key: 'inbound-list', title: '入库单列表', icon: ClipboardList, path: '/inbounds', permission: 'platform:firstleg:inbound:list' },
|
|
|
|
|
{ key: 'inbound-dedup', title: '入库单测量', icon: ClipboardList, path: '/prototype/domestic-warehouse/inbound-dedup', hidden: true },
|
|
|
|
|
{ key: 'inbound-measure', title: '测量数据审核', icon: FileCheck2, path: '/prototype/domestic-warehouse/inbound-measure', hidden: true },
|
2026-08-04 21:01:39 +08:00
|
|
|
{ key: 'locations', title: '库位管理', icon: WarehouseIcon, path: '/master-data/locations', permission: 'platform:location:list' },
|
|
|
|
|
{ key: 'pallets', title: '托盘管理', icon: WarehouseIcon, path: '/master-data/pallets', permission: 'platform:pallet:list' },
|
2026-08-03 11:45:45 +08:00
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'overseas-delivery',
|
|
|
|
|
title: '海外派送',
|
|
|
|
|
icon: Truck,
|
2026-08-05 10:46:42 +08:00
|
|
|
hidden: true,
|
2026-08-03 11:45:45 +08:00
|
|
|
children: [
|
|
|
|
|
{ key: 'outbound-dispatch', title: '派送单列表', icon: Truck, path: '/prototype/overseas-delivery/dispatch-list' },
|
|
|
|
|
{ key: 'outbound-vehicle', title: '排车列表', icon: Truck, path: '/prototype/overseas-delivery/vehicle-scheduling' },
|
|
|
|
|
{ key: 'outbound-bol', title: 'BOL', icon: FileCheck2, path: '/prototype/overseas-delivery/bol' },
|
|
|
|
|
{ key: 'outbound-selfpick', title: '自提单', icon: ClipboardList, path: '/prototype/overseas-delivery/self-pickup' },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'service-center',
|
|
|
|
|
title: '服务中心',
|
|
|
|
|
icon: ClipboardList,
|
2026-08-05 10:46:42 +08:00
|
|
|
hidden: true,
|
2026-08-03 11:45:45 +08:00
|
|
|
children: [
|
|
|
|
|
{ key: 'booking-list', title: '订舱列表', icon: ClipboardList, path: '/prototype/service-center/booking-list' },
|
|
|
|
|
{ key: 'truck-list', title: '拖车列表', icon: Truck, path: '/prototype/service-center/truck-list' },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'supplier-center',
|
|
|
|
|
title: '供应商中心',
|
|
|
|
|
icon: ContactRound,
|
|
|
|
|
children: [
|
2026-08-05 10:46:42 +08:00
|
|
|
{ key: 'supplier-list', title: '供应商列表', icon: ContactRound, path: '/prototype/supplier-center/supplier-list', hidden: true },
|
|
|
|
|
{ key: 'express-companies', title: '快递公司', icon: Truck, path: '/master-data/express-companies', permission: 'platform:express:list' },
|
|
|
|
|
{ key: 'counterparties', title: '往来单位', icon: ContactRound, path: '/master-data/counterparties', permission: 'platform:counterparty:list' },
|
2026-08-03 11:45:45 +08:00
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'container-management',
|
|
|
|
|
title: '货柜管理',
|
|
|
|
|
icon: WarehouseIcon,
|
2026-08-05 10:46:42 +08:00
|
|
|
hidden: true,
|
2026-08-03 11:45:45 +08:00
|
|
|
children: [
|
|
|
|
|
{ key: 'container-list', title: '货柜列表', icon: WarehouseIcon, path: '/prototype/container-management/container-list' },
|
|
|
|
|
{ key: 'container-manifest', title: '装柜清单', icon: ClipboardList, path: '/prototype/container-management/manifest' },
|
|
|
|
|
{ key: 'container-preload', title: '预装清单', icon: ClipboardList, path: '/prototype/container-management/preload-list' },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'overseas-business',
|
|
|
|
|
title: '海外业务',
|
|
|
|
|
icon: Building2,
|
2026-08-05 10:46:42 +08:00
|
|
|
hidden: true,
|
2026-08-03 11:45:45 +08:00
|
|
|
children: [
|
|
|
|
|
{ key: 'overseas-purchase', title: '采购订单', icon: ClipboardList, path: '/prototype/overseas-business/purchase-orders' },
|
|
|
|
|
{ key: 'overseas-truck', title: '本土卡车', icon: Truck, path: '/prototype/overseas-business/local-truck' },
|
|
|
|
|
{ key: 'overseas-looseboard', title: '本土散板', icon: ClipboardList, path: '/prototype/overseas-business/local-looseboard' },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-08-05 10:46:42 +08:00
|
|
|
key: 'overseas-inventory',
|
|
|
|
|
title: '海外仓库存',
|
|
|
|
|
icon: WarehouseIcon,
|
|
|
|
|
hidden: true,
|
2026-08-03 11:45:45 +08:00
|
|
|
children: [
|
|
|
|
|
{ key: 'fba-inbound-order', title: '入库单', icon: ClipboardList, path: '/prototype/fba-returns/inbound-orders' },
|
|
|
|
|
{ key: 'fba-relabel-order', title: '换标单', icon: ClipboardList, path: '/prototype/fba-returns/relabel-orders' },
|
|
|
|
|
{ key: 'fba-outbound-order', title: '出库单', icon: ClipboardList, path: '/prototype/fba-returns/outbound-orders' },
|
|
|
|
|
{ key: 'fba-pallet', title: '托盘管理', icon: WarehouseIcon, path: '/prototype/fba-returns/pallet-management' },
|
|
|
|
|
{ key: 'fba-carton', title: '箱号管理', icon: ClipboardList, path: '/prototype/fba-returns/carton-management' },
|
|
|
|
|
{ key: 'fba-product', title: '产品库', icon: Database, path: '/prototype/fba-returns/product-catalog' },
|
|
|
|
|
{ key: 'fba-return-stock', title: '退货库存', icon: WarehouseIcon, path: '/prototype/fba-returns/return-stock' },
|
2026-08-05 10:46:42 +08:00
|
|
|
{ key: 'overseas-held-stock', title: '暂扣库存', icon: WarehouseIcon, path: '/prototype/overseas-inventory/stock-list' },
|
2026-08-03 11:45:45 +08:00
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'finance',
|
|
|
|
|
title: '财务管理',
|
2026-08-05 10:46:42 +08:00
|
|
|
icon: CircleDollarSign,
|
|
|
|
|
hidden: true,
|
2026-07-30 17:59:43 +08:00
|
|
|
children: [
|
2026-08-03 11:45:45 +08:00
|
|
|
{ key: 'customer-arap', title: '客户应收应付', icon: Database, path: '/prototype/finance/customer-arap' },
|
|
|
|
|
{ key: 'customer-recon-stmt', title: '客户对账单', icon: FileCheck2, path: '/prototype/finance/customer-reconciliation' },
|
|
|
|
|
{ key: 'settlement-ar', title: '结算单应收', icon: FileCheck2, path: '/prototype/finance/settlement-ar' },
|
|
|
|
|
{ key: 'settlement-ap', title: '结算单应付', icon: FileCheck2, path: '/prototype/finance/settlement-ap' },
|
|
|
|
|
{ key: 'finance-cost', title: '费用分析', icon: Database, path: '/prototype/finance/cost-analysis' },
|
|
|
|
|
{ key: 'profit', title: '利润表', icon: Database, path: '/prototype/finance/profit-report' },
|
2026-07-30 17:59:43 +08:00
|
|
|
],
|
|
|
|
|
},
|
2026-08-03 11:45:45 +08:00
|
|
|
{
|
|
|
|
|
key: 'sales',
|
|
|
|
|
title: '销售管理',
|
|
|
|
|
icon: Users,
|
2026-08-05 10:46:42 +08:00
|
|
|
hidden: true,
|
2026-08-03 11:45:45 +08:00
|
|
|
children: [
|
|
|
|
|
{ key: 'sales-visits', title: '拜访记录', icon: Users, path: '/prototype/sales/visit-records' },
|
|
|
|
|
{ key: 'sales-commission', title: '销售提成', icon: Database, path: '/prototype/sales/commission' },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-08-05 10:46:42 +08:00
|
|
|
key: 'general',
|
|
|
|
|
title: '通用',
|
|
|
|
|
icon: Database,
|
|
|
|
|
iconColor: '#722ed1',
|
2026-08-03 11:45:45 +08:00
|
|
|
children: [
|
2026-08-05 10:46:42 +08:00
|
|
|
// { key: 'general-fba', title: 'FBA编码地址', icon: Database, path: '/prototype/general/fba-address', hidden: true },
|
|
|
|
|
// { key: 'general-voyage', title: '船期价格表', icon: Database, path: '/prototype/general/voyage-price-list', hidden: true },
|
|
|
|
|
// { key: 'first-leg-orders', title: '头程订单', icon: ClipboardList, path: '/orders/first-leg', permission: 'platform:order:list', hidden: true },
|
|
|
|
|
// {
|
|
|
|
|
// key: 'first-leg-pricing',
|
|
|
|
|
// title: '头程计费',
|
|
|
|
|
// icon: CircleDollarSign,
|
|
|
|
|
// children: [
|
|
|
|
|
// { key: 'fba-customer-rules', title: '客户计费规则', icon: Database, path: '/first-leg-pricing/customer-rules', permission: 'firstleg:fba-pricing:rule:view' },
|
|
|
|
|
// ],
|
|
|
|
|
// },
|
|
|
|
|
{ key: 'general-product', title: '产品信息', icon: Database, path: '/prototype/general/product-information', hidden: false },
|
|
|
|
|
{ key: 'general-fee', title: '费用项目', icon: Database, path: '/prototype/general/fee-items', hidden: false },
|
|
|
|
|
{ key: 'warehouses', title: '仓库管理', icon: WarehouseIcon, path: '/warehouses', permission: 'platform:warehouse:list' },
|
|
|
|
|
{ key: 'fba-carrier-rates', title: '船期价格表', icon: CircleDollarSign, path: '/first-leg-pricing/carrier-rates', permission: 'firstleg:fba-pricing:rate:view' },
|
|
|
|
|
{ key: 'vehicle-list', title: '车辆列表', icon: Truck, path: '/prototype/vehicle-management/vehicle-list', hidden: false },
|
|
|
|
|
{ key: 'download-center', title: '下载中心', icon: Download, path: '/downloads', permission: 'platform:download:list' },
|
2026-08-03 11:45:45 +08:00
|
|
|
],
|
|
|
|
|
},
|
2026-07-30 17:59:43 +08:00
|
|
|
{
|
|
|
|
|
key: 'system',
|
|
|
|
|
title: '系统管理',
|
|
|
|
|
icon: Settings,
|
2026-08-03 15:50:24 +08:00
|
|
|
iconColor: '#8c8c8c',
|
2026-07-30 17:59:43 +08:00
|
|
|
children: [
|
2026-08-03 11:45:45 +08:00
|
|
|
{ key: 'internal-users', title: '用户管理', icon: Users, path: '/system/users', permission: 'platform:user:list' },
|
|
|
|
|
{ key: 'roles', title: '角色权限', icon: ShieldCheck, path: '/system/roles', permission: 'platform:role:list' },
|
2026-08-05 10:46:42 +08:00
|
|
|
{ key: 'tenant-management', title: '租户管理', icon: Building2, path: '/prototype/system-management/tenant-management', hidden: true },
|
|
|
|
|
{ key: 'brand-settings', title: '品牌设置', icon: Settings, path: '/prototype/system-management/brand-settings', hidden: true },
|
2026-07-30 17:59:43 +08:00
|
|
|
{ key: 'permissions', title: '菜单权限', icon: KeyRound, path: '/system/permissions', permission: 'platform:permission:list' },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
export function getNavigationLeaves(nodes: AdminNavigationNode[]): AdminNavigationLeaf[] {
|
|
|
|
|
return nodes.flatMap((node) => {
|
|
|
|
|
if (node.children?.length) return getNavigationLeaves(node.children)
|
2026-08-03 11:45:45 +08:00
|
|
|
if (!node.path) return []
|
2026-07-30 17:59:43 +08:00
|
|
|
return [node as AdminNavigationLeaf]
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function filterAdminNavigation(
|
|
|
|
|
nodes: AdminNavigationNode[],
|
|
|
|
|
permissionCodes: ReadonlySet<string>,
|
|
|
|
|
): AdminNavigationNode[] {
|
|
|
|
|
return nodes.flatMap((node) => {
|
2026-08-03 11:45:45 +08:00
|
|
|
if (node.hidden) return []
|
2026-07-30 17:59:43 +08:00
|
|
|
if (node.children?.length) {
|
|
|
|
|
const children = filterAdminNavigation(node.children, permissionCodes)
|
|
|
|
|
return children.length ? [{ ...node, children }] : []
|
|
|
|
|
}
|
2026-08-03 20:50:18 +08:00
|
|
|
const alternativePermissions = node.permissionAnyOf ?? []
|
|
|
|
|
const requiresPermission = Boolean(node.permission || alternativePermissions.length)
|
|
|
|
|
const hasPermission = !requiresPermission || Boolean(
|
|
|
|
|
(node.permission && permissionCodes.has(node.permission))
|
|
|
|
|
|| alternativePermissions.some((permission) => permissionCodes.has(permission)),
|
|
|
|
|
)
|
|
|
|
|
if (!node.path || !hasPermission) return []
|
2026-07-30 17:59:43 +08:00
|
|
|
return [{ ...node }]
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getFirstNavigationLeaf(nodes: AdminNavigationNode[]): AdminNavigationLeaf | null {
|
|
|
|
|
return getNavigationLeaves(nodes)[0] ?? null
|
|
|
|
|
}
|