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
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:
@@ -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),
|
||||
|
||||
@@ -267,6 +267,6 @@ const chartOptions = computed(() => {
|
||||
|
||||
.trend-chart {
|
||||
width: 100%;
|
||||
height: 190px;
|
||||
height: 200px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -456,7 +456,5 @@ const chartOptions = computed(() => {
|
||||
.trend-chart {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user