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: '', }, 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) { if (!timestamp) return '-'; return String(timestamp).slice(0, 19); }, formatPercent(value) { if (value === null || value === undefined) return '-'; return `${(Number(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'; case 'in_progress': return 'text-warning'; 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'; }, async fetchAPI(url) { try { const response = await fetch(url); return await response.json(); } catch (error) { return { success: false, error: String(error) }; } }, 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');