物业主开发

This commit is contained in:
Zy
2026-05-08 11:30:28 +08:00
parent a8bb4e66ad
commit 1b738c0f4d
118 changed files with 2891 additions and 979 deletions

View File

@@ -0,0 +1,273 @@
<template>
<slot name="default" />
<el-dialog append-to-body v-model="dialog.dialogVisible" :title="dialog.dialogTitle" width="1050">
<el-table @row-click="handleRowClick" ref="multipleTableRef" :data="tableData" border @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" align="center" width="55" type="index" />
<el-table-column label="业主" align="center">
<template #default="scope">
<span>{{ scope.row.residentName ? scope.row.residentName : '---' }}</span>
</template>
</el-table-column>
<el-table-column label="性别" align="center">
<template #default="scope">
<dict-tag :options="sys_user_sex" :value="scope.row.gender"></dict-tag>
</template>
</el-table-column>
<el-table-column label="手机号" align="center" prop="phone" />
<el-table-column label="房屋" align="center" prop="house_name" />
</el-table>
<template #footer>
<div class="dialog-footer flex flex-items-center flex-justify-between">
<pagination
style="margin-top: 0"
layout="total, prev, pager, next, jumper"
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getlist"
/>
<div>
<el-button @click="conback">退出</el-button>
<el-button type="primary" @click="confirm"> 确定 </el-button>
</div>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { getBuildinglistAPI, getBuildingOnly, getHouselistAPI } from '@/api/system/house';
import { BillHouse, listBillHouse, queryResidentList_holder } from '@/api/system/SdbBill';
import { ref, watch, nextTick, getCurrentInstance, ComponentInternalInstance, toRefs } from 'vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_user_sex } = toRefs<any>(proxy?.useDict('sys_user_sex'));
const { com_build_use } = toRefs<any>(proxy?.useDict('com_build_use'));
const props = defineProps({
userid: {
type: String || Array<string>,
default: ''
},
returnvalue: {
type: String,
default: 'houseId'
},
titleName: {
type: String,
default: '选择房屋'
},
/** 是否多选 false 单选 */
isMultiple: {
type: Boolean,
default: false
}
});
const { userid, 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,
buildingUse: null,
buildingId: null,
unitNo: null
});
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 = () => {
if (!userid.value || !tableData.value.length) return;
multipleSelection.value = [];
let arr = [];
if (userid.value && typeof userid.value === 'string' && userid.value.trim() !== '') {
arr = userid.value.split(',');
} else if (Array.isArray(userid.value)) {
arr = [...userid.value];
}
if (!arr.length) return;
arr.forEach((item) => {
const target = tableData.value.find((row) => row.userId == item);
target && multipleSelection.value.push(target);
});
nextTick(() => {
if (multipleTableRef.value) {
multipleTableRef.value.clearSelection();
multipleSelection.value.forEach((item) => {
multipleTableRef.value.toggleRowSelection(item, true);
});
}
});
};
// ====================== 方法 ======================
// 获取列表
function getlist() {
loading.value = true;
queryResidentList_holder(queryParams.value)
.then((res) => {
tableData.value = res.rows;
total.value = res.total;
handleUserSelection();
})
.catch((error) => {
console.error('获取用户列表失败:', error);
})
.finally(() => {
loading.value = false;
});
}
/** 楼栋列表 */
const buildinglist = ref([]);
/** 房屋列表 */
const unitlist = ref([]);
/** 筛选房屋类型 */
function handleChangeHouse(val: string) {
getBuildinglistAPI(val).then((res) => {
buildinglist.value = res.data;
});
}
/** 单元列表 */
function handleChangebuilding(val: string) {
getHouselistAPI(val).then((res) => {
unitlist.value = res.data.units;
});
}
// ==============================================
// ✅ 新增:处理 selection-change多选专用
// ==============================================
function handleSelectionChange(selection: BillHouse[]) {
if (isMultiple.value) {
multipleSelection.value = selection;
}
}
// ==============================================
// 最佳单选方案:点击行选中,无报错、无循环、最稳定
// ==============================================
// 优化 handleRowClick避免重复
function handleRowClick(row: BillHouse) {
if (!multipleTableRef.value) return;
if (isMultiple.value) {
const index = multipleSelection.value.findIndex((item) => item.houseId === row.houseId);
if (index > -1) {
// 取消选中
multipleSelection.value.splice(index, 1);
multipleTableRef.value?.toggleRowSelection(row, false);
} else {
// 选中
multipleSelection.value.push(row);
multipleTableRef.value?.toggleRowSelection(row, true);
}
} else {
// 【单选模式】先清空,再选中当前行
multipleSelection.value = [];
multipleTableRef.value.clearSelection();
multipleSelection.value = [row];
multipleTableRef.value.toggleRowSelection(row, true);
}
}
// 重置查询
function resetQuery() {
queryParams.value = {
pageNum: 1,
pageSize: 10,
houseuse: null,
buildingId: null,
unitNo: null
};
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({
open: () => {
getlist();
dialog.value.dialogVisible = true;
}
});
</script>
<style scoped lang="scss">
.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>

View File

@@ -1,12 +1,28 @@
<template>
<div class="top-right-btn" :style="style">
<el-row>
<el-tooltip v-if="search" class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top">
<el-button circle icon="Search" @click="toggleSearch()" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="刷新" placement="top">
<el-button circle icon="Refresh" @click="refresh()" />
</el-tooltip>
<div>
<!-- 添加按钮 -->
<el-button :disabled="props.disableAdd" v-if="props.showAdd" icon="Plus" type="primary" @click="toggleSearch()"> 新建 </el-button>
<!-- 批量操作下拉框 -->
<el-dropdown v-if="props.showDelete || props.showEdit || $slots.dropdown" trigger="click" style="margin-left: 10px">
<el-button @click.stop="refresh"> {{ props.moreTitle }} </el-button>
<!-- 👇 核心优先使用外部插槽没有则使用默认内容 -->
<template #dropdown>
<!-- 这里直接渲染不嵌套插槽 -->
<el-dropdown-menu v-if="!$slots.dropdown">
<el-dropdown-item :disabled="props.disableEdit" v-if="props.showEdit" @click="emits('update:edit')"> 编辑 </el-dropdown-item>
<el-dropdown-item :disabled="props.disableDelete" v-if="props.showDelete" @click="emits('update:delete')"> 批量删除 </el-dropdown-item>
</el-dropdown-menu>
<!-- 外部自定义内容直接渲染不包任何多余标签 -->
<template v-else>
<slot name="dropdown" />
</template>
</template>
</el-dropdown>
<!-- 显隐列保留你原有逻辑 -->
<el-tooltip v-if="columns" class="item" effect="dark" content="显示/隐藏列" placement="top">
<div class="show-btn">
<el-popover placement="bottom" trigger="click">
@@ -16,7 +32,7 @@
:data="columns"
show-checkbox
node-key="key"
:props="{ label: 'label', children: 'children' } as any"
:props="{ label: 'label', children: 'children' }"
@check="columnChange"
></el-tree>
<template #reference>
@@ -25,65 +41,77 @@
</el-popover>
</div>
</el-tooltip>
</el-row>
</div>
</div>
</template>
<script setup lang="ts">
import { propTypes } from '@/utils/propTypes';
import { CSSProperties } from 'vue';
const props = defineProps({
showSearch: propTypes.bool.def(true),
columns: propTypes.fieldOption,
search: propTypes.bool.def(true),
gutter: propTypes.number.def(10)
gutter: propTypes.number.def(10),
moreTitle: propTypes.string.def('批量操作'),
showAdd: propTypes.bool.def(true),
showDelete: propTypes.bool.def(true),
showEdit: propTypes.bool.def(true),
disableAdd: propTypes.bool.def(false),
disableDelete: propTypes.bool.def(false),
disableEdit: propTypes.bool.def(false)
});
const columnRef = ref<ElTreeInstance>();
const emits = defineEmits(['update:showSearch', 'queryTable']);
const columnRef = ref();
const emits = defineEmits(['update:add', 'update:edit', 'update:delete']);
const style = computed(() => {
const ret: any = {};
const style = computed<CSSProperties>(() => {
const ret: CSSProperties = {};
if (props.gutter) {
ret.marginRight = `${props.gutter / 2}px`;
}
return ret;
});
// 搜索
// 添加
function toggleSearch() {
emits('update:showSearch', !props.showSearch);
emits('update:add');
}
// 刷新
const isExpent = ref(false);
// 批量操作展开
function refresh() {
emits('queryTable');
isExpent.value = !isExpent.value;
}
// 更改数据列的显示隐藏
function columnChange(...args: any[]) {
// 更改显示隐藏
function columnChange(...args) {
props.columns?.forEach((item) => {
item.visible = args[1].checkedKeys.includes(item.key);
});
}
// 显隐列初始默认隐藏列
onMounted(() => {
props.columns?.forEach((item) => {
if (item.visible) {
columnRef.value?.setChecked(item.key, true, false);
// value.value.push(item.key);
}
});
});
</script>
<style lang="scss" scoped>
.hiddenActions {
margin-left: 10px;
}
:deep(.el-transfer__button) {
border-radius: 50%;
display: block;
margin-left: 0px;
}
:deep(.el-transfer__button:first-child) {
margin-bottom: 10px;
}
@@ -91,11 +119,13 @@ onMounted(() => {
.my-el-transfer {
text-align: center;
}
.tree-header {
width: 100%;
line-height: 24px;
text-align: center;
}
.show-btn {
margin-left: 12px;
}

View File

@@ -4,7 +4,7 @@
<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" :model="queryParams" :inline="true">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" @submit.prevent="handleQuery">
<el-form-item label="角色名称" prop="roleName">
<el-input v-model="queryParams.roleName" placeholder="请输入角色名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
@@ -13,8 +13,8 @@
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
<el-button type="primary" @click="handleQuery">搜索</el-button>
<el-button @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>

View File

@@ -24,7 +24,7 @@
<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" :model="queryParams" :inline="true">
<el-form ref="queryFormRef" @submit.prevent="handleQuery" :model="queryParams" :inline="true">
<el-form-item label="用户名称" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
@@ -32,8 +32,8 @@
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="() => resetQuery()">重置</el-button>
<el-button type="primary" @click="handleQuery">搜索</el-button>
<el-button @click="() => resetQuery()">重置</el-button>
</el-form-item>
</el-form>
</el-card>