8/31 dev 权限,sse,账单,bug
This commit is contained in:
320
src/components/Holder/complainUser.vue
Normal file
320
src/components/Holder/complainUser.vue
Normal file
@@ -0,0 +1,320 @@
|
||||
<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: 'complainUser'
|
||||
},
|
||||
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;
|
||||
console.log('获取列表');
|
||||
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());
|
||||
console.log(multipleSelection.value.map((item) => item.id));
|
||||
} 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 = [];
|
||||
}
|
||||
}
|
||||
|
||||
console.log('handleSelectionChange', selection);
|
||||
}
|
||||
// 重置查询
|
||||
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>
|
||||
Reference in New Issue
Block a user