1068 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			1068 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			HTML
		
	
	
	
| <!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: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
 | |
|             background-color: #f5f5f5;
 | |
|             color: #333;
 | |
|             height: 100vh;
 | |
|             margin: 0;
 | |
|             padding: 0;
 | |
|         }
 | |
| 
 | |
|         .container {
 | |
|             max-width: 1600px;
 | |
|             margin: 0 auto;
 | |
|             padding: 20px;
 | |
|         }
 | |
| 
 | |
|         /* 页面头部 */
 | |
|         .page-header {
 | |
|             background: white;
 | |
|             padding: 16px 24px;
 | |
|             margin-bottom: 16px;
 | |
|             border-radius: 8px;
 | |
|             box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
 | |
|             display: flex;
 | |
|             justify-content: space-between;
 | |
|             align-items: center;
 | |
|         }
 | |
| 
 | |
|         .page-title {
 | |
|             font-size: 24px;
 | |
|             font-weight: 600;
 | |
|             color: #262626;
 | |
|         }
 | |
| 
 | |
|         /* 按钮样式 */
 | |
|         .btn {
 | |
|             height: 40px;
 | |
|             padding: 0 16px;
 | |
|             border: none;
 | |
|             border-radius: 6px;
 | |
|             font-size: 14px;
 | |
|             cursor: pointer;
 | |
|             display: inline-flex;
 | |
|             align-items: center;
 | |
|             justify-content: center;
 | |
|             transition: all 0.3s;
 | |
|             gap: 5px;
 | |
|         }
 | |
| 
 | |
|         .btn-primary {
 | |
|             background: #1890ff;
 | |
|             color: white;
 | |
|         }
 | |
| 
 | |
|         .btn-primary:hover {
 | |
|             background: #40a9ff;
 | |
|         }
 | |
| 
 | |
|         .btn-success {
 | |
|             background: #52c41a;
 | |
|             color: white;
 | |
|         }
 | |
| 
 | |
|         .btn-success:hover {
 | |
|             background: #73d13d;
 | |
|         }
 | |
| 
 | |
|         .btn-danger {
 | |
|             background: #ff4d4f;
 | |
|             color: white;
 | |
|         }
 | |
| 
 | |
|         .btn-danger:hover {
 | |
|             background: #ff7875;
 | |
|         }
 | |
| 
 | |
|         .btn-default {
 | |
|             background: white;
 | |
|             color: #666;
 | |
|             border: 1px solid #ddd;
 | |
|         }
 | |
| 
 | |
|         .btn-default:hover {
 | |
|             border-color: #1890ff;
 | |
|             color: #1890ff;
 | |
|         }
 | |
| 
 | |
|         .btn-sm {
 | |
|             height: 32px;
 | |
|             padding: 0 12px;
 | |
|             font-size: 12px;
 | |
|         }
 | |
| 
 | |
|         .button-group {
 | |
|             display: flex;
 | |
|             gap: 12px;
 | |
|         }
 | |
| 
 | |
|         /* 搜索区域 */
 | |
|         .search-section {
 | |
|             background: white;
 | |
|             padding: 24px;
 | |
|             margin-bottom: 16px;
 | |
|             border-radius: 8px;
 | |
|             box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
 | |
|         }
 | |
| 
 | |
|         .search-form {
 | |
|             display: grid;
 | |
|             grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
 | |
|             gap: 16px;
 | |
|             margin-bottom: 16px;
 | |
|         }
 | |
| 
 | |
|         .form-item {
 | |
|             display: flex;
 | |
|             flex-direction: column;
 | |
|             gap: 8px;
 | |
|         }
 | |
| 
 | |
|         .form-label {
 | |
|             font-size: 14px;
 | |
|             color: #666;
 | |
|             font-weight: 500;
 | |
|         }
 | |
| 
 | |
|         .form-input {
 | |
|             height: 40px;
 | |
|             padding: 0 12px;
 | |
|             border: 1px solid #ddd;
 | |
|             border-radius: 6px;
 | |
|             font-size: 14px;
 | |
|         }
 | |
| 
 | |
|         .form-input:focus {
 | |
|             outline: none;
 | |
|             border-color: #1890ff;
 | |
|             box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
 | |
|         }
 | |
| 
 | |
|         /* 表格样式 */
 | |
|         .table-section {
 | |
|             background: white;
 | |
|             border-radius: 8px;
 | |
|             box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
 | |
|             overflow: hidden;
 | |
|         }
 | |
| 
 | |
|         .table-wrapper {
 | |
|             overflow-x: auto;
 | |
|         }
 | |
| 
 | |
|         .permissions-table {
 | |
|             width: 100%;
 | |
|             border-collapse: collapse;
 | |
|             font-size: 14px;
 | |
|         }
 | |
| 
 | |
|         .permissions-table th {
 | |
|             background: #fafafa;
 | |
|             padding: 12px 16px;
 | |
|             text-align: left;
 | |
|             font-weight: 600;
 | |
|             color: #666;
 | |
|             border-bottom: 1px solid #f0f0f0;
 | |
|             white-space: nowrap;
 | |
|         }
 | |
| 
 | |
|         .permissions-table td {
 | |
|             padding: 12px 16px;
 | |
|             border-bottom: 1px solid #f9f9f9;
 | |
|             vertical-align: top;
 | |
|         }
 | |
| 
 | |
|         .permissions-table tbody tr:hover {
 | |
|             background: #f8f9fa;
 | |
|         }
 | |
| 
 | |
|         .permission-row {
 | |
|             cursor: pointer;
 | |
|             transition: background-color 0.2s ease;
 | |
|         }
 | |
| 
 | |
|         .permission-row.parent-row {
 | |
|             font-weight: 500;
 | |
|         }
 | |
| 
 | |
|         .permission-row.child-row {
 | |
|             background-color: #fafafa;
 | |
|         }
 | |
| 
 | |
|         .permission-row.level-1 td:first-child {
 | |
|             padding-left: 60px;
 | |
|             position: relative;
 | |
|         }
 | |
| 
 | |
|         .permission-row.level-1 td:first-child::before {
 | |
|             content: "└─";
 | |
|             position: absolute;
 | |
|             left: 35px;
 | |
|             color: #bdc3c7;
 | |
|         }
 | |
| 
 | |
|         .permission-row.level-2 td:first-child {
 | |
|             padding-left: 100px;
 | |
|             position: relative;
 | |
|         }
 | |
| 
 | |
|         .permission-row.level-2 td:first-child::before {
 | |
|             content: "└─";
 | |
|             position: absolute;
 | |
|             left: 75px;
 | |
|             color: #bdc3c7;
 | |
|         }
 | |
| 
 | |
|         .permission-row.level-3 td:first-child {
 | |
|             padding-left: 140px;
 | |
|             position: relative;
 | |
|         }
 | |
| 
 | |
|         .permission-row.level-3 td:first-child::before {
 | |
|             content: "└─";
 | |
|             position: absolute;
 | |
|             left: 115px;
 | |
|             color: #bdc3c7;
 | |
|         }
 | |
| 
 | |
|         .permission-code {
 | |
|             font-family: monospace;
 | |
|             background-color: #f0f0f0;
 | |
|             padding: 4px 8px;
 | |
|             border-radius: 4px;
 | |
|             font-size: 12px;
 | |
|         }
 | |
| 
 | |
|         .permission-type {
 | |
|             padding: 4px 8px;
 | |
|             border-radius: 4px;
 | |
|             font-size: 12px;
 | |
|             font-weight: 500;
 | |
|         }
 | |
| 
 | |
|         .type-menu {
 | |
|             background: #e6f7ff;
 | |
|             color: #1890ff;
 | |
|             border: 1px solid #91d5ff;
 | |
|         }
 | |
| 
 | |
|         .type-button {
 | |
|             background: #f6ffed;
 | |
|             color: #52c41a;
 | |
|             border: 1px solid #b7eb8f;
 | |
|         }
 | |
| 
 | |
|         .expand-btn {
 | |
|             background: none;
 | |
|             border: none;
 | |
|             cursor: pointer;
 | |
|             padding: 4px;
 | |
|             margin-right: 8px;
 | |
|             color: #1890ff;
 | |
|             font-size: 12px;
 | |
|             transition: all 0.3s;
 | |
|             border-radius: 4px;
 | |
|         }
 | |
| 
 | |
|         .expand-btn:hover {
 | |
|             background: #e6f7ff;
 | |
|         }
 | |
| 
 | |
|         .expand-btn.collapsed {
 | |
|             transform: rotate(-90deg);
 | |
|         }
 | |
| 
 | |
|         .actions {
 | |
|             display: flex;
 | |
|             gap: 5px;
 | |
|         }
 | |
| 
 | |
|         /* 弹窗样式 */
 | |
|         .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: #fafafa;
 | |
|             padding: 16px 24px;
 | |
|             display: flex;
 | |
|             justify-content: space-between;
 | |
|             align-items: center;
 | |
|             border-bottom: 1px solid #f0f0f0;
 | |
|         }
 | |
| 
 | |
|         .modal-header h3 {
 | |
|             font-size: 18px;
 | |
|             font-weight: 600;
 | |
|             color: #262626;
 | |
|         }
 | |
| 
 | |
|         .close {
 | |
|             background: none;
 | |
|             border: none;
 | |
|             color: #666;
 | |
|             font-size: 24px;
 | |
|             cursor: pointer;
 | |
|             padding: 0;
 | |
|             width: 30px;
 | |
|             height: 30px;
 | |
|             display: flex;
 | |
|             align-items: center;
 | |
|             justify-content: center;
 | |
|             border-radius: 50%;
 | |
|             transition: all 0.3s;
 | |
|         }
 | |
| 
 | |
|         .close:hover {
 | |
|             background-color: #f0f0f0;
 | |
|             color: #333;
 | |
|         }
 | |
| 
 | |
|         .modal-body {
 | |
|             padding: 24px;
 | |
|         }
 | |
| 
 | |
|         .form-group {
 | |
|             margin-bottom: 20px;
 | |
|         }
 | |
| 
 | |
|         .form-group label {
 | |
|             display: block;
 | |
|             margin-bottom: 8px;
 | |
|             font-weight: 500;
 | |
|             color: #333;
 | |
|             font-size: 14px;
 | |
|         }
 | |
| 
 | |
|         .form-group input,
 | |
|         .form-group select,
 | |
|         .form-group textarea {
 | |
|             width: 100%;
 | |
|             padding: 10px 12px;
 | |
|             border: 1px solid #ddd;
 | |
|             border-radius: 6px;
 | |
|             font-size: 14px;
 | |
|             transition: all 0.3s;
 | |
|         }
 | |
| 
 | |
|         .form-group input:focus,
 | |
|         .form-group select:focus,
 | |
|         .form-group textarea:focus {
 | |
|             outline: none;
 | |
|             border-color: #1890ff;
 | |
|             box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
 | |
|         }
 | |
| 
 | |
|         .form-group textarea {
 | |
|             resize: vertical;
 | |
|             min-height: 80px;
 | |
|         }
 | |
| 
 | |
|         .form-group small {
 | |
|             color: #666;
 | |
|             font-size: 12px;
 | |
|             margin-top: 4px;
 | |
|             display: block;
 | |
|         }
 | |
| 
 | |
|         .modal-footer {
 | |
|             background-color: #fafafa;
 | |
|             padding: 12px 24px;
 | |
|             display: flex;
 | |
|             justify-content: flex-end;
 | |
|             gap: 12px;
 | |
|             border-top: 1px solid #f0f0f0;
 | |
|         }
 | |
| 
 | |
|         /* 右键菜单样式 */
 | |
|         .context-menu {
 | |
|             position: fixed;
 | |
|             background-color: #fff;
 | |
|             border: 1px solid #f0f0f0;
 | |
|             border-radius: 6px;
 | |
|             box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
 | |
|             z-index: 2000;
 | |
|             padding: 4px 0;
 | |
|             min-width: 120px;
 | |
|             display: none;
 | |
|         }
 | |
| 
 | |
|         .context-menu-item {
 | |
|             padding: 8px 16px;
 | |
|             cursor: pointer;
 | |
|             font-size: 14px;
 | |
|             transition: background-color 0.2s ease;
 | |
|             display: flex;
 | |
|             align-items: center;
 | |
|             gap: 8px;
 | |
|             color: #333;
 | |
|         }
 | |
| 
 | |
|         .context-menu-item:hover {
 | |
|             background-color: #f8f9fa;
 | |
|         }
 | |
| 
 | |
|         /* 空状态样式 */
 | |
|         .empty-state {
 | |
|             text-align: center;
 | |
|             padding: 60px 20px;
 | |
|             color: #999;
 | |
|         }
 | |
| 
 | |
|         .empty-state h3 {
 | |
|             margin-bottom: 10px;
 | |
|             color: #666;
 | |
|             font-size: 16px;
 | |
|             font-weight: 500;
 | |
|         }
 | |
| 
 | |
|         .empty-state p {
 | |
|             font-size: 14px;
 | |
|         }
 | |
| 
 | |
|         /* 响应式设计 */
 | |
|         @media (max-width: 768px) {
 | |
|             .container {
 | |
|                 padding: 10px;
 | |
|             }
 | |
| 
 | |
|             .page-header {
 | |
|                 padding: 12px 16px;
 | |
|                 flex-direction: column;
 | |
|                 gap: 12px;
 | |
|                 align-items: flex-start;
 | |
|             }
 | |
| 
 | |
|             .search-section {
 | |
|                 padding: 16px;
 | |
|             }
 | |
| 
 | |
|             .permissions-table {
 | |
|                 font-size: 12px;
 | |
|             }
 | |
| 
 | |
|             .permissions-table th,
 | |
|             .permissions-table td {
 | |
|                 padding: 8px 10px;
 | |
|             }
 | |
| 
 | |
|             .modal-content {
 | |
|                 width: 95vw;
 | |
|                 margin: 10px;
 | |
|             }
 | |
|         }
 | |
|     </style>
 | |
| </head>
 | |
| <body>
 | |
|     <div class="container">
 | |
|         <!-- 页面头部 -->
 | |
|         <div class="page-header">
 | |
|             <h1 class="page-title">权限编辑</h1>
 | |
|             <div class="button-group">
 | |
|                 <button class="btn btn-success" onclick="openAddModal()">
 | |
|                     新增权限
 | |
|                 </button>
 | |
|             </div>
 | |
|         </div>
 | |
| 
 | |
|         <!-- 搜索区域 -->
 | |
|         <div class="search-section">
 | |
|             <div class="search-form">
 | |
|                 <div class="form-item">
 | |
|                     <input type="text" class="form-input" id="searchInput" placeholder="搜索权限编码、名称或描述...">
 | |
|                 </div>
 | |
|             </div>
 | |
|             <div class="button-group">
 | |
|                 <button class="btn btn-primary" onclick="searchPermissions()">搜索</button>
 | |
|                 <button class="btn btn-default" onclick="resetSearch()">重置</button>
 | |
|             </div>
 | |
|         </div>
 | |
| 
 | |
|         <!-- 表格区域 -->
 | |
|         <div class="table-section">
 | |
|             <div class="table-wrapper">
 | |
|                 <table class="permissions-table">
 | |
|                     <thead>
 | |
|                         <tr>
 | |
|                             <th>权限编码</th>
 | |
|                             <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>
 | |
| 
 | |
|     <!-- 权限弹窗 -->
 | |
|     <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>
 | |
|                     <div class="form-group">
 | |
|                         <label for="visibleRoles">可见身份</label>
 | |
|                         <select id="visibleRoles" name="visibleRoles" multiple size="5">
 | |
|                             <option value="经营者">经营者</option>
 | |
|                             <option value="商户">商户</option>
 | |
|                             <option value="代理商">代理商</option>
 | |
|                             <option value="分销商">分销商</option>
 | |
|                             <option value="品牌管理员">品牌管理员</option>
 | |
|                         </select>
 | |
|                         <small>按住 Ctrl 键可多选</small>
 | |
|                     </div>
 | |
|                 </form>
 | |
|             </div>
 | |
|             <div class="modal-footer">
 | |
|                 <button type="button" class="btn btn-default" 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',
 | |
|                     visibleRoles: ['经营者', '商户', '代理商']
 | |
|                 },
 | |
|                 {
 | |
|                     id: 2,
 | |
|                     code: 'SYSTEM_USER',
 | |
|                     name: '用户管理',
 | |
|                     description: '用户管理功能',
 | |
|                     parentCode: 'SYSTEM',
 | |
|                     type: 'menu',
 | |
|                     visibleRoles: ['经营者', '商户']
 | |
|                 },
 | |
|                 {
 | |
|                     id: 3,
 | |
|                     code: 'SYSTEM_USER_ADD',
 | |
|                     name: '添加用户',
 | |
|                     description: '添加新用户按钮',
 | |
|                     parentCode: 'SYSTEM_USER',
 | |
|                     type: 'button',
 | |
|                     visibleRoles: ['经营者']
 | |
|                 },
 | |
|                 {
 | |
|                     id: 4,
 | |
|                     code: 'SYSTEM_USER_EDIT',
 | |
|                     name: '编辑用户',
 | |
|                     description: '编辑用户信息按钮',
 | |
|                     parentCode: 'SYSTEM_USER',
 | |
|                     type: 'button',
 | |
|                     visibleRoles: ['经营者', '代理商']
 | |
|                 },
 | |
|                 {
 | |
|                     id: 5,
 | |
|                     code: 'SYSTEM_ROLE',
 | |
|                     name: '角色管理',
 | |
|                     description: '角色管理功能',
 | |
|                     parentCode: 'SYSTEM',
 | |
|                     type: 'menu',
 | |
|                     visibleRoles: ['经营者']
 | |
|                 },
 | |
|                 {
 | |
|                     id: 6,
 | |
|                     code: 'CONTENT',
 | |
|                     name: '内容管理',
 | |
|                     description: '内容管理模块',
 | |
|                     parentCode: '',
 | |
|                     type: 'menu',
 | |
|                     visibleRoles: ['商户', '代理商', '分销商']
 | |
|                 }
 | |
|             ];
 | |
|             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;
 | |
|             let rowClass = 'permission-row';
 | |
| 
 | |
|             if (isParent) {
 | |
|                 rowClass += ' parent-row';
 | |
|             } else {
 | |
|                 rowClass += ' child-row level-' + level;
 | |
|             }
 | |
| 
 | |
|             let html = `
 | |
|                 <tr class="${rowClass}"
 | |
|                     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>${item.visibleRoles ? item.visibleRoles.join(', ') : '-'}</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 parentCode = row.dataset.code;
 | |
|             let nextRow = row.nextElementSibling;
 | |
| 
 | |
|             const isExpanded = btn.textContent === '▼';
 | |
|             btn.textContent = isExpanded ? '▶' : '▼';
 | |
|             btn.classList.toggle('collapsed', isExpanded);
 | |
| 
 | |
|             // 找到这个权限的所有直接和间接子权限
 | |
|             const childCodes = getChildPermissionCodes(parentCode);
 | |
| 
 | |
|             // 切换相关子行的显示状态
 | |
|             while (nextRow) {
 | |
|                 const nextRowCode = nextRow.dataset.code;
 | |
|                 const nextRowParent = permissions.find(p => p.code === nextRowCode)?.parentCode;
 | |
| 
 | |
|                 // 如果当前行是目标权限的子权限(直接或间接)
 | |
|                 if (childCodes.includes(nextRowCode)) {
 | |
|                     nextRow.style.display = isExpanded ? 'none' : 'table-row';
 | |
|                 } else if (!isChildOfAny(nextRowCode, childCodes)) {
 | |
|                     // 如果不是子权限,说明已到达同级或上级权限,停止处理
 | |
|                     break;
 | |
|                 }
 | |
| 
 | |
|                 nextRow = nextRow.nextElementSibling;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // 获取某个权限的所有子权限编码(递归)
 | |
|         function getChildPermissionCodes(parentCode) {
 | |
|             const children = [];
 | |
| 
 | |
|             function findChildren(code) {
 | |
|                 permissions.forEach(p => {
 | |
|                     if (p.parentCode === code) {
 | |
|                         children.push(p.code);
 | |
|                         findChildren(p.code); // 递归查找子权限
 | |
|                     }
 | |
|                 });
 | |
|             }
 | |
| 
 | |
|             findChildren(parentCode);
 | |
|             return children;
 | |
|         }
 | |
| 
 | |
|         // 检查某个权限是否为指定权限列表中任一权限的子权限
 | |
|         function isChildOfAny(code, parentCodes) {
 | |
|             const permission = permissions.find(p => p.code === code);
 | |
|             if (!permission) return false;
 | |
| 
 | |
|             return parentCodes.includes(permission.parentCode) ||
 | |
|                    (permission.parentCode && isChildOfAny(permission.parentCode, parentCodes));
 | |
|         }
 | |
| 
 | |
|         // 显示右键菜单
 | |
|         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;
 | |
| 
 | |
|             // 设置可见身份选项
 | |
|             const visibleRolesSelect = document.getElementById('visibleRoles');
 | |
|             Array.from(visibleRolesSelect.options).forEach(option => {
 | |
|                 option.selected = permission.visibleRoles && permission.visibleRoles.includes(option.value);
 | |
|             });
 | |
| 
 | |
|             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');
 | |
| 
 | |
|             // 获取可见身份选项
 | |
|             const visibleRolesSelect = document.getElementById('visibleRoles');
 | |
|             const selectedRoles = Array.from(visibleRolesSelect.selectedOptions).map(option => option.value);
 | |
| 
 | |
|             // 验证
 | |
|             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,
 | |
|                 visibleRoles: selectedRoles
 | |
|             };
 | |
| 
 | |
|             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>
 |