Files
mce/templates/admin/static/js/site-settings.js
Victor_Jay 20067f7640
Some checks failed
CI / Lint + Build (push) Has been cancelled
fix(admin): 修复多页面设置加载失败 — renderOtherSettings 空指针崩溃
根因:site-settings.js 的 renderOtherSettings() 无条件访问
#general-settings 元素,但 brand/security/registration 三个模板
无此容器,导致 TypeError 被 catch 吞掉 → '加载设置失败'。

额外修复:
- footer.html: ExtraJS 添加 ?v= 缓存版本哈希
- site-settings.js: API 失败时显式展示服务端错误消息(不再静默 return)
- site_settings.go: Set() 改用 UPSERT(clause.OnConflict),
  修复 GORM Save() 对不存在 key 首次保存时返回 ErrRecordNotFound
2026-06-30 18:56:42 +08:00

170 lines
6.5 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.

/* =====================================================
MetaLab 管理面板 - 站点设置
单一职责:加载/保存站点设置,开关即时生效,文本输入手动保存
===================================================== */
(function() {
'use strict';
const TOGGLE_KEYS = [
{ id: 'maintenance-enabled', key: 'maintenance.enabled' },
{ id: 'registration-enabled', key: 'registration.enabled' },
{ id: 'registration-show-guidelines', key: 'registration.show_guidelines' },
{ id: 'registration-force-guidelines', key: 'registration.force_guidelines' },
{ id: 'audit-enabled', key: 'audit.enabled' },
{ id: 'audit-username', key: 'audit.username_audit' },
{ id: 'audit-avatar', key: 'audit.avatar_audit' },
{ id: 'audit-bio', key: 'audit.bio_audit' }
];
// 已有专属 UI 的设置 key不在"其他设置"中重复显示
var DEDICATED_KEYS = {
'site.framework': true, 'site.copyright_start': true,
'site.name': true, 'site.tagline': true, 'site.contact_email': true,
'site.operator_name': true, 'site.icp_number': true, 'site.icp_license': true,
'site.police_number': true, 'site.wenwangwen': true, 'site.algorithm_filing': true,
'site.privacy_url': true, 'site.terms_url': true,
'site.guidelines': true,
'maintenance.enabled': true,
'registration.enabled': true, 'registration.show_guidelines': true,
'registration.force_guidelines': true, 'registration.guidelines_timer': true,
'password.strength': true,
'audit.enabled': true, 'audit.username_audit': true, 'audit.avatar_audit': true, 'audit.bio_audit': true
};
// --- 初始加载 ---
async function loadSettings() {
try {
var res = await api.get('/api/admin/site-settings');
if (!res.success) {
showToast(res.message || '加载设置失败', 'error');
return;
}
// 所有开关
var audit = res.data.audit || {};
var allSettings = res.data.all || {};
TOGGLE_KEYS.forEach(function(t) {
var el = document.getElementById(t.id);
if (!el) return;
if (t.id === 'maintenance-enabled') {
el.checked = allSettings[t.key] === 'true';
} else if (t.id === 'registration-enabled') {
el.checked = allSettings[t.key] !== 'false';
} else {
el.checked = audit[t.key.split('.')[1]] === true;
}
el.addEventListener('change', function() {
updateBoolSetting(t.key, el.checked, el);
});
});
// 文本输入保存按钮
bindSaveButtons();
// 社区准则文本域
var guidelinesEl = document.getElementById('setting-site-guidelines');
if (guidelinesEl) {
guidelinesEl.value = allSettings['site.guidelines'] || '';
var guidelinesBtn = document.querySelector('[data-key="site.guidelines"]');
if (guidelinesBtn) {
guidelinesBtn.addEventListener('click', function() {
updateTextSetting('site.guidelines', guidelinesEl.value, guidelinesBtn);
});
}
}
// 其他设置过滤已有专属UI的key
renderOtherSettings(allSettings);
} catch (e) {
showToast('加载设置失败', 'error');
}
}
// --- 文本输入保存 ---
function bindSaveButtons() {
document.querySelectorAll('.btn-save, .btn-save-select').forEach(function(btn) {
btn.addEventListener('click', function() {
var key = btn.getAttribute('data-key');
var inputId = btn.getAttribute('data-input');
var input = document.getElementById(inputId);
if (!key || !input) return;
updateTextSetting(key, input.value, btn);
});
});
}
async function updateTextSetting(key, value, btn) {
btn.disabled = true;
btn.textContent = '...';
try {
var res = await api.put('/api/admin/site-settings', {
key: key,
value: value
});
if (res.success) {
showToast('设置已更新', 'success');
} else {
showToast(res.message || '保存失败', 'error');
}
} catch (e) {
showToast('保存失败', 'error');
} finally {
btn.disabled = false;
btn.textContent = '保存';
}
}
// --- 更新布尔设置 ---
async function updateBoolSetting(key, value, toggle) {
toggle.disabled = true;
try {
var res = await api.put('/api/admin/site-settings/bool', {
key: key,
value: String(value)
});
if (res.success) {
showToast('设置已更新', 'success');
} else {
showToast(res.message || '保存失败', 'error');
toggle.checked = !value; // 回滚
}
} catch (e) {
showToast('保存失败', 'error');
toggle.checked = !value; // 回滚
} finally {
toggle.disabled = false;
}
}
// --- 渲染其他设置过滤已有专属UI的key ---
function renderOtherSettings(all) {
var container = document.getElementById('general-settings');
if (!container) return; // 部分页面无此容器
var filtered = [];
Object.keys(all).forEach(function(k) {
if (!DEDICATED_KEYS[k]) filtered.push(k);
});
if (filtered.length === 0) {
container.innerHTML = '<div class="settings-empty">暂无其他设置</div>';
return;
}
container.innerHTML = '';
filtered.forEach(function(key) {
var val = all[key] || '';
var item = document.createElement('div');
item.className = 'setting-item';
item.innerHTML =
'<div class="setting-info">' +
'<span class="setting-label">' + escapeHtml(key) + '</span>' +
'<span class="setting-value">' + escapeHtml(val.length > 80 ? val.slice(0, 80) + '...' : val) + '</span>' +
'</div>';
container.appendChild(item);
});
}
// --- 入口 ---
loadSettings();
})();