backend/src/views/apps/babyhealth/dashborad/index.vue
2026-02-05 15:23:37 +08:00

289 lines
8.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="statistics-container">
<!-- 第一行宝贝统计 -->
<el-row :gutter="20" class="data-overview">
<el-col :span="8" v-for="item in babyData" :key="item.title">
<el-card shadow="hover" class="data-card">
<div class="card-content">
<div class="icon-box" :style="{ backgroundColor: item.color }">
<i :class="item.icon"></i>
</div>
<div class="text-box">
<div class="title">{{ item.title }}</div>
<div class="value">{{ item.value.toLocaleString() }}</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 第二行:用户统计 -->
<el-row :gutter="20" class="data-overview" style="margin-top: 20px;">
<el-col :span="8" v-for="item in userData" :key="item.title">
<el-card shadow="hover" class="data-card">
<div class="card-content">
<div class="icon-box" :style="{ backgroundColor: item.color }">
<i :class="item.icon"></i>
</div>
<div class="text-box">
<div class="title">{{ item.title }}</div>
<div class="value">{{ item.value.toLocaleString() }}</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 图表区域 -->
<el-row :gutter="20" class="charts-row" style="margin-top: 20px;">
<!-- 宝贝增长趋势柱状图 -->
<el-col :span="12">
<el-card shadow="hover" header="宝贝增长趋势">
<div ref="babyChartRef" class="chart-box"></div>
</el-card>
</el-col>
<!-- 用户增长趋势柱状图 -->
<el-col :span="12">
<el-card shadow="hover" header="用户增长趋势">
<div ref="userChartRef" class="chart-box"></div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, shallowRef, nextTick } from 'vue';
import * as echarts from 'echarts';
import { getBabyCounts, getUserCounts } from '@/api/babyhealth';
// --- 类型定义 ---
interface SummaryItem {
title: string;
value: number;
icon: string;
color: string;
percentage: number;
isUp: boolean;
}
// --- 响应式数据 ---
const babyChartRef = ref<HTMLElement | null>(null);
const userChartRef = ref<HTMLElement | null>(null);
const babyChartInstance = shallowRef<echarts.ECharts | null>(null);
const userChartInstance = shallowRef<echarts.ECharts | null>(null);
const babyData = ref<SummaryItem[]>([
{ title: '总宝贝数', value: 0, icon: 'fa-solid fa-baby', color: '#67C23A', percentage: 0, isUp: false },
{ title: '男宝宝数', value: 0, icon: 'fa-solid fa-person', color: '#409EFF', percentage: 0, isUp: false },
{ title: '女宝宝数', value: 0, icon: 'fa-solid fa-person-dress', color: '#F56C6C', percentage: 0, isUp: false }
]);
const userData = ref<SummaryItem[]>([
{ title: '总用户数', value: 0, icon: 'fa-solid fa-users', color: '#3973FF', percentage: 0, isUp: false },
{ title: '父亲数', value: 0, icon: 'fa-solid fa-person', color: '#409EFF', percentage: 0, isUp: false },
{ title: '母亲数', value: 0, icon: 'fa-solid fa-person-dress', color: '#F56C6C', percentage: 0, isUp: false }
]);
// 调用宝贝统计接口
async function fetchGetBabyDatas() {
const res = await getBabyCounts();
if (res.code === 200 && res.data) {
const { total, male, female } = res.data;
// 更新 babyData
babyData.value = [
{ title: '总宝贝数', value: total, icon: 'fa-solid fa-baby', color: '#67C23A', percentage: 0, isUp: false },
{ title: '男宝宝数', value: male !== undefined ? male : 0, icon: 'fa-solid fa-person', color: '#409EFF', percentage: 0, isUp: false },
{ title: '女宝宝数', value: female !== undefined ? female : 0, icon: 'fa-solid fa-person-dress', color: '#F56C6C', percentage: 0, isUp: false }
];
// 更新宝贝柱状图
updateBabyChart(total, male, female);
}
}
// 调用用户统计接口
async function fetchGetUserDatas() {
const res = await getUserCounts();
if (res.code === 200 && res.data) {
const { total, father, mother } = res.data;
// 更新 userData
userData.value = [
{ title: '总用户数', value: total, icon: 'fa-solid fa-users', color: '#3973FF', percentage: 0, isUp: false },
{ title: '父亲数', value: father !== undefined ? father : 0, icon: 'fa-solid fa-person', color: '#409EFF', percentage: 0, isUp: false },
{ title: '母亲数', value: mother !== undefined ? mother : 0, icon: 'fa-solid fa-person-dress', color: '#F56C6C', percentage: 0, isUp: false }
];
// 更新用户柱状图
updateUserChart(total, father, mother);
}
}
// 初始化宝贝柱状图
const initBabyChart = () => {
if (babyChartRef.value) {
babyChartInstance.value = echarts.init(babyChartRef.value);
babyChartInstance.value.setOption({
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: {
type: 'category',
data: ['总宝贝数', '男宝宝数', '女宝宝数'],
axisTick: { alignWithLabel: true }
},
yAxis: { type: 'value' },
series: [
{
name: '数量',
type: 'bar',
barWidth: '60%',
data: [
{ value: 0, itemStyle: { color: '#67C23A' } },
{ value: 0, itemStyle: { color: '#409EFF' } },
{ value: 0, itemStyle: { color: '#F56C6C' } }
]
}
]
});
}
};
// 初始化用户柱状图
const initUserChart = () => {
if (userChartRef.value) {
userChartInstance.value = echarts.init(userChartRef.value);
userChartInstance.value.setOption({
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: {
type: 'category',
data: ['总用户数', '父亲数', '母亲数'],
axisTick: { alignWithLabel: true }
},
yAxis: { type: 'value' },
series: [
{
name: '数量',
type: 'bar',
barWidth: '60%',
data: [
{ value: 0, itemStyle: { color: '#3973FF' } },
{ value: 0, itemStyle: { color: '#409EFF' } },
{ value: 0, itemStyle: { color: '#F56C6C' } }
]
}
]
});
}
};
// 更新宝贝柱状图数据
const updateBabyChart = (total: number, male: number, female: number) => {
if (babyChartInstance.value) {
babyChartInstance.value.setOption({
series: [{
data: [
{ value: total, itemStyle: { color: '#67C23A' } },
{ value: male, itemStyle: { color: '#409EFF' } },
{ value: female, itemStyle: { color: '#F56C6C' } }
]
}]
});
}
};
// 更新用户柱状图数据
const updateUserChart = (total: number, father: number, mother: number) => {
if (userChartInstance.value) {
userChartInstance.value.setOption({
series: [{
data: [
{ value: total, itemStyle: { color: '#3973FF' } },
{ value: father, itemStyle: { color: '#409EFF' } },
{ value: mother, itemStyle: { color: '#F56C6C' } }
]
}]
});
}
};
// 生命周期与自适应
const handleResize = () => {
babyChartInstance.value?.resize();
userChartInstance.value?.resize();
};
onMounted(() => {
initBabyChart();
initUserChart();
fetchGetBabyDatas();
fetchGetUserDatas();
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
babyChartInstance.value?.dispose();
userChartInstance.value?.dispose();
});
</script>
<style lang="scss" scoped>
.statistics-container {
padding: 20px;
min-height: 100vh;
}
.data-overview {
margin-bottom: 20px;
}
.data-card {
.card-content {
display: flex;
align-items: center;
.icon-box {
width: 56px;
height: 56px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
color: #fff;
font-size: 24px;
}
}
.text-box {
.title {
font-size: 14px;
color: #909399;
}
.value {
font-size: 24px;
font-weight: bold;
margin: 4px 0;
color: var(--el-text-color-primary);
}
}
}
.charts-row {
margin-top: 20px;
.chart-box {
height: 350px;
width: 100%;
}
}
</style>