前端配置迁至 config/local.js,完善支付、模板库与部署脚本
- 页面直接引用 local.js 设置 SOON_DEPLOY_CONFIG,移除 deploy-config - Docker sync-config 生成 local.js;更新 README 与 agent-core 说明 - 模板库自动扫描 .soon;新增后端部署/种子/排查脚本 - 完善支付配置、订阅弹窗与后台支付管理页 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,290 +1,272 @@
|
||||
(function () {
|
||||
|
||||
'use strict';
|
||||
|
||||
|
||||
|
||||
var Api = function () { return window.AdminApi; };
|
||||
|
||||
var Ui = function () { return window.AdminUi; };
|
||||
|
||||
var I = function () { return window.AdminI18n; };
|
||||
|
||||
var Form = function () { return window.AdminForm; };
|
||||
|
||||
var Tabs = function () { return window.AdminTabs; };
|
||||
|
||||
var PARAM_KEYS = [
|
||||
'site.base_url',
|
||||
'site.front_base_url',
|
||||
'alipay.app_id',
|
||||
'wechat.app_id',
|
||||
'wechat.mch_id',
|
||||
'wechat.mch_serial_no',
|
||||
];
|
||||
|
||||
|
||||
var CHANNEL_CONFIG = {
|
||||
|
||||
alipay: {
|
||||
|
||||
label: '支付宝',
|
||||
|
||||
kinds: [
|
||||
|
||||
{ kind: 'private_key', file: 'alipay_private_key.pem', label: '应用私钥 private_key' },
|
||||
|
||||
{ kind: 'public_key', file: 'alipay_public_key.pem', label: '支付宝公钥 public_key' },
|
||||
|
||||
],
|
||||
|
||||
},
|
||||
|
||||
wechat: {
|
||||
|
||||
label: '微信支付',
|
||||
|
||||
kinds: [
|
||||
|
||||
{ kind: 'mch_private_key', file: 'wechat_mch_private_key.pem', label: '商户私钥 mch_private_key' },
|
||||
|
||||
{ kind: 'api_v3_key', file: 'wechat_api_v3_key.pem', label: 'APIv3 密钥 api_v3_key' },
|
||||
|
||||
{ kind: 'platform_cert', file: 'wechat_platform_cert.pem', label: '平台证书 platform_cert' },
|
||||
|
||||
],
|
||||
|
||||
},
|
||||
|
||||
var KEY_FIELDS = {
|
||||
alipay: [
|
||||
{ kind: 'private_key', file: 'alipay_private_key.pem', label: '应用私钥' },
|
||||
{ kind: 'public_key', file: 'alipay_public_key.pem', label: '支付宝公钥' },
|
||||
],
|
||||
wechat: [
|
||||
{ kind: 'mch_private_key', file: 'wechat_mch_private_key.pem', label: '商户私钥' },
|
||||
{ kind: 'api_v3_key', file: 'wechat_api_v3_key.pem', label: 'APIv3 密钥', text: true },
|
||||
{ kind: 'platform_cert', file: 'wechat_platform_cert.pem', label: '平台证书' },
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
function cfgId(key) {
|
||||
return 'pay_' + key.replace(/\./g, '_');
|
||||
}
|
||||
|
||||
function fileSet(items) {
|
||||
|
||||
var set = {};
|
||||
|
||||
(items || []).forEach(function (p) { set[p.name] = p; });
|
||||
|
||||
return set;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function channelFormHtml(channel, files) {
|
||||
|
||||
var cfg = CHANNEL_CONFIG[channel];
|
||||
|
||||
var rows = cfg.kinds.map(function (k) {
|
||||
|
||||
var ok = !!files[k.file];
|
||||
|
||||
var statusCls = ok ? 'soon-admin-key-form-row__status--ok' : 'soon-admin-key-form-row__status--miss';
|
||||
|
||||
var statusText = ok ? '已就绪' : '缺失';
|
||||
|
||||
var id = channel + '_' + k.kind;
|
||||
|
||||
return '<div class="soon-admin-key-form-row">' +
|
||||
|
||||
'<div><div class="soon-admin-key-form-row__label">' + I().esc(k.label) + '</div>' +
|
||||
|
||||
'<div class="soon-admin-key-form-row__status ' + statusCls + '">' + statusText + '</div></div>' +
|
||||
|
||||
'<textarea id="' + id + '" class="soon-input soon-input--sm soon-input--textarea" rows="4" ' +
|
||||
|
||||
'placeholder="粘贴 PEM 内容或拖入 .pem 文件"></textarea>' +
|
||||
|
||||
'<button type="button" class="soon-btn soon-btn--sm pay-upload-btn" data-channel="' + channel +
|
||||
|
||||
'" data-kind="' + k.kind + '" data-target="' + id + '">上传</button></div>';
|
||||
|
||||
}).join('');
|
||||
|
||||
return '<div class="soon-admin-form">' + Form().panel(cfg.label + ' 密钥', rows) +
|
||||
|
||||
'<details class="soon-admin-hint"><summary>回调说明</summary>' +
|
||||
|
||||
'<p>notify:<code>/api/v1/pay/' + channel + '/notify</code>(须配置 site.base_url 为 HTTPS 外网地址)</p></details>' +
|
||||
|
||||
'<div class="soon-admin-upload" id="payDrop_' + channel + '">拖拽 .pem 到此处自动填入对应字段</div></div>';
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function filesTabHtml(items) {
|
||||
|
||||
var rows = (items || []).map(function (p) {
|
||||
|
||||
return '<tr><td>' + I().esc(p.name) + '</td><td>' + p.bytes + ' B</td><td>' +
|
||||
|
||||
I().formatDateTime(p.mtime) + '</td></tr>';
|
||||
|
||||
}).join('');
|
||||
|
||||
return Ui().dataTable(['文件名', '大小', '修改时间'], rows);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function uploadPem(channel, kind, content, onDone) {
|
||||
|
||||
return Api().fetch('payment/upload', {
|
||||
|
||||
method: 'POST',
|
||||
|
||||
body: JSON.stringify({ channel: channel, kind: kind, content: content }),
|
||||
|
||||
}).then(function (j) {
|
||||
|
||||
if (j) {
|
||||
|
||||
soonToast('密钥已上传', 'success');
|
||||
|
||||
if (onDone) onDone();
|
||||
|
||||
}
|
||||
|
||||
return j;
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function bindChannel(channel, panel) {
|
||||
|
||||
panel.querySelectorAll('.pay-upload-btn').forEach(function (btn) {
|
||||
|
||||
btn.onclick = function () {
|
||||
|
||||
var target = Ui().el(btn.dataset.target);
|
||||
|
||||
var content = target ? target.value.trim() : '';
|
||||
|
||||
if (!content) {
|
||||
|
||||
soonToast('请先粘贴或拖入 PEM 内容', 'warn');
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
Ui().runFormSubmit(btn, function () {
|
||||
return uploadPem(btn.dataset.channel, btn.dataset.kind, content, render);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
var drop = Ui().el('payDrop_' + channel);
|
||||
|
||||
if (!drop) return;
|
||||
|
||||
drop.ondragover = function (e) { e.preventDefault(); drop.classList.add('is-dragover'); };
|
||||
|
||||
drop.ondragleave = function () { drop.classList.remove('is-dragover'); };
|
||||
|
||||
drop.ondrop = function (e) {
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
drop.classList.remove('is-dragover');
|
||||
|
||||
var file = e.dataTransfer.files[0];
|
||||
|
||||
if (!file) return;
|
||||
|
||||
file.text().then(function (text) {
|
||||
|
||||
var name = file.name.toLowerCase();
|
||||
|
||||
var cfg = CHANNEL_CONFIG[channel];
|
||||
|
||||
var matched = cfg.kinds.find(function (k) { return name.indexOf(k.kind.replace(/_/g, '')) >= 0 || name === k.file; });
|
||||
|
||||
var targetId = matched ? channel + '_' + matched.kind : channel + '_' + cfg.kinds[0].kind;
|
||||
|
||||
var ta = Ui().el(targetId);
|
||||
|
||||
if (ta) ta.value = text;
|
||||
|
||||
soonToast('已填入 ' + (matched ? matched.label : '首个字段') + ',请确认后点击上传', 'success');
|
||||
|
||||
});
|
||||
|
||||
function deployDefaults() {
|
||||
var cfg = window.SOON_DEPLOY_CONFIG || {};
|
||||
return {
|
||||
apiBase: String(cfg.api_v1_base || '').replace(/\/api\/v1\/?$/, ''),
|
||||
frontBase: cfg.front_base_url || cfg.web_base || '',
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function parseDisplayChannels(values) {
|
||||
var raw = values && values['payment.display_channels'] != null
|
||||
? values['payment.display_channels'] : 'alipay';
|
||||
return String(raw).split(/[\s,]+/).filter(function (v) { return v === 'alipay' || v === 'wechat'; });
|
||||
}
|
||||
|
||||
function statusLine(readiness) {
|
||||
readiness = readiness || {};
|
||||
var a = readiness.alipay || {};
|
||||
var w = readiness.wechat || {};
|
||||
return '<div class="soon-admin-pay-status">' +
|
||||
'<span class="soon-admin-pay-status__item' + (a.ready ? ' is-ok' : ' is-miss') + '">支付宝</span>' +
|
||||
'<span class="soon-admin-pay-status__item' + (w.ready ? ' is-ok' : ' is-miss') + '">微信支付</span></div>';
|
||||
}
|
||||
|
||||
function field(label, control, wide) {
|
||||
return '<div class="soon-admin-pay-field' + (wide ? ' soon-admin-pay-field--wide' : '') + '">' +
|
||||
'<label class="soon-admin-pay-field__label">' + I().esc(label) + '</label>' +
|
||||
'<div class="soon-admin-pay-field__control">' + control + '</div></div>';
|
||||
}
|
||||
|
||||
function grid(cells) {
|
||||
return '<div class="soon-admin-pay-grid">' + cells.join('') + '</div>';
|
||||
}
|
||||
|
||||
function textInput(key, values, placeholder) {
|
||||
var val = values && values[key] != null ? values[key] : '';
|
||||
return Form().input(cfgId(key), { value: val, placeholder: placeholder || '' });
|
||||
}
|
||||
|
||||
function readonlyInput(value) {
|
||||
return '<input class="soon-input soon-input--sm soon-input--readonly" readonly value="' +
|
||||
I().esc(value || '') + '">';
|
||||
}
|
||||
|
||||
function displayChannelsField(selected) {
|
||||
if (!selected.length) selected = ['alipay'];
|
||||
var mk = function (ch, label) {
|
||||
var on = selected.indexOf(ch) >= 0;
|
||||
return '<label class="soon-admin-check"><input type="checkbox" class="pay-disp-ch" value="' + ch + '"' +
|
||||
(on ? ' checked' : '') + '> ' + label + '</label>';
|
||||
};
|
||||
return field('用户端展示',
|
||||
'<div class="soon-admin-pay-checks">' + mk('alipay', '支付宝') + mk('wechat', '微信') + '</div>', true);
|
||||
}
|
||||
|
||||
function siteGrid(values, notifyUrls) {
|
||||
var defs = deployDefaults();
|
||||
return grid([
|
||||
displayChannelsField(parseDisplayChannels(values)),
|
||||
field('API 外网', textInput('site.base_url', values, defs.apiBase)),
|
||||
field('前端站点', textInput('site.front_base_url', values, defs.frontBase)),
|
||||
field('支付宝回调', readonlyInput(notifyUrls.alipay)),
|
||||
field('微信回调', readonlyInput(notifyUrls.wechat)),
|
||||
]);
|
||||
}
|
||||
|
||||
function keyField(channel, keyDef, files) {
|
||||
var ok = !!files[keyDef.file];
|
||||
var id = channel + '_' + keyDef.kind;
|
||||
var pasteId = id + '_paste';
|
||||
var badge = '<span class="soon-admin-pay-key__badge ' + (ok ? 'is-ok' : 'is-miss') + '">' +
|
||||
(ok ? '已配置' : '未配置') + '</span>';
|
||||
var inner;
|
||||
if (keyDef.text) {
|
||||
inner = badge +
|
||||
Form().input(id + '_text', { placeholder: 'APIv3 密钥', className: 'soon-admin-pay-key__text' }) +
|
||||
'<button type="button" class="soon-btn soon-btn--xs soon-btn--ghost pay-key-upload" data-channel="' +
|
||||
channel + '" data-kind="' + keyDef.kind + '" data-source="' + id + '_text">写入</button>';
|
||||
} else {
|
||||
inner = badge +
|
||||
'<label class="soon-btn soon-btn--xs soon-btn--ghost soon-admin-pay-key__file">上传' +
|
||||
'<input type="file" class="pay-key-file" accept=".pem,.txt,.key" data-channel="' +
|
||||
channel + '" data-kind="' + keyDef.kind + '" hidden></label>' +
|
||||
'<button type="button" class="soon-btn soon-btn--xs soon-btn--ghost pay-key-paste" data-target="' +
|
||||
pasteId + '">粘贴</button>' +
|
||||
'<div id="' + pasteId + '_box" class="soon-admin-pay-key__paste">' +
|
||||
'<textarea id="' + pasteId + '" class="soon-input soon-input--sm" rows="2" placeholder="PEM"></textarea>' +
|
||||
'<button type="button" class="soon-btn soon-btn--xs pay-key-commit" data-channel="' + channel +
|
||||
'" data-kind="' + keyDef.kind + '" data-target="' + pasteId + '">确认</button></div>';
|
||||
}
|
||||
return field(keyDef.label, '<div class="soon-admin-pay-key">' + inner + '</div>');
|
||||
}
|
||||
|
||||
function channelGrid(channel, files, values) {
|
||||
var cells = [];
|
||||
if (channel === 'alipay') {
|
||||
cells.push(field('APPID', textInput('alipay.app_id', values, '开放平台 APPID')));
|
||||
KEY_FIELDS.alipay.forEach(function (f) { cells.push(keyField('alipay', f, files)); });
|
||||
} else {
|
||||
cells.push(field('AppID', textInput('wechat.app_id', values, 'AppID')));
|
||||
cells.push(field('商户号', textInput('wechat.mch_id', values, 'mch_id')));
|
||||
cells.push(field('证书序列号', textInput('wechat.mch_serial_no', values, 'serial_no')));
|
||||
KEY_FIELDS.wechat.forEach(function (f) { cells.push(keyField('wechat', f, files)); });
|
||||
}
|
||||
return grid(cells);
|
||||
}
|
||||
|
||||
function formHtml(values, files, notifyUrls, readiness) {
|
||||
var tabs = Tabs().html('paymentTabs', [
|
||||
{ id: 'alipay', label: '支付宝' },
|
||||
{ id: 'wechat', label: '微信' },
|
||||
], {
|
||||
alipay: channelGrid('alipay', files, values),
|
||||
wechat: channelGrid('wechat', files, values),
|
||||
}, 'alipay');
|
||||
|
||||
return '<div class="soon-admin-form soon-admin-form--pay">' +
|
||||
'<div class="soon-admin-form-panel soon-admin-form-panel--pay">' +
|
||||
statusLine(readiness) + siteGrid(values, notifyUrls) + tabs +
|
||||
'</div>' + Form().actions('保存', 'payFormSave') + '</div>';
|
||||
}
|
||||
|
||||
function collectParams() {
|
||||
var items = PARAM_KEYS.map(function (key) {
|
||||
var el = Ui().el(cfgId(key));
|
||||
return { key: key, value: el ? el.value.trim() : '' };
|
||||
});
|
||||
var disp = [];
|
||||
document.querySelectorAll('.pay-disp-ch:checked').forEach(function (el) { disp.push(el.value); });
|
||||
items.push({ key: 'payment.display_channels', value: disp.join(',') });
|
||||
return items;
|
||||
}
|
||||
|
||||
function uploadKey(channel, kind, content) {
|
||||
return Api().fetch('payment/upload', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ channel: channel, kind: kind, content: content }),
|
||||
});
|
||||
}
|
||||
|
||||
function bindKeys(panel) {
|
||||
panel.querySelectorAll('.pay-key-file').forEach(function (input) {
|
||||
input.onchange = function () {
|
||||
var file = input.files && input.files[0];
|
||||
if (!file) return;
|
||||
file.text().then(function (text) {
|
||||
var content = text.trim();
|
||||
if (!content) {
|
||||
soonToast('文件为空', 'warn');
|
||||
return;
|
||||
}
|
||||
uploadKey(input.dataset.channel, input.dataset.kind, content).then(function (j) {
|
||||
if (j) {
|
||||
soonToast('已保存', 'success');
|
||||
render();
|
||||
}
|
||||
});
|
||||
}).finally(function () { input.value = ''; });
|
||||
};
|
||||
});
|
||||
|
||||
panel.querySelectorAll('.pay-key-paste').forEach(function (btn) {
|
||||
btn.onclick = function () {
|
||||
var box = Ui().el(btn.dataset.target + '_box');
|
||||
if (!box) return;
|
||||
var open = box.classList.toggle('is-open');
|
||||
var wrap = box.closest('.soon-admin-pay-field');
|
||||
if (wrap) wrap.classList.toggle('soon-admin-pay-field--wide', open);
|
||||
if (open) {
|
||||
var ta = Ui().el(btn.dataset.target);
|
||||
if (ta) ta.focus();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
panel.querySelectorAll('.pay-key-upload, .pay-key-commit').forEach(function (btn) {
|
||||
btn.onclick = function () {
|
||||
var content = '';
|
||||
if (btn.classList.contains('pay-key-upload')) {
|
||||
var src = Ui().el(btn.dataset.source);
|
||||
content = src ? src.value.trim() : '';
|
||||
} else {
|
||||
var ta = Ui().el(btn.dataset.target);
|
||||
content = ta ? ta.value.trim() : '';
|
||||
}
|
||||
if (!content) {
|
||||
soonToast('内容为空', 'warn');
|
||||
return;
|
||||
}
|
||||
Ui().runFormSubmit(btn, function () {
|
||||
return uploadKey(btn.dataset.channel, btn.dataset.kind, content).then(function (j) {
|
||||
if (j) {
|
||||
soonToast('已保存', 'success');
|
||||
render();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function bindSave() {
|
||||
Ui().bindFormSubmit('payFormSave', function () {
|
||||
return Api().postJson('payment/config', { items: collectParams() }).then(function (j) {
|
||||
if (j) {
|
||||
soonToast('已保存', 'success');
|
||||
render();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function render() {
|
||||
|
||||
var panel = Ui().el('panel-payment');
|
||||
|
||||
if (!panel) return;
|
||||
|
||||
panel.innerHTML = '<div class="soon-admin-page">' + Ui().pageHeader('支付配置', '按渠道管理密钥文件,上传后立即热加载') +
|
||||
|
||||
Ui().skeletonTable() + '</div>';
|
||||
|
||||
|
||||
var header = Ui().pageHeader('支付配置');
|
||||
panel.innerHTML = '<div class="soon-admin-page">' + header + Ui().skeletonTable() + '</div>';
|
||||
|
||||
Api().get('payment/status').then(function (j) {
|
||||
|
||||
if (!j) return;
|
||||
|
||||
var items = j.data.items || [];
|
||||
|
||||
var files = fileSet(items);
|
||||
|
||||
|
||||
|
||||
var tabsHtml = Tabs().html('paymentTabs', [
|
||||
|
||||
{ id: 'alipay', label: '支付宝' },
|
||||
|
||||
{ id: 'wechat', label: '微信支付' },
|
||||
|
||||
{ id: 'files', label: '文件清单' },
|
||||
|
||||
], {
|
||||
|
||||
alipay: channelFormHtml('alipay', files),
|
||||
|
||||
wechat: channelFormHtml('wechat', files),
|
||||
|
||||
files: filesTabHtml(items),
|
||||
|
||||
}, 'alipay');
|
||||
|
||||
|
||||
|
||||
panel.innerHTML = '<div class="soon-admin-page">' +
|
||||
|
||||
Ui().pageHeader('支付配置', '按渠道管理密钥文件,上传后立即热加载') +
|
||||
|
||||
tabsHtml + '</div>';
|
||||
|
||||
|
||||
|
||||
var tabsRoot = Ui().el('paymentTabs');
|
||||
|
||||
Tabs().bind(tabsRoot);
|
||||
|
||||
bindChannel('alipay', panel);
|
||||
|
||||
bindChannel('wechat', panel);
|
||||
|
||||
var data = j.data || {};
|
||||
panel.innerHTML = '<div class="soon-admin-page">' + header +
|
||||
formHtml(data.params || {}, fileSet(data.items), data.notify_urls || {}, data.readiness) +
|
||||
'</div>';
|
||||
Tabs().bind(Ui().el('paymentTabs'));
|
||||
bindSave();
|
||||
bindKeys(panel);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
window.AdminViews = window.AdminViews || {};
|
||||
|
||||
window.AdminViews.payment = { render: render };
|
||||
|
||||
})();
|
||||
|
||||
|
||||
@@ -5,39 +5,22 @@
|
||||
var Ui = function () { return window.AdminUi; };
|
||||
var I = function () { return window.AdminI18n; };
|
||||
var Form = function () { return window.AdminForm; };
|
||||
var Tabs = function () { return window.AdminTabs; };
|
||||
var Filter = function () { return window.AdminFilter; };
|
||||
var State = function () { return window.AdminState; };
|
||||
var Router = function () { return window.AdminRouter; };
|
||||
|
||||
var SETTING_LABELS = {
|
||||
'site.base_url': 'API 站点外网地址',
|
||||
'site.front_base_url': '前端站点地址',
|
||||
'site.base_url': 'API 站点外网地址(见支付配置)',
|
||||
'payment.display_channels': '前端展示渠道(见支付配置)',
|
||||
'alipay.app_id': '支付宝 APPID(见支付配置)',
|
||||
'wechat.app_id': '微信 AppID(见支付配置)',
|
||||
'wechat.mch_id': '微信商户号(见支付配置)',
|
||||
'wechat.mch_serial_no': '商户证书序列号(见支付配置)',
|
||||
};
|
||||
|
||||
var valuesByKey = {};
|
||||
var allItems = [];
|
||||
|
||||
function siteFields() {
|
||||
var cfg = window.SOON_DEPLOY_CONFIG || {};
|
||||
var apiBase = String(cfg.api_v1_base || '').replace(/\/api\/v1\/?$/, '');
|
||||
var frontBase = cfg.front_base_url || cfg.web_base || '';
|
||||
return [
|
||||
{
|
||||
key: 'site.base_url',
|
||||
label: 'API 站点外网地址',
|
||||
hint: '须 HTTPS 外网可达;支付宝/微信 notify 回调依赖此地址',
|
||||
placeholder: apiBase,
|
||||
},
|
||||
{
|
||||
key: 'site.front_base_url',
|
||||
label: '前端站点地址',
|
||||
hint: '支付完成 return_url 跳转至会员页',
|
||||
placeholder: frontBase,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function settingLabel(key) {
|
||||
if (SETTING_LABELS[key]) {
|
||||
return I().esc(key) + '<br><span class="soon-admin-setting-hint">' + I().esc(SETTING_LABELS[key]) + '</span>';
|
||||
@@ -50,17 +33,6 @@
|
||||
return I().esc(val);
|
||||
}
|
||||
|
||||
function siteFormHtml() {
|
||||
var fields = siteFields().map(function (f) {
|
||||
var val = valuesByKey[f.key] != null ? valuesByKey[f.key] : '';
|
||||
return Form().field(f.label,
|
||||
Form().input('cfg_' + f.key.replace(/\./g, '_'), { value: val, placeholder: f.placeholder }),
|
||||
f.hint);
|
||||
}).join('');
|
||||
return '<div class="soon-admin-form">' + Form().panel('站点与回调地址', fields) +
|
||||
Form().actions('保存站点配置', 'settingsSiteSave') + '</div>';
|
||||
}
|
||||
|
||||
function allSettingsHtml(filterQ) {
|
||||
var q = (filterQ || '').toLowerCase();
|
||||
var items = allItems.filter(function (s) {
|
||||
@@ -82,21 +54,6 @@
|
||||
return filter + Ui().dataTable(['配置项', '当前值', '更新时间', '操作'], rows);
|
||||
}
|
||||
|
||||
function bindSiteForm() {
|
||||
Ui().bindFormSubmit('settingsSiteSave', function () {
|
||||
var items = siteFields().map(function (f) {
|
||||
var el = Ui().el('cfg_' + f.key.replace(/\./g, '_'));
|
||||
return { key: f.key, value: el ? el.value.trim() : '' };
|
||||
});
|
||||
return Api().postJson('settings/batch', { items: items }).then(function (r) {
|
||||
if (r) {
|
||||
soonToast('站点配置已保存', 'success');
|
||||
render();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function bindAllSettings() {
|
||||
var st = State().settings;
|
||||
var searchBtn = Ui().el('settingsSearchBtn');
|
||||
@@ -154,7 +111,7 @@
|
||||
var panel = Ui().el('panel-settings');
|
||||
if (!panel) return;
|
||||
var st = State().settings;
|
||||
var header = Ui().pageHeader('系统设置', '站点地址与运行时参数;修改后即时生效');
|
||||
var header = Ui().pageHeader('系统设置', '运行时参数;支付相关请在「支付配置」维护');
|
||||
panel.innerHTML = '<div class="soon-admin-page">' + header + Ui().skeletonTable() + '</div>';
|
||||
|
||||
Api().get('settings').then(function (j) {
|
||||
@@ -168,20 +125,7 @@
|
||||
allItems = j.data.items || [];
|
||||
allItems.forEach(function (s) { valuesByKey[s.key] = s.value; });
|
||||
|
||||
var tabsHtml = Tabs().html('settingsTabs', [
|
||||
{ id: 'site', label: '站点配置' },
|
||||
{ id: 'all', label: '全部配置' },
|
||||
], {
|
||||
site: siteFormHtml(),
|
||||
all: allSettingsHtml(st.q),
|
||||
}, st.tab);
|
||||
|
||||
panel.innerHTML = '<div class="soon-admin-page">' + header + tabsHtml + '</div>';
|
||||
Tabs().bind(Ui().el('settingsTabs'), function (tabId) {
|
||||
st.tab = tabId;
|
||||
Router().syncQuery();
|
||||
});
|
||||
bindSiteForm();
|
||||
panel.innerHTML = '<div class="soon-admin-page">' + header + allSettingsHtml(st.q) + '</div>';
|
||||
bindAllSettings();
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user