From 2a0b910c7a717d2c9cd526ac45967776066524b6 Mon Sep 17 00:00:00 2001 From: BianLzhaoMin <11485688+bianliangzhaomin123@user.noreply.gitee.com> Date: Mon, 2 Feb 2026 11:10:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/sMsSendManage/loopSend.js | 9 + src/components/PersonPicker/index.vue | 345 ++++++++++++--------- src/views/index.vue | 13 +- src/views/sMsSendManage/loopSend/config.js | 4 + src/views/sMsSendManage/loopSend/edit.vue | 190 +++++++++--- src/views/sMsSendManage/loopSend/index.vue | 26 +- 6 files changed, 395 insertions(+), 192 deletions(-) diff --git a/src/api/sMsSendManage/loopSend.js b/src/api/sMsSendManage/loopSend.js index 7428093..30ad10c 100644 --- a/src/api/sMsSendManage/loopSend.js +++ b/src/api/sMsSendManage/loopSend.js @@ -91,3 +91,12 @@ export function resendOneLoopMsgAPI(data) { }) } +// 立即执行一次 +export function runOneLoopMsgAPI(data) { + return request({ + url: '/msgJob/executeOnceJob', + method: 'POST', + data, + }) +} + diff --git a/src/components/PersonPicker/index.vue b/src/components/PersonPicker/index.vue index d30b183..433bbc7 100644 --- a/src/components/PersonPicker/index.vue +++ b/src/components/PersonPicker/index.vue @@ -101,34 +101,54 @@ - +
- 已选人员 + {{ activeTab === 'person' ? '已选人员' : '已选分组人员' }} 清空
-
- {{ person.workerName }} - - - -
-
- 暂未选择人员 -
+ + + +
@@ -147,9 +167,13 @@ const props = defineProps({ type: Array, default: () => [], }, + groupList: { + type: Array, + default: () => [], + }, }) -const emit = defineEmits(['update:modelValue', 'confirm']) +const emit = defineEmits(['update:modelValue', 'update:groupList', 'confirm']) const activeTab = ref('person') const searchKeyword = ref('') @@ -170,6 +194,9 @@ const selectedGroupIds = ref([]) // 标志位:是否正在同步选中状态 const isSyncing = ref(false) +// 标志位:是否已经初始化过(用于区分是用户操作还是外部数据变化) +const isInitialized = ref(false) + // 过滤后的人员列表 const filteredPersonList = computed(() => { if (!searchKeyword.value) { @@ -190,7 +217,7 @@ const filteredGroupList = computed(() => { } const keyword = groupSearchKeyword.value.toLowerCase() return groupList.value.filter((item) => { - const groupName = (item.groupName || '').toLowerCase() + const groupName = (item.groupName || item.workerName || '').toLowerCase() return groupName.includes(keyword) }) }) @@ -198,46 +225,46 @@ const filteredGroupList = computed(() => { // 选中的分组数量 const selectedGroupCount = computed(() => selectedGroupIds.value.length) -// 所有已选人员(包括直接选择的人员和从分组中选择的人员) -const allSelectedPersons = computed(() => { - const personMap = new Map() +// 直接选择的人员列表(用于人员tab显示) +const directSelectedPersons = computed(() => { + return directSelectedPersonIds.value + .map((personId) => { + const person = personList.value.find((p) => p.id === personId) + if (person) { + return { + id: person.id, + workerName: person.workerName, + orgName: person.orgName, + sex: person.sex, + phone: person.phone, + } + } + return null + }) + .filter(Boolean) +}) - // 添加直接选择的人员 - directSelectedPersonIds.value.forEach((personId) => { - const person = personList.value.find((p) => p.id === personId) - if (person) { - personMap.set(person.id, { - id: person.id, - workerName: person.workerName, - orgName: person.orgName, - sex: person.sex, - phone: person.phone, - }) - } - }) - - // 添加从分组中选择的人员(去重) +// 从分组中选择的人员列表(用于分组tab显示,不去重) +const groupSelectedPersons = computed(() => { + const persons = [] selectedGroupIds.value.forEach((groupId) => { const group = groupList.value.find((g) => g.id === groupId) if (group && group.workerList && Array.isArray(group.workerList)) { group.workerList.forEach((worker) => { if (worker && worker.id) { - // 如果已存在,不覆盖(保持直接选择的数据) - if (!personMap.has(worker.id)) { - personMap.set(worker.id, { - id: worker.id, - workerName: worker.workerName || worker.name || '', - orgName: worker.orgName || '', - sex: worker.sex, - phone: worker.phone || '', - }) - } + persons.push({ + id: worker.id, + groupId: groupId, + workerName: worker.workerName || worker.name || '', + orgName: worker.orgName || '', + sex: worker.sex, + phone: worker.phone || '', + }) } }) } }) - - return Array.from(personMap.values()) + return persons }) // 直接选择的人员数量 @@ -249,6 +276,7 @@ const selectedPersonCount = computed(() => { const handleTabChange = (tabName) => { searchKeyword.value = '' groupSearchKeyword.value = '' + // 切换tab时,只同步当前tab的选中状态,不清空已选项 nextTick(() => { if (tabName === 'person') { syncPersonTableSelection() @@ -293,47 +321,37 @@ const onPersonSelectionChange = (selection) => { updateSelectedPersons() } -// 更新选中的所有人(合并直接选择的人员和分组中的人员) +// 更新选中的内容 const updateSelectedPersons = () => { - const personMap = new Map() + // 标记为已初始化,避免watch反向同步 + isInitialized.value = true - // 添加直接选择的人员 - directSelectedPersonIds.value.forEach((personId) => { - const person = personList.value.find((p) => p.id === personId) - if (person) { - personMap.set(person.id, { - id: person.id, - workerName: person.workerName, - orgName: person.orgName, - sex: person.sex, - phone: person.phone, - }) - } - }) - - // 添加从分组中选择的人员(去重) - selectedGroupIds.value.forEach((groupId) => { - const group = groupList.value.find((g) => g.id === groupId) - if (group && group.workerList && Array.isArray(group.workerList)) { - group.workerList.forEach((worker) => { - if (worker && worker.id) { - // 如果已存在,不覆盖(保持直接选择的数据) - if (!personMap.has(worker.id)) { - personMap.set(worker.id, { - id: worker.id, - workerName: worker.workerName || worker.name || '', - orgName: worker.orgName || '', - sex: worker.sex, - phone: worker.phone || '', - }) - } + // 更新人员列表 + const persons = directSelectedPersonIds.value + .map((personId) => { + const person = personList.value.find((p) => p.id === personId) + if (person) { + return { + id: person.id, + workerName: person.workerName, + orgName: person.orgName, + sex: person.sex, + phone: person.phone, } - }) - } - }) + } + return null + }) + .filter(Boolean) + emit('update:modelValue', persons) - // 触发更新事件 - emit('update:modelValue', Array.from(personMap.values())) + // 更新分组列表 + const groups = selectedGroupIds.value + .map((groupId) => { + const group = groupList.value.find((g) => g.id === groupId) + return group ? { id: group.id, groupName: group.groupName || group.workerName } : null + }) + .filter(Boolean) + emit('update:groupList', groups) } // 分组选择变化 @@ -354,7 +372,7 @@ const onGroupSelectionChange = (selection) => { // 合并保留的ID和当前选中的可见ID selectedGroupIds.value = [...preservedIds, ...selectedVisibleIds] - // 触发更新,合并所有选中的人员 + // 触发更新,传递分组列表 updateSelectedPersons() } @@ -418,40 +436,10 @@ const syncGroupTableSelection = () => { }) } -// 移除单个人员 +// 移除单个人员(仅用于人员tab) const handleRemovePerson = (personId) => { - // 检查该人员是否在直接选择列表中 - const isDirectSelected = directSelectedPersonIds.value.includes(personId) - - // 检查该人员是否来自分组 - const groupsContainingPerson = [] - selectedGroupIds.value.forEach((groupId) => { - const group = groupList.value.find((g) => g.id === groupId) - if (group && group.workerList && Array.isArray(group.workerList)) { - const hasPerson = group.workerList.some((w) => w.id === personId) - if (hasPerson) { - groupsContainingPerson.push(groupId) - } - } - }) - - // 如果人员既在直接选择列表中,又在分组中,只移除直接选择,保留分组 - if (isDirectSelected) { - directSelectedPersonIds.value = directSelectedPersonIds.value.filter( - (id) => id !== personId, - ) - } else if (groupsContainingPerson.length > 0) { - // 如果人员只来自分组(不在直接选择列表中),移除对应的分组 - selectedGroupIds.value = selectedGroupIds.value.filter( - (id) => !groupsContainingPerson.includes(id), - ) - nextTick(() => { - syncGroupTableSelection() - }) - } - + directSelectedPersonIds.value = directSelectedPersonIds.value.filter((id) => id !== personId) updateSelectedPersons() - // 同步人员表格 nextTick(() => { syncPersonTableSelection() @@ -461,15 +449,18 @@ const handleRemovePerson = (personId) => { // 清空所有选择 const handleClearAll = () => { isSyncing.value = true - directSelectedPersonIds.value = [] - selectedGroupIds.value = [] - emit('update:modelValue', []) - if (personTableRef.value) { - personTableRef.value.clearSelection() - } - if (groupTableRef.value) { - groupTableRef.value.clearSelection() + if (activeTab.value === 'person') { + directSelectedPersonIds.value = [] + if (personTableRef.value) { + personTableRef.value.clearSelection() + } + } else { + selectedGroupIds.value = [] + if (groupTableRef.value) { + groupTableRef.value.clearSelection() + } } + updateSelectedPersons() nextTick(() => { isSyncing.value = false }) @@ -495,7 +486,11 @@ const getGroupList = async () => { try { const result = await getAllGroupAPI() if (result.code === 200 && result.rows) { - groupList.value = result.rows || [] + // 统一分组数据格式:将 workerName 转换为 groupName + groupList.value = (result.rows || []).map((group) => ({ + ...group, + groupName: group.groupName || group.workerName || '', + })) nextTick(() => { syncGroupTableSelection() }) @@ -505,37 +500,99 @@ const getGroupList = async () => { } } -// 监听 modelValue 变化,仅在初始化时同步(避免干扰用户操作) +// 监听 modelValue 变化 watch( () => props.modelValue, (newVal) => { - // 如果内部状态已经有数据,说明是用户操作导致的,不需要反向同步 - // 只在初始化时(两个列表都为空)才同步 - if (directSelectedPersonIds.value.length > 0 || selectedGroupIds.value.length > 0) { - return - } - if (!newVal || newVal.length === 0) { - directSelectedPersonIds.value = [] + // 如果外部数据为空,且内部状态也为空,则不需要处理 + if (directSelectedPersonIds.value.length === 0) { + return + } + // 如果外部数据为空,但内部状态有值,且未初始化过,则清空 + if (!isInitialized.value) { + directSelectedPersonIds.value = [] + } return } - // 初始化时,尝试区分哪些是直接选择的人员 - // 由于此时还没有分组数据,先假设所有人员都是直接选择的 - // 等分组数据加载后,会通过 updateSelectedPersons 重新计算 - const personIds = newVal.map((p) => p.id).filter(Boolean) - directSelectedPersonIds.value = personIds + // 获取外部传入的人员ID列表 + const externalPersonIds = newVal.map((p) => p.id).filter(Boolean) - if (activeTab.value === 'person') { - nextTick(() => { - syncPersonTableSelection() - }) + // 如果外部数据与内部状态一致,不需要更新 + const currentIds = new Set(directSelectedPersonIds.value.map((id) => Number(id))) + const externalIds = new Set(externalPersonIds.map((id) => Number(id))) + const isEqual = + currentIds.size === externalIds.size && + [...currentIds].every((id) => externalIds.has(id)) + + if (isEqual && isInitialized.value) { + return + } + + // 更新选中的人员ID(仅在未初始化或外部数据变化时) + if (!isInitialized.value || !isEqual) { + directSelectedPersonIds.value = externalPersonIds + isInitialized.value = true + + if (activeTab.value === 'person') { + nextTick(() => { + syncPersonTableSelection() + }) + } + } + }, + { deep: true, immediate: true }, +) + +// 监听 groupList 变化 +watch( + () => props.groupList, + (newVal) => { + if (!newVal || newVal.length === 0) { + // 如果外部数据为空,且内部状态也为空,则不需要处理 + if (selectedGroupIds.value.length === 0) { + return + } + // 如果外部数据为空,但内部状态有值,且未初始化过,则清空 + if (!isInitialized.value) { + selectedGroupIds.value = [] + } + return + } + + // 获取外部传入的分组ID列表 + const externalGroupIds = newVal.map((g) => g.id).filter(Boolean) + + // 如果外部数据与内部状态一致,不需要更新 + const currentIds = new Set(selectedGroupIds.value.map((id) => Number(id))) + const externalIds = new Set(externalGroupIds.map((id) => Number(id))) + const isEqual = + currentIds.size === externalIds.size && + [...currentIds].every((id) => externalIds.has(id)) + + if (isEqual && isInitialized.value) { + return + } + + // 更新选中的分组ID(仅在未初始化或外部数据变化时) + if (!isInitialized.value || !isEqual) { + selectedGroupIds.value = externalGroupIds + isInitialized.value = true + + if (activeTab.value === 'group') { + nextTick(() => { + syncGroupTableSelection() + }) + } } }, { deep: true, immediate: true }, ) onMounted(() => { + // 重置初始化标志,确保能正确同步外部传入的数据 + isInitialized.value = false getPersonList() getGroupList() }) diff --git a/src/views/index.vue b/src/views/index.vue index c56ce7a..6951d87 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -33,14 +33,21 @@