Files
estate/src/components/Holder/complainUser.vue
2026-09-20 08:57:53 +08:00

317 lines
9.3 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 v-if="$slots.default" />
<div v-else class="residentBox" @click="choiceResidentFun">
<div v-if="multipleSelection.length === 0 && props.username === null" class="defaultBox">
<span>请选择</span>
<el-icon><Search /></el-icon>
</div>
<div v-else class="overflow-auto overflow-hidden w-full text-ellipsis text-nowrap">
<span>姓名:{{ multipleSelection[0]?.nickName ?? props.username }}</span>
</div>
</div>
<el-dialog v-model="dialog.dialogVisible" :title="dialog.dialogTitle" append-to-body width="1050">
<nav class="flex flex-items-center mb-8">
<div class="flex flex-items-center">
<div style="width: 70px; text-align: right; padding-right: 10px">员工姓名</div>
<div class="mr-5 flex-1">
<el-input v-model="queryParams.nickName" clearable placeholder="请输入员工姓名" style="width: 170px" />
</div>
</div>
<div class="flex flex-items-center">
<div style="width: 70px; text-align: right; padding-right: 10px">手机号</div>
<div class="mr-5 flex-1">
<el-input v-model="queryParams.phonenumber" clearable placeholder="请输入手机号" style="width: 170px" />
</div>
</div>
<div>
<el-button type="primary" @click="getList">查询</el-button>
<el-button @click="resetQuery">重置</el-button>
</div>
</nav>
<el-table ref="multipleTableRef" :data="tableData" border @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="nickName" />
<el-table-column align="center" label="性别" prop="sex" width="85">
<template #default="{ row }">
<dict-tag :options="sys_user_sex" :value="row.sex" />
</template>
</el-table-column>
<el-table-column align="center" label="角色" prop="roleName" />
<el-table-column v-if="props.userType == 'repair'" align="center" label="当前任务数量" prop="taskCount" />
<el-table-column align="center" label="手机号码" prop="phonenumber" />
<el-table-column align="center" label="状态" prop="status">
<template #default="{ row }">
<el-tag :type="row.status === '0' ? 'success' : 'danger'">{{ row.status === '0' ? '正常' : '停用' }}</el-tag>
</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"
layout="total, sizes, prev, pager, next, jumper"
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 { ResidentVO } from '@/api/system/residentReviewProcess/resident/type';
import { getUserByRoleKey } from '@/api/system/user';
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'));
interface tableType extends ResidentVO {
housePath?: string;
}
const props = defineProps({
userid: {
type: String,
default: ''
},
username: {
type: String,
default: null
},
returnvalue: {
type: String,
default: 'userId'
},
titleName: {
type: String,
default: '选择投诉处理人员'
},
userType: {
type: String,
default: ''
},
isMultiple: {
type: Boolean,
default: false
}
});
const { userid, titleName, userType, returnvalue } = toRefs(props);
// ✅ 手动设置默认值(替代 withDefaults)
const emit = defineEmits<{
change: [value: string | number | (string | number)[] | tableType | tableType[]]; // 支持返回数组
}>();
const dialog = ref({
dialogVisible: false,
dialogTitle: titleName.value
});
const queryParams = ref({
pageNum: 1,
pageSize: 10,
nickName: null,
phonenumber: null
});
const multipleTableRef = ref();
const tableData = ref<tableType[]>([]);
const total = ref(0);
const loading = ref(false);
const isFetching = ref(false); // 加锁,防止重复触发 selection-change
// 存储选中项
const multipleSelection = ref<tableType[]>([]);
const isMultiple = computed(() => props.isMultiple ?? false);
// 优化 handleUserSelection,避免重复查找
const handleUserSelection = () => {
isFetching.value = true;
if (tableData.value.length <= 0 || !multipleTableRef.value) {
isFetching.value = false;
return;
}
const rowsToSelect = tableData.value.filter((row) => {
return multipleSelection.value.some((selected) => selected.userId === row.userId);
});
multipleTableRef.value.clearSelection();
if (rowsToSelect.length > 0) {
nextTick(() => {
rowsToSelect.forEach((row) => {
multipleTableRef.value?.toggleRowSelection(row, true);
});
isFetching.value = false;
});
} else {
isFetching.value = false;
}
};
// 监听
// 监听优化:只在 userid 变化时触发
watch(userid, handleUserSelection, { immediate: true });
// ====================== 方法 ======================
// 打开选择框
function choiceResidentFun() {
dialog.value.dialogVisible = true;
multipleSelection.value = []; // 打开时清空之前的选择
if (typeof props.userid === 'string' && props.userid.trim() !== '') {
multipleSelection.value = [
{
userId: props.userid
} as ResidentVO
];
} else if (Array.isArray(props.userid)) {
multipleSelection.value = props.userid.map((id) => ({ userId: id })) as tableType[];
}
getList();
}
// 获取列表
function getList() {
loading.value = true;
getUserByRoleKey({
...queryParams.value,
roleKey: userType.value
})
.then((res) => {
tableData.value = res.rows;
total.value = res.total;
handleUserSelection();
})
.catch((error) => {
console.error('获取用户列表失败:', error);
})
.finally(() => {
loading.value = false;
});
}
// ==============================================
// ✅ 新增:处理 selection-change(多选专用)
// ==============================================
function handleSelectionChange(selection: tableType[]) {
if (isFetching.value) return;
if (isMultiple.value) {
const currentSelectedIds = new Set(selection.map((item) => item.id));
const otherPageSelections = multipleSelection.value.filter((item) => {
const isCurrentPageItem = tableData.value.some((row) => row.id === item.id);
if (isCurrentPageItem) {
return currentSelectedIds.has(item.id);
} else {
return true;
}
});
// 3. 合并:其他页保留的 + 当前页新选中的
const finalSelection = [...otherPageSelections, ...selection];
// 4. 去重(以防万一)
const uniqueMap = new Map();
finalSelection.forEach((item) => uniqueMap.set(item.id, item));
multipleSelection.value = Array.from(uniqueMap.values());
} else {
// --- 单选逻辑 ---
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,
nickName: null,
phonenumber: 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 tableType])
);
}
} else {
// 单选
if (returnvalue.value === '*') {
// ✅ 返回整个对象
emit('change', multipleSelection.value[multipleSelection.value.length - 1]);
} else {
// 返回指定字段
emit('change', multipleSelection.value[multipleSelection.value.length - 1][returnvalue.value as keyof tableType]);
}
}
}
onMounted(() => {
// 初始化
});
defineExpose({
open: () => {
dialog.value.dialogVisible = true;
getList();
}
});
</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>