240 lines
11 KiB
HTML
240 lines
11 KiB
HTML
<div class="layui-card">
|
||
<div class="layui-card-header">已安装模板 <span id="gdTip" class="layui-badge layui-bg-blue" style="margin-left:8px;"></span></div>
|
||
<div class="layui-card-body">
|
||
<table id="tplTable" lay-filter="tplTable"></table>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="layui-card">
|
||
<div class="layui-card-header">插件模板绑定(整站皮肤按插件独立选择)</div>
|
||
<div class="layui-card-body">
|
||
<table class="layui-table">
|
||
<thead>
|
||
<tr><th>插件</th><th>当前激活</th><th>选择模板</th><th>操作</th></tr>
|
||
</thead>
|
||
<tbody id="bindBody"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="layui-card">
|
||
<div class="layui-card-header">安装新模板(上传 zip 包)</div>
|
||
<div class="layui-card-body">
|
||
<button type="button" class="layui-btn" id="uploadBtn"><i class="layui-icon"></i>上传模板包</button>
|
||
<div style="color:#999;margin-top:8px;line-height:1.6;">
|
||
zip 内须含 <code>templates/<name>/template.json</code> 与 <code>static/</code> 资源,安装后落到
|
||
<code>templates/<name>/</code> 与 <code>public/static/templates/<name>/</code>。
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="layui-card">
|
||
<div class="layui-card-header">配置备份(导入 / 导出)</div>
|
||
<div class="layui-card-body">
|
||
<button type="button" class="layui-btn layui-btn-normal" id="exportBtn"><i class="layui-icon"></i>导出配置</button>
|
||
<button type="button" class="layui-btn" id="importBtn"><i class="layui-icon"></i>导入配置</button>
|
||
<div style="color:#999;margin-top:8px;line-height:1.6;">
|
||
导出含「插件模板绑定 + 界面设置 + 配色覆盖」的 JSON;导入会安全跳过未安装模板的绑定,可跨站点克隆皮肤方案。
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<script type="text/html" id="tplOperate">
|
||
<a class="layui-btn layui-btn-xs" lay-event="setDefault">设默认</a>
|
||
<a class="layui-btn layui-btn-xs {{d.enabled ? 'layui-btn-warm' : 'layui-btn-normal'}}" lay-event="toggle">{{d.enabled ? '禁用' : '启用'}}</a>
|
||
{{# if (d.variable_defs && Object.keys(d.variable_defs).length > 0) { }}
|
||
<a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="vars">配色</a>
|
||
{{# } }}
|
||
<a class="layui-btn layui-btn-xs layui-btn-danger" lay-event="uninstall">卸载</a>
|
||
</script>
|
||
<script>
|
||
layui.use(['table', 'form', 'layer', 'upload', 'jquery'], function () {
|
||
var table = layui.table, form = layui.form, layer = layui.layer, upload = layui.upload, $ = layui.jquery;
|
||
var state = { installed: [], addon: [], active_map: {} };
|
||
|
||
function api(url, data) {
|
||
return $.ajax({ url: url, type: 'POST', data: data || {}, dataType: 'json' });
|
||
}
|
||
|
||
function load() {
|
||
api('index').then(function (res) {
|
||
if (res.code !== 0) { layer.msg(res.message || '加载失败', { icon: 2 }); return; }
|
||
state.installed = res.data.installed || [];
|
||
state.addon = res.data.addon || [];
|
||
state.active_map = res.data.active_map || {};
|
||
state.global_default = res.data.global_default || 'default';
|
||
$('#gdTip').text('全站默认:' + (state.global_default === 'default' ? '自带视图' : state.global_default));
|
||
renderTpl();
|
||
renderBind();
|
||
}, function () { layer.msg('请求失败', { icon: 2 }); });
|
||
}
|
||
|
||
function renderTpl() {
|
||
table.render({
|
||
elem: '#tplTable',
|
||
data: state.installed,
|
||
page: false,
|
||
cols: [[
|
||
{ field: 'preview', title: '预览', width: 90, templet: function (d) {
|
||
if (! d.preview_url) { return '<span style="color:#999">无</span>'; }
|
||
var badge = (d.preview_urls && d.preview_urls.length > 1)
|
||
? ' <span class="layui-badge layui-bg-orange" style="vertical-align:top;">' + d.preview_urls.length + '图</span>' : '';
|
||
return '<img src="' + d.preview_url + '" style="max-height:40px;cursor:zoom-in;" onclick="openPreview(\'' + d.name + '\')">' + badge;
|
||
} },
|
||
{ field: 'name', title: '标识', width: 120 },
|
||
{ field: 'title', title: '名称', minWidth: 140 },
|
||
{ field: 'version', title: '版本', width: 80 },
|
||
{ field: 'target_addon', title: '适用插件', minWidth: 120, templet: function (d) {
|
||
return (d.target_addon || []).join(', ');
|
||
} },
|
||
{ field: 'applied_to', title: '已应用', minWidth: 120, templet: function (d) {
|
||
return (d.applied_to || []).join(', ') || '<span style="color:#999">未绑定</span>';
|
||
} },
|
||
{ field: 'enabled', title: '状态', width: 80, templet: function (d) {
|
||
return d.enabled
|
||
? '<span class="layui-badge layui-bg-green">启用</span>'
|
||
: '<span class="layui-badge layui-bg-gray">禁用</span>';
|
||
} },
|
||
{ title: '操作', width: 260, toolbar: '#tplOperate', fixed: 'right' }
|
||
]]
|
||
});
|
||
}
|
||
|
||
function renderBind() {
|
||
var html = '';
|
||
state.addon.forEach(function (a) {
|
||
var cur = state.active_map[a.name] || 'default';
|
||
var opts = '<option value="default">默认(自带视图)</option>';
|
||
state.installed.forEach(function (t) {
|
||
if ((t.target_addon || []).indexOf(a.name) >= 0) {
|
||
var sel = (cur === t.name) ? 'selected' : '';
|
||
opts += '<option value="' + t.name + '" ' + sel + '>' + t.title + ' (' + t.version + ')</option>';
|
||
}
|
||
});
|
||
html += '<tr>'
|
||
+ '<td>' + a.title + ' <span style="color:#999">' + a.name + '</span></td>'
|
||
+ '<td>' + (cur === 'default' ? '默认' : cur) + '</td>'
|
||
+ '<td><select class="layui-input-inline" data-addon="' + a.name + '" style="width:220px;">' + opts + '</select></td>'
|
||
+ '<td><button type="button" class="layui-btn layui-btn-xs" onclick="saveBind(\'' + a.name + '\', this)">保存</button></td>'
|
||
+ '</tr>';
|
||
});
|
||
$('#bindBody').html(html);
|
||
form.render('select');
|
||
}
|
||
|
||
window.saveBind = function (addon, btn) {
|
||
var sel = $(btn).closest('tr').find('select').val();
|
||
api('setActive', { addon: addon, template: sel }).then(function (res) {
|
||
layer.msg(res.message || '已保存', { icon: 1 });
|
||
load();
|
||
}, function () { layer.msg('保存失败', { icon: 2 }); });
|
||
};
|
||
|
||
// 预览灯箱:多图走 layer.photos 画廊,单图直接放大
|
||
window.openPreview = function (name) {
|
||
var tpl = null;
|
||
for (var i = 0; i < state.installed.length; i++) {
|
||
if (state.installed[i].name === name) { tpl = state.installed[i]; break; }
|
||
}
|
||
if (! tpl || ! tpl.preview_urls || ! tpl.preview_urls.length) { return; }
|
||
if (tpl.preview_urls.length === 1) {
|
||
layer.open({ type: 1, title: tpl.title || name, area: 'auto',
|
||
content: '<img src="' + tpl.preview_urls[0] + '" style="max-width:90vw;max-height:80vh;">' });
|
||
} else {
|
||
var data = tpl.preview_urls.map(function (u) { return { src: u, thumb: u, alt: tpl.title || name }; });
|
||
layer.photos({ photos: { title: tpl.title || name, data: data }, anim: 5 });
|
||
}
|
||
};
|
||
|
||
table.on('tool(tplTable)', function (obj) {
|
||
if (obj.event === 'setDefault') {
|
||
api('setDefault', { template: obj.data.name }).then(function (res) {
|
||
layer.msg(res.message || '已设默认', { icon: 1 });
|
||
load();
|
||
}, function () { layer.msg('操作失败', { icon: 2 }); });
|
||
} else if (obj.event === 'toggle') {
|
||
var enabled = obj.data.enabled ? 0 : 1;
|
||
api('toggle', { name: obj.data.name, enabled: enabled }).then(function (res) {
|
||
layer.msg(res.message || '已更新', { icon: 1 });
|
||
load();
|
||
}, function () { layer.msg('操作失败', { icon: 2 }); });
|
||
} else if (obj.event === 'vars') {
|
||
openVarsModal(obj.data);
|
||
} else if (obj.event === 'uninstall') {
|
||
layer.confirm('确认卸载模板「' + obj.data.name + '」?将删除其文件并解除绑定。', function (i) {
|
||
api('uninstall', { name: obj.data.name }).then(function (res) {
|
||
layer.msg(res.message || '已卸载', { icon: 1 });
|
||
load();
|
||
}, function () { layer.msg('卸载失败', { icon: 2 }); });
|
||
});
|
||
}
|
||
});
|
||
|
||
function openVarsModal(tpl) {
|
||
var defs = tpl.variable_defs || {};
|
||
var overrides = tpl.variable_overrides || {};
|
||
var rows = '';
|
||
Object.keys(defs).forEach(function (k) {
|
||
var d = defs[k];
|
||
var cur = overrides[k] != null ? overrides[k] : (d.default || '');
|
||
rows += '<div class="layui-form-item">'
|
||
+ '<label class="layui-form-label">' + (d.label || k) + '</label>'
|
||
+ '<div class="layui-input-block">'
|
||
+ '<input type="text" class="layui-input" data-k="' + k + '" value="' + cur + '" placeholder="默认:' + (d.default || '') + '">'
|
||
+ (d.type === 'color' ? ' <span style="color:#999;font-size:12px;">颜色填 #xxxxxx</span>' : '')
|
||
+ '</div></div>';
|
||
});
|
||
if (rows === '') { rows = '<div style="padding:10px;color:#999;">该模板未声明可配置变量</div>'; }
|
||
var html = '<form class="layui-form" style="padding:15px;" id="varsForm">'
|
||
+ '<div style="color:#999;margin-bottom:10px;">模板:' + tpl.title + '(' + tpl.name + ')— 保存后无需改 HTML 即生效</div>'
|
||
+ rows + '</form>';
|
||
layer.open({
|
||
type: 1, title: '风格配色 - ' + tpl.title, area: ['520px', 'auto'], content: html,
|
||
btn: ['保存', '取消'],
|
||
yes: function (idx) {
|
||
var data = {};
|
||
$('#varsForm input[data-k]').each(function () {
|
||
data[$(this).data('k')] = $(this).val();
|
||
});
|
||
api('saveVariables', { name: tpl.name, vars: data }).then(function (res) {
|
||
layer.msg(res.message || '已保存', { icon: 1 });
|
||
layer.close(idx);
|
||
load();
|
||
}, function () { layer.msg('保存失败', { icon: 2 }); });
|
||
}
|
||
});
|
||
}
|
||
|
||
upload.render({
|
||
elem: '#uploadBtn',
|
||
url: 'upload',
|
||
accept: 'file',
|
||
exts: 'zip',
|
||
done: function (res) {
|
||
if (res.code === 0) { layer.msg('安装成功', { icon: 1 }); load(); }
|
||
else { layer.msg(res.message || '安装失败', { icon: 2 }); }
|
||
},
|
||
error: function () { layer.msg('上传失败', { icon: 2 }); }
|
||
});
|
||
|
||
// 导出配置:直接触发浏览器下载
|
||
$('#exportBtn').on('click', function () {
|
||
window.location.href = 'exportConfig';
|
||
});
|
||
|
||
// 导入配置:复用 upload 组件,仅接受 .json
|
||
upload.render({
|
||
elem: '#importBtn',
|
||
url: 'importConfig',
|
||
accept: 'file',
|
||
exts: 'json',
|
||
done: function (res) {
|
||
if (res.code === 0) { layer.msg(res.message || '导入成功', { icon: 1 }); load(); }
|
||
else { layer.msg(res.message || '导入失败', { icon: 2 }); }
|
||
},
|
||
error: function () { layer.msg('上传失败', { icon: 2 }); }
|
||
});
|
||
|
||
load();
|
||
});
|
||
</script>
|