- +
-

- 系统概览 -

+ +
+
☀️ 早上好,{{ currentUser.username || '创作者' }}
+
+ 你的团队本月已完成 {{ stats.published }} 篇内容发布 + +
+
- +
加载中...
- -
- -
暂无数据
-
- -
-
-
选题总数
-
{{ stats.total }}
-
-
-
待处理
-
{{ stats.pending }}
-
-
-
待审查
-
{{ stats.review }}
-
-
-
待发布
-
{{ stats.ready }}
-
-
-
已发布
-
{{ stats.published }}
-
-
-
今日新增
-
{{ stats.today }}
-
-
- -

- 模块状态 - - 定时任务: - - - {{ schedulerRunning ? '运行中' : '未启动' }} - -

-
-
-
- {{ mod.title }} - - - - {{ mod.enabled ? '已启用' : '已禁用' }} - + +
@@ -214,6 +405,8 @@ currentUser: { username: '' }, currentPage: 'overview', loadingStats: false, + loadingTodos: false, + loadingSuggestions: false, stats: { total: 0, pending: 0, @@ -222,6 +415,11 @@ published: 0, today: 0 }, + todoTopics: [], + monthlyGoal: 0, + monthlyProgress: 0, + topTrendingTopic: null, + adminCollapsed: true, modules: [], schedulerStatus: '', schedulerRunning: false, @@ -254,6 +452,10 @@ this.isLoggedIn = true; this.currentPage = 'overview'; this.fetchStats(); + this.fetchModules(); + this.fetchUpcomingPlan(); + this.fetchTopTopics(); + this.computeMonthlyGoal(); } catch (error) { this.loginError = '用户名或密码错误'; } finally { @@ -443,6 +645,145 @@ this.$message.success('已保存'); } catch (e) { this.$message.error('保存失败: ' + e.message); } finally { this.promptSaving = null; } + }, + async fetchTodos() { + this.loadingTodos = true; + try { + const token = localStorage.getItem('authToken'); + const resp = await fetch('/api/topics?limit=10&sort_by=priority_score&order=desc', { + headers: { 'Authorization': 'Bearer ' + token } + }); + if (resp.ok) { + const data = await resp.json(); + this.todoTopics = (data || []).filter(t => t.status === 'pending' || t.status === 'review' || t.status === 'ready'); + } + } catch (e) { + console.error('获取待办失败:', e); + } finally { this.loadingTodos = false; } + }, + async fetchSuggestions() { + this.loadingSuggestions = true; + try { + const token = localStorage.getItem('authToken'); + const resp = await fetch('/api/topics?limit=5&sort_by=priority_score&order=desc', { + headers: { 'Authorization': 'Bearer ' + token } + }); + if (resp.ok) { + const data = await resp.json(); + if (data && data.length > 0) { + this.topTrendingTopic = { + title: data[0].title, + score: data[0].total_score || data[0].priority_score + }; + } + } + } catch (e) { + console.error('获取建议失败:', e); + } finally { this.loadingSuggestions = false; } + }, + getPriorityClass(topic) { + const p = topic.priority || ''; + if (p === '高' || p === 'high' || topic.priority_score >= 80) return 'high'; + if (p === '中' || p === 'medium' || topic.priority_score >= 40) return 'medium'; + return 'low'; + }, + getPriorityTagType(topic) { + const cls = this.getPriorityClass(topic); + return cls === 'high' ? 'danger' : cls === 'medium' ? 'warning' : 'info'; + }, + getPriorityLabel(topic) { + const p = topic.priority || ''; + if (p === '高' || p === 'high' || topic.priority_score >= 80) return '高优先级'; + if (p === '中' || p === 'medium' || topic.priority_score >= 40) return '中优先级'; + return '普通'; + }, + isOverdue(topic) { + if (!topic.ready_at && !topic.published_at) return false; + const target = new Date(topic.ready_at || topic.published_at); + const today = new Date(); + today.setHours(0, 0, 0, 0); + return target < today && topic.status !== 'published'; + }, + getStatusLabel(status) { + const map = { 'pending': '待处理', 'review': '待审查', 'ready': '待发布', 'published': '已发布', 'draft': '草稿' }; + return map[status] || status; + }, + getPriorityClass(topic) { + if (topic.priority_score >= 80) return 'high'; + if (topic.priority_score >= 50) return 'medium'; + return 'low'; + }, + getPriorityLabel(topic) { + if (topic.priority_score >= 80) return '高优先级'; + if (topic.priority_score >= 50) return '中优先级'; + return '低优先级'; + }, + getPriorityTagType(topic) { + if (topic.priority_score >= 80) return 'danger'; + if (topic.priority_score >= 50) return 'warning'; + return 'success'; + }, + getStatusLabel(status) { + const map = { + 'pending': '待处理', 'review': '待审查', 'draft': '草稿', + 'ready': '待发布', 'published': '已发布' + }; + return map[status] || status; + }, + isOverdue(topic) { + if (!topic.due_date) return false; + try { + const due = new Date(topic.due_date); + const now = new Date(); + now.setHours(23, 59, 59, 999); + return due < now; + } catch (e) { return false; } + }, + computeMonthlyGoal() { + try { + const now = new Date(); + const year = now.getFullYear(); + const month = now.getMonth() + 1; + const published = this.stats.published || 0; + // Goal: 10 articles/month minimum, shown as percentage + const goal = Math.max(10, published * 1.5); + this.monthlyGoal = Math.round(goal); + this.monthlyProgress = Math.min(100, Math.round((published / goal) * 100)); + } catch (e) { + this.monthlyGoal = 10; + this.monthlyProgress = 0; + } + }, + async fetchTopTopics() { + this.loadingTodos = true; + this.loadingSuggestions = true; + try { + const token = localStorage.getItem('authToken'); + if (!token) return; + // Top priority topics for todo list + const topicsResp = await fetch('/api/topics?limit=10&sort_by=priority_score&order=desc', { + headers: { 'Authorization': 'Bearer ' + token } + }); + if (topicsResp.ok) { + const topics = await topicsResp.json(); + // Filter to pending/review/ready for todo list + this.todoTopics = (topics || []).filter(t => + ['pending', 'review', 'draft', 'ready'].includes(t.status) + ).slice(0, 5); + // Top trending for AI suggestion + if (this.todoTopics.length > 0) { + this.topTrendingTopic = this.todoTopics[0]; + } + } + } catch (e) { + console.error('获取待办话题失败:', e); + } finally { + this.loadingTodos = false; + this.loadingSuggestions = false; + } + }, + toggleAdminSection() { + this.adminCollapsed = !this.adminCollapsed; } }, mounted() { @@ -463,6 +804,10 @@ this.fetchStats(); this.fetchModules(); this.fetchUpcomingPlan(); + this.fetchTopTopics(); + this.computeMonthlyGoal(); + this.fetchTodos(); + this.fetchSuggestions(); }) .catch(() => { localStorage.removeItem('authToken');