综述: 新增权限管理系统页面
- 添加了一个完整的权限管理系统HTML页面,包含样式和交互功能 - 实现了权限的增删改查功能,支持树状结构展示父子权限关系 - 提供了右键菜单操作,支持快速添加子权限、编辑和删除权限 - 实现了搜索功能,可根据权限编码、名称或描述进行搜索 - 添加了表单验证,确保权限数据的完整性和一致性
This commit is contained in:
		
							parent
							
								
									7ac41611a0
								
							
						
					
					
						commit
						a2aee4d64c
					
				|  | @ -0,0 +1,916 @@ | |||
| <!DOCTYPE html> | ||||
| <html lang="zh-CN"> | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <title>权限管理系统</title> | ||||
|     <style> | ||||
|         * { | ||||
|             margin: 0; | ||||
|             padding: 0; | ||||
|             box-sizing: border-box; | ||||
|         } | ||||
| 
 | ||||
|         body { | ||||
|             font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | ||||
|             background-color: #f5f7fa; | ||||
|             color: #333; | ||||
|             height: 100vh; | ||||
|             padding: 20px; | ||||
|         } | ||||
| 
 | ||||
|         .container { | ||||
|             max-width: 1200px; | ||||
|             margin: 0 auto; | ||||
|             background-color: #fff; | ||||
|             border-radius: 8px; | ||||
|             box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); | ||||
|             overflow: hidden; | ||||
|         } | ||||
| 
 | ||||
|         .header { | ||||
|             background: linear-gradient(135deg, #2c3e50, #3498db); | ||||
|             color: #fff; | ||||
|             padding: 20px 30px; | ||||
|             display: flex; | ||||
|             justify-content: space-between; | ||||
|             align-items: center; | ||||
|         } | ||||
| 
 | ||||
|         .header h1 { | ||||
|             font-size: 1.8rem; | ||||
|             font-weight: 600; | ||||
|         } | ||||
| 
 | ||||
|         .header-actions { | ||||
|             display: flex; | ||||
|             gap: 10px; | ||||
|         } | ||||
| 
 | ||||
|         .btn { | ||||
|             padding: 10px 20px; | ||||
|             border: none; | ||||
|             border-radius: 5px; | ||||
|             cursor: pointer; | ||||
|             font-size: 14px; | ||||
|             font-weight: 500; | ||||
|             transition: all 0.3s ease; | ||||
|             display: inline-flex; | ||||
|             align-items: center; | ||||
|             gap: 5px; | ||||
|         } | ||||
| 
 | ||||
|         .btn-primary { | ||||
|             background-color: #3498db; | ||||
|             color: #fff; | ||||
|         } | ||||
| 
 | ||||
|         .btn-primary:hover { | ||||
|             background-color: #2980b9; | ||||
|             transform: translateY(-2px); | ||||
|         } | ||||
| 
 | ||||
|         .btn-success { | ||||
|             background-color: #27ae60; | ||||
|             color: #fff; | ||||
|         } | ||||
| 
 | ||||
|         .btn-success:hover { | ||||
|             background-color: #229954; | ||||
|         } | ||||
| 
 | ||||
|         .btn-danger { | ||||
|             background-color: #e74c3c; | ||||
|             color: #fff; | ||||
|         } | ||||
| 
 | ||||
|         .btn-danger:hover { | ||||
|             background-color: #c0392b; | ||||
|         } | ||||
| 
 | ||||
|         .btn-warning { | ||||
|             background-color: #f39c12; | ||||
|             color: #fff; | ||||
|         } | ||||
| 
 | ||||
|         .btn-warning:hover { | ||||
|             background-color: #e67e22; | ||||
|         } | ||||
| 
 | ||||
|         /* 搜索栏 */ | ||||
|         .search-bar { | ||||
|             padding: 20px 30px; | ||||
|             background-color: #f8f9fa; | ||||
|             border-bottom: 1px solid #dee2e6; | ||||
|         } | ||||
| 
 | ||||
|         .search-form { | ||||
|             display: flex; | ||||
|             gap: 15px; | ||||
|             align-items: center; | ||||
|         } | ||||
| 
 | ||||
|         .search-input { | ||||
|             flex: 1; | ||||
|             padding: 10px 15px; | ||||
|             border: 1px solid #ddd; | ||||
|             border-radius: 5px; | ||||
|             font-size: 14px; | ||||
|         } | ||||
| 
 | ||||
|         .search-input:focus { | ||||
|             outline: none; | ||||
|             border-color: #3498db; | ||||
|             box-shadow: 0 0 5px rgba(52, 152, 219, 0.3); | ||||
|         } | ||||
| 
 | ||||
|         /* 表格样式 */ | ||||
|         .table-container { | ||||
|             padding: 0 30px 30px 30px; | ||||
|             overflow-x: auto; | ||||
|         } | ||||
| 
 | ||||
|         .permissions-table { | ||||
|             width: 100%; | ||||
|             border-collapse: collapse; | ||||
|             margin-top: 20px; | ||||
|             background-color: #fff; | ||||
|         } | ||||
| 
 | ||||
|         .permissions-table th, | ||||
|         .permissions-table td { | ||||
|             padding: 12px 15px; | ||||
|             text-align: left; | ||||
|             border-bottom: 1px solid #eee; | ||||
|         } | ||||
| 
 | ||||
|         .permissions-table th { | ||||
|             background-color: #f8f9fa; | ||||
|             font-weight: 600; | ||||
|             color: #2c3e50; | ||||
|             position: sticky; | ||||
|             top: 0; | ||||
|             z-index: 10; | ||||
|         } | ||||
| 
 | ||||
|         .permissions-table tr:hover { | ||||
|             background-color: #f8f9fa; | ||||
|         } | ||||
| 
 | ||||
|         .permission-row { | ||||
|             cursor: pointer; | ||||
|             transition: background-color 0.2s ease; | ||||
|         } | ||||
| 
 | ||||
|         .permission-row.parent-row { | ||||
|             font-weight: 500; | ||||
|         } | ||||
| 
 | ||||
|         .permission-row.child-row { | ||||
|             background-color: #fafbfc; | ||||
|         } | ||||
| 
 | ||||
|         .permission-row.child-row td:first-child { | ||||
|             padding-left: 45px; | ||||
|             position: relative; | ||||
|         } | ||||
| 
 | ||||
|         .permission-row.child-row td:first-child::before { | ||||
|             content: "└─"; | ||||
|             position: absolute; | ||||
|             left: 25px; | ||||
|             color: #bdc3c7; | ||||
|         } | ||||
| 
 | ||||
|         .permission-code { | ||||
|             font-family: monospace; | ||||
|             background-color: #ecf0f1; | ||||
|             padding: 2px 6px; | ||||
|             border-radius: 3px; | ||||
|             font-size: 13px; | ||||
|         } | ||||
| 
 | ||||
|         .permission-type { | ||||
|             padding: 4px 8px; | ||||
|             border-radius: 12px; | ||||
|             font-size: 12px; | ||||
|             font-weight: 500; | ||||
|         } | ||||
| 
 | ||||
|         .type-menu { | ||||
|             background-color: #3498db; | ||||
|             color: #fff; | ||||
|         } | ||||
| 
 | ||||
|         .type-button { | ||||
|             background-color: #27ae60; | ||||
|             color: #fff; | ||||
|         } | ||||
| 
 | ||||
|         .expand-btn { | ||||
|             background: none; | ||||
|             border: none; | ||||
|             cursor: pointer; | ||||
|             padding: 2px 5px; | ||||
|             margin-right: 8px; | ||||
|             color: #3498db; | ||||
|             font-size: 12px; | ||||
|             transition: transform 0.3s ease; | ||||
|         } | ||||
| 
 | ||||
|         .expand-btn.collapsed { | ||||
|             transform: rotate(-90deg); | ||||
|         } | ||||
| 
 | ||||
|         .actions { | ||||
|             display: flex; | ||||
|             gap: 5px; | ||||
|         } | ||||
| 
 | ||||
|         .btn-sm { | ||||
|             padding: 5px 10px; | ||||
|             font-size: 12px; | ||||
|         } | ||||
| 
 | ||||
|         /* 弹窗样式 */ | ||||
|         .modal { | ||||
|             display: none; | ||||
|             position: fixed; | ||||
|             z-index: 1000; | ||||
|             left: 0; | ||||
|             top: 0; | ||||
|             width: 100%; | ||||
|             height: 100%; | ||||
|             background-color: rgba(0, 0, 0, 0.5); | ||||
|         } | ||||
| 
 | ||||
|         .modal.show { | ||||
|             display: flex; | ||||
|             align-items: center; | ||||
|             justify-content: center; | ||||
|         } | ||||
| 
 | ||||
|         .modal-content { | ||||
|             background-color: #fff; | ||||
|             border-radius: 8px; | ||||
|             width: 500px; | ||||
|             max-width: 90vw; | ||||
|             max-height: 90vh; | ||||
|             overflow-y: auto; | ||||
|             box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); | ||||
|             transform: scale(0.8); | ||||
|             transition: transform 0.3s ease; | ||||
|         } | ||||
| 
 | ||||
|         .modal.show .modal-content { | ||||
|             transform: scale(1); | ||||
|         } | ||||
| 
 | ||||
|         .modal-header { | ||||
|             background: linear-gradient(135deg, #2c3e50, #3498db); | ||||
|             color: #fff; | ||||
|             padding: 20px 25px; | ||||
|             display: flex; | ||||
|             justify-content: space-between; | ||||
|             align-items: center; | ||||
|         } | ||||
| 
 | ||||
|         .modal-header h3 { | ||||
|             font-size: 1.2rem; | ||||
|             font-weight: 600; | ||||
|         } | ||||
| 
 | ||||
|         .close { | ||||
|             background: none; | ||||
|             border: none; | ||||
|             color: #fff; | ||||
|             font-size: 24px; | ||||
|             cursor: pointer; | ||||
|             padding: 0; | ||||
|             width: 30px; | ||||
|             height: 30px; | ||||
|             display: flex; | ||||
|             align-items: center; | ||||
|             justify-content: center; | ||||
|             border-radius: 50%; | ||||
|             transition: background-color 0.3s ease; | ||||
|         } | ||||
| 
 | ||||
|         .close:hover { | ||||
|             background-color: rgba(255, 255, 255, 0.2); | ||||
|         } | ||||
| 
 | ||||
|         .modal-body { | ||||
|             padding: 25px; | ||||
|         } | ||||
| 
 | ||||
|         .form-group { | ||||
|             margin-bottom: 20px; | ||||
|         } | ||||
| 
 | ||||
|         .form-group label { | ||||
|             display: block; | ||||
|             margin-bottom: 8px; | ||||
|             font-weight: 500; | ||||
|             color: #2c3e50; | ||||
|         } | ||||
| 
 | ||||
|         .form-group input, | ||||
|         .form-group select, | ||||
|         .form-group textarea { | ||||
|             width: 100%; | ||||
|             padding: 10px 15px; | ||||
|             border: 1px solid #ddd; | ||||
|             border-radius: 5px; | ||||
|             font-size: 14px; | ||||
|             transition: border-color 0.3s ease; | ||||
|         } | ||||
| 
 | ||||
|         .form-group input:focus, | ||||
|         .form-group select:focus, | ||||
|         .form-group textarea:focus { | ||||
|             outline: none; | ||||
|             border-color: #3498db; | ||||
|             box-shadow: 0 0 5px rgba(52, 152, 219, 0.3); | ||||
|         } | ||||
| 
 | ||||
|         .form-group textarea { | ||||
|             resize: vertical; | ||||
|             min-height: 80px; | ||||
|         } | ||||
| 
 | ||||
|         .modal-footer { | ||||
|             background-color: #f8f9fa; | ||||
|             padding: 15px 25px; | ||||
|             display: flex; | ||||
|             justify-content: flex-end; | ||||
|             gap: 10px; | ||||
|             border-top: 1px solid #dee2e6; | ||||
|         } | ||||
| 
 | ||||
|         /* 右键菜单样式 */ | ||||
|         .context-menu { | ||||
|             position: fixed; | ||||
|             background-color: #fff; | ||||
|             border: 1px solid #ddd; | ||||
|             border-radius: 5px; | ||||
|             box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); | ||||
|             z-index: 2000; | ||||
|             padding: 5px 0; | ||||
|             min-width: 120px; | ||||
|             display: none; | ||||
|         } | ||||
| 
 | ||||
|         .context-menu-item { | ||||
|             padding: 8px 15px; | ||||
|             cursor: pointer; | ||||
|             font-size: 14px; | ||||
|             transition: background-color 0.2s ease; | ||||
|             display: flex; | ||||
|             align-items: center; | ||||
|             gap: 8px; | ||||
|         } | ||||
| 
 | ||||
|         .context-menu-item:hover { | ||||
|             background-color: #f8f9fa; | ||||
|         } | ||||
| 
 | ||||
|         /* 空状态样式 */ | ||||
|         .empty-state { | ||||
|             text-align: center; | ||||
|             padding: 60px 20px; | ||||
|             color: #7f8c8d; | ||||
|         } | ||||
| 
 | ||||
|         .empty-state h3 { | ||||
|             margin-bottom: 10px; | ||||
|             color: #95a5a6; | ||||
|         } | ||||
| 
 | ||||
|         /* 响应式设计 */ | ||||
|         @media (max-width: 768px) { | ||||
|             .container { | ||||
|                 margin: 10px; | ||||
|                 border-radius: 0; | ||||
|             } | ||||
|              | ||||
|             .header { | ||||
|                 padding: 15px 20px; | ||||
|                 flex-direction: column; | ||||
|                 gap: 15px; | ||||
|             } | ||||
|              | ||||
|             .search-bar { | ||||
|                 padding: 15px 20px; | ||||
|             } | ||||
|              | ||||
|             .table-container { | ||||
|                 padding: 0 20px 20px 20px; | ||||
|             } | ||||
|              | ||||
|             .permissions-table { | ||||
|                 font-size: 14px; | ||||
|             } | ||||
|              | ||||
|             .permissions-table th, | ||||
|             .permissions-table td { | ||||
|                 padding: 8px 10px; | ||||
|             } | ||||
|              | ||||
|             .modal-content { | ||||
|                 width: 95vw; | ||||
|                 margin: 10px; | ||||
|             } | ||||
|         } | ||||
|     </style> | ||||
| </head> | ||||
| <body> | ||||
|     <div class="container"> | ||||
|         <!-- 头部 --> | ||||
|         <div class="header"> | ||||
|             <h1>权限管理系统</h1> | ||||
|             <div class="header-actions"> | ||||
|                 <button class="btn btn-success" onclick="openAddModal()"> | ||||
|                     ➕ 新增权限 | ||||
|                 </button> | ||||
|             </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <!-- 搜索栏 --> | ||||
|         <div class="search-bar"> | ||||
|             <div class="search-form"> | ||||
|                 <input type="text" class="search-input" id="searchInput" placeholder="搜索权限编码、名称或描述..."> | ||||
|                 <button class="btn btn-primary" onclick="searchPermissions()">🔍 搜索</button> | ||||
|                 <button class="btn btn-warning" onclick="resetSearch()">🔄 重置</button> | ||||
|             </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <!-- 表格容器 --> | ||||
|         <div class="table-container"> | ||||
|             <table class="permissions-table"> | ||||
|                 <thead> | ||||
|                     <tr> | ||||
|                         <th>权限编码</th> | ||||
|                         <th>权限名称</th> | ||||
|                         <th>权限描述</th> | ||||
|                         <th>权限类型</th> | ||||
|                         <th>父级权限</th> | ||||
|                         <th>操作</th> | ||||
|                     </tr> | ||||
|                 </thead> | ||||
|                 <tbody id="permissionsTableBody"> | ||||
|                     <!-- 数据将通过JavaScript动态加载 --> | ||||
|                 </tbody> | ||||
|             </table> | ||||
|             <div class="empty-state" id="emptyState" style="display: none;"> | ||||
|                 <h3>暂无权限数据</h3> | ||||
|                 <p>点击"新增权限"按钮开始添加权限信息</p> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- 权限弹窗 --> | ||||
|     <div id="permissionModal" class="modal"> | ||||
|         <div class="modal-content"> | ||||
|             <div class="modal-header"> | ||||
|                 <h3 id="modalTitle">新增权限</h3> | ||||
|                 <button type="button" class="close" onclick="closeModal()">×</button> | ||||
|             </div> | ||||
|             <div class="modal-body"> | ||||
|                 <form id="permissionForm"> | ||||
|                     <div class="form-group"> | ||||
|                         <label for="permissionCode">权限编码 *</label> | ||||
|                         <input type="text" id="permissionCode" name="code" required placeholder="请输入全局唯一的权限编码"> | ||||
|                     </div> | ||||
|                     <div class="form-group"> | ||||
|                         <label for="permissionName">权限名称 *</label> | ||||
|                         <input type="text" id="permissionName" name="name" required placeholder="请输入权限名称"> | ||||
|                     </div> | ||||
|                     <div class="form-group"> | ||||
|                         <label for="permissionDesc">权限描述</label> | ||||
|                         <textarea id="permissionDesc" name="description" placeholder="请输入权限描述(可选)"></textarea> | ||||
|                     </div> | ||||
|                     <div class="form-group"> | ||||
|                         <label for="parentCode">父级权限编码</label> | ||||
|                         <select id="parentCode" name="parentCode"> | ||||
|                             <option value="">无父级权限</option> | ||||
|                             <!-- 选项将通过JavaScript动态加载 --> | ||||
|                         </select> | ||||
|                     </div> | ||||
|                     <div class="form-group"> | ||||
|                         <label for="permissionType">权限类型 *</label> | ||||
|                         <select id="permissionType" name="type" required> | ||||
|                             <option value="">请选择权限类型</option> | ||||
|                             <option value="menu">菜单</option> | ||||
|                             <option value="button">按钮</option> | ||||
|                         </select> | ||||
|                     </div> | ||||
|                 </form> | ||||
|             </div> | ||||
|             <div class="modal-footer"> | ||||
|                 <button type="button" class="btn btn-warning" onclick="closeModal()">取消</button> | ||||
|                 <button type="button" class="btn btn-success" onclick="savePermission()">保存</button> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- 右键菜单 --> | ||||
|     <div id="contextMenu" class="context-menu"> | ||||
|         <div class="context-menu-item" onclick="addChildPermission()"> | ||||
|             ➕ 添加子权限 | ||||
|         </div> | ||||
|         <div class="context-menu-item" onclick="editPermission()"> | ||||
|             ✏️ 编辑权限 | ||||
|         </div> | ||||
|         <div class="context-menu-item" onclick="deletePermission()"> | ||||
|             🗑️ 删除权限 | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <script> | ||||
|         // 权限数据存储 | ||||
|         let permissions = []; | ||||
|         let currentEditingId = null; | ||||
|         let contextMenuTargetRow = null; | ||||
| 
 | ||||
|         // 初始化示例数据 | ||||
|         function initSampleData() { | ||||
|             permissions = [ | ||||
|                 { | ||||
|                     id: 1, | ||||
|                     code: 'SYSTEM', | ||||
|                     name: '系统管理', | ||||
|                     description: '系统管理模块', | ||||
|                     parentCode: '', | ||||
|                     type: 'menu' | ||||
|                 }, | ||||
|                 { | ||||
|                     id: 2, | ||||
|                     code: 'SYSTEM_USER', | ||||
|                     name: '用户管理', | ||||
|                     description: '用户管理功能', | ||||
|                     parentCode: 'SYSTEM', | ||||
|                     type: 'menu' | ||||
|                 }, | ||||
|                 { | ||||
|                     id: 3, | ||||
|                     code: 'SYSTEM_USER_ADD', | ||||
|                     name: '添加用户', | ||||
|                     description: '添加新用户按钮', | ||||
|                     parentCode: 'SYSTEM_USER', | ||||
|                     type: 'button' | ||||
|                 }, | ||||
|                 { | ||||
|                     id: 4, | ||||
|                     code: 'SYSTEM_USER_EDIT', | ||||
|                     name: '编辑用户', | ||||
|                     description: '编辑用户信息按钮', | ||||
|                     parentCode: 'SYSTEM_USER', | ||||
|                     type: 'button' | ||||
|                 }, | ||||
|                 { | ||||
|                     id: 5, | ||||
|                     code: 'SYSTEM_ROLE', | ||||
|                     name: '角色管理', | ||||
|                     description: '角色管理功能', | ||||
|                     parentCode: 'SYSTEM', | ||||
|                     type: 'menu' | ||||
|                 }, | ||||
|                 { | ||||
|                     id: 6, | ||||
|                     code: 'CONTENT', | ||||
|                     name: '内容管理', | ||||
|                     description: '内容管理模块', | ||||
|                     parentCode: '', | ||||
|                     type: 'menu' | ||||
|                 } | ||||
|             ]; | ||||
|             renderPermissionsTable(); | ||||
|         } | ||||
| 
 | ||||
|         // 渲染权限表格 | ||||
|         function renderPermissionsTable(data = permissions) { | ||||
|             const tableBody = document.getElementById('permissionsTableBody'); | ||||
|             const emptyState = document.getElementById('emptyState'); | ||||
|              | ||||
|             if (data.length === 0) { | ||||
|                 tableBody.innerHTML = ''; | ||||
|                 emptyState.style.display = 'block'; | ||||
|                 return; | ||||
|             } | ||||
|              | ||||
|             emptyState.style.display = 'none'; | ||||
|              | ||||
|             // 构建树状结构 | ||||
|             const tree = buildPermissionTree(data); | ||||
|             let html = ''; | ||||
|              | ||||
|             tree.forEach(item => { | ||||
|                 html += renderPermissionRow(item, 0); | ||||
|             }); | ||||
|              | ||||
|             tableBody.innerHTML = html; | ||||
|         } | ||||
| 
 | ||||
|         // 构建权限树 | ||||
|         function buildPermissionTree(data) { | ||||
|             const map = {}; | ||||
|             const roots = []; | ||||
|              | ||||
|             // 创建映射 | ||||
|             data.forEach(item => { | ||||
|                 map[item.code] = { ...item, children: [] }; | ||||
|             }); | ||||
|              | ||||
|             // 构建树结构 | ||||
|             data.forEach(item => { | ||||
|                 if (item.parentCode && map[item.parentCode]) { | ||||
|                     map[item.parentCode].children.push(map[item.code]); | ||||
|                 } else { | ||||
|                     roots.push(map[item.code]); | ||||
|                 } | ||||
|             }); | ||||
|              | ||||
|             return roots; | ||||
|         } | ||||
| 
 | ||||
|         // 渲染权限行 | ||||
|         function renderPermissionRow(item, level) { | ||||
|             const hasChildren = item.children && item.children.length > 0; | ||||
|             const isParent = level === 0; | ||||
|             const parentClass = isParent ? 'parent-row' : 'child-row'; | ||||
|              | ||||
|             let html = ` | ||||
|                 <tr class="permission-row ${parentClass}"  | ||||
|                     data-id="${item.id}"  | ||||
|                     data-code="${item.code}" | ||||
|                     oncontextmenu="showContextMenu(event, this)"> | ||||
|                     <td> | ||||
|                         ${hasChildren ? `<button class="expand-btn" onclick="toggleChildren(this)">▼</button>` : ''} | ||||
|                         <span class="permission-code">${item.code}</span> | ||||
|                     </td> | ||||
|                     <td>${item.name}</td> | ||||
|                     <td>${item.description || '-'}</td> | ||||
|                     <td><span class="permission-type type-${item.type}">${item.type === 'menu' ? '菜单' : '按钮'}</span></td> | ||||
|                     <td>${item.parentCode || '-'}</td> | ||||
|                     <td class="actions"> | ||||
|                         <button class="btn btn-primary btn-sm" onclick="editPermissionById(${item.id})">编辑</button> | ||||
|                         <button class="btn btn-danger btn-sm" onclick="deletePermissionById(${item.id})">删除</button> | ||||
|                     </td> | ||||
|                 </tr> | ||||
|             `; | ||||
|              | ||||
|             // 添加子权限行 | ||||
|             if (hasChildren) { | ||||
|                 item.children.forEach(child => { | ||||
|                     html += renderPermissionRow(child, level + 1); | ||||
|                 }); | ||||
|             } | ||||
|              | ||||
|             return html; | ||||
|         } | ||||
| 
 | ||||
|         // 展开/收起子权限 | ||||
|         function toggleChildren(btn) { | ||||
|             const row = btn.closest('tr'); | ||||
|             const code = row.dataset.code; | ||||
|             let nextRow = row.nextElementSibling; | ||||
|              | ||||
|             const isExpanded = btn.textContent === '▼'; | ||||
|             btn.textContent = isExpanded ? '▶' : '▼'; | ||||
|             btn.classList.toggle('collapsed', isExpanded); | ||||
|              | ||||
|             // 切换所有子行的显示状态 | ||||
|             while (nextRow && nextRow.classList.contains('child-row')) { | ||||
|                 nextRow.style.display = isExpanded ? 'none' : 'table-row'; | ||||
|                 nextRow = nextRow.nextElementSibling; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // 显示右键菜单 | ||||
|         function showContextMenu(event, row) { | ||||
|             event.preventDefault(); | ||||
|             contextMenuTargetRow = row; | ||||
|              | ||||
|             const menu = document.getElementById('contextMenu'); | ||||
|             menu.style.display = 'block'; | ||||
|             menu.style.left = event.pageX + 'px'; | ||||
|             menu.style.top = event.pageY + 'px'; | ||||
|         } | ||||
| 
 | ||||
|         // 隐藏右键菜单 | ||||
|         function hideContextMenu() { | ||||
|             document.getElementById('contextMenu').style.display = 'none'; | ||||
|             contextMenuTargetRow = null; | ||||
|         } | ||||
| 
 | ||||
|         // 点击其他地方隐藏右键菜单 | ||||
|         document.addEventListener('click', hideContextMenu); | ||||
| 
 | ||||
|         // 打开新增弹窗 | ||||
|         function openAddModal() { | ||||
|             currentEditingId = null; | ||||
|             document.getElementById('modalTitle').textContent = '新增权限'; | ||||
|             document.getElementById('permissionForm').reset(); | ||||
|             updateParentCodeOptions(); | ||||
|             document.getElementById('permissionModal').classList.add('show'); | ||||
|         } | ||||
| 
 | ||||
|         // 添加子权限 | ||||
|         function addChildPermission() { | ||||
|             if (!contextMenuTargetRow) return; | ||||
|              | ||||
|             const parentCode = contextMenuTargetRow.dataset.code; | ||||
|             openAddModal(); | ||||
|             document.getElementById('parentCode').value = parentCode; | ||||
|             hideContextMenu(); | ||||
|         } | ||||
| 
 | ||||
|         // 编辑权限 | ||||
|         function editPermission() { | ||||
|             if (!contextMenuTargetRow) return; | ||||
|              | ||||
|             const id = parseInt(contextMenuTargetRow.dataset.id); | ||||
|             editPermissionById(id); | ||||
|             hideContextMenu(); | ||||
|         } | ||||
| 
 | ||||
|         // 根据ID编辑权限 | ||||
|         function editPermissionById(id) { | ||||
|             const permission = permissions.find(p => p.id === id); | ||||
|             if (!permission) return; | ||||
|              | ||||
|             currentEditingId = id; | ||||
|             document.getElementById('modalTitle').textContent = '编辑权限'; | ||||
|             document.getElementById('permissionCode').value = permission.code; | ||||
|             document.getElementById('permissionName').value = permission.name; | ||||
|             document.getElementById('permissionDesc').value = permission.description || ''; | ||||
|             document.getElementById('permissionType').value = permission.type; | ||||
|              | ||||
|             updateParentCodeOptions(permission.code); | ||||
|             document.getElementById('parentCode').value = permission.parentCode || ''; | ||||
|              | ||||
|             document.getElementById('permissionModal').classList.add('show'); | ||||
|         } | ||||
| 
 | ||||
|         // 删除权限 | ||||
|         function deletePermission() { | ||||
|             if (!contextMenuTargetRow) return; | ||||
|              | ||||
|             const id = parseInt(contextMenuTargetRow.dataset.id); | ||||
|             deletePermissionById(id); | ||||
|             hideContextMenu(); | ||||
|         } | ||||
| 
 | ||||
|         // 根据ID删除权限 | ||||
|         function deletePermissionById(id) { | ||||
|             const permission = permissions.find(p => p.id === id); | ||||
|             if (!permission) return; | ||||
|              | ||||
|             // 检查是否有子权限 | ||||
|             const hasChildren = permissions.some(p => p.parentCode === permission.code); | ||||
|             if (hasChildren) { | ||||
|                 alert('该权限下有子权限,请先删除子权限!'); | ||||
|                 return; | ||||
|             } | ||||
|              | ||||
|             if (confirm(`确定要删除权限"${permission.name}"吗?`)) { | ||||
|                 permissions = permissions.filter(p => p.id !== id); | ||||
|                 renderPermissionsTable(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // 更新父级权限选项 | ||||
|         function updateParentCodeOptions(excludeCode = '') { | ||||
|             const select = document.getElementById('parentCode'); | ||||
|             select.innerHTML = '<option value="">无父级权限</option>'; | ||||
|              | ||||
|             permissions.forEach(permission => { | ||||
|                 if (permission.code !== excludeCode && permission.type === 'menu') { | ||||
|                     const option = document.createElement('option'); | ||||
|                     option.value = permission.code; | ||||
|                     option.textContent = `${permission.code} (${permission.name})`; | ||||
|                     select.appendChild(option); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         // 保存权限 | ||||
|         function savePermission() { | ||||
|             const form = document.getElementById('permissionForm'); | ||||
|             const formData = new FormData(form); | ||||
|              | ||||
|             const code = formData.get('code').trim(); | ||||
|             const name = formData.get('name').trim(); | ||||
|             const description = formData.get('description').trim(); | ||||
|             const parentCode = formData.get('parentCode'); | ||||
|             const type = formData.get('type'); | ||||
|              | ||||
|             // 验证 | ||||
|             if (!code || !name || !type) { | ||||
|                 alert('请填写必填项!'); | ||||
|                 return; | ||||
|             } | ||||
|              | ||||
|             // 检查权限编码是否重复(编辑时排除自身) | ||||
|             const existingPermission = permissions.find(p => p.code === code && p.id !== currentEditingId); | ||||
|             if (existingPermission) { | ||||
|                 alert('权限编码已存在!'); | ||||
|                 return; | ||||
|             } | ||||
|              | ||||
|             // 验证父级权限 | ||||
|             if (parentCode) { | ||||
|                 const parentPermission = permissions.find(p => p.code === parentCode); | ||||
|                 if (!parentPermission) { | ||||
|                     alert('父级权限不存在!'); | ||||
|                     return; | ||||
|                 } | ||||
|                 if (parentPermission.type !== 'menu') { | ||||
|                     alert('只能选择菜单类型的权限作为父级权限!'); | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|             const permissionData = { | ||||
|                 code, | ||||
|                 name, | ||||
|                 description, | ||||
|                 parentCode: parentCode || '', | ||||
|                 type | ||||
|             }; | ||||
|              | ||||
|             if (currentEditingId) { | ||||
|                 // 编辑权限 | ||||
|                 const index = permissions.findIndex(p => p.id === currentEditingId); | ||||
|                 if (index !== -1) { | ||||
|                     permissions[index] = { ...permissions[index], ...permissionData }; | ||||
|                 } | ||||
|             } else { | ||||
|                 // 新增权限 | ||||
|                 const newId = Math.max(...permissions.map(p => p.id), 0) + 1; | ||||
|                 permissions.push({ id: newId, ...permissionData }); | ||||
|             } | ||||
|              | ||||
|             closeModal(); | ||||
|             renderPermissionsTable(); | ||||
|         } | ||||
| 
 | ||||
|         // 关闭弹窗 | ||||
|         function closeModal() { | ||||
|             document.getElementById('permissionModal').classList.remove('show'); | ||||
|             currentEditingId = null; | ||||
|         } | ||||
| 
 | ||||
|         // 搜索权限 | ||||
|         function searchPermissions() { | ||||
|             const keyword = document.getElementById('searchInput').value.trim().toLowerCase(); | ||||
|             if (!keyword) { | ||||
|                 renderPermissionsTable(); | ||||
|                 return; | ||||
|             } | ||||
|              | ||||
|             const filteredPermissions = permissions.filter(permission =>  | ||||
|                 permission.code.toLowerCase().includes(keyword) || | ||||
|                 permission.name.toLowerCase().includes(keyword) || | ||||
|                 (permission.description && permission.description.toLowerCase().includes(keyword)) | ||||
|             ); | ||||
|              | ||||
|             renderPermissionsTable(filteredPermissions); | ||||
|         } | ||||
| 
 | ||||
|         // 重置搜索 | ||||
|         function resetSearch() { | ||||
|             document.getElementById('searchInput').value = ''; | ||||
|             renderPermissionsTable(); | ||||
|         } | ||||
| 
 | ||||
|         // 监听回车键搜索 | ||||
|         document.getElementById('searchInput').addEventListener('keypress', function(e) { | ||||
|             if (e.key === 'Enter') { | ||||
|                 searchPermissions(); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         // 监听ESC键关闭弹窗 | ||||
|         document.addEventListener('keydown', function(e) { | ||||
|             if (e.key === 'Escape') { | ||||
|                 closeModal(); | ||||
|                 hideContextMenu(); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         // 阻止弹窗背景点击关闭 | ||||
|         document.getElementById('permissionModal').addEventListener('click', function(e) { | ||||
|             if (e.target === this) { | ||||
|                 closeModal(); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         // 初始化页面 | ||||
|         document.addEventListener('DOMContentLoaded', function() { | ||||
|             initSampleData(); | ||||
|         }); | ||||
|     </script> | ||||
| </body> | ||||
| </html> | ||||
		Loading…
	
		Reference in New Issue