From e69afe7f68fefd09101ab9a6811190d9655957e9 Mon Sep 17 00:00:00 2001
From: wersd <1523826083@qq.com>
Date: Sun, 10 Aug 2025 18:41:14 +0800
Subject: [PATCH] =?UTF-8?q?add=20=E8=BF=90=E8=90=A5=E6=95=B0=E6=8D=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.vscode/settings.json | 4 +-
package-lock.json | 46 ++++++
package.json | 1 +
src/api/crm/project/monthly/index.ts | 10 ++
.../project/component/ProjectCompareChart.vue | 148 ++++++++++++++++++
.../project/component/ProjectMonthlyChart.vue | 50 ++++--
.../monthly/ProjectMonthlyStatForm.vue | 4 +
src/views/crm/project/monthly/index.vue | 15 +-
8 files changed, 258 insertions(+), 20 deletions(-)
create mode 100644 src/views/crm/project/component/ProjectCompareChart.vue
diff --git a/.vscode/settings.json b/.vscode/settings.json
index cc1dae50d..9ff35324d 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -62,7 +62,7 @@
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
- "editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
+ "editor.defaultFormatter": "vscode.typescript-language-features"
},
"[typescriptreact]": {
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
@@ -87,7 +87,7 @@
"source.fixAll.stylelint": "explicit"
},
"[vue]": {
- "editor.defaultFormatter": "octref.vetur"
+ "editor.defaultFormatter": "Vue.volar"
},
"i18n-ally.localesPaths": ["src/locales"],
"i18n-ally.keystyle": "nested",
diff --git a/package-lock.json b/package-lock.json
index 78dd16ce4..cba14c83b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -55,6 +55,7 @@
"video.js": "^7.21.5",
"vue": "3.5.12",
"vue-dompurify-html": "^4.1.4",
+ "vue-echarts": "^7.0.3",
"vue-i18n": "9.10.2",
"vue-router": "4.4.5",
"vue-types": "^5.1.1",
@@ -16879,6 +16880,51 @@
}
}
},
+ "node_modules/vue-echarts": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/vue-echarts/-/vue-echarts-7.0.3.tgz",
+ "integrity": "sha512-/jSxNwOsw5+dYAUcwSfkLwKPuzTQ0Cepz1LxCOpj2QcHrrmUa/Ql0eQqMmc1rTPQVrh2JQ29n2dhq75ZcHvRDw==",
+ "license": "MIT",
+ "dependencies": {
+ "vue-demi": "^0.13.11"
+ },
+ "peerDependencies": {
+ "@vue/runtime-core": "^3.0.0",
+ "echarts": "^5.5.1",
+ "vue": "^2.7.0 || ^3.1.1"
+ },
+ "peerDependenciesMeta": {
+ "@vue/runtime-core": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vue-echarts/node_modules/vue-demi": {
+ "version": "0.13.11",
+ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
+ "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
"node_modules/vue-eslint-parser": {
"version": "9.4.3",
"resolved": "https://mirrors.huaweicloud.com/repository/npm/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz",
diff --git a/package.json b/package.json
index 83e530071..e8cb03d84 100644
--- a/package.json
+++ b/package.json
@@ -72,6 +72,7 @@
"video.js": "^7.21.5",
"vue": "3.5.12",
"vue-dompurify-html": "^4.1.4",
+ "vue-echarts": "^7.0.3",
"vue-i18n": "9.10.2",
"vue-router": "4.4.5",
"vue-types": "^5.1.1",
diff --git a/src/api/crm/project/monthly/index.ts b/src/api/crm/project/monthly/index.ts
index d18caeb30..42602b526 100644
--- a/src/api/crm/project/monthly/index.ts
+++ b/src/api/crm/project/monthly/index.ts
@@ -63,4 +63,14 @@ export const syncProjectMonthlyStat = async (data: ProjectMonthlyStatVO) => {
// 查询项目月度统计图数据
export const getProjectMonthlyStatChartData = async (params: any) => {
return await request.get({ url: `/crm/project-monthly-stat/get-chart-data`, params})
+}
+
+// 查询项目对比图数据
+export const getProjectCompareChartData = async (data: any) => {
+ return await request.post({ url: `/crm/project-monthly-stat/get-project-data`, data})
+}
+
+// 获取项目列表
+export const getProjectSimpleList = async () => {
+ return await request.get({ url: `/crm/project-monthly-stat/project-simple-list` })
}
\ No newline at end of file
diff --git a/src/views/crm/project/component/ProjectCompareChart.vue b/src/views/crm/project/component/ProjectCompareChart.vue
new file mode 100644
index 000000000..0a79917f8
--- /dev/null
+++ b/src/views/crm/project/component/ProjectCompareChart.vue
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/crm/project/component/ProjectMonthlyChart.vue b/src/views/crm/project/component/ProjectMonthlyChart.vue
index 95c8070f1..b98444862 100644
--- a/src/views/crm/project/component/ProjectMonthlyChart.vue
+++ b/src/views/crm/project/component/ProjectMonthlyChart.vue
@@ -3,7 +3,7 @@
@@ -38,7 +38,7 @@
-
+
@@ -66,7 +66,7 @@ use([
])
const queryParams = reactive({
- projectId: undefined,
+ projectIds: [],
startMonth: undefined,
endMonth: undefined
@@ -77,9 +77,14 @@ const chartData = ref([])
const chartOption = ref({})
const getChartData = async () => {
+ // 控制结束时间>=起始时间
+ if (queryParams.startMonth && queryParams.endMonth && queryParams.endMonth < queryParams.startMonth) {
+ ElMessage.error('结束月份不能小于起始月份')
+ return
+ }
// 获取月度统计数据
chartData.value = await ProjectMonthlyStatApi.getProjectMonthlyStatChartData({
- projectId: queryParams.projectId,
+ projectIds: queryParams.projectIds,
startMonth: queryParams.startMonth,
endMonth: queryParams.endMonth
})
@@ -98,37 +103,48 @@ function updateChartOption() {
const totalIncome = chartData.value.map(item => item.totalIncome)
const grossProfit = chartData.value.map(item => item.grossProfit)
const grossProfitRate = chartData.value.map(item => item.grossProfitRate)
+ const perGunCharging = chartData.value.map(item => item.perGunCharging)
+ const perGunProfit = chartData.value.map(item => item.perGunProfit)
+ const electricityServiceFee = chartData.value.map(item => item.electricityServiceFee)
chartOption.value = {
- title: { text: '项目月度统计(折柱混合)', left: 'center' },
tooltip: { trigger: 'axis' },
- legend: { data: ['充电量', '总营收', '毛利润', '毛利率'], top: 30 },
+ legend: { data: [
+ '充电量(万度)', '总营收(万元)', '毛利润(万元)', '毛利率(%)',
+ '单枪充电量(度)', '单枪服务费(元)', '度电服务费(元)'
+ ], top: 30 },
xAxis: [
{ type: 'category', data: xData, axisTick: { alignWithLabel: true }}
],
yAxis: [
- { type: 'value', name: '金额(万元)', position: 'left', min: null, max: null, alignTicks: true }, // 0: 总营收/毛利润
- { type: 'value', name: '充电量(万度)', position: 'left', offset: 120, min: null, max: null, alignTicks: true }, // 1: 充电量
- { type: 'value', name: '毛利率(%)', position: 'right', min: null, max: null, axisLabel: { formatter: '{value}%' }, alignTicks: true } // 2: 毛利率
+ { type: 'value', name: '金额/充电量', position: 'left', min: null, max: null, alignTicks: true }, // 0: 总营收/毛利润/充电量/单枪充电量/单枪服务费/度电服务费
+ { type: 'value', name: '毛利率', position: 'right', min: null, max: null, axisLabel: { formatter: '{value}%' }, alignTicks: true } // 1: 毛利率
],
series: [
- { name: '总营收', type: 'bar', data: totalIncome, yAxisIndex: 0, barMaxWidth: 28 },
- { name: '毛利润', type: 'bar', data: grossProfit, yAxisIndex: 0, barMaxWidth: 28 },
- { name: '充电量', type: 'bar', data: chargingQuantity, yAxisIndex: 1, barMaxWidth: 28 },
- { name: '毛利率', type: 'line', data: grossProfitRate, yAxisIndex: 2, smooth: true, symbol: 'circle', symbolSize: 8 }
+ { name: '总营收(万元)', type: 'bar', data: totalIncome, yAxisIndex: 0, barMaxWidth: 28 },
+ { name: '毛利润(万元)', type: 'bar', data: grossProfit, yAxisIndex: 0, barMaxWidth: 28 },
+ { name: '充电量(万度)', type: 'bar', data: chargingQuantity, yAxisIndex: 0, barMaxWidth: 28 },
+ { name: '单枪充电量(度)', type: 'bar', data: perGunCharging, yAxisIndex: 0, barMaxWidth: 28 },
+ { name: '单枪服务费(元)', type: 'bar', data: perGunProfit, yAxisIndex: 0, barMaxWidth: 28 },
+ { name: '度电服务费(元)', type: 'bar', data: electricityServiceFee, yAxisIndex: 0, barMaxWidth: 28 },
+ { name: '毛利率(%)', type: 'line', data: grossProfitRate, yAxisIndex: 1, smooth: true, symbol: 'circle', symbolSize: 8 }
]
}
}
onMounted(async () => {
// 获取项目列表
- const list = await ProjectApi.getProjectSimpleList()
- projectList.value = [{ id: undefined, name: '全部' }, ...list]
- queryParams.projectId = undefined
+ const list = await ProjectMonthlyStatApi.getProjectSimpleList()
+ projectList.value = [{ id: undefined, deptName: '全部' }, ...list]
+ queryParams.projectIds = []
await getChartData()
})
diff --git a/src/views/crm/project/monthly/ProjectMonthlyStatForm.vue b/src/views/crm/project/monthly/ProjectMonthlyStatForm.vue
index 9c58462d1..f4e45cfc0 100644
--- a/src/views/crm/project/monthly/ProjectMonthlyStatForm.vue
+++ b/src/views/crm/project/monthly/ProjectMonthlyStatForm.vue
@@ -38,6 +38,9 @@
@@ -54,6 +57,7 @@