AI-accounting-soft-uniApp/components/BudgetPieChart/BudgetPieChart.vue
ni ziyi 70715bb0c8 feat(新增):
新增ocr获取信息后,批量直接入库功能
2025-12-25 14:55:08 +08:00

194 lines
4.1 KiB
Vue

<template>
<view class="chart-container">
<view :id="chartId" class="echart-container"></view>
</view>
</template>
<script setup>
import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
import * as echarts from 'echarts'
const props = defineProps({
budget: {
type: Number,
default: 0
},
used: {
type: Number,
default: 0
},
chartId: {
type: String,
default: 'budgetPieChart'
}
})
let chartInstance = null
const chartData = computed(() => {
const total = props.budget || 1
const used = Math.min(props.used, total)
const remaining = Math.max(total - used, 0)
return {
budget: props.budget,
used: used,
remaining: remaining
}
})
const initChart = () => {
// 等待 DOM 渲染
nextTick(() => {
// 使用 uni.createSelectorQuery 获取 DOM 元素
const query = uni.createSelectorQuery()
query.select(`#${props.chartId}`).boundingClientRect()
query.exec((res) => {
if (!res || !res[0]) {
console.error('Chart container not found')
return
}
// 在 H5 环境下直接使用 document.getElementById
// #ifdef H5
const chartDom = document.getElementById(props.chartId)
if (chartDom) {
chartInstance = echarts.init(chartDom)
updateChart()
}
// #endif
})
})
}
const updateChart = () => {
if (!chartInstance) return
const { used, remaining } = chartData.value
const option = {
backgroundColor: 'transparent',
series: [
{
type: 'pie',
radius: ['60%', '70%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 20,
borderWidth: 2
},
label: {
show: true,
position: 'center',
formatter: function() {
return `{a|剩余预算}\n{b|¥${remaining.toFixed(2)}}`
},
rich: {
a: {
fontSize: 13,
color: 'rgba(255, 255, 255, 0.75)',
lineHeight: 20,
fontWeight: 'normal'
},
b: {
fontSize: 17,
color: '#fff',
fontWeight: 'bold',
lineHeight: 32
}
}
},
labelLine: {
show: false
},
data: [
{
value: used,
name: '已用',
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#8673C3' },
{ offset: 1, color: '#8673C3' }
]
},
shadowBlur: 10,
shadowColor: 'rgba(255, 107, 107, 0.3)'
}
},
{
value: remaining,
name: '剩余',
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#69db7c' },
{ offset: 1, color: '#51cf66' }
]
},
shadowBlur: 10,
shadowColor: 'rgba(81, 207, 102, 0.3)'
}
}
],
emphasis: {
scale: true,
scaleSize: 5,
itemStyle: {
shadowBlur: 15,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.4)'
}
},
animationType: 'scale',
animationEasing: 'cubicOut',
animationDelay: 0
}
]
}
chartInstance.setOption(option, true)
}
watch(() => [props.budget, props.used], () => {
updateChart()
}, { deep: true })
onMounted(() => {
setTimeout(() => {
initChart()
}, 200)
})
onBeforeUnmount(() => {
if (chartInstance) {
chartInstance.dispose()
chartInstance = null
}
})
</script>
<style scoped>
.chart-container {
width: 280rpx;
height: 280rpx;
margin: 0 auto;
}
.echart-container {
width: 100%;
height: 100%;
}
</style>