# 📋 sys_menu 菜单配置规范 > **版本**: v1.0 | **创建**: 2025-07-01 | **维护**: 系统管理员 ## 🚨 核心原则 ### ⭐ 重复节点检查规则(强制要求) **🔴 在新增任何菜单节点前,必须先检查是否存在同名节点** ```sql -- 新增菜单前的强制检查 SELECT menu_id, menu_name, parent_id, path, menu_type FROM sys_menu WHERE menu_name = '要新增的菜单名称' AND parent_id = 目标父级ID; -- 处理原则: -- 1. 存在同名同父级节点 → 直接使用现有节点,不新增 -- 2. 存在同名不同父级 → 评估是否需要合并或重命名 -- 3. 完全不存在 → 可以安全新增 ``` **应用场景**: - ✅ 存在"场地预约"目录 → 直接使用,不重复创建 - ✅ 存在"后勤管理"根目录 → 在其下创建子模块 - ❌ 避免创建重复的业务模块导致菜单混乱 ## 🏗️ 菜单类型配置规范 ### 1. 📁 目录菜单 (menu_type = 'M') **用途**:菜单分组和导航组织 ```sql -- 配置要点 component = NULL -- 目录无组件 perms = NULL -- 目录无权限标识 path = '英文路径' -- 使用英文路径 icon = '图标名' -- 统一图标库 ``` ### 2. 🖥️ 功能菜单 (menu_type = 'C') **用途**:具体功能页面,对应前端组件 ```sql -- 配置要点 component = '组件路径' -- 指向前端组件 perms = 'module:func:op' -- 权限格式:模块:功能:操作 path = '功能路径' -- 对应前端路由 parent_id = 目录ID -- 必须有父级目录 ``` ### 3. 🔘 权限按钮 (menu_type = 'F') **用途**:页面内操作权限控制 ```sql -- 配置要点 path = '' -- 空字符串 component = NULL -- 无组件 perms = '具体权限' -- 必须配置权限标识 parent_id = 功能菜单ID -- 指向对应功能菜单 ``` ## 🎨 ID 分配规范 ### ID 规划原则 ``` 根目录(业务模块): 1xxx... (雪花ID) 或 1000-1999 二级目录: 2000-2999 功能页面: 业务范围(2100-2199 物业,2200-2299 场地) 权限按钮: 功能页面ID + 1-9 递增 ``` ### 实际分配示例 ``` 1937078443615322113 -- 后勤管理(根目录) ├── 2100 -- 物业管理(子目录) ├── 2140 -- 场地预约(子目录) ├── 2210 -- 场地管理(功能页面) │ ├── 2211 -- 场地查询(权限) │ ├── 2212 -- 场地新增(权限) │ └── 2213 -- 场地修改(权限) └── 2220 -- 场地预约(功能页面) ├── 2221 -- 预约查询(权限) └── 2222 -- 预约新增(权限) ``` ## 🔐 权限命名规范 ### 标准格式 ``` 格式:{模块}:{功能}:{操作} 标准操作类型: - query : 查询 - add : 新增 - edit : 修改 - remove : 删除 - export : 导出 - import : 导入 - list : 列表 - approve: 审批 ``` ### 权限配置示例 ```sql -- 场地管理模块权限 'hq:venue:list' -- 场地列表权限 'hq:venue:query' -- 场地查询权限 'hq:venue:add' -- 场地新增权限 'hq:venue:edit' -- 场地修改权限 'hq:venue:remove' -- 场地删除权限 -- 场地预约模块权限 'hq:reservation:list' -- 预约列表 'hq:reservation:add' -- 预约申请 'hq:reservation:approve' -- 预约审批 ``` ## 📋 标准操作流程 ### 新增业务模块完整流程 #### 第 1 步:重复检查(🔴 强制执行) ```sql -- 检查是否存在同名根目录 SELECT menu_id, menu_name, parent_id FROM sys_menu WHERE menu_name = '新业务模块名' AND parent_id = 0; -- 检查是否存在同名子目录 SELECT menu_id, menu_name, parent_id FROM sys_menu WHERE menu_name = '新子模块名' AND parent_id = 目标父级ID; ``` #### 第 2 步:创建目录(如不存在) ```sql -- 根目录 INSERT INTO sys_menu (...) VALUES ( 雪花ID, '业务模块名', 0, 排序, '英文路径', NULL, 'M', '0', '0', NULL, '图标', 1, NOW(), '模块说明' ); -- 子目录 INSERT INTO sys_menu (...) VALUES ( 2xxx, '子模块名', 父ID, 排序, '英文路径', NULL, 'M', '0', '0', NULL, '图标', 1, NOW(), '子模块说明' ); ``` #### 第 3 步:创建功能菜单 ```sql INSERT INTO sys_menu (...) VALUES ( 2xx0, '功能名称', 父ID, 排序, '功能路径', '组件路径', 'C', '0', '0', '权限标识', '图标', 1, NOW(), '功能说明' ); ``` #### 第 4 步:创建权限按钮(批量) ```sql INSERT INTO sys_menu (...) VALUES (2xx1, '查询', 功能ID, 1, '', NULL, 'F', '0', '0', 'module:func:query', '#', 1, NOW(), '查询权限'), (2xx2, '新增', 功能ID, 2, '', NULL, 'F', '0', '0', 'module:func:add', '#', 1, NOW(), '新增权限'), (2xx3, '修改', 功能ID, 3, '', NULL, 'F', '0', '0', 'module:func:edit', '#', 1, NOW(), '修改权限'), (2xx4, '删除', 功能ID, 4, '', NULL, 'F', '0', '0', 'module:func:remove', '#', 1, NOW(), '删除权限'); ``` #### 第 5 步:验证结果 ```sql -- 验证菜单层级结构 SELECT CASE WHEN parent_id = 0 THEN CONCAT('└── ', menu_name, ' (', menu_id, ')') WHEN parent_id = 根ID THEN CONCAT(' ├── ', menu_name, ' (', menu_id, ')') ELSE CONCAT(' ├── ', menu_name, ' (', menu_id, ')') END AS menu_structure FROM sys_menu WHERE menu_id = 根ID OR parent_id = 根ID OR parent_id IN ( SELECT menu_id FROM sys_menu WHERE parent_id = 根ID ) ORDER BY parent_id, order_num; ``` ## ⚠️ 配置注意事项 ### 🚨 强制要求 1. **重复检查**:任何菜单新增前必须检查重复,优先使用现有节点 2. **ID 唯一性**:确保 menu_id 全局唯一,建议使用雪花 ID 3. **层级正确**:parent_id 必须指向已存在的父级菜单 4. **权限格式**:权限标识必须遵循{模块}:{功能}:{操作}格式 5. **路径规范**:使用英文路径,避免中文和特殊字符 ### ✅ 最佳实践 1. **分批创建**:先创建目录,再创建功能,最后创建权限 2. **合理排序**:使用 order_num 控制菜单显示顺序 3. **重复利用**:充分利用现有菜单目录,避免重复创建 4. **图标统一**:使用 Element Plus 或项目统一的图标库 5. **备注完整**:remark 字段记录菜单用途和创建原因 ### 🔄 维护流程 1. **变更前备份**:`CREATE TABLE sys_menu_backup_YYYYMMDD AS SELECT * FROM sys_menu;` 2. **重复性检查**:执行标准重复检查 SQL 3. **分步验证**:每步操作后检查结果 4. **权限同步**:菜单变更后及时更新角色权限 5. **文档同步**:菜单结构变更必须同步更新 API 文档 ## 🛠️ 常用检查 SQL ### 重复菜单检查 ```sql -- 检查重复菜单名称 SELECT menu_name, COUNT(*) as count, GROUP_CONCAT(menu_id) as ids FROM sys_menu GROUP BY menu_name HAVING count > 1; -- 检查孤儿菜单(父菜单不存在) SELECT m1.menu_id, m1.menu_name, m1.parent_id FROM sys_menu m1 LEFT JOIN sys_menu m2 ON m1.parent_id = m2.menu_id WHERE m1.parent_id != 0 AND m2.menu_id IS NULL; -- ID冲突检查 SELECT COUNT(*) as count FROM sys_menu WHERE menu_id = 要使用的ID; -- count = 0 才可以使用该ID ``` ### 菜单结构查看 ```sql -- 查看指定模块的完整菜单树 SELECT CASE WHEN parent_id = 0 THEN CONCAT('└── ', menu_name) WHEN parent_id = 根ID THEN CONCAT(' ├── ', menu_name) ELSE CONCAT(' ├── ', menu_name) END AS menu_structure, menu_id, path, menu_type FROM sys_menu WHERE menu_id = 根ID OR parent_id = 根ID OR parent_id IN ( SELECT menu_id FROM sys_menu WHERE parent_id = 根ID ) ORDER BY parent_id, order_num; ``` ## 📊 成功案例参考 ### 场地预约模块配置实例 ```sql -- 成功案例:基于重复检查规则的正确配置流程 -- 1. 检查发现现有"后勤管理"根目录(ID: 1937078443615322113) -- 2. 直接使用现有根目录,不重复创建 -- 3. 在现有根目录下创建"场地预约"子目录(ID: 2140) -- 4. 在子目录下创建具体功能菜单 -- 最终形成清晰层级结构: -- └── 后勤管理 (1937078443615322113) [使用现有] -- └── 场地预约 (2140) [新增子目录] -- ├── 场地管理 (2210) -- ├── 场地预约 (2220) -- └── 使用统计 (2230) -- 避免的问题:重复创建"后勤管理"导致菜单混乱 ``` ## 🎯 路由配置规范 ### 路径命名标准 ```sql -- 目录路径:英文小写,简洁明了 path = 'hq' -- 后勤管理 path = 'venue' -- 场地预约 path = 'pm' -- 物业管理 -- 功能路径:功能英文名 path = 'info' -- 信息管理 path = 'reservation' -- 预约管理 path = 'statistics' -- 统计分析 ``` ### 组件路径规范 ```sql -- 格式:{模块}/{子模块}/{页面} component = 'hq/venue/index' -- 场地管理页面 component = 'hq/venue/reservation' -- 场地预约页面 component = 'hq/pm/repairOrder' -- 报修订单页面 ``` --- ## 📝 变更记录 | 版本 | 日期 | 变更内容 | 变更人 | | ---- | ---------- | ---------------------------------- | ---------- | | v1.0 | 2025-07-01 | 初始版本,基于场地预约模块实践总结 | 系统管理员 | | v1.0 | 2025-07-01 | **新增重复节点检查规则** | 系统管理员 | --- **🎯 使用说明**:本规范适用于所有新增菜单配置,严格遵循重复检查规则,优先使用现有节点,确保系统菜单结构的一致性和可维护性。遇到问题请参考本文档或联系系统管理员。