diff --git a/README.md b/README.md index c988b190..75d17edb 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ pnpm test 工作台已从一期路由与菜单移除;登录后按当前角色进入第一个有权限的业务页面。一期 Admin 路由聚焦登录、客户及注册审核、平台收货仓、快递公司、往来单位、头程订单、内部用户、角色管理和菜单权限。 -后台菜单按“业务管理”和“系统管理”分组。企业审核、仓库、头程订单、平台角色、菜单及按钮权限资源均接入真实接口;内部用户使用 IAM 的真实查询和新增接口。运行时不再提供本地演示业务数据或静态角色预览。 +后台菜单仅展示已接入的一期模块;其余平台原型菜单保留为导航配置中的注释,待对应业务契约接入后再恢复展示。企业审核、仓库、头程订单、平台角色、菜单及按钮权限资源均接入真实接口;内部用户使用 IAM 的真实查询和新增接口。运行时不再提供本地演示业务数据或静态角色预览。 ## 本地 Gateway 联调 diff --git a/src/components/AdminSidebarMenu.test.ts b/src/components/AdminSidebarMenu.test.ts index 9bab1fc2..def2ca02 100644 --- a/src/components/AdminSidebarMenu.test.ts +++ b/src/components/AdminSidebarMenu.test.ts @@ -19,29 +19,23 @@ const stubs = { } describe('AdminSidebarMenu', () => { - it('renders grouped page entries and forwards the active route', () => { + it('renders the visible phase-one modules and forwards the active route', () => { const wrapper = mount(AdminSidebarMenu, { props: { items: adminNavigation, activePath: '/system/users', collapsed: false }, global: { stubs }, }) expect(wrapper.get('nav').attributes('data-active')).toBe('/system/users') - expect(wrapper.findAll('[data-kind="group"] header').map((node) => node.text())).toEqual([ - '业务管理', - '基础资料', - '系统管理', - ]) - expect(wrapper.findAll('[data-kind="item"]').map((node) => node.attributes('data-index'))).toEqual([ - '/customers', - '/customers/reviews', - '/warehouses', - '/orders/first-leg', - '/master-data/express-companies', - '/master-data/counterparties', - '/system/users', - '/system/roles', - '/system/permissions', - ]) + const groupTitles = wrapper.findAll('[data-kind="group"] header').map((node) => node.text()) + const paths = wrapper.findAll('[data-kind="item"]').map((node) => node.attributes('data-index')) + + expect(groupTitles).toContain('客户管理') + expect(groupTitles).toContain('系统管理') + expect(groupTitles).not.toContain('财务管理') + expect(paths).not.toContain('/dashboard') + expect(paths).toContain('/orders/first-leg') + expect(paths).not.toContain('/prototype/reports/overview') + expect(paths).toContain('/system/permissions') }) it('forwards desktop collapse state to Element Plus', () => { @@ -61,7 +55,8 @@ describe('AdminSidebarMenu', () => { }) expect(wrapper.text()).toContain('菜单权限') - expect(wrapper.text()).not.toContain('客户管理') + expect(wrapper.text()).not.toContain('首页数据看板') + expect(wrapper.text()).not.toContain('客户列表') }) it('recursively renders three navigation levels', () => { diff --git a/src/navigation/adminNavigation.test.ts b/src/navigation/adminNavigation.test.ts index 8d3f0825..d56ce38f 100644 --- a/src/navigation/adminNavigation.test.ts +++ b/src/navigation/adminNavigation.test.ts @@ -8,57 +8,59 @@ import { } from './adminNavigation' describe('adminNavigation', () => { - it('keeps phase-one pages grouped without exposing the deferred dashboard', () => { + it('keeps only implemented phase-one menus visible while retaining prototype nodes as comments', () => { expect(adminNavigation.map((item) => item.title)).toEqual([ - '业务管理', - '基础资料', + '客户管理', + '国内仓', + '头程订单', + '通用', '系统管理', ]) expect(adminNavigation[0].children?.map((item) => item.title)).toEqual([ - '客户管理', + '客户列表', '企业审核', - '仓库管理', - '头程订单', ]) - expect(adminNavigation[1].children?.map((item) => item.title)).toEqual(['快递公司', '往来单位']) - expect(adminNavigation[2].children?.map((item) => item.title)).toEqual([ - '内部用户', - '角色管理', + expect(adminNavigation.find((item) => item.key === 'general')?.children?.map((item) => item.title)).toContain('快递公司') + expect(adminNavigation.find((item) => item.key === 'system')?.children?.map((item) => item.title)).toEqual([ + '用户管理', + '角色权限', '菜单权限', ]) }) - it('assigns every page a unique path and permission code', () => { + it('assigns every page a unique path and keeps real permissions unique', () => { const leaves = getNavigationLeaves(adminNavigation) const paths = leaves.map((item) => item.path) - const permissions = leaves.map((item) => item.permission) + const permissions = leaves.flatMap((item) => item.permission ? [item.permission] : []) - expect(paths).toEqual([ - '/customers', - '/customers/reviews', - '/warehouses', - '/orders/first-leg', - '/master-data/express-companies', - '/master-data/counterparties', - '/system/users', - '/system/roles', - '/system/permissions', - ]) + expect(paths).not.toContain('/dashboard') + expect(paths).not.toContain('/prototype/finance/profit-report') + expect(paths).not.toContain('/prototype/system-management/brand-settings') + expect(paths).toContain('/system/permissions') expect(new Set(paths).size).toBe(paths.length) expect(new Set(permissions).size).toBe(permissions.length) expect(leaves.every((item) => Boolean(item.icon))).toBe(true) }) - it('recursively filters leaves without mutating the source tree', () => { + it('filters protected pages without mutating the source tree', () => { const sourceSnapshot = JSON.stringify(adminNavigation, (key, value) => key === 'icon' ? '[icon]' : value) const filtered = filterAdminNavigation(adminNavigation, new Set(['platform:permission:list'])) - expect(getNavigationLeaves(filtered).map((item) => item.path)).toEqual(['/system/permissions']) + const paths = getNavigationLeaves(filtered).map((item) => item.path) + expect(paths).toContain('/system/permissions') + expect(paths).not.toContain('/prototype/finance/profit-report') + expect(paths).not.toContain('/customers') expect(filtered).not.toBe(adminNavigation) - expect(filtered[0]).not.toBe(adminNavigation[2]) + expect(filtered.find((item) => item.key === 'system')).not.toBe(adminNavigation.find((item) => item.key === 'system')) expect(JSON.stringify(adminNavigation, (key, value) => key === 'icon' ? '[icon]' : value)).toBe(sourceSnapshot) }) + it('supports manually hiding a module without removing its route definition', () => { + const filtered = filterAdminNavigation([{ key: 'hidden', title: '隐藏模块', icon: adminNavigation[0].icon, path: '/hidden', hidden: true }], new Set()) + + expect(filtered).toEqual([]) + }) + it('keeps directories based on accessible descendants instead of a directory permission', () => { const nodes: AdminNavigationNode[] = [{ key: 'operations', diff --git a/src/navigation/adminNavigation.ts b/src/navigation/adminNavigation.ts index dd1f2daf..7af24ee6 100644 --- a/src/navigation/adminNavigation.ts +++ b/src/navigation/adminNavigation.ts @@ -1,6 +1,5 @@ import type { Component } from 'vue' import { - BriefcaseBusiness, Building2, ContactRound, ClipboardList, @@ -20,44 +19,215 @@ export interface AdminNavigationNode { icon: Component path?: string permission?: string + /** Set to true to remove a prototype module from the sidebar without deleting its route. */ + hidden?: boolean children?: AdminNavigationNode[] } export interface AdminNavigationLeaf extends AdminNavigationNode { path: string - permission: string + permission?: string children?: never } export const adminNavigation: AdminNavigationNode[] = [ + // 原型菜单暂未接入一期范围;恢复展示时移除对应注释即可。 + // { key: 'dashboard', title: '首页数据看板', icon: ClipboardList, path: '/dashboard' }, { - key: 'business', - title: '业务管理', - icon: BriefcaseBusiness, + key: 'customers', + title: '客户管理', + icon: Building2, children: [ - { key: 'customers', title: '客户管理', icon: Building2, path: '/customers', permission: 'platform:customer:list' }, + { key: 'customers-list', title: '客户列表', icon: Building2, path: '/customers', permission: 'platform:customer:list' }, { key: 'enterprise-reviews', title: '企业审核', icon: FileCheck2, path: '/customers/reviews', permission: 'platform:customer:review' }, - { key: 'warehouses', title: '仓库管理', icon: WarehouseIcon, path: '/warehouses', permission: 'platform:warehouse:list' }, - { key: 'first-leg-orders', title: '头程订单', icon: ClipboardList, path: '/orders/first-leg', permission: 'platform:order:list' }, + // { key: 'warehouse-tier-fee', title: '海外仓阶梯费用', icon: Building2, path: '/prototype/customer-management/warehouse-tier-fee' }, ], }, { - key: 'master-data', - title: '基础资料', + key: 'domestic-warehouse', + title: '国内仓', + icon: WarehouseIcon, + children: [ + { key: 'warehouses', title: '仓库管理', icon: WarehouseIcon, path: '/warehouses', permission: 'platform:warehouse:list' }, + // { key: 'inbound-list', title: '入库单列表', icon: ClipboardList, path: '/prototype/domestic-warehouse/inbound-list' }, + // { key: 'inbound-dedup', title: '入库单测量', icon: ClipboardList, path: '/prototype/domestic-warehouse/inbound-dedup' }, + // { key: 'inbound-measure', title: '测量数据审核', icon: FileCheck2, path: '/prototype/domestic-warehouse/inbound-measure' }, + // { key: 'inbound-receive', title: '接收审核', icon: FileCheck2, path: '/prototype/domestic-warehouse/inbound-receive' }, + // { key: 'overseas-location', title: '库位管理', icon: WarehouseIcon, path: '/prototype/domestic-warehouse/location-management' }, + // { key: 'pallet-management', title: '托盘管理', icon: WarehouseIcon, path: '/prototype/domestic-warehouse/pallet-management' }, + ], + }, + { + key: 'first-leg-orders', + title: '头程订单', + icon: ClipboardList, + path: '/orders/first-leg', + permission: 'platform:order:list', + }, + /* { + key: 'overseas-delivery', + title: '海外派送', + icon: Truck, + 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: 'overseas-inventory', + title: '海外仓库存', + icon: WarehouseIcon, + children: [ + { key: 'overseas-stock', title: '库存列表', icon: WarehouseIcon, path: '/prototype/overseas-inventory/stock-list' }, + { key: 'overseas-inbound', title: '海外入库单', icon: ClipboardList, path: '/prototype/overseas-inventory/inbound-orders' }, + { key: 'overseas-outbound', title: '出库单', icon: ClipboardList, path: '/prototype/overseas-inventory/outbound-orders' }, + { key: 'overseas-relabel', title: '换标订单', icon: ClipboardList, path: '/prototype/overseas-inventory/relabel-orders' }, + ], + }, + { + key: 'service-center', + title: '服务中心', + icon: ClipboardList, + 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: [ + { key: 'supplier-list', title: '供应商列表', icon: ContactRound, path: '/prototype/supplier-center/supplier-list' }, + ], + }, + { + key: 'container-management', + title: '货柜管理', + icon: WarehouseIcon, + 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, + children: [ + { key: 'overseas-users', title: '用户管理', icon: Users, path: '/prototype/overseas-business/users' }, + { 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' }, + ], + }, + { + key: 'overseas-warehouse-orders', + title: '海外仓下单', + icon: ClipboardList, + children: [ + { key: 'overseas-order-inbound', title: '退货入库', icon: ClipboardList, path: '/prototype/overseas-warehouse-orders/return-inbound' }, + { key: 'overseas-order-relabel', title: '退货换标', icon: ClipboardList, path: '/prototype/overseas-warehouse-orders/return-relabel' }, + { key: 'overseas-order-outbound', title: '退货出库', icon: ClipboardList, path: '/prototype/overseas-warehouse-orders/return-outbound' }, + ], + }, + { + key: 'fba-returns', + title: 'FBA退货', + icon: FileCheck2, + 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' }, + ], + }, + { + key: 'finance', + title: '财务管理', icon: Database, children: [ - { 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' }, + { 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' }, ], }, + { + key: 'vehicle-management', + title: '车辆管理', + icon: Truck, + children: [ + { key: 'vehicle-list', title: '车辆列表', icon: Truck, path: '/prototype/vehicle-management/vehicle-list' }, + ], + }, + { + key: 'reports', + title: '报表中心', + icon: Database, + children: [ + { key: 'report-overview', title: '报表总览', icon: Database, path: '/prototype/reports/overview' }, + { key: 'report-income', title: '收支报表', icon: Database, path: '/prototype/reports/income-expense' }, + { key: 'report-ar-aging', title: '应收账龄分析', icon: Database, path: '/prototype/reports/ar-aging' }, + { key: 'report-cost', title: '费用构成分析', icon: Database, path: '/prototype/reports/cost-composition' }, + { key: 'report-freight', title: '运费报表', icon: Database, path: '/prototype/reports/freight' }, + { key: 'report-business', title: '业务统计报表', icon: Database, path: '/prototype/reports/business-statistics' }, + ], + }, + */ + { + key: 'general', + title: '通用', + icon: Database, + children: [ + // { key: 'general-product', title: '产品信息', icon: Database, path: '/prototype/general/product-information' }, + // { key: 'general-fee', title: '费用项目', icon: Database, path: '/prototype/general/fee-items' }, + // { key: 'general-fba', title: 'FBA编码地址', icon: Database, path: '/prototype/general/fba-address' }, + // { key: 'general-pallet', title: '托盘管理', icon: WarehouseIcon, path: '/prototype/general/pallet-management' }, + // { key: 'general-location', title: '库位/货架管理', icon: WarehouseIcon, path: '/prototype/general/location-management' }, + { 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' }, + // { key: 'general-voyage', title: '船期价格表', icon: Database, path: '/prototype/general/voyage-price-list' }, + ], + }, + /* { + key: 'sales', + title: '销售管理', + icon: Users, + children: [ + { key: 'sales-visits', title: '拜访记录', icon: Users, path: '/prototype/sales/visit-records' }, + { key: 'sales-commission', title: '销售提成', icon: Database, path: '/prototype/sales/commission' }, + ], + }, + { + key: 'messages', + title: '消息中心', + icon: ClipboardList, + children: [ + { key: 'message-management', title: '消息管理', icon: ClipboardList, path: '/prototype/messages/management' }, + { key: 'message-template', title: '模板管理', icon: ClipboardList, path: '/prototype/messages/templates' }, + { key: 'my-messages', title: '我的消息', icon: ClipboardList, path: '/prototype/messages/my-messages' }, + ], + }, + */ { key: 'system', title: '系统管理', icon: Settings, children: [ - { 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' }, + { 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' }, { key: 'permissions', title: '菜单权限', icon: KeyRound, path: '/system/permissions', permission: 'platform:permission:list' }, + // { key: 'tenant-management', title: '租户管理', icon: Building2, path: '/prototype/system-management/tenant-management' }, + // { key: 'brand-settings', title: '品牌设置', icon: Settings, path: '/prototype/system-management/brand-settings' }, ], }, ] @@ -65,7 +235,7 @@ export const adminNavigation: AdminNavigationNode[] = [ export function getNavigationLeaves(nodes: AdminNavigationNode[]): AdminNavigationLeaf[] { return nodes.flatMap((node) => { if (node.children?.length) return getNavigationLeaves(node.children) - if (!node.path || !node.permission) return [] + if (!node.path) return [] return [node as AdminNavigationLeaf] }) } @@ -75,11 +245,12 @@ export function filterAdminNavigation( permissionCodes: ReadonlySet, ): AdminNavigationNode[] { return nodes.flatMap((node) => { + if (node.hidden) return [] if (node.children?.length) { const children = filterAdminNavigation(node.children, permissionCodes) return children.length ? [{ ...node, children }] : [] } - if (!node.path || !node.permission || !permissionCodes.has(node.permission)) return [] + if (!node.path || (node.permission && !permissionCodes.has(node.permission))) return [] return [{ ...node }] }) } diff --git a/src/router/index.ts b/src/router/index.ts index 995ecd3c..0fecbe2d 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -3,6 +3,7 @@ import AppLayout from '@/layouts/AppLayout.vue' import { decideRouteAccess } from '@/router/accessDecision' import { useAccessPreviewStore } from '@/stores/accessPreview' import { useAuthStore } from '@/stores/auth' +import { prototypeModuleRoutes } from '@/router/prototypeModuleRoutes' const router = createRouter({ history: createWebHistory(), @@ -24,6 +25,7 @@ const router = createRouter({ component: () => import('@/views/ModuleView.vue'), meta: { title: '进入管理后台', description: '正在进入当前角色可访问的业务页面' }, }, + ...prototypeModuleRoutes, { path: 'customers', name: 'customers', diff --git a/src/router/prototypeModuleRoutes.ts b/src/router/prototypeModuleRoutes.ts new file mode 100644 index 00000000..81ff03d5 --- /dev/null +++ b/src/router/prototypeModuleRoutes.ts @@ -0,0 +1,135 @@ +import type { RouteRecordRaw } from 'vue-router' +import type { Component } from 'vue' + +type PrototypeRoute = readonly [path: string, name: string, title: string] +type ModuleView = () => Promise + +const moduleViews = { + dashboard: () => import('@/views/prototype/dashboard/ModuleView.vue'), + customerManagement: () => import('@/views/prototype/customer-management/ModuleView.vue'), + domesticWarehouse: () => import('@/views/prototype/domestic-warehouse/ModuleView.vue'), + overseasDelivery: () => import('@/views/prototype/overseas-delivery/ModuleView.vue'), + overseasInventory: () => import('@/views/prototype/overseas-inventory/ModuleView.vue'), + serviceCenter: () => import('@/views/prototype/service-center/ModuleView.vue'), + supplierCenter: () => import('@/views/prototype/supplier-center/ModuleView.vue'), + containerManagement: () => import('@/views/prototype/container-management/ModuleView.vue'), + overseasBusiness: () => import('@/views/prototype/overseas-business/ModuleView.vue'), + overseasWarehouseOrders: () => import('@/views/prototype/overseas-warehouse-orders/ModuleView.vue'), + fbaReturns: () => import('@/views/prototype/fba-returns/ModuleView.vue'), + finance: () => import('@/views/prototype/finance/ModuleView.vue'), + vehicleManagement: () => import('@/views/prototype/vehicle-management/ModuleView.vue'), + reports: () => import('@/views/prototype/reports/ModuleView.vue'), + general: () => import('@/views/prototype/general/ModuleView.vue'), + sales: () => import('@/views/prototype/sales/ModuleView.vue'), + messages: () => import('@/views/prototype/messages/ModuleView.vue'), + systemManagement: () => import('@/views/prototype/system-management/ModuleView.vue'), +} satisfies Record + +function createRoutes(view: ModuleView, routes: PrototypeRoute[]): RouteRecordRaw[] { + return routes.map(([path, name, title]) => ({ + path, + name, + component: view, + props: { title, description: `用于承载${title}相关业务信息` }, + meta: { title, description: `用于承载${title}相关业务信息` }, + })) +} + +export const prototypeModuleRoutes: RouteRecordRaw[] = [ + ...createRoutes(moduleViews.dashboard, [['dashboard', 'dashboard', '首页数据看板']]), + ...createRoutes(moduleViews.customerManagement, [ + ['prototype/customer-management/warehouse-tier-fee', 'warehouse-tier-fee', '海外仓阶梯费用'], + ]), + ...createRoutes(moduleViews.domesticWarehouse, [ + ['prototype/domestic-warehouse/inbound-list', 'inbound-list', '入库单列表'], + ['prototype/domestic-warehouse/inbound-dedup', 'inbound-dedup', '入库单测量'], + ['prototype/domestic-warehouse/inbound-measure', 'inbound-measure', '测量数据审核'], + ['prototype/domestic-warehouse/inbound-receive', 'inbound-receive', '接收审核'], + ['prototype/domestic-warehouse/location-management', 'domestic-location-management', '库位管理'], + ['prototype/domestic-warehouse/pallet-management', 'domestic-pallet-management', '托盘管理'], + ]), + ...createRoutes(moduleViews.overseasDelivery, [ + ['prototype/overseas-delivery/dispatch-list', 'outbound-dispatch', '派送单列表'], + ['prototype/overseas-delivery/vehicle-scheduling', 'outbound-vehicle', '排车列表'], + ['prototype/overseas-delivery/bol', 'outbound-bol', 'BOL'], + ['prototype/overseas-delivery/self-pickup', 'outbound-selfpick', '自提单'], + ]), + ...createRoutes(moduleViews.overseasInventory, [ + ['prototype/overseas-inventory/stock-list', 'overseas-stock', '库存列表'], + ['prototype/overseas-inventory/inbound-orders', 'overseas-inbound', '海外入库单'], + ['prototype/overseas-inventory/outbound-orders', 'overseas-outbound', '出库单'], + ['prototype/overseas-inventory/relabel-orders', 'overseas-relabel', '换标订单'], + ]), + ...createRoutes(moduleViews.serviceCenter, [ + ['prototype/service-center/booking-list', 'booking-list', '订舱列表'], + ['prototype/service-center/truck-list', 'truck-list', '拖车列表'], + ]), + ...createRoutes(moduleViews.supplierCenter, [ + ['prototype/supplier-center/supplier-list', 'supplier-list', '供应商列表'], + ]), + ...createRoutes(moduleViews.containerManagement, [ + ['prototype/container-management/container-list', 'container-list', '货柜列表'], + ['prototype/container-management/manifest', 'container-manifest', '装柜清单'], + ['prototype/container-management/preload-list', 'container-preload', '预装清单'], + ]), + ...createRoutes(moduleViews.overseasBusiness, [ + ['prototype/overseas-business/users', 'overseas-users', '用户管理'], + ['prototype/overseas-business/purchase-orders', 'overseas-purchase', '采购订单'], + ['prototype/overseas-business/local-truck', 'overseas-truck', '本土卡车'], + ['prototype/overseas-business/local-looseboard', 'overseas-looseboard', '本土散板'], + ]), + ...createRoutes(moduleViews.overseasWarehouseOrders, [ + ['prototype/overseas-warehouse-orders/return-inbound', 'overseas-order-inbound', '退货入库'], + ['prototype/overseas-warehouse-orders/return-relabel', 'overseas-order-relabel', '退货换标'], + ['prototype/overseas-warehouse-orders/return-outbound', 'overseas-order-outbound', '退货出库'], + ]), + ...createRoutes(moduleViews.fbaReturns, [ + ['prototype/fba-returns/inbound-orders', 'fba-inbound-order', '入库单'], + ['prototype/fba-returns/relabel-orders', 'fba-relabel-order', '换标单'], + ['prototype/fba-returns/outbound-orders', 'fba-outbound-order', '出库单'], + ['prototype/fba-returns/pallet-management', 'fba-pallet', '托盘管理'], + ['prototype/fba-returns/carton-management', 'fba-carton', '箱号管理'], + ['prototype/fba-returns/product-catalog', 'fba-product', '产品库'], + ['prototype/fba-returns/return-stock', 'fba-return-stock', '退货库存'], + ]), + ...createRoutes(moduleViews.finance, [ + ['prototype/finance/customer-arap', 'customer-arap', '客户应收应付'], + ['prototype/finance/customer-reconciliation', 'customer-recon-stmt', '客户对账单'], + ['prototype/finance/settlement-ar', 'settlement-ar', '结算单应收'], + ['prototype/finance/settlement-ap', 'settlement-ap', '结算单应付'], + ['prototype/finance/cost-analysis', 'finance-cost', '费用分析'], + ['prototype/finance/profit-report', 'profit', '利润表'], + ]), + ...createRoutes(moduleViews.vehicleManagement, [ + ['prototype/vehicle-management/vehicle-list', 'vehicle-list', '车辆列表'], + ]), + ...createRoutes(moduleViews.reports, [ + ['prototype/reports/overview', 'report-overview', '报表总览'], + ['prototype/reports/income-expense', 'report-income', '收支报表'], + ['prototype/reports/ar-aging', 'report-ar-aging', '应收账龄分析'], + ['prototype/reports/cost-composition', 'report-cost', '费用构成分析'], + ['prototype/reports/freight', 'report-freight', '运费报表'], + ['prototype/reports/business-statistics', 'report-business', '业务统计报表'], + ]), + ...createRoutes(moduleViews.general, [ + ['prototype/general/product-information', 'general-product', '产品信息'], + ['prototype/general/fee-items', 'general-fee', '费用项目'], + ['prototype/general/fba-address', 'general-fba', 'FBA编码地址'], + ['prototype/general/pallet-management', 'general-pallet', '托盘管理'], + ['prototype/general/location-management', 'general-location', '库位/货架管理'], + ['prototype/general/voyage-price-list', 'general-voyage', '船期价格表'], + ]), + ...createRoutes(moduleViews.sales, [ + ['prototype/sales/visit-records', 'sales-visits', '拜访记录'], + ['prototype/sales/commission', 'sales-commission', '销售提成'], + ]), + ...createRoutes(moduleViews.messages, [ + ['prototype/messages/management', 'message-management', '消息管理'], + ['prototype/messages/templates', 'message-template', '模板管理'], + ['prototype/messages/my-messages', 'my-messages', '我的消息'], + ]), + ...createRoutes(moduleViews.systemManagement, [ + ['prototype/system-management/tenant-management', 'tenant-management', '租户管理'], + ['prototype/system-management/brand-settings', 'brand-settings', '品牌设置'], + ]), +] diff --git a/src/styles/shell.css b/src/styles/shell.css index e3f0b300..481bf3be 100644 --- a/src/styles/shell.css +++ b/src/styles/shell.css @@ -7,7 +7,23 @@ .brand-logo-mark { display: none; width: 40px; height: 40px; } .is-collapsed .brand-logo-full { display: none; } .is-collapsed .brand-logo-mark { display: block; } -.side-nav { flex: 1; min-height: 0; overflow-y: auto; padding: 14px 10px; } +.side-nav { + flex: 1; + min-height: 0; + overflow-y: auto; + padding: 14px 10px; + scrollbar-color: rgba(203, 214, 230, .5) transparent; + scrollbar-width: thin; +} +.side-nav::-webkit-scrollbar { width: 10px; } +.side-nav::-webkit-scrollbar-track, +.side-nav::-webkit-scrollbar-corner { background: transparent; } +.side-nav::-webkit-scrollbar-thumb { border: 3px solid transparent; border-radius: 999px; background: rgba(203, 214, 230, .5); background-clip: padding-box; } +.side-nav::-webkit-scrollbar-thumb:hover { background-color: rgba(225, 233, 244, .78); } +.side-nav::-webkit-scrollbar-button, +.side-nav::-webkit-scrollbar-button:single-button, +.side-nav::-webkit-scrollbar-button:vertical:decrement, +.side-nav::-webkit-scrollbar-button:vertical:increment { display: none; width: 0; height: 0; background: transparent; } .side-nav-menu { width: 100%; border-right: 0 !important; diff --git a/src/views/ExpressCompaniesView.vue b/src/views/ExpressCompaniesView.vue index af3dd59f..3bcc0d22 100644 --- a/src/views/ExpressCompaniesView.vue +++ b/src/views/ExpressCompaniesView.vue @@ -255,13 +255,16 @@ onMounted(() => { 重新查询 - - - +
+
+
+

快递公司列表

+

共 {{ total }} 家快递公司

+
+ 新增快递公司 +
{ .tracking-template { display: block; max-width: 100%; color: #475467; font-family: "Cascadia Code", Consolas, monospace; font-size: 11px; overflow-wrap: anywhere; } .remark-cell { display: -webkit-box; overflow: hidden; line-height: 1.5; -webkit-box-orient: vertical; -webkit-line-clamp: 2; } .form-help code { color: #475467; font-family: "Cascadia Code", Consolas, monospace; } + +@media (max-width: 720px) { + .panel-heading .el-button { width: 100%; } +} diff --git a/src/views/InternalUsersView.vue b/src/views/InternalUsersView.vue index 7d900542..53e88e05 100644 --- a/src/views/InternalUsersView.vue +++ b/src/views/InternalUsersView.vue @@ -242,8 +242,9 @@ onMounted(loadUsers) - +
+

内部用户 共 {{ filteredRows.length }} 条

数据和维护操作均由平台 IAM 提供,角色关系按成员维度保存。

新增用户
diff --git a/src/views/PartnersView.vue b/src/views/PartnersView.vue index 57a82045..4426e287 100644 --- a/src/views/PartnersView.vue +++ b/src/views/PartnersView.vue @@ -289,11 +289,7 @@ onMounted(loadPartners) - +