From 69298c2ffaed195c360205d79bd342795a56e722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E8=AF=9A?= Date: Thu, 15 Jan 2026 17:12:27 +0800 Subject: [PATCH] =?UTF-8?q?=E8=89=B2=E5=BD=A9=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Web/src/views/BudgetView.vue | 88 +++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 31 deletions(-) diff --git a/Web/src/views/BudgetView.vue b/Web/src/views/BudgetView.vue index 9838cc4..648acf7 100644 --- a/Web/src/views/BudgetView.vue +++ b/Web/src/views/BudgetView.vue @@ -421,42 +421,68 @@ const getProgressColor = (budget) => { const ratio = Math.min(Math.max(budget.current / budget.limit, 0), 1) - let startColor, midColor, endColor + // 颜色插值辅助函数 + const interpolate = (start, end, t) => { + return Math.round(start + (end - start) * t) + } + + // 多段颜色渐变计算 + const getGradientColor = (value, stops) => { + // 找到当前值所在的区间 + let startStop = stops[0] + let endStop = stops[stops.length - 1] + + for (let i = 0; i < stops.length - 1; i++) { + if (value >= stops[i].p && value <= stops[i + 1].p) { + startStop = stops[i] + endStop = stops[i + 1] + break + } + } + + // 计算区间内的相对比例 + const range = endStop.p - startStop.p + const t = (value - startStop.p) / range + + const r = interpolate(startStop.c.r, endStop.c.r, t) + const g = interpolate(startStop.c.g, endStop.c.g, t) + const b = interpolate(startStop.c.b, endStop.c.b, t) + + return `rgb(${r}, ${g}, ${b})` + } + + let stops if (budget.category === BudgetCategory.Expense) { - // 支出: 绿 -> 蓝 -> 红 - startColor = { r: 82, g: 196, b: 26 } // 绿 - midColor = { r: 25, g: 137, b: 250 } // 蓝 - endColor = { r: 238, g: 10, b: 36 } // 红 - } else if (budget.category === BudgetCategory.Income) { - // 收入: 红 -> 蓝 -> 绿 - startColor = { r: 238, g: 10, b: 36 } // 红 - midColor = { r: 25, g: 137, b: 250 } // 蓝 - endColor = { r: 82, g: 196, b: 26 } // 绿 + // 支出: 这是一个"安全 -> 警示 -> 危险"的过程 + // 使用 蓝绿色 -> 黄色 -> 橙红色的渐变,更加自然且具有高级感 + stops = [ + { p: 0, c: { r: 64, g: 169, b: 255 } }, // 0% 清新的蓝色 (Safe/Fresh) + { p: 0.4, c: { r: 54, g: 207, b: 201 } }, // 40% 青色过渡 + { p: 0.7, c: { r: 250, g: 173, b: 20 } }, // 70% 温暖的黄色 (Warning) + { p: 1, c: { r: 255, g: 77, b: 79 } } // 100% 柔和的红色 (Danger) + ] } else { - // 存款: 红 -> 蓝 -> 绿 - startColor = { r: 238, g: 10, b: 36 } // 红 - midColor = { r: 25, g: 137, b: 250 } // 蓝 - endColor = { r: 82, g: 196, b: 26 } // 绿 + // 收入/存款: 这是一个"开始 -> 积累 -> 达成"的过程 + // 采用 红色(未开始) -> 橙色(进行中) -> 绿色(达成) 的经典逻辑,但调整了色值使其更现代 + // stops = [ + // { p: 0, c: { r: 255, g: 120, b: 117 } }, // 0% 淡红 (Start) + // { p: 0.3, c: { r: 255, g: 197, b: 61 } }, // 30% 金黄 (Progress) + // { p: 0.7, c: { r: 115, g: 209, b: 61 } }, // 70% 草绿 (Good) + // { p: 1, c: { r: 35, g: 120, b: 4 } } // 100% 深绿 (Excellent) + // ] + + // 如果用户喜欢"红->蓝"的逻辑,可以尝试这种"红->白->蓝"的冷暖过渡: + stops = [ + { p: 0, c: { r: 245, g: 34, b: 45 } }, // 深红 + { p: 0.45, c: { r: 255, g: 204, b: 204 } }, // 浅红 + { p: 0.5, c: { r: 240, g: 242, b: 245 } }, // 中性灰白 + { p: 0.55, c: { r: 186, g: 231, b: 255 } }, // 浅蓝 + { p: 1, c: { r: 24, g: 144, b: 255 } } // 深蓝 + ] } - let r, g, b - - if (ratio < 0.5) { - // 从start到mid - const t = ratio * 2 - r = Math.round(startColor.r + (midColor.r - startColor.r) * t) - g = Math.round(startColor.g + (midColor.g - startColor.g) * t) - b = Math.round(startColor.b + (midColor.b - startColor.b) * t) - } else { - // 从mid到end - const t = (ratio - 0.5) * 2 - r = Math.round(midColor.r + (endColor.r - midColor.r) * t) - g = Math.round(midColor.g + (endColor.g - midColor.g) * t) - b = Math.round(midColor.b + (endColor.b - midColor.b) * t) - } - - return `rgb(${r}, ${g}, ${b})` + return getGradientColor(ratio, stops) } const showArchiveSummary = async () => {