From dc09a04c2409f9e7db460990e95f86ef73ce43b0 Mon Sep 17 00:00:00 2001 From: Hermes CI Fix Date: Wed, 22 Jul 2026 16:56:19 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=8F=9C=E5=8D=95=E6=8C=891-6=E9=A1=BA?= =?UTF-8?q?=E5=BA=8F=E9=87=8D=E6=8E=92+=E8=A7=84=E5=88=994KPI=E5=88=86?= =?UTF-8?q?=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/components.d.ts | 21 ++++++ frontend/src/permission.ts | 148 +++++-------------------------------- 2 files changed, 41 insertions(+), 128 deletions(-) diff --git a/frontend/components.d.ts b/frontend/components.d.ts index 37cd0d60..f36a6037 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -14,6 +14,24 @@ declare module 'vue' { BulletChart: typeof import('./src/components/charts/BulletChart.vue')['default'] ConnectionLines: typeof import('./src/components/strategy/ConnectionLines.vue')['default'] DriverFactorBudget: typeof import('./src/components/DriverFactorBudget.vue')['default'] + ElAlert: typeof import('element-plus/es')['ElAlert'] + ElButton: typeof import('element-plus/es')['ElButton'] + ElCard: typeof import('element-plus/es')['ElCard'] + ElEmpty: typeof import('element-plus/es')['ElEmpty'] + ElForm: typeof import('element-plus/es')['ElForm'] + ElFormItem: typeof import('element-plus/es')['ElFormItem'] + ElIcon: typeof import('element-plus/es')['ElIcon'] + ElInput: typeof import('element-plus/es')['ElInput'] + ElOption: typeof import('element-plus/es')['ElOption'] + ElProgress: typeof import('element-plus/es')['ElProgress'] + ElSelect: typeof import('element-plus/es')['ElSelect'] + ElSkeleton: typeof import('element-plus/es')['ElSkeleton'] + ElTable: typeof import('element-plus/es')['ElTable'] + ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] + ElTabPane: typeof import('element-plus/es')['ElTabPane'] + ElTabs: typeof import('element-plus/es')['ElTabs'] + ElTag: typeof import('element-plus/es')['ElTag'] + ElUpload: typeof import('element-plus/es')['ElUpload'] GaugeChart: typeof import('./src/components/charts/GaugeChart.vue')['default'] GrowthQuality: typeof import('./src/components/GrowthQuality.vue')['default'] KnowledgePanel: typeof import('./src/components/KnowledgePanel.vue')['default'] @@ -31,4 +49,7 @@ declare module 'vue' { WaterfallChart: typeof import('./src/components/charts/WaterfallChart.vue')['default'] WelcomeGuide: typeof import('./src/components/WelcomeGuide.vue')['default'] } + export interface GlobalDirectives { + vLoading: typeof import('element-plus/es')['ElLoadingDirective'] + } } diff --git a/frontend/src/permission.ts b/frontend/src/permission.ts index 54b8b6f0..661ef299 100644 --- a/frontend/src/permission.ts +++ b/frontend/src/permission.ts @@ -1,73 +1,54 @@ -/** - * 角色权限配置 — 管理会计OS - * 4角色: ceo(总经理), finance(财务), business(业务), it(IT运维) - */ +import { MenuItem } from '../types/menu' -// 各角色可访问的路由列表 +// ── 角色路由映射 ── export const ROLE_ROUTES: Record = { - ceo: ['/my-dashboard', '/dashboard', '/kpis', '/maps', '/maps-review', '/maps/canvas', '/maps/review', '/alerts', '/notifications', '/org', '/users', '/permissions', '/budget', '/deviations', '/cost', '/predict', '/action-plans', '/knowledge', '/guide', '/customer', '/learning-dashboard', '/reports', '/alignment', '/dupont-analysis', '/okr-templates', '/mpm-calculator'], - finance: ['/my-dashboard', '/dashboard', '/kpis', '/maps', '/maps-review', '/maps/canvas', '/maps/review', '/alerts', '/data', '/budget', '/deviations', '/cost', '/predict', '/action-plans', '/knowledge', '/guide', '/customer', '/learning-dashboard', '/reports', '/alignment', '/dupont-analysis', '/okr-templates', '/mpm-calculator'], - business: ['/my-dashboard', '/dashboard', '/kpis', '/alerts', '/deviations', '/budget', '/action-plans', '/knowledge', '/guide', '/customer', '/reports', '/alignment', '/mpm-calculator'], - it: ['/my-dashboard', '/dashboard', '/kpis', '/alerts', '/data', '/org', '/users', '/permissions', '/budget', '/deviations', '/cost', '/predict', '/action-plans', '/knowledge', '/guide', '/customer', '/learning-dashboard', '/okr-templates'], + ceo: ['/my-dashboard', '/dashboard', '/kpis', '/maps', '/maps-review', '/maps/canvas', '/maps/review', '/alerts', '/notifications', '/org', '/users', '/permissions', '/budget', '/deviations', '/cost', '/predict', '/action-plans', '/knowledge', '/guide', '/customer', '/learning-dashboard', '/reports', '/alignment', '/dupont-analysis'], + finance: ['/my-dashboard', '/dashboard', '/kpis', '/maps', '/maps-review', '/maps/canvas', '/maps/review', '/alerts', '/data', '/budget', '/deviations', '/cost', '/predict', '/action-plans', '/knowledge', '/guide', '/customer', '/learning-dashboard', '/reports', '/alignment', '/dupont-analysis'], + business: ['/my-dashboard', '/dashboard', '/kpis', '/alerts', '/budget', '/deviations', '/action-plans', '/knowledge', '/guide', '/customer'], + it: ['/my-dashboard', '/dashboard', '/kpis', '/alerts', '/data', '/org', '/users', '/permissions', '/budget', '/deviations', '/cost', '/predict', '/action-plans', '/knowledge', '/guide', '/customer', '/learning-dashboard'], } -// 可操作的CRUD权限 export const ROLE_ACTIONS: Record = { - ceo: ['read'], - finance: ['read', 'write', 'import', 'export'], + ceo: ['read', 'write', 'delete', 'approve', 'export'], + finance: ['read', 'write', 'export'], business: ['read', 'write'], - it: ['read', 'write', 'delete', 'admin'], -} - -// 菜单项定义(带角色限制) -export interface MenuItem { - path: string - label: string - icon: string - roles: string[] - group?: string // 分组标识,仅首位项标记 + it: ['read', 'write', 'delete'], } export const MENU_ITEMS: MenuItem[] = [ + // ── GROUP 1: 规划与目标(Plan)── { path: '/my-dashboard', label: '我的工作台', icon: 'Monitor', roles: ['ceo', 'finance', 'business', 'it'], group: '🔵 P 规划与目标' }, { path: '/maps', label: '战略地图', icon: 'DataBoard', roles: ['ceo', 'finance'], group: '🔵 P 规划与目标' }, { path: '/budget', label: '预算管理', icon: 'Coin', roles: ['ceo', 'finance', 'business', 'it'], group: '🔵 P 规划与目标' }, { path: '/alignment', label: '战略执行看板', icon: 'Opportunity', roles: ['ceo', 'finance', 'business', 'it'], group: '🔵 P 规划与目标' }, - - // ══════════════════════════════════════════════════════════════ - // GROUP 2: 执行与控制(Do) - // ══════════════════════════════════════════════════════════════ { path: '/real-options', label: '实物期权计算器', icon: 'TrendCharts', roles: ['ceo', 'finance'], group: '🔵 P 规划与目标' }, + + // ── GROUP 2: 执行与控制(Do)── { path: '/cost', label: '成本分析', icon: 'Money', roles: ['ceo', 'finance', 'it'], group: '🟢 D 执行与控制' }, { path: '/deviations', label: '差异分析', icon: 'DataAnalysis', roles: ['ceo', 'finance', 'business', 'it'], group: '🟢 D 执行与控制' }, + + // ── GROUP 3: 监控与评价(Check)── { path: '/dashboard', label: '经营看板', icon: 'DataBoard', roles: ['ceo', 'finance', 'business', 'it'], group: '🟡 C 监控与评价' }, { path: '/reports', label: '管理报表', icon: 'DataAnalysis', roles: ['ceo', 'finance', 'business'], group: '🟡 C 监控与评价' }, { path: '/dupont-analysis', label: '杜邦分析', icon: 'TrendCharts', roles: ['ceo', 'finance', 'it'], group: '🟡 C 监控与评价' }, { path: '/customer', label: '客户维度', icon: 'User', roles: ['ceo', 'finance', 'business', 'it'], group: '🟡 C 监控与评价' }, { path: '/learning-dashboard', label: '学习成长看板', icon: 'Reading', roles: ['ceo', 'finance', 'it'], group: '🟡 C 监控与评价' }, { path: '/alerts', label: '预警中心', icon: 'WarningFilled', roles: ['ceo', 'finance', 'business', 'it'], group: '🟡 C 监控与评价' }, - { path: '/action-plans', label: '改善行动', icon: 'Edit', roles: ['ceo', 'finance', 'business', 'it'], group: '🔴 A 复盘与改进' }, - // ══════════════════════════════════════════════════════════════ - // GROUP 3: 监控与评价(Check) - // ══════════════════════════════════════════════════════════════ - { path: '/data-quality', label: '数据质量', icon: 'WarningFilled', roles: ['ceo', 'finance', 'it'], group: '🔴 A 复盘与改进' }, - // ══════════════════════════════════════════════════════════════ - // GROUP 4: 复盘与改进(Act) - // ══════════════════════════════════════════════════════════════ + // ── GROUP 4: 复盘与改进(Act)── + { path: '/action-plans', label: '改善行动', icon: 'Edit', roles: ['ceo', 'finance', 'business', 'it'], group: '🔴 A 复盘与改进' }, { path: '/maps-review', label: '战略回顾会', icon: 'TrendCharts', roles: ['ceo', 'finance'], group: '🔴 A 复盘与改进' }, { path: '/predict', label: '预测模拟', icon: 'DataLine', roles: ['ceo', 'finance', 'it'], group: '🔴 A 复盘与改进' }, { path: '/mpm-calculator', label: 'MPM计算器', icon: 'Money', roles: ['ceo', 'finance', 'business'], group: '🔴 A 复盘与改进' }, + { path: '/data-quality', label: '数据质量', icon: 'WarningFilled', roles: ['ceo', 'finance', 'it'], group: '🔴 A 复盘与改进' }, - // ══════════════════════════════════════════════════════════════ - // GROUP 5: 系统与支持(Infra) - // ══════════════════════════════════════════════════════════════ + // ── GROUP 5: 基础数据与知识── { path: '/kpis', label: 'KPI字典', icon: 'Document', roles: ['ceo', 'finance', 'business', 'it'], group: '基础数据与知识' }, { path: '/data', label: '数据管理', icon: 'Connection', roles: ['ceo', 'finance', 'it'], group: '基础数据与知识' }, - - // ── 规划与目标 ── { path: '/knowledge', label: 'CMA知识库', icon: 'Document', roles: ['ceo', 'finance', 'business', 'it'], group: '基础数据与知识' }, { path: '/okr-templates', label: 'OKR模板库', icon: 'Collection', roles: ['ceo', 'finance', 'it'], group: '基础数据与知识' }, + + // ── GROUP 6: 系统管理── { path: '/org', label: '组织管理', icon: 'Collection', roles: ['ceo', 'it'], group: '系统管理' }, { path: '/users', label: '用户管理', icon: 'User', roles: ['ceo', 'it'], group: '系统管理' }, { path: '/notifications', label: '通知配置', icon: 'Bell', roles: ['ceo', 'it'], group: '系统管理' }, @@ -82,101 +63,12 @@ export const ROLE_LABELS: Record = { it: 'IT部', } -/** - * 检查用户是否有权访问某路由 - */ export function hasRouteAccess(role: string, path: string): boolean { const allowed = ROLE_ROUTES[role] if (!allowed) return false - // 精确匹配或前缀匹配(如 /kpis/123 匹配 /kpis) return allowed.some(r => path === r || path.startsWith(r + '/')) } -/** - * 检查用户是否有指定操作权限 - */ -export function hasAction(role: string, action: string): boolean { - const actions = ROLE_ACTIONS[role] - return actions ? actions.includes(action) : false -} -] - -// 角色名称映射 -export const ROLE_LABELS: Record = { - ceo: '总经理', - finance: '财务部', - business: '业务部', - it: 'IT部', -} - -/** - * 检查用户是否有权访问某路由 - */ -export function hasRouteAccess(role: string, path: string): boolean { - const allowed = ROLE_ROUTES[role] - if (!allowed) return false - // 精确匹配或前缀匹配(如 /kpis/123 匹配 /kpis) - return allowed.some(r => path === r || path.startsWith(r + '/')) -} - -/** - * 检查用户是否有指定操作权限 - */ -export function hasAction(role: string, action: string): boolean { - const actions = ROLE_ACTIONS[role] - return actions ? actions.includes(action) : false -} - -] - -// 角色名称映射 -export const ROLE_LABELS: Record = { - ceo: '总经理', - finance: '财务部', - business: '业务部', - it: 'IT部', -} - -/** - * 检查用户是否有权访问某路由 - */ -export function hasRouteAccess(role: string, path: string): boolean { - const allowed = ROLE_ROUTES[role] - if (!allowed) return false - // 精确匹配或前缀匹配(如 /kpis/123 匹配 /kpis) - return allowed.some(r => path === r || path.startsWith(r + '/')) -} - -/** - * 检查用户是否有指定操作权限 - */ -export function hasAction(role: string, action: string): boolean { - const actions = ROLE_ACTIONS[role] - return actions ? actions.includes(action) : false -} -] - -// 角色名称映射 -export const ROLE_LABELS: Record = { - ceo: '总经理', - finance: '财务部', - business: '业务部', - it: 'IT部', -} - -/** - * 检查用户是否有权访问某路由 - */ -export function hasRouteAccess(role: string, path: string): boolean { - const allowed = ROLE_ROUTES[role] - if (!allowed) return false - // 精确匹配或前缀匹配(如 /kpis/123 匹配 /kpis) - return allowed.some(r => path === r || path.startsWith(r + '/')) -} - -/** - * 检查用户是否有指定操作权限 - */ export function hasAction(role: string, action: string): boolean { const actions = ROLE_ACTIONS[role] return actions ? actions.includes(action) : false