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 @@