diff --git a/pages/detail/detail.vue b/pages/detail/detail.vue
index 3d2bf28..0881802 100755
--- a/pages/detail/detail.vue
+++ b/pages/detail/detail.vue
@@ -85,11 +85,26 @@
-
+
+
+
+ 最新净值
+ {{ (navHistory[navHistory.length - 1]?.nav || 1).toFixed(4) }}
+
+
+ {{ navStatistics?.totalReturn >= 0 ? '↑' : '↓' }}
+ {{ Math.abs(navStatistics?.totalReturn || 0).toFixed(2) }}%
+
+
+
+
+
+
+
@@ -102,7 +117,7 @@
最大回撤
- {{ navStatistics.maxDrawdown.toFixed(2) }}%
+ -{{ navStatistics.maxDrawdown.toFixed(2) }}%
夏普比率
@@ -494,10 +509,10 @@ const drawNavChart = () => {
nextTick(() => {
const ctx = uni.createCanvasContext('navChart', this);
- // 画布尺寸 (rpx -> px)
- const width = 320;
- const height = 200;
- const padding = { top: 20, right: 20, bottom: 30, left: 45 };
+ // 画布尺寸 (rpx -> px) - 全宽
+ const width = 350;
+ const height = 180;
+ const padding = { top: 10, right: 10, bottom: 25, left: 10 };
const chartWidth = width - padding.left - padding.right;
const chartHeight = height - padding.top - padding.bottom;
@@ -511,39 +526,23 @@ const drawNavChart = () => {
const totalReturn = navStatistics.value?.totalReturn || 0;
const isPositive = totalReturn >= 0;
const lineColor = isPositive ? '#059669' : '#DC2626';
- const fillColor = isPositive ? 'rgba(5, 150, 105, 0.15)' : 'rgba(220, 38, 38, 0.15)';
+ const fillColorTop = isPositive ? 'rgba(5, 150, 105, 0.3)' : 'rgba(220, 38, 38, 0.3)';
+ const fillColorBottom = isPositive ? 'rgba(5, 150, 105, 0.02)' : 'rgba(220, 38, 38, 0.02)';
// 清空画布
ctx.clearRect(0, 0, width, height);
- // 绘制网格线
- ctx.setStrokeStyle('#E5E7EB');
- ctx.setLineWidth(0.5);
- for (let i = 0; i <= 4; i++) {
- const y = padding.top + (chartHeight / 4) * i;
- ctx.beginPath();
- ctx.moveTo(padding.left, y);
- ctx.lineTo(width - padding.right, y);
- ctx.stroke();
- }
-
- // Y轴标签
- ctx.setFillStyle('#9CA3AF');
- ctx.setFontSize(10);
- ctx.setTextAlign('right');
- for (let i = 0; i <= 4; i++) {
- const y = padding.top + (chartHeight / 4) * i;
- const val = maxVal - (range / 4) * i;
- ctx.fillText(val.toFixed(2), padding.left - 5, y + 3);
- }
-
// 计算点坐标
const points = data.map((item, index) => ({
x: padding.left + (chartWidth / (data.length - 1 || 1)) * index,
y: padding.top + chartHeight - ((item.nav - minVal) / range) * chartHeight
}));
- // 绘制填充区域
+ // 绘制渐变填充区域
+ const gradient = ctx.createLinearGradient(0, padding.top, 0, padding.top + chartHeight);
+ gradient.addColorStop(0, fillColorTop);
+ gradient.addColorStop(1, fillColorBottom);
+
ctx.beginPath();
points.forEach((p, i) => {
if (i === 0) ctx.moveTo(p.x, p.y);
@@ -552,16 +551,29 @@ const drawNavChart = () => {
ctx.lineTo(points[points.length - 1].x, padding.top + chartHeight);
ctx.lineTo(points[0].x, padding.top + chartHeight);
ctx.closePath();
- ctx.setFillStyle(fillColor);
+ ctx.setFillStyle(gradient);
ctx.fill();
- // 绘制曲线
+ // 绘制平滑曲线
ctx.beginPath();
ctx.setStrokeStyle(lineColor);
ctx.setLineWidth(2);
+ ctx.setLineCap('round');
+ ctx.setLineJoin('round');
+
+ // 使用贝塞尔曲线平滑
points.forEach((p, i) => {
- if (i === 0) ctx.moveTo(p.x, p.y);
- else ctx.lineTo(p.x, p.y);
+ if (i === 0) {
+ ctx.moveTo(p.x, p.y);
+ } else {
+ // 二次贝塞尔曲线
+ const prev = points[i - 1];
+ const cpX = (prev.x + p.x) / 2;
+ ctx.quadraticCurveTo(prev.x, prev.y, cpX, (prev.y + p.y) / 2);
+ if (i === points.length - 1) {
+ ctx.quadraticCurveTo(cpX, (prev.y + p.y) / 2, p.x, p.y);
+ }
+ }
});
ctx.stroke();
@@ -575,7 +587,7 @@ const drawNavChart = () => {
const idx = Math.min(i * step, data.length - 1);
const x = padding.left + (chartWidth / (data.length - 1 || 1)) * idx;
const dateStr = data[idx].date.split('-').slice(1).join('/');
- ctx.fillText(dateStr, x, height - 8);
+ ctx.fillText(dateStr, x, height - 5);
}
ctx.draw();
@@ -1354,7 +1366,90 @@ const deletePortfolio = async () => {
background-color: #FFFFFF;
border-radius: 20rpx;
padding: 24rpx;
- min-height: 400rpx;
+ min-height: 300rpx;
+}
+
+.nav-chart-wrapper {
+ display: flex;
+ flex-direction: column;
+}
+
+/* 净值指标 */
+.nav-indicator {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ padding: 16rpx 0 24rpx;
+ border-bottom: 1rpx solid #F3F4F6;
+ margin-bottom: 16rpx;
+}
+
+.indicator-main {
+ display: flex;
+ flex-direction: column;
+ gap: 8rpx;
+}
+
+.indicator-label {
+ font-size: 24rpx;
+ color: #9CA3AF;
+}
+
+.indicator-value {
+ font-size: 44rpx;
+ font-weight: 700;
+ font-family: 'DIN Alternate', sans-serif;
+ color: #1F2937;
+}
+
+.indicator-change {
+ display: flex;
+ align-items: center;
+ gap: 4rpx;
+ padding: 8rpx 16rpx;
+ border-radius: 8rpx;
+ margin-top: 8rpx;
+}
+
+.indicator-change.positive {
+ background-color: #ECFDF5;
+}
+
+.indicator-change.negative {
+ background-color: #FEF2F2;
+}
+
+.change-arrow {
+ font-size: 28rpx;
+ font-weight: 600;
+}
+
+.indicator-change.positive .change-arrow,
+.indicator-change.positive .change-value {
+ color: #059669;
+}
+
+.indicator-change.negative .change-arrow,
+.indicator-change.negative .change-value {
+ color: #DC2626;
+}
+
+.change-value {
+ font-size: 28rpx;
+ font-weight: 600;
+ font-family: 'DIN Alternate', sans-serif;
+}
+
+/* 图表区域 */
+.chart-area {
+ width: 100%;
+ height: 360rpx;
+ position: relative;
+}
+
+.nav-canvas {
+ width: 100%;
+ height: 100%;
}
.nav-loading,
@@ -1363,7 +1458,7 @@ const deletePortfolio = async () => {
flex-direction: column;
align-items: center;
justify-content: center;
- height: 400rpx;
+ height: 300rpx;
gap: 16rpx;
}
@@ -1426,21 +1521,23 @@ const deletePortfolio = async () => {
}
.nav-stats {
- display: flex;
- justify-content: space-between;
- margin-top: 24rpx;
- padding-top: 24rpx;
- border-top: 1rpx solid #E5E7EB;
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 16rpx;
+ margin-top: 20rpx;
+ padding-top: 20rpx;
+ border-top: 1rpx solid #F3F4F6;
}
.nav-stats .stat-item {
text-align: center;
+ padding: 12rpx 0;
}
.nav-stats .stat-label {
display: block;
font-size: 22rpx;
- color: #6B7280;
+ color: #9CA3AF;
margin-bottom: 8rpx;
}
@@ -1448,14 +1545,15 @@ const deletePortfolio = async () => {
display: block;
font-size: 28rpx;
font-weight: 600;
- color: #111827;
+ font-family: 'DIN Alternate', sans-serif;
+ color: #1F2937;
}
.nav-stats .text-red {
- color: #EF4444;
+ color: #059669;
}
.nav-stats .text-green {
- color: #10B981;
+ color: #DC2626;
}
\ No newline at end of file