diff --git a/frontend/components.d.ts b/frontend/components.d.ts index b9476931..37cd0d60 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -14,54 +14,6 @@ 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'] - ElAside: typeof import('element-plus/es')['ElAside'] - ElButton: typeof import('element-plus/es')['ElButton'] - ElCard: typeof import('element-plus/es')['ElCard'] - ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] - ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup'] - ElCol: typeof import('element-plus/es')['ElCol'] - ElContainer: typeof import('element-plus/es')['ElContainer'] - ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] - ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] - ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] - ElDialog: typeof import('element-plus/es')['ElDialog'] - ElDivider: typeof import('element-plus/es')['ElDivider'] - ElDropdown: typeof import('element-plus/es')['ElDropdown'] - ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] - ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] - ElEmpty: typeof import('element-plus/es')['ElEmpty'] - ElForm: typeof import('element-plus/es')['ElForm'] - ElFormItem: typeof import('element-plus/es')['ElFormItem'] - ElHeader: typeof import('element-plus/es')['ElHeader'] - ElIcon: typeof import('element-plus/es')['ElIcon'] - ElInput: typeof import('element-plus/es')['ElInput'] - ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] - ElMain: typeof import('element-plus/es')['ElMain'] - ElMenu: typeof import('element-plus/es')['ElMenu'] - ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] - ElOption: typeof import('element-plus/es')['ElOption'] - ElPagination: typeof import('element-plus/es')['ElPagination'] - ElProgress: typeof import('element-plus/es')['ElProgress'] - ElRadio: typeof import('element-plus/es')['ElRadio'] - ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] - ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] - ElRow: typeof import('element-plus/es')['ElRow'] - ElSelect: typeof import('element-plus/es')['ElSelect'] - ElSkeleton: typeof import('element-plus/es')['ElSkeleton'] - ElSlider: typeof import('element-plus/es')['ElSlider'] - ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] - ElSwitch: typeof import('element-plus/es')['ElSwitch'] - 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'] - ElTimeline: typeof import('element-plus/es')['ElTimeline'] - ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem'] - ElTooltip: typeof import('element-plus/es')['ElTooltip'] - ElTree: typeof import('element-plus/es')['ElTree'] - 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'] @@ -79,7 +31,4 @@ 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 1cf23d53..54b8b6f0 100644 --- a/frontend/src/permission.ts +++ b/frontend/src/permission.ts @@ -29,11 +29,6 @@ export interface MenuItem { } export const MENU_ITEMS: MenuItem[] = [ - // ── 基础数据 ── - { path: '/kpis', label: 'KPI字典', icon: 'Document', roles: ['ceo', 'finance', 'business', 'it'], group: '基础数据与知识' }, - { path: '/data', label: '数据管理', icon: 'Connection', roles: ['ceo', 'finance', 'it'], group: '基础数据与知识' }, - - // ── 规划与目标 ── { 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 规划与目标' }, @@ -42,31 +37,35 @@ export const MENU_ITEMS: MenuItem[] = [ // ══════════════════════════════════════════════════════════════ // GROUP 2: 执行与控制(Do) // ══════════════════════════════════════════════════════════════ + { path: '/real-options', label: '实物期权计算器', icon: 'TrendCharts', roles: ['ceo', 'finance'], group: '🔵 P 规划与目标' }, { path: '/cost', label: '成本分析', icon: 'Money', roles: ['ceo', 'finance', 'it'], group: '🟢 D 执行与控制' }, { path: '/deviations', label: '差异分析', icon: 'DataAnalysis', roles: ['ceo', 'finance', 'business', 'it'], group: '🟢 D 执行与控制' }, - { path: '/action-plans', label: '改善行动', icon: 'Edit', roles: ['ceo', 'finance', 'business', 'it'], group: '🔴 A 复盘与改进' }, - - // ══════════════════════════════════════════════════════════════ - // 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) // ══════════════════════════════════════════════════════════════ { path: '/maps-review', label: '战略回顾会', icon: 'TrendCharts', roles: ['ceo', 'finance'], group: '🔴 A 复盘与改进' }, { path: '/predict', label: '预测模拟', icon: 'DataLine', roles: ['ceo', 'finance', 'it'], group: '🔴 A 复盘与改进' }, - { path: '/real-options', label: '实物期权计算器', icon: 'TrendCharts', roles: ['ceo', 'finance'], group: '🔵 P 规划与目标' }, { path: '/mpm-calculator', label: 'MPM计算器', icon: 'Money', roles: ['ceo', 'finance', 'business'], group: '🔴 A 复盘与改进' }, // ══════════════════════════════════════════════════════════════ // GROUP 5: 系统与支持(Infra) // ══════════════════════════════════════════════════════════════ + { 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: '基础数据与知识' }, { path: '/org', label: '组织管理', icon: 'Collection', roles: ['ceo', 'it'], group: '系统管理' }, @@ -100,3 +99,85 @@ 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 +}