This commit is contained in:
BianLzhaoMin 2026-02-10 10:07:29 +08:00
parent 2c1d9d1eb8
commit 9c6b1670f1
3 changed files with 286 additions and 124 deletions

View File

@ -22,42 +22,41 @@
<!-- 左侧可选人员列表 -->
<div class="member-list-left">
<div class="search-bar">
<el-row :gutter="10">
<el-row style="width: 100%">
<el-col :span="12">
<el-input
v-model="searchDept"
placeholder="输入部门名称"
clearable
prefix-icon="Search"
style="width: 99%"
/>
</el-col>
<el-col :span="12">
<el-col :span="12" style="text-align: right">
<el-input
v-model="searchName"
placeholder="输入人员姓名"
clearable
prefix-icon="Search"
style="width: 99%"
/>
</el-col>
</el-row>
</div>
<div class="table-container">
<el-table
ref="personTableRef"
:data="filteredPersonList"
height="300"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="orgName" label="人员所属" width="150" />
<el-table-column prop="workerName" label="姓名" width="120" />
<el-table-column prop="sex" label="性别" width="80">
<template #default="{ row }">
{{ row.sex == 1 ? '男' : '女' }}
</template>
</el-table-column>
<el-table-column prop="phone" label="电话" />
</el-table>
<el-auto-resizer>
<template #default="{ height, width }">
<el-table-v2
ref="personTableRef"
:columns="tableColumns"
:data="filteredPersonList"
:width="width"
:height="height"
row-key="id"
fixed
/>
</template>
</el-auto-resizer>
</div>
</div>
@ -107,9 +106,11 @@
</template>
<script setup name="AddAndEditForm">
import { ref, computed, watch, onMounted, nextTick } from 'vue'
import { ref, computed, watch, onMounted, nextTick, h } from 'vue'
import { ElCheckbox } from 'element-plus'
import { getAllPersonAPI } from '@/api/personManage/person.js'
import { Close } from '@element-plus/icons-vue'
import { debounce } from '@/utils'
const props = defineProps({
formData: {
@ -126,6 +127,81 @@ const personList = ref([])
const selectedPersonList = ref([])
//
const isSyncing = ref(false)
// Map
const selectedMembersMap = ref(new Map())
//
const debouncedFilter = ref(null)
// keys
const selectedRowKeys = ref([])
// Set
const selectedIdsSet = computed(() => {
if (!props.formData.workerList || props.formData.workerList.length === 0) {
return new Set()
}
return new Set(props.formData.workerList.map((id) => Number(id)))
})
//
const tableColumns = computed(() => {
return [
{
key: 'selection',
width: 55,
cellRenderer: ({ rowData }) => {
const isSelected = selectedRowKeys.value.includes(rowData.id)
return h(ElCheckbox, {
modelValue: isSelected,
'onUpdate:modelValue': (val) => {
handleRowSelect({ row: rowData, checked: val })
},
})
},
headerCellRenderer: () => {
const allSelected =
filteredPersonList.value.length > 0 &&
filteredPersonList.value.every((row) => selectedRowKeys.value.includes(row.id))
const someSelected = filteredPersonList.value.some((row) =>
selectedRowKeys.value.includes(row.id),
)
return h(ElCheckbox, {
modelValue: allSelected,
indeterminate: someSelected && !allSelected,
'onUpdate:modelValue': (val) => {
handleSelectAll({ checked: val })
},
})
},
},
{
key: 'orgName',
dataKey: 'orgName',
title: '人员所属',
width: 150,
},
{
key: 'workerName',
dataKey: 'workerName',
title: '姓名',
width: 120,
},
{
key: 'sex',
dataKey: 'sex',
title: '性别',
width: 80,
cellRenderer: ({ rowData }) => {
return rowData.sex == 1 ? '男' : '女'
},
},
{
key: 'phone',
dataKey: 'phone',
title: '电话',
width: 200,
},
]
})
//
const rules = {
@ -146,8 +222,16 @@ const rules = {
],
}
//
const filteredPersonList = computed(() => {
// 使ref
const filteredPersonList = ref([])
//
const performFilter = () => {
if (personList.value.length === 0) {
filteredPersonList.value = []
return
}
let filtered = personList.value
//
@ -168,15 +252,52 @@ const filteredPersonList = computed(() => {
})
}
return filtered
})
filteredPersonList.value = filtered
//
//
nextTick(() => {
syncTableSelection()
})
}
//
const createDebouncedFilter = () => {
debouncedFilter.value = debounce(performFilter, 300)
}
// 使Map
const selectedMembers = computed(() => {
if (!props.formData.workerList || props.formData.workerList.length === 0) {
selectedMembersMap.value.clear()
return []
}
return personList.value.filter((item) => props.formData.workerList.includes(item.id))
// Map使
if (selectedMembersMap.value.size === props.formData.workerList.length) {
const cachedIds = Array.from(selectedMembersMap.value.keys())
const currentIds = props.formData.workerList.map((id) => Number(id))
const idsMatch =
cachedIds.every((id) => currentIds.includes(id)) &&
currentIds.every((id) => cachedIds.includes(id))
if (idsMatch) {
return Array.from(selectedMembersMap.value.values())
}
}
// Map
selectedMembersMap.value.clear()
const result = []
const selectedIdsSet = new Set(props.formData.workerList.map((id) => Number(id)))
// Map
for (const item of personList.value) {
if (selectedIdsSet.has(Number(item.id))) {
selectedMembersMap.value.set(Number(item.id), item)
result.push(item)
}
}
return result
})
//
@ -194,6 +315,10 @@ const getPersonList = async () => {
sex: item.sex,
phone: item.phone,
}))
//
performFilter()
// watch
// syncTableSelection
}
@ -212,10 +337,10 @@ watch(
if (isSyncing.value) {
return
}
// workerList
if (personList.value.length > 0 && newVal && newVal.length > 0) {
//
if (personList.value.length > 0) {
//
const newValStr = JSON.stringify(newVal.sort())
const newValStr = JSON.stringify((newVal || []).sort())
const oldValStr = oldVal ? JSON.stringify(oldVal.sort()) : ''
if (newValStr !== oldValStr) {
nextTick(() => {
@ -249,139 +374,164 @@ watch(
},
)
//
// 使Set
const syncTableSelection = () => {
if (
!personTableRef.value ||
!props.formData.workerList ||
props.formData.workerList.length === 0
) {
//
if (personTableRef.value) {
isSyncing.value = true
personTableRef.value.clearSelection()
// clearSelection
nextTick(() => {
isSyncing.value = false
})
}
return
}
// watch handleSelectionChange
isSyncing.value = true
// workerList
const selectedIds = props.formData.workerList.map((id) => Number(id))
nextTick(() => {
// selection-change isSyncing=true
personTableRef.value.clearSelection()
//
// 使
filteredPersonList.value.forEach((row) => {
const rowId = Number(row.id)
if (selectedIds.includes(rowId)) {
personTableRef.value.toggleRowSelection(row, true)
}
})
//
nextTick(() => {
nextTick(() => {
isSyncing.value = false
})
})
})
}
//
const handleSelectionChange = (selection) => {
//
//
if (isSyncing.value) {
return
}
selectedPersonList.value = selection
// workerList
// ID
const allSelectedIds = new Set(props.formData.workerList || [])
// ID
const currentFilteredIds = new Set(filteredPersonList.value.map((item) => item.id))
//
// currentFilteredIds
currentFilteredIds.forEach((id) => {
if (!selection.find((s) => s.id === id)) {
allSelectedIds.delete(id)
}
})
//
selection.forEach((item) => {
allSelectedIds.add(item.id)
})
// watch
isSyncing.value = true
props.formData.workerList = Array.from(allSelectedIds)
if (!props.formData.workerList || props.formData.workerList.length === 0) {
//
selectedRowKeys.value = []
nextTick(() => {
isSyncing.value = false
})
return
}
// 使computedSet
const idsSet = selectedIdsSet.value
//
const visibleSelectedKeys = filteredPersonList.value
.filter((row) => idsSet.has(Number(row.id)))
.map((row) => row.id)
selectedRowKeys.value = visibleSelectedKeys
//
nextTick(() => {
isSyncing.value = false
})
}
//
//
const handleRowSelect = ({ row, checked }) => {
if (isSyncing.value) {
return
}
isSyncing.value = true
const rowId = Number(row.id)
const allSelectedIds = new Set(props.formData.workerList || [])
if (checked) {
allSelectedIds.add(rowId)
if (!selectedRowKeys.value.includes(row.id)) {
selectedRowKeys.value.push(row.id)
}
} else {
allSelectedIds.delete(rowId)
const index = selectedRowKeys.value.indexOf(row.id)
if (index > -1) {
selectedRowKeys.value.splice(index, 1)
}
}
props.formData.workerList = Array.from(allSelectedIds)
nextTick(() => {
isSyncing.value = false
})
}
// /
const handleSelectAll = ({ checked }) => {
if (isSyncing.value) {
return
}
isSyncing.value = true
const allSelectedIds = new Set(props.formData.workerList || [])
const currentFilteredIds = filteredPersonList.value.map((item) => Number(item.id))
if (checked) {
//
currentFilteredIds.forEach((id) => {
allSelectedIds.add(id)
})
selectedRowKeys.value = filteredPersonList.value.map((row) => row.id)
} else {
//
currentFilteredIds.forEach((id) => {
allSelectedIds.delete(id)
})
// keys
selectedRowKeys.value = []
}
props.formData.workerList = Array.from(allSelectedIds)
nextTick(() => {
isSyncing.value = false
})
}
//
const handleRemoveMember = (memberId) => {
if (!props.formData.workerList) {
props.formData.workerList = []
selectedRowKeys.value = []
return
}
// watch
const memberIdNum = Number(memberId)
// watch
isSyncing.value = true
props.formData.workerList = props.formData.workerList.filter((id) => id !== memberId)
// workerList
props.formData.workerList = props.formData.workerList.filter((id) => Number(id) !== memberIdNum)
// selectedRowKeys
// selectedRowKeys
const index = selectedRowKeys.value.findIndex((id) => Number(id) === memberIdNum)
if (index > -1) {
selectedRowKeys.value.splice(index, 1)
}
//
// 使 nextTick DOM
nextTick(() => {
//
isSyncing.value = false
//
syncTableSelection()
})
}
//
//
const handleClearAll = () => {
// watch
isSyncing.value = true
props.formData.workerList = []
if (personTableRef.value) {
personTableRef.value.clearSelection()
}
selectedRowKeys.value = []
nextTick(() => {
isSyncing.value = false
})
}
//
// 使
watch([() => searchDept.value, () => searchName.value], () => {
//
//
if (
personList.value.length > 0 &&
props.formData.workerList &&
props.formData.workerList.length > 0
) {
// handleSelectionChange
isSyncing.value = true
//
nextTick(() => {
nextTick(() => {
syncTableSelection()
})
})
if (personList.value.length > 0) {
// 使
if (debouncedFilter.value) {
debouncedFilter.value()
} else {
performFilter()
}
}
})
onMounted(() => {
//
createDebouncedFilter()
//
getPersonList()
})
@ -436,6 +586,8 @@ defineExpose({
border-radius: 4px;
overflow: hidden;
background: #fff;
height: 300px;
min-height: 300px;
}
}

View File

@ -140,6 +140,10 @@ const actionColumns = [
link: true,
handler: (row) => {
if (row.taskStatus === '0') {
proxy.$modal.msgError('该任务已启用,无法删除')
return
}
proxy.$modal
.confirm('是否确认删除该循环发送任务?')
.then(async () => {

View File

@ -24,6 +24,7 @@
style="width: 100%"
max-height="500px"
>
<el-table-column type="index" label="序号" align="center" width="80" />
<el-table-column prop="workerName" label="姓名" align="center" width="120" />
<el-table-column
prop="orgName"
@ -156,6 +157,11 @@ const tableColumns = [
return typeMap[row.msgType] || '-'
},
},
{
prop: 'messageContent',
label: '短信内容',
showOverflowTooltip: true,
},
{
prop: 'remark',
label: '备注',