From c8b6d2faba4607667796c77b6d734bd978d00f4b Mon Sep 17 00:00:00 2001 From: fyy <2717885210@qq.com> Date: Mon, 14 Jul 2025 11:53:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8D=E4=BF=9D=E6=B4=81?= =?UTF-8?q?=E3=80=81=E7=BB=BF=E6=A4=8D=E3=80=81=E5=9B=AD=E5=8C=BA=E3=80=81?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E7=94=A8=E6=88=B7=E7=9B=B8=E5=85=B3bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../property/community/community-modal.vue | 7 +- .../src/views/property/community/data.ts | 36 +- .../rentalPlan-detial-modal.vue | 24 +- .../rentalPlan-modal.vue | 19 +- .../reportStatistics/index.vue | 361 +++++++++--------- .../inspectionTask/data.ts | 17 +- .../inspectionTask/index.vue | 4 +- apps/web-antd/src/views/property/room/data.ts | 14 +- apps/web-antd/src/views/system/user/data.tsx | 3 +- .../src/plugins/extra-app-config.ts | 10 +- .../base/shared/src/constants/dict-enum.ts | 2 +- 11 files changed, 254 insertions(+), 243 deletions(-) diff --git a/apps/web-antd/src/views/property/community/community-modal.vue b/apps/web-antd/src/views/property/community/community-modal.vue index 606687f5..a2ffc793 100644 --- a/apps/web-antd/src/views/property/community/community-modal.vue +++ b/apps/web-antd/src/views/property/community/community-modal.vue @@ -65,7 +65,9 @@ const [BasicModal, modalApi] = useVbenModal({ if (isUpdate.value && id) { const record = await communityInfo(id); await formApi.setValues(record); + } + setupDeptSelect(); await markInitialized(); modalApi.modalLoading(false); @@ -83,7 +85,10 @@ async function handleConfirm() { } // getValues获取为一个readonly的对象 需要修改必须先深拷贝一次 const data: CommunityForm = cloneDeep(await formApi.getValues()); - data.cityFullName = currentSelectNode.fullName; + // data.cityFullName = currentSelectNode? currentSelectNode.fullName : data.cityFullName; + if (currentSelectNode?.fullName) { + data.cityFullName = currentSelectNode.fullName; + } await (isUpdate.value ? communityUpdate(data) : communityAdd(data)); resetInitialized(); emit('reload'); diff --git a/apps/web-antd/src/views/property/community/data.ts b/apps/web-antd/src/views/property/community/data.ts index 4a869a8b..1cc717ce 100644 --- a/apps/web-antd/src/views/property/community/data.ts +++ b/apps/web-antd/src/views/property/community/data.ts @@ -42,14 +42,14 @@ export const columns: VxeGridProps['columns'] = [ title: '地址', field: 'addr', }, - { - title: '经度', - field: 'lon', - }, - { - title: '维度', - field: 'lat', - }, + // { + // title: '经度', + // field: 'lon', + // }, + // { + // title: '维度', + // field: 'lat', + // }, { title: '占地面积', field: 'area', @@ -136,16 +136,16 @@ export const modalSchema: FormSchemaGetter = () => [ component: 'Input', rules: 'required', }, - { - label: '经度', - fieldName: 'lon', - component: 'Input', - }, - { - label: '维度', - fieldName: 'lat', - component: 'Input', - }, + // { + // label: '经度', + // fieldName: 'lon', + // component: 'Input', + // }, + // { + // label: '维度', + // fieldName: 'lat', + // component: 'Input', + // }, { label: '占地面积', fieldName: 'area', diff --git a/apps/web-antd/src/views/property/greenPlantRentalManagement/leasePogramManagement/rentalPlan-detial-modal.vue b/apps/web-antd/src/views/property/greenPlantRentalManagement/leasePogramManagement/rentalPlan-detial-modal.vue index f7af2e4f..e16c8d50 100644 --- a/apps/web-antd/src/views/property/greenPlantRentalManagement/leasePogramManagement/rentalPlan-detial-modal.vue +++ b/apps/web-antd/src/views/property/greenPlantRentalManagement/leasePogramManagement/rentalPlan-detial-modal.vue @@ -39,7 +39,29 @@ const detailSchema = [ api: async () => { const res = await plantsProductList({state:1,inventory:0}); plantListData = res.rows || []; - return res; + + // 获取当前模态框的数据,包含已添加的产品列表 + const modalData = modalApi.getData(); + const existingProducts = modalData?.existingProducts || []; + const currentProductId = modalData?.currentProductId; + + // 过滤掉已经添加到产品列表中的产品 + // 如果是编辑模式,需要排除当前正在编辑的产品 + const filteredRows = res.rows.filter((item: any) => { + // 如果是编辑模式且是当前正在编辑的产品,则保留 + if (currentProductId && item.id === currentProductId) { + return true; + } + // 过滤掉已添加的产品 + return !existingProducts.some((existing: any) => + existing.productId === item.id || existing.id === item.id + ); + }); + + return { + ...res, + rows: filteredRows + }; }, resultField: 'rows', labelField: 'plantName', diff --git a/apps/web-antd/src/views/property/greenPlantRentalManagement/leasePogramManagement/rentalPlan-modal.vue b/apps/web-antd/src/views/property/greenPlantRentalManagement/leasePogramManagement/rentalPlan-modal.vue index b6a9a9c2..55600aa4 100644 --- a/apps/web-antd/src/views/property/greenPlantRentalManagement/leasePogramManagement/rentalPlan-modal.vue +++ b/apps/web-antd/src/views/property/greenPlantRentalManagement/leasePogramManagement/rentalPlan-modal.vue @@ -29,10 +29,10 @@ const [BasicForm, formApi] = useVbenForm({ // 默认label宽度 px labelWidth: 120, // 通用配置项 会影响到所有表单项 - componentProps: computed(() => ({ + componentProps: { class: 'w-full', - disabled: isReadonly.value, - })), + disabled: isReadonly, + }, }, schema: modalSchema(), showDefaultActions: false, @@ -143,10 +143,13 @@ const detailColumns = [ title: '操作', key: 'action', fixed: 'right' as const, + width: 200, }, ]; function handleAddDetail() { - detailModalApi.setData({}); + detailModalApi.setData({ + existingProducts: detailTable.value + }); detailModalApi.open(); } //添加植物组合包产品 @@ -169,7 +172,13 @@ function handleViewDetail(record: any) { } // 编辑产品详情 function handleEditDetail(record: any, index: number) { - detailModalApi.setData({ ...record, index, readonly: false }); + detailModalApi.setData({ + ...record, + index, + readonly: false, + existingProducts: detailTable.value.filter((item: any, i: number) => i !== index), + currentProductId: record.productId || record.id + }); detailModalApi.open(); } //分类字典 diff --git a/apps/web-antd/src/views/property/greenPlantRentalManagement/reportStatistics/index.vue b/apps/web-antd/src/views/property/greenPlantRentalManagement/reportStatistics/index.vue index c2410298..1fcca068 100644 --- a/apps/web-antd/src/views/property/greenPlantRentalManagement/reportStatistics/index.vue +++ b/apps/web-antd/src/views/property/greenPlantRentalManagement/reportStatistics/index.vue @@ -5,7 +5,7 @@ import { onMounted, ref } from 'vue'; import { EchartsUI, useEcharts } from '@vben/plugins/echarts'; -import { Button,Radio } from 'ant-design-vue'; +import { Button,Radio,Spin } from 'ant-design-vue'; import type { RadioChangeEvent } from 'ant-design-vue'; import { statisticsByTime, @@ -44,42 +44,30 @@ const timeUnit = ref(1) const countOrderAndAmountDataAmount = ref(0); const countOrderAndAmountDataOrder = ref(0); const countAchievedRateData = ref(null); -const countCustomersData = ref(null); +const countCustomersData = ref(0); const xAxisData = ref([]); const seriesData = ref([]); -onMounted(async () => { - // 任务数 - const countOrderAndAmountData= await countOrderAndAmount(); +const loading = ref(false); +async function fetchOrderAndAmount() { + const countOrderAndAmountData = await countOrderAndAmount(); countOrderAndAmountDataAmount.value = countOrderAndAmountData.amount; countOrderAndAmountDataOrder.value = countOrderAndAmountData.num; - //活跃用户 +} + +async function fetchCustomers() { const countCustomersDataRes: any = await countCustomers(); countCustomersData.value = countCustomersDataRes.count; +} + +async function fetchAchievedRate() { const countAchievedRateDataRes: any = await countAchievedRate(); countAchievedRateData.value = countAchievedRateDataRes.rate; - // 查询订单数量趋势 +} + +async function fetchOrderTrend() { const res = await statisticsByTime({ timeUnit: timeUnit.value }); xAxisData.value = res?.time ?? []; seriesData.value = 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 - })); renderEcharts({ tooltip: { trigger: 'axis' }, xAxis: { @@ -87,31 +75,34 @@ onMounted(async () => { data: xAxisData.value, boundaryGap: false, }, - yAxis: { type: 'value', - axisLabel: { - formatter: (value) => `${value * 100}%` - }, - }, + yAxis: { type: 'value', axisLabel: { formatter: (value) => `${value * 100}%` } }, series: [ { name: '订单趋势', type: 'line', - data: seriesData.value ||[], + data: seriesData.value || [], smooth: true, }, ], }); +} + +async function fetchLeasePie() { + const data = await countByRentalType(); + const convertedData = data.map((item: { amount: number; type: string }) => ({ + value: item.amount, + name: item.type, + })); renderLeasePie({ title: { text: '租赁金额分布', left: 'center' }, tooltip: { trigger: 'item' }, legend: { orient: 'vertical', left: 'left' }, series: [ { - // name: '金额', type: 'pie', radius: '60%', center: ['50%', '50%'], - data:convertedData || [], + data: convertedData || [], emphasis: { itemStyle: { shadowBlur: 10, @@ -126,20 +117,19 @@ onMounted(async () => { }, ], }); +} + +async function fetchCustomerTypesBar() { + const countByCusTypeData: any = await countByCusType(); renderCustomerTypesBar({ title: { text: '客户类型分布' }, tooltip: { trigger: 'axis' }, xAxis: { type: 'category', - data: ['企业客户','个人客户','政府机构','商业地产','其他'], + data: ['企业客户', '个人客户', '政府机构', '商业地产', '其他'], boundaryGap: true, }, - yAxis: { type: 'value', - axisLabel: { - // formatter: (value: number) => `${parseInt(value.toString())}` - }, - // interval: 0, - }, + yAxis: { type: 'value' }, series: [ { name: '客户数', @@ -148,16 +138,21 @@ onMounted(async () => { }, ], }); +} + +async function fetchCustomerRenewalLine() { + const countRenewRateData: any = await countRenewRate(); renderCustomerRenewalLine({ title: { text: '客户续租率趋势' }, - tooltip: { trigger: 'axis', - formatter: function(params:any) { + tooltip: { + trigger: 'axis', + formatter: function (params: any) { let result = params[0].axisValue + '
'; - params.forEach((item:any) => { + params.forEach((item: any) => { result += item.marker + item.seriesName + ':' + item.data + '%
'; }); return result; - } + }, }, xAxis: { type: 'category', @@ -165,10 +160,9 @@ onMounted(async () => { boundaryGap: false, }, yAxis: { - type: 'value', - axisLabel: { - formatter: '{value}%', - }, }, + type: 'value', + axisLabel: { formatter: '{value}%' }, + }, series: [ { name: '续租率', @@ -178,78 +172,55 @@ onMounted(async () => { }, ], }); +} + +async function fetchConservationTasksBar() { + const countAchievedData: any = await countAchieved(); renderConservationTasksBar({ title: { text: '养护任务完成情况' }, tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' }, - formatter: function(params:any) { - // params 是一个数组,包含每个系列的当前项 - let result = params[0].axisValue + '
'; - params.forEach((item:any) => { - if (item.seriesName === '完成率') { - // 假设原始数据是 80,显示为 80% - result += item.marker + item.seriesName + ':' + item.data + '%
'; - } else { - result += item.marker + item.seriesName + ':' + item.data + '
'; - } - }); - return result; - } - }, - legend: { - data: ['计划任务数', '已完成数', '完成率'], + formatter: function (params: any) { + let result = params[0].axisValue + '
'; + params.forEach((item: any) => { + if (item.seriesName === '完成率') { + result += item.marker + item.seriesName + ':' + item.data + '%
'; + } else { + result += item.marker + item.seriesName + ':' + item.data + '
'; + } + }); + return result; + }, }, + legend: { data: ['计划任务数', '已完成数', '完成率'] }, xAxis: [ { type: 'category', - data: ['修剪整形','肥水管理','中耕除草','病虫害防治','越冬防寒'], + data: ['修剪整形', '肥水管理', '中耕除草', '病虫害防治', '越冬防寒'], }, ], yAxis: [ - { - type: 'value', - name: '任务数', - min: 0, - max: 200, - position: 'left', - }, - { - type: 'value', - name: '完成率', - min: 0, - max: 100, - position: 'right', - axisLabel: { - formatter: '{value}%', - }, - }, + { 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.total || [], - }, - { - name: '已完成数', - type: 'bar', - data: countAchievedData.finish || [], - }, - { - name: '完成率', - type: 'line', - yAxisIndex: 1, - data: countAchievedData.rate || [], - }, + { name: '计划任务数', type: 'bar', data: countAchievedData.total || [] }, + { name: '已完成数', type: 'bar', data: countAchievedData.finish || [] }, + { name: '完成率', type: 'line', yAxisIndex: 1, data: countAchievedData.rate || [] }, ], }); +} + +async function fetchMaintenanceQualityScoresPei() { + const countByCusScoreData: any = await countByCusScore(); + const countByCusScoreDataList = countByCusScoreData.map((item: { score: string; count: number }) => ({ + value: item.count, + name: item.score, + })); renderMaintenanceQualityScoresPei({ title: { text: '养护质量评分分布', left: 'center' }, - tooltip: { - trigger: 'item', - formatter: '{b} : {d}%', - }, + tooltip: { trigger: 'item', formatter: '{b} : {d}%' }, legend: { orient: 'horizontal', left: 'center', @@ -263,13 +234,27 @@ onMounted(async () => { radius: '60%', center: ['50%', '50%'], data: countByCusScoreDataList || [], - label: { - formatter: '{b} {d}%', - show: true, - }, + label: { formatter: '{b} {d}%', show: true }, }, ], }); +} + +onMounted(async () => { + loading.value = true; + try { + await fetchOrderAndAmount(); + await fetchCustomers(); + await fetchAchievedRate(); + await fetchOrderTrend(); + await fetchLeasePie(); + await fetchCustomerTypesBar(); + await fetchCustomerRenewalLine(); + await fetchConservationTasksBar(); + await fetchMaintenanceQualityScoresPei(); + } finally { + loading.value = false; + } }); // 切换视图模式 async function handleViewModeChange(e: RadioChangeEvent): Promise { @@ -313,101 +298,103 @@ function formatNumber(num: number | string) {