2026-09-28 15:48:35 +08:00
|
|
|
|
/* 简航 SimpleNav —— 后台交互 */
|
|
|
|
|
|
(function () {
|
|
|
|
|
|
'use strict';
|
2026-09-28 16:43:10 +08:00
|
|
|
|
var $ = function (s, r) { return (r || document).querySelector(s); };
|
|
|
|
|
|
var $$ = function (s, r) { return Array.prototype.slice.call((r || document).querySelectorAll(s)); };
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------- 显示图标预览 ---------- */
|
|
|
|
|
|
function setPreview(src) {
|
|
|
|
|
|
var box = $('#f-icon-preview');
|
|
|
|
|
|
if (!box) return;
|
|
|
|
|
|
box.innerHTML = src ? '<img src="' + src + '" alt="">' : '<span class="ph">无</span>';
|
|
|
|
|
|
}
|
2026-09-28 15:48:35 +08:00
|
|
|
|
|
|
|
|
|
|
/* ---------- 自动获取网站信息 ---------- */
|
|
|
|
|
|
var fetchBtn = $('#btn-fetch');
|
|
|
|
|
|
if (fetchBtn) {
|
|
|
|
|
|
fetchBtn.addEventListener('click', function () {
|
|
|
|
|
|
var url = ($('#f-url') || {}).value || '';
|
|
|
|
|
|
var hint = $('#fetch-hint');
|
|
|
|
|
|
if (!url) { if (hint) hint.textContent = '请先填写网址'; return; }
|
2026-09-28 16:43:10 +08:00
|
|
|
|
if (hint) hint.textContent = '正在获取标题与高清图标…';
|
2026-09-28 15:48:35 +08:00
|
|
|
|
fetchBtn.disabled = true;
|
|
|
|
|
|
fetch(fetchBtn.getAttribute('data-url') + '?url=' + encodeURIComponent(url))
|
|
|
|
|
|
.then(function (r) { return r.json(); })
|
|
|
|
|
|
.then(function (d) {
|
|
|
|
|
|
fetchBtn.disabled = false;
|
|
|
|
|
|
if (!d.ok) { if (hint) hint.textContent = d.msg || '获取失败'; return; }
|
|
|
|
|
|
if ($('#f-name')) $('#f-name').value = d.name || '';
|
|
|
|
|
|
if ($('#f-short') && !$('#f-short').value) $('#f-short').value = d.short || d.name || '';
|
2026-09-28 16:43:10 +08:00
|
|
|
|
|
|
|
|
|
|
var auto = $('#f-icon-auto');
|
|
|
|
|
|
var locked = auto && !auto.checked;
|
|
|
|
|
|
if (!locked) {
|
|
|
|
|
|
if ($('#f-icon')) $('#f-icon').value = d.icon || '';
|
|
|
|
|
|
setPreview(d.icon_url || '');
|
|
|
|
|
|
}
|
|
|
|
|
|
var parts = ['已获取名称'];
|
|
|
|
|
|
parts.push(d.icon ? ('已获取图标(候选 ' + (d.candidates || 1) + ' 个)') : '未找到图标,将回退为首字母块');
|
|
|
|
|
|
if (locked) parts.push('(当前为自定义图标,未覆盖)');
|
|
|
|
|
|
if (hint) hint.textContent = parts.join(',');
|
2026-09-28 15:48:35 +08:00
|
|
|
|
})
|
|
|
|
|
|
.catch(function () { fetchBtn.disabled = false; if (hint) hint.textContent = '网络错误'; });
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-09-28 16:43:10 +08:00
|
|
|
|
|
|
|
|
|
|
/* ---------- 图标:上传文件 / 自定义地址 ---------- */
|
|
|
|
|
|
var fileInput = $('#f-icon-file');
|
|
|
|
|
|
if (fileInput) {
|
|
|
|
|
|
fileInput.addEventListener('change', function () {
|
|
|
|
|
|
var f = fileInput.files && fileInput.files[0];
|
|
|
|
|
|
if (!f) return;
|
|
|
|
|
|
var auto = $('#f-icon-auto');
|
|
|
|
|
|
if (auto) auto.checked = false; // 一旦自定义,自动取消「允许自动更新」
|
|
|
|
|
|
if (window.URL && URL.createObjectURL) { setPreview(URL.createObjectURL(f)); }
|
|
|
|
|
|
var hint = $('#fetch-hint');
|
|
|
|
|
|
if (hint) hint.textContent = '已选择本地图标:' + f.name + '(保存后生效,并将锁定不再自动更新)';
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
var urlInput = $('#f-icon-url');
|
|
|
|
|
|
if (urlInput) {
|
|
|
|
|
|
urlInput.addEventListener('change', function () {
|
|
|
|
|
|
var v = urlInput.value.trim();
|
|
|
|
|
|
if (!v) return;
|
|
|
|
|
|
var auto = $('#f-icon-auto');
|
|
|
|
|
|
if (auto) auto.checked = false;
|
|
|
|
|
|
setPreview(v);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
var autoBox = $('#f-icon-auto');
|
|
|
|
|
|
if (autoBox) {
|
|
|
|
|
|
autoBox.addEventListener('change', function () {
|
|
|
|
|
|
var hint = $('#fetch-hint');
|
|
|
|
|
|
if (!hint) return;
|
|
|
|
|
|
hint.textContent = autoBox.checked
|
|
|
|
|
|
? '已开启自动更新:该网站图标会在「全部更新」时刷新'
|
|
|
|
|
|
: '已关闭自动更新:该网站图标将被锁定';
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------- 清空表单 ---------- */
|
2026-09-28 15:48:35 +08:00
|
|
|
|
var resetBtn = $('#btn-reset');
|
|
|
|
|
|
if (resetBtn) {
|
|
|
|
|
|
resetBtn.addEventListener('click', function () {
|
2026-09-28 16:43:10 +08:00
|
|
|
|
['f-id', 'f-url', 'f-name', 'f-short', 'f-icon', 'f-icon-url'].forEach(function (id) {
|
|
|
|
|
|
var el = $('#' + id); if (el) el.value = id === 'f-id' ? '0' : '';
|
|
|
|
|
|
});
|
|
|
|
|
|
var fi = $('#f-icon-file'); if (fi) fi.value = '';
|
2026-09-28 15:48:35 +08:00
|
|
|
|
var v = $('#f-visible'); if (v) v.checked = true;
|
2026-09-28 16:43:10 +08:00
|
|
|
|
var a = $('#f-icon-auto'); if (a) a.checked = true;
|
2026-09-28 15:48:35 +08:00
|
|
|
|
var h = $('#fetch-hint'); if (h) h.textContent = '';
|
2026-09-28 16:43:10 +08:00
|
|
|
|
setPreview('');
|
2026-09-28 15:48:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------- 编辑网站:回填表单 ---------- */
|
2026-09-28 16:43:10 +08:00
|
|
|
|
$$('.edit-site').forEach(function (btn) {
|
2026-09-28 15:48:35 +08:00
|
|
|
|
btn.addEventListener('click', function () {
|
|
|
|
|
|
var d = btn.dataset;
|
|
|
|
|
|
$('#f-id').value = d.id;
|
|
|
|
|
|
$('#f-group').value = d.group;
|
|
|
|
|
|
$('#f-url').value = d.url;
|
|
|
|
|
|
$('#f-name').value = d.name;
|
|
|
|
|
|
$('#f-short').value = d.short;
|
|
|
|
|
|
$('#f-icon').value = d.icon;
|
2026-09-28 16:43:10 +08:00
|
|
|
|
if ($('#f-icon-url')) $('#f-icon-url').value = '';
|
|
|
|
|
|
if ($('#f-icon-file')) $('#f-icon-file').value = '';
|
|
|
|
|
|
if ($('#f-icon-auto')) $('#f-icon-auto').checked = d.custom !== '1';
|
2026-09-28 15:48:35 +08:00
|
|
|
|
$('#f-visible').checked = d.visible === '1';
|
2026-09-28 16:43:10 +08:00
|
|
|
|
setPreview(d.iconUrl || '');
|
|
|
|
|
|
var hint = $('#fetch-hint');
|
|
|
|
|
|
if (hint) hint.textContent = d.custom === '1' ? '该网站图标为自定义,不会被「全部更新」覆盖' : '';
|
2026-09-28 15:48:35 +08:00
|
|
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------- 编辑板块 / 引擎 ---------- */
|
2026-09-28 16:43:10 +08:00
|
|
|
|
$$('.edit-group').forEach(function (btn) {
|
2026-09-28 15:48:35 +08:00
|
|
|
|
btn.addEventListener('click', function () {
|
|
|
|
|
|
var d = btn.dataset;
|
|
|
|
|
|
$('#g-id').value = d.id; $('#g-name').value = d.name;
|
|
|
|
|
|
$('#g-display').value = d.display; $('#g-visible').checked = d.visible === '1';
|
|
|
|
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
var gReset = $('#g-reset');
|
|
|
|
|
|
if (gReset) gReset.addEventListener('click', function () { $('#g-id').value = '0'; $('#g-name').value = ''; $('#g-display').value = 'grid'; $('#g-visible').checked = true; });
|
|
|
|
|
|
|
2026-09-28 16:43:10 +08:00
|
|
|
|
$$('.edit-engine').forEach(function (btn) {
|
2026-09-28 15:48:35 +08:00
|
|
|
|
btn.addEventListener('click', function () {
|
|
|
|
|
|
var d = btn.dataset;
|
|
|
|
|
|
$('#e-id').value = d.id; $('#e-name').value = d.name; $('#e-slug').value = d.slug;
|
|
|
|
|
|
$('#e-search').value = d.search; $('#e-param').value = d.param; $('#e-sug').value = d.sug;
|
|
|
|
|
|
$('#e-sugtype').value = d.sugtype; $('#e-color').value = d.color; $('#e-active').checked = d.active === '1';
|
|
|
|
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
var eReset = $('#e-reset');
|
|
|
|
|
|
if (eReset) eReset.addEventListener('click', function () {
|
|
|
|
|
|
['e-id', 'e-name', 'e-slug', 'e-search', 'e-sug', 'e-color'].forEach(function (id) { var el = $('#' + id); if (el) el.value = id === 'e-id' ? '0' : ''; });
|
|
|
|
|
|
$('#e-param').value = 'q'; $('#e-active').checked = false;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-09-28 16:43:10 +08:00
|
|
|
|
/* ---------- 列表内「显示 / 隐藏」原地切换(不跳页) ---------- */
|
|
|
|
|
|
$$('form.js-toggle').forEach(function (form) {
|
|
|
|
|
|
form.addEventListener('submit', function (ev) {
|
|
|
|
|
|
var btn = $('button.toggle', form);
|
|
|
|
|
|
if (btn) btn.disabled = true;
|
|
|
|
|
|
ev.preventDefault();
|
|
|
|
|
|
fetch(form.getAttribute('action'), {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
body: new FormData(form),
|
|
|
|
|
|
credentials: 'same-origin',
|
|
|
|
|
|
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
|
|
|
|
|
})
|
|
|
|
|
|
.then(function (r) { return r.json(); })
|
|
|
|
|
|
.then(function (d) {
|
|
|
|
|
|
if (btn) btn.disabled = false;
|
|
|
|
|
|
if (!d || !d.ok) { alert((d && d.msg) || '切换失败'); return; }
|
|
|
|
|
|
applyToggleState(form, d.state);
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(function () {
|
|
|
|
|
|
if (btn) btn.disabled = false;
|
|
|
|
|
|
// AJAX 失败时退回普通提交,保证功能可用(表单已带 p,会回到当前页)
|
|
|
|
|
|
var ajax = form.querySelector('input[name="ajax"]');
|
|
|
|
|
|
if (ajax) { ajax.parentNode.removeChild(ajax); }
|
|
|
|
|
|
form.submit();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
function applyToggleState(form, state) {
|
|
|
|
|
|
var on = state === 1;
|
|
|
|
|
|
var btn = $('button.toggle', form);
|
|
|
|
|
|
if (btn) {
|
|
|
|
|
|
btn.classList.toggle('on', on);
|
|
|
|
|
|
btn.setAttribute('data-on', on ? '1' : '0');
|
|
|
|
|
|
btn.setAttribute('title', on ? '当前:显示中(点击隐藏)' : '当前:已隐藏(点击显示)');
|
|
|
|
|
|
var svg = btn.querySelector('path[stroke]');
|
|
|
|
|
|
var circle = btn.querySelector('circle');
|
|
|
|
|
|
if (on) {
|
|
|
|
|
|
if (svg) svg.remove();
|
|
|
|
|
|
} else if (circle && !svg) {
|
|
|
|
|
|
var p = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
|
|
|
|
p.setAttribute('d', 'M3 3l18 18');
|
|
|
|
|
|
p.setAttribute('stroke', 'currentColor');
|
|
|
|
|
|
p.setAttribute('stroke-width', '2');
|
|
|
|
|
|
p.setAttribute('stroke-linecap', 'round');
|
|
|
|
|
|
btn.querySelector('svg').appendChild(p);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 同行的状态文字(如搜索引擎的「已显示 / 已隐藏」)
|
|
|
|
|
|
var cell = form.closest('td') || form.parentNode;
|
|
|
|
|
|
var st = cell ? $('.js-state', cell) : null;
|
|
|
|
|
|
if (st) {
|
|
|
|
|
|
st.textContent = on ? (st.dataset.onText || '已显示') : (st.dataset.offText || '已隐藏');
|
|
|
|
|
|
st.classList.toggle('on', on);
|
|
|
|
|
|
st.classList.toggle('off', !on);
|
|
|
|
|
|
}
|
|
|
|
|
|
var row = form.closest('tr');
|
|
|
|
|
|
if (row) {
|
|
|
|
|
|
row.classList.toggle('row-off', !on);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------- 一键「全部更新」(分批循环) ---------- */
|
2026-09-28 15:48:35 +08:00
|
|
|
|
var upBtn = $('#btn-update-all');
|
|
|
|
|
|
if (upBtn) {
|
|
|
|
|
|
upBtn.addEventListener('click', function () {
|
2026-09-28 16:43:10 +08:00
|
|
|
|
if (!confirm('将批量刷新所有网站的名称与自动图标。\n用户自定义简称不受影响;标记为「自定义」的图标会被跳过。\n确定继续?')) return;
|
2026-09-28 15:48:35 +08:00
|
|
|
|
var base = upBtn.getAttribute('data-url');
|
2026-09-28 16:43:10 +08:00
|
|
|
|
var total = 0, updated = 0, failed = 0, skipped = 0, offset = 0;
|
2026-09-28 15:48:35 +08:00
|
|
|
|
upBtn.disabled = true;
|
|
|
|
|
|
function step() {
|
2026-09-28 16:43:10 +08:00
|
|
|
|
fetch(base + '?offset=' + offset + '&limit=3', { credentials: 'same-origin' })
|
2026-09-28 15:48:35 +08:00
|
|
|
|
.then(function (r) { return r.json(); })
|
|
|
|
|
|
.then(function (d) {
|
2026-09-28 16:43:10 +08:00
|
|
|
|
total = d.total; updated += d.updated; failed += d.failed; skipped += (d.skipped || 0); offset = d.offset;
|
2026-09-28 15:48:35 +08:00
|
|
|
|
upBtn.textContent = '⟳ 更新中 ' + Math.min(offset, total) + '/' + total;
|
|
|
|
|
|
if (!d.done) { step(); }
|
|
|
|
|
|
else {
|
|
|
|
|
|
upBtn.disabled = false;
|
2026-09-28 16:43:10 +08:00
|
|
|
|
upBtn.textContent = '⟳ 全部更新(名称 + 自动图标)';
|
|
|
|
|
|
alert('更新完成:\n成功 ' + updated + ' 个\n跳过(自定义图标)' + skipped + ' 个\n失败/无变化 ' + failed + ' 个\n共 ' + total + ' 个网站。');
|
2026-09-28 15:48:35 +08:00
|
|
|
|
location.reload();
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2026-09-28 16:43:10 +08:00
|
|
|
|
.catch(function () { upBtn.disabled = false; upBtn.textContent = '⟳ 全部更新(名称 + 自动图标)'; alert('更新中断,请重试。'); });
|
2026-09-28 15:48:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
step();
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
})();
|