From 3df460667d74868d0a54fce12d5e881bbd6c0c1a Mon Sep 17 00:00:00 2001
From: WXL <wl_5969728@163.com>
Date: 星期四, 17 九月 2026 17:11:18 +0800
Subject: [PATCH] 青岛维护统计
---
src/views/statistics/case.vue | 369 +++++++++++++++++++++++++---------------------------
1 files changed, 175 insertions(+), 194 deletions(-)
diff --git a/src/views/statistics/case.vue b/src/views/statistics/case.vue
index 0b7f00b..8bc4386 100644
--- a/src/views/statistics/case.vue
+++ b/src/views/statistics/case.vue
@@ -35,16 +35,25 @@
</el-form-item>
<el-form-item>
- <el-button type="primary" icon="el-icon-search" @click="handleSearch">鏌ヨ</el-button>
- <el-button icon="el-icon-refresh" @click="handleReset">閲嶇疆</el-button>
- <el-button type="success" icon="el-icon-download" @click="handleExport">瀵煎嚭</el-button>
+ <el-button type="primary" icon="el-icon-search" @click="handleSearch"
+ >鏌ヨ</el-button
+ >
+ <el-button icon="el-icon-refresh" @click="handleReset"
+ >閲嶇疆</el-button
+ >
+ <el-button
+ type="success"
+ icon="el-icon-download"
+ @click="handleExport"
+ >瀵煎嚭</el-button
+ >
</el-form-item>
</el-form>
</el-card>
<!-- 鏁版嵁姒傝 -->
<el-row :gutter="20" class="overview-row">
- <el-col :span="6" v-for="item in overviewData" :key="item.title">
+ <el-col :span="8" v-for="item in overviewData" :key="item.title">
<el-card class="overview-card">
<div class="overview-content">
<div class="overview-icon" :style="{ backgroundColor: item.color }">
@@ -71,7 +80,7 @@
type="text"
@click="toggleChartType('trend')"
>
- {{ chartTypes.trend === 'line' ? '鏌辩姸鍥�' : '鎶樼嚎鍥�' }}
+ {{ chartTypes.trend === "line" ? "鏌辩姸鍥�" : "鎶樼嚎鍥�" }}
</el-button>
</div>
<EChartsWrapper
@@ -117,15 +126,23 @@
border
stripe
>
- <el-table-column label="鍦板尯" prop="region" align="center" />
- <el-table-column label="鏈堜唤" prop="month" align="center" >
+ <el-table-column label="鍦板尯" prop="region" align="center" />
+ <el-table-column label="鏈堜唤" prop="month" align="center">
<template slot-scope="{ row }">
{{ formatMonth(row.month) }}
</template>
</el-table-column>
- <el-table-column label="娼滃湪鎹愮尞妗堜緥鏁�" prop="potentialCount" align="center" />
- <el-table-column label="瀹屾垚鎹愮尞鏁伴噺" prop="completedCount" align="center" />
- <el-table-column label="瀹屾垚鐜�" prop="completionRate" align="center" >
+ <el-table-column
+ label="娼滃湪鎹愮尞妗堜緥鏁�"
+ prop="potentialCount"
+ align="center"
+ />
+ <el-table-column
+ label="瀹屾垚鎹愮尞鏁伴噺"
+ prop="completedCount"
+ align="center"
+ />
+ <el-table-column label="瀹屾垚鐜�" prop="completionRate" align="center">
<template slot-scope="{ row }">
<el-progress
:percentage="row.completionRate"
@@ -135,13 +152,9 @@
<span>{{ row.completionRate }}%</span>
</template>
</el-table-column>
- <el-table-column label="妗堜緥鏄庣粏" align="center" >
+ <el-table-column label="妗堜緥鏄庣粏" align="center">
<template slot-scope="{ row }">
- <el-button
- type="text"
- size="mini"
- @click="viewCaseDetail(row)"
- >
+ <el-button type="text" size="mini" @click="viewCaseDetail(row)">
鏌ョ湅({{ row.caseCount || 0 }})
</el-button>
</template>
@@ -161,26 +174,21 @@
</el-card>
<!-- 妗堜緥鏄庣粏寮圭獥 -->
- <el-dialog
- title="妗堜緥鏄庣粏"
- :visible.sync="showDetail"
-
- append-to-body
- >
- <case-detail
- :query="detailQuery"
- @close="showDetail = false"
- />
+ <el-dialog title="妗堜緥鏄庣粏" :visible.sync="showDetail" append-to-body>
+ <case-detail :query="detailQuery" @close="showDetail = false" />
</el-dialog>
</div>
</template>
<script>
-import EChartsWrapper from '@/components/charts/EChartsWrapper.vue';
-import CaseDetail from './components/CaseDetail.vue';
-
+import EChartsWrapper from "@/components/charts/EChartsWrapper.vue";
+import CaseDetail from "./components/CaseDetail.vue";
+import { caseTrendArea, caseTrend, caseTrendMoon, totals,trendList } from "@/api/businessApi";
+import { toTotalsList, toMonth, toNum } from "@/utils/totals";
+import statisticsMode from "@/utils/statisticsMode";
+import { COLORS, axisTooltip, grid, xAxis, yAxis, legend, barSeries, lineSeries } from "@/utils/chartStyle";
export default {
- name: 'CaseStatistics',
+ name: "CaseStatistics",
components: {
EChartsWrapper,
CaseDetail
@@ -195,15 +203,15 @@
limit: 10
},
- // 鍦板尯閫夐」
+ // 鍦板尯閫夐」锛坴alue 鐩存帴浼犲湴鍖哄悕绉扮粰鎺ュ彛 area 瀛楁锛�
regionOptions: [
- { label: '闈掑矝鍦板尯', value: 'qingdao' },
- { label: '鏃ョ収鍦板尯', value: 'rizhao' },
- { label: '娴庡崡鍦板尯', value: 'jinan' },
- { label: '鐑熷彴鍦板尯', value: 'yantai' },
- { label: '濞佹捣鍦板尯', value: 'weihai' },
- { label: '娼嶅潑鍦板尯', value: 'weifang' },
- { label: '涓存矀鍦板尯', value: 'linyi' }
+ { label: "闈掑矝鍦板尯", value: "闈掑矝鍦板尯" },
+ { label: "鏃ョ収鍦板尯", value: "鏃ョ収鍦板尯" },
+ { label: "娴庡崡鍦板尯", value: "娴庡崡鍦板尯" },
+ { label: "鐑熷彴鍦板尯", value: "鐑熷彴鍦板尯" },
+ { label: "濞佹捣鍦板尯", value: "濞佹捣鍦板尯" },
+ { label: "娼嶅潑鍦板尯", value: "娼嶅潑鍦板尯" },
+ { label: "涓存矀鍦板尯", value: "涓存矀鍦板尯" }
],
// 鍔犺浇鐘舵��
@@ -215,15 +223,32 @@
// 姒傝鏁版嵁
overviewData: [
- { title: '鎬绘綔鍦ㄦ崘鐚渚�', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-s-flag', color: '#409EFF' },
- { title: '鎬诲畬鎴愭崘鐚暟閲�', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-circle-check', color: '#67C23A' },
- { title: '骞冲潎瀹屾垚鐜�', value: '0%', desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-s-data', color: '#E6A23C' },
- { title: '娑夊強妗堜緥鏁�', value: 0, desc: '缁熻鍛ㄦ湡鍐�', icon: 'el-icon-document', color: '#F56C6C' }
+ {
+ title: "鎬绘綔鍦ㄦ崘鐚渚�",
+ value: 0,
+ desc: "缁熻鍛ㄦ湡鍐�",
+ icon: "el-icon-s-flag",
+ color: "#5B8FF9"
+ },
+ {
+ title: "鎬诲畬鎴愭崘鐚暟閲�",
+ value: 0,
+ desc: "缁熻鍛ㄦ湡鍐�",
+ icon: "el-icon-circle-check",
+ color: "#5AD8A6"
+ },
+ {
+ title: "骞冲潎瀹屾垚鐜�",
+ value: "0%",
+ desc: "缁熻鍛ㄦ湡鍐�",
+ icon: "el-icon-s-data",
+ color: "#F6BD16"
+ }
],
// 鍥捐〃绫诲瀷
chartTypes: {
- trend: 'line'
+ trend: "line"
},
// 鍥捐〃鏁版嵁
@@ -234,200 +259,157 @@
// 寮圭獥鎺у埗
showDetail: false,
- detailQuery: {},
-
- // 妯℃嫙鏁版嵁
- mockData: {
- table: [
- { id: 1, region: '闈掑矝鍦板尯', month: '2024-01', potentialCount: 15, completedCount: 8, completionRate: 53.3, caseCount: 5 },
- { id: 2, region: '闈掑矝鍦板尯', month: '2024-02', potentialCount: 18, completedCount: 10, completionRate: 55.6, caseCount: 6 },
- { id: 3, region: '鏃ョ収鍦板尯', month: '2024-01', potentialCount: 8, completedCount: 4, completionRate: 50.0, caseCount: 3 },
- { id: 4, region: '鏃ョ収鍦板尯', month: '2024-02', potentialCount: 10, completedCount: 6, completionRate: 60.0, caseCount: 4 },
- { id: 5, region: '娴庡崡鍦板尯', month: '2024-01', potentialCount: 20, completedCount: 12, completionRate: 60.0, caseCount: 7 },
- { id: 6, region: '娴庡崡鍦板尯', month: '2024-02', potentialCount: 22, completedCount: 14, completionRate: 63.6, caseCount: 8 }
- ],
- trend: [
- { month: '2024-01', potential: 55, completed: 31 },
- { month: '2024-02', potential: 64, completed: 39 },
- { month: '2024-03', potential: 58, completed: 35 },
- { month: '2024-04', potential: 62, completed: 40 },
- { month: '2024-05', potential: 68, completed: 45 },
- { month: '2024-06', potential: 72, completed: 50 }
- ],
- region: [
- { region: '闈掑矝鍦板尯', potential: 150, completed: 85 },
- { region: '鏃ョ収鍦板尯', potential: 80, completed: 45 },
- { region: '娴庡崡鍦板尯', potential: 200, completed: 120 },
- { region: '鐑熷彴鍦板尯', potential: 100, completed: 60 }
- ]
- }
+ detailQuery: {}
};
},
created() {
this.loadData();
+ // 鏁版嵁婧�(mock/鐪熷疄鎺ュ彛)鍒囨崲鏃堕噸鏂板姞杞�
+ this.$watch(() => statisticsMode.useMock, () => this.loadData());
},
methods: {
+ // 缁勮鎺ュ彛鏌ヨ鏉′欢
+ buildQuery() {
+ const q = {};
+ if (this.query.region && this.query.region.length) {
+ q.area = this.query.region.join(",");
+ }
+ if (this.query.monthRange && this.query.monthRange.length === 2) {
+ q.startTime = this.query.monthRange[0];
+ q.endTime = this.query.monthRange[1];
+ }
+ return q;
+ },
+
// 鍔犺浇鏁版嵁
async loadData() {
this.loading = true;
try {
- // 妯℃嫙鎺ュ彛璋冪敤
- await new Promise(resolve => setTimeout(resolve, 500));
+ const q = this.buildQuery();
+ const [areaRes, trendRes, moonRes, totalsRes] = await Promise.all([
+ caseTrendArea(q),
+ caseTrend(q),
+ caseTrendMoon(q),
+ totals(q)
+ ]);
- // 澶勭悊琛ㄦ牸鏁版嵁
- let filteredData = [...this.mockData.table];
+ // 鍦板尯鍒嗗竷瀵规瘮
+ this.chartData.region = toTotalsList(areaRes).map(item => ({
+ region: item.area || item.region || "",
+ potential: toNum(item.potentialCount),
+ completed: toNum(item.completeCount)
+ }));
- if (this.query.region.length > 0) {
- filteredData = filteredData.filter(item =>
- this.query.region.includes(item.region.replace('鍦板尯', '').toLowerCase())
- );
- }
+ // 妗堜緥瓒嬪娍
+ this.chartData.trend = toTotalsList(trendRes).map(item => ({
+ month: toMonth(item.donateTime) || toMonth(item.month),
+ potential: toNum(item.potentialCount),
+ completed: toNum(item.completeCount)
+ }));
- if (this.query.monthRange && this.query.monthRange.length === 2) {
- const [start, end] = this.query.monthRange;
- filteredData = filteredData.filter(item =>
- item.month >= start && item.month <= end
- );
- }
+ // 鏄庣粏琛紙鍦板尯 x 鏈堜唤锛�
+ const all = toTotalsList(moonRes).map(item => {
+ const potential = toNum(item.potentialCount);
+ const completed = toNum(item.completeCount);
+ const rate =
+ potential > 0
+ ? Number(((completed / potential) * 100).toFixed(1))
+ : 0;
+ return {
+ region: item.area || item.region || "",
+ month: toMonth(item.donateTime) || toMonth(item.month),
+ potentialCount: potential,
+ completedCount: completed,
+ completionRate: rate,
+ caseCount: toNum(item.totalCount) || potential
+ };
+ });
+ this.total = all.length;
+ const start = (this.query.page - 1) * this.query.limit;
+ this.tableData = all.slice(start, start + this.query.limit);
- this.tableData = filteredData;
- this.total = filteredData.length;
-
- // 鏇存柊姒傝鏁版嵁
- this.updateOverviewData(filteredData);
-
- // 鏇存柊鍥捐〃鏁版嵁
- this.chartData.trend = [...this.mockData.trend];
- this.chartData.region = [...this.mockData.region];
-
+ this.updateOverviewData(toTotalsList(totalsRes));
} catch (error) {
- console.error('鍔犺浇鏁版嵁澶辫触:', error);
- this.$message.error('鏁版嵁鍔犺浇澶辫触');
+ console.error("鍔犺浇鏁版嵁澶辫触:", error);
+ this.$message.error("鏁版嵁鍔犺浇澶辫触");
} finally {
this.loading = false;
}
},
- // 鏇存柊姒傝鏁版嵁
- updateOverviewData(data) {
- if (data.length === 0) return;
-
- const totalPotential = data.reduce((sum, item) => sum + item.potentialCount, 0);
- const totalCompleted = data.reduce((sum, item) => sum + item.completedCount, 0);
- const avgRate = data.length > 0
- ? (data.reduce((sum, item) => sum + item.completionRate, 0) / data.length).toFixed(1)
- : 0;
- const totalCases = data.reduce((sum, item) => sum + (item.caseCount || 0), 0);
+ // 鏇存柊姒傝鏁版嵁锛堟�绘綔鍦�/鎬诲畬鎴愭崘鐚暟鏉ヨ嚜 /opo/totals/totals 鎺ュ彛锛�
+ updateOverviewData(totalsList) {
+ const totalPotential = totalsList.reduce(
+ (sum, item) => sum + toNum(item.totalPotential),
+ 0
+ );
+ const totalCompleted = totalsList.reduce(
+ (sum, item) => sum + toNum(item.totalCompletion),
+ 0
+ );
+ const avgRate =
+ totalPotential > 0
+ ? ((totalCompleted / totalPotential) * 100).toFixed(1)
+ : 0;
this.overviewData[0].value = totalPotential;
this.overviewData[1].value = totalCompleted;
this.overviewData[2].value = `${avgRate}%`;
- this.overviewData[3].value = totalCases;
},
// 鑾峰彇瓒嬪娍鍥捐〃閰嶇疆
getTrendChartOptions() {
- const isLine = this.chartTypes.trend === 'line';
+ const isLine = this.chartTypes.trend === "line";
+ const xData = this.chartData.trend.map(item => item.month);
+ const potential = this.chartData.trend.map(item => item.potential);
+ const completed = this.chartData.trend.map(item => item.completed);
+
+ const series = isLine
+ ? [
+ lineSeries("娼滃湪鎹愮尞鏁�", potential, COLORS.blue, { area: true }),
+ lineSeries("瀹屾垚鎹愮尞鏁�", completed, COLORS.green, { area: true })
+ ]
+ : [
+ barSeries("娼滃湪鎹愮尞鏁�", potential, COLORS.blue),
+ barSeries("瀹屾垚鎹愮尞鏁�", completed, COLORS.green)
+ ];
return {
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'shadow'
- }
- },
- legend: {
- data: ['娼滃湪鎹愮尞鏁�', '瀹屾垚鎹愮尞鏁�']
- },
- grid: {
- left: '3%',
- right: '4%',
- bottom: '3%',
- containLabel: true
- },
- xAxis: {
- type: 'category',
- data: this.chartData.trend.map(item => this.formatMonth(item.month))
- },
- yAxis: {
- type: 'value',
- name: '鏁伴噺(涓�)'
- },
- series: [
- {
- name: '娼滃湪鎹愮尞鏁�',
- type: isLine ? 'line' : 'bar',
- data: this.chartData.trend.map(item => item.potential),
- itemStyle: { color: '#409EFF' },
- smooth: isLine,
- lineStyle: isLine ? { width: 3 } : {}
- },
- {
- name: '瀹屾垚鎹愮尞鏁�',
- type: isLine ? 'line' : 'bar',
- data: this.chartData.trend.map(item => item.completed),
- itemStyle: { color: '#67C23A' },
- smooth: isLine,
- lineStyle: isLine ? { width: 3 } : {}
- }
- ]
+ tooltip: axisTooltip(),
+ legend: legend(["娼滃湪鎹愮尞鏁�", "瀹屾垚鎹愮尞鏁�"]),
+ grid: grid(),
+ xAxis: xAxis(xData),
+ yAxis: yAxis(),
+ series
};
},
// 鑾峰彇鍦板尯鍥捐〃閰嶇疆
getRegionChartOptions() {
return {
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'shadow'
- }
- },
- legend: {
- data: ['娼滃湪鎹愮尞鏁�', '瀹屾垚鎹愮尞鏁�']
- },
- grid: {
- left: '3%',
- right: '4%',
- bottom: '3%',
- containLabel: true
- },
- xAxis: {
- type: 'category',
- data: this.chartData.region.map(item => item.region)
- },
- yAxis: {
- type: 'value',
- name: '鏁伴噺(涓�)'
- },
+ tooltip: axisTooltip(),
+ legend: legend(["娼滃湪鎹愮尞鏁�", "瀹屾垚鎹愮尞鏁�"]),
+ grid: grid(),
+ xAxis: xAxis(this.chartData.region.map(item => item.region)),
+ yAxis: yAxis(),
series: [
- {
- name: '娼滃湪鎹愮尞鏁�',
- type: 'bar',
- data: this.chartData.region.map(item => item.potential),
- itemStyle: { color: '#409EFF' }
- },
- {
- name: '瀹屾垚鎹愮尞鏁�',
- type: 'bar',
- data: this.chartData.region.map(item => item.completed),
- itemStyle: { color: '#67C23A' }
- }
+ barSeries("娼滃湪鎹愮尞鏁�", this.chartData.region.map(item => item.potential), COLORS.blue),
+ barSeries("瀹屾垚鎹愮尞鏁�", this.chartData.region.map(item => item.completed), COLORS.green)
]
};
},
// 鑾峰彇杩涘害鏉¢鑹�
getProgressColor(percentage) {
- if (percentage >= 60) return '#67C23A';
- if (percentage >= 50) return '#E6A23C';
- return '#F56C6C';
+ if (percentage >= 60) return COLORS.green;
+ if (percentage >= 50) return COLORS.amber;
+ return COLORS.coral;
},
// 鏍煎紡鍖栨湀浠芥樉绀�
formatMonth(month) {
- if (!month) return '';
- return month.replace('-', '骞�') + '鏈�';
+ if (!month) return "";
+ return month.replace("-", "骞�") + "鏈�";
},
// 澶勭悊鏌ヨ
@@ -456,13 +438,12 @@
// 澶勭悊瀵煎嚭
handleExport() {
- this.$message.info('瀵煎嚭鍔熻兘寮�鍙戜腑...');
- // 杩欓噷鍙互璋冪敤瀵煎嚭鎺ュ彛
+ this.$message.info("瀵煎嚭鍔熻兘寮�鍙戜腑...");
},
// 鍒囨崲鍥捐〃绫诲瀷
toggleChartType(type) {
- this.chartTypes[type] = this.chartTypes[type] === 'line' ? 'bar' : 'line';
+ this.chartTypes[type] = this.chartTypes[type] === "line" ? "bar" : "line";
},
// 鏌ョ湅妗堜緥璇︽儏
@@ -537,7 +518,7 @@
.overview-desc {
font-size: 12px;
- color: #C0C4CC;
+ color: #c0c4cc;
}
.chart-row {
--
Gitblit v1.9.3