Files
estate/src/views/system/report/dailyFeeReport.vue
2026-09-10 16:12:22 +08:00

616 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="p-2 h-full flex flex-col">
<pageHeader></pageHeader>
<div class="flex-1 overflow-auto">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form
ref="queryFormRef"
:inline="true"
:model="queryParams"
:rules="formRules"
label-position="right"
label-width="170px"
@submit.prevent="handleQuery"
>
<div>
<el-form-item label="日期" prop="payTime">
<el-date-picker
v-model="payTime"
end-placeholder="结束日期"
format="YYYY-MM-DD"
range-separator="至"
start-placeholder="开始日期"
type="daterange"
value-format="YYYY-MM-DD"
@clear="handleClear()"
@update:model-value="(val: string[]) => handleUpdateLeaseDate(val)"
/>
</el-form-item>
<el-form-item label="户主" prop="residentName">
<el-input v-model.trim="queryParams.residentName" placeholder="请输入户主姓名"></el-input>
</el-form-item>
<el-form-item label="房号" prop="houseName">
<el-input v-model.trim="queryParams.houseName" placeholder="请输入房号"></el-input>
</el-form-item>
<el-form-item label="">
<div v-show="!isShow" class="flex justify-end">
<el-button type="primary" @click="handleQuery">查询</el-button>
<el-button @click="resetQuery">重置</el-button>
<div class="flex items-center cursor-pointer hover:color-blue" style="margin-left: 10px; font-size: 13px" @click="toggleShow">
<span>展开</span>
<el-icon style="margin-left: 5px"><ArrowDownBold /></el-icon>
</div>
</div>
</el-form-item>
</div>
<div v-show="isShow">
<el-form-item label="现金" prop="cashAmount">
<el-input v-model.trim="queryParams.cashAmount" placeholder="区间搜索 1000-10000"></el-input>
</el-form-item>
<el-form-item label="支付宝" prop="alipayAmount">
<el-input v-model.trim="queryParams.alipayAmount" placeholder="区间搜索 1000-10000"></el-input>
</el-form-item>
<el-form-item label="宜当付" prop="yiDangFuAmount">
<el-input v-model.trim="queryParams.yiDangFuAmount" placeholder="区间搜索 1000-10000"></el-input>
</el-form-item>
<el-form-item label="银联" prop="unionPayAmount">
<el-input v-model.trim="queryParams.unionPayAmount" placeholder="区间搜索 1000-10000"></el-input>
</el-form-item>
<el-form-item label="转账" prop="transferAmount">
<el-input v-model.trim="queryParams.transferAmount" placeholder="区间搜索 1000-10000"></el-input>
</el-form-item>
<el-form-item label="微信" prop="wechatAmount">
<el-input v-model.trim="queryParams.wechatAmount" placeholder="区间搜索 1000-10000"></el-input>
</el-form-item>
<el-form-item label="押金抵顶" prop="depositAmount">
<el-input v-model.trim="queryParams.depositAmount" placeholder="区间搜索 1000-10000"></el-input>
</el-form-item>
<el-form-item label="房产抵顶" prop="propertyOffsetAmount">
<el-input v-model.trim="queryParams.propertyOffsetAmount" placeholder="区间搜索 1000-10000"></el-input>
</el-form-item>
<el-form-item label="减免优惠" prop="discountAmount">
<el-input v-model.trim="queryParams.discountAmount" placeholder="区间搜索 1000-10000"></el-input>
</el-form-item>
</div>
<div v-show="isShow">
<el-form-item label="水费" prop="waterFee">
<el-input v-model.trim="queryParams.waterFee" placeholder="区间搜索 1000-10000"></el-input>
</el-form-item>
<el-form-item label="电费" prop="electricFee">
<el-input v-model.trim="queryParams.electricFee" placeholder="区间搜索 1000-10000"></el-input>
</el-form-item>
<el-form-item label="押金" prop="deposit">
<el-input v-model.trim="queryParams.deposit" placeholder="区间搜索 1000-10000"></el-input>
</el-form-item>
</div>
<div v-show="isShow" class="flex justify-between flex-wrap">
<div v-for="(item, index) in queryParamsList" :key="index">
<el-form-item :label="`${item.chargeTypeName} (金额)`">
<el-input v-model.trim="queryParams.chargeTypeFilters[index].amount" placeholder="区间搜索 1000-10000"></el-input>
</el-form-item>
<el-form-item :label="`${item.chargeTypeName} (开始时间)`">
<el-date-picker
v-model="queryParams.chargeTypeFilters[index].startDate"
clearable
end-placeholder="结束时间"
format="YYYY-MM-DD HH:mm:ss"
range-separator="到"
start-placeholder="开始时间"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
@clear="
() => {
queryParams.chargeTypeFilters[index].startDate = '';
queryParams.chargeTypeFilters[index].startDateBegin = '';
queryParams.chargeTypeFilters[index].startDateEnd = '';
}
"
@update:model-value="
(val: string[]) => {
if (val.length === 0) {
return;
}
queryParams.chargeTypeFilters[index].startDateBegin = val[0];
queryParams.chargeTypeFilters[index].startDateEnd = val[1];
}
"
/>
</el-form-item>
<el-form-item :label="`${item.chargeTypeName} (结束时间)`">
<el-date-picker
v-model="queryParams.chargeTypeFilters[index].endDate"
clearable
end-placeholder="结束时间"
format="YYYY-MM-DD HH:mm:ss"
range-separator="到"
start-placeholder="开始时间"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
@clear="
() => {
queryParams.chargeTypeFilters[index].endDate = '';
queryParams.chargeTypeFilters[index].endDateBegin = '';
queryParams.chargeTypeFilters[index].endDateEnd = '';
}
"
@update:model-value="
(val: string[]) => {
if (val.length === 0) {
return;
}
queryParams.chargeTypeFilters[index].endDateBegin = val[0];
queryParams.chargeTypeFilters[index].endDateEnd = val[1];
}
"
/>
</el-form-item>
</div>
</div>
<!-- <div v-show="isShow">-->
<!-- <el-form-item label="经办人" prop="handler">-->
<!-- <el-input v-model.trim="queryParams.handler" placeholder="请输入经办人姓名"></el-input>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="备注" prop="remark">-->
<!-- <el-input v-model.trim="queryParams.remark" placeholder="请输入备注"></el-input>-->
<!-- </el-form-item>-->
<!-- </div>-->
<div v-show="isShow" class="flex justify-end">
<el-button type="primary" @click="handleQuery">查询</el-button>
<el-button @click="resetQuery">重置</el-button>
<div class="flex items-center cursor-pointer hover:color-blue" style="margin-left: 10px; font-size: 13px" @click="toggleShow">
<span>收起</span>
<el-icon style="margin-left: 5px"><ArrowUpBold /></el-icon>
</div>
</div>
</el-form>
</el-card>
</div>
</transition>
<el-card class="el-table-box" shadow="never">
<template #header>
<div class="flex items-center justify-between">
<span>收费日报</span>
<div>
<el-button type="primary" @click="handleExport">导出</el-button>
<el-button type="primary" @click="handleExportAll">导出所有</el-button>
</div>
</div>
</template>
<el-table v-loading="loading" :data="ArrearsDetailList" :row-class-name="handleRowClass" border>
<el-table-column align="center" fixed="left" label="日期" prop="payTime" width="100" />
<el-table-column align="center" fixed="left" label="房屋信息">
<template #default>
<el-table-column align="center" label="户主" prop="residentName" width="100"></el-table-column>
<el-table-column align="center" label="房号" prop="houseName" width="100"></el-table-column>
</template>
</el-table-column>
<el-table-column align="center" label="支付方式">
<template #default>
<el-table-column align="center" label="现金类" prop="cashAmount">
<template #default>
<el-table-column align="center" label="现金" prop="cashAmount"></el-table-column>
<el-table-column align="center" label="支付宝" prop="alipayAmount"></el-table-column>
<el-table-column align="center" label="宜当付" prop="yiDangFuAmount"></el-table-column>
<el-table-column align="center" label="银联" prop="unionPayAmount"></el-table-column>
<el-table-column align="center" label="转账" prop="transferAmount"></el-table-column>
<el-table-column align="center" label="微信" prop="wechatAmount"></el-table-column>
</template>
</el-table-column>
<el-table-column align="center" label="顶账类" prop="deposit">
<template #default>
<el-table-column align="center" label="押金抵顶" prop="depositAmount"></el-table-column>
<el-table-column align="center" label="房产抵顶" prop="propertyOffsetAmount"></el-table-column>
<el-table-column align="center" label="减免优惠" prop="discountAmount"></el-table-column>
</template>
</el-table-column>
</template>
</el-table-column>
<el-table-column align="center" label="水费" prop="waterFee" />
<el-table-column align="center" label="电费" prop="electricFee" />
<el-table-column align="center" label="押金" prop="deposit" />
<el-table-column align="center" label="收费项目" prop="chargeTypeAmounts">
<template #default>
<el-table-column v-for="(item, index) in rowChargeTypesList" :key="index" :label="item" align="center">
<template #default="scope">
<el-table-column v-if="item !== '卡表' && item !== '其他'" align="center" label="金额">
<template #default="scope">
{{ scope.row.chargeTypeAmounts[item].amount }}
</template>
</el-table-column>
<el-table-column v-if="item !== '卡表' && item !== '其他'" align="center" label="开始时间" width="100">
<template #default="scope">
{{ scope.row.chargeTypeAmounts[item].startDate }}
</template>
</el-table-column>
<el-table-column v-if="item !== '卡表' && item !== '其他'" align="center" label="结束时间" width="100">
<template #default="scope">
{{ scope.row.chargeTypeAmounts[item].endDate }}
</template>
</el-table-column>
<span v-if="item == '卡表' || item == '其他'">{{ scope.row.chargeTypeAmounts[item].amount }}</span>
</template>
</el-table-column>
</template>
</el-table-column>
<!-- <el-table-column label="经办人" align="center" prop="handler" />-->
<!-- <el-table-column show-overflow-tooltip width="250" label="备注" align="center" prop="remark" />-->
</el-table>
<pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
</el-card>
</div>
</div>
</template>
<script lang="ts" setup>
import pagination from '@/components/Pagination/index.vue';
import pageHeader from '@/components/Pageheader/index.vue';
import { getCommunitylistAPI } from '@/api/system/community';
import { ColumnConfig, exportToExcel } from '@/utils/xlsx';
import { listHouseUseType } from '@/api/system/houseUse';
import { getDailyFeeReportDynamicList, getDailyFeeReportList } from '@/api/system/report/dailyFeeReport';
import { Datum } from '@/api/system/report/dailyFeeReport/type';
import { FormRules } from 'element-plus';
import { ArrowDownBold, ArrowUpBold } from '@element-plus/icons-vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const ArrearsDetailList = ref<Datum[]>([]);
const loading = ref(true);
const showSearch = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
// 查询小区
const villageList = ref([]);
function getAllVillage() {
getCommunitylistAPI().then((res) => {
villageList.value = [...res.rows];
});
}
// 房屋类型
const houseUseList = ref([]);
function getHouseUseList() {
listHouseUseType().then((res) => {
houseUseList.value = [...res.rows];
});
}
const isShow = ref(false);
function toggleShow() {
isShow.value = !isShow.value;
}
const queryParams = ref({
pageNum: 1,
pageSize: 10,
startDate: '',
endDate: '',
residentName: '',
houseName: '',
cashAmount: '',
alipayAmount: '',
yiDangFuAmount: '',
unionPayAmount: '',
transferAmount: '',
wechatAmount: '',
depositAmount: '',
propertyOffsetAmount: '',
discountAmount: '',
waterFee: '',
electricFee: '',
deposit: '',
handler: '',
remark: '',
chargeTypeFilters: []
});
const staticSearchKey = [
'cashAmount',
'alipayAmount',
'yiDangFuAmount',
'unionPayAmount',
'transferAmount',
'wechatAmount',
'depositAmount',
'propertyOffsetAmount',
'discountAmount',
'waterFee',
'electricFee',
'deposit'
];
// 动态生成表单验证规则
const formRules = computed<FormRules>(() => {
const dynamicRules: FormRules = {};
staticSearchKey.forEach((item) => {
dynamicRules[item] = [
{
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'
}
];
});
return dynamicRules;
});
const payTime = ref([]);
function handleClear() {
payTime.value = [];
queryParams.value.startDate = '';
queryParams.value.endDate = '';
}
function handleUpdateLeaseDate(val: string[]) {
queryParams.value.startDate = val[0];
queryParams.value.endDate = val[1];
}
// 参数列表
const queryParamsList = ref([]);
function getQueryParams() {
getDailyFeeReportDynamicList().then((res) => {
queryParamsList.value = res.data;
handleAddQuery();
});
}
function handleAddQuery() {
const list = queryParamsList.value.map((item) => {
return {
'chargeTypeName': item.chargeTypeName,
'chargeTypeId': item.chargeTypeId,
'amount': '',
startDate: '',
endDate: '',
'startDateBegin': '',
'startDateEnd': '',
'endDateBegin': '',
'endDateEnd': ''
};
});
queryParamsList.value = list;
queryParams.value.chargeTypeFilters = list;
}
const rowChargeTypesList = ref([]);
/** 查询电抄记录列表 */
const getList = async () => {
loading.value = true;
const search = { ...queryParams.value };
// 移除空字符串字段
Object.keys(search).forEach((key) => {
if (search[key] === '-1') {
search[key] = undefined;
}
});
getDailyFeeReportList(search)
.then((res) => {
ArrearsDetailList.value = [...(res.rows ?? [])];
if (res.rows) {
rowChargeTypesList.value = Object.keys(res.rows[0].chargeTypeAmounts) ?? [];
}
total.value = res.total;
})
.finally(() => {
loading.value = false;
});
};
function handleRowClass({ row }) {
if (row.residentName === '当日合计') {
// 示例1:收费项目等于物业费的行加深色
return 'bg-dark';
}
return '';
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value = {
endDate: undefined,
startDate: undefined,
pageNum: 1,
pageSize: 10,
residentName: '',
houseName: '',
cashAmount: '',
alipayAmount: '',
yiDangFuAmount: '',
unionPayAmount: '',
transferAmount: '',
wechatAmount: '',
depositAmount: '',
propertyOffsetAmount: '',
discountAmount: '',
waterFee: '',
electricFee: '',
deposit: '',
handler: '',
remark: '',
chargeTypeFilters: []
};
handleAddQuery();
handleClear();
handleQuery();
};
/** 导出Excel */
const handleExport = () => {
if (ArrearsDetailList.value.length === 0) {
proxy?.$modal.msgWarning('没有可导出的数据');
return;
}
handleExportDate(ArrearsDetailList.value);
};
/** 导出所有Excel */
const handleExportAll = () => {
let data = [];
const search = {
...queryParams.value
};
// 移除空字符串字段
Object.keys(search).forEach((key) => {
if (search[key] === '-1') {
search[key] = undefined;
}
});
getDailyFeeReportList(search)
.then((res) => {
data = [...(res.rows ?? [])];
handleExportDate(data);
})
.finally(() => {
loading.value = false;
});
};
const handleExportDate = (data: Datum[], title: string = '收费日报') => {
// 显示加载提示
loading.value = true;
// 使用 setTimeout 让出主线程,避免阻塞UI
setTimeout(() => {
try {
const columns: ColumnConfig[] = [
{ label: '日期', prop: 'payTime' },
{
label: '房屋信息',
prop: 'houseName',
children: [
{ label: '户主', prop: 'residentName' },
{ label: '户号', prop: 'houseName' }
]
},
{
label: '支付方式',
prop: 'paymentMethod',
children: [
{
label: '现金类',
children: [
{ label: '现金', prop: 'cashAmount' },
{ label: '支付宝', prop: 'alipayAmount' },
{ label: '宜当付', prop: 'yiDangFuAmount' },
{ label: '银联', prop: 'unionPayAmount' },
{ label: '转账', prop: 'transferAmount' },
{ label: '微信商户', prop: 'wechatAmount' }
]
},
{
label: '顶账类',
children: [
{ label: '押金抵顶', prop: 'depositAmount' },
{ label: '房产抵顶', prop: 'propertyOffsetAmount' },
{ label: '减免优惠', prop: 'discountAmount' }
]
}
]
},
{ label: '水费', prop: 'waterFee' },
{ label: '电费', prop: 'electricFee' },
{ label: '押金', prop: 'deposit' },
{
label: '收费项目',
prop: 'chargeTypeAmounts',
children: [
...rowChargeTypesList.value.map((item) => {
return {
label: item,
children: ['其他', '卡表'].includes(item)
? []
: [
{ label: '金额', prop: `chargeTypeAmounts.${item}.amount` },
{ label: '开始时间', prop: `chargeTypeAmounts.${item}.startDate` },
{ label: '截止时间', prop: `chargeTypeAmounts.${item}.endDate` }
]
};
})
]
}
// { label: '经办人', prop: 'inspectorName' },
// { label: '备注', prop: 'remark' }
];
exportToExcel(data, columns, `${title}_${new Date().getTime()}`, undefined, ({ row }) => {
if (row.residentName === '当日合计') {
return {
fill: {
fgColor: { rgb: 'E2EFDA' },
patternType: 'solid'
}
};
}
});
} catch (error) {
proxy?.$modal.msgError('导出失败,请重试');
console.error('导出错误:', error);
} finally {
loading.value = false;
}
}, 100);
};
onMounted(() => {
getList();
getHouseUseList();
getAllVillage();
getQueryParams();
});
</script>
<style lang="scss" scoped>
.el-table-box {
height: 800px;
}
.el-table {
height: calc(100% - 80px);
}
/* 深度穿透修改表格行背景 */
:deep(.bg-dark td) {
background-color: #f5f7fa !important;
}
</style>