Files
reader-star/apps/admin/src/views/user/game/modules/LineCharts.vue
rjl 40a6850c3f chore(admin): 优化构建配置并升级资源格式
- 新增构建插件:打包分析、压缩、自动部署、图片优化和PWA支持
- 将SVG资源转换为WebP格式以提升加载性能
- 添加新的背景图片资源用于用户界面优化
- 更新环境配置:调整基础URL和服务地址
- 优化用户界面组件以适配新的资源格式
- 更新package.json依赖以支持新的构建功能
- 改进构建流程以支持自动部署和性能监控
2026-03-13 17:52:13 +08:00

157 lines
3.9 KiB
Vue

<script setup lang="ts">
import * as echarts from 'echarts/core'
import { ref, watch } from 'vue'
import { type ECOption, useEcharts } from '@/hooks/common/echarts'
interface BarDatum {
group: string | number
total: number
correct: number
}
const props = defineProps<{
data: BarDatum[]
height?: number
}>()
// 缓存上一次的数据,用于深度比较
const lastDataCache = ref<string>('')
/**
* 深度比较数据是否发生变化
*/
function isDataChanged(newData: BarDatum[]): boolean {
const newDataStr = JSON.stringify(newData)
if (newDataStr === lastDataCache.value) {
return false
}
lastDataCache.value = newDataStr
return true
}
/**
* 构建柱状图配置
* 说明:根据传入的数据生成双系列柱状图(答题数量/正确数量)
*/
function buildOption(list: BarDatum[]): ECOption {
const categories = list.map(i => String(i.group))
const total = list.map(i => i.total)
const correct = list.map(i => i.correct)
return {
// 禁用动画,避免频繁刷新时的抖动
animation: false,
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
},
legend: {
data: ['答题数量', '预估得分'],
top: 0,
right: 0,
icon: 'circle',
textStyle: {
color: '#666',
},
},
grid: { left: 16, right: 16, top: 40, bottom: 0, containLabel: true },
xAxis: {
type: 'category',
data: categories,
axisTick: { show: false }, // 隐藏 x 轴刻度线
axisLine: { show: false }, // 隐藏 x 轴线
axisLabel: {
color: '#003366', // 深蓝色字体
fontWeight: 'bold',
fontSize: 14,
margin: 16,
},
},
yAxis: {
type: 'value',
minInterval: 1,
axisTick: { show: false }, // 隐藏 y 轴刻度线
axisLine: { show: false }, // 隐藏 y 轴线
splitLine: { show: false }, // 隐藏 y 轴网格线
axisLabel: { show: false }, // 隐藏 y 轴标签
},
series: [
{
name: '答题数量',
type: 'bar',
data: total,
barWidth: 24,
itemStyle: {
// 从上到下:浅蓝 -> 深蓝
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#e0f2fe' }, // sky-100 (顶部浅色)
{ offset: 1, color: '#3b82f6' }, // blue-500 (底部深色)
]),
borderRadius: [4, 4, 0, 0],
},
label: {
show: true,
position: 'top',
color: '#003366', // 深蓝色
fontWeight: 'bold',
fontSize: 18,
offset: [0, 2],
},
},
{
name: '预估得分',
type: 'bar',
data: correct,
barWidth: 24,
itemStyle: {
// 从上到下:浅橙 -> 深橙
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#fff7ed' }, // orange-50 (顶部极浅)
{ offset: 1, color: '#f97316' }, // orange-500 (底部深色)
]),
borderRadius: [4, 4, 0, 0],
},
label: {
show: true,
position: 'top',
color: '#ea580c', // orange-600
fontWeight: 'bold',
fontSize: 18,
offset: [0, 2],
},
},
],
}
}
// 初始化图表
const { domRef, updateOptions } = useEcharts<ECOption>(() => buildOption(props.data || []))
/**
* 监听数据变化并更新图表
* 说明:只有当数据真正发生变化时才更新图表,避免不必要的重绘
*/
watch(
() => props.data,
(newData) => {
// 只有数据真正变化时才更新
if (isDataChanged(newData || [])) {
updateOptions(() => buildOption(newData || []))
}
},
{ deep: true },
)
</script>
<template>
<div
ref="domRef"
class="w-full"
:style="{ height: `${props.height ?? 180}px` }"
/>
</template>
<style scoped>
</style>