diff --git a/urbanLifelineServ/.bin/database/postgres/sql/admin_routes_architecture.md b/urbanLifelineServ/.bin/database/postgres/sql/admin_routes_architecture.md new file mode 100644 index 00000000..77895c6e --- /dev/null +++ b/urbanLifelineServ/.bin/database/postgres/sql/admin_routes_architecture.md @@ -0,0 +1,229 @@ +# 管理后台路由架构说明 + +## 三层架构设计 + +### 第一层:外层主 Sidebar (platform 服务) +显示在平台主界面的侧边栏,包含用户应用和管理后台入口。 + +**布局**: `SidebarLayout` +**服务**: `platform` + +#### 用户应用入口 +- 泰豪AI助手 (`/aichat`) +- 全部应用 (`/agents`) +- 智能体编排 (`/app/workflow`) - iframe +- 招标助手 (`/app/bidding`) - iframe +- 泰豪小电 (`/app/workcase`) - iframe + +#### 管理后台入口(iframe类型) +1. **平台管理后台** (`/admin/platform`) + - iframe_url: `/platform/admin` + - 权限: `perm_platform_admin` + +2. **智能标书管理后台** (`/admin/bidding`) + - iframe_url: `/bidding/admin` + - 权限: `perm_bidding_admin` + +3. **泰豪小电管理后台** (`/admin/workcase`) + - iframe_url: `/workcase/admin` + - 权限: `perm_workcase_admin` + +--- + +### 第二层:AdminSidebarLayout (各服务内部) +每个管理后台的内部二级菜单,使用 `AdminSidebarLayout` 布局。 + +**布局**: `AdminSidebarLayout` +**类型**: `route`(非 iframe) + +--- + +## Platform 管理后台(第二层) + +**服务**: `platform` +**布局**: `AdminSidebarLayout` + +### 视图列表 + +| 序号 | 视图名称 | URL | 组件路径 | 权限 | +|------|---------|-----|----------|------| +| 1 | 数据概览 | `/admin/overview` | `admin/overview/OverviewView.vue` | `perm_platform_admin_overview` | +| 2 | 用户管理 | `/admin/user` | `admin/user/UserView.vue` | `perm_platform_admin_user` | +| 3 | 知识库 | `/admin/knowledge` | `admin/knowledge/KnowledgeView.vue` | `perm_platform_admin_knowledge` | +| 4 | 系统配置 | `/admin/config` | `admin/config/ConfigView.vue` | `perm_platform_admin_config` | + +### 权限配置 +```sql +-- 管理后台入口权限 +PERM-0601: perm_platform_admin (platform:admin:view) + +-- 内部功能权限 +PERM-0602: perm_platform_admin_overview (platform:admin:overview) +PERM-0603: perm_platform_admin_user (platform:admin:user) +PERM-0604: perm_platform_admin_knowledge (platform:admin:knowledge) +PERM-0605: perm_platform_admin_config (platform:admin:config) +``` + +--- + +## Bidding 管理后台(第二层) + +**服务**: `bidding` +**布局**: `AdminSidebarLayout` + +### 权限配置 +```sql +-- 管理后台入口权限 +PERM-0611: perm_bidding_admin (bidding:admin:view) +``` + +*注: bidding 管理后台的具体视图需要后续配置* + +--- + +## Workcase 管理后台(第二层) + +**服务**: `workcase` +**布局**: `AdminSidebarLayout` + +### 视图列表 + +| 序号 | 视图名称 | URL | 组件路径 | 权限 | +|------|---------|-----|----------|------| +| 1 | 数据概览 | `/admin/overview` | `admin/overview/OverviewView.vue` | `perm_workcase_overview` | +| 2 | 知识库管理 | `/admin/knowledge` | `admin/knowledge/KnowLedgeView.vue` | `perm_workcase_knowledge` | +| 3 | 工单管理 | `/admin/workcase` | `admin/workcase/WorkcaseView.vue` | `perm_workcase_tickets` | +| 4 | 对话数据 | `/admin/customerChat` | `admin/customerChat/CustomerChatView.vue` | `perm_workcase_conversation` | +| 5 | 智能体管理 | `/admin/agent` | `admin/agent/AgentView.vue` | `perm_workcase_agent` | +| 6 | 日志管理 | `/admin/log` | *(目录)* | `perm_workcase_log` | +| 6.1 | └ 知识库日志 | `/admin/log/knowledge` | `admin/log/knowledgeLog/KnowledgeLogView.vue` | `perm_workcase_log` | +| 6.2 | └ 工单日志 | `/admin/log/workcase` | `admin/log/workcaseLog/WorkcaseLogView.vue` | `perm_workcase_log` | +| 6.3 | └ 系统日志 | `/admin/log/system` | `admin/log/systemLog/SystemLogView.vue` | `perm_workcase_log` | + +### 权限配置 +```sql +-- 管理后台入口权限 +PERM-0621: perm_workcase_admin (workcase:admin:view) + +-- 内部功能权限 +PERM-0622: perm_workcase_overview (workcase:overview:view) +PERM-0623: perm_workcase_knowledge (workcase:knowledge:view) +PERM-0624: perm_workcase_tickets (workcase:tickets:view) +PERM-0625: perm_workcase_conversation (workcase:conversation:view) +PERM-0626: perm_workcase_agent (workcase:agent:view) +PERM-0627: perm_workcase_log (workcase:log:view) +``` + +--- + +## 路由过滤规则 + +### 外层主 Sidebar (platform/SidebarLayout) +显示所有 `service='platform'` 且 `layout='SidebarLayout'` 的视图: +- 用户应用入口(普通路由和iframe) +- 管理后台入口(iframe类型) + +### 内层 AdminSidebarLayout +各服务内部,显示 `layout='AdminSidebarLayout'` 且 `url.startsWith('/admin')` 的视图: + +#### Platform AdminSidebar +```typescript +service === 'platform' && +layout === 'AdminSidebarLayout' && +url.startsWith('/admin') +``` + +#### Bidding AdminSidebar +```typescript +service === 'bidding' && +layout === 'AdminSidebarLayout' && +url.startsWith('/admin') +``` + +#### Workcase AdminSidebar +```typescript +service === 'workcase' && +layout === 'AdminSidebarLayout' && +url.startsWith('/admin') +``` + +--- + +## 数据库表关系 + +### 视图类型说明 +| type | view_type | 说明 | 示例 | +|------|-----------|------|------| +| 1 | route | 普通路由视图 | 数据概览、用户管理 | +| 1 | iframe | iframe嵌入视图 | 管理后台入口、Dify编排 | +| 0 | route | 目录(不可点击) | 日志管理 | +| 3 | route | 默认首页(隐藏) | 智能客服首页 | + +### 视图权限关联 +每个视图通过 `tb_sys_view_permission` 表关联到对应权限: +- 一个视图可以关联多个权限(AND 逻辑) +- 用户需要拥有所有关联权限才能访问该视图 + +### 角色权限继承 +- **超级管理员**: 自动拥有所有权限 +- **系统管理员**: 拥有所有 module 的管理权限(除删除外) +- **普通用户**: 基础查看权限 + 平台基础菜单访问 +- **访客**: 仅查看权限 + 部分平台菜单访问 + +--- + +## 前端实现注意事项 + +### 1. Layout 组件 +- **SidebarLayout**: 外层主侧边栏(platform 主界面) +- **AdminSidebarLayout**: 内层管理侧边栏(各管理后台内部) +- **BlankLayout**: 空白布局(如智能客服首页) + +### 2. iframe 嵌套 +- 外层 platform 的 iframe 菜单 → 加载各服务的管理后台 +- 各服务内部使用 AdminSidebarLayout 渲染二级菜单 +- iframe_url 必须正确指向实际服务地址 + +### 3. 路由配置 +所有 `/admin/*` 路径统一用于管理后台: +- platform: `/admin/overview`, `/admin/user` 等 +- bidding: `/admin/*` (待定义) +- workcase: `/admin/overview`, `/admin/knowledge` 等 + +### 4. 权限验证 +前端需要根据 `loginDomain.userViews` 动态渲染菜单: +- 检查 `layout` 字段匹配当前布局 +- 检查 `service` 字段匹配当前服务 +- 检查用户是否拥有关联的权限 + +--- + +## 迁移说明 + +如果从旧的单层架构迁移到三层架构: + +1. **视图迁移** + - 将原 platform 的管理视图改为 `layout='AdminSidebarLayout'` + - URL 统一改为 `/admin/*` 格式 + - 在 platform 主侧边栏添加 iframe 入口 + +2. **权限迁移** + - 保留原有权限定义 + - 新增管理后台入口权限(如 `perm_platform_admin`) + - 更新视图权限关联 + +3. **前端组件** + - 创建 AdminSidebarLayout 组件(参考 workcase 实现) + - 更新 platform 路由配置支持 `/admin/*` 路径 + - 配置 iframe 路由指向各服务管理后台 + +--- + +## 总结 + +这个三层架构设计实现了: +- ✅ **统一入口**: 所有管理后台统一在 platform 主侧边栏 +- ✅ **独立管理**: 各服务管理后台相互独立,便于维护 +- ✅ **权限细分**: 入口权限 + 功能权限双重控制 +- ✅ **灵活扩展**: 新增服务只需添加 iframe 入口和内部视图 +- ✅ **用户体验**: 统一的导航风格,清晰的层级结构 diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTablePermission.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTablePermission.sql index 134b5dfd..dcf5082a 100644 --- a/urbanLifelineServ/.bin/database/postgres/sql/createTablePermission.sql +++ b/urbanLifelineServ/.bin/database/postgres/sql/createTablePermission.sql @@ -158,7 +158,7 @@ CREATE TABLE sys.tb_sys_view ( url VARCHAR(255) DEFAULT NULL, -- 视图URL component VARCHAR(255) DEFAULT NULL, -- 视图组件 icon VARCHAR(100) DEFAULT NULL, -- 视图图标 - type INTEGER DEFAULT 0, -- 视图类型:0=目录 1=菜单页面 2=按钮 + type INTEGER DEFAULT 0, -- 视图类型:0=导航栏 1=侧边栏 2=按钮,3空白页 view_type VARCHAR(20) DEFAULT 'route', -- 页面类型:route=路由页面 iframe=嵌入页面 iframe_url VARCHAR(500) DEFAULT NULL, -- iframe URL(仅当view_type=iframe时有效) service VARCHAR(20) DEFAULT 'platform', -- 所属服务:platform=平台应用 bidding=招标应用 workcase=客服应用 diff --git a/urbanLifelineServ/.bin/database/postgres/sql/initDataPermission.sql b/urbanLifelineServ/.bin/database/postgres/sql/initDataPermission.sql index 5d7ea90e..bf545953 100644 --- a/urbanLifelineServ/.bin/database/postgres/sql/initDataPermission.sql +++ b/urbanLifelineServ/.bin/database/postgres/sql/initDataPermission.sql @@ -156,6 +156,38 @@ INSERT INTO sys.tb_sys_permission ( ('PERM-0504', 'perm_platform_workcase', '泰豪小电访问', 'platform:workcase:view', '访问泰豪小电客服(iframe)', 'module_workcase', true, 'system', NULL, now(), false), ('PERM-0505', 'perm_platform_workflow', '智能体编排访问', 'platform:workflow:view', '访问智能体编排(iframe)', 'module_system', + true, 'system', NULL, now(), false), + +-- Platform 管理后台功能权限 +('PERM-0601', 'perm_platform_admin', '平台管理后台', 'platform:admin:view', '访问平台管理后台', 'module_system', + true, 'system', NULL, now(), false), +('PERM-0602', 'perm_platform_admin_overview', '平台数据概览', 'platform:admin:overview', '访问平台数据概览', 'module_system', + true, 'system', NULL, now(), false), +('PERM-0603', 'perm_platform_admin_user', '平台用户管理', 'platform:admin:user', '访问平台用户管理', 'module_system', + true, 'system', NULL, now(), false), +('PERM-0604', 'perm_platform_admin_knowledge', '平台知识库', 'platform:admin:knowledge', '访问平台知识库', 'module_knowledge', + true, 'system', NULL, now(), false), +('PERM-0605', 'perm_platform_admin_config', '平台系统配置', 'platform:admin:config', '访问平台系统配置', 'module_config', + true, 'system', NULL, now(), false), + +-- Bidding 管理后台功能权限 +('PERM-0611', 'perm_bidding_admin', '招标管理后台', 'bidding:admin:view', '访问招标管理后台', 'module_bidding', + true, 'system', NULL, now(), false), + +-- Workcase 管理后台功能权限 +('PERM-0621', 'perm_workcase_admin', '客服管理后台', 'workcase:admin:view', '访问客服管理后台', 'module_workcase', + true, 'system', NULL, now(), false), +('PERM-0622', 'perm_workcase_overview', '数据概览', 'workcase:overview:view', '访问泰豪小电数据概览', 'module_workcase', + true, 'system', NULL, now(), false), +('PERM-0623', 'perm_workcase_knowledge', '知识库管理', 'workcase:knowledge:view', '访问知识库管理', 'module_workcase', + true, 'system', NULL, now(), false), +('PERM-0624', 'perm_workcase_tickets', '工单管理', 'workcase:tickets:view', '访问工单管理', 'module_workcase', + true, 'system', NULL, now(), false), +('PERM-0625', 'perm_workcase_conversation', '对话数据', 'workcase:conversation:view', '访问对话数据管理', 'module_workcase', + true, 'system', NULL, now(), false), +('PERM-0626', 'perm_workcase_agent', '智能体管理', 'workcase:agent:view', '访问智能体管理', 'module_workcase', + true, 'system', NULL, now(), false), +('PERM-0627', 'perm_workcase_log', '日志管理', 'workcase:log:view', '访问日志管理', 'module_workcase', true, 'system', NULL, now(), false); -- ============================= @@ -184,6 +216,24 @@ INSERT INTO sys.tb_sys_view ( ('VIEW-P004', 'view_platform_workcase', '泰豪小电', NULL, '/app/workcase', NULL, 'Service', 1, 'iframe', '/workcase/', 'platform', 'SidebarLayout', 50, '客服应用(iframe)', 'system', now(), false), +-- 管理后台入口(iframe类型,显示在外层主sidebar) +('VIEW-P101', 'view_platform_admin_entry', '平台管理后台', NULL, '/admin/platform', NULL, 'Setting', 1, + 'iframe', '/platform/admin', 'platform', 'AdminIframeSidebarLayout', 100, '平台管理后台入口', 'system', now(), false), +('VIEW-P102', 'view_bidding_admin_entry', '智能标书管理后台', NULL, '/admin/bidding', NULL, 'Document', 1, + 'iframe', '/bidding/admin', 'platform', 'AdminIframeSidebarLayout', 110, '智能标书管理后台入口', 'system', now(), false), +('VIEW-P103', 'view_workcase_admin_entry', '泰豪小电管理后台', NULL, '/admin/workcase', NULL, 'Service', 1, + 'iframe', '/workcase/admin', 'platform', 'AdminIframeSidebarLayout', 120, '泰豪小电管理后台入口', 'system', now(), false), + +-- 平台管理后台内部视图(AdminSidebarLayout布局,在platform服务内) +('VIEW-P201', 'view_platform_admin_overview', '数据概览', NULL, '/admin/overview', 'admin/overview/OverviewView.vue', 'DataLine', 1, + 'route', NULL, 'platform', 'AdminSidebarLayout', 210, '平台数据概览', 'system', now(), false), +('VIEW-P202', 'view_platform_admin_user', '用户管理', NULL, '/admin/user', 'admin/user/UserView.vue', 'User', 1, + 'route', NULL, 'platform', 'AdminSidebarLayout', 220, '平台用户管理', 'system', now(), false), +('VIEW-P203', 'view_platform_admin_knowledge', '知识库', NULL, '/admin/knowledge', 'admin/knowledge/KnowledgeView.vue', 'Document', 1, + 'route', NULL, 'platform', 'AdminSidebarLayout', 230, '平台知识库管理', 'system', now(), false), +('VIEW-P204', 'view_platform_admin_config', '系统配置', NULL, '/admin/config', 'admin/config/ConfigView.vue', 'Setting', 1, + 'route', NULL, 'platform', 'AdminSidebarLayout', 240, '平台系统配置', 'system', now(), false), + -- -- 系统管理目录 -- ('VIEW-P100', 'view_system', '系统管理', NULL, '/system', NULL, 'Settings', 0, -- 'route', NULL, 'platform', 'SidebarLayout', 100, '系统管理目录', 'system', now(), false), @@ -228,14 +278,38 @@ INSERT INTO sys.tb_sys_view ( -- ========================= -- 客服应用菜单 (workcase) -- ========================= -('VIEW-W001', 'view_workcase_home', '智能客服', NULL, '/', 'public/AIChat/AIChatView.vue', 'House', 3, +-- 用户端视图 +('VIEW-W001', 'view_workcase_home', '智能客服', NULL, '/aichat', 'public/AIChat/AIChatView.vue', 'House', 3, 'route', NULL, 'workcase', 'BlankLayout', 10, '智能客服首页', 'system', now(), false), -('VIEW-W002', 'view_workcase_list', '工单列表', NULL, '/list', 'workcase/List', 'Tickets', 1, - 'route', NULL, 'workcase', 'SidebarLayout', 20, '工单列表页面', 'system', now(), false), +-- 管理端视图(使用 AdminSidebarLayout 布局) +('VIEW-W101', 'view_workcase_admin_overview', '数据概览', NULL, '/admin/overview', 'admin/overview/OverviewView.vue', 'DataLine', 1, + 'route', NULL, 'workcase', 'AdminSidebarLayout', 110, '泰豪小电数据概览', 'system', now(), false), -('VIEW-W003', 'view_workcase_detail', '工单详情', NULL, '/detail', 'workcase/Detail', 'Document', 1, - 'route', NULL, 'workcase', 'SidebarLayout', 30, '工单详情页面', 'system', now(), false); +('VIEW-W102', 'view_workcase_admin_knowledge', '知识库管理', NULL, '/admin/knowledge', 'admin/knowledge/KnowLedgeView.vue', 'Document', 1, + 'route', NULL, 'workcase', 'AdminSidebarLayout', 120, '知识库文档管理', 'system', now(), false), + +('VIEW-W103', 'view_workcase_admin_tickets', '工单管理', NULL, '/admin/workcase', 'admin/workcase/WorkcaseView.vue', 'Tickets', 1, + 'route', NULL, 'workcase', 'AdminSidebarLayout', 130, '客服工单管理', 'system', now(), false), + +('VIEW-W104', 'view_workcase_admin_conversation', '对话数据', NULL, '/admin/customerChat', 'admin/customerChat/CustomerChatView.vue', 'ChatDotRound', 1, + 'route', NULL, 'workcase', 'AdminSidebarLayout', 140, '客户对话数据管理', 'system', now(), false), + +('VIEW-W105', 'view_workcase_admin_agent', '智能体管理', NULL, '/admin/agent', 'admin/agent/AgentView.vue', 'Service', 1, + 'route', NULL, 'workcase', 'AdminSidebarLayout', 150, '智能体配置管理', 'system', now(), false), + +-- 日志管理(带子级的目录) +('VIEW-W106', 'view_workcase_admin_log', '日志管理', NULL, '/admin/log', NULL, 'List', 0, + 'route', NULL, 'workcase', 'AdminSidebarLayout', 160, '日志管理目录', 'system', now(), false), + +('VIEW-W107', 'view_workcase_admin_log_knowledge', '知识库日志', 'view_workcase_admin_log', '/admin/log/knowledge', 'admin/log/knowledgeLog/KnowledgeLogView.vue', 'Document', 1, + 'route', NULL, 'workcase', 'AdminSidebarLayout', 161, '知识库操作日志', 'system', now(), false), + +('VIEW-W108', 'view_workcase_admin_log_workcase', '工单日志', 'view_workcase_admin_log', '/admin/log/workcase', 'admin/log/workcaseLog/WorkcaseLogView.vue', 'Tickets', 1, + 'route', NULL, 'workcase', 'AdminSidebarLayout', 162, '工单操作日志', 'system', now(), false), + +('VIEW-W109', 'view_workcase_admin_log_system', '系统日志', 'view_workcase_admin_log', '/admin/log/system', 'admin/log/systemLog/SystemLogView.vue', 'Setting', 1, + 'route', NULL, 'workcase', 'AdminSidebarLayout', 163, '系统运行日志', 'system', now(), false); -- ============================= -- 6. 角色权限关联(超级管理员拥有所有权限) @@ -318,10 +392,34 @@ INSERT INTO sys.tb_sys_view_permission ( ('VP-P004', 'view_platform_workcase', 'perm_platform_workcase', 'system', NULL, now(), false), ('VP-P005', 'view_platform_workflow', 'perm_platform_workflow', 'system', NULL, now(), false), --- Workcase服务内部视图关联(使用同一个workcase访问权限) +-- 管理后台入口权限关联(iframe入口) +('VP-P101', 'view_platform_admin_entry', 'perm_platform_admin', 'system', NULL, now(), false), +('VP-P102', 'view_bidding_admin_entry', 'perm_bidding_admin', 'system', NULL, now(), false), +('VP-P103', 'view_workcase_admin_entry', 'perm_workcase_admin', 'system', NULL, now(), false), + +-- 平台管理后台内部视图权限关联(AdminSidebarLayout) +('VP-P201', 'view_platform_admin_overview', 'perm_platform_admin_overview', 'system', NULL, now(), false), +('VP-P202', 'view_platform_admin_user', 'perm_platform_admin_user', 'system', NULL, now(), false), +('VP-P203', 'view_platform_admin_knowledge', 'perm_platform_admin_knowledge', 'system', NULL, now(), false), +('VP-P204', 'view_platform_admin_config', 'perm_platform_admin_config', 'system', NULL, now(), false), + +-- Workcase服务用户端视图关联(使用同一个workcase访问权限) ('VP-W001', 'view_workcase_home', 'perm_platform_workcase', 'system', NULL, now(), false), ('VP-W002', 'view_workcase_list', 'perm_platform_workcase', 'system', NULL, now(), false), -('VP-W003', 'view_workcase_detail', 'perm_platform_workcase', 'system', NULL, now(), false); +('VP-W003', 'view_workcase_detail', 'perm_platform_workcase', 'system', NULL, now(), false), + +-- Workcase服务管理端视图关联 +('VP-W101', 'view_workcase_admin_overview', 'perm_workcase_overview', 'system', NULL, now(), false), +('VP-W102', 'view_workcase_admin_knowledge', 'perm_workcase_knowledge', 'system', NULL, now(), false), +('VP-W103', 'view_workcase_admin_tickets', 'perm_workcase_tickets', 'system', NULL, now(), false), +('VP-W104', 'view_workcase_admin_conversation', 'perm_workcase_conversation', 'system', NULL, now(), false), +('VP-W105', 'view_workcase_admin_agent', 'perm_workcase_agent', 'system', NULL, now(), false), + +-- 日志管理视图关联(包括父级和子级) +('VP-W106', 'view_workcase_admin_log', 'perm_workcase_log', 'system', NULL, now(), false), +('VP-W107', 'view_workcase_admin_log_knowledge', 'perm_workcase_log', 'system', NULL, now(), false), +('VP-W108', 'view_workcase_admin_log_workcase', 'perm_workcase_log', 'system', NULL, now(), false), +('VP-W109', 'view_workcase_admin_log_system', 'perm_workcase_log', 'system', NULL, now(), false); -- -- 用户管理视图关联用户权限(已注释,因为view_user被注释掉了) -- -- ('VP-0001', 'view_user', 'perm_user_view', 'system', NULL, now(), false), diff --git a/urbanLifelineServ/.bin/database/postgres/sql/workcase_admin_routes_summary.md b/urbanLifelineServ/.bin/database/postgres/sql/workcase_admin_routes_summary.md new file mode 100644 index 00000000..129959ce --- /dev/null +++ b/urbanLifelineServ/.bin/database/postgres/sql/workcase_admin_routes_summary.md @@ -0,0 +1,130 @@ +# Workcase 管理端路由配置总结 + +## 已配置的管理端视图 + +### 1. 数据概览 (Overview) +- **view_id**: `view_workcase_admin_overview` +- **URL**: `/admin/overview` +- **组件**: `admin/overview/OverviewView.vue` +- **图标**: DataLine +- **权限**: `perm_workcase_overview` (workcase:overview:view) +- **描述**: 泰豪小电数据概览 + +### 2. 知识库管理 (Knowledge) +- **view_id**: `view_workcase_admin_knowledge` +- **URL**: `/admin/knowledge` +- **组件**: `admin/knowledge/KnowLedgeView.vue` +- **图标**: Document +- **权限**: `perm_workcase_knowledge` (workcase:knowledge:view) +- **描述**: 知识库文档管理 + +### 3. 工单管理 (Tickets) +- **view_id**: `view_workcase_admin_tickets` +- **URL**: `/admin/workcase` +- **组件**: `admin/workcase/WorkcaseView.vue` +- **图标**: Tickets +- **权限**: `perm_workcase_tickets` (workcase:tickets:view) +- **描述**: 客服工单管理 + +### 4. 对话数据 (Conversation) +- **view_id**: `view_workcase_admin_conversation` +- **URL**: `/admin/customerChat` +- **组件**: `admin/customerChat/CustomerChatView.vue` +- **图标**: ChatDotRound +- **权限**: `perm_workcase_conversation` (workcase:conversation:view) +- **描述**: 客户对话数据管理 + +### 5. 智能体管理 (Agent) +- **view_id**: `view_workcase_admin_agent` +- **URL**: `/admin/agent` +- **组件**: `admin/agent/AgentView.vue` +- **图标**: Service +- **权限**: `perm_workcase_agent` (workcase:agent:view) +- **描述**: 智能体配置管理 + +### 6. 日志管理 (Logs) +#### 6.1 日志管理目录 +- **view_id**: `view_workcase_admin_log` +- **URL**: `/admin/log` +- **组件**: NULL (目录类型) +- **图标**: List +- **type**: 0 (目录) +- **权限**: `perm_workcase_log` (workcase:log:view) +- **描述**: 日志管理目录 + +#### 6.2 知识库日志 +- **view_id**: `view_workcase_admin_log_knowledge` +- **parent_id**: `view_workcase_admin_log` +- **URL**: `/admin/log/knowledge` +- **组件**: `admin/log/knowledgeLog/KnowledgeLogView.vue` +- **图标**: Document +- **权限**: `perm_workcase_log` (workcase:log:view) +- **描述**: 知识库操作日志 + +#### 6.3 工单日志 +- **view_id**: `view_workcase_admin_log_workcase` +- **parent_id**: `view_workcase_admin_log` +- **URL**: `/admin/log/workcase` +- **组件**: `admin/log/workcaseLog/WorkcaseLogView.vue` +- **图标**: Tickets +- **权限**: `perm_workcase_log` (workcase:log:view) +- **描述**: 工单操作日志 + +#### 6.4 系统日志 +- **view_id**: `view_workcase_admin_log_system` +- **parent_id**: `view_workcase_admin_log` +- **URL**: `/admin/log/system` +- **组件**: `admin/log/systemLog/SystemLogView.vue` +- **图标**: Setting +- **权限**: `perm_workcase_log` (workcase:log:view) +- **描述**: 系统运行日志 + +## 权限配置 + +### 新增权限 +1. **PERM-0601**: `perm_workcase_overview` - 数据概览 +2. **PERM-0602**: `perm_workcase_knowledge` - 知识库管理 +3. **PERM-0603**: `perm_workcase_tickets` - 工单管理 +4. **PERM-0604**: `perm_workcase_conversation` - 对话数据 +5. **PERM-0605**: `perm_workcase_agent` - 智能体管理 +6. **PERM-0606**: `perm_workcase_log` - 日志管理 + +### 视图权限关联 +- VP-W101 ~ VP-W105: 管理端主功能视图 +- VP-W106 ~ VP-W109: 日志管理视图(含父级和3个子级) + +## 布局配置 +- **Layout**: `AdminSidebarLayout` +- **Service**: `workcase` +- **Type**: 1 (菜单项) / 0 (目录) +- **View Type**: `route` + +## Order Number 排序 +- 110: 数据概览 +- 120: 知识库管理 +- 130: 工单管理 +- 140: 对话数据 +- 150: 智能体管理 +- 160: 日志管理(父级) + - 161: 知识库日志 + - 162: 工单日志 + - 163: 系统日志 + +## 路由过滤规则 +在 `AdminSidebarLayout.vue` 的 `loadMenuFromStorage()` 中: +```typescript +const sidebarViews = userViews.filter((view: any) => + view.layout === 'SidebarLayout' && // 使用 SidebarLayout 布局 + !view.parentId && // 顶级菜单 + view.type === 1 && // 菜单类型 + view.service === 'workcase' && // workcase 服务 + view.url?.startsWith('/admin') // admin 路由 +) +``` + +## 注意事项 +1. 所有管理端路由都以 `/admin/` 开头 +2. 日志管理使用父子级结构(type=0 的目录 + type=1 的子菜单) +3. 所有视图都使用 `AdminSidebarLayout` 布局 +4. 权限都归属于 `module_workcase` 模块 +5. 超级管理员和系统管理员默认拥有所有 workcase 管理端权限 diff --git a/urbanLifelineWeb/.gitignore b/urbanLifelineWeb/.gitignore index e3bd013f..61651879 100644 --- a/urbanLifelineWeb/.gitignore +++ b/urbanLifelineWeb/.gitignore @@ -42,6 +42,10 @@ pnpm-debug.log* .temp/ .turbo/ +# Module Federation +**/__mf__temp/ +**/.__mf__temp/ + # Testing coverage/ **/coverage/ diff --git a/urbanLifelineWeb/packages/bidding/src/types/shared.d.ts b/urbanLifelineWeb/packages/bidding/src/types/shared.d.ts index f352e00e..f7e18a36 100644 --- a/urbanLifelineWeb/packages/bidding/src/types/shared.d.ts +++ b/urbanLifelineWeb/packages/bidding/src/types/shared.d.ts @@ -181,4 +181,5 @@ declare module 'shared/layouts' { import { DefineComponent } from 'vue' export const BlankLayout: DefineComponent<{}, {}, any> + export const AdminSidebarLayout: DefineComponent<{}, {}, any> } diff --git a/urbanLifelineWeb/packages/platform/src/layouts/AdminIframeSidebarLayout/AdminIframeSidebarLayout.scss b/urbanLifelineWeb/packages/platform/src/layouts/AdminIframeSidebarLayout/AdminIframeSidebarLayout.scss new file mode 100644 index 00000000..c3479624 --- /dev/null +++ b/urbanLifelineWeb/packages/platform/src/layouts/AdminIframeSidebarLayout/AdminIframeSidebarLayout.scss @@ -0,0 +1,291 @@ +.sidebar-layout { + display: flex; + width: 100%; + height: 100vh; + overflow: hidden; +} + +// ==================== 侧边栏 ==================== +.sidebar { + width: 220px; + height: 100%; + background: #F0EAF4; + display: flex; + flex-direction: column; + color: #333; + flex-shrink: 0; + transition: width 0.3s ease; + border-right: 1px solid rgba(0, 0, 0, 0.08); + + &.collapsed { + width: 64px; + + .sidebar-header { + padding: 16px 12px; + justify-content: center; + + .logo { + justify-content: center; + } + + .collapse-btn { + position: static; + margin-left: 0; + } + } + + .nav-item { + justify-content: center; + padding: 12px; + } + + .user-section { + justify-content: center; + padding: 16px 12px; + } + } +} + +// 侧边栏头部 +.sidebar-header { + padding: 16px 20px; + border-bottom: 1px solid rgba(0, 0, 0, 0.08); + display: flex; + align-items: center; + justify-content: space-between; +} + +.collapse-btn { + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 6px; + cursor: pointer; + color: #888; + transition: all 0.2s; + + &:hover { + background: rgba(124, 58, 237, 0.1); + color: #7c3aed; + } +} + +.logo { + display: flex; + align-items: center; + gap: 10px; + + .logo-img { + width: 40px; + height: 40px; + border-radius: 6px; + object-fit: contain; + background: #fff; + padding: 2px; + } + + .logo-text { + font-size: 16px; + font-weight: 600; + color: #333; + } +} + +// 导航菜单 +.nav-menu { + flex: 1; + overflow-y: auto; + padding: 12px 0; + + &::-webkit-scrollbar { + width: 4px; + } + + &::-webkit-scrollbar-thumb { + background: rgba(0, 0, 0, 0.2); + border-radius: 4px; + } +} + +.nav-section { + padding: 8px 0; +} + +.nav-item { + display: flex; + align-items: center; + gap: 12px; + padding: 14px 20px; + margin-bottom: 4px; + cursor: pointer; + transition: all 0.2s ease; + color: #555; + font-size: 14px; + + &:hover { + background: rgba(124, 58, 237, 0.1); + color: #7c3aed; + } + + &.active { + background: rgba(124, 58, 237, 0.15); + color: #7c3aed; + font-weight: 500; + } + + .el-icon { + font-size: 18px; + flex-shrink: 0; + } + + span { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } +} + +// 用户信息 +.user-section { + padding: 16px; + border-top: 1px solid rgba(0, 0, 0, 0.08); + display: flex; + align-items: center; + gap: 12px; + + .user-avatar { + width: 36px; + height: 36px; + cursor: pointer; + transition: transform 0.2s; + flex-shrink: 0; + + &:hover { + transform: scale(1.1); + } + + :deep(.el-avatar) { + width: 100%; + height: 100%; + } + } + + .user-name { + flex: 1; + font-size: 14px; + font-weight: 500; + color: #303133; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .back-icon { + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 6px; + cursor: pointer; + color: #606266; + transition: all 0.2s; + flex-shrink: 0; + + &:hover { + color: #7c3aed; + background: rgba(124, 58, 237, 0.1); + } + + .el-icon { + font-size: 18px; + } + } +} + +// ==================== 主内容区 ==================== +.main-content { + flex: 1; + height: 100%; + overflow: hidden; + background: #fff; + position: relative; +} + +// iframe 容器 +.iframe-container { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + position: relative; +} + +.iframe-header { + height: 56px; + padding: 0 24px; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid #e5e7eb; + background: #fafafa; + flex-shrink: 0; +} + +.iframe-title { + font-size: 16px; + font-weight: 600; + color: #333; +} + +.content-iframe { + flex: 1; + width: 100%; + height: 100%; + border: none; + background: #fff; +} + +.iframe-loading { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + color: #7c3aed; + font-size: 14px; + z-index: 10; + + .el-icon { + font-size: 32px; + } +} + +// ==================== 响应式 ==================== +@media (max-width: 768px) { + .sidebar { + width: 64px; + + &:not(.collapsed) { + width: 220px; + position: fixed; + left: 0; + top: 0; + z-index: 1000; + box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1); + } + } + + .iframe-header { + padding: 0 16px; + + .iframe-title { + font-size: 14px; + } + } +} diff --git a/urbanLifelineWeb/packages/platform/src/layouts/AdminSidebarLayout/AdminSidebarLayout.vue b/urbanLifelineWeb/packages/platform/src/layouts/AdminIframeSidebarLayout/AdminIframeSidebarLayout.vue similarity index 95% rename from urbanLifelineWeb/packages/platform/src/layouts/AdminSidebarLayout/AdminSidebarLayout.vue rename to urbanLifelineWeb/packages/platform/src/layouts/AdminIframeSidebarLayout/AdminIframeSidebarLayout.vue index 24209620..d4ea4c55 100644 --- a/urbanLifelineWeb/packages/platform/src/layouts/AdminSidebarLayout/AdminSidebarLayout.vue +++ b/urbanLifelineWeb/packages/platform/src/layouts/AdminIframeSidebarLayout/AdminIframeSidebarLayout.vue @@ -143,13 +143,12 @@ function loadMenuFromStorage(): MenuItem[] { const userViews = loginDomain.userViews || [] - // 过滤出 SidebarLayout 的顶级菜单(没有 parentId,且属于 platform 服务,且不是admin路由) + // 过滤出 AdminIframeSidebarLayout 的菜单(3个 iframe 管理后台入口) const sidebarViews = userViews.filter((view: any) => - view.layout === 'SidebarLayout' && - !view.parentId && + view.layout === 'AdminIframeSidebarLayout' && // AdminIframeSidebarLayout 布局 + view.viewType === 'iframe' && // iframe 类型 view.type === 1 && // type 1 是侧边栏菜单 - view.service === 'platform' && // 只显示 platform 服务的视图 - view.url?.startsWith('/admin') // 只留admin 路由(由 AdminSidebar 管理) + view.service === 'platform' // platform 服务 ) // 按 orderNum 排序 @@ -288,5 +287,5 @@ watch( \ No newline at end of file diff --git a/urbanLifelineWeb/packages/platform/src/layouts/AdminSidebarLayout/README.md b/urbanLifelineWeb/packages/platform/src/layouts/AdminIframeSidebarLayout/README.md similarity index 100% rename from urbanLifelineWeb/packages/platform/src/layouts/AdminSidebarLayout/README.md rename to urbanLifelineWeb/packages/platform/src/layouts/AdminIframeSidebarLayout/README.md diff --git a/urbanLifelineWeb/packages/platform/src/layouts/SidebarLayout/SidebarLayout.vue b/urbanLifelineWeb/packages/platform/src/layouts/SidebarLayout/SidebarLayout.vue index 0c149be4..1c6ebb00 100644 --- a/urbanLifelineWeb/packages/platform/src/layouts/SidebarLayout/SidebarLayout.vue +++ b/urbanLifelineWeb/packages/platform/src/layouts/SidebarLayout/SidebarLayout.vue @@ -165,7 +165,7 @@ function loadMenuFromStorage(): MenuItem[] { !view.url?.startsWith('/admin') // 排除 admin 路由(由 AdminSidebar 管理) ) hasAdmin.value = userViews.filter((view: any) => - view.layout === 'SidebarLayout' && + view.layout === 'AdminIframeSidebarLayout' && !view.parentId && view.type === 1 && // type 1 是侧边栏菜单 view.service === 'platform' && // 只显示 platform 服务的视图 @@ -266,7 +266,7 @@ const handleUserCommand = (command: string) => { const loginDomain = JSON.parse(loginDomainStr) const userViews = loginDomain.userViews || [] const adminViews = userViews.filter((view: any) => - view.service === 'platform' && view.url?.startsWith('/admin') + view.service === 'platform' && view.url?.startsWith('/admin') && view.viewType === 'iframe' ) if (adminViews.length > 0) { // 按 orderNum 排序,跳转到第一个 diff --git a/urbanLifelineWeb/packages/platform/src/layouts/index.ts b/urbanLifelineWeb/packages/platform/src/layouts/index.ts index 8ac1780c..56b811d1 100644 --- a/urbanLifelineWeb/packages/platform/src/layouts/index.ts +++ b/urbanLifelineWeb/packages/platform/src/layouts/index.ts @@ -1,3 +1,4 @@ export { default as SidebarLayout } from "./SidebarLayout/SidebarLayout.vue"; +export { default as AdminIframeSidebarLayout } from "./AdminIframeSidebarLayout/AdminIframeSidebarLayout.vue"; // BlankLayout从shared导入 -export { BlankLayout } from 'shared/layouts'; \ No newline at end of file +export { BlankLayout, AdminSidebarLayout } from 'shared/layouts'; \ No newline at end of file diff --git a/urbanLifelineWeb/packages/platform/src/router/dynamicRoute.ts b/urbanLifelineWeb/packages/platform/src/router/dynamicRoute.ts index 81201cfd..585ead6b 100644 --- a/urbanLifelineWeb/packages/platform/src/router/dynamicRoute.ts +++ b/urbanLifelineWeb/packages/platform/src/router/dynamicRoute.ts @@ -18,14 +18,15 @@ import { import type { TbSysViewDTO } from 'shared/types' import type { RouteRecordRaw } from 'vue-router' import router from './index' -import { SidebarLayout, BlankLayout } from '@/layouts' +import { SidebarLayout, BlankLayout, AdminIframeSidebarLayout } from '@/layouts' // Platform 布局组件映射 const platformLayoutMap: Record Promise> = { 'SidebarLayout': () => Promise.resolve({ default: SidebarLayout }), 'BlankLayout': () => Promise.resolve({ default: BlankLayout }), 'NavigationLayout': () => Promise.resolve({ default: SidebarLayout }), - 'BasicLayout': () => Promise.resolve({ default: SidebarLayout }) + 'BasicLayout': () => Promise.resolve({ default: SidebarLayout }), + 'AdminIframeSidebarLayout': () => Promise.resolve({ default: AdminIframeSidebarLayout }) } // 视图组件加载器 diff --git a/urbanLifelineWeb/packages/platform/src/router/index.ts b/urbanLifelineWeb/packages/platform/src/router/index.ts index 5d2bceb0..5d4a2136 100644 --- a/urbanLifelineWeb/packages/platform/src/router/index.ts +++ b/urbanLifelineWeb/packages/platform/src/router/index.ts @@ -54,9 +54,34 @@ router.beforeEach((to, from, next) => { dynamicRoutesLoaded = true const loaded = loadRoutesFromStorage() - if (loaded && to.path !== '/') { - // 动态路由已加载,重新导航到目标路由 - next({ ...to, replace: true }) + if (loaded) { + if (to.path === '/') { + // 访问根路径,重定向到第一个可用路由 + const firstRoute = getFirstAvailableRoute() + if (firstRoute && firstRoute !== '/') { + // 只有当第一个路由不是 / 时才重定向,避免无限循环 + console.log('[Platform Router] 根路径重定向到:', firstRoute) + next({ path: firstRoute, replace: true }) + return + } else { + // 第一个路由就是 /,直接放行 + console.log('[Platform Router] 第一个路由就是根路径,直接放行') + } + } else { + // 动态路由已加载,重新导航到目标路由 + next({ ...to, replace: true }) + return + } + } + } + + // 如果已登录且访问根路径,但动态路由已加载,重定向到第一个可用路由 + if (hasToken && to.path === '/' && dynamicRoutesLoaded) { + const firstRoute = getFirstAvailableRoute() + if (firstRoute && firstRoute !== '/') { + // 只有当第一个路由不是 / 时才重定向,避免无限循环 + console.log('[Platform Router] 已登录访问根路径,重定向到:', firstRoute) + next({ path: firstRoute, replace: true }) return } } @@ -64,6 +89,52 @@ router.beforeEach((to, from, next) => { next() }) +/** + * 获取第一个可用的路由路径 + */ +function getFirstAvailableRoute(): string | null { + try { + console.log('[Platform Router] 开始获取第一个可用路由...') + + const loginDomainStr = localStorage.getItem('loginDomain') + if (!loginDomainStr) { + console.warn('[Platform Router] localStorage 中没有 loginDomain') + return null + } + + const loginDomain = JSON.parse(loginDomainStr) + const userViews = loginDomain.userViews || [] + + console.log('[Platform Router] 所有用户视图:', userViews.length) + + // 过滤出 platform 服务的非 admin 视图 + // 注意:不限制 type,因为首页路由可能是 type=3(路由类型)而不是 type=1(菜单类型) + const platformViews = userViews.filter((view: any) => + view.service === 'platform' && + !view.url?.startsWith('/admin') && + view.url // 必须有 url 字段 + ) + + console.log('[Platform Router] Platform 服务视图:', platformViews) + + if (platformViews.length === 0) { + console.warn('[Platform Router] 没有找到 platform 服务的视图') + return null + } + + // 按 orderNum 排序 + platformViews.sort((a: any, b: any) => (a.orderNum || 0) - (b.orderNum || 0)) + + const firstRoute = platformViews[0].url + console.log('[Platform Router] 第一个路由:', firstRoute, '视图:', platformViews[0].name) + + return firstRoute + } catch (error) { + console.error('[Platform Router] 获取首页路由失败:', error) + return null + } +} + // 导出动态路由生成函数 export { addDynamicRoutes, loadRoutesFromStorage } from './dynamicRoute' diff --git a/urbanLifelineWeb/packages/platform/src/types/shared.d.ts b/urbanLifelineWeb/packages/platform/src/types/shared.d.ts index f352e00e..6b534714 100644 --- a/urbanLifelineWeb/packages/platform/src/types/shared.d.ts +++ b/urbanLifelineWeb/packages/platform/src/types/shared.d.ts @@ -181,4 +181,6 @@ declare module 'shared/layouts' { import { DefineComponent } from 'vue' export const BlankLayout: DefineComponent<{}, {}, any> + export const AdminSidebarLayout: DefineComponent<{}, {}, any> + } diff --git a/urbanLifelineWeb/packages/platform/src/views/admin/overview/OverviewView.scss b/urbanLifelineWeb/packages/platform/src/views/admin/overview/OverviewView.scss new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/urbanLifelineWeb/packages/platform/src/views/admin/overview/OverviewView.scss @@ -0,0 +1 @@ + diff --git a/urbanLifelineWeb/packages/platform/src/views/admin/overview/OverviewView.vue b/urbanLifelineWeb/packages/platform/src/views/admin/overview/OverviewView.vue new file mode 100644 index 00000000..4e7e9919 --- /dev/null +++ b/urbanLifelineWeb/packages/platform/src/views/admin/overview/OverviewView.vue @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/urbanLifelineWeb/packages/platform/src/views/admin/userManagement/UserManagementView.scss b/urbanLifelineWeb/packages/platform/src/views/admin/userManagement/UserManagementView.scss new file mode 100644 index 00000000..e69de29b diff --git a/urbanLifelineWeb/packages/platform/src/views/admin/userManagement/UserManagementView.vue b/urbanLifelineWeb/packages/platform/src/views/admin/userManagement/UserManagementView.vue new file mode 100644 index 00000000..1e4d725d --- /dev/null +++ b/urbanLifelineWeb/packages/platform/src/views/admin/userManagement/UserManagementView.vue @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/urbanLifelineWeb/packages/platform/src/views/common/IframeView.vue b/urbanLifelineWeb/packages/platform/src/views/common/IframeView.vue deleted file mode 100644 index 1e35c572..00000000 --- a/urbanLifelineWeb/packages/platform/src/views/common/IframeView.vue +++ /dev/null @@ -1,90 +0,0 @@ -