8/31 dev 权限,sse,账单,bug

This commit is contained in:
Zy
2026-08-31 08:09:28 +08:00
parent bfdfa05cd7
commit e4417f35b1
82 changed files with 2327 additions and 1644 deletions

View 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>