feat(dashboard): 优化大帐统计展示
- 大帐统计改为显示账户余额,移除出入库卡片 - 柱状图展示收入和支出数据,按月份正序排列 - 奖惩记录从数据库真实查询,区分奖励和惩罚 - 修复惩罚记录显示问题(类型匹配) - 账户余额显示在图例右侧 - 修复ESLint变量重复声明警告
This commit is contained in:
parent
7272342fe6
commit
77b78ac64d
@ -85,7 +85,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="dashboard-content-bottom-right">
|
<div class="dashboard-content-bottom-right">
|
||||||
<div class="dashboard-content-bottom-right-title">大帐统计</div>
|
<div class="dashboard-content-bottom-right-title">大帐统计</div>
|
||||||
<BarChart :height="'200px'" :data="barChartData" :card-data="barCardData" />
|
<BarChart :data="barChartData" :balance="balance" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -171,12 +171,8 @@ const centerRightData = ref({
|
|||||||
// 柱状图数据
|
// 柱状图数据
|
||||||
const barChartData = ref<{ category: string; monthlyStandard: number; perCapita: number }[]>([])
|
const barChartData = ref<{ category: string; monthlyStandard: number; perCapita: number }[]>([])
|
||||||
|
|
||||||
// 卡片数据
|
// 账户余额
|
||||||
const barCardData = ref({
|
const balance = ref(0)
|
||||||
inProgress: 0,
|
|
||||||
toWarehouse: 0,
|
|
||||||
outWarehouse: 0
|
|
||||||
})
|
|
||||||
|
|
||||||
// 基本信息数据
|
// 基本信息数据
|
||||||
const basicInfo = ref({
|
const basicInfo = ref({
|
||||||
@ -374,14 +370,8 @@ const loadData = async (prisonerId: number) => {
|
|||||||
// 更新柱状图数据
|
// 更新柱状图数据
|
||||||
barChartData.value = res.consumptionMonthlyData || []
|
barChartData.value = res.consumptionMonthlyData || []
|
||||||
|
|
||||||
// 更新消费汇总
|
// 更新账户余额
|
||||||
if (res.consumptionSummary) {
|
balance.value = res.balance || 0
|
||||||
barCardData.value = {
|
|
||||||
inProgress: res.consumptionSummary.inProgress || 0,
|
|
||||||
toWarehouse: res.consumptionSummary.toWarehouse || 0,
|
|
||||||
outWarehouse: res.consumptionSummary.outWarehouse || 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新基本信息
|
// 更新基本信息
|
||||||
basicInfo.value = {
|
basicInfo.value = {
|
||||||
|
|||||||
@ -1,22 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="supply-chart-container">
|
<div class="supply-chart-container" ref="containerRef">
|
||||||
<!-- 卡片统计 -->
|
|
||||||
<div class="chart-cards">
|
|
||||||
<div class="chart-card-item">
|
|
||||||
<div class="card-value">{{ cardData.inProgress }}</div>
|
|
||||||
<div class="card-label">进行中</div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-card-item">
|
|
||||||
<div class="card-value">{{ cardData.toWarehouse }}</div>
|
|
||||||
<div class="card-label">待入库</div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-card-item">
|
|
||||||
<div class="card-value">{{ cardData.outWarehouse }}</div>
|
|
||||||
<div class="card-label">已出库</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 柱状图 -->
|
<!-- 柱状图 -->
|
||||||
<EChart :options="barOption" :height="height" />
|
<EChart ref="chartRef" :options="barOption" :height="height" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -24,7 +9,7 @@
|
|||||||
import type { EChartsOption } from 'echarts'
|
import type { EChartsOption } from 'echarts'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import EChart from '@/components/Echart/src/Echart.vue'
|
import EChart from '@/components/Echart/src/Echart.vue'
|
||||||
import { computed, watch } from 'vue'
|
import { computed, ref, onMounted, watch } from 'vue'
|
||||||
|
|
||||||
defineOptions({ name: 'BarChart' })
|
defineOptions({ name: 'BarChart' })
|
||||||
|
|
||||||
@ -34,49 +19,45 @@ interface ChartDataItem {
|
|||||||
perCapita: number
|
perCapita: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CardData {
|
|
||||||
inProgress: number
|
|
||||||
toWarehouse: number
|
|
||||||
outWarehouse: number
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
width?: number
|
width?: number | string
|
||||||
height?: string
|
height?: string
|
||||||
data?: ChartDataItem[]
|
data?: ChartDataItem[]
|
||||||
cardData?: CardData
|
balance?: number
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
width: 400,
|
width: '100%',
|
||||||
height: '300px',
|
|
||||||
data: () => [],
|
data: () => [],
|
||||||
cardData: () => ({
|
balance: () => 0
|
||||||
inProgress: 5,
|
|
||||||
toWarehouse: 5,
|
|
||||||
outWarehouse: 5
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const containerRef = ref<HTMLElement>()
|
||||||
|
const chartRef = ref()
|
||||||
|
|
||||||
// 创建图表配置
|
// 创建图表配置
|
||||||
const createChartOption = (): EChartsOption => {
|
const createChartOption = (): EChartsOption => {
|
||||||
const categories = props.data.map((item) => item.category)
|
const categories = props.data.map((item) => item.category)
|
||||||
const monthlyStandardData = props.data.map((item) => item.monthlyStandard ?? 0)
|
const monthlyStandardData = props.data.map((item) => item.monthlyStandard ?? 0)
|
||||||
const perCapitaData = props.data.map((item) => item.perCapita ?? 0)
|
const perCapitaData = props.data.map((item) => item.perCapita ?? 0)
|
||||||
|
|
||||||
// 创建底色数据(最大值50)
|
// 动态计算最大值,确保能够显示所有数据
|
||||||
const maxValue = 50
|
const maxDataValue = Math.max(...monthlyStandardData, ...perCapitaData, 100)
|
||||||
const monthlyStandardBgData = categories.map((_, index) => maxValue - monthlyStandardData[index])
|
// 向上取整到百位,并留出 20% 空间
|
||||||
const perCapitaBgData = categories.map((_, index) => maxValue - perCapitaData[index])
|
const maxValue = Math.ceil(maxDataValue * 1.2 / 100) * 100
|
||||||
|
|
||||||
|
// 创建底色数据(填充到 maxValue)
|
||||||
|
const monthlyStandardBgData = categories.map((_, index) => Math.max(0, maxValue - monthlyStandardData[index]))
|
||||||
|
const perCapitaBgData = categories.map((_, index) => Math.max(0, maxValue - perCapitaData[index]))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
grid: {
|
grid: {
|
||||||
left: '10%',
|
left: '8%',
|
||||||
right: '15%',
|
right: '8%',
|
||||||
top: '20%',
|
top: '25%',
|
||||||
bottom: '15%',
|
bottom: '18%',
|
||||||
containLabel: false
|
containLabel: false
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
@ -100,8 +81,8 @@ const createChartOption = (): EChartsOption => {
|
|||||||
yAxis: {
|
yAxis: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 50,
|
max: maxValue,
|
||||||
interval: 10,
|
interval: Math.ceil(maxValue / 5 / 100) * 100,
|
||||||
axisLine: {
|
axisLine: {
|
||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
@ -110,7 +91,8 @@ const createChartOption = (): EChartsOption => {
|
|||||||
},
|
},
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
color: '#D8F0FF',
|
color: '#D8F0FF',
|
||||||
fontSize: 10
|
fontSize: 10,
|
||||||
|
formatter: (value: number) => value.toString()
|
||||||
},
|
},
|
||||||
splitLine: {
|
splitLine: {
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
@ -119,17 +101,33 @@ const createChartOption = (): EChartsOption => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// 图例和余额放在同一行
|
||||||
legend: {
|
legend: {
|
||||||
data: ['支出', '收入'],
|
data: [
|
||||||
top: '5%',
|
{ name: '支出', icon: 'rect' },
|
||||||
right: '10%',
|
{ name: '收入', icon: 'rect' }
|
||||||
|
],
|
||||||
|
top: '3%',
|
||||||
|
left: '8%',
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: '#6D869A',
|
color: '#6D869A',
|
||||||
fontSize: 9
|
fontSize: 10
|
||||||
},
|
},
|
||||||
itemWidth: 9,
|
itemWidth: 12,
|
||||||
itemHeight: 9,
|
itemHeight: 8,
|
||||||
itemGap: 25
|
itemGap: 15
|
||||||
|
},
|
||||||
|
// 使用 title 显示余额,放在图例右侧
|
||||||
|
title: {
|
||||||
|
text: `账户余额: ${props.balance}元`,
|
||||||
|
left: 'auto',
|
||||||
|
right: '8%',
|
||||||
|
top: '3%',
|
||||||
|
textStyle: {
|
||||||
|
color: '#00d4ff',
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
@ -144,7 +142,6 @@ const createChartOption = (): EChartsOption => {
|
|||||||
},
|
},
|
||||||
formatter: function (params: any) {
|
formatter: function (params: any) {
|
||||||
let result = params[0].name + '<br/>'
|
let result = params[0].name + '<br/>'
|
||||||
// 只显示数据系列,不显示底色系列
|
|
||||||
params.forEach((param: any) => {
|
params.forEach((param: any) => {
|
||||||
if (param.seriesName === '支出' || param.seriesName === '收入') {
|
if (param.seriesName === '支出' || param.seriesName === '收入') {
|
||||||
result += param.marker + param.seriesName + ': ' + param.value + '<br/>'
|
result += param.marker + param.seriesName + ': ' + param.value + '<br/>'
|
||||||
@ -154,7 +151,7 @@ const createChartOption = (): EChartsOption => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
// 支出数据(渐变)- 先绘制,作为底层
|
// 支出数据
|
||||||
{
|
{
|
||||||
name: '支出',
|
name: '支出',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
@ -165,40 +162,36 @@ const createChartOption = (): EChartsOption => {
|
|||||||
type: 'linear',
|
type: 'linear',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
x2: 0,
|
x2: 1,
|
||||||
y2: 1,
|
y2: 0,
|
||||||
colorStops: [
|
colorStops: [
|
||||||
{
|
{ offset: 0, color: '#10A0F2' },
|
||||||
offset: 0,
|
{ offset: 0.5, color: '#0D8BD9' },
|
||||||
color: '#10A0F2'
|
{ offset: 1, color: '#0A6EB0' }
|
||||||
},
|
|
||||||
{
|
|
||||||
offset: 1,
|
|
||||||
color: 'rgba(0, 82, 184, 0)'
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
|
||||||
},
|
},
|
||||||
barWidth: '20%',
|
borderRadius: [2, 2, 0, 0]
|
||||||
barGap: '20%'
|
|
||||||
},
|
},
|
||||||
// 支出底色 - 后绘制,堆叠在数据上方
|
barWidth: '25%',
|
||||||
|
barGap: '30%'
|
||||||
|
},
|
||||||
|
// 支出底色
|
||||||
{
|
{
|
||||||
name: '支出底色',
|
name: '支出底色',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
stack: 'monthly',
|
stack: 'monthly',
|
||||||
data: monthlyStandardBgData,
|
data: monthlyStandardBgData,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: '#38668D70'
|
color: 'rgba(56, 102, 141, 0.3)'
|
||||||
},
|
},
|
||||||
barWidth: '20%',
|
barWidth: '25%',
|
||||||
barGap: '20%',
|
barGap: '30%',
|
||||||
silent: true,
|
silent: true,
|
||||||
tooltip: {
|
tooltip: {
|
||||||
show: false
|
show: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 收入数据(渐变)
|
// 收入数据
|
||||||
{
|
{
|
||||||
name: '收入',
|
name: '收入',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
@ -209,33 +202,30 @@ const createChartOption = (): EChartsOption => {
|
|||||||
type: 'linear',
|
type: 'linear',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
x2: 0,
|
x2: 1,
|
||||||
y2: 1,
|
y2: 0,
|
||||||
colorStops: [
|
colorStops: [
|
||||||
{
|
{ offset: 0, color: '#FFA58D' },
|
||||||
offset: 0,
|
{ offset: 0.5, color: '#E88F5A' },
|
||||||
color: '#FFA58D'
|
{ offset: 1, color: '#D07530' }
|
||||||
},
|
|
||||||
{
|
|
||||||
offset: 1,
|
|
||||||
color: 'rgba(87, 140, 205, 0)'
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
|
||||||
},
|
},
|
||||||
barWidth: '20%',
|
borderRadius: [2, 2, 0, 0]
|
||||||
barGap: '80%'
|
|
||||||
},
|
},
|
||||||
|
barWidth: '25%',
|
||||||
|
barGap: '30%'
|
||||||
|
},
|
||||||
|
// 收入底色
|
||||||
{
|
{
|
||||||
name: '收入底色',
|
name: '收入底色',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
stack: 'perCapita',
|
stack: 'perCapita',
|
||||||
data: perCapitaBgData,
|
data: perCapitaBgData,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: '#38668D70'
|
color: 'rgba(56, 102, 141, 0.3)'
|
||||||
},
|
},
|
||||||
barWidth: '20%',
|
barWidth: '25%',
|
||||||
barGap: '80%',
|
barGap: '30%',
|
||||||
silent: true,
|
silent: true,
|
||||||
tooltip: {
|
tooltip: {
|
||||||
show: false
|
show: false
|
||||||
@ -247,15 +237,6 @@ const createChartOption = (): EChartsOption => {
|
|||||||
|
|
||||||
// 柱状图配置
|
// 柱状图配置
|
||||||
const barOption = computed(() => createChartOption())
|
const barOption = computed(() => createChartOption())
|
||||||
|
|
||||||
// 监听数据变化,更新图表
|
|
||||||
watch(
|
|
||||||
() => [props.data, props.cardData],
|
|
||||||
() => {
|
|
||||||
// 数据变化时,computed 会自动更新
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@ -265,30 +246,4 @@ watch(
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-cards {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-around;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-card-item {
|
|
||||||
text-align: center;
|
|
||||||
padding: 4px;
|
|
||||||
background: rgba(56, 102, 141, 0.3);
|
|
||||||
border-radius: 8px;
|
|
||||||
min-width: 100px;
|
|
||||||
|
|
||||||
.card-value {
|
|
||||||
font-size: 1.6vh;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #00d4ff;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-label {
|
|
||||||
font-size: 1.5vh;
|
|
||||||
color: rgba(255, 255, 255, 0.8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -65,7 +65,7 @@ const filteredList = computed(() => {
|
|||||||
} else if (activeFilter.value === 'reward') {
|
} else if (activeFilter.value === 'reward') {
|
||||||
return listData.value.filter((item) => item.type === 'reward')
|
return listData.value.filter((item) => item.type === 'reward')
|
||||||
} else {
|
} else {
|
||||||
return listData.value.filter((item) => item.type === 'danger')
|
return listData.value.filter((item) => item.type === 'punishment')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user