chore: 重写初始提交(清空历史,整理后全量提交)

This commit is contained in:
ywxapp
2026-08-16 16:54:14 +08:00
commit 6c1a106bc1
1808 changed files with 238144 additions and 0 deletions
+239
View File
@@ -0,0 +1,239 @@
<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>
+63
View File
@@ -0,0 +1,63 @@
<div class="layui-card" style="margin:15px;">
<div class="layui-card-header">界面设置</div>
<div class="layui-card-body">
<form class="layui-form" id="settingForm">
<div class="layui-form-item">
<label class="layui-form-label">全站默认模板</label>
<div class="layui-input-inline" style="width:320px;">
<select name="default_template" id="defaultTemplate">
<option value="default">默认(自带视图)</option>
</select>
</div>
<div class="layui-form-mid layui-word-aux">未单独绑定模板的插件,统一使用此默认模板</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">会员自选风格</label>
<div class="layui-input-inline">
<input type="checkbox" name="allow_member_select" id="allowSelect" lay-skin="switch" lay-text="开启|关闭">
</div>
<div class="layui-form-mid layui-word-aux">允许前台会员切换风格(开启后,已安装模板提供覆盖视图的插件页面右下角自动出现切换器,按访客 cookie 生效,无需登录)</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="saveSetting">保存设置</button>
</div>
</div>
</form>
</div>
</div>
<script>
layui.use(['form', 'layer', 'jquery'], function () {
var form = layui.form, layer = layui.layer, $ = layui.jquery;
// 拉取已安装模板填充下拉 + 当前设置
$.ajax({
url: 'index', type: 'POST', dataType: 'json',
success: function (res) {
if (res.code !== 0) { return; }
var inst = res.data.installed || [];
var gd = res.data.global_default || 'default';
var sel = $('#defaultTemplate');
inst.forEach(function (t) {
sel.append('<option value="' + t.name + '">' + t.title + ' (' + t.version + ')</option>');
});
sel.val(gd);
$('#allowSelect').prop('checked', !!res.data.allow_member_select);
form.render('select');
form.render('checkbox');
}
});
form.on('submit(saveSetting)', function (data) {
$.ajax({
url: 'setting', type: 'POST', data: data.field, dataType: 'json',
success: function (res) {
if (res.code === 0) { layer.msg('已保存', { icon: 1 }); }
else { layer.msg(res.message || '保存失败', { icon: 2 }); }
},
error: function () { layer.msg('请求失败', { icon: 2 }); }
});
return false;
});
});
</script>