Files
sunProduct/src/components/Holder/house.vue
2026-08-31 08:09:28 +08:00

386 lines
12 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>
<slot name="default" />
<el-dialog v-model="dialog.dialogVisible" :title="dialog.dialogTitle" append-to-body width="1400">
<nav class="flex flex-items-center mb-8 justify-between">
<!-- <div class="flex flex-items-center">
<div style="width: 70px; text-align: right; padding-right: 10px">房屋类型</div>
<div class="flex-1">
<el-select @change="handleChangeHouse" style="width: 140px" v-model="queryParams.houseUseTypeId">
<el-option v-for="(item, index) in houseUseTypeList" :key="index" :label="item.name" :value="item.id"></el-option>
</el-select>
</div>
</div> -->
<div class="flex flex-items-center">
<div class="flex flex-items-center">
<div style="width: 50px; text-align: right; padding-right: 10px">楼栋</div>
<div class="flex-1"></div>
<el-select v-model="queryParams.buildingId" clearable style="width: 120px" @change="handleChangeBuilding">
<el-option v-for="(item, index) in buildingList" :key="index" :label="item.buildingName" :value="item.id"></el-option>
</el-select>
</div>
<div class="flex flex-items-center mr-20px">
<div style="width: 50px; text-align: right; padding-right: 10px">单元</div>
<div class="flex-1">
<el-select v-model="queryParams.unitNoId" clearable style="width: 120px" @change="handleChangeUnit">
<el-option v-for="(item, index) in unitList" :key="index" :label="item.unitNoName" :value="item.unitNoId"></el-option>
</el-select>
</div>
</div>
<div class="flex flex-items-center mr-20px">
<div style="width: 80px; text-align: right; padding-right: 10px">房屋类型</div>
<div class="flex-1">
<el-select v-model="queryParams.houseUseTypeId" clearable style="width: 120px" @change="handleHouseUseChange">
<el-option v-for="(item, index) in houseUseTypeList" :key="index" :label="item.name" :value="item.id"></el-option>
</el-select>
</div>
</div>
<div class="flex flex-items-center mr-20px">
<div style="width: 60px; text-align: right; padding-right: 10px">房屋号</div>
<div class="flex-1">
<el-input v-model="queryParams.houseNo" clearable placeholder="请输入房屋号" style="width: 120px" @clear="handleClear"></el-input>
</div>
</div>
<div class="flex flex-items-center mr-20px">
<div style="width: 80px; text-align: right; padding-right: 10px">业主名称</div>
<div class="flex-1">
<el-input v-model="queryParams.residentName" clearable placeholder="请输入业主名称" style="width: 120px" @clear="handleClear"></el-input>
</div>
</div>
</div>
<div>
<el-button type="primary" @click="getList">查询</el-button>
<el-button @click="resetQuery">重置</el-button>
</div>
</nav>
<el-table
ref="multipleTableRef"
v-loading="loading"
:data="tableData"
border
row-key="houseId"
style="height: 500px"
@selection-change="handleSelectionChange"
>
<el-table-column align="center" type="selection" width="55" />
<el-table-column align="center" label="序号" type="index" width="55" />
<el-table-column align="center" label="小区" prop="villageName" />
<el-table-column align="center" label="楼栋" prop="buildingName" />
<el-table-column align="center" label="单元" prop="unitNoName" />
<el-table-column align="center" label="房间" prop="houseNo" />
<el-table-column align="center" label="业主">
<template #default="scope">
<span>{{ scope.row.residentName ? scope.row.residentName : '---' }}</span>
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer flex flex-items-center flex-justify-between">
<pagination
v-show="total > 0"
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNum"
:total="total"
style="margin-top: 0"
@pagination="getList"
/>
<div>
<el-button @click="conBack">退出</el-button>
<el-button type="primary" @click="confirm"> 确定 </el-button>
</div>
</div>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { getBuildinglistAPI, getHouselistAPI } from '@/api/system/house';
import { listHouseUseType_add_house } from '@/api/system/houseUse';
import { BillHouse, listBillHouse } from '@/api/system/SdbBill';
import { ref, nextTick, toRefs } from 'vue';
import pagination from '@/components/Pagination/index.vue';
const props = defineProps({
returnvalue: {
type: String,
default: 'houseId'
},
titleName: {
type: String,
default: '选择房屋'
},
/** 是否多选 false 单选 */
isMultiple: {
type: Boolean,
default: false
}
});
const { titleName, returnvalue } = toRefs(props);
// ✅ 手动设置默认值(替代 withDefaults)
const emit = defineEmits<{
change: [value: string | number | (string | number)[] | BillHouse | BillHouse[]]; // 支持返回数组
}>();
const dialog = ref({
dialogVisible: false,
dialogTitle: titleName.value
});
const queryParams = ref({
pageNum: 1,
pageSize: 10,
houseNo: '',
buildingId: null,
unitNoId: null,
houseUseTypeId: null,
residentName: ''
});
const multipleTableRef = ref();
const tableData = ref<BillHouse[]>([]);
const total = ref(0);
const loading = ref(false);
// 存储选中项
const multipleSelection = ref<BillHouse[]>([]);
const isMultiple = computed(() => props.isMultiple ?? false);
// 优化 handleUserSelection,避免重复查找
const handleUserSelection = () => {
isFetching.value = true;
if (!multipleTableRef.value || !tableData.value.length) {
isFetching.value = false;
return;
}
// 1. 获取当前页所有行的 houseId 集合,用于快速判断
// const currentPageIds = new Set(tableData.value.map((item) => item.houseId));
const rowsToSelect = tableData.value.filter((row) => {
return multipleSelection.value.some((selected) => selected.houseId === row.houseId);
});
multipleTableRef.value.clearSelection();
if (rowsToSelect.length > 0) {
nextTick(() => {
rowsToSelect.forEach((row) => {
multipleTableRef.value?.toggleRowSelection(row, true);
});
isFetching.value = false;
});
} else {
isFetching.value = false;
}
};
// ====================== 方法 ======================
const houseUseTypeList = ref([]);
const isFetching = ref(false);
// 获取列表
function getList() {
isFetching.value = true; // 🔒 加锁:禁止 selection-change 更新数据
loading.value = true;
listBillHouse(queryParams.value)
.then((res) => {
tableData.value = res.rows;
total.value = res.total;
handleUserSelection();
})
.catch((error) => {
console.error('获取列表失败:', error);
})
.finally(() => {
loading.value = false;
isFetching.value = false;
});
}
/** 楼栋列表 */
const buildingList = ref([]);
/** 房屋列表 */
const unitList = ref([]);
/** 获取楼栋 列表 */
function handleChangeHouse() {
getBuildinglistAPI().then((res) => {
buildingList.value = res.data;
});
}
function handleChangeHouseUseType() {
listHouseUseType_add_house().then((res) => {
houseUseTypeList.value = res.data;
});
}
function handleClear() {
queryParams.value.residentName = '';
getList();
}
function handleHouseUseChange() {
getList();
}
/** 单元列表 */
function handleChangeBuilding(val: string) {
queryParams.value.unitNoId = null;
unitList.value = [];
if (queryParams.value.buildingId) {
getHouselistAPI(val).then((res) => {
unitList.value = res.data.units;
getList();
});
}
getList();
}
function handleChangeUnit() {
getList();
}
// ==============================================
// ✅ 新增:处理 selection-change(多选专用)
// ==============================================
function handleSelectionChange(selection: BillHouse[]) {
// 🛑 如果正在获取数据/恢复状态,直接返回,不要更新 multipleSelection
if (isFetching.value) return;
if (isMultiple.value) {
// 1. 获取当前表格中所有选中行的 ID
const currentSelectedIds = new Set(selection.map((item) => item.houseId));
const otherPageSelections = multipleSelection.value.filter((item) => {
// 判断 item 是否属于当前页
const isCurrentPageItem = tableData.value.some((row) => row.houseId === item.houseId);
if (isCurrentPageItem) {
// 如果是当前页的数据,检查是否在当前 selection 中
return currentSelectedIds.has(item.houseId);
} else {
// 如果不是当前页的数据,保留(假设是之前页选的)
return true;
}
});
// 去重合并
const finalSelection = [...otherPageSelections, ...selection];
// 再次去重以防万一
const uniqueMap = new Map();
finalSelection.forEach((item) => uniqueMap.set(item.houseId, item));
multipleSelection.value = Array.from(uniqueMap.values());
console.log(multipleSelection.value.map((item) => item.houseId));
} else {
// ✅ 补充:单选模式下,selection-change 触发时,直接取最新的一项
// 这样配合 handleRowClick 的 clearSelection + toggle 就能实现完美单选
if (selection.length > 0) {
const lastSelected = selection[selection.length - 1];
multipleSelection.value = [lastSelected];
nextTick(() => {
if (selection.length > 1) {
multipleTableRef.value?.clearSelection();
multipleTableRef.value?.toggleRowSelection(lastSelected, true);
}
});
} else {
multipleSelection.value = [];
}
}
}
// 重置查询
function resetQuery() {
queryParams.value = {
pageNum: 1,
pageSize: 10,
houseUseTypeId: null,
buildingId: null,
unitNoId: null,
houseNo: '',
residentName: ''
};
getList();
}
// 退出
function conBack() {
dialog.value.dialogVisible = false;
resetQuery();
}
// ==============================================
// ✅ 核心修改:根据 returnValue 返回对应的值
// ==============================================
function confirm() {
dialog.value.dialogVisible = false;
if (isMultiple.value) {
// 多选
if (returnvalue.value === '*') {
// ✅ 返回整个对象数组
emit('change', multipleSelection.value);
} else {
// 返回指定字段数组
emit(
'change',
multipleSelection.value.map((item) => item[returnvalue.value as keyof BillHouse])
);
}
} else {
// 单选
if (returnvalue.value === '*') {
// ✅ 返回整个对象
emit('change', multipleSelection.value[0]);
} else {
// 返回指定字段
emit('change', multipleSelection.value[0][returnvalue.value as keyof BillHouse]);
}
}
multipleSelection.value = [];
multipleTableRef.value.clearSelection();
}
defineExpose({
/**
* @description 打开弹窗
* @param { string[] } showIdArray 显示的房屋ID数组
* @param { object } params 额外查询参数对象
* */
open: (showIdArray: string[] = [], params: object = {}) => {
multipleSelection.value = [];
if (Object.keys(params).length > 0) {
queryParams.value = Object.assign({}, queryParams.value, params);
}
// 显示的房屋ID数组
if (showIdArray && showIdArray.length > 0) {
multipleSelection.value = showIdArray.map(
(id) =>
({
houseId: id
}) as BillHouse
);
}
// 刷新列表
getList();
// 刷新房屋用途类型列表
handleChangeHouseUseType();
// 刷新房屋用途类型列表
handleChangeHouse();
// 显示弹窗
dialog.value.dialogVisible = true;
}
});
</script>
<style lang="scss" scoped>
.residentBox {
width: 385px;
height: 35px;
line-height: 35px;
border: 1px solid #ccc;
border-radius: 5px;
padding: 0 10px;
.defaultBox {
display: flex;
align-items: center;
justify-content: space-between;
}
cursor: pointer;
&:hover {
background-color: #f4f8ff;
}
}
</style>