// 修复后的 Vue 3 setup 函数体 // 所有变量和方法必须在 return 之前定义 const API_BASE = window.location.origin; // 1. 状态变量 const isLoggedIn = ref(false); const isAdmin = ref(false); const loginForm = reactive({ username: '', password: '' }); const loginError = ref(''); const status = ref({}); const topics = ref([]); const filterStatus = ref(''); const generating = ref(false); const optimizing = ref(false); const loadingAll = ref(false); const loadingTable = ref(false); const loadingLogs = ref(false); const loadingOverlay = ref(false); const loadingText = ref(''); const pipeline = ref({ status_distribution: {} }); const pipelineLoading = ref(false); const pipelineModules = ref([]); const previewVisible = ref(false); const previewTopic = ref({ title: '' }); const previewPlatform = ref('zhihu'); const previewHtml = ref(''); const fullScreenPreview = ref(false); const showLogs = ref(false); const logType = ref('creator'); const logDate = ref(new Date().toISOString().split('T')[0]); const logContent = ref(''); // 2. 计算属性 const filteredTopics = computed(() => { if (!filterStatus.value) return topics.value || []; return (topics.value || []).filter(t => t && t.status === filterStatus.value); }); // 3. 工具函数 const formatDate = (val) => { if (!val) return '-'; const d = new Date(val); if (isNaN(d.getTime())) return val; return d.toLocaleString('zh-CN', { hour12: false }); }; const formatRelativeTime = (val) => { if (!val) return '-'; const d = new Date(val); if (isNaN(d.getTime())) return '-'; const now = new Date(); const diff = now - d; const minutes = Math.floor(diff / 60000); if (minutes < 1) return '刚刚'; if (minutes < 60) return `${minutes}分钟前`; const hours = Math.floor(minutes / 60); if (hours < 24) return `${hours}小时前`; const days = Math.floor(hours / 24); if (days < 7) return `${days}天前`; return formatDate(val); }; // 4. 业务方法 const countByStatus = (status) => { return (topics.value || []).filter(t => t.status === status).length; }; const getPriorityType = (score) => { if (!score) return ''; if (score >= 20) return 'danger'; if (score >= 15) return 'warning'; return 'success'; }; const getStatusClass = (status) => { const map = { '待处理': 'pending', '待审查': 'review', '待发布': 'ready', '已发布': 'published' }; return map[status] || ''; }; const refresh = async () => { try { const [s, t] = await Promise.all([ fetch(API_BASE + '/api/system/status').then(r => r.json()), fetch(API_BASE + '/api/topics').then(r => r.json()) ]); status.value = s; topics.value = t; } catch (e) { ElMessage.error('刷新失败:' + e.message); } }; const refreshPipeline = async () => { pipelineLoading.value = true; try { const res = await fetch(API_BASE + '/api/system/pipeline/status'); if (res.ok) { const data = await res.json(); pipeline.value = data; pipelineModules.value = Object.entries(data.pipeline_modules || {}).map(([name, info]) => ({ module: name, last_run: info.last_run || '未运行', status_ok: !info.has_error && info.exists, status_text: info.exists && !info.has_error ? '正常' : info.exists ? '有错误' : '缺失', error: info.has_error ? '检测到错误' : '' })); } } catch (e) { ElMessage.error('获取流水线状态失败'); } finally { pipelineLoading.value = false; } }; const refreshAll = async () => { loadingAll.value = true; try { await Promise.all([refresh(), refreshPipeline()]); ElMessage.success('刷新成功'); } catch (e) { ElMessage.error('刷新失败'); } finally { loadingAll.value = false; } }; const triggerGenerate = async () => { generating.value = true; try { const res = await fetch(API_BASE + '/api/system/generate/run', { method: 'POST' }); const data = await res.json(); if (data.result && data.result.ok) { ElMessage.success('创作任务已启动'); setTimeout(refresh, 3000); } else { ElMessage.error('启动失败:' + (data.error || '未知错误')); } } catch (e) { ElMessage.error('请求失败:' + e.message); } finally { generating.value = false; } }; const triggerOptimize = async () => { optimizing.value = true; try { const res = await fetch(API_BASE + '/api/system/optimize/run', { method: 'POST' }); const data = await res.json(); if (data.summary) { ElNotification({ title: '优化完成', message: `自动通过 ${data.summary.passed_auto || 0} 篇,需人工 ${data.summary.need_manual || 0} 篇`, type: 'success' }); await refresh(); } else { ElMessage.success('优化完成'); } } catch (e) { ElMessage.error('优化失败:' + e.message); } finally { optimizing.value = false; } }; const openPreview = async (topic) => { previewTopic.value = { id: topic.id, title: topic.title }; previewPlatform.value = 'zhihu'; previewVisible.value = true; await loadPreview(); }; const loadPreview = async () => { previewHtml.value = ''; console.log('[Preview] Loading topic:', previewTopic.value.id, 'platform:', previewPlatform.value); try { const res = await fetch(`${API_BASE}/api/articles/${previewTopic.value.id}/preview?platform=${previewPlatform.value}`); console.log('[Preview] Response status:', res.status); if (res.ok) { const data = await res.json(); console.log('[Preview] Got HTML, length:', data.html?.length); const parser = new DOMParser(); const doc = parser.parseFromString(data.html, 'text/html'); const contentDiv = doc.querySelector('.content'); console.log('[Preview] Found .content:', !!contentDiv); if (contentDiv) { previewHtml.value = contentDiv.innerHTML; console.log('[Preview] Set previewHtml from .content'); } else { const header = doc.querySelector('.header'); const footer = doc.querySelector('footer'); const tags = doc.querySelector('.tags'); const interaction = doc.querySelector('.interaction'); if (header) header.remove(); if (footer) footer.remove(); if (tags) tags.remove(); if (interaction) interaction.remove(); previewHtml.value = doc.body.innerHTML; console.log('[Preview] Set previewHtml from body.innerHTML'); } } else if (res.status === 404) { previewHtml.value = '
暂未创作文章,请先点击创作按钮生成
请求失败,请检查后端服务是否运行