/** * BTC 预测交易看板 - Vue 3 应用 */ const { createApp } = Vue; const app = createApp({ data() { return { // 状态 loading: false, activeTab: 'trades', serverTime: '', // 当前交易 currentTrade: null, // 交易历史 tradeHistory: { data: [], page: 1, limit: 10, total: 0, total_pages: 0 }, tradeFilter: { month: '', day: '' }, // 准确率 accuracyFilter: { month: this.getCurrentMonth() }, monthlyStats: {}, dailyStats: { data: [], page: 1, limit: 10, total: 0, total_pages: 0 }, // 弹窗数据 hourlyModal: { date: '', data: [] } }; }, mounted() { // 初始化月份筛选 this.accuracyFilter.month = this.getCurrentMonth(); // 加载数据 this.loadCurrentTrade(); this.loadTradeHistory(1); // 每分钟更新时间 this.updateServerTime(); setInterval(() => this.updateServerTime(), 60000); }, methods: { // ==================== 工具方法 ==================== getCurrentMonth() { const now = new Date(); return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`; }, updateServerTime() { const now = new Date(); this.serverTime = now.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); }, formatTime(timestamp, timestamp_cst) { // 优先使用后端返回的 CST 时间 if (timestamp_cst) { return timestamp_cst; } if (!timestamp) return '-'; const date = new Date(timestamp); return date.toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); }, formatPercent(value) { if (value === null || value === undefined) return '-'; return `${(value * 100).toFixed(1)}%`; }, getPredictionClass(prediction) { if (prediction === 'UP') return 'text-success fw-bold'; if (prediction === 'DOWN') return 'text-danger fw-bold'; return ''; }, getStatusClass(status) { switch (status) { case 'success': return 'text-success'; case 'test': return 'text-info'; case 'failure': return 'text-danger'; default: return 'text-muted'; } }, getStatusText(status) { switch (status) { case 'success': return '成功'; case 'test': return '测试'; case 'failure': return '失败'; case 'in_progress': return '进行中'; default: return status || '-'; } }, getAccuracyClass(accuracy) { if (accuracy >= 60) return 'text-success fw-bold'; if (accuracy >= 50) return 'text-warning'; return 'text-danger'; }, // ==================== API 调用 ==================== async fetchAPI(url) { try { const response = await fetch(url); const data = await response.json(); if (!data.success) { console.error('API Error:', data); } return data; } catch (error) { console.error('Fetch Error:', error); return { success: false, error: error.message }; } }, async loadCurrentTrade() { const data = await this.fetchAPI('/api/trades/current'); if (data.success) { this.currentTrade = data.data; } }, async loadTradeHistory(page) { if (page < 1) return; this.loading = true; let url = `/api/trades/history?page=${page}&limit=${this.tradeHistory.limit}`; if (this.tradeFilter.month) { url += `&month=${this.tradeFilter.month}`; } if (this.tradeFilter.day) { url += `&day=${this.tradeFilter.day}`; } const data = await this.fetchAPI(url); if (data.success) { this.tradeHistory = { data: data.data, page: data.page, limit: data.limit, total: data.total, total_pages: data.total_pages }; } this.loading = false; }, clearTradeFilter() { this.tradeFilter.month = ''; this.tradeFilter.day = ''; this.loadTradeHistory(1); }, async loadAccuracyData() { this.loading = true; // 加载月度统计 const monthlyData = await this.fetchAPI( `/api/accuracy/monthly?month=${this.accuracyFilter.month}` ); if (monthlyData.success) { this.monthlyStats = monthlyData.data || {}; } // 加载每日统计 await this.loadDailyStats(1); this.loading = false; }, async loadDailyStats(page) { if (page < 1) return; const data = await this.fetchAPI( `/api/accuracy/daily?month=${this.accuracyFilter.month}&page=${page}&limit=10` ); if (data.success) { this.dailyStats = { data: data.data, page: data.page, limit: data.limit, total: data.total, total_pages: data.total_pages }; } }, async showHourlyDetails(date) { this.loading = true; this.hourlyModal.date = date; const data = await this.fetchAPI(`/api/accuracy/hourly/${date}`); if (data.success) { this.hourlyModal.data = data.data || []; } this.loading = false; // 显示弹窗 const modal = new bootstrap.Modal(document.getElementById('hourlyModal')); modal.show(); } }, watch: { activeTab(newTab) { if (newTab === 'accuracy' && !this.dailyStats.data.length) { this.loadAccuracyData(); } } } }); app.mount('#app');