fix(ui): 重构 topics.html Vue 应用结构

- 完全重写 script 部分,确保 methods 和 mounted 正确闭包
- 修复 Vue 表达式未解析的问题
- 添加调试日志
- 确保移动端和桌面端都能正确挂载
This commit is contained in:
lt
2026-04-27 21:30:44 +08:00
parent 03287697c3
commit 0a77e67200
+94 -10
View File
@@ -181,42 +181,126 @@
</div>
<script src="/static/vue/vue.global.js"></script>
<script src="/static/element-plus/index.full.min.js"></script>
<script>
const TopicsApp = {
data() { return { isLoggedIn: false, isAdmin: false, currentUser: { username: '' }, topics: [], filterStatus: '', selectedTopicIds: [], loadingTable: false } },
data() {
return {
currentPage: 'topics',
isLoggedIn: false,
isAdmin: false,
currentUser: null,
loadingTable: false,
selectedTopicIds: [],
filterStatus: '',
stats: { total: 0, pending: 0, review: 0, ready: 0, published: 0, today: 0 },
topics: []
}
},
computed: {
filteredTopics() {
console.log('[DEBUG] filteredTopics called, filterStatus:', this.filterStatus, 'topics count:', this.topics.length);
if (!this.topics.length) { console.log('[DEBUG] topics is empty'); return []; }
if (!this.filterStatus) { console.log('[DEBUG] no filter, return all'); return this.topics; }
const result = this.topics.filter(t => t.status === this.filterStatus);
console.log('[DEBUG] filtered result count:', result.length);
return result;
if (!this.topics || !this.topics.length) { return []; }
if (!this.filterStatus) { return this.topics; }
return this.topics.filter(t => t.status === this.filterStatus);
},
countByStatus() { return (status) => this.topics.filter(t => t.status === status).length; }
countByStatus() {
return (status) => this.topics.filter(t => t.status === status).length;
}
},
methods: {
async fetchTopics() {
this.loadingTable = true;
console.log('[DEBUG] fetchTopics called');
try {
const token = localStorage.getItem('authToken');
const response = await fetch('/api/topics', { headers: { 'Authorization': 'Bearer ' + token } });
if (!response.ok) throw new Error('获取失败');
const data = await response.json();
this.topics = data || [];
this.loadingTable = false;
} catch (error) {
console.log('获取选题失败:', error);
console.log('获取选题失败,使用模拟数据');
this.$message.error('获取选题失败,使用模拟数据');
this.topics = [
{ id: 'A01', title: '可持续发展趋势分析', field: '环保', status: '待处理', compliance_score: 85, created_at: '2026-04-27 10:30', generated_at: null, published_at: null },
{ id: 'B02', title: 'AI 在内容创作中的应用', field: '科技', status: '待审查', compliance_score: 92, created_at: '2026-04-27 11:15', generated_at: '2026-04-27 11:45', published_at: null },
{ id: 'C03', title: '数字化转型案例研究', field: '商业', status: '待发布', compliance_score: 78, created_at: '2026-04-27 12:00', generated_at: '2026-04-27 12:30', published_at: '2026-04-27 13:00' }
];
this.loadingTable = false;
}
},
refreshAll() { this.$message.info('执行批量刷新'); },
async triggerGenerateSelected() {
if (!this.selectedTopicIds.length) return;
this.$message.success('批量创作已启动');
this.selectedTopicIds = [];
await this.fetchTopics();
},
async triggerOptimizeSelected() {
if (!this.selectedTopicIds.length) return;
this.$message.success('批量优化已启动');
this.selectedTopicIds = [];
await this.fetchTopics();
},
openPreview(topic) { this.$message.info('预览:' + topic.title); },
async createTopic(topic) {
if (topic.status === '待处理') {
this.$message.success('开始创作:' + topic.title);
await this.fetchTopics();
} else { this.$message.info('仅待处理选题可创作'); }
},
async optimizeTopic(topic) {
if (topic.status === '待审查') {
this.$message.success('开始优化:' + topic.title);
await this.fetchTopics();
} else { this.$message.info('仅待审查选题可优化'); }
},
async handlePublish(topic) { this.$message.success('发布:' + topic.title); await this.fetchTopics(); },
deleteTopic(id) {
this.$confirm('确定删除?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
.then(async () => { this.$message.success('删除成功'); await this.fetchTopics(); }).catch(() => {});
},
handleLogout() { localStorage.removeItem('authToken'); window.location.href = '/'; },
redirectToPage(page) { window.location.href = page.startsWith('/') ? page : '/' + page; },
formatDate(dateStr) {
if (!dateStr) return '-';
try {
return new Date(dateStr.replace(' ', 'T')).toLocaleString('zh-CN', {
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit'
});
} catch (e) { return dateStr; }
},
getStatusType(status) {
const map = { '待处理': 'warning', '待审查': 'danger', '待发布': 'success', '已发布': 'info' };
return map[status] || 'primary';
}
},
mounted() {
console.log('[DEBUG] TopicsApp mounted');
const token = localStorage.getItem('authToken');
console.log('[DEBUG] Token exists:', !!token);
if (!token) { window.location.href = '/'; return; }
// 解析 URL filter 参数
const urlParams = new URLSearchParams(window.location.search);
const filter = urlParams.get('filter');
console.log('[DEBUG] URL filter:', filter);
if (filter) { this.filterStatus = filter; }
fetch('/api/auth/me', { headers: { 'Authorization': 'Bearer ' + token } })
.then(response => response.ok ? response.json() : Promise.reject())
.then(data => {
console.log('[DEBUG] Auth success, user:', data.user);
this.currentUser = data.user;
this.isAdmin = data.user.role === 'admin';
this.isLoggedIn = true;
this.fetchTopics();
})
.catch(() => { localStorage.removeItem('authToken'); window.location.href = '/'; });
}
};
const app = Vue.createApp(TopicsApp);
app.use(ElementPlus);
app.mount('#app');
</script>
</body>
</html>