admin-vben5/apps/web-antd/src/views/property/greenPlantRentalManagement/reportStatistics/index.vue

451 lines
11 KiB
Vue
Raw Normal View History

2025-06-26 18:02:43 +08:00
<script setup lang="ts">
2025-06-27 18:03:13 +08:00
import type { EchartsUIType } from '@vben/plugins/echarts';
import { onMounted, ref } from 'vue';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
2025-06-26 18:02:43 +08:00
import { Button } from 'ant-design-vue';
import { statisticsByTime,
countByRentalType,
countByCusType,
countRenewRate,
countByCusScore,
countOrderAndAmount,
countAchievedRate,
countAchieved
} from '#/api/property/reportStatistics';
2025-07-05 09:11:29 +08:00
2025-06-26 18:02:43 +08:00
const orderLineRef = ref<EchartsUIType>();
const leasePieRef = ref<EchartsUIType>();
const customerTypesBarRef = ref<EchartsUIType>();
const customerRenewalLineRef = ref<EchartsUIType>();
const conservationTasksBarRef = ref<EchartsUIType>();
const maintenanceQualityScoresPeiRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(orderLineRef);
const { renderEcharts: renderLeasePie } = useEcharts(leasePieRef);
2025-06-27 18:03:13 +08:00
const { renderEcharts: renderCustomerTypesBar } =
useEcharts(customerTypesBarRef);
const { renderEcharts: renderCustomerRenewalLine } = useEcharts(
customerRenewalLineRef,
);
const { renderEcharts: renderConservationTasksBar } = useEcharts(
conservationTasksBarRef,
);
const { renderEcharts: renderMaintenanceQualityScoresPei } = useEcharts(
maintenanceQualityScoresPeiRef,
);
2025-07-05 09:11:29 +08:00
const timeUnit = ref<number>(1)
2025-06-26 18:02:43 +08:00
const countOrderAndAmountDataAmount = ref<number>(0);
const countOrderAndAmountDataOrder = ref<number>(0);
const countAchievedRateData = ref<any>(null);
2025-07-05 09:11:29 +08:00
onMounted(async () => {
// 任务数
const countOrderAndAmountData= await countOrderAndAmount();
countOrderAndAmountDataAmount.value = countOrderAndAmountData.amount;
countOrderAndAmountDataOrder.value = countOrderAndAmountData.num;
const countAchievedRateDataRes: any = await countAchievedRate();
countAchievedRateData.value = countAchievedRateDataRes.rate;
2025-07-05 09:11:29 +08:00
// 查询订单数量趋势
const res = await statisticsByTime({ timeUnit: timeUnit.value });
const xAxisData = res?.time ?? [];
const seriesData = res?.counts ?? [];
// 租赁金额分布
const data = await countByRentalType();//返回的内容是amount: 1, type: "单点"
// 转换字段名为value和name
const convertedData = data.map((item: { amount: number; type: string }) => ({
value: item.amount,
name: item.type
}));
// 客户类型分配
const countByCusTypeData:any = await countByCusType();
//客户续租率趋势
const countRenewRateData:any = await countRenewRate();
// 养护任务完成情况
const countAchievedData:any = await countAchieved();
// 养护质量评分分布
const countByCusScoreData:any = await countByCusScore();
const countByCusScoreDataList = countByCusScoreData.map((item: { score: string; count: number }) => ({
value: item.count,
name: item.score
}));
2025-06-26 18:02:43 +08:00
renderEcharts({
tooltip: { trigger: 'axis' },
xAxis: {
type: 'category',
2025-07-05 09:11:29 +08:00
data: xAxisData,
2025-06-26 18:02:43 +08:00
boundaryGap: false,
},
yAxis: { type: 'value' },
series: [
{
name: '订单数',
type: 'line',
data: seriesData ||[],
2025-06-26 18:02:43 +08:00
smooth: true,
},
],
});
renderLeasePie({
title: { text: '租赁金额分布', left: 'center' },
tooltip: { trigger: 'item' },
legend: { orient: 'vertical', left: 'left' },
series: [
{
// name: '金额',
2025-06-26 18:02:43 +08:00
type: 'pie',
radius: '60%',
center: ['50%', '50%'],
data:convertedData || [],
2025-06-26 18:02:43 +08:00
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
label: {
formatter: '{b}: {c} ({d}%)',
show: true,
},
},
],
});
renderCustomerTypesBar({
title: { text: '客户类型分配' },
tooltip: { trigger: 'axis' },
xAxis: {
type: 'category',
data: countByCusTypeData.type || [],
boundaryGap: true,
2025-06-26 18:02:43 +08:00
},
yAxis: { type: 'value' },
series: [
{
name: '订单数',
type: 'bar',
data: countByCusTypeData.counts || [],
2025-06-26 18:02:43 +08:00
},
],
});
renderCustomerRenewalLine({
2025-06-27 18:03:13 +08:00
title: { text: '客户续租率趋势' },
2025-06-26 18:02:43 +08:00
tooltip: { trigger: 'axis' },
xAxis: {
type: 'category',
data: countRenewRateData.month || [],
2025-06-26 18:02:43 +08:00
boundaryGap: false,
},
yAxis: { type: 'value' },
series: [
{
name: '订单数',
type: 'line',
data: countRenewRateData.rate || [],
2025-06-26 18:02:43 +08:00
smooth: true,
},
],
});
renderConservationTasksBar({
2025-06-27 18:03:13 +08:00
title: { text: '养护任务完成情况' },
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
2025-06-26 18:02:43 +08:00
},
2025-06-27 18:03:13 +08:00
legend: {
data: ['计划任务数', '已完成数', '完成率'],
2025-06-26 18:02:43 +08:00
},
2025-06-27 18:03:13 +08:00
xAxis: [
{
type: 'category',
data: countAchievedData.type || [],
2025-06-27 18:03:13 +08:00
},
],
yAxis: [
{
type: 'value',
name: '任务数',
min: 0,
max: 200,
position: 'left',
},
{
type: 'value',
name: '完成率',
min: 0,
max: 100,
position: 'right',
axisLabel: {
formatter: '{value}%',
},
},
],
series: [
{
name: '计划任务数',
type: 'bar',
data: countAchievedData.toral || [],
2025-06-27 18:03:13 +08:00
},
{
name: '已完成数',
type: 'bar',
data: countAchievedData.finish || [],
2025-06-27 18:03:13 +08:00
},
{
name: '完成率',
type: 'line',
yAxisIndex: 1,
data: countAchievedData.rate || [],
2025-06-27 18:03:13 +08:00
},
],
2025-06-26 18:02:43 +08:00
});
renderMaintenanceQualityScoresPei({
2025-06-27 18:03:13 +08:00
title: { text: '养护质量评分分布', left: 'center' },
tooltip: {
trigger: 'item',
formatter: '{b} : {d}%',
},
legend: {
orient: 'horizontal',
left: 'center',
bottom: 10,
data: ['一星', '两星', '三星', '四星', '五星'],
},
series: [
{
name: '评分',
type: 'pie',
radius: '60%',
center: ['50%', '50%'],
data: countByCusScoreDataList || [],
2025-06-27 18:03:13 +08:00
label: {
formatter: '{b} {d}%',
show: true,
},
},
],
});
2025-06-26 18:02:43 +08:00
});
2025-07-05 09:11:29 +08:00
const nodeOptions = [
{ label: '日', value: 1 },
{ label: '周', value: 2 },
{ label: '月', value: 3 },
];
function handleAssociationChange(e: any) {
timeUnit.value = e.target.value;
}
function formatNumber(num: number | string) {
if (!num && num !== 0) {
return '';
}
num = num.toString();
const parts = num.split('.');
let integerPart: string = parts[0] || '0';
const decimalPart = parts.length > 1 ? '.' + parts[1] : '';
const rgx = /(\d+)(\d{3})/;//整体表示匹配一组由多个数字后跟三个数字组成的字符串
while (rgx.test(integerPart)) {
integerPart = integerPart.replace(rgx, `$1${','}$2`);
}
return integerPart + decimalPart;
}
2025-06-26 18:02:43 +08:00
</script>
2025-06-25 11:17:58 +08:00
<template>
2025-06-26 18:02:43 +08:00
<div class="main">
<div class="box">
2025-06-27 18:03:13 +08:00
<div class="title">
<div class="title-text">绿植租赁业务统计报表</div>
<div class="title-operate">
<div class="export">
<Button size="large" style="color: #fff; background-color: #22c55e">
导出数据
</Button>
</div>
</div>
</div>
<div class="content">
2025-06-30 17:42:56 +08:00
<div class="row">
<div class="box">
<div class="title">总订单数</div>
<div class="number">{{formatNumber(countOrderAndAmountDataAmount)}}</div>
<!-- <div class="percent">8.9%</div> -->
</div>
<div class="box">
<div class="title">累计租赁金额</div>
<div class="number">{{ formatNumber(countOrderAndAmountDataOrder) }}</div>
</div>
<div class="box">
<div class="title">绿植养护完成率</div>
<div class="number">{{ countAchievedRateData }}</div>
2025-06-30 17:42:56 +08:00
</div>
</div>
2025-06-27 18:03:13 +08:00
<div class="row-first">
<div class="item1">
2025-07-05 09:11:29 +08:00
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;">
<span style="font-size: 18px; font-weight: bold;">订单数量趋势</span>
<div>
<RadioGroup
v-model:value="timeUnit"
:options="nodeOptions"
button-style="solid"
option-type="button"
@change="handleAssociationChange"
/>
</div>
</div>
2025-06-27 18:03:13 +08:00
<EchartsUI
ref="orderLineRef"
height="350px"
width="100%"
style="background: #fff; border-radius: 8px"
/>
</div>
<div class="item2">
<EchartsUI
ref="leasePieRef"
height="350px"
width="100%"
style="background: #fff; border-radius: 8px"
/>
</div>
</div>
<div class="row-second">
<div class="item1">
<EchartsUI
ref="customerTypesBarRef"
height="350px"
width="100%"
style="background: #fff; border-radius: 8px"
/>
</div>
<div class="item2">
<EchartsUI
ref="customerRenewalLineRef"
height="350px"
width="100%"
style="background: #fff; border-radius: 8px"
/>
</div>
</div>
<div class="row-third">
<EchartsUI
ref="conservationTasksBarRef"
height="100%"
width="100%"
style="background: #fff; border-radius: 8px"
/>
2025-06-26 18:02:43 +08:00
</div>
2025-06-27 18:03:13 +08:00
<div class="row-fouth">
<EchartsUI
ref="maintenanceQualityScoresPeiRef"
height="100%"
width="100%"
style="background: #fff; border-radius: 8px"
/>
2025-06-26 18:02:43 +08:00
</div>
2025-06-27 18:03:13 +08:00
</div>
2025-06-25 11:17:58 +08:00
</div>
2025-06-26 18:02:43 +08:00
</div>
</template>
<style lang="scss" scoped>
.main {
width: 100%;
2025-06-27 18:03:13 +08:00
.box {
2025-06-26 18:02:43 +08:00
height: 100%;
2025-06-27 18:03:13 +08:00
margin: 40px;
.title {
display: flex;
justify-content: space-between;
.title-text {
font-size: 25px;
font-weight: bold;
}
.title-operate {
2025-06-26 18:02:43 +08:00
display: flex;
2025-06-27 18:03:13 +08:00
.export {
margin-left: 20px;
2025-06-26 18:02:43 +08:00
}
2025-06-27 18:03:13 +08:00
}
2025-06-26 18:02:43 +08:00
}
2025-06-27 18:03:13 +08:00
.content {
flex: 1;
height: 100%;
padding: 10px;
.row {
display: flex;
justify-content: space-between;
2025-06-30 17:42:56 +08:00
.box{
width: 300px;
height: 120px;
background-color: #fff;
border-radius: 8px;
margin: 40px 0px;
padding: 10px;
.title{
font-size: 20px;
}
.number{
font-size: 25px;
font-weight: bold;
}
.percent{
font-size: 15px; }
}
2025-06-27 18:03:13 +08:00
}
.row-first {
display: flex;
justify-content: space-between;
height: 400px;
2025-07-05 09:11:29 +08:00
margin-bottom: 50px;
2025-06-27 18:03:13 +08:00
}
.row-second {
display: flex;
justify-content: space-between;
height: 400px;
2025-07-05 09:11:29 +08:00
margin-bottom: 50px;
2025-06-27 18:03:13 +08:00
}
.row-third {
height: 400px;
margin-bottom: 50px;
2025-06-27 18:03:13 +08:00
}
.row-fouth {
height: 400px;
}
.item1 {
width: 45%;
2025-06-26 18:02:43 +08:00
height: 100%;
2025-07-05 09:11:29 +08:00
background-color: #fff;
padding: 10px;
border-radius: 8px;
// margin: 20px;
2025-06-27 18:03:13 +08:00
}
.item2 {
width: 50%;
height: 100%;
2025-07-05 09:11:29 +08:00
background-color: #fff;
padding: 10px;
border-radius: 8px;
2025-06-27 18:03:13 +08:00
}
2025-06-26 18:02:43 +08:00
}
}
}
2025-06-27 18:03:13 +08:00
</style>