Files
YwxAppThink/app/backend/view/template/index.html
T

240 lines
11 KiB
HTML
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.
<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">&#xe67c;</i>上传模板包</button>
<div style="color:#999;margin-top:8px;line-height:1.6;">
zip 内须含 <code>templates/&lt;name&gt;/template.json</code><code>static/</code> 资源,安装后落到
<code>templates/&lt;name&gt;/</code><code>public/static/templates/&lt;name&gt;/</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">&#xe67d;</i>导出配置</button>
<button type="button" class="layui-btn" id="importBtn"><i class="layui-icon">&#xe681;</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>