fix
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 17s
Docker Build & Deploy / Deploy to Production (push) Successful in 8s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s

This commit is contained in:
SunCheng
2026-02-19 21:34:55 +08:00
parent 986f46b84c
commit 7a39258bc8
3 changed files with 86 additions and 6 deletions

View File

@@ -166,6 +166,7 @@
type="bar"
:data="varianceChartData"
:options="varianceChartOptions"
:plugins="varianceChartPlugins"
class="chart-body variance-chart"
:style="{ height: calculateChartHeight(budgets) + 'px' }"
/>
@@ -447,6 +448,78 @@ const calculateChartHeight = (budgets) => {
return calculatedHeight
}
const varianceLabelPlugin = {
id: 'variance-label-plugin',
afterDatasetsDraw: (chart) => {
const dataset = chart.data?.datasets?.[0]
const metaData = dataset?._meta
if (!dataset || !metaData) {
return
}
const meta = chart.getDatasetMeta(0)
if (!meta?.data) {
return
}
const { ctx, chartArea } = chart
const fontFamily = '"PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif'
ctx.save()
ctx.font = `12px ${fontFamily}`
ctx.textBaseline = 'middle'
meta.data.forEach((bar, index) => {
const item = metaData[index]
if (!item || item.value === 0) {
return
}
const label = formatVarianceLabelValue(item.value)
const textWidth = ctx.measureText(label).width
const position = bar.tooltipPosition ? bar.tooltipPosition() : { x: bar.x, y: bar.y }
const offset = 8
const isPositive = item.value > 0
ctx.fillStyle = getVarianceLabelColor(item.value)
let x = position.x + (isPositive ? offset : -offset)
const y = position.y
if (chartArea) {
const rightLimit = chartArea.right - 4
const leftLimit = chartArea.left + 4
if (isPositive && x + textWidth > rightLimit) {
x = rightLimit - textWidth
}
if (!isPositive && x - textWidth < leftLimit) {
x = leftLimit + textWidth
}
}
ctx.textAlign = isPositive ? 'left' : 'right'
ctx.fillText(label, x, y)
})
ctx.restore()
}
}
const varianceChartPlugins = computed(() => [varianceLabelPlugin])
const formatVarianceLabelValue = (value) => {
const absValue = Math.abs(Math.round(value || 0))
return absValue.toLocaleString(undefined, {
minimumFractionDigits: 0,
maximumFractionDigits: 0
})
}
const getVarianceLabelColor = (value) => {
if (props.activeTab === BudgetCategory.Expense) {
return value > 0 ? getCssVar('--van-danger-color') : getCssVar('--van-success-color')
}
return value > 0 ? getCssVar('--van-success-color') : getCssVar('--van-danger-color')
}
// 偏差分析图表数据
const varianceChartData = computed(() => {
if (!props.budgets || props.budgets.length === 0) {
@@ -469,10 +542,19 @@ const varianceChartData = computed(() => {
const monthlyData = data.filter((item) => item.type === BudgetPeriodType.Month)
const annualData = data.filter((item) => item.type === BudgetPeriodType.Year)
monthlyData.sort((a, b) => Math.abs(b.value) - Math.abs(a.value))
annualData.sort((a, b) => Math.abs(b.value) - Math.abs(a.value))
const sortByLimitAndRemaining = (a, b) => {
if (a.limit !== b.limit) {
return a.limit - b.limit
}
const remainingA = a.limit - a.current
const remainingB = b.limit - b.current
return remainingB - remainingA
}
const sortedData = [...annualData, ...monthlyData]
monthlyData.sort(sortByLimitAndRemaining)
annualData.sort(sortByLimitAndRemaining)
const sortedData = [...monthlyData, ...annualData]
return {
labels: sortedData.map((item) => item.name),

View File

@@ -267,6 +267,6 @@ const chartOptions = computed(() => {
.trend-chart {
width: 100%;
height: 190px;
height: 200px;
}
</style>

View File

@@ -456,7 +456,5 @@ const chartOptions = computed(() => {
.trend-chart {
width: 100%;
height: 200px;
overflow: hidden;
position: relative;
}
</style>