// Wiki 页面组件:百科条目管理 + 历史上的今天(维基百科)同步 export default { template: `

百科条目(共 {{ entries.length }} 条)

ID 分类 标题 简介 排序 状态 操作
{{ e.id }} {{ catName(e.category) }} {{ e.title }} {{ e.brief }} {{ e.sort }} {{ e.status === 1 ? '显示' : '隐藏' }}
暂无条目

维基百科数据同步

数据来源:中文维基百科「X月X日」页面(共 366 页,含大事记/出生/逝世/节假日习俗)。服务启动时若数据缺失会自动抓取;之后按配置间隔(默认 7 天)定时增量刷新。

日期覆盖
{{ syncStatus.totalDays || 0 }} / 366 天
条目总数
{{ syncStatus.totalItems || 0 }}
同步状态
进行中 已完成 部分失败 失败 未同步
最近同步:{{ formatTime(syncStatus.lastSync.startedAt) }} ~ {{ syncStatus.lastSync.finishedAt ? formatTime(syncStatus.lastSync.finishedAt) : '进行中' }}
触发方式:{{ triggerName(syncStatus.lastSync.trigger) }} | 成功 {{ syncStatus.lastSync.success }} 页 / 失败 {{ syncStatus.lastSync.failed }} 页

{{ editor.id ? '编辑条目' : '新增条目' }}

`, data() { return { tab: 'entries', categories: [ { key: 'term', name: '二十四节气' }, { key: 'lunarFest', name: '农历节日' }, { key: 'solarFest', name: '公历节日' }, { key: 'ganzhi', name: '干支生肖' }, { key: 'termExplain', name: '黄历术语' }, { key: 'other', name: '其他' }, ], entries: [], filterCat: '', filterKeyword: '', editor: { visible: false, id: null, form: { category: 'other', title: '', brief: '', content: '', sort: 0, status: 1 }, }, saving: false, syncing: false, syncStatus: {}, syncTimer: null, }; }, mounted() { this.loadEntries(); this.loadSyncStatus(); }, beforeUnmount() { if (this.syncTimer) clearInterval(this.syncTimer); }, methods: { catName(key) { const c = this.categories.find(x => x.key === key); return c ? c.name : key; }, formatTime(t) { if (!t) return ''; return new Date(t).toLocaleString('zh-CN', { hour12: false }); }, triggerName(t) { return { startup: '启动自动', cron: '定时任务', admin: '手动触发' }[t] || t; }, async api(url, options = {}) { const res = await fetch(url, { headers: { 'Content-Type': 'application/json' }, ...options, }); if (res.status === 401) { alert('登录已过期,请重新登录'); window.location.href = '/admin'; throw new Error('未授权'); } const data = await res.json(); if (data.code !== 0) throw new Error(data.msg || '请求失败'); return data.data; }, async loadEntries() { try { const params = new URLSearchParams(); if (this.filterCat) params.set('category', this.filterCat); if (this.filterKeyword) params.set('keyword', this.filterKeyword); const data = await this.api('/admin/api/wiki/entries?' + params.toString()); this.entries = data.list || []; } catch (e) { console.error(e); } }, openEditor(entry) { this.editor.id = entry ? entry.id : null; this.editor.form = entry ? { category: entry.category, title: entry.title, brief: entry.brief, content: entry.content, sort: entry.sort, status: entry.status } : { category: 'other', title: '', brief: '', content: '', sort: 0, status: 1 }; this.editor.visible = true; }, async saveEntry() { const f = this.editor.form; if (!f.title.trim()) { alert('标题不能为空'); return; } this.saving = true; try { if (this.editor.id) { await this.api(`/admin/api/wiki/entries/${this.editor.id}`, { method: 'PUT', body: JSON.stringify(f) }); } else { await this.api('/admin/api/wiki/entries', { method: 'POST', body: JSON.stringify(f) }); } this.editor.visible = false; this.loadEntries(); } catch (e) { alert(e.message); } finally { this.saving = false; } }, async removeEntry(entry) { if (!confirm(`确认删除条目「${entry.title}」?`)) return; try { await this.api(`/admin/api/wiki/entries/${entry.id}`, { method: 'DELETE' }); this.loadEntries(); } catch (e) { alert(e.message); } }, async loadSyncStatus() { try { this.syncStatus = await this.api('/admin/api/wiki/sync-status'); // 同步进行中时每 10 秒自动刷新 if (this.syncStatus.running && !this.syncTimer) { this.syncTimer = setInterval(() => this.loadSyncStatus(), 10000); } else if (!this.syncStatus.running && this.syncTimer) { clearInterval(this.syncTimer); this.syncTimer = null; } } catch (e) { console.error(e); } }, async triggerSync() { if (!confirm('全量同步将抓取 366 个维基百科页面(约 3-5 分钟),确认执行?')) return; this.syncing = true; try { await this.api('/admin/api/wiki/sync', { method: 'POST' }); this.loadSyncStatus(); } catch (e) { alert(e.message); } finally { this.syncing = false; } }, }, }