9/20
This commit is contained in:
@@ -100,7 +100,7 @@ const props = defineProps({
|
||||
},
|
||||
userType: {
|
||||
type: String,
|
||||
default: 'complainUser'
|
||||
default: ''
|
||||
},
|
||||
isMultiple: {
|
||||
type: Boolean,
|
||||
|
||||
@@ -19,20 +19,21 @@
|
||||
<el-button @click="resetQuery">重置</el-button>
|
||||
</div>
|
||||
</nav>
|
||||
<el-table
|
||||
ref="multipleTableRef"
|
||||
:data="tableData"
|
||||
border
|
||||
@select-all="handleSelectionChange"
|
||||
@row-click="handleRowClick"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column align="center" type="selection" width="55" />
|
||||
<el-table ref="multipleTableRef" :data="tableData" border>
|
||||
<el-table-column align="center" width="55">
|
||||
<template #header>
|
||||
<el-checkbox v-model="isSelectAll" :disabled="!isMultiple" :indeterminate="isIndeterminate" @change="handleSelectAllChange" />
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-checkbox :model-value="isRowSelected(scope.row)" @change="(val: boolean) => handleRowCheckChange(scope.row, val)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="物料名称" prop="materialName" />
|
||||
<el-table-column align="center" label="品牌" prop="brand" />
|
||||
<el-table-column align="center" label="规格型号" prop="model" />
|
||||
<el-table-column align="center" label="单位" prop="unit" />
|
||||
<el-table-column v-if="!isHidden" align="center" label="数量" prop="stockNum" />
|
||||
<el-table-column align="center" label="所属小区" prop="villageName" />
|
||||
<el-table-column align="center" label="备注" prop="remark" />
|
||||
</el-table>
|
||||
|
||||
@@ -48,7 +49,7 @@
|
||||
/>
|
||||
<div>
|
||||
<el-button @click="conback">退出</el-button>
|
||||
<el-button type="primary" @click="confirm"> 确定 </el-button>
|
||||
<el-button :loading="loading" type="primary" @click="confirm"> 确定 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -58,7 +59,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { listMaterial } from '@/api/system/Tmaterial';
|
||||
import { MaterialVO } from '@/api/system/Tmaterial/type';
|
||||
import { ref, nextTick, getCurrentInstance, ComponentInternalInstance, toRefs } from 'vue';
|
||||
import { ComponentInternalInstance, getCurrentInstance, ref, toRefs } from 'vue';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
@@ -109,41 +110,87 @@ const tableData = ref<MaterialVO[]>([]);
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
|
||||
// 存储选中项
|
||||
/** 存储所有选中项(跨页累计,非全选模式使用) */
|
||||
const multipleSelection = ref<MaterialVO[]>([]);
|
||||
// 多选
|
||||
/** 是否已跨页全选(标记模式,不实际持有全部数据) */
|
||||
const isSelectAll = ref(false);
|
||||
/** 表头复选框半选状态(部分选中时为 true) */
|
||||
const isIndeterminate = ref(false);
|
||||
/** 全选模式下被手动取消勾选的 id 列表 */
|
||||
const deselectedIds = ref<string[]>([]);
|
||||
|
||||
const isMultiple = computed(() => props.isMultiple ?? false);
|
||||
|
||||
// 优化 handleUserSelection,避免重复查找
|
||||
const handleUserSelection = () => {
|
||||
if (!userid.value || !tableData.value.length) return;
|
||||
|
||||
multipleSelection.value = [];
|
||||
if (userid.value.trim() !== '') {
|
||||
const arr = userid.value.split(',');
|
||||
arr.forEach((item) => {
|
||||
const target = tableData.value.find((row) => row.id == item);
|
||||
target && multipleSelection.value.push(target);
|
||||
});
|
||||
nextTick(() => {
|
||||
if (multipleTableRef.value) {
|
||||
multipleTableRef.value.clearSelection();
|
||||
multipleSelection.value.forEach((item) => {
|
||||
multipleTableRef.value.toggleRowSelection(item, true);
|
||||
});
|
||||
}
|
||||
});
|
||||
/** 判断某行是否已选中 */
|
||||
function isRowSelected(row: MaterialVO): boolean {
|
||||
if (isSelectAll.value) {
|
||||
// 全选模式:只要不在取消列表中就算选中
|
||||
return !deselectedIds.value.includes(row.id);
|
||||
}
|
||||
};
|
||||
return multipleSelection.value.some((item) => item.id === row.id);
|
||||
}
|
||||
|
||||
// 监听
|
||||
// 监听优化:只在 userid 变化时触发
|
||||
// watch(userid, handleUserSelection, { immediate: true });
|
||||
// watch(tableData, () => {
|
||||
// if (userid.value) handleUserSelection(); // 仅在有 userid 时重新处理
|
||||
// });
|
||||
/** 更新表头复选框状态:全选 / 半选 / 空 */
|
||||
function updateIndeterminate() {
|
||||
if (isSelectAll.value) {
|
||||
// 全选模式下:有取消勾选的行 → 半选,否则 → 全选
|
||||
isIndeterminate.value = deselectedIds.value.length > 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// 非全选模式:只要有已勾选的行即为半选。
|
||||
// 注意:不能用 selectedCount >= total 去做“自动升级为全选”,
|
||||
// 否则搜索/筛选后 total 变小,会把之前积累的 multipleSelection 误判并清空,覆盖已选数据。
|
||||
isIndeterminate.value = multipleSelection.value.length > 0;
|
||||
}
|
||||
|
||||
/** 跨页全选 / 取消全选(仅设标记,不拉取数据) */
|
||||
function handleSelectAllChange(val: boolean) {
|
||||
isSelectAll.value = val;
|
||||
deselectedIds.value = [];
|
||||
multipleSelection.value = [];
|
||||
updateIndeterminate();
|
||||
}
|
||||
|
||||
/** 单行勾选 / 取消勾选 */
|
||||
function handleRowCheckChange(row: MaterialVO, checked: boolean) {
|
||||
if (!isMultiple.value) {
|
||||
// 单选模式:仅保留当前行
|
||||
if (checked) {
|
||||
multipleSelection.value = [row];
|
||||
} else {
|
||||
multipleSelection.value = [];
|
||||
}
|
||||
isSelectAll.value = false;
|
||||
deselectedIds.value = [];
|
||||
updateIndeterminate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSelectAll.value) {
|
||||
// 全选模式:维护取消列表
|
||||
if (checked) {
|
||||
deselectedIds.value = deselectedIds.value.filter((id) => id !== row.id);
|
||||
} else {
|
||||
if (!deselectedIds.value.includes(row.id)) {
|
||||
deselectedIds.value.push(row.id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 非全选模式:维护选中列表
|
||||
if (checked) {
|
||||
if (!isRowSelected(row)) {
|
||||
multipleSelection.value.push(row);
|
||||
}
|
||||
} else {
|
||||
multipleSelection.value = multipleSelection.value.filter((item) => item.id !== row.id);
|
||||
}
|
||||
}
|
||||
updateIndeterminate();
|
||||
}
|
||||
|
||||
// ====================== 方法 ======================
|
||||
|
||||
// 获取列表
|
||||
function getlist() {
|
||||
loading.value = true;
|
||||
@@ -151,46 +198,16 @@ function getlist() {
|
||||
.then((res) => {
|
||||
tableData.value = res.rows;
|
||||
total.value = res.total;
|
||||
updateIndeterminate();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('获取物料列表失败:', error);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
// ✅ 新增:处理 selection-change(多选专用)
|
||||
// ==============================================
|
||||
function handleSelectionChange(selection: MaterialVO[]) {
|
||||
if (isMultiple.value) {
|
||||
multipleSelection.value = selection;
|
||||
}
|
||||
}
|
||||
// ==============================================
|
||||
// 最佳单选方案:点击行选中,无报错、无循环、最稳定
|
||||
// ==============================================
|
||||
// 优化 handleRowClick,避免重复
|
||||
function handleRowClick(row: MaterialVO) {
|
||||
if (!multipleTableRef.value) return;
|
||||
|
||||
if (isMultiple.value) {
|
||||
const index = multipleSelection.value.findIndex((item) => item.id === row.id);
|
||||
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 = {
|
||||
@@ -199,6 +216,7 @@ function resetQuery() {
|
||||
brand: '',
|
||||
materialName: ''
|
||||
};
|
||||
resetSelection();
|
||||
getlist();
|
||||
}
|
||||
|
||||
@@ -208,44 +226,90 @@ function conback() {
|
||||
resetQuery();
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
// ✅ 核心修改:根据 returnvalue 返回对应的值
|
||||
// ==============================================
|
||||
function confirm() {
|
||||
dialog.value.dialogVisible = false;
|
||||
/** 重置所有选中状态 */
|
||||
function resetSelection() {
|
||||
multipleSelection.value = [];
|
||||
isSelectAll.value = false;
|
||||
isIndeterminate.value = false;
|
||||
deselectedIds.value = [];
|
||||
}
|
||||
|
||||
/** 根据 returnvalue 发射选中结果 */
|
||||
function emitResult(data: MaterialVO[]) {
|
||||
if (isMultiple.value) {
|
||||
// 多选
|
||||
if (returnvalue.value === '*') {
|
||||
// ✅ 返回整个对象数组
|
||||
emit('change', multipleSelection.value);
|
||||
emit('change', data);
|
||||
} else {
|
||||
// 返回指定字段数组
|
||||
emit(
|
||||
'change',
|
||||
multipleSelection.value.map((item) => item[returnvalue.value as keyof MaterialVO])
|
||||
data.map((item) => item[returnvalue.value as keyof MaterialVO])
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// 单选
|
||||
if (returnvalue.value === '*') {
|
||||
// ✅ 返回整个对象
|
||||
emit('change', multipleSelection.value[0]);
|
||||
emit('change', data[0]);
|
||||
} else {
|
||||
// 返回指定字段
|
||||
emit('change', multipleSelection.value[0][returnvalue.value as keyof MaterialVO]);
|
||||
emit('change', data[0]?.[returnvalue.value as keyof MaterialVO]);
|
||||
}
|
||||
}
|
||||
|
||||
multipleSelection.value = [];
|
||||
multipleTableRef.value.clearSelection();
|
||||
}
|
||||
// 初始化
|
||||
getlist();
|
||||
|
||||
/** 确定:全选模式下按需拉取全部数据,非全选模式直接使用已选列表 */
|
||||
async function confirm() {
|
||||
if (isMultiple.value && isSelectAll.value && total.value > 0) {
|
||||
// 全选模式:此时才真正拉取全部数据,排除已取消勾选的行
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await listMaterial({
|
||||
...queryParams.value,
|
||||
pageNum: 1,
|
||||
pageSize: total.value
|
||||
});
|
||||
const allData: MaterialVO[] = res.rows || [];
|
||||
const filteredData = allData.filter((item) => !deselectedIds.value.includes(item.id));
|
||||
dialog.value.dialogVisible = false;
|
||||
emitResult(filteredData);
|
||||
} catch (error) {
|
||||
console.error('获取全部物料数据失败:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
} else {
|
||||
// 非全选模式:直接使用已收集的选中列表
|
||||
dialog.value.dialogVisible = false;
|
||||
emitResult(multipleSelection.value);
|
||||
}
|
||||
|
||||
resetSelection();
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open: () => {
|
||||
// 重置查询条件,避免残留上次的筛选条件
|
||||
queryParams.value = {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
brand: '',
|
||||
materialName: ''
|
||||
};
|
||||
// 立即清空旧列表数据,避免闪烁
|
||||
tableData.value = [];
|
||||
total.value = 0;
|
||||
// 重置全选标记
|
||||
isSelectAll.value = false;
|
||||
deselectedIds.value = [];
|
||||
// 根据 userid(逗号分隔)初始化选中状态
|
||||
const arr = userid.value ? userid.value.split(',').filter((id) => id) : [];
|
||||
if (arr.length > 0) {
|
||||
multipleSelection.value = arr.map((id) => ({ id }) as MaterialVO);
|
||||
isIndeterminate.value = true;
|
||||
} else {
|
||||
multipleSelection.value = [];
|
||||
isIndeterminate.value = false;
|
||||
}
|
||||
|
||||
getlist();
|
||||
dialog.value.dialogVisible = true;
|
||||
handleUserSelection();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,71 +1,71 @@
|
||||
<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>
|
||||
<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>
|
||||
<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" />
|
||||
<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>
|
||||
<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 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>
|
||||
<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"
|
||||
style="margin-top: 0"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<div>
|
||||
<el-button @click="conBack">退出</el-button>
|
||||
<el-button type="primary" @click="confirm"> 确定 </el-button>
|
||||
<el-button type="primary" @click="getList">查询</el-button>
|
||||
<el-button @click="resetQuery">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</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"
|
||||
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>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
@@ -143,6 +143,7 @@ const multipleTableRef = ref();
|
||||
const tableData = ref<BillUserlist[]>([]);
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
|
||||
/** 存储所有选中项(跨页累计,非全选模式使用) */
|
||||
const multipleSelection = ref<BillUserlist[]>([]);
|
||||
/** 是否已跨页全选(标记模式,不实际持有全部数据) */
|
||||
|
||||
Reference in New Issue
Block a user