feat: 收益曲线优化
1. 修复 Canvas 实例,添加 this 参数 2. 曲线颜色随收益正负变化(绿涨红跌) 3. 空状态增加回填按钮,引导用户生成收益曲线
This commit is contained in:
parent
8f7b64fa7f
commit
86a3f57c4a
@ -77,6 +77,10 @@
|
|||||||
<!-- 无数据 -->
|
<!-- 无数据 -->
|
||||||
<view v-else-if="!navHistory || navHistory.length === 0" class="nav-empty">
|
<view v-else-if="!navHistory || navHistory.length === 0" class="nav-empty">
|
||||||
<text class="empty-text">暂无收益数据</text>
|
<text class="empty-text">暂无收益数据</text>
|
||||||
|
<text class="empty-hint">回填历史净值后可生成收益曲线</text>
|
||||||
|
<button class="backfill-btn" @click="handleBackfillNav" :disabled="backfillLoading">
|
||||||
|
{{ backfillLoading ? '回填中...' : '生成收益曲线' }}
|
||||||
|
</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 收益曲线 -->
|
<!-- 收益曲线 -->
|
||||||
@ -480,6 +484,7 @@ const navLoading = ref(false);
|
|||||||
const navPeriod = ref('30d');
|
const navPeriod = ref('30d');
|
||||||
const navHistory = ref([]);
|
const navHistory = ref([]);
|
||||||
const navStatistics = ref(null);
|
const navStatistics = ref(null);
|
||||||
|
const backfillLoading = ref(false);
|
||||||
|
|
||||||
// 绘制收益曲线
|
// 绘制收益曲线
|
||||||
const drawNavChart = () => {
|
const drawNavChart = () => {
|
||||||
@ -487,7 +492,7 @@ const drawNavChart = () => {
|
|||||||
if (!data || data.length === 0) return;
|
if (!data || data.length === 0) return;
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const ctx = uni.createCanvasContext('navChart');
|
const ctx = uni.createCanvasContext('navChart', this);
|
||||||
|
|
||||||
// 画布尺寸 (rpx -> px)
|
// 画布尺寸 (rpx -> px)
|
||||||
const width = 320;
|
const width = 320;
|
||||||
@ -502,6 +507,12 @@ const drawNavChart = () => {
|
|||||||
const maxVal = Math.max(...values) * 1.02;
|
const maxVal = Math.max(...values) * 1.02;
|
||||||
const range = maxVal - minVal || 1;
|
const range = maxVal - minVal || 1;
|
||||||
|
|
||||||
|
// 判断收益正负,决定颜色
|
||||||
|
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)';
|
||||||
|
|
||||||
// 清空画布
|
// 清空画布
|
||||||
ctx.clearRect(0, 0, width, height);
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
@ -541,12 +552,12 @@ const drawNavChart = () => {
|
|||||||
ctx.lineTo(points[points.length - 1].x, padding.top + chartHeight);
|
ctx.lineTo(points[points.length - 1].x, padding.top + chartHeight);
|
||||||
ctx.lineTo(points[0].x, padding.top + chartHeight);
|
ctx.lineTo(points[0].x, padding.top + chartHeight);
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.setFillStyle('rgba(6, 78, 59, 0.15)');
|
ctx.setFillStyle(fillColor);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
// 绘制曲线
|
// 绘制曲线
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.setStrokeStyle('#064E3B');
|
ctx.setStrokeStyle(lineColor);
|
||||||
ctx.setLineWidth(2);
|
ctx.setLineWidth(2);
|
||||||
points.forEach((p, i) => {
|
points.forEach((p, i) => {
|
||||||
if (i === 0) ctx.moveTo(p.x, p.y);
|
if (i === 0) ctx.moveTo(p.x, p.y);
|
||||||
@ -571,6 +582,32 @@ const drawNavChart = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 回填净值历史
|
||||||
|
const handleBackfillNav = async () => {
|
||||||
|
if (!portfolioId.value) return;
|
||||||
|
|
||||||
|
backfillLoading.value = true;
|
||||||
|
try {
|
||||||
|
const response = await api.assets.backfillNavHistory(portfolioId.value, true);
|
||||||
|
if (response.code === 200) {
|
||||||
|
uni.showToast({
|
||||||
|
title: `成功生成 ${response.data.recordsCreated} 条记录`,
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
// 重新获取净值历史
|
||||||
|
await fetchNavHistory();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('回填净值失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '生成失败,请重试',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
backfillLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 交易表单
|
// 交易表单
|
||||||
const showTransactionForm = ref(false);
|
const showTransactionForm = ref(false);
|
||||||
const transactionType = ref('buy'); // buy 或 sell
|
const transactionType = ref('buy'); // buy 或 sell
|
||||||
@ -1323,9 +1360,11 @@ const deletePortfolio = async () => {
|
|||||||
.nav-loading,
|
.nav-loading,
|
||||||
.nav-empty {
|
.nav-empty {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 400rpx;
|
height: 400rpx;
|
||||||
|
gap: 16rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-text,
|
.loading-text,
|
||||||
@ -1334,6 +1373,26 @@ const deletePortfolio = async () => {
|
|||||||
color: #9CA3AF;
|
color: #9CA3AF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-hint {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #D1D5DB;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backfill-btn {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
padding: 16rpx 32rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #FFFFFF;
|
||||||
|
background-color: #059669;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backfill-btn[disabled] {
|
||||||
|
background-color: #9CA3AF;
|
||||||
|
}
|
||||||
|
|
||||||
.nav-chart-wrapper {
|
.nav-chart-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user