chore: 重写初始提交(清空历史,整理后全量提交)

This commit is contained in:
ywxapp
2026-08-16 16:54:14 +08:00
commit 6c1a106bc1
1808 changed files with 238144 additions and 0 deletions
+751
View File
@@ -0,0 +1,751 @@
/**
* =============================================
* 权限管理模块 - 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;
},
/**
* 检查单个权限
* 支持:
* - 精确 keyadmin:edit
* - 控制器通配:admin:*(覆盖该控制器下所有动作)
* - 超级管理员:* / *:*
* - 裸动作 token(结合 setController):view → admin:viewcreate → 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;
// 完整 keycontroller: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 ? `<i class="${btn.icon}"></i>` : '';
// const disabled = btn.disabled ? 'disabled' : '';
// const style = btn.style ? `style="${btn.style}"` : '';
// return `<button class="layui-btn ${color} ${size}" lay-event="${btn.layEvent}" title="${btn.title}" ${disabled} ${style}>
// ${icon}${btn.title}
// </button>`;
// }).join('');
// const containerStyle = options.containerStyle ? `style="${options.containerStyle}"` : '';
// return `<div class="${containerClass}" ${containerStyle}>${html}</div>`;
},
/**
* 生成行工具条HTML
* @param {Array} buttons - 按钮配置数组
* @param {Object} options - 额外选项
* @returns {String} HTML字符串
*/
renderRowToolbar: function (buttons, options = {}) {
const filteredButtons = this.filterButtons(buttons);
if (filteredButtons.length === 0) return '';
const html = filteredButtons.map(btn => {
const color = btn.color || 'layui-btn-primary layui-btn-xs';
const icon = btn.icon ? `<i class="${btn.icon}"></i>` : '';
const disabled = btn.disabled ? 'disabled' : '';
const style = btn.style ? `style="${btn.style}"` : '';
return `<a class="layui-btn ${color}" lay-event="${btn.layEvent}" title="${btn.title}" ${disabled} ${style}>
${icon}
</a>`;
}).join('');
return `<div class="layui-btn-group">${html}</div>`;
},
/**
* 清除缓存
*/
clearCache: function () {
_permissionCache = {};
},
/**
* 批量检查权限
* @param {Array} permissions - 权限列表
* @returns {Object} 权限检查结果对象
*/
batchCheck: function (permissions) {
if (!_isArray(permissions)) return {};
return permissions.reduce((result, perm) => {
result[perm] = this.has(perm);
return result;
}, {});
},
/**
* 预加载常用权限
* @param {Array} permissions - 常用权限列表
*/
preload: function (permissions = ['create', 'edit', 'delete', 'view']) {
permissions.forEach(perm => this.has(perm));
}
};
})();
/**
* =============================================
* 动态权限更新管理器
* =============================================
*/
const PermissionManager = (function () {
// 私有变量
const _listeners = [];
let _refreshTimeout = null;
// 公共方法
return {
/**
* 添加权限变更监听器
* @param {Function} callback - 回调函数
* @param {String} id - 监听器ID(用于移除)
*/
onPermissionChange: function (callback, id = null) {
_listeners.push({ callback, id });
},
/**
* 移除权限变更监听器
* @param {String} id - 监听器ID
*/
offPermissionChange: function (id) {
const index = _listeners.findIndex(l => l.id === id);
if (index !== -1) {
_listeners.splice(index, 1);
}
},
/**
* 触发权限变更
* @param {Array} newPermissions - 新的权限列表
* @param {Object} user - 用户信息
*/
triggerChange: function (newPermissions, user = null) {
// 更新权限
Auth.init(newPermissions, user);
// 保存到localStorage
if (typeof localStorage !== 'undefined') {
localStorage.setItem('permissions', JSON.stringify(newPermissions));
if (user) {
localStorage.setItem('user', JSON.stringify(user));
}
}
// 通知所有监听器
_listeners.forEach(({ callback }) => {
try {
callback(newPermissions, user);
} catch (e) {
console.error('权限变更监听器执行出错:', e);
}
});
// 防抖刷新表格
clearTimeout(_refreshTimeout);
_refreshTimeout = setTimeout(() => {
this.refreshTables();
}, 100);
},
/**
* 刷新所有表格
*/
refreshTables: function () {
console.log('🔄 刷新表格 - 需要在项目中实现');
// 触发自定义事件
const event = new CustomEvent('permissionChanged', {
detail: { permissions: Auth.getPermissions() }
});
if (typeof window !== 'undefined') {
window.dispatchEvent(event);
}
},
/**
* 从服务器同步最新权限
* @param {String} url - 权限接口地址
* @returns {Promise} 同步结果
*/
syncPermissions: function (url = '/api/user/permissions') {
// return new Promise((resolve, reject) => {
// if (typeof $ !== 'undefined' && $.ajax) {
// $.get(url, function (res) {
// if (res.code === 200 || res.success) {
// this.triggerChange(res.data.permissions || res.data, res.data.user);
// resolve(true);
// } else {
// reject(new Error(res.msg || '同步权限失败'));
// }
// }.bind(this)).fail(function (xhr) {
// reject(new Error('网络请求失败: ' + xhr.statusText));
// });
// } else if (typeof fetch !== 'undefined') {
// fetch(url)
// .then(res => res.json())
// .then(res => {
// if (res.code === 200 || res.success) {
// this.triggerChange(res.data.permissions || res.data, res.data.user);
// resolve(true);
// } else {
// reject(new Error(res.msg || '同步权限失败'));
// }
// })
// .catch(err => reject(err));
// } else {
// reject(new Error('不支持的HTTP请求方式'));
// }
// }.bind(this));
},
/**
* 清除所有监听器
*/
clearListeners: function () {
_listeners.length = 0;
}
};
})();
/**
* =============================================
* 权限按钮组件
* =============================================
*/
class AuthButton extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this._clickHandler = this._handleClick.bind(this);
}
static get observedAttributes() {
return ['permission', 'text', 'icon', 'type', 'size', 'disabled'];
}
connectedCallback() {
this.render();
}
disconnectedCallback() {
this.shadowRoot.removeEventListener('click', this._clickHandler);
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
this.render();
}
}
_handleClick(e) {
if (!this.hasAttribute('disabled')) {
this.dispatchEvent(new CustomEvent('auth-click', {
bubbles: true,
composed: true,
detail: { button: this }
}));
}
}
render() {
const permission = this.getAttribute('permission');
const text = this.getAttribute('text') || '按钮';
const icon = this.getAttribute('icon') || '';
const type = this.getAttribute('type') || 'primary';
const size = this.getAttribute('size') || '';
const disabled = this.hasAttribute('disabled');
const hasPermission = !permission || Auth.has(permission);
// 无权限时隐藏
if (!hasPermission) {
this.shadowRoot.innerHTML = '';
this.style.display = 'none';
return;
}
this.style.display = '';
const iconHtml = icon ? `<i class="${icon}"></i>` : '';
const btnClass = `layui-btn layui-btn-${type} ${size}`;
const disabledAttr = disabled ? 'disabled' : '';
this.shadowRoot.innerHTML = `
<style>
:host {
display: inline-block;
}
.layui-btn {
display: inline-block;
height: 38px;
line-height: 38px;
padding: 0 18px;
background-color: #009688;
color: #fff;
white-space: nowrap;
text-align: center;
font-size: 14px;
border: none;
border-radius: 2px;
cursor: pointer;
box-sizing: border-box;
text-decoration: none;
}
.layui-btn:hover {
opacity: 0.9;
}
.layui-btn[disabled] {
opacity: 0.5;
cursor: not-allowed;
}
.layui-btn-xs {
height: 26px;
line-height: 26px;
padding: 0 10px;
font-size: 12px;
}
.layui-btn-sm {
height: 30px;
line-height: 30px;
padding: 0 10px;
font-size: 12px;
}
.layui-btn-lg {
height: 44px;
line-height: 44px;
padding: 0 25px;
font-size: 16px;
}
.layui-btn-primary {
background-color: #fff;
color: #333;
border: 1px solid #d2d2d2;
}
.layui-btn-normal {
background-color: #1E9FFF;
}
.layui-btn-warm {
background-color: #FFB800;
}
.layui-btn-danger {
background-color: #FF5722;
}
i {
margin-right: 5px;
}
</style>
<button class="${btnClass}" ${disabledAttr}>
${iconHtml}${text}
</button>
`;
// 添加点击事件
const btn = this.shadowRoot.querySelector('button');
btn.removeEventListener('click', this._clickHandler);
btn.addEventListener('click', this._clickHandler);
}
}
// 注册自定义元素
if (typeof customElements !== 'undefined' && !customElements.get('auth-button')) {
customElements.define('auth-button', AuthButton);
}
/**
* =============================================
* 导出模块
* =============================================
*/
exports('auth', {
// 核心对象auth
Auth: Auth,
PermissionConfig: PermissionConfig,
PermissionManager: PermissionManager,
// 便捷方法(直接暴露常用方法)
init: Auth.init.bind(Auth),
setController: Auth.setController.bind(Auth),
has: Auth.has.bind(Auth),
hasAny: Auth.hasAny.bind(Auth),
hasAll: Auth.hasAll.bind(Auth),
getPermissions: Auth.getPermissions.bind(Auth),
renderToolbar: Auth.renderToolbar.bind(Auth),
renderRowToolbar: Auth.renderRowToolbar.bind(Auth),
filterButtons: Auth.filterButtons.bind(Auth),
// 配置方法
getToolbarConfig: PermissionConfig.getToolbarConfig.bind(PermissionConfig),
registerButton: PermissionConfig.registerButton.bind(PermissionConfig),
registerButtons: PermissionConfig.registerButtons.bind(PermissionConfig),
// 管理器方法
onPermissionChange: PermissionManager.onPermissionChange.bind(PermissionManager),
triggerChange: PermissionManager.triggerChange.bind(PermissionManager),
syncPermissions: PermissionManager.syncPermissions.bind(PermissionManager),
// 按钮组件
AuthButton: AuthButton
});
});