完成品牌删除模块
This commit is contained in:
@@ -240,6 +240,7 @@ import {
|
||||
deleteDeleteBrandHistory,
|
||||
getDeleteBrandHistory,
|
||||
getDeleteBrandTaskDetail,
|
||||
getDeleteBrandTaskDetails,
|
||||
getDeleteBrandTaskDownloadUrl,
|
||||
runDeleteBrand,
|
||||
type DeleteBrandPreviewRow,
|
||||
@@ -260,6 +261,7 @@ const queuePushResult = ref('')
|
||||
const queuePayloadText = ref('')
|
||||
const taskDetails = ref<Record<number, DeleteBrandTaskDetailVo>>({})
|
||||
const pollTimer = ref<number | null>(null)
|
||||
const pollingInFlight = ref(false)
|
||||
const displayPaths = computed(() => selectedPaths.value.slice(0, 10))
|
||||
const currentSectionItems = computed(() => currentRunItems.value)
|
||||
const historySectionItems = computed(() =>
|
||||
@@ -276,7 +278,7 @@ const hasVisibleItems = computed(() => currentSectionItems.value.length > 0 || h
|
||||
|
||||
function normalizeDeleteBrandItems(items: DeleteBrandResultItem[]) {
|
||||
return items.map((item) => {
|
||||
if (!item.matched) {
|
||||
if (item.matched === false) {
|
||||
return {
|
||||
...item,
|
||||
success: false,
|
||||
@@ -351,6 +353,22 @@ function isTaskRunning(taskId: number) {
|
||||
return status === 'RUNNING'
|
||||
}
|
||||
|
||||
function isTaskTerminal(taskId: number) {
|
||||
const status = taskDetails.value[taskId]?.task?.status
|
||||
return status === 'SUCCESS' || status === 'FAILED'
|
||||
}
|
||||
|
||||
function getPollingTaskIds() {
|
||||
return Array.from(
|
||||
new Set(
|
||||
currentRunItems.value
|
||||
.filter((item) => item.matched && (item.taskId || 0) > 0)
|
||||
.map(getItemTaskId)
|
||||
.filter((id) => id > 0 && !isTaskTerminal(id)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
function canDownloadTaskResult(item: DeleteBrandResultItem) {
|
||||
const taskId = item.taskId
|
||||
if (!taskId || !item.matched) return false
|
||||
@@ -358,32 +376,61 @@ function canDownloadTaskResult(item: DeleteBrandResultItem) {
|
||||
return status === 'SUCCESS'
|
||||
}
|
||||
|
||||
async function refreshTaskDetails() {
|
||||
const taskIds = Array.from(new Set(resultItems.value.map(getItemTaskId).filter((id) => id > 0)))
|
||||
for (const id of taskIds) {
|
||||
try {
|
||||
const detail = await getDeleteBrandTaskDetail(id)
|
||||
taskDetails.value[id] = detail
|
||||
} catch {
|
||||
// ignore polling failures
|
||||
async function refreshTaskDetails(taskIds?: number[]) {
|
||||
const ids = taskIds || getPollingTaskIds()
|
||||
if (!ids.length) return
|
||||
|
||||
try {
|
||||
const batch = await getDeleteBrandTaskDetails(ids)
|
||||
for (const detail of batch.items || []) {
|
||||
const id = detail?.task?.id
|
||||
if (typeof id === 'number' && id > 0) {
|
||||
taskDetails.value[id] = detail
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore polling failures
|
||||
}
|
||||
}
|
||||
|
||||
function getPollIntervalMs() {
|
||||
return document.visibilityState === 'visible' ? 4500 : 10000
|
||||
}
|
||||
|
||||
function scheduleNextPoll() {
|
||||
if (pollTimer.value) return
|
||||
pollTimer.value = window.setTimeout(async () => {
|
||||
pollTimer.value = null
|
||||
if (pollingInFlight.value) {
|
||||
scheduleNextPoll()
|
||||
return
|
||||
}
|
||||
|
||||
const taskIds = getPollingTaskIds()
|
||||
if (!taskIds.length) return
|
||||
|
||||
pollingInFlight.value = true
|
||||
try {
|
||||
await refreshTaskDetails(taskIds)
|
||||
} finally {
|
||||
pollingInFlight.value = false
|
||||
}
|
||||
|
||||
const stillRunning = taskIds.some((id) => isTaskRunning(id))
|
||||
if (stillRunning) {
|
||||
scheduleNextPoll()
|
||||
}
|
||||
}, getPollIntervalMs())
|
||||
}
|
||||
|
||||
function ensurePolling() {
|
||||
if (pollTimer.value) return
|
||||
pollTimer.value = window.setInterval(async () => {
|
||||
await refreshTaskDetails()
|
||||
const stillRunning = Object.keys(taskDetails.value).some((id) => isTaskRunning(Number(id)))
|
||||
if (!stillRunning) {
|
||||
stopPolling()
|
||||
}
|
||||
}, 1500)
|
||||
scheduleNextPoll()
|
||||
}
|
||||
|
||||
function stopPolling() {
|
||||
if (pollTimer.value) {
|
||||
window.clearInterval(pollTimer.value)
|
||||
window.clearTimeout(pollTimer.value)
|
||||
pollTimer.value = null
|
||||
}
|
||||
}
|
||||
@@ -464,7 +511,7 @@ async function submitRun() {
|
||||
currentRunItems.value = normalizedItems
|
||||
syncResultState()
|
||||
|
||||
await refreshTaskDetails()
|
||||
await refreshTaskDetails(getPollingTaskIds())
|
||||
ensurePolling()
|
||||
|
||||
const api = getPywebviewApi()
|
||||
@@ -537,13 +584,39 @@ async function loadHistory() {
|
||||
}
|
||||
}
|
||||
|
||||
function removeRecordFromView(resultId: number) {
|
||||
const removedFromCurrent = currentRunItems.value.find((item) => item.resultId === resultId)
|
||||
const removedTaskId = removedFromCurrent?.taskId
|
||||
|
||||
currentRunItems.value = currentRunItems.value.filter((item) => item.resultId !== resultId)
|
||||
historyItems.value = historyItems.value.filter((item) => item.resultId !== resultId)
|
||||
|
||||
if (removedTaskId && !currentRunItems.value.some((item) => item.taskId === removedTaskId)) {
|
||||
delete taskDetails.value[removedTaskId]
|
||||
}
|
||||
|
||||
syncResultState()
|
||||
|
||||
if (getPollingTaskIds().length === 0) {
|
||||
stopPolling()
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteHistoryRecord(resultId: number) {
|
||||
try {
|
||||
await deleteDeleteBrandHistory(resultId)
|
||||
removeRecordFromView(resultId)
|
||||
await loadHistory()
|
||||
ElMessage.success('已删除')
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '删除失败')
|
||||
const message = error instanceof Error ? error.message : '删除失败'
|
||||
// 后端可能已被清理/重复点击导致「记录不存在」,前端也应立即移除并停止轮询
|
||||
if (typeof message === 'string' && message.includes('记录不存在')) {
|
||||
removeRecordFromView(resultId)
|
||||
ElMessage.success('已删除')
|
||||
return
|
||||
}
|
||||
ElMessage.error(message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -205,12 +205,17 @@ export interface DeleteBrandTaskDetailVo {
|
||||
line_progress: DeleteBrandLineProgress
|
||||
}
|
||||
|
||||
export interface DeleteBrandTaskBatchVo {
|
||||
items: DeleteBrandTaskDetailVo[]
|
||||
missingTaskIds: number[]
|
||||
}
|
||||
|
||||
export interface DeleteBrandResultItem {
|
||||
resultId?: number
|
||||
fileKey?: string
|
||||
sourceFilename: string
|
||||
shopName?: string
|
||||
matched: boolean
|
||||
matched?: boolean
|
||||
shopId?: string
|
||||
platform?: string
|
||||
openStoreUrl?: string
|
||||
@@ -365,6 +370,10 @@ export function getDeleteBrandTaskDetail(taskId: number) {
|
||||
return unwrapJavaResponse(get<JavaApiResponse<DeleteBrandTaskDetailVo>>(`${JAVA_API_PREFIX}/delete-brand/tasks/${taskId}`))
|
||||
}
|
||||
|
||||
export function getDeleteBrandTaskDetails(taskIds: number[]) {
|
||||
return unwrapJavaResponse(post<JavaApiResponse<DeleteBrandTaskBatchVo>, { taskIds: number[] }>(`${JAVA_API_PREFIX}/delete-brand/tasks/batch`, { taskIds }))
|
||||
}
|
||||
|
||||
export function getDeleteBrandTaskDownloadUrl(taskId: number) {
|
||||
return getJavaDownloadUrl(`/delete-brand/tasks/${taskId}/download`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user