| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- 'use strict';
- (function () {
- const views = {
- crawl: document.getElementById('viewCrawl'),
- gen: document.getElementById('viewGen'),
- history: document.getElementById('viewHistory'),
- settings: document.getElementById('viewSettings'),
- };
- const navItems = document.querySelectorAll('.nav-item[data-view]');
- let settingsLoaded = false;
- let logTimer = null;
- let lastLogSignature = '';
- function showSettingsStatus(message, type = 'info') {
- const status = document.getElementById('settingsStatus');
- status.textContent = message;
- status.className = `badge ${type === 'success' ? 'badge-ok' : type === 'error' ? 'badge-fail' : 'badge-info'}`;
- }
- async function loadModels(selectedModel) {
- const select = document.getElementById('settingsModel');
- const response = await fetch('/api/models');
- if (!response.ok) throw new Error('模型列表加载失败');
- const data = await response.json();
- select.innerHTML = '';
- for (const model of data.models || []) {
- const option = document.createElement('option');
- option.value = typeof model === 'string' ? model : model.id;
- option.textContent = typeof model === 'string' ? model : (model.label || model.name || model.id);
- option.selected = option.value === selectedModel;
- select.appendChild(option);
- }
- }
- async function loadSettings() {
- showSettingsStatus('加载中…');
- const response = await fetch('/api/settings');
- if (!response.ok) throw new Error('设置加载失败');
- const settings = await response.json();
- document.getElementById('settingsApiKey').value = settings.apiKey || '';
- document.getElementById('settingsApiBase').value = settings.apiBase || '';
- document.getElementById('settingsRootDir').value = settings.rootDir || '';
- document.getElementById('pageSizeInput').value = settings.pageSize || 50;
- document.getElementById('delayInput').value = settings.delayMs ?? 300;
- document.getElementById('settingsCookie').value = settings.cookie || '';
- await loadModels(settings.model);
- showSettingsStatus('已同步');
- }
- async function saveSettings() {
- const button = document.getElementById('btnSaveSettings');
- button.disabled = true;
- showSettingsStatus('保存中…');
- try {
- const response = await fetch('/api/settings', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- apiKey: document.getElementById('settingsApiKey').value.trim(),
- model: document.getElementById('settingsModel').value,
- rootDir: document.getElementById('settingsRootDir').value.trim(),
- pageSize: Number(document.getElementById('pageSizeInput').value),
- delayMs: Number(document.getElementById('delayInput').value),
- cookie: document.getElementById('settingsCookie').value.trim(),
- }),
- });
- const data = await response.json();
- if (!response.ok) throw new Error(data.error || `HTTP ${response.status}`);
- await loadSettings();
- showSettingsStatus('已保存', 'success');
- } catch (error) {
- showSettingsStatus(error.message, 'error');
- } finally {
- button.disabled = false;
- }
- }
- async function initSettings() {
- if (settingsLoaded) return;
- document.getElementById('btnSaveSettings').addEventListener('click', saveSettings);
- document.getElementById('btnClearLog').addEventListener('click', () => {
- clearLogs().catch(() => {});
- });
- try {
- await loadSettings();
- settingsLoaded = true;
- } catch (error) {
- showSettingsStatus(error.message, 'error');
- }
- }
- function escapeLogText(value) {
- const div = document.createElement('div');
- div.textContent = String(value ?? '');
- return div.innerHTML;
- }
- function renderLogs(logs) {
- const panel = document.getElementById('logPanel');
- if (!panel) return;
- const sourceMap = { task: '任务', system: '系统' };
- const levelMap = { info: 'INFO', success: 'OK', warn: 'WARN', error: 'FAIL' };
- panel.innerHTML = logs.map((log) => {
- const level = levelMap[log.level] || 'INFO';
- const levelClass = log.level === 'success' ? 'ok' : log.level || 'info';
- return `
- <div class="log-line">
- <span class="log-ts">${escapeLogText(log.time)}</span>
- <span class="log-source ${log.source === 'system' ? 'system' : 'task'}">${sourceMap[log.source] || log.source}</span>
- <span class="log-lvl ${levelClass}">${level}</span>
- <span class="log-msg">${escapeLogText(log.message)}</span>
- </div>`;
- }).join('');
- }
- async function refreshLogs() {
- const response = await fetch('/api/logs');
- if (!response.ok) return;
- const data = await response.json();
- const logs = (data.logs || []).sort((a, b) => b.seq - a.seq);
- const signature = logs.map((log) => `${log.seq}:${log.level}:${log.message}`).join('|');
- if (signature === lastLogSignature) return;
- lastLogSignature = signature;
- renderLogs(logs);
- }
- function startLogPolling() {
- if (logTimer) return;
- refreshLogs();
- logTimer = setInterval(refreshLogs, 1500);
- }
- async function clearLogs() {
- await fetch('/api/logs/clear', { method: 'POST' });
- lastLogSignature = '';
- await refreshLogs();
- }
- function switchView(name) {
- for (const [key, el] of Object.entries(views)) {
- el.classList.toggle('active', key === name);
- }
- navItems.forEach((n) => n.classList.toggle('active', n.dataset.view === name));
- if (name === 'crawl' && window.shanCrawlInit) window.shanCrawlInit();
- if (name === 'gen' && window.shanGenInit) window.shanGenInit();
- if (name === 'history' && window.shanHistoryInit) window.shanHistoryInit();
- if (name === 'settings') initSettings();
- }
- navItems.forEach((n) => n.addEventListener('click', () => switchView(n.dataset.view)));
- switchView('crawl');
- startLogPolling();
- const splash = document.getElementById('splash');
- if (splash) {
- setTimeout(() => splash.classList.add('hide'), 1800);
- setTimeout(() => splash.remove(), 2400);
- }
- })();
|