import { computed, ref } from "vue"; import { isRequestCancelled } from "@/services/api/request-controller.js"; import { getRequestErrorMessage } from "@/services/api/request-error-message.js"; export const useBatchDeletion = ({ items, visibleItems = items, getId = (item) => item?.id, canDelete = (item) => item?.canDelete === true, deleteOne, resourceName, isActive = () => true, onEmpty = () => {}, }) => { const selectionMode = ref(false); const selectedIds = ref([]); const confirmationVisible = ref(false); const deleting = ref(false); const notice = ref(""); const error = ref(""); const deletableItems = computed(() => visibleItems.value.filter(canDelete)); const allSelected = computed( () => deletableItems.value.length > 0 && deletableItems.value.every((item) => selectedIds.value.includes(getId(item))), ); const selectedCount = computed(() => selectedIds.value.length); const confirmationMessage = computed( () => `将选中的 ${selectedCount.value} 条${resourceName}移入回收站;管理员可在保留期内恢复。`, ); const clearFeedback = () => { notice.value = ""; error.value = ""; }; const exitSelectionMode = () => { selectionMode.value = false; selectedIds.value = []; confirmationVisible.value = false; }; const enterSelectionMode = () => { if (!deletableItems.value.length || deleting.value) return; clearFeedback(); selectedIds.value = []; selectionMode.value = true; }; const isSelected = (item) => selectedIds.value.includes(getId(item)); const toggleSelection = (item) => { if (!selectionMode.value || !canDelete(item) || deleting.value) return; const id = getId(item); selectedIds.value = isSelected(item) ? selectedIds.value.filter((selectedId) => selectedId !== id) : [...selectedIds.value, id]; clearFeedback(); }; const toggleAll = () => { if (!selectionMode.value || deleting.value) return; selectedIds.value = allSelected.value ? [] : deletableItems.value.map(getId); clearFeedback(); }; const requestDelete = () => { if (!selectionMode.value || !selectedIds.value.length || deleting.value) return; clearFeedback(); confirmationVisible.value = true; }; const cancelDelete = () => { if (!deleting.value) confirmationVisible.value = false; }; const confirmDelete = async () => { if (!selectionMode.value || !selectedIds.value.length || deleting.value) return; const pendingIds = selectedIds.value.slice(); deleting.value = true; clearFeedback(); let deletedCount = 0; let failedRequest = null; try { for (const id of pendingIds) { const target = items.value.find((item) => getId(item) === id); if (!target || !canDelete(target)) continue; try { await deleteOne(target); } catch (cause) { failedRequest = cause; break; } if (!isActive()) return; deletedCount += 1; items.value = items.value.filter((item) => getId(item) !== id); selectedIds.value = selectedIds.value.filter((selectedId) => selectedId !== id); } if (!isActive()) return; confirmationVisible.value = false; if (failedRequest && !isRequestCancelled(failedRequest)) { const failureCopy = getRequestErrorMessage( failedRequest, `剩余${resourceName}删除失败,请稍后重试。`, ); error.value = deletedCount ? `已删除 ${deletedCount} 条;${failureCopy}` : failureCopy; } else if (deletedCount) { notice.value = `已删除 ${deletedCount} 条${resourceName}。`; } if (!items.value.length) { exitSelectionMode(); onEmpty(); } else if (!selectedIds.value.length) { selectionMode.value = false; } } finally { if (isActive()) deleting.value = false; } }; return { selectionMode, selectedIds, selectedCount, deletableItems, allSelected, confirmationVisible, confirmationMessage, deleting, notice, error, enterSelectionMode, exitSelectionMode, isSelected, toggleSelection, toggleAll, requestDelete, cancelDelete, confirmDelete, }; };