diff --git a/frontend/paper-trading.html b/frontend/paper-trading.html index c835b8b..23f5ac1 100644 --- a/frontend/paper-trading.html +++ b/frontend/paper-trading.html @@ -1907,9 +1907,11 @@ const data = await response.json(); if (data.success) { this.dailyReturns = data.data; - // 等待 DOM 更新后渲染图表 + // 等待 DOM 更新后渲染图表,添加额外延迟确保 canvas 完全渲染 this.$nextTick(() => { - this.renderCharts(); + setTimeout(() => { + this.renderCharts(); + }, 100); }); } } catch (e) { @@ -1928,154 +1930,198 @@ // 渲染净值曲线图 renderBalanceChart() { const canvas = document.getElementById('balanceChart'); - if (!canvas) return; + if (!canvas) { + console.warn('balanceChart canvas not found'); + return; + } // 销毁已存在的图表 if (this.balanceChart) { this.balanceChart.destroy(); + this.balanceChart = null; } const ctx = canvas.getContext('2d'); + if (!ctx) { + console.warn('Cannot get 2d context'); + return; + } + + // 确保有数据 + if (!this.dailyReturns || this.dailyReturns.length === 0) { + console.warn('No daily returns data'); + return; + } + const labels = this.dailyReturns.map(d => d.date); const balanceData = this.dailyReturns.map(d => d.balance); - this.balanceChart = new Chart(ctx, { - type: 'line', - data: { - labels: labels, - datasets: [{ - label: '账户净值', - data: balanceData, - borderColor: '#00ff41', - backgroundColor: 'rgba(0, 255, 65, 0.1)', - borderWidth: 2, - fill: true, - tension: 0.4, - pointRadius: 2, - pointHoverRadius: 5 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - display: false - }, - tooltip: { - mode: 'index', - intersect: false, - callbacks: { - label: function(context) { - return '净值: $' + context.parsed.y.toFixed(2); + console.log('Rendering balance chart:', { labels, balanceData }); + + try { + this.balanceChart = new Chart(ctx, { + type: 'line', + data: { + labels: labels, + datasets: [{ + label: '账户净值', + data: balanceData, + borderColor: '#00ff41', + backgroundColor: 'rgba(0, 255, 65, 0.1)', + borderWidth: 2, + fill: true, + tension: 0.4, + pointRadius: 2, + pointHoverRadius: 5 + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false + }, + tooltip: { + mode: 'index', + intersect: false, + callbacks: { + label: function(context) { + return '净值: $' + context.parsed.y.toFixed(2); + } } } - } - }, - scales: { - x: { - display: true, - grid: { - color: 'rgba(255, 255, 255, 0.1)' - }, - ticks: { - color: 'rgba(255, 255, 255, 0.6)', - maxRotation: 45, - minRotation: 45 - } }, - y: { - display: true, - grid: { - color: 'rgba(255, 255, 255, 0.1)' + scales: { + x: { + display: true, + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + color: 'rgba(255, 255, 255, 0.6)', + maxRotation: 45, + minRotation: 45 + } }, - ticks: { - color: 'rgba(255, 255, 255, 0.6)', - callback: function(value) { - return '$' + value.toFixed(0); + y: { + display: true, + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + color: 'rgba(255, 255, 255, 0.6)', + callback: function(value) { + return '$' + value.toFixed(0); + } } } + }, + interaction: { + mode: 'nearest', + axis: 'x', + intersect: false } - }, - interaction: { - mode: 'nearest', - axis: 'x', - intersect: false } - } - }); + }); + console.log('Balance chart rendered successfully'); + } catch (e) { + console.error('Error rendering balance chart:', e); + } }, // 渲染收益率柱状图 renderReturnsChart() { const canvas = document.getElementById('returnsChart'); - if (!canvas) return; + if (!canvas) { + console.warn('returnsChart canvas not found'); + return; + } // 销毁已存在的图表 if (this.returnsChart) { this.returnsChart.destroy(); + this.returnsChart = null; } const ctx = canvas.getContext('2d'); + if (!ctx) { + console.warn('Cannot get 2d context'); + return; + } + + // 确保有数据 + if (!this.dailyReturns || this.dailyReturns.length === 0) { + console.warn('No daily returns data'); + return; + } + const labels = this.dailyReturns.map(d => d.date); const returnsData = this.dailyReturns.map(d => d.return_percent); const colors = returnsData.map(v => v >= 0 ? '#00ff41' : '#ff4444'); - this.returnsChart = new Chart(ctx, { - type: 'bar', - data: { - labels: labels, - datasets: [{ - label: '日收益率', - data: returnsData, - backgroundColor: colors, - borderColor: colors, - borderWidth: 1 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - display: false - }, - tooltip: { - callbacks: { - label: function(context) { - const value = context.parsed.y; - return '收益率: ' + (value >= 0 ? '+' : '') + value.toFixed(2) + '%'; - } - } - } + console.log('Rendering returns chart:', { labels, returnsData }); + + try { + this.returnsChart = new Chart(ctx, { + type: 'bar', + data: { + labels: labels, + datasets: [{ + label: '日收益率', + data: returnsData, + backgroundColor: colors, + borderColor: colors, + borderWidth: 1 + }] }, - scales: { - x: { - display: true, - grid: { + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { display: false }, - ticks: { - color: 'rgba(255, 255, 255, 0.6)', - maxRotation: 45, - minRotation: 45 + tooltip: { + callbacks: { + label: function(context) { + const value = context.parsed.y; + return '收益率: ' + (value >= 0 ? '+' : '') + value.toFixed(2) + '%'; + } + } } }, - y: { - display: true, - grid: { - color: 'rgba(255, 255, 255, 0.1)' + scales: { + x: { + display: true, + grid: { + display: false + }, + ticks: { + color: 'rgba(255, 255, 255, 0.6)', + maxRotation: 45, + minRotation: 45 + } }, - ticks: { - color: 'rgba(255, 255, 255, 0.6)', - callback: function(value) { - return value.toFixed(1) + '%'; + y: { + display: true, + grid: { + color: 'rgba(255, 255, 255, 0.1)' + }, + ticks: { + color: 'rgba(255, 255, 255, 0.6)', + callback: function(value) { + return value.toFixed(1) + '%'; + } } } } } - } - }); + }); + console.log('Returns chart rendered successfully'); + } catch (e) { + console.error('Error rendering returns chart:', e); + } } }, computed: { @@ -2161,9 +2207,11 @@ if (this.dailyReturns.length === 0) { this.fetchDailyReturns(); } else { - // 数据已存在,需要重新渲染图表 + // 数据已存在,需要重新渲染图表,添加延迟确保 DOM 显示 this.$nextTick(() => { - this.renderCharts(); + setTimeout(() => { + this.renderCharts(); + }, 100); }); } }