8/19 报表-租赁管理

This commit is contained in:
Zy
2026-08-19 16:56:18 +08:00
parent 144b9d9670
commit 3b0e3928eb
9 changed files with 1211 additions and 329 deletions

View File

@@ -0,0 +1,20 @@
import request from '@/utils/request';
import {
leaseManagementDetailChargeTypesResponse,
leaseManagementDetailRequest,
leaseManagementDetailResponse
} from '@/api/system/report/LeaseManagementDetails/type';
export const getLeaseManagementDetailList = (data?: leaseManagementDetailRequest): Promise<leaseManagementDetailResponse> => {
return request({
url: '/leaseManagementDetail/list',
method: 'POST',
data: data
});
};
export const getLeaseManagementDetailChargeTypesList = (): Promise<leaseManagementDetailChargeTypesResponse> => {
return request({
url: '/leaseManagementDetail/chargeTypes',
method: 'POST'
});
};

View File

@@ -0,0 +1,203 @@
export type leaseManagementDetailRequest = {
/**
* 面积
*/
areaSize?: string;
/**
* 收费模式多选
*/
chargeModes?: string[];
/**
* 收费标准多选
*/
chargeStandards?: string[];
/**
* 按收费类型筛选应收/已收/未收金额区间
*/
chargeTypeFilters?: ChargeTypeFilter[];
/**
* 租赁起始日-开始
*/
leaseStartDateBegin?: string;
/**
* 租赁起始日-结束
*/
leaseStartDateEnd?: string;
/**
* 承租方
*/
lessee?: string;
/**
* 当前页码
*/
pageNum: number;
/**
* 每页条数
*/
pageSize: number;
/**
* 手机号
*/
phone?: string;
/**
* 房号
*/
roomNumber?: string;
/**
* 小区多选
*/
villageIds?: number[];
leaseEndDateBegin?: string;
leaseEndDateEnd?: string;
};
export type ChargeTypeFilter = {
/**
* 收费类型名称
*/
chargeTypeName?: string;
/**
* 收费类型ID
*/
chargeTypeId?: string;
/**
* 应收上限
*/
receivableMax?: string;
/**
* 应收下限
*/
receivableMin?: string;
/**
* 已收上限
*/
receivedMax: string;
/**
* 已收下限
*/
receivedMin: string;
/**
* 未收上限
*/
uncollectedMax: string;
/**
* 未收下限
*/
uncollectedMin: string;
};
export type leaseManagementDetailResponse = {
/**
* 收费类型
*/
chargeItems: ChargeItem[];
code: number;
msg: string;
/**
* 当前页明细行数据
*/
rows: Row[];
/**
* 总记录数
*/
total: number;
};
export type ChargeItem = {
/**
* 收费类型ID
*/
chargeTypeId: string;
/**
* 收费类型名称
*/
chargeTypeName: string;
};
export type Row = {
/**
* 小区名称
*/
area: string;
/**
* 面积(㎡)
*/
areaSize: string;
/**
* 小区ID
*/
buildingId: string;
/**
* 收费模式
*/
chargeMode: string;
/**
* 收费标准
*/
chargeStandard: string;
/**
* 房屋ID
*/
houseId: string;
/**
* 签约记录ID
*/
id: string;
/**
* 租赁终止日
*/
leaseEndDate: string;
/**
* 租赁起始日
*/
leaseStartDate: string;
/**
* 承租方
*/
lessee: string;
/**
* 电话
*/
phone: string;
/**
* 各收费类型应收
*/
receivableAmounts: { [key: string]: any };
/**
* 各收费类型已收
*/
receivedAmounts: { [key: string]: any };
/**
* 房号
*/
roomNumber: string;
/**
* 各收费类型未收
*/
uncollectedAmounts: { [key: string]: any };
[property: string]: any;
};
export type leaseManagementDetailChargeTypesResponse = {
code: number;
data: Datum[];
msg: string;
};
export type Datum = {
chargeTypeId: string;
chargeTypeName: string;
};
// 0000000000000000
export interface queryParamsType extends leaseManagementDetailRequest {
chargeTypeFilters: chargeTypeFiltersType[];
}
export interface chargeTypeFiltersType extends ChargeTypeFilter {
receivable?: string;
uncollected?: string;
received?: string;
chargeTypeName?: string;
}

View File

@@ -305,3 +305,36 @@ h6 {
.el-menu--horizontal .el-sub-menu__title .svg-icon + span { .el-menu--horizontal .el-sub-menu__title .svg-icon + span {
margin-left: 3px; margin-left: 3px;
} }
.scrollbarBox {
// 美化滚动条
&::-webkit-scrollbar {
width: 5px;
height: 3px;
}
&::-webkit-scrollbar-track {
width: 6px;
background: rgba(#101F1C, 0.1);
-webkit-border-radius: 2em;
-moz-border-radius: 2em;
border-radius: 2em;
}
&::-webkit-scrollbar-thumb {
background-color: rgba(144,147,153,.3);
background-clip: padding-box;
min-height: 28px;
-webkit-border-radius: 2em;
-moz-border-radius: 2em;
border-radius: 2em;
transition: background-color .3s;
cursor: pointer;
}
&::-webkit-scrollbar-thumb:hover {
background-color: rgba(144,147,153,.2);
}
}

View File

@@ -49,7 +49,9 @@ const whiteVillageId = [
'/points/*', '/points/*',
'/outstandingFeeSummary/*', '/outstandingFeeSummary/*',
'/paidInExpenses/*' '/paidInExpenses/*',
// 租赁管理明细
'/leaseManagementDetail/*'
// '/building/list' // '/building/list'
]; ];

View File

@@ -2,136 +2,116 @@
<div class="p-2 h-full flex flex-col"> <div class="p-2 h-full flex flex-col">
<pageHeader></pageHeader> <pageHeader></pageHeader>
<el-card shadow="hover"> <el-card shadow="hover">
<el-form ref="queryFormRef" label-width="140" :model="queryParams" @submit.prevent="handleQuery" :inline="true"> <el-form ref="queryFormRef" label-width="140" :rules="formRules" :model="queryParams" @submit.prevent="handleQuery" :inline="true">
<div> <div>
<el-form-item label="区域(多选)" prop="villageId"> <el-form-item label="区域(多选)" prop="villageIds">
<el-select v-model="queryParams.villageIds" :multiple="true" placeholder="请选择区域">
<el-option v-for="item in villageList" :key="item.villageId" :label="item.villageName" :value="item.villageId" />
</el-select>
</el-form-item>
<el-form-item label="承租方" prop="lessee">
<el-input v-model.trim="queryParams.lessee" placeholder="请输入承租方" />
</el-form-item>
<el-form-item label="电话" prop="phone">
<el-input type="tel" v-model.trim="queryParams.phone" placeholder="请输入电话" />
</el-form-item>
<el-form-item label="房号" prop="roomNumber">
<el-input v-model.trim="queryParams.roomNumber" placeholder="请输入房号" />
</el-form-item>
</div>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]" style="overflow: auto; overflow-y: hidden">
<div>
<!-- 搜索条件 面积/收费模式/收费标准 -->
<div>
<el-form-item label="面积" prop="areaSize" style="margin-bottom: 0">
<el-input v-model.trim="queryParams.areaSize" placeholder="请输入面积区间" />
</el-form-item>
<el-form-item label="收费模式(多选)" prop="chargeModes" style="margin-bottom: 0">
<el-select v-model="queryParams.chargeModes" placeholder="请选择收费模式" :multiple="true">
<el-option v-for="item in com_house_rental_pricing_model" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="收费标准(多选)" prop="chargeStandards" style="margin-bottom: 0">
<el-select v-model="queryParams.chargeStandards" placeholder="请选择收费标准" :multiple="true">
<el-option v-for="item in com_house_rental_pricing" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</div>
<div class="line"></div>
<el-scrollbar>
<div style="width: fit-content">
<!-- 搜索条件 应收/已收/未收 -->
<div class="queryBox_over">
<el-form-item
:label="`${item.chargeTypeName}(应收)`"
:prop="`chargeTypeFilters[${index}].receivable`"
v-for="(item, index) in queryParams.chargeTypeFilters"
:key="index"
>
<el-input v-model.trim="queryParams.chargeTypeFilters[index].receivable" placeholder="区间搜索 示例100-2000" />
</el-form-item>
</div>
<div class="queryBox_over">
<el-form-item
:label="`${item.chargeTypeName}(已收)`"
:prop="`chargeTypeFilters[${index}].received`"
v-for="(item, index) in queryParams.chargeTypeFilters"
:key="index"
>
<el-input v-model.trim="queryParams.chargeTypeFilters[index].received" placeholder="区间搜索 示例100-2000" />
</el-form-item>
</div>
<div class="queryBox_over">
<el-form-item
v-for="(item, index) in [...queryParams.chargeTypeFilters]"
style="margin-bottom: 0"
:label="`${item.chargeTypeName}(未收)`"
:key="index"
:prop="`chargeTypeFilters[${index}].uncollected`"
>
<el-input v-model.trim="queryParams.chargeTypeFilters[index].uncollected" placeholder="区间搜索 示例100-2000" />
</el-form-item>
</div>
</div>
</el-scrollbar>
<div class="line"></div>
<!-- 搜索条件 租赁起始日期/租赁终止日期 -->
<div class="flex justify-between">
<div>
<el-form-item label="租赁起始日期" prop="leaseStartDate">
<el-date-picker <el-date-picker
v-model="queryTime" v-model="leaseStartDate"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
@clear="handleClear('leaseStartDate')"
@update:model-value="(val: string[]) => handleUpdateLeaseDate('leaseStartDate', val)"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="请选择"
/>
</el-form-item>
<el-form-item label="租赁终止日期" prop="leaseEndDate">
<el-date-picker
v-model="leaseEndDate"
type="daterange" type="daterange"
@update:model-value="handleQueryTime"
range-separator="" range-separator=""
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
format="YYYY-MM-DD" format="YYYY-MM-DD"
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
@clear="handleClear('leaseEndDate')"
@update:model-value="(val: string[]) => handleUpdateLeaseDate('leaseEndDate', val)"
placeholder="请选择"
/> />
</el-form-item> </el-form-item>
<el-form-item label="承租方" prop="chargeItemId">
<el-select @change="handleVillageChange" v-model="queryParams.villageId" placeholder="请选择">
<el-option v-for="item in villageList" :key="item.villageId" :label="item.villageName" :value="item.villageId" />
</el-select>
</el-form-item>
<el-form-item label="电话" prop="houseNo">
<el-input v-model.trim="queryParams.houseNo" placeholder="关键字查询" />
</el-form-item>
<el-form-item label="房号" prop="month">
<el-select v-model="queryParams.month" placeholder="关键字查询">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</div>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<div>
<div>
<el-form-item label="面积" prop="month">
<el-select v-model="queryParams.month" placeholder="请选择">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="收费模式(多选)" prop="month">
<el-select v-model="queryParams.month" placeholder="请选择">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="收费标准(多选)" prop="month">
<el-select v-model="queryParams.month" placeholder="请选择">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</div>
<div>
<el-form-item label="租金(应收)" prop="month">
<el-select v-model="queryParams.month" placeholder="请选择">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="押金(应收)" prop="month">
<el-select v-model="queryParams.month" placeholder="区间搜索 1000-10000">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="物业管理费(应收)" prop="month">
<el-select v-model="queryParams.month" placeholder="请选择">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="**(应收)" prop="month">
<el-select v-model="queryParams.month" placeholder="关键字查询">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</div>
<div>
<el-form-item label="租金(已收)" prop="month">
<el-select v-model="queryParams.month" placeholder="请选择">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="押金(已收)" prop="month">
<el-select v-model="queryParams.month" placeholder="区间搜索 1000-10000">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="物业管理费(已收)" prop="month">
<el-select v-model="queryParams.month" placeholder="请选择">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="**(已收)" prop="month">
<el-select v-model="queryParams.month" placeholder="关键字查询">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</div>
<div>
<el-form-item label="租金(未收)" prop="month">
<el-select v-model="queryParams.month" placeholder="请选择">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="押金(未收)" prop="month">
<el-select v-model="queryParams.month" placeholder="区间搜索 1000-10000">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="物业管理费(未收)" prop="month">
<el-select v-model="queryParams.month" placeholder="请选择">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="**(未收)" prop="month">
<el-select v-model="queryParams.month" placeholder="关键字查询">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</div>
<div class="flex justify-between">
<div>
<el-form-item label="租赁起始日期" prop="month">
<el-select v-model="queryParams.month" placeholder="请选择">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="租赁终止日期" prop="month">
<el-select v-model="queryParams.month" placeholder="请选择">
<el-option v-for="item in MonthList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</div> </div>
<div v-if="showSearch"> <div v-if="showSearch">
<el-form-item label=""> <el-form-item label="">
<el-button type="primary">查询</el-button> <el-button type="primary" @click="handleQuery">查询</el-button>
<el-button @click="resetQuery">重置</el-button> <el-button @click="resetQuery">重置</el-button>
<div class="arrowBox" style="margin-left: 10px" v-if="showSearch" @click="ChangeShowSearch(false)"> <div class="arrowBox" style="margin-left: 10px" v-if="showSearch" @click="ChangeShowSearch(false)">
<span>收起</span> <span>收起</span>
@@ -149,7 +129,7 @@
</transition> </transition>
<div v-if="!showSearch"> <div v-if="!showSearch">
<el-form-item label=""> <el-form-item label="">
<el-button type="primary">查询</el-button> <el-button type="primary" @click="handleQuery">查询</el-button>
<el-button @click="resetQuery">重置</el-button> <el-button @click="resetQuery">重置</el-button>
<div class="arrowBox" style="margin-left: 10px" v-if="showSearch" @click="ChangeShowSearch(false)"> <div class="arrowBox" style="margin-left: 10px" v-if="showSearch" @click="ChangeShowSearch(false)">
<span>收起</span> <span>收起</span>
@@ -175,26 +155,39 @@
</el-col> </el-col>
</el-row> </el-row>
</template> </template>
<el-table :span-method="mergeSpanMethod" v-loading="loading" border :data="ArrearsDetailList"> <el-table v-loading="loading" border :data="ArrearsDetailList">
<el-table-column label="序号" width="80" align="center" prop="serialNumber" />
<el-table-column label="区域" align="center" prop="area" /> <el-table-column label="区域" align="center" prop="area" />
<el-table-column label="房号" align="center" prop="houseNo" /> <el-table-column label="承租方" align="center" prop="lessee" />
<el-table-column label="客户" align="center" prop="customerName" /> <el-table-column label="电话" width="120" align="center" prop="phone" />
<el-table-column label="联系电话" align="center" prop="phone" /> <el-table-column label="房号" align="center" prop="roomNumber" />
<el-table-column label="欠费月数" width="80" align="center" prop="arrearsMonths" /> <el-table-column label="面积" width="80" align="center" prop="areaSize" />
<el-table-column label="应收账期" align="center" prop="billPeriod" /> <el-table-column label="收费模式" align="center" prop="chargeMode" />
<el-table-column label="欠费项目" align="center"> <el-table-column label="收费标准" align="center" prop="chargeStandard" />
<el-table-column label="租赁起始日" align="center" prop="leaseStartDate" />
<el-table-column label="租赁终止日" align="center" prop="leaseEndDate" />
<el-table-column label="应收项目" align="center">
<template #default> <template #default>
<template v-for="(item, index) in dynamicColumns" :key="index"> <el-table-column v-for="(item, index) in dynamicColumns" :key="index" :label="item.chargeTypeName" align="center" prop="totalAmount">
<el-table-column :label="item" align="center">
<template #default="scope"> <template #default="scope">
{{ scope.row.chargeItemAmounts?.[item] || 0 }} <span>{{ scope.row.receivableAmounts[item.chargeTypeId] }}</span>
</template> </template>
</el-table-column> </el-table-column>
</template> </template>
<el-table-column label="金额合计" align="center" prop="totalAmount"> </el-table-column>
<el-table-column label="已收项目" align="center">
<template #default>
<el-table-column v-for="(item, index) in dynamicColumns" :key="index" :label="item.chargeTypeName" align="center" prop="totalAmount">
<template #default="scope"> <template #default="scope">
<span style="color: red">{{ scope.row.totalAmount }}</span> <span>{{ scope.row.receivedAmounts[item.chargeTypeId] }}</span>
</template>
</el-table-column>
</template>
</el-table-column>
<el-table-column label="未收项目" align="center">
<template #default>
<el-table-column v-for="(item, index) in dynamicColumns" :key="index" :label="item.chargeTypeName" align="center" prop="totalAmount">
<template #default="scope">
<span>{{ scope.row.uncollectedAmounts[item.chargeTypeId] }}</span>
</template> </template>
</el-table-column> </el-table-column>
</template> </template>
@@ -204,109 +197,331 @@
</el-card> </el-card>
</div> </div>
</template> </template>
账龄
<script setup name="Arrears" lang="ts"> <script setup name="Arrears" lang="ts">
import pagination from '@/components/Pagination/index.vue'; import pagination from '@/components/Pagination/index.vue';
import pageHeader from '@/components/Pageheader/index.vue'; import pageHeader from '@/components/Pageheader/index.vue';
import { getArrearsDetails } from '@/api/system/report/arrears/arrears';
import { getCommunitylistAPI } from '@/api/system/community'; import { getCommunitylistAPI } from '@/api/system/community';
import { OwnerArrearsDetailVo } from '@/api/system/report/arrears/type';
import { validator } from '@/utils/Reg';
import { ColumnConfig, exportToExcel } from '@/utils/xlsx'; import { ColumnConfig, exportToExcel } from '@/utils/xlsx';
import { ArrowDownBold, ArrowUpBold } from '@element-plus/icons-vue'; import { ArrowDownBold, ArrowUpBold } from '@element-plus/icons-vue';
import { getLeaseManagementDetailChargeTypesList, getLeaseManagementDetailList } from '@/api/system/report/LeaseManagementDetails';
import { Datum, queryParamsType, Row } from '@/api/system/report/LeaseManagementDetails/type';
import { FormRules } from 'element-plus';
import { validator } from '@/utils/Reg';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const ArrearsDetailList = ref<OwnerArrearsDetailVo[]>([]); const { com_house_rental_pricing_model, com_house_rental_pricing } = toRefs<any>(
proxy?.useDict('com_house_rental_pricing_model', 'com_house_rental_pricing')
);
const ArrearsDetailList = ref<Row[]>([]);
const loading = ref(true); const loading = ref(true);
const showSearch = ref(true);
const total = ref(0); const total = ref(0);
const showSearch = ref(true);
function ChangeShowSearch(bol: boolean) { function ChangeShowSearch(bol: boolean) {
showSearch.value = bol; showSearch.value = bol;
} }
const queryFormRef = ref<ElFormInstance>();
const queryTime = ref([]); const leaseStartDate = ref([]);
function handleQueryTime(val: string[]) { const leaseEndDate = ref([]);
queryParams.value.startTime = val[0]; function handleUpdateLeaseDate(type: 'leaseStartDate' | 'leaseEndDate', val: string[]) {
queryParams.value.endTime = val[1]; if (type === 'leaseStartDate') {
queryParams.value.leaseStartDateBegin = val[0];
queryParams.value.leaseStartDateEnd = val[1];
} else if (type === 'leaseEndDate') {
queryParams.value.leaseEndDateBegin = val[0];
queryParams.value.leaseEndDateEnd = val[1];
}
} }
const data = reactive({ function handleClear(type: 'leaseStartDate' | 'leaseEndDate') {
if (type === 'leaseStartDate') {
leaseStartDate.value = [];
queryParams.value.leaseStartDateBegin = '';
queryParams.value.leaseStartDateEnd = '';
} else if (type === 'leaseEndDate') {
leaseEndDate.value = [];
queryParams.value.leaseEndDateBegin = '';
queryParams.value.leaseEndDateEnd = '';
}
}
const queryFormRef = ref<ElFormInstance>();
const data = reactive<{
queryParams: queryParamsType;
}>({
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
villageId: '-1', villageIds: [],
startTime: '', lessee: undefined,
endTime: '', phone: undefined,
houseNo: '', roomNumber: undefined,
month: '-1' areaSize: undefined,
}, chargeTypeFilters: [],
rule: [ chargeModes: [],
{ chargeStandards: [],
validator: (_: any, value: string, callback: (arg0?: Error) => any) => { leaseEndDateBegin: undefined,
if (value === '' || value === undefined || value === null) { leaseEndDateEnd: undefined
return callback();
} }
if (!validator(value, 'fullFilterReg')) {
return callback(new Error('请输入正确的房号'));
}
}
}
]
}); });
const { queryParams } = toRefs(data); const { queryParams } = toRefs(data);
// 动态生成表单验证规则
const formRules = computed<FormRules>(() => {
const dynamicRules: FormRules = {};
queryParams.value.chargeTypeFilters.forEach((_, index) => {
dynamicRules[`chargeTypeFilters[${index}].receivable`] = [
{
validator: (_: any, value: string, callback: (error?: any) => void) => {
if (!value || value === '') {
callback();
return;
}
const rangePattern = /^(-?\d+(\.\d{1,2})?)-(-?\d+(\.\d{1,2})?)?$/;
const singlePattern = /^-?\d+(\.\d{1,2})?$/;
const MAX_AMOUNT = 99999999;
if (rangePattern.test(value)) {
const parts = value.split('-');
const min = Number(parts[0]);
const max = parts[1] !== '' ? Number(parts[1]) : null; // 空字符串才是 null
// 校验下限
if (Math.abs(min) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
// 只有当 max 存在时才校验
if (max !== null) {
if (Math.abs(max) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
if (min > max) {
return callback(new Error('下限不能大于上限'));
}
}
return callback();
} else if (singlePattern.test(value)) {
const amount = Number(value);
if (Math.abs(amount) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
} else {
return callback();
}
} else {
callback(new Error('格式错误,示例: 0-1000、-1000、0-、1000'));
}
},
trigger: 'blur'
}
];
dynamicRules[`chargeTypeFilters[${index}].received`] = [
{
validator: (_: any, value: string, callback: (error?: any) => void) => {
if (!value || value === '') {
callback();
return;
}
const rangePattern = /^(-?\d+(\.\d{1,2})?)-(-?\d+(\.\d{1,2})?)?$/;
const singlePattern = /^-?\d+(\.\d{1,2})?$/;
const MAX_AMOUNT = 99999999;
if (rangePattern.test(value)) {
const parts = value.split('-');
const min = Number(parts[0]);
const max = parts[1] !== '' ? Number(parts[1]) : null; // 空字符串才是 null
// 校验下限
if (Math.abs(min) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
// 只有当 max 存在时才校验
if (max !== null) {
if (Math.abs(max) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
if (min > max) {
return callback(new Error('下限不能大于上限'));
}
}
return callback();
} else if (singlePattern.test(value)) {
const amount = Number(value);
if (Math.abs(amount) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
} else {
return callback();
}
} else {
callback(new Error('格式错误,示例: 0-1000、-1000、0-、1000'));
}
},
trigger: 'blur'
}
];
dynamicRules[`chargeTypeFilters[${index}].uncollected`] = [
{
validator: (_: any, value: string, callback: (error?: any) => void) => {
if (!value || value === '') {
callback();
return;
}
const rangePattern = /^(-?\d+(\.\d{1,2})?)-(-?\d+(\.\d{1,2})?)?$/;
const singlePattern = /^-?\d+(\.\d{1,2})?$/;
const MAX_AMOUNT = 99999999;
if (rangePattern.test(value)) {
const parts = value.split('-');
const min = Number(parts[0]);
const max = parts[1] !== '' ? Number(parts[1]) : null; // 空字符串才是 null
// 校验下限
if (Math.abs(min) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
// 只有当 max 存在时才校验
if (max !== null) {
if (Math.abs(max) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
if (min > max) {
return callback(new Error('下限不能大于上限'));
}
}
return callback();
} else if (singlePattern.test(value)) {
const amount = Number(value);
if (Math.abs(amount) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
} else {
return callback();
}
} else {
callback(new Error('格式错误,示例: 0-1000、-1000、0-、1000'));
}
},
trigger: 'blur'
}
];
});
// 面积
dynamicRules[`areaSize`] = [
{
validator: (_: any, value: string, callback: (error?: any) => void) => {
if (!value || value === '') {
callback();
return;
}
const rangePattern = /^(-?\d+(\.\d{1,2})?)-(-?\d+(\.\d{1,2})?)?$/;
const singlePattern = /^-?\d+(\.\d{1,2})?$/;
const MAX_AMOUNT = 99999999;
if (rangePattern.test(value)) {
const [min, max] = value.split('-').map(Number);
if (Math.abs(min) > MAX_AMOUNT || Math.abs(max) > MAX_AMOUNT) {
return callback(new Error(`面积不能超过 ${MAX_AMOUNT}`));
} else if (min > max) {
return callback(new Error('下限不能大于上限'));
} else {
return callback();
}
} else if (singlePattern.test(value)) {
const amount = Number(value);
if (Math.abs(amount) > MAX_AMOUNT) {
return callback(new Error(`面积不能超过 ${MAX_AMOUNT}`));
} else {
return callback();
}
} else {
callback(new Error('格式错误,示例: 0-100、-100、0-、100'));
}
},
trigger: 'blur'
}
];
// tel
dynamicRules[`tel`] = [
{
validator: (_: any, value: string, callback: (error?: any) => void) => {
if (!value || value === '') {
callback();
return;
}
validator(value, 'phone') ? callback() : callback(new Error('请输入正确的手机号'));
},
trigger: 'blur'
}
];
// 承租方
dynamicRules[`lessee`] = [
{
validator: (_: any, value: string, callback: (error?: any) => void) => {
if (!value || value === '') {
callback();
return;
}
validator(value, 'Character') ? callback() : callback(new Error('请输入正确的承租方姓名'));
},
trigger: 'blur'
}
];
// 房间号
dynamicRules[`roomNumber`] = [
{
validator: (_: any, value: string, callback: (error?: any) => void) => {
if (!value || value === '') {
callback();
return;
}
validator(value, 'Character') ? callback() : callback(new Error('请输入正确的房间号'));
},
trigger: 'blur'
}
];
return dynamicRules;
});
// 查询小区 // 查询小区
const villageList = ref([]); const villageList = ref([]);
function getAllVillage() { function getAllVillage() {
getCommunitylistAPI().then((res) => { getCommunitylistAPI().then((res) => {
villageList.value = [ villageList.value = [...res.rows];
{
villageId: '-1',
villageName: '全部区域'
},
...res.rows
];
}); });
} }
// 区域改变时,查询收费项目 // 动态列名列表
function handleVillageChange(val: string) { const dynamicColumns = ref<Datum[]>([]);
queryParams.value.villageId = val; // 在获取数据后更新动态列
queryParams.value.houseNo = '-1'; const updateDynamicColumns = () => {
} getLeaseManagementDetailChargeTypesList().then((res) => {
// 月份 dynamicColumns.value = res.data;
const MonthList = ref([]); queryParams.value.chargeTypeFilters = dynamicColumns.value.map((item) => {
function getMonthList() {
const arr = Array.from({ length: 12 }).fill((i) => {
return { return {
id: `${i + 1}`, chargeTypeId: item.chargeTypeId,
name: `${i + 1}` chargeTypeName: item.chargeTypeName,
receivable: undefined,
received: undefined,
uncollected: undefined,
receivableMin: undefined,
receivableMax: undefined,
receivedMin: undefined,
receivedMax: undefined,
uncollectedMin: undefined,
uncollectedMax: undefined
}; };
}); });
MonthList.value = [];
MonthList.value = [
{
id: '-1',
name: '全部月数'
},
...arr
];
}
// 动态列名列表▐╬ .3╬
const dynamicColumns = ref<string[]>([]);
// 在获取数据后更新动态列
const updateDynamicColumns = (list: any[]) => {
const columns = new Set<string>();
list.forEach((row) => {
if (row.chargeItemAmounts) {
Object.keys(row.chargeItemAmounts).forEach((key) => columns.add(key));
}
}); });
return Array.from(columns);
}; };
/** 查询电抄记录列表 */ /** 查询电抄记录列表 */
const getList = async () => { const getList = () => {
loading.value = true; loading.value = true;
const search = { ...queryParams.value }; const search = { ...queryParams.value };
// 移除空字符串字段 // 移除空字符串字段
@@ -314,14 +529,36 @@ const getList = async () => {
if (search[key] === '-1') { if (search[key] === '-1') {
search[key] = undefined; search[key] = undefined;
} }
if (key === 'chargeTypeFilters') {
search[key] = search[key]
.map((item) => {
// 1000-2000, -1000, 1000-, 1000
// 处理空字符串
if (!item.receivable && !item.received && !item.uncollected) {
return null;
}
const [receivableMin, receivableMax] = item.receivable ? item.receivable.split('-') : [];
const [receivedMin, receivedMax] = item.received ? item.received.split('-') : [];
const [uncollectedMin, uncollectedMax] = item.uncollected ? item.uncollected.split('-') : [];
return {
chargeTypeId: item.chargeTypeId,
receivableMin: receivableMin || undefined,
receivableMax: receivableMax || undefined,
receivedMin: receivedMin || undefined,
receivedMax: receivedMax || undefined,
uncollectedMin: uncollectedMin || undefined,
uncollectedMax: uncollectedMax || undefined
};
})
.filter(Boolean);
}
}); });
getArrearsDetails(search)
getLeaseManagementDetailList(search)
.then((res) => { .then((res) => {
ArrearsDetailList.value = res.rows; ArrearsDetailList.value = res.rows;
total.value = res.total; total.value = res.total;
dynamicColumns.value = updateDynamicColumns(res.rows);
ArrearsDetailList.value[ArrearsDetailList.value.length - 1].serialNumber = '合计';
}) })
.finally(() => { .finally(() => {
loading.value = false; loading.value = false;
@@ -330,44 +567,119 @@ const getList = async () => {
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
// 校验表单
queryFormRef.value?.validate((valid) => {
if (valid) {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
}
});
}; };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
leaseStartDate.value = [];
leaseEndDate.value = [];
queryParams.value = {
pageNum: 1,
pageSize: 10,
villageIds: [],
lessee: undefined,
phone: undefined,
roomNumber: undefined,
areaSize: undefined,
chargeTypeFilters: [],
chargeModes: [],
chargeStandards: [],
leaseEndDateBegin: undefined,
leaseEndDateEnd: undefined
};
queryParams.value.chargeTypeFilters = dynamicColumns.value.map((item) => {
return {
chargeTypeId: item.chargeTypeId,
chargeTypeName: item.chargeTypeName,
receivable: undefined,
received: undefined,
uncollected: undefined,
receivableMin: undefined,
receivableMax: undefined,
receivedMin: undefined,
receivedMax: undefined,
uncollectedMin: undefined,
uncollectedMax: undefined
};
});
handleQuery(); handleQuery();
}; };
/** 导出Excel */ /** 导出Excel */
const handleExport = () => { const handleExport = async () => {
if (ArrearsDetailList.value.length === 0) { if (ArrearsDetailList.value.length === 0) {
proxy?.$modal.msgWarning('没有可导出的数据'); proxy?.$modal.msgWarning('没有可导出的数据');
return; return;
} }
// 显示加载提示 // const oldSearch = JSON.parse(
// JSON.stringify({
// ...queryParams.value,
// pageNum: undefined,
// pageSize: undefined
// })
// );
loading.value = true; loading.value = true;
// let allData = [];
try {
// const res = await getLeaseManagementDetailList(oldSearch);
// allData = res.rows;
// 显示加载提示
// 使用 setTimeout 让出主线程避免阻塞UI // 使用 setTimeout 让出主线程避免阻塞UI
setTimeout(() => { setTimeout(() => {
try { try {
const columns: ColumnConfig[] = [ const columns: ColumnConfig[] = [
{ label: '序号', prop: 'serialNumber' },
{ label: '区域', prop: 'area' }, { label: '区域', prop: 'area' },
{ label: '房号', prop: 'houseNo' }, { label: '承租方', prop: 'lessee' },
{ label: '客户', prop: 'customerName' }, { label: '电话', prop: 'phone' },
{ label: '联系方式', prop: 'phone' }, { label: '房号', prop: 'roomNumber' },
{ label: '欠费月数', prop: 'arrearsMonths' }, { label: '面积', prop: 'areaSize' },
{ label: '应收账期', prop: 'billPeriod' }, { label: '收费模式', prop: 'chargeMode' },
{ label: '收费标准', prop: 'chargeStandard' },
{ label: '租赁起始日', prop: 'leaseStartDate' },
{ label: '租赁终止日', prop: 'leaseEndDate' },
{ {
label: '欠费项目', label: '应收项目',
children: [ children: [
...dynamicColumns.value.map((item) => { ...dynamicColumns.value.map((item) => {
return { label: item, prop: `chargeItemAmounts.${item}` }; return {
label: item.chargeTypeName,
prop: `receivableAmounts.${item.chargeTypeId}`
};
})
]
},
{
label: '已收项目',
children: [
...dynamicColumns.value.map((item) => {
return {
label: item.chargeTypeName,
prop: `receivedAmounts.${item.chargeTypeId}`
};
})
]
},
{
label: '未收项目',
children: [
...dynamicColumns.value.map((item) => {
return {
label: item.chargeTypeName,
prop: `uncollectedAmounts.${item.chargeTypeId}`
};
}) })
] ]
} }
]; ];
exportToExcel(ArrearsDetailList.value, columns, `应收欠费明细表_${new Date().getTime()}`, mergeSpanMethod); exportToExcel(ArrearsDetailList.value, columns, `租赁管理明细表_${new Date().getTime()}`);
} catch (error) { } catch (error) {
proxy?.$modal.msgError('导出失败,请重试'); proxy?.$modal.msgError('导出失败,请重试');
console.error('导出错误:', error); console.error('导出错误:', error);
@@ -375,33 +687,38 @@ const handleExport = () => {
loading.value = false; loading.value = false;
} }
}, 100); }, 100);
}; } catch (error) {
proxy?.$modal.msgError('导出失败,请重试');
const mergeSpanMethod = ({ columnIndex, rowIndex }) => { loading.value = false;
if (rowIndex === ArrearsDetailList.value.length - 1) {
const rowspan = 6;
if (columnIndex === 0) {
return { rowspan: 1, colspan: 1 };
} }
if (columnIndex === 1) {
return { rowspan: 1, colspan: rowspan };
}
if (columnIndex < rowspan + 1) {
return { rowspan: 0, colspan: 0 };
}
}
// 普通行不合并
return { rowspan: 1, colspan: 1 };
}; };
onMounted(() => { onMounted(() => {
updateDynamicColumns();
getList(); getList();
getMonthList();
getAllVillage(); getAllVillage();
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.line {
height: 1px;
background: #eee;
margin: 15px 0;
}
.queryBox_over {
display: flex;
flex-wrap: nowrap;
flex-direction: row;
overflow: auto;
overflow-y: hidden;
& > div {
display: flex;
flex-wrap: nowrap;
}
}
.el-table { .el-table {
height: calc(100% - 80px); height: calc(100% - 80px);
} }

View File

@@ -161,7 +161,9 @@ const exportData = () => {
proxy?.download( proxy?.download(
'/businessDailyReport/village/export', '/businessDailyReport/village/export',
{ {
...search ...search,
// pageNum: undefined,
// pageSize: undefined
}, },
`营业日报_按小区统计.xlsx` `营业日报_按小区统计.xlsx`
); );
@@ -169,7 +171,9 @@ const exportData = () => {
proxy?.download( proxy?.download(
'/businessDailyReport/chargeItem/export', '/businessDailyReport/chargeItem/export',
{ {
...search ...search,
// pageNum: undefined,
// pageSize: undefined
}, },
`营业日报_按收费项目统计.xlsx` `营业日报_按收费项目统计.xlsx`
); );

View File

@@ -4,7 +4,7 @@
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form ref="queryFormRef" label-width="80px" :model="queryParams" @submit.prevent="handleQuery" :inline="true"> <el-form ref="queryFormRef" label-width="80px" :model="queryParams" :rules="rules" @submit.prevent="handleQuery" :inline="true">
<div class="flex justify-between"> <div class="flex justify-between">
<div> <div>
<el-form-item label="楼栋(多选)" prop="buildingIds"> <el-form-item label="楼栋(多选)" prop="buildingIds">
@@ -85,11 +85,12 @@ import pagination from '@/components/Pagination/index.vue';
import pageHeader from '@/components/Pageheader/index.vue'; import pageHeader from '@/components/Pageheader/index.vue';
import { ColumnConfig, exportToExcel } from '@/utils/xlsx'; import { ColumnConfig, exportToExcel } from '@/utils/xlsx';
import { listHouseUseType } from '@/api/system/houseUse'; import { listHouseUseType } from '@/api/system/houseUse';
import { DailyFeeReportVo } from '@/api/system/report/dailyFeeReport/type';
import { getMonthFeeReportList } from '@/api/system/report/monthFeeReport'; import { getMonthFeeReportList } from '@/api/system/report/monthFeeReport';
import { Row } from '@/api/system/report/monthFeeReport/type';
import { FormRules } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const ArrearsDetailList = ref<DailyFeeReportVo[]>([]); const ArrearsDetailList = ref<Row[]>([]);
const loading = ref(true); const loading = ref(true);
const showSearch = ref(true); const showSearch = ref(true);
const total = ref(0); const total = ref(0);
@@ -109,10 +110,282 @@ const data = reactive({
yearRate: '', yearRate: '',
totalArrears: '', totalArrears: '',
totalPrepaid: '' totalPrepaid: ''
},
rules: {
monthReceivable: [
{
validator: (_: any, value: string, callback: (error?: any) => void) => {
if (!value || value === '') {
callback();
return;
}
const rangePattern = /^(-?\d+(\.\d{1,2})?)-(-?\d+(\.\d{1,2})?)?$/;
const singlePattern = /^-?\d+(\.\d{1,2})?$/;
const MAX_AMOUNT = 99999999;
if (rangePattern.test(value)) {
const parts = value.split('-');
const min = Number(parts[0]);
const max = parts[1] !== '' ? Number(parts[1]) : null; // 空字符串才是 null
// 校验下限
if (Math.abs(min) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
// 只有当 max 存在时才校验
if (max !== null) {
if (Math.abs(max) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
if (min > max) {
return callback(new Error('下限不能大于上限'));
}
}
return callback();
} else if (singlePattern.test(value)) {
const amount = Number(value);
if (Math.abs(amount) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
} else {
return callback();
}
} else {
callback(new Error('格式错误,示例: 0-1000、-1000、0-、1000'));
}
},
trigger: 'blur'
}
],
monthUnpaid: [
{
validator: (_: any, value: string, callback: (error?: any) => void) => {
if (!value || value === '') {
callback();
return;
}
const rangePattern = /^(-?\d+(\.\d{1,2})?)-(-?\d+(\.\d{1,2})?)?$/;
const singlePattern = /^-?\d+(\.\d{1,2})?$/;
const MAX_AMOUNT = 99999999;
if (rangePattern.test(value)) {
const parts = value.split('-');
const min = Number(parts[0]);
const max = parts[1] !== '' ? Number(parts[1]) : null; // 空字符串才是 null
// 校验下限
if (Math.abs(min) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
// 只有当 max 存在时才校验
if (max !== null) {
if (Math.abs(max) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
if (min > max) {
return callback(new Error('下限不能大于上限'));
}
}
return callback();
} else if (singlePattern.test(value)) {
const amount = Number(value);
if (Math.abs(amount) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
} else {
return callback();
}
} else {
callback(new Error('格式错误,示例: 0-1000、-1000、0-、1000'));
}
},
trigger: 'blur'
}
],
yearReceivable: [
{
validator: (_: any, value: string, callback: (error?: any) => void) => {
if (!value || value === '') {
callback();
return;
}
const rangePattern = /^(-?\d+(\.\d{1,2})?)-(-?\d+(\.\d{1,2})?)?$/;
const singlePattern = /^-?\d+(\.\d{1,2})?$/;
const MAX_AMOUNT = 99999999;
if (rangePattern.test(value)) {
const parts = value.split('-');
const min = Number(parts[0]);
const max = parts[1] !== '' ? Number(parts[1]) : null; // 空字符串才是 null
// 校验下限
if (Math.abs(min) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
// 只有当 max 存在时才校验
if (max !== null) {
if (Math.abs(max) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
if (min > max) {
return callback(new Error('下限不能大于上限'));
}
}
return callback();
} else if (singlePattern.test(value)) {
const amount = Number(value);
if (Math.abs(amount) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
} else {
return callback();
}
} else {
callback(new Error('格式错误,示例: 0-1000、-1000、0-、1000'));
}
},
trigger: 'blur'
}
],
yearUnpaid: [
{
validator: (_: any, value: string, callback: (error?: any) => void) => {
if (!value || value === '') {
callback();
return;
}
const rangePattern = /^(-?\d+(\.\d{1,2})?)-(-?\d+(\.\d{1,2})?)?$/;
const singlePattern = /^-?\d+(\.\d{1,2})?$/;
const MAX_AMOUNT = 99999999;
if (rangePattern.test(value)) {
const parts = value.split('-');
const min = Number(parts[0]);
const max = parts[1] !== '' ? Number(parts[1]) : null; // 空字符串才是 null
// 校验下限
if (Math.abs(min) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
// 只有当 max 存在时才校验
if (max !== null) {
if (Math.abs(max) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
if (min > max) {
return callback(new Error('下限不能大于上限'));
}
}
return callback();
} else if (singlePattern.test(value)) {
const amount = Number(value);
if (Math.abs(amount) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
} else {
return callback();
}
} else {
callback(new Error('格式错误,示例: 0-1000、-1000、0-、1000'));
}
},
trigger: 'blur'
}
],
totalArrears: [
{
validator: (_: any, value: string, callback: (error?: any) => void) => {
if (!value || value === '') {
callback();
return;
}
const rangePattern = /^(-?\d+(\.\d{1,2})?)-(-?\d+(\.\d{1,2})?)?$/;
const singlePattern = /^-?\d+(\.\d{1,2})?$/;
const MAX_AMOUNT = 99999999;
if (rangePattern.test(value)) {
const parts = value.split('-');
const min = Number(parts[0]);
const max = parts[1] !== '' ? Number(parts[1]) : null; // 空字符串才是 null
// 校验下限
if (Math.abs(min) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
// 只有当 max 存在时才校验
if (max !== null) {
if (Math.abs(max) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
if (min > max) {
return callback(new Error('下限不能大于上限'));
}
}
return callback();
} else if (singlePattern.test(value)) {
const amount = Number(value);
if (Math.abs(amount) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
} else {
return callback();
}
} else {
callback(new Error('格式错误,示例: 0-1000、-1000、0-、1000'));
}
},
trigger: 'blur'
}
],
totalPrepaid: [
{
validator: (_: any, value: string, callback: (error?: any) => void) => {
if (!value || value === '') {
callback();
return;
}
const rangePattern = /^(-?\d+(\.\d{1,2})?)-(-?\d+(\.\d{1,2})?)?$/;
const singlePattern = /^-?\d+(\.\d{1,2})?$/;
const MAX_AMOUNT = 99999999;
if (rangePattern.test(value)) {
const parts = value.split('-');
const min = Number(parts[0]);
const max = parts[1] !== '' ? Number(parts[1]) : null; // 空字符串才是 null
// 校验下限
if (Math.abs(min) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
// 只有当 max 存在时才校验
if (max !== null) {
if (Math.abs(max) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
}
if (min > max) {
return callback(new Error('下限不能大于上限'));
}
}
return callback();
} else if (singlePattern.test(value)) {
const amount = Number(value);
if (Math.abs(amount) > MAX_AMOUNT) {
return callback(new Error(`金额不能超过 ${MAX_AMOUNT}`));
} else {
return callback();
}
} else {
callback(new Error('格式错误,示例: 0-1000、-1000、0-、1000'));
}
},
trigger: 'blur'
}
]
} }
}); });
// 动态生成表单验证规则╘╬
const { queryParams } = toRefs(data); const { queryParams, rules } = toRefs(data);
// 查询房屋类型列表 // 查询房屋类型列表
const allHouseUseList = ref([]); const allHouseUseList = ref([]);

View File

@@ -161,14 +161,26 @@ const resetQuery = () => {
handleQuery(); handleQuery();
}; };
/** 导出Excel */ /** 导出Excel */
const handleExport = () => { const handleExport = async () => {
if (ArrearsDetailList.value.length === 0) { if (ArrearsDetailList.value.length === 0) {
proxy?.$modal.msgWarning('没有可导出的数据'); proxy?.$modal.msgWarning('没有可导出的数据');
return; return;
} }
const search = { ...queryParams.value };
// 移除空字符串字段
Object.keys(search).forEach((key) => {
if (search[key] === '-1') {
search[key] = undefined;
}
});
// 显示加载提示 // 显示加载提示
loading.value = true; loading.value = true;
try {
// const res = await getPrepaidBalanceList(search);
// ArrearsDetailList.value = [...res.rows];
// 使用 setTimeout 让出主线程避免阻塞UI // 使用 setTimeout 让出主线程避免阻塞UI
setTimeout(() => { setTimeout(() => {
try { try {
@@ -200,6 +212,11 @@ const handleExport = () => {
loading.value = false; loading.value = false;
} }
}, 100); }, 100);
} catch (error) {
loading.value = false;
proxy?.$modal.msgError('导出失败,请重试');
return;
}
}; };
onMounted(() => { onMounted(() => {
getList(); getList();

View File

@@ -18,7 +18,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="客户" prop="customerName"> <el-form-item label="客户" prop="customerName">
<el-input v-model.trim="queryParams.customerName"></el-input> <el-input v-model.trim="queryParams.customerName" placeholder="请输入客户姓名"></el-input>
</el-form-item> </el-form-item>
</div> </div>
<div> <div>
@@ -153,14 +153,23 @@ const resetQuery = () => {
handleQuery(); handleQuery();
}; };
/** 导出Excel */ /** 导出Excel */
const handleExport = () => { const handleExport = async () => {
if (ArrearsDetailList.value.length === 0) { if (ArrearsDetailList.value.length === 0) {
proxy?.$modal.msgWarning('没有可导出的数据'); proxy?.$modal.msgWarning('没有可导出的数据');
return; return;
} }
const search = { ...queryParams.value, pageNum: undefined, pageSize: undefined };
// 显示加载提示 // 移除空字符串字段
Object.keys(search).forEach((key) => {
if (search[key] === '-1') {
search[key] = undefined;
}
});
loading.value = true; loading.value = true;
try {
// const res = await getPrepaidBalanceDetailList(search);
// ArrearsDetailList.value = [...res.rows];
// 显示加载提示
// 使用 setTimeout 让出主线程避免阻塞UI // 使用 setTimeout 让出主线程避免阻塞UI
setTimeout(() => { setTimeout(() => {
try { try {
@@ -196,6 +205,10 @@ const handleExport = () => {
loading.value = false; loading.value = false;
} }
}, 100); }, 100);
} catch (error) {
loading.value = false;
proxy?.$modal.msgError('导出失败,请重试');
}
}; };
onMounted(() => { onMounted(() => {
getList(); getList();