完成菜单权限添加和软件菜单改造
This commit is contained in:
@@ -54,6 +54,10 @@
|
||||
<button type="button" class="btn-run" :disabled="running" @click="submitRun">
|
||||
{{ running ? '解析中...' : '开始解析' }}
|
||||
</button>
|
||||
<button type="button" class="btn-run btn-queue" :disabled="pushing || !queueRunnableItems.length"
|
||||
@click="pushToPythonQueue">
|
||||
{{ pushing ? '推送中...' : '推送到 Python 队列' }}
|
||||
</button>
|
||||
<span class="loading-msg">
|
||||
{{ running ? '正在读取删除品牌数据,请稍候…' : '解析后会在右侧展示按国家分组的去重结果' }}
|
||||
</span>
|
||||
@@ -133,9 +137,6 @@
|
||||
<span class="status" :class="getTaskStatusInfo(item).className">
|
||||
{{ getTaskStatusInfo(item).text }}
|
||||
</span>
|
||||
<button v-if="item.openStoreUrl" type="button" class="download" @click="triggerItemRun(item)">
|
||||
打开紫鸟
|
||||
</button>
|
||||
<button v-if="canDownloadTaskResult(item)" type="button" class="download"
|
||||
@click="downloadTaskResult(item)">
|
||||
下载
|
||||
@@ -254,9 +255,11 @@ const queuePayloadText = ref('')
|
||||
const taskDetails = ref<Record<number, DeleteBrandTaskDetailVo>>({})
|
||||
const pollTimer = ref<number | null>(null)
|
||||
const pollingInFlight = ref(false)
|
||||
const pushing = ref(false)
|
||||
const chainStarted = ref(false)
|
||||
const activeItemKey = ref('')
|
||||
const autoAdvancing = ref(false)
|
||||
const autoRetryTimer = ref<number | null>(null)
|
||||
const displayPaths = computed(() => selectedPaths.value.slice(0, 10))
|
||||
|
||||
const currentSectionItems = computed(() => {
|
||||
@@ -375,10 +378,14 @@ const historySectionItems = computed(() =>
|
||||
),
|
||||
);
|
||||
const hasVisibleItems = computed(() => currentSectionItems.value.length > 0 || historySectionItems.value.length > 0)
|
||||
const queueRunnableItems = computed(() =>
|
||||
currentSectionItems.value.filter((item) => canPushToQueue(item) && getQueueStatus(item) === '未入队'),
|
||||
)
|
||||
|
||||
function persistSessionTasks() {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.localStorage.setItem(getStorageKey(), JSON.stringify(sessionTasks.value))
|
||||
if (sessionTasks.value.length === 0) window.localStorage.removeItem(getStorageKey())
|
||||
else window.localStorage.setItem(getStorageKey(), JSON.stringify(sessionTasks.value))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,7 +497,7 @@ function shouldShowProgress(item: DeleteBrandResultItem) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return { success: false, retryable: true }
|
||||
}
|
||||
|
||||
// 单文件任务:完成/终态后隐藏。
|
||||
@@ -773,7 +780,7 @@ function canDownloadTaskResult(item: DeleteBrandResultItem) {
|
||||
if (item.downloadUrl) return true
|
||||
|
||||
const taskId = item.taskId
|
||||
if (!taskId) return false
|
||||
if (!taskId) return { success: false, retryable: false }
|
||||
const status = taskDetails.value[taskId]?.task?.status
|
||||
return status === 'SUCCESS'
|
||||
}
|
||||
@@ -782,7 +789,7 @@ function mergeTaskDetailItemsIntoSession(detail: DeleteBrandTaskDetailVo) {
|
||||
const taskId = detail?.task?.id
|
||||
if (!taskId) return false
|
||||
const sessionTask = sessionTasks.value.find(t => t.taskId === taskId)
|
||||
if (!sessionTask) return false
|
||||
if (!sessionTask) return { success: false, retryable: false }
|
||||
|
||||
const detailItems = detail?.items || []
|
||||
let changed = false
|
||||
@@ -953,6 +960,7 @@ function stopPolling() {
|
||||
window.clearTimeout(pollTimer.value)
|
||||
pollTimer.value = null
|
||||
}
|
||||
clearAutoRetryTimer()
|
||||
}
|
||||
|
||||
async function uploadPathsToJava(paths: Array<string | BrandExpandFolderItem>) {
|
||||
@@ -1031,7 +1039,9 @@ async function submitRun() {
|
||||
const hasMatchedItems = normalizedItems.some((item) => item.matchStatus === 'MATCHED')
|
||||
const hasPendingItems = normalizedItems.some((item) => item.matchStatus && item.matchStatus !== 'MATCHED')
|
||||
|
||||
queuePushResult.value = '解析完成,等待手动点击打开紫鸟进行单任务处理'
|
||||
queuePushResult.value = hasMatchedItems
|
||||
? '解析完成,可在左侧点击“推送到 Python 队列”开始串行处理'
|
||||
: '解析完成,当前没有可推送的已匹配文件'
|
||||
queuePayloadText.value = ''
|
||||
|
||||
if (normalizedItems.length && normalizedItems[0].taskId) {
|
||||
@@ -1042,7 +1052,7 @@ async function submitRun() {
|
||||
if (hasPendingItems) {
|
||||
ElMessage.warning('部分文件尚未命中可用店铺索引,已保留状态信息,请等待后台刷新后重试。')
|
||||
} else if (hasMatchedItems) {
|
||||
ElMessage.success('删除品牌解析完成,请手动推送')
|
||||
ElMessage.success('删除品牌解析完成,请在左侧推送到 Python 队列')
|
||||
} else {
|
||||
ElMessage.warning('当前没有可推送的已匹配文件,请先等待店铺索引刷新。')
|
||||
}
|
||||
@@ -1143,6 +1153,26 @@ function debugAutoAdvance(step: string, payload?: Record<string, unknown>) {
|
||||
console.log(`[DeleteBrandAutoAdvance] ${step}`, payload || {})
|
||||
}
|
||||
|
||||
function clearAutoRetryTimer() {
|
||||
if (autoRetryTimer.value) {
|
||||
window.clearTimeout(autoRetryTimer.value)
|
||||
autoRetryTimer.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleAutoAdvanceRetry(delayMs = 3000) {
|
||||
clearAutoRetryTimer()
|
||||
autoRetryTimer.value = window.setTimeout(() => {
|
||||
autoRetryTimer.value = null
|
||||
maybeAutoAdvance().catch(() => undefined)
|
||||
}, delayMs)
|
||||
}
|
||||
|
||||
function isQueueBusyError(message?: string) {
|
||||
if (!message) return false
|
||||
return message.includes('当前店铺正在执行中')
|
||||
}
|
||||
|
||||
function isPythonQueueBusy() {
|
||||
for (const task of sessionTasks.value) {
|
||||
for (const item of task.items) {
|
||||
@@ -1160,9 +1190,13 @@ function getItemKey(item: DeleteBrandResultItem) {
|
||||
return `task:${item.taskId || 0}:${item.sourceFilename || ''}`
|
||||
}
|
||||
|
||||
function canPushToQueue(item: DeleteBrandResultItem) {
|
||||
return Boolean(item.taskId && (item.matchStatus === 'MATCHED' || item.matched))
|
||||
}
|
||||
|
||||
function findNextAutoRunnableItem() {
|
||||
return currentSectionItems.value.find((item) => {
|
||||
if (!item.openStoreUrl) return false
|
||||
if (!canPushToQueue(item)) return false
|
||||
if (getItemKey(item) === activeItemKey.value) return false
|
||||
const sessionItem = findSessionItem(item.taskId, item)
|
||||
if (sessionItem?._completed) return false
|
||||
@@ -1177,20 +1211,20 @@ async function runItem(item: DeleteBrandResultItem, options?: { auto?: boolean }
|
||||
taskId: item.taskId,
|
||||
sourceFilename: item.sourceFilename,
|
||||
})
|
||||
return false
|
||||
return { success: false, retryable: true }
|
||||
}
|
||||
|
||||
const api = getPywebviewApi()
|
||||
|
||||
const taskId = item.taskId
|
||||
if (!taskId) return false
|
||||
if (!taskId) return { success: false, retryable: false }
|
||||
|
||||
const sessionTask = sessionTasks.value.find(t => t.taskId === taskId)
|
||||
if (!sessionTask) return false
|
||||
if (!sessionTask) return { success: false, retryable: false }
|
||||
|
||||
if (!api?.enqueue_json) {
|
||||
ElMessage.error('当前环境未启用 pywebview enqueue_json(浏览器环境不会推送)')
|
||||
return false
|
||||
return { success: false, retryable: false }
|
||||
}
|
||||
|
||||
const payload = {
|
||||
@@ -1216,11 +1250,39 @@ async function runItem(item: DeleteBrandResultItem, options?: { auto?: boolean }
|
||||
activeItemKey.value = getItemKey(item)
|
||||
saveSessionTask(taskId, sessionTask.items, qpr, queuePayloadText.value)
|
||||
ensurePolling(true)
|
||||
queuePushResult.value = qpr
|
||||
return { success: true, retryable: false }
|
||||
} else {
|
||||
qpr = `推送到 Python 队列失败:${pushResult?.error || '未知错误'}`
|
||||
const message = pushResult?.error || '未知错误'
|
||||
qpr = `推送到 Python 队列失败:${message}`
|
||||
queuePushResult.value = qpr
|
||||
saveSessionTask(taskId, sessionTask.items, qpr, queuePayloadText.value)
|
||||
if (options?.auto && isQueueBusyError(message)) {
|
||||
return { success: false, retryable: true }
|
||||
}
|
||||
}
|
||||
queuePushResult.value = qpr
|
||||
return Boolean(pushResult?.success)
|
||||
return { success: false, retryable: false }
|
||||
}
|
||||
|
||||
async function pushToPythonQueue() {
|
||||
const nextItem = findNextAutoRunnableItem()
|
||||
if (!nextItem) {
|
||||
ElMessage.warning('当前没有可推送的已匹配文件')
|
||||
return
|
||||
}
|
||||
|
||||
pushing.value = true
|
||||
chainStarted.value = true
|
||||
clearAutoRetryTimer()
|
||||
const started = await runItem(nextItem)
|
||||
pushing.value = false
|
||||
if (!started.success) {
|
||||
chainStarted.value = false
|
||||
activeItemKey.value = ''
|
||||
return
|
||||
}
|
||||
ElMessage.success('已开始按顺序推送到 Python 队列')
|
||||
}
|
||||
|
||||
async function maybeAutoAdvance() {
|
||||
@@ -1294,7 +1356,15 @@ async function maybeAutoAdvance() {
|
||||
nextKey: getItemKey(nextItem),
|
||||
})
|
||||
const started = await runItem(nextItem, { auto: true })
|
||||
if (!started) {
|
||||
if (!started.success) {
|
||||
if (started.retryable) {
|
||||
debugAutoAdvance('delay retry next item', {
|
||||
taskId: nextItem.taskId,
|
||||
sourceFilename: nextItem.sourceFilename,
|
||||
})
|
||||
scheduleAutoAdvanceRetry()
|
||||
return
|
||||
}
|
||||
debugAutoAdvance('stop chain: runItem returned false', {
|
||||
taskId: nextItem.taskId,
|
||||
sourceFilename: nextItem.sourceFilename,
|
||||
@@ -1306,15 +1376,6 @@ async function maybeAutoAdvance() {
|
||||
}
|
||||
}
|
||||
|
||||
async function triggerItemRun(item: DeleteBrandResultItem) {
|
||||
chainStarted.value = true
|
||||
const started = await runItem(item)
|
||||
if (!started) {
|
||||
chainStarted.value = false
|
||||
activeItemKey.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadSessionTasksFromStorage()
|
||||
const taskIds = getPollingTaskIds()
|
||||
|
||||
Reference in New Issue
Block a user