Files
YwxAppThink/public/static/backend/modules/addon.js
T

260 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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', {}); // 输出模块接口
});