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';
|
|
|
|
|
|
2025-07-07 14:38:01 +08:00
|
|
|
|
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
|
|
|
|
|
2025-07-07 14:38:01 +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 () => {
|
2025-07-07 14:38:01 +08:00
|
|
|
|
// 任务数
|
|
|
|
|
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 ?? [];
|
2025-07-07 14:38:01 +08:00
|
|
|
|
// 租赁金额分布
|
|
|
|
|
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',
|
2025-07-07 14:38:01 +08:00
|
|
|
|
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: [
|
|
|
|
|
{
|
2025-07-07 14:38:01 +08:00
|
|
|
|
// name: '金额',
|
2025-06-26 18:02:43 +08:00
|
|
|
|
type: 'pie',
|
|
|
|
|
radius: '60%',
|
|
|
|
|
center: ['50%', '50%'],
|
2025-07-07 14:38:01 +08:00
|
|
|
|
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',
|
2025-07-07 14:38:01 +08:00
|
|
|
|
data: countByCusTypeData.type || [],
|
|
|
|
|
boundaryGap: true,
|
2025-06-26 18:02:43 +08:00
|
|
|
|
},
|
|
|
|
|
yAxis: { type: 'value' },
|
|
|
|
|
series: [
|
|
|
|
|
{
|
|
|
|
|
name: '订单数',
|
|
|
|
|
type: 'bar',
|
2025-07-07 14:38:01 +08:00
|
|
|
|
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',
|
2025-07-07 14:38:01 +08:00
|
|
|
|
data: countRenewRateData.month || [],
|
2025-06-26 18:02:43 +08:00
|
|
|
|
boundaryGap: false,
|
|
|
|
|
},
|
|
|
|
|
yAxis: { type: 'value' },
|
|
|
|
|
series: [
|
|
|
|
|
{
|
|
|
|
|
name: '订单数',
|
|
|
|
|
type: 'line',
|
2025-07-07 14:38:01 +08:00
|
|
|
|
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',
|
2025-07-07 14:38:01 +08:00
|
|
|
|
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',
|
2025-07-07 14:38:01 +08:00
|
|
|
|
data: countAchievedData.toral || [],
|
2025-06-27 18:03:13 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: '已完成数',
|
|
|
|
|
type: 'bar',
|
2025-07-07 14:38:01 +08:00
|
|
|
|
data: countAchievedData.finish || [],
|
2025-06-27 18:03:13 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: '完成率',
|
|
|
|
|
type: 'line',
|
|
|
|
|
yAxisIndex: 1,
|
2025-07-07 14:38:01 +08:00
|
|
|
|
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%'],
|
2025-07-07 14:38:01 +08:00
|
|
|
|
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;
|
|
|
|
|
}
|
2025-07-07 14:38:01 +08:00
|
|
|
|
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>
|
2025-07-07 14:38:01 +08:00
|
|
|
|
<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-07-07 14:38:01 +08:00
|
|
|
|
|
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>
|