260 lines
12 KiB
JavaScript
260 lines
12 KiB
JavaScript
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/<name>-<version>.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', {}); // 输出模块接口
|
||
});
|