feat(aihr): 重构运营后台与租户知识治理
- 重构运营总览、侧栏和内容运营工作台\n- 增加大喇叭、成果审核与运营统计链路\n- 补齐租户、知识空间、分类和知识维护闭环\n- 加固标签页租户上下文、停用租户写保护与迁移预检
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
export const operationsConsoleRoles = ['superadmin', 'hr_operator'];
|
||||
export const tenantGovernanceRoles = ['superadmin'];
|
||||
|
||||
type MatchedRoute = { meta?: { roles?: readonly string[] } };
|
||||
|
||||
export const hasRequiredRouteRole = (userRoles: readonly string[], requiredRoles: unknown): boolean => {
|
||||
if (requiredRoles === undefined) return true;
|
||||
if (!Array.isArray(requiredRoles) || requiredRoles.length === 0) return false;
|
||||
return userRoles.some((role) => requiredRoles.includes(role));
|
||||
};
|
||||
|
||||
export const canAccessMatchedRoutes = (userRoles: readonly string[], matched: readonly MatchedRoute[]): boolean =>
|
||||
matched.every((record) => hasRequiredRouteRole(userRoles, record.meta?.roles));
|
||||
|
||||
export const normalizeOperationPath = (path: string): string => path.replace(/^\/admin(?=\/|$)/, '/recruit');
|
||||
@@ -1,6 +1,9 @@
|
||||
import { createWebHistory, createRouter, RouteRecordRaw } from 'vue-router';
|
||||
/* Layout */
|
||||
import Layout from '@/layout/index.vue';
|
||||
import { operationsConsoleRoles, tenantGovernanceRoles } from './access';
|
||||
|
||||
export { operationsConsoleRoles, tenantGovernanceRoles } from './access';
|
||||
|
||||
/**
|
||||
* Note: 路由配置项
|
||||
@@ -66,25 +69,35 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
path: '',
|
||||
component: Layout,
|
||||
redirect: '/index',
|
||||
meta: { roles: operationsConsoleRoles },
|
||||
children: [
|
||||
{
|
||||
path: '/index',
|
||||
component: () => import('@/views/index.vue'),
|
||||
name: 'Index',
|
||||
meta: { title: '首页', icon: 'dashboard', affix: true }
|
||||
meta: { title: '运营总览', icon: 'dashboard', affix: true, hideTagsView: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/recruit',
|
||||
alias: '/admin',
|
||||
component: Layout,
|
||||
redirect: '/recruit/interview',
|
||||
redirect: (to) => (['/admin', '/admin/'].includes(to.path) ? '/admin/org-permission' : '/recruit/interview'),
|
||||
alwaysShow: true,
|
||||
meta: { title: '招聘与组织', icon: 'user', roles: operationsConsoleRoles },
|
||||
children: [
|
||||
{
|
||||
path: 'interview',
|
||||
component: () => import('@/views/recruit/interview.vue'),
|
||||
name: 'RecruitInterview',
|
||||
meta: { title: 'AI面试', icon: 'user' }
|
||||
},
|
||||
{
|
||||
path: 'org-permission',
|
||||
component: () => import('@/views/admin/org-permission.vue'),
|
||||
name: 'OrgPermission',
|
||||
meta: { title: '组织与权限', icon: 'tree', activeMenu: '/recruit/org-permission' }
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -93,25 +106,26 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
component: Layout,
|
||||
redirect: '/train/practice',
|
||||
alwaysShow: true,
|
||||
meta: { title: '训练运营', icon: 'peoples' },
|
||||
meta: { title: '训练运营', icon: 'peoples', roles: operationsConsoleRoles },
|
||||
children: [
|
||||
{
|
||||
path: 'practice',
|
||||
component: () => import('@/views/train/practice.vue'),
|
||||
name: 'TrainPractice',
|
||||
meta: { title: '三角色对练', icon: 'peoples' }
|
||||
meta: { title: '训练看板', icon: 'peoples' }
|
||||
},
|
||||
{
|
||||
path: 'scenarios',
|
||||
component: () => import('@/views/train/scenarios.vue'),
|
||||
name: 'TrainScenarios',
|
||||
meta: { title: '场景库', icon: 'list' }
|
||||
meta: { title: '场景与评分', icon: 'list' }
|
||||
},
|
||||
{
|
||||
path: 'rubrics',
|
||||
component: () => import('@/views/train/rubrics.vue'),
|
||||
name: 'TrainRubrics',
|
||||
meta: { title: 'Rubric 规则', icon: 'list' }
|
||||
hidden: true,
|
||||
meta: { title: '评分规则', icon: 'list', activeMenu: '/train/scenarios' }
|
||||
},
|
||||
{
|
||||
path: 'reviews',
|
||||
@@ -123,67 +137,92 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
path: 'difficulties',
|
||||
component: () => import('@/views/train/difficulties.vue'),
|
||||
name: 'TrainDifficulties',
|
||||
meta: { title: '常见难题', icon: 'chart' }
|
||||
hidden: true,
|
||||
meta: { title: '常见难题', icon: 'chart', activeMenu: '/train/scenarios' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/community/questions',
|
||||
path: '/operations',
|
||||
component: Layout,
|
||||
redirect: '/operations/work-results',
|
||||
alwaysShow: true,
|
||||
meta: { title: '工作成果', icon: 'list', roles: operationsConsoleRoles },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/community/questions.vue'),
|
||||
name: 'CommunityQuestions',
|
||||
meta: { title: '问题榜运营', icon: 'message' }
|
||||
path: 'work-results',
|
||||
component: () => import('@/views/operations/work-results.vue'),
|
||||
name: 'OperationsWorkResults',
|
||||
meta: { title: '项目工作成果', icon: 'list' }
|
||||
},
|
||||
{
|
||||
path: 'work-report-review',
|
||||
component: () => import('@/views/operations/work-report-review.vue'),
|
||||
name: 'OperationsWorkReportReview',
|
||||
meta: { title: '成果投稿审核', icon: 'finish' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/knowledge/cases',
|
||||
path: '/knowledge',
|
||||
component: Layout,
|
||||
redirect: '/knowledge/processing',
|
||||
alwaysShow: true,
|
||||
meta: { title: '知识运营', icon: 'documentation', roles: operationsConsoleRoles },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/knowledge/cases.vue'),
|
||||
name: 'KnowledgeCases',
|
||||
meta: { title: '案例沉淀', icon: 'list' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/knowledge/spaces',
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/knowledge/spaces.vue'),
|
||||
name: 'KnowledgeSpaces',
|
||||
meta: { title: '知识空间', icon: 'tree-table' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/knowledge/sop',
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/knowledge/sop.vue'),
|
||||
name: 'KnowledgeSop',
|
||||
meta: { title: 'SOP知识库', icon: 'documentation' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/knowledge/processing',
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
path: 'processing',
|
||||
component: () => import('@/views/knowledge/processing.vue'),
|
||||
name: 'KnowledgeProcessing',
|
||||
meta: { title: '资料处理', icon: 'upload' }
|
||||
},
|
||||
{
|
||||
path: 'sop',
|
||||
component: () => import('@/views/knowledge/sop.vue'),
|
||||
name: 'KnowledgeSop',
|
||||
meta: { title: 'SOP知识库', icon: 'documentation' }
|
||||
},
|
||||
{
|
||||
path: 'spaces',
|
||||
component: () => import('@/views/knowledge/spaces.vue'),
|
||||
name: 'KnowledgeSpaces',
|
||||
meta: { title: '知识空间', icon: 'tree-table' }
|
||||
},
|
||||
{
|
||||
path: 'cases',
|
||||
component: () => import('@/views/knowledge/cases.vue'),
|
||||
name: 'KnowledgeCases',
|
||||
meta: { title: '成果投稿', icon: 'list' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/content',
|
||||
component: Layout,
|
||||
redirect: '/content/broadcast',
|
||||
alwaysShow: true,
|
||||
meta: { title: '内容运营', icon: 'message', roles: operationsConsoleRoles },
|
||||
children: [
|
||||
{
|
||||
path: 'broadcast',
|
||||
component: () => import('@/views/content/broadcast.vue'),
|
||||
name: 'ContentBroadcast',
|
||||
meta: { title: '银城大喇叭', icon: 'message' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/community',
|
||||
component: Layout,
|
||||
redirect: '/community/questions',
|
||||
alwaysShow: true,
|
||||
meta: { title: '共创运营', icon: 'message', roles: operationsConsoleRoles },
|
||||
children: [
|
||||
{
|
||||
path: 'questions',
|
||||
component: () => import('@/views/community/questions.vue'),
|
||||
name: 'CommunityQuestions',
|
||||
meta: { title: '问题榜', icon: 'message' }
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -192,7 +231,7 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
component: Layout,
|
||||
redirect: '/system/model',
|
||||
alwaysShow: true,
|
||||
meta: { title: '系统设置', icon: 'system' },
|
||||
meta: { title: '平台设置', icon: 'system', roles: operationsConsoleRoles },
|
||||
children: [
|
||||
{
|
||||
path: 'model',
|
||||
@@ -204,6 +243,7 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
path: 'prompts',
|
||||
component: () => import('@/views/system/prompts.vue'),
|
||||
name: 'SystemPromptTemplates',
|
||||
hidden: true,
|
||||
meta: { title: '提示词模板', icon: 'edit' }
|
||||
},
|
||||
{
|
||||
@@ -211,26 +251,21 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
component: () => import('@/views/system/web-search.vue'),
|
||||
name: 'SystemWebSearch',
|
||||
meta: { title: '全网AI配置', icon: 'search' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/admin',
|
||||
component: Layout,
|
||||
redirect: '/admin/org-permission',
|
||||
children: [
|
||||
},
|
||||
{
|
||||
path: 'org-permission',
|
||||
component: () => import('@/views/admin/org-permission.vue'),
|
||||
name: 'OrgPermission',
|
||||
meta: { title: '组织与权限', icon: 'tree' }
|
||||
path: 'tenant',
|
||||
component: () => import('@/views/system/tenant/index.vue'),
|
||||
name: 'SystemTenantGovernance',
|
||||
meta: { title: '租户管理', icon: 'peoples', roles: tenantGovernanceRoles }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/competency',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
redirect: '/competency/growth',
|
||||
meta: { roles: operationsConsoleRoles },
|
||||
children: [
|
||||
{
|
||||
path: 'growth',
|
||||
|
||||
Reference in New Issue
Block a user