layui.define(['http', 'setter', 'auth', 'upload', 'form', 'layer', 'table'], function (exports) { var table = layui.table, form = layui.form, layer = layui.layer, $ = layui.$, upload = layui.upload, http = layui.http , auth = layui.auth; var _savedPerms = (layui.data('backend').permission) || []; auth.init(_savedPerms); auth.setController('addon'); var ADDON_DEV = window.ADDON_DEV || 0; // 清空后台侧栏菜单的本地缓存(Layui data 存储于 setter.appName 下,键为 'menus')。 // 插件卸载/安装/启停会改变 admin_power 菜单,必须清掉缓存让侧栏重新向后端拉取, // 否则侧栏会一直显示(或一直缺)已变更插件的菜单。 function clearMenuCache() { var appName = (layui.setter && layui.setter.appName) || 'backend'; layui.data(appName, { key: 'menus', remove: true }); // 兼容旧键名(历史版本曾写入 'menu') layui.data(appName, { key: 'menu', remove: true }); } // ===== 渲染核心表格 ===== var { access_token, refresh_token } = layui.setter.getTokens(); table.render({ elem: '#dataTable' , url: "index" // 此处应填写你真实的后端接口地址 , cols: [[ { field: 'title', title: '插件名称', minWidth: 180 }, { field: 'name', title: '标识', width: 120 }, { field: 'author', title: '开发者', width: 120 } , { field: 'version', title: '版本号', width: 100, align: 'center' } , { field: 'description', title: '功能描述', minWidth: 250 } , { field: 'status', title: '运行状态', width: 110, align: 'center', templet: '#switchTpl' } // 绑定开关模板 , { fixed: 'right', title: '操作', toolbar: '#operateTpl', width: 300, align: 'right' } // 绑定操作栏模板 ]] , page: true // 开启分页 , limit: 10 , limits: [10, 20, 50] , skin: 'line' // 设定表格风格为行边框风格 , response: { statusName: 'code' // 状态码字段 , statusCode: 0 // 成功状态码 , msgName: 'message' // 提示信息字段(后端返回 message) , countName: 'count' // 数据总数字段 , dataName: 'data' // 数据列表字段 } , done: function (res, curr, count) { var view = $('#dataTable').next('.layui-table-view'); view.find('[data-perm]').each(function () { var perm = $(this).attr('data-perm'); if (perm && !auth.has(perm)) { $(this).remove(); } }); $('.layui-btn[data-perm]').each(function () { var perm = $(this).attr('data-perm'); if (perm && !auth.has(perm)) { $(this).remove(); } }); // 开发模式关闭时隐藏操作列的「设计」按钮 // if (!ADDON_DEV) { $('.dev-design').remove(); } } }); // ===== 事件绑定(提前到这里:即便下方 upload/form 初始化出错,行内按钮依然可用) ===== table.on('toolbar(dataTable)', function (elem) { switch (elem.event) { case 'dataCreate': active.dataCreate({ pid: 0, type: 1 }); break; case 'dataRecybin': active.dataRecybin(); break; } }); table.on('tool(dataTable)', function (elem) { var data = elem.data; // 插件标识:优先用 name,缺失时回退到 id(后端 index 两种都返回) var addonName = data.name || data.id || ''; switch (elem.event) { case 'update': active.dataEdit(data); break; case 'config': if (!addonName) { layer.msg('缺少插件标识', { icon: 2 }); break; } location.href = 'setting?addon=' + encodeURIComponent(addonName); break; case 'uninstall': if (!addonName) { layer.msg('缺少插件标识', { icon: 2 }); break; } active.uninstall(addonName); break; case 'more': var that = this; var dropdown = layui.dropdown; // 水平对齐方式 dropdown.render({ elem: that, align: 'center', // align 已配置在元素 `lay-options` 属性上 show: true,accordion : true, data: [{ title: '设计', id: 'design' }, { title: '打包', id: 'pack' }, { title: '发布', id: 'submitOfficial' }], click: function (data, othis) { switch (data.id) { case 'pack': // 触发后端 addon::pack 下载该插件的 zip 包(addon/temp/-.zip) if (!addonName) { layer.msg('缺少插件标识', { icon: 2 }); break; } window.location.href = 'pack?name=' + encodeURIComponent(addonName); break; case 'design': if (!addonName) { layer.msg('缺少插件标识', { icon: 2 }); break; } location.href = 'design?addon=' + encodeURIComponent(addonName); break; case 'submitOfficial': // 发布到官方市场:打包本机已装插件并 POST 到官方提交接口,进入待审核 if (!addonName) { layer.msg('缺少插件标识', { icon: 2 }); break; } layer.confirm('确定将「' + (data.title || addonName) + '」发布到官方市场吗?\n提交后需官方审核通过才公开上架。', function (index) { layer.close(index); var loadIdx = layer.load(1, { shade: [0.3, '#fff'] }); http.post('submitOfficial', { name: addonName }) .then(function (res) { layer.close(loadIdx); if (res.code === 0) { layer.msg(res.message || '已提交,等待官方审核', { icon: 1 }); } else { layer.msg(res.message || res.msg || '提交失败', { icon: 2 }); } }) .catch(function () { layer.close(loadIdx); layer.msg('请求失败', { icon: 2 }); }); }); break; default: break; } }, }); break; case 'default': console.warn('未处理的操作事件:', elem.event, data); } }); // 顶部功能按钮 + 行内操作共用 var active = { uninstall: function (name) { layer.prompt({ formType: 1 , title: '敏感操作,请验证口令' }, function (value, index) { layer.close(index); http.delete('uninstall', { name: name }) .then((res) => { if (res.code === 0) { layer.close(index); clearMenuCache(); // 插件菜单已删除,清掉侧栏菜单缓存 location.reload(); // 重新加载整页,侧栏立即刷新 } else { layer.msg(res.msg || '操作失败'); } }); }); }, batchDel: function () { // 批量卸载 var checkStatus = table.checkStatus('dataTable'); var data = checkStatus.data; if (data.length === 0) { layer.msg('请至少选中一项进行卸载', { icon: 0 }); return; } layer.confirm('确定要批量卸载选中的 ' + data.length + ' 个插件吗?', function (index) { layer.msg('正在批量卸载...', { icon: 1 }); table.reload('dataTable'); layer.close(index); }); }, refresh: function () { // 刷新表格 table.reload('dataTable'); layer.msg('已刷新列表', { icon: 1 }); } }; $('.layui-btn').on('click', function () { var type = $(this).data('type'); active[type] && active[type].call(this); }); console.log(layui.setter.getTokens()); var inst = upload.render({ elem: '#buttonInstall', field: 'file', url: "upload", // 上传接口地址 accept: 'file', // 只允许上传文件 // 注意:不要设置 acceptMime。该属性会让浏览器文件选择框按 MIME 过滤, // 而 zip 在本机常被识别为 application/x-zip-compressed / application/octet-stream, // 导致选择框中插件包被置灰、无法选中。仅用 exts 在选完后校验后缀即可。 exts: 'zip', // 仅允许 zip 插件包(后端 AddonService 只支持 zip) headers: { 'Authorization': `Bearer ${access_token}` }, // 鉴权 done: function (res) { // 上传成功的回调 if (res.code == 0) { layer.msg('插件安装成功!', { icon: 1 }); clearMenuCache(); // 插件菜单已变化,清掉侧栏菜单缓存 location.reload(); // 重新加载整页,侧栏立即刷新 } else { layer.msg('安装失败: ' + res.message, { icon: 2 }); } }, error: function () { // 上传失败的回调 // 当上传失败时,可在此处生成「重新上传」按钮,并执行该方法重新触发上传提交 /* $('#btn').on('click', function(){ inst.upload(); }) */ } }); // ===== 监听顶部搜索表单提交 ===== form.on('submit(searchBtn)', function (data) { var field = data.field; // 获取搜索表单的值 // 重载表格,将搜索条件传给后端 table.reload('dataTable', { where: field, page: { curr: 1 } // 搜索后回到第一页 }); return false; // 阻止表单默认跳转 }); // ===== 监听状态开关(启用/停用)的切换 ===== form.on('switch(statusSwitch)', function (obj) { var name = this.value; // value 绑定的是插件名 d.id var status = obj.elem.checked ? 1 : 0; // 根据开关状态赋值 var action = status ? 'enable' : 'disable'; var that = obj.othis; http.post('toggle', { name: name, action: action }) .then(function (res) { if (res.code === 0) { layer.msg(status ? '已启用' : '已停用', { icon: 1 }); clearMenuCache(); // 菜单可见性已变化,清掉侧栏菜单缓存(下次导航即刷新) } else { layer.msg(res.message || '操作失败', { icon: 2 }); that.find('input').prop('checked', !status); form.render(); } }) .catch(function () { layer.msg('请求失败', { icon: 2 }); that.find('input').prop('checked', !status); form.render(); }); }); exports('addon', {}); // 输出模块接口 });