/** * ============================================= * 权限管理模块 - Layui版本 * Auth + RBAC 权限控制完整实现 * ============================================= * * 使用说明: * 1. 放置在layui的modules目录下 * 2. 使用:layui.use('permission', function(mod){...}) * 3. 或者直接:var permission = layui.permission; * * @version 2.0.0 - Layui Module * @author Permission Module * @date 2024-01-01 */ layui.define(function (exports) { 'use strict'; /** * ============================================= * 权限配置管理器 * ============================================= */ const PermissionConfig = (function () { // 私有变量 const _defaultToolbar = { create: { title: '创建资源', layEvent: 'dataCreate', icon: 'layui-icon-add-1', permission: 'create', color: 'layui-btn-normal', size: '', show: true }, recycle: { title: '回收站', layEvent: 'dataRecycle', icon: 'layui-icon-refresh', permission: 'recycle', color: 'layui-btn-warm', size: '', show: true }, filter: { title: '筛选', layEvent: 'filter', icon: 'layui-icon-search', permission: 'filter', color: 'layui-btn-primary', size: '', show: true }, export: { title: '导出', layEvent: 'export', icon: 'layui-icon-download-circle', permission: 'export', color: 'layui-btn-primary', size: '', show: true }, print: { title: '打印', layEvent: 'print', icon: 'layui-icon-print', permission: 'print', color: 'layui-btn-primary', size: '', show: true }, refresh: { title: '刷新', layEvent: 'refresh', icon: 'layui-icon-refresh', permission: 'refresh', color: 'layui-btn-primary', size: '', show: true }, batchDelete: { title: '批量删除', layEvent: 'batchDelete', icon: 'layui-icon-delete', permission: 'batch_delete', color: 'layui-btn-danger', size: '', show: true }, import: { title: '导入', layEvent: 'import', icon: 'layui-icon-upload-circle', permission: 'import', color: 'layui-btn-primary', size: '', show: true } }; const _rowToolbar = { view: { title: '查看', layEvent: 'dataView', icon: 'layui-icon-eye', permission: 'view', color: 'layui-btn-primary layui-btn-xs', size: 'layui-btn-xs', show: true }, edit: { title: '编辑', layEvent: 'dataEdit', icon: 'layui-icon-edit', permission: 'edit', color: 'layui-btn-normal layui-btn-xs', size: 'layui-btn-xs', show: true }, delete: { title: '删除', layEvent: 'dataDelete', icon: 'layui-icon-delete', permission: 'delete', color: 'layui-btn-danger layui-btn-xs', size: 'layui-btn-xs', show: true }, copy: { title: '复制', layEvent: 'dataCopy', icon: 'layui-icon-file-b', permission: 'copy', color: 'layui-btn-warm layui-btn-xs', size: 'layui-btn-xs', show: true }, enable: { title: '启用', layEvent: 'dataEnable', icon: 'layui-icon-play', permission: 'enable', color: 'layui-btn-normal layui-btn-xs', size: 'layui-btn-xs', show: true }, disable: { title: '禁用', layEvent: 'dataDisable', icon: 'layui-icon-pause', permission: 'disable', color: 'layui-btn-warm layui-btn-xs', size: 'layui-btn-xs', show: true }, audit: { title: '审核', layEvent: 'dataAudit', icon: 'layui-icon-ok-circle', permission: 'audit', color: 'layui-btn-normal layui-btn-xs', size: 'layui-btn-xs', show: true }, unaudit: { title: '取消审核', layEvent: 'dataUnaudit', icon: 'layui-icon-close', permission: 'unaudit', color: 'layui-btn-warm layui-btn-xs', size: 'layui-btn-xs', show: true } }; const _customButtons = { toolbar: {}, row: {} }; // 工具函数 const _isArray = function (obj) { return Object.prototype.toString.call(obj) === '[object Array]'; }; // 公共方法 return { /** * 获取默认工具条配置 */ getDefaultToolbar: function () { return Object.assign({}, _defaultToolbar); }, /** * 获取行工具条配置 */ getRowToolbar: function () { return Object.assign({}, _rowToolbar); }, /** * 注册自定义按钮 * @param {String} type - 按钮类型 (toolbar/row) * @param {String} key - 按钮唯一标识 * @param {Object} config - 按钮配置 */ registerButton: function (type, key, config) { if (!['toolbar', 'row'].includes(type)) { console.error('按钮类型必须是 toolbar 或 row'); return false; } if (!_customButtons[type]) { _customButtons[type] = {}; } _customButtons[type][key] = Object.assign({ title: key, layEvent: key, icon: 'layui-icon-more', permission: key, color: 'layui-btn-primary', size: '', show: true }, config); return true; }, /** * 获取工具条配置 * @param {String} type - toolbar/row * @param {Array} keys - 需要的按钮key列表 * @param {Object} customConfig - 自定义配置(覆盖默认配置) * @returns {Array} 配置数组 */ getToolbarConfig: function (type, keys = [], customConfig = {}) { const baseConfig = type === 'toolbar' ? _defaultToolbar : _rowToolbar; const customBtns = _customButtons[type] || {}; // 合并基础配置和自定义配置 const allConfig = Object.assign({}, baseConfig, customBtns); // 应用自定义配置覆盖 Object.keys(customConfig).forEach(key => { if (allConfig[key]) { allConfig[key] = Object.assign(allConfig[key], customConfig[key]); } }); // 如果指定了keys,只返回指定的按钮 if (keys.length > 0) { return keys.map(key => allConfig[key]).filter(Boolean); } // 返回所有配置 return Object.values(allConfig).filter(btn => btn.show !== false); }, /** * 获取单个按钮配置 * @param {String} type - toolbar/row * @param {String} key - 按钮key * @returns {Object} 按钮配置 */ getButtonConfig: function (type, key) { const config = this.getToolbarConfig(type); return config.find(btn => btn.layEvent === key || (btn.permission && btn.permission.includes(key))); }, /** * 批量注册按钮 * @param {String} type - 按钮类型 * @param {Object} buttons - 按钮配置对象 */ registerButtons: function (type, buttons) { Object.keys(buttons).forEach(key => { this.registerButton(type, key, buttons[key]); }); }, /** * 清除自定义按钮 * @param {String} type - 按钮类型 * @param {String} key - 按钮key,不传则清除所有 */ clearCustomButtons: function (type, key) { if (key) { delete _customButtons[type]?.[key]; } else { _customButtons[type] = {}; } } }; })(); /** * ============================================= * 权限验证工具类 * ============================================= */ const Auth = (function () { // 私有变量 let _permissions = []; let _permissionCache = {}; let _user = null; let _controller = null; // 当前页面对应的控制器(用于裸动作 token 解析) // 工具函数 const _isArray = function (obj) { return Object.prototype.toString.call(obj) === '[object Array]'; }; const _isString = function (obj) { return typeof obj === 'string'; }; // 前端短词 → 后端动作 同义词(按钮配置常用 create,权限 key 用 add) const _synonyms = { create: 'add', recycle: 'recyclebin', batch_delete: 'delete', batchDelete: 'delete' }; const _splitPermissions = function (perm) { if (_isArray(perm)) return perm; if (_isString(perm)) return perm.split(',').map(p => p.trim()); return []; }; // 公共方法 return { /** * 初始化权限 * @param {Array} permissions - 用户权限列表 * @param {Object} user - 用户信息 */ init: function (permissions = [], user = null) { _permissions = permissions || []; _user = user; _controller = null; this.clearCache(); }, /** * 获取当前用户 * @returns {Object} 用户信息 */ getUser: function () { return _user; }, /** * 设置当前用户 * @param {Object} user - 用户信息 */ setUser: function (user) { _user = user; }, /** * 获取权限列表 * @returns {Array} 权限列表 */ getPermissions: function () { return _permissions; }, /** * 设置当前页面对应的控制器(用于裸动作 token 解析,如 has('view') → admin:view) */ setController: function (controller) { _controller = controller || null; this.clearCache(); return this; }, /** * 检查单个权限 * 支持: * - 精确 key:admin:edit * - 控制器通配:admin:*(覆盖该控制器下所有动作) * - 超级管理员:* / *:* * - 裸动作 token(结合 setController):view → admin:view,create → admin:add(同义词) * @param {String} permission - 权限标识 * @returns {Boolean} */ has: function (permission) { if (!permission) return true; // 使用缓存 if (_permissionCache[permission] !== undefined) { return _permissionCache[permission]; } const perms = _splitPermissions(permission); const result = perms.some(p => this._match(p)); _permissionCache[permission] = result; return result; }, /** * 单个权限标识匹配(内部) */ _match: function (p) { if (!p) return true; // 精确匹配 if (_permissions.indexOf(p) > -1) return true; // 超级管理员放行 if (_permissions.indexOf('*') > -1 || _permissions.indexOf('*:*') > -1) return true; // 完整 key:controller:action / controller:* if (p.indexOf(':') > -1) { const parts = p.split(':'); const ctrl = parts[0]; const act = parts[1]; if (act === '*') { return _permissions.indexOf(ctrl + ':*') > -1; } return _permissions.indexOf(ctrl + ':' + act) > -1 || _permissions.indexOf(ctrl + ':*') > -1; } // 裸动作 token:结合当前控制器解析(含同义词) const action = _synonyms[p] || p; if (_controller) { if (_permissions.indexOf(_controller + ':*') > -1) return true; if (_permissions.indexOf(_controller + ':' + action) > -1) return true; } return false; }, /** * 检查多个权限(满足一个即可) * @param {Array} permissions - 权限列表 * @returns {Boolean} */ hasAny: function (permissions) { if (!_isArray(permissions) || permissions.length === 0) return false; return permissions.some(p => this.has(p)); }, /** * 检查所有权限 * @param {Array} permissions - 权限列表 * @returns {Boolean} */ hasAll: function (permissions) { if (!_isArray(permissions) || permissions.length === 0) return false; return permissions.every(p => this.has(p)); }, /** * 检查权限并返回权限标识 * @param {String|Array} permissions - 权限标识 * @returns {String|Boolean} 有权限返回权限标识,无权限返回false */ check: function (permissions) { if (_isArray(permissions)) { for (let p of permissions) { if (this.has(p)) { return p; } } return false; } return this.has(permissions) ? permissions : false; }, /** * 过滤有权限的按钮 * @param {Array} buttons - 按钮配置数组 * @returns {Array} 有权限的按钮数组 */ filterButtons: function (buttons) { if (!_isArray(buttons)) return []; return buttons.filter(btn => { // 如果没有设置permission,默认显示 if (!btn.permission) return true; // 支持多个权限,用逗号分隔 const perms = _splitPermissions(btn.permission); return this.hasAny(perms); }); }, /** * 生成工具条HTML * @param {Array} buttons - 按钮配置数组 * @param {String} containerClass - 容器样式类 * @param {Object} options - 额外选项 * @returns {String} HTML字符串 */ renderToolbar: function (buttons, containerClass = 'layui-btn-container', options = {}) { const filteredButtons = this.filterButtons(buttons); if (filteredButtons.length === 0) return ''; console.log('🔧 渲染工具条 - 需要在项目中实现',filteredButtons); // const html = filteredButtons.map(btn => { // const color = btn.color || 'layui-btn-primary'; // const size = btn.size || ''; // const icon = btn.icon ? `` : ''; // const disabled = btn.disabled ? 'disabled' : ''; // const style = btn.style ? `style="${btn.style}"` : ''; // return ``; // }).join(''); // const containerStyle = options.containerStyle ? `style="${options.containerStyle}"` : ''; // return `