feat: 绿植租赁方案添加产品数量

This commit is contained in:
fyy 2025-07-07 14:38:01 +08:00
parent f5d7d70cc2
commit 9ab72435c7
5 changed files with 184 additions and 98 deletions

View File

@ -80,7 +80,7 @@ export interface RentalPlanForm extends BaseEntity {
/** /**
* 绿 * 绿
*/ */
productList:any[]; productList?:any[];
} }

View File

@ -1,64 +1,6 @@
import type { statisticsByTimeQuery } from './model'; import type { statisticsByTimeQuery } from './model';
import type { ID, IDS } from '#/api/common';
import type { PageResult } from '#/api/common';
import { commonExport } from '#/api/helper';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
// /**
// * 查询绿植租赁-租赁方案列表
// * @param params
// * @returns 绿植租赁-租赁方案列表
// */
// export function rentalPlanList(params?: RentalPlanQuery) {
// return requestClient.get<PageResult<RentalPlanVO>>('/property/rentalPlan/list', { params });
// }
// /**
// * 导出绿植租赁-租赁方案列表
// * @param params
// * @returns 绿植租赁-租赁方案列表
// */
// export function rentalPlanExport(params?: RentalPlanQuery) {
// return commonExport('/property/rentalPlan/export', params ?? {});
// }
// /**
// * 查询绿植租赁-租赁方案详情
// * @param id id
// * @returns 绿植租赁-租赁方案详情
// */
// export function rentalPlanInfo(id: ID) {
// return requestClient.get<RentalPlanVO>(`/property/rentalPlan/${id}`);
// }
// /**
// * 新增绿植租赁-租赁方案
// * @param data
// * @returns void
// */
// export function rentalPlanAdd(data: RentalPlanForm) {
// return requestClient.postWithMsg<void>('/property/rentalPlan', data);
// }
// /**
// * 更新绿植租赁-租赁方案
// * @param data
// * @returns void
// */
// export function rentalPlanUpdate(data: RentalPlanForm) {
// return requestClient.putWithMsg<void>('/property/rentalPlan', data);
// }
// /**
// * 删除绿植租赁-租赁方案
// * @param id id
// * @returns void
// */
// export function rentalPlanRemove(id: ID | IDS) {
// return requestClient.deleteWithMsg<void>(`/property/rentalPlan/${id}`);
// }
/** /**
* *
@ -68,3 +10,61 @@ import { requestClient } from '#/api/request';
export function statisticsByTime(params:statisticsByTimeQuery) { export function statisticsByTime(params:statisticsByTimeQuery) {
return requestClient.get<any>('/property/rentalOrder/statisticsByTime', { params }); return requestClient.get<any>('/property/rentalOrder/statisticsByTime', { params });
} }
/**
*
* @param timeUnit
* @returns void
*/
export function countByCusType() {
return requestClient.get<any>('/property/rentalOrder/countByCusType');
}
/**
*
* @param
* @returns void
*/
export function countRenewRate() {
return requestClient.get<any>('/property/rentalOrder/countRenewRate');
}
/**
*
* @param
* @returns void
*/
export function countByRentalType() {
return requestClient.get<any>('/property/rentalOrder/countByRentalType');
}
/**
*
* @param
* @returns void
*/
export function countOrderAndAmount( ) {
return requestClient.get<any>('/property/rentalOrder/countOrderAndAmount', );
}
/**
* 绿
* @param
* @returns void
*/
export function countAchievedRate() {
return requestClient.get<any>('/property/orderMaintain/countAchievedRate');
}
/**
*
* @param
* @returns void
*/
export function countAchieved() {
return requestClient.get<any>('/property/orderMaintain/countAchieved');
}
/**
*
* @param
* @returns void
*/
export function countByCusScore() {
return requestClient.get<any>('/property/orderMaintain/countByCusScore');
}

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue'; import { computed, ref, watch } from 'vue';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { cloneDeep } from '@vben/utils'; import { cloneDeep } from '@vben/utils';
@ -25,6 +25,10 @@ const title = computed(() => {
// //
let plantListData: any[] = []; let plantListData: any[] = [];
const detailIndex = ref<number>();//index, const detailIndex = ref<number>();//index,
//
const productNumMax = ref<any>(0);
const detailSchema = [ const detailSchema = [
{ {
label: '产品名称', label: '产品名称',
@ -33,7 +37,7 @@ const detailSchema = [
componentProps: { componentProps: {
disabled: isView, disabled: isView,
api: async () => { api: async () => {
const res = await plantsProductList({inventory:0}); const res = await plantsProductList({state:1,inventory:0});
plantListData = res.rows || []; plantListData = res.rows || [];
return res; return res;
}, },
@ -47,6 +51,7 @@ const detailSchema = [
// //
await formApi.setValues({ await formApi.setValues({
plantCode: selectedService.plantCode, plantCode: selectedService.plantCode,
inventory: selectedService.inventory,
plantType: selectedService.plantType, plantType: selectedService.plantType,
imgPath: selectedService.imgPath, imgPath: selectedService.imgPath,
specification: selectedService.specification, specification: selectedService.specification,
@ -54,11 +59,32 @@ const detailSchema = [
state: selectedService.state, state: selectedService.state,
remark: selectedService.remark, remark: selectedService.remark,
}); });
//
productNumMax.value = selectedService.inventory || 0;
} }
}, },
}, },
rules: 'required', rules: 'required',
}, },
{
label: '产品库存',
fieldName: 'inventory',
component: 'Input',
componentProps: {
disabled: true,
},
rules: 'required',
},
{
label: '添加数量',
fieldName: 'productNum',
component: 'InputNumber',
componentProps: {
min: 1,
max: productNumMax,
},
rules: 'required',
},
{ {
label: '产品编号', label: '产品编号',
fieldName: 'plantCode', fieldName: 'plantCode',
@ -138,6 +164,22 @@ const [BasicForm, formApi] = useVbenForm({
showDefaultActions: false, showDefaultActions: false,
}); });
// inventoryproductNumMax
watch(async () => {
const values = await formApi.getValues();
return values.inventory;
}, async (newInventory) => {
if (newInventory) {
productNumMax.value = newInventory;
// productNum
const values = await formApi.getValues();
const currentProductNum = values.productNum;
if (currentProductNum && currentProductNum > newInventory) {
formApi.setFieldValue('productNum', newInventory);
}
}
}, { immediate: true });
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff( const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
{ {
initializedGetter: defaultFormValueGetter(formApi), initializedGetter: defaultFormValueGetter(formApi),
@ -185,7 +227,7 @@ async function handleConfirm() {
const selectedService = plantListData.find(item => item.id === data.plantName); const selectedService = plantListData.find(item => item.id === data.plantName);
if (selectedService) { if (selectedService) {
data.plantName = selectedService.plantName; data.plantName = selectedService.plantName;
data.id = selectedService.id data.productId = selectedService.id
} }
if (isUpdate.value) { if (isUpdate.value) {
data.index = detailIndex.value; data.index = detailIndex.value;

View File

@ -88,7 +88,8 @@ async function handleConfirm() {
} }
// getValuesreadonly // getValuesreadonly
const data = cloneDeep(await formApi.getValues()); const data = cloneDeep(await formApi.getValues());
data.productIds = detailTable.value.map((item:any) => item.id); // data.productIds = detailTable.value.map((item:any) => item.productId);
data.productList = detailTable.value;
await (isUpdate.value ? rentalPlanUpdate({...data,id:editId.value}) : rentalPlanAdd(data)); await (isUpdate.value ? rentalPlanUpdate({...data,id:editId.value}) : rentalPlanAdd(data));
resetInitialized(); resetInitialized();
emit('reload'); emit('reload');
@ -114,6 +115,7 @@ const detailColumns = [
{ title: '产品编号', dataIndex: 'plantCode', key: 'plantCode' }, { title: '产品编号', dataIndex: 'plantCode', key: 'plantCode' },
{ title: '产品名称', dataIndex: 'plantName', key: 'plantName' }, { title: '产品名称', dataIndex: 'plantName', key: 'plantName' },
{ title: '产品分类', dataIndex: 'plantType', key: 'plantType' }, { title: '产品分类', dataIndex: 'plantType', key: 'plantType' },
{ title: '产品数量', dataIndex: 'productNum', key: 'productNum' },
{ {
title: '图片', title: '图片',
dataIndex: 'imgPath', dataIndex: 'imgPath',
@ -131,7 +133,7 @@ const detailColumns = [
dataIndex: 'state', dataIndex: 'state',
key: 'state', key: 'state',
customRender: ({ value }: { value: number }) => customRender: ({ value }: { value: number }) =>
value === 1 ? '上架' : '下架', value == 1 ? '上架' : '下架',
}, },
{ {
title: '操作', title: '操作',

View File

@ -7,7 +7,15 @@ import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import { Button } from 'ant-design-vue'; import { Button } from 'ant-design-vue';
import { statisticsByTime } from '#/api/property/reportStatistics'; import { statisticsByTime,
countByRentalType,
countByCusType,
countRenewRate,
countByCusScore,
countOrderAndAmount,
countAchievedRate,
countAchieved
} from '#/api/property/reportStatistics';
const orderLineRef = ref<EchartsUIType>(); const orderLineRef = ref<EchartsUIType>();
const leasePieRef = ref<EchartsUIType>(); const leasePieRef = ref<EchartsUIType>();
@ -31,11 +39,39 @@ const { renderEcharts: renderMaintenanceQualityScoresPei } = useEcharts(
); );
const timeUnit = ref<number>(1) const timeUnit = ref<number>(1)
const countOrderAndAmountDataAmount = ref<number>(0);
const countOrderAndAmountDataOrder = ref<number>(0);
const countAchievedRateData = ref<any>(null);
onMounted(async () => { onMounted(async () => {
//
const countOrderAndAmountData= await countOrderAndAmount();
countOrderAndAmountDataAmount.value = countOrderAndAmountData.amount;
countOrderAndAmountDataOrder.value = countOrderAndAmountData.num;
const countAchievedRateDataRes: any = await countAchievedRate();
countAchievedRateData.value = countAchievedRateDataRes.rate;
// //
const res = await statisticsByTime({ timeUnit: timeUnit.value }); const res = await statisticsByTime({ timeUnit: timeUnit.value });
const xAxisData = res?.time ?? []; const xAxisData = res?.time ?? [];
const seriesData = res?.counts ?? []; const seriesData = res?.counts ?? [];
//
const data = await countByRentalType();//amount: 1, type: ""
// valuename
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({ renderEcharts({
tooltip: { trigger: 'axis' }, tooltip: { trigger: 'axis' },
xAxis: { xAxis: {
@ -48,7 +84,7 @@ onMounted(async () => {
{ {
name: '订单数', name: '订单数',
type: 'line', type: 'line',
data: seriesData, data: seriesData ||[],
smooth: true, smooth: true,
}, },
], ],
@ -59,17 +95,11 @@ onMounted(async () => {
legend: { orient: 'vertical', left: 'left' }, legend: { orient: 'vertical', left: 'left' },
series: [ series: [
{ {
name: '金额', // name: '',
type: 'pie', type: 'pie',
radius: '60%', radius: '60%',
center: ['50%', '50%'], center: ['50%', '50%'],
data: [ data:convertedData || [],
{ value: 1048, name: '办公楼' },
{ value: 735, name: '商场' },
{ value: 580, name: '酒店' },
{ value: 484, name: '医院' },
{ value: 300, name: '其他' },
],
emphasis: { emphasis: {
itemStyle: { itemStyle: {
shadowBlur: 10, shadowBlur: 10,
@ -89,15 +119,15 @@ onMounted(async () => {
tooltip: { trigger: 'axis' }, tooltip: { trigger: 'axis' },
xAxis: { xAxis: {
type: 'category', type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月'], data: countByCusTypeData.type || [],
boundaryGap: false, boundaryGap: true,
}, },
yAxis: { type: 'value' }, yAxis: { type: 'value' },
series: [ series: [
{ {
name: '订单数', name: '订单数',
type: 'bar', type: 'bar',
data: [120, 132, 101, 134, 90, 230], data: countByCusTypeData.counts || [],
}, },
], ],
}); });
@ -106,7 +136,7 @@ onMounted(async () => {
tooltip: { trigger: 'axis' }, tooltip: { trigger: 'axis' },
xAxis: { xAxis: {
type: 'category', type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月'], data: countRenewRateData.month || [],
boundaryGap: false, boundaryGap: false,
}, },
yAxis: { type: 'value' }, yAxis: { type: 'value' },
@ -114,7 +144,7 @@ onMounted(async () => {
{ {
name: '订单数', name: '订单数',
type: 'line', type: 'line',
data: [120, 132, 101, 134, 90, 230], data: countRenewRateData.rate || [],
smooth: true, smooth: true,
}, },
], ],
@ -131,7 +161,7 @@ onMounted(async () => {
xAxis: [ xAxis: [
{ {
type: 'category', type: 'category',
data: ['朝阳区', '海淀区', '西城区', '东城区'], data: countAchievedData.type || [],
}, },
], ],
yAxis: [ yAxis: [
@ -157,18 +187,18 @@ onMounted(async () => {
{ {
name: '计划任务数', name: '计划任务数',
type: 'bar', type: 'bar',
data: [156, 140, 130, 120], data: countAchievedData.toral || [],
}, },
{ {
name: '已完成数', name: '已完成数',
type: 'bar', type: 'bar',
data: [152, 135, 125, 110], data: countAchievedData.finish || [],
}, },
{ {
name: '完成率', name: '完成率',
type: 'line', type: 'line',
yAxisIndex: 1, yAxisIndex: 1,
data: [97.4, 96.4, 96.2, 91.7], data: countAchievedData.rate || [],
}, },
], ],
}); });
@ -190,13 +220,7 @@ onMounted(async () => {
type: 'pie', type: 'pie',
radius: '60%', radius: '60%',
center: ['50%', '50%'], center: ['50%', '50%'],
data: [ data: countByCusScoreDataList || [],
{ value: 12.43, name: '一星' },
{ value: 12.26, name: '两星' },
{ value: 16.87, name: '三星' },
{ value: 25.75, name: '四星' },
{ value: 32.68, name: '五星' },
],
label: { label: {
formatter: '{b} {d}%', formatter: '{b} {d}%',
show: true, show: true,
@ -211,9 +235,23 @@ const nodeOptions = [
{ label: '月', value: 3 }, { label: '月', value: 3 },
]; ];
function handleAssociationChange(e: any) { function handleAssociationChange(e: any) {
console.log(e);
timeUnit.value = e.target.value; 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;
}
</script> </script>
<template> <template>
<div class="main"> <div class="main">
@ -232,12 +270,17 @@ function handleAssociationChange(e: any) {
<div class="row"> <div class="row">
<div class="box"> <div class="box">
<div class="title">总订单数</div> <div class="title">总订单数</div>
<div class="number">13,132</div> <div class="number">{{formatNumber(countOrderAndAmountDataAmount)}}</div>
<div class="percent">8.9%</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>
</div> </div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div> </div>
<div class="row-first"> <div class="row-first">
<div class="item1"> <div class="item1">
@ -251,7 +294,6 @@ function handleAssociationChange(e: any) {
option-type="button" option-type="button"
@change="handleAssociationChange" @change="handleAssociationChange"
/> />
</div> </div>
</div> </div>
<EchartsUI <EchartsUI
@ -378,7 +420,7 @@ function handleAssociationChange(e: any) {
.row-third { .row-third {
height: 400px; height: 400px;
margin-bottom: 50px; margin-bottom: 50px;
} }
.row-fouth { .row-fouth {