chore: 移除项目内无用代码(28 文件)
- 清理任务面板/SPA 化改造遗留的未使用变量与函数(模板管理配套 6 函数、 payloadForDisplay/showTip/hasRunning/schedulePoll/removeMatchedRowLocally 等 30+ 处) - 清理未使用 import(getBrandTemplateXlsxUrl/uploadTempFileToJava/getProductRiskTasksBatch 等) - useTaskProgressLoop 改为仅类型导入;progress/task-request 缓存未用参数精简 - DeleteBrandRunService 删除永不执行的 taskItems 死分支 - 全量 vue-tsc(含 --noUnusedLocals/--noUnusedParameters)0 错误;648 测试全过
This commit is contained in:
-33
@@ -379,39 +379,6 @@ public class DeleteBrandRunService {
|
||||
FileTaskEntity taskEntity = taskById.get(entity.getTaskId());
|
||||
item.setCreatedAt(fmt(taskEntity == null ? null : taskEntity.getCreatedAt()));
|
||||
item.setFinishedAt(fmt(taskEntity == null ? null : taskEntity.getFinishedAt()));
|
||||
List<DeleteBrandResultItemVo> taskItems = null;
|
||||
if (taskItems != null && entity.getSourceFilename() != null && !entity.getSourceFilename().isBlank()) {
|
||||
for (DeleteBrandResultItemVo candidate : taskItems) {
|
||||
if (candidate == null) {
|
||||
continue;
|
||||
}
|
||||
if (entity.getSourceFilename().equals(candidate.getSourceFilename())) {
|
||||
item.setMatched(candidate.isMatched());
|
||||
item.setMatchStatus(candidate.getMatchStatus());
|
||||
item.setMatchMessage(candidate.getMatchMessage());
|
||||
// 如果物理结果已经成功,说明已经处理过,强制标记为匹配成功
|
||||
if (item.isSuccess() && candidate.getShopId() != null && !candidate.getShopId().isBlank()) {
|
||||
item.setMatched(true);
|
||||
item.setMatchStatus(ZiniaoShopIndexService.MATCH_STATUS_MATCHED);
|
||||
item.setMatchMessage(null);
|
||||
}
|
||||
|
||||
item.setShopId(candidate.getShopId());
|
||||
item.setCompanyName(candidate.getCompanyName());
|
||||
item.setPlatform(candidate.getPlatform());
|
||||
item.setOpenStoreUrl(candidate.getOpenStoreUrl());
|
||||
|
||||
// 历史表里可能只有通用 errorMessage,优先返回任务当时的真实 error
|
||||
// 但如果物理结果已成功,就不应该再显示所谓“未匹配”的报错
|
||||
if (item.isSuccess()) {
|
||||
item.setError(null);
|
||||
} else if ((item.getError() == null || item.getError().isBlank()) && candidate.getError() != null && !candidate.getError().isBlank()) {
|
||||
item.setError(candidate.getError());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}).toList());
|
||||
|
||||
@@ -258,24 +258,6 @@ function effectivePatentToken() {
|
||||
return getStoredApiSecret('appearance-patent-token').trim()
|
||||
}
|
||||
|
||||
function maskSecret(secret: string) {
|
||||
if (!secret) return ''
|
||||
if (secret.length <= 10) return '***'
|
||||
return `${secret.slice(0, 6)}***${secret.slice(-4)}`
|
||||
}
|
||||
|
||||
function payloadForDisplay<T extends { data?: Record<string, unknown> }>(payload: T) {
|
||||
return {
|
||||
...payload,
|
||||
data: payload.data
|
||||
? {
|
||||
...payload.data,
|
||||
api_key: maskSecret(String(payload.data.api_key || '')),
|
||||
patent_token: maskSecret(String(payload.data.patent_token || '')),
|
||||
}
|
||||
: payload.data,
|
||||
}
|
||||
}
|
||||
|
||||
function uidForStorage() {
|
||||
return typeof window !== 'undefined' ? window.localStorage.getItem('uid') || '0' : '0'
|
||||
|
||||
@@ -159,9 +159,6 @@ function baseName(path: string) {
|
||||
|
||||
let pollTimer: number | null = null
|
||||
|
||||
function hasRunning() {
|
||||
return tasks.value.some((item) => isRunning(item))
|
||||
}
|
||||
|
||||
function isRunning(item: BrandTaskItem) {
|
||||
return (item.status || '').toLowerCase() === 'running'
|
||||
@@ -452,19 +449,6 @@ async function downloadTemplate(kind: 'xlsx' | 'zip') {
|
||||
}
|
||||
}
|
||||
|
||||
function schedulePoll() {
|
||||
if (pollTimer) {
|
||||
window.clearTimeout(pollTimer)
|
||||
pollTimer = null
|
||||
}
|
||||
pollTimer = window.setTimeout(async () => {
|
||||
await loadTasks()
|
||||
if (hasRunning()) {
|
||||
schedulePoll()
|
||||
}
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void loadTasks()
|
||||
})
|
||||
|
||||
@@ -264,11 +264,6 @@ const progressLoop = useTaskProgressLoop<CollectDataTaskDetailVo>({
|
||||
|
||||
const displayFileNames = computed(() => selectedFileNames.value.slice(0, 8))
|
||||
|
||||
function countryLabel(code: string) {
|
||||
const row = COUNTRY_OPTIONS.find((o) => o.code === code)
|
||||
return row?.label ?? code
|
||||
}
|
||||
|
||||
function selectCountry(code: string) {
|
||||
if (!COUNTRY_OPTIONS.some((row) => row.code === code)) return
|
||||
if (selectedCountryCode.value === code) return
|
||||
|
||||
@@ -154,17 +154,14 @@ import { ElMessage } from 'element-plus'
|
||||
import AmazonToolPageShell from '@/pages/amazon/components/AmazonToolPageShell.vue';
|
||||
import TaskCenterPanel from '@/shared/components/tasks/TaskCenterPanel.vue'
|
||||
import type { TaskItemView, TaskStatCard } from '@/shared/components/tasks/types'
|
||||
import { expandBrandFolderRecursive, getBrandTemplateXlsxUrl, getBrandTemplateZipUrl } from '@/shared/api/brand'
|
||||
import { expandBrandFolderRecursive } from '@/shared/api/brand'
|
||||
import type { BrandExpandFolderItem } from '@/shared/api/brand'
|
||||
import {
|
||||
deleteConvertHistory,
|
||||
deleteConvertTemplate,
|
||||
getConvertHistory,
|
||||
getConvertResultDownloadUrl,
|
||||
getConvertTemplates,
|
||||
importConvertTemplate,
|
||||
runConvert,
|
||||
setDefaultConvertTemplate,
|
||||
type ConvertResultItem,
|
||||
type ConvertTemplateVo,
|
||||
} from '@/shared/api/java-modules'
|
||||
@@ -180,18 +177,11 @@ const convertRunning = ref(false)
|
||||
const convertResultItems = ref<ConvertResultItem[]>([])
|
||||
const convertTemplates = ref<ConvertTemplateVo[]>([])
|
||||
const convertTemplateId = ref('')
|
||||
const templateUploading = ref(false)
|
||||
const templateSavingDefault = ref(false)
|
||||
const templateDeleting = ref(false)
|
||||
const templateInputRef = ref<HTMLInputElement>()
|
||||
|
||||
const convertDisplayPaths = computed(() => convertSelectedPaths.value.slice(0, 8))
|
||||
const currentConvertTemplate = computed(
|
||||
() => convertTemplates.value.find((item) => item.id === convertTemplateId.value) || null,
|
||||
)
|
||||
const canDeleteCurrentTemplate = computed(
|
||||
() => Boolean(currentConvertTemplate.value && !currentConvertTemplate.value.builtIn),
|
||||
)
|
||||
|
||||
// ---- 右侧任务面板统一视图(TaskCenterPanel)----
|
||||
const convertCards = computed<TaskStatCard[]>(() => [
|
||||
@@ -362,127 +352,6 @@ async function selectConvertFolder() {
|
||||
}
|
||||
}
|
||||
|
||||
function openTemplatePicker() {
|
||||
templateInputRef.value?.click()
|
||||
}
|
||||
|
||||
async function handleTemplateFileChange(event: Event) {
|
||||
const input = event.target as HTMLInputElement
|
||||
const file = input.files?.[0]
|
||||
input.value = ''
|
||||
|
||||
if (!file) {
|
||||
return
|
||||
}
|
||||
|
||||
// 浏览器侧直接读内容的唯一入口:扩展名与内容都在这里拦,空模板传上去后
|
||||
// 转换阶段每个文件都会失败,不如在上传时就说清楚。
|
||||
if (!(await passGuard(checkSelectedFiles([file.name], { allowedExtensions: ['.txt'] })))) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
templateUploading.value = true
|
||||
const templateContent = await file.text()
|
||||
if (!templateContent.trim()) {
|
||||
await passGuard(
|
||||
guardBlocked(
|
||||
'模板内容为空',
|
||||
`${file.name} 里没有任何内容。\n空模板无法用于格式转换,请填好列映射后重新上传。`,
|
||||
'convert.empty-template',
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
const templateName = file.name.replace(/\.[^.]+$/, '') || file.name
|
||||
|
||||
const template = await importConvertTemplate({
|
||||
templateName,
|
||||
templateContent,
|
||||
})
|
||||
|
||||
await loadConvertTemplates(template.id)
|
||||
ElMessage.success(`模板已上传:${template.templateName}`)
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '模板上传失败')
|
||||
} finally {
|
||||
templateUploading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function saveCurrentTemplateAsDefault() {
|
||||
if (!currentConvertTemplate.value) {
|
||||
ElMessage.warning('请先选择模板')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
templateSavingDefault.value = true
|
||||
await setDefaultConvertTemplate(currentConvertTemplate.value.templateCode || currentConvertTemplate.value.id)
|
||||
await loadConvertTemplates(currentConvertTemplate.value.id)
|
||||
ElMessage.success('默认模板已更新')
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '设置默认模板失败')
|
||||
} finally {
|
||||
templateSavingDefault.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function removeCurrentTemplate() {
|
||||
if (!currentConvertTemplate.value) {
|
||||
ElMessage.warning('请先选择模板')
|
||||
return
|
||||
}
|
||||
if (currentConvertTemplate.value.builtIn) {
|
||||
ElMessage.warning('内置模板不允许删除')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
templateDeleting.value = true
|
||||
await deleteConvertTemplate(currentConvertTemplate.value.templateCode || currentConvertTemplate.value.id)
|
||||
const deletedTemplateId = currentConvertTemplate.value.id
|
||||
await loadConvertTemplates()
|
||||
if (convertTemplateId.value === deletedTemplateId) {
|
||||
convertTemplateId.value = convertTemplates.value[0]?.id || ''
|
||||
}
|
||||
ElMessage.success('模板已删除')
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '删除模板失败')
|
||||
} finally {
|
||||
templateDeleting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function saveTemplateFromUrl(url: string, fallbackFilename: string, bridgeMethod?: 'save_template_xlsx' | 'save_template_zip') {
|
||||
const api = getPywebviewApi()
|
||||
|
||||
if (bridgeMethod && api?.[bridgeMethod]) {
|
||||
const result = await api[bridgeMethod]!()
|
||||
if (result.success) {
|
||||
ElMessage.success(`已保存:${result.path || fallbackFilename}`)
|
||||
} else if (result.error && result.error !== '用户取消') {
|
||||
ElMessage.error(result.error)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const result = await saveUrlWithProgress(url, fallbackFilename)
|
||||
if (result.success) {
|
||||
ElMessage.success(`已保存:${result.path || fallbackFilename}`)
|
||||
} else if (result.error && result.error !== '用户取消') {
|
||||
ElMessage.error(result.error)
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadTemplateXlsx() {
|
||||
await saveTemplateFromUrl(getBrandTemplateXlsxUrl(), '品牌文档格式_模板.xlsx', 'save_template_xlsx')
|
||||
}
|
||||
|
||||
async function downloadTemplateZip() {
|
||||
await saveTemplateFromUrl(getBrandTemplateZipUrl(), '模板2-以文件夹方式上传.zip', 'save_template_zip')
|
||||
}
|
||||
|
||||
async function submitConvertRun() {
|
||||
if (!convertUploadedFiles.value.length) {
|
||||
ElMessage.warning('请先选择待转换 Excel 文件或文件夹')
|
||||
|
||||
@@ -119,7 +119,6 @@ import {
|
||||
deleteDeleteBrandHistory,
|
||||
getDeleteBrandHistory,
|
||||
getDeleteBrandResultDownloadUrl,
|
||||
getDeleteBrandTaskDetails,
|
||||
getDeleteBrandTaskProgress,
|
||||
getDeleteBrandTaskDownloadUrl,
|
||||
runDeleteBrand,
|
||||
|
||||
@@ -213,7 +213,6 @@ import ModuleTemplateDownload from '@/shared/components/ModuleTemplateDownload.v
|
||||
import ZiniaoVersionSetting from '@/shared/components/ZiniaoVersionSetting.vue'
|
||||
import TaskCenterPanel from '@/shared/components/tasks/TaskCenterPanel.vue'
|
||||
import type { TaskItemView, TaskStatCard } from '@/shared/components/tasks/types'
|
||||
import { LISTING_FILTER_OPTIONS, type ListingFilterValue } from '@/pages/brand/components/listingFilters'
|
||||
import { expandBrandFolderRecursive } from '@/shared/api/brand'
|
||||
import { getPywebviewApi, type PywebviewApi, type UploadedJavaFile } from '@/shared/bridges/pywebview'
|
||||
import { getTaskPollIntervalMs } from '@/shared/task-progress-config'
|
||||
@@ -242,7 +241,6 @@ import {
|
||||
getPriceTrackLoopRun,
|
||||
getPriceTrackResultDownloadUrl,
|
||||
getPriceTrackTaskProgressBatch,
|
||||
getPriceTrackTasksBatch,
|
||||
getTaskSkipPriceAsinsPaginated,
|
||||
listPriceTrackCandidates,
|
||||
matchPriceTrackShops,
|
||||
@@ -942,88 +940,6 @@ async function removeMatchedRow(row: PriceTrackShopQueueItem) {
|
||||
}
|
||||
}
|
||||
|
||||
async function pushToPythonQueueLegacy() {
|
||||
if (!hasValidMode.value) {
|
||||
ElMessage.warning('请至少选择一个跟价模式')
|
||||
return
|
||||
}
|
||||
if (asinModeEnabled.value && !asinFiles.value.length) {
|
||||
ElMessage.warning('请先选择 ASIN 文件')
|
||||
return
|
||||
}
|
||||
const api = getPywebviewApi()
|
||||
if (!api?.enqueue_json) {
|
||||
ElMessage.error('当前环境未启用 pywebview enqueue_json')
|
||||
return
|
||||
}
|
||||
const matchedRows = matchedItems.value.filter((i) => i.matched)
|
||||
if (!matchedRows.length) {
|
||||
ElMessage.warning('无可用匹配店铺')
|
||||
return
|
||||
}
|
||||
pushing.value = true
|
||||
queuePayloadText.value = ''
|
||||
try {
|
||||
// 1. 先调用 Java API 创建任务(落库,含 skip ASIN)
|
||||
const taskReq = {
|
||||
userId: 0, // 会被 java-modules 自动填充
|
||||
statusMode: statusModeEnabled.value,
|
||||
asinMode: asinModeEnabled.value,
|
||||
items: matchedRows as unknown as Record<string, unknown>[],
|
||||
asinFiles: resolveAsinRequestPaths(),
|
||||
countryCodes: resolveCountryCodesForRequest(),
|
||||
}
|
||||
const taskVo = await createPriceTrackTask(taskReq)
|
||||
taskSnapshots.value = {
|
||||
...taskSnapshots.value,
|
||||
[taskVo.taskId]: {
|
||||
task: { id: taskVo.taskId, status: 'RUNNING' },
|
||||
items: taskVo.items,
|
||||
},
|
||||
}
|
||||
taskDetails.value = {
|
||||
...taskDetails.value,
|
||||
[taskVo.taskId]: 'RUNNING',
|
||||
}
|
||||
saveTaskSnapshotsToStorage()
|
||||
saveTaskDetailsToStorage()
|
||||
|
||||
const firstRow = matchedRows[0]
|
||||
const asinRowsByCountry = await loadAsinRowsForAppClient(taskVo)
|
||||
const minimumPriceByCountryAndAsin = buildMinimumPriceMapForAppClient(taskVo, asinRowsByCountry)
|
||||
|
||||
// 2. 推送 app_client 当前消费的字段
|
||||
const queuePayload = {
|
||||
type: 'price-track-run',
|
||||
ts: Date.now(),
|
||||
data: {
|
||||
task_id: taskVo.taskId,
|
||||
ziniao_version: ziniaoVersion.value,
|
||||
shop_name: (firstRow.shopName || '').trim(),
|
||||
shopName: (firstRow.shopName || '').trim(),
|
||||
companyName: firstRow.companyName || '',
|
||||
shopMallName: firstRow.shopMallName || '',
|
||||
country_codes: resolveCountryCodesForRequest(),
|
||||
mode: priceTrackModeForAppClient(),
|
||||
asin_rows_by_country: asinRowsByCountry,
|
||||
minimum_price_by_country_and_asin: minimumPriceByCountryAndAsin,
|
||||
},
|
||||
}
|
||||
queuePayloadText.value = JSON.stringify(queuePayload, null, 2)
|
||||
|
||||
await enqueueCreatedTask(api, taskVo.taskId, queuePayload)
|
||||
queuePushResult.value = `任务 ${taskVo.taskId} 已入队,等待执行完成...`
|
||||
addPollingTask(taskVo.taskId)
|
||||
scheduleNextPoll(true)
|
||||
ElMessage.success('已启动任务')
|
||||
} catch (e) {
|
||||
queuePushResult.value = e instanceof Error ? e.message : '推送异常'
|
||||
ElMessage.error(queuePushResult.value)
|
||||
} finally {
|
||||
pushing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 任务状态轮询 ==========
|
||||
async function buildQueuePayload(taskVo: PriceTrackCreateTaskVo, row: PriceTrackShopQueueItem) {
|
||||
const shopName = (row.shopName || '').trim()
|
||||
@@ -1155,11 +1071,6 @@ async function waitForTaskTerminal(taskId: number) {
|
||||
}
|
||||
}
|
||||
|
||||
async function pushToPythonQueue() {
|
||||
autoQueueEnabled.value = true
|
||||
await processMatchedQueue()
|
||||
}
|
||||
|
||||
function nextMatchedQueueItem() {
|
||||
return matchedItems.value.find((item) => item.matched)
|
||||
}
|
||||
|
||||
@@ -159,7 +159,6 @@ import {
|
||||
getProductRiskHistory,
|
||||
getProductRiskResultDownloadUrl,
|
||||
getProductRiskTaskProgressBatch,
|
||||
getProductRiskTasksBatch,
|
||||
listProductRiskCandidates,
|
||||
matchProductRiskShops,
|
||||
putProductRiskCountryPreference,
|
||||
|
||||
@@ -452,18 +452,6 @@ function toPublishTaskView(detail: PublishTaskDetailVo): TaskItemView {
|
||||
}
|
||||
}
|
||||
|
||||
const summary = computed(() => {
|
||||
const files = visibleTasks.value.flatMap((detail) => detail.files || [])
|
||||
const successCount = files.filter((file) => ['SUCCESS', 'COMPLETED'].includes(normalizeStatus(file.status))).length
|
||||
const failedCount = files.filter((file) => ['FAILED', 'CANCELLED'].includes(normalizeStatus(file.status))).length
|
||||
return {
|
||||
total: files.length,
|
||||
completed: successCount + failedCount,
|
||||
successCount,
|
||||
failedCount,
|
||||
}
|
||||
})
|
||||
|
||||
const operationHint = computed(() => {
|
||||
if (uploading.value) return '正在逐个上传文件到后端...'
|
||||
if (parsing.value) return '正在解析 Excel、匹配店铺并创建批次...'
|
||||
|
||||
@@ -618,13 +618,6 @@ function isRecordMissingError(error: unknown) {
|
||||
return /记录不存在|任务不存在|不存在|已删除|not\s*found|404/i.test(message);
|
||||
}
|
||||
|
||||
function removeMatchedRowLocally(row: QueryAsinShopQueueItem) {
|
||||
matchedItems.value = matchedItems.value.filter(
|
||||
(item) => rowKeyForMatch(item) !== rowKeyForMatch(row),
|
||||
);
|
||||
saveMatchedItems();
|
||||
}
|
||||
|
||||
function removeHistoryItemLocally(item: QueryAsinHistoryItem) {
|
||||
historyItems.value = historyItems.value.filter((row) => {
|
||||
if (item.resultId != null && row.resultId === item.resultId) return false;
|
||||
|
||||
@@ -115,7 +115,7 @@ import TaskCenterPanel from '@/shared/components/tasks/TaskCenterPanel.vue'
|
||||
import type { TaskItemView, TaskStatCard } from '@/shared/components/tasks/types'
|
||||
import ZiniaoVersionSetting from '@/shared/components/ZiniaoVersionSetting.vue'
|
||||
import { LISTING_FILTER_OPTIONS, type ListingFilterValue } from '@/pages/brand/components/listingFilters'
|
||||
import { activateShopMatchTask, addShopMatchCandidate, createShopMatchTask, deleteShopMatchCandidate, deleteShopMatchHistory, deleteShopMatchTask, getShopMatchCountryPreference, getShopMatchDashboard, getShopMatchHistory, getShopMatchResultDownloadUrl, getShopMatchTaskProgressBatch, getShopMatchTasksBatch, listShopMatchCandidates, matchShopMatchShops, putShopMatchCountryPreference, type ShopMatchCandidateVo, type ShopMatchCreateTaskItem, type ShopMatchDashboardVo, type ShopMatchHistoryItem, type ShopMatchShopQueueItem, type ShopMatchTaskDetailVo } from '@/shared/api/java-modules'
|
||||
import { activateShopMatchTask, addShopMatchCandidate, createShopMatchTask, deleteShopMatchCandidate, deleteShopMatchHistory, deleteShopMatchTask, getShopMatchCountryPreference, getShopMatchDashboard, getShopMatchHistory, getShopMatchResultDownloadUrl, getShopMatchTaskProgressBatch, listShopMatchCandidates, matchShopMatchShops, putShopMatchCountryPreference, type ShopMatchCandidateVo, type ShopMatchCreateTaskItem, type ShopMatchDashboardVo, type ShopMatchHistoryItem, type ShopMatchShopQueueItem, type ShopMatchTaskDetailVo } from '@/shared/api/java-modules'
|
||||
import { getPywebviewApi } from '@/shared/bridges/pywebview'
|
||||
import { getTaskPollIntervalMs } from '@/shared/task-progress-config'
|
||||
import { createCategorizedTimers } from '@/shared/utils/categorized-timers'
|
||||
|
||||
@@ -134,7 +134,6 @@ import {
|
||||
type SimilarAsinParseVo,
|
||||
type UploadedFileRef,
|
||||
type UploadFileVo,
|
||||
uploadTempFileToJava,
|
||||
} from '@/shared/api/java-modules'
|
||||
import { expandBrandFolderRecursive, type BrandExpandFolderItem } from '@/shared/api/brand'
|
||||
import { getPywebviewApi, type ProxyMode } from '@/shared/bridges/pywebview'
|
||||
|
||||
@@ -288,15 +288,6 @@ function statusText(status?: number | string) {
|
||||
return String(status ?? '未知')
|
||||
}
|
||||
|
||||
function statusClass(status?: number | string) {
|
||||
const numeric = Number(status)
|
||||
if (numeric === 1 || String(status).toLowerCase() === 'running') return 'running'
|
||||
if (numeric === 2 || ['success', 'completed', 'done'].includes(String(status).toLowerCase())) return 'success'
|
||||
if (numeric === 3 || ['failed', 'error'].includes(String(status).toLowerCase())) return 'failed'
|
||||
if (['cancelled', 'stopped'].includes(String(status).toLowerCase())) return 'cancelled'
|
||||
return 'pending'
|
||||
}
|
||||
|
||||
function isBusy(status?: number | string) {
|
||||
const numeric = Number(status)
|
||||
return numeric === 0 || numeric === 1 || String(status).toLowerCase() === 'pending' || String(status).toLowerCase() === 'running'
|
||||
@@ -320,10 +311,6 @@ function latestMessage(item: VariantEntry) {
|
||||
return (raw || '').trim() || '等待后台处理…'
|
||||
}
|
||||
|
||||
function taskMeta(item: VariantEntry) {
|
||||
return { source: item.meta?.source || '' }
|
||||
}
|
||||
|
||||
function resultFiles(item?: VariantEntry) {
|
||||
return toArray(item?.file_url)
|
||||
}
|
||||
|
||||
@@ -762,13 +762,6 @@ function isRecordMissingError(error: unknown) {
|
||||
return /记录不存在|任务不存在|不存在|已删除|not\s*found|404/i.test(message);
|
||||
}
|
||||
|
||||
function removeMatchedRowLocally(row: WithdrawShopQueueItem) {
|
||||
matchedItems.value = matchedItems.value.filter(
|
||||
(item) => rowKeyForMatch(item) !== rowKeyForMatch(row),
|
||||
);
|
||||
saveMatchedItems();
|
||||
}
|
||||
|
||||
function removeHistoryItemLocally(item: WithdrawHistoryItem) {
|
||||
historyItems.value = historyItems.value.filter((row) => {
|
||||
if (item.resultId != null && row.resultId === item.resultId) return false;
|
||||
|
||||
@@ -164,13 +164,6 @@ function toggleUpdatePanel() {
|
||||
updatePanel.value = !updatePanel.value
|
||||
}
|
||||
|
||||
function showTip(message: string) {
|
||||
toastText.value = message
|
||||
window.setTimeout(() => {
|
||||
if (toastText.value === message) toastText.value = ''
|
||||
}, 2600)
|
||||
}
|
||||
|
||||
async function fetchVersion() {
|
||||
try {
|
||||
const resp = await window.fetch('/api/version', { credentials: 'same-origin' })
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
:source-url="currentWorkspace.sceneAsset.sourceUrl" :loading="currentWorkspace.sceneAsset.uploading"
|
||||
allow-url compact action-label="上传场景图" selected-label="场景图" url-label="场景图链接" url-placeholder="也可以粘贴场景图链接"
|
||||
@select="(file) => setAsset(activeTab, 'sceneAsset', file)"
|
||||
@url-change="(url) => setAssetUrl(activeTab, 'sceneAsset', url, 'image/*')" preview-clickable
|
||||
@url-change="(url) => setAssetUrl(activeTab, 'sceneAsset', url)" preview-clickable
|
||||
@preview="openImagePreview" @clear="clearAsset(activeTab, 'sceneAsset')" />
|
||||
</AiSectionCard>
|
||||
</div>
|
||||
@@ -190,7 +190,7 @@
|
||||
:source-url="currentWorkspace.modelAsset.sourceUrl" :loading="currentWorkspace.modelAsset.uploading"
|
||||
allow-url compact preview-clickable action-label="上传模特图" selected-label="模特图" url-label="模特图链接"
|
||||
url-placeholder="也可以粘贴模特图链接" @select="(file) => setAsset(activeTab, 'modelAsset', file)"
|
||||
@url-change="(url) => setAssetUrl(activeTab, 'modelAsset', url, 'image/*')" @preview="openImagePreview"
|
||||
@url-change="(url) => setAssetUrl(activeTab, 'modelAsset', url)" @preview="openImagePreview"
|
||||
@clear="clearAsset(activeTab, 'modelAsset')" />
|
||||
</AiSectionCard>
|
||||
|
||||
@@ -277,7 +277,7 @@
|
||||
:source-url="currentWorkspace.speechAsset.sourceUrl" :loading="currentWorkspace.speechAsset.uploading"
|
||||
allow-url compact action-label="上传背景音乐" selected-label="背景音乐" url-label="背景音乐链接"
|
||||
@select="(file) => setAsset(activeTab, 'speechAsset', file)"
|
||||
@url-change="(url) => setAssetUrl(activeTab, 'speechAsset', url, 'audio/*')"
|
||||
@url-change="(url) => setAssetUrl(activeTab, 'speechAsset', url)"
|
||||
@clear="clearAsset(activeTab, 'speechAsset')" />
|
||||
</template>
|
||||
|
||||
@@ -786,7 +786,7 @@ function resolvePreviewKindFromMediaType(mediaType: string): PreviewKind {
|
||||
return 'file'
|
||||
}
|
||||
|
||||
function resolveMediaTypeFromUrl(url: string, accept = ''): MediaType {
|
||||
function resolveMediaTypeFromUrl(url: string): MediaType {
|
||||
const cleanUrl = url.split('?')[0]?.toLowerCase() || ''
|
||||
if (/\.(png|jpe?g|webp|gif|bmp|svg)$/.test(cleanUrl)) return 'image'
|
||||
if (/\.(mp4|mov|webm|m4v|ogg|avi|mkv)$/.test(cleanUrl)) return 'video'
|
||||
@@ -794,10 +794,10 @@ function resolveMediaTypeFromUrl(url: string, accept = ''): MediaType {
|
||||
return 'file'
|
||||
}
|
||||
|
||||
function setAssetUrl(tab: WorkspaceTab, assetKey: AssetKey, url: string, accept = '') {
|
||||
function setAssetUrl(tab: WorkspaceTab, assetKey: AssetKey, url: string) {
|
||||
const target = workspaces[tab][assetKey]
|
||||
const normalizedUrl = (url || '').trim()
|
||||
const mediaType = resolveMediaTypeFromUrl(normalizedUrl, accept)
|
||||
const mediaType = resolveMediaTypeFromUrl(normalizedUrl)
|
||||
target.sourceUrl = normalizedUrl
|
||||
target.previewUrl = normalizedUrl
|
||||
target.fileName = normalizedUrl ? '在线素材' : ''
|
||||
@@ -855,7 +855,7 @@ function setProductAssetUrl(tab: WorkspaceTab, index: number, url: string) {
|
||||
target.sourceUrl = normalizedUrl
|
||||
target.previewUrl = normalizedUrl
|
||||
target.fileName = normalizedUrl ? '在线素材' : ''
|
||||
target.mediaType = resolveMediaTypeFromUrl(normalizedUrl, 'image/*')
|
||||
target.mediaType = resolveMediaTypeFromUrl(normalizedUrl)
|
||||
target.previewKind = resolvePreviewKindFromMediaType(target.mediaType)
|
||||
scheduleDeliveryGridLayout()
|
||||
}
|
||||
@@ -1517,26 +1517,6 @@ function findMediaFileUrl(value: unknown) {
|
||||
]) || findUrlMatching(value, isMediaFileUrl)
|
||||
}
|
||||
|
||||
function resolveWorkflowStatus(value: unknown) {
|
||||
return (findTextByKeys(value, [
|
||||
'execute_status',
|
||||
'executeStatus',
|
||||
'workflow_status',
|
||||
'workflowStatus',
|
||||
'status',
|
||||
]) || '').toUpperCase()
|
||||
}
|
||||
|
||||
function isTerminalWorkflowStatus(status: string) {
|
||||
const normalized = (status || '').toUpperCase()
|
||||
return ['SUCCESS', 'SUCCEEDED', 'COMPLETED', 'DONE', 'FINISHED', 'FAILED', 'FAIL', 'ERROR', 'CANCELED', 'CANCELLED'].includes(normalized)
|
||||
}
|
||||
|
||||
function isFailedWorkflowStatus(status: string) {
|
||||
const normalized = (status || '').toUpperCase()
|
||||
return ['FAILED', 'FAIL', 'ERROR', 'CANCELED', 'CANCELLED'].includes(normalized)
|
||||
}
|
||||
|
||||
function findTextByKeys(value: unknown, keys: string[]): string {
|
||||
if (!value) return ''
|
||||
const normalizedKeys = keys.map((key) => key.toLowerCase())
|
||||
@@ -1751,7 +1731,7 @@ async function synthesizeVoice() {
|
||||
const result = await waitForImageVideoTask(ticket)
|
||||
const audioUrl = findMediaFileUrl(result)
|
||||
if (audioUrl) {
|
||||
setAssetUrl(activeTab.value, 'speechAsset', audioUrl, 'audio/*')
|
||||
setAssetUrl(activeTab.value, 'speechAsset', audioUrl)
|
||||
setVoiceStatus('语音合成完成,已写入最终配音文件', 'success')
|
||||
ElMessage.success('语音合成完成,已写入口播音频链接')
|
||||
} else {
|
||||
@@ -1794,7 +1774,7 @@ async function setVoiceCloneAsset(file: File) {
|
||||
|
||||
function setVoiceCloneUrl(url: string) {
|
||||
const normalizedUrl = (url || '').trim()
|
||||
const mediaType = resolveMediaTypeFromUrl(normalizedUrl, 'audio/*,video/*')
|
||||
const mediaType = resolveMediaTypeFromUrl(normalizedUrl)
|
||||
voiceCloneAsset.sourceUrl = normalizedUrl
|
||||
voiceCloneAsset.previewUrl = normalizedUrl
|
||||
voiceCloneAsset.fileName = normalizedUrl ? '在线克隆素材' : ''
|
||||
|
||||
@@ -659,10 +659,6 @@ function refreshModalFooter() {
|
||||
modalShow.workspaceRegenerate = src === 'workspace'
|
||||
}
|
||||
|
||||
function imageModalVisible(img: string) {
|
||||
return img.startsWith('data:') || img.startsWith('http')
|
||||
}
|
||||
|
||||
function openImageModal(images: string[], index: number, opts?: ModalSourceOpts) {
|
||||
const o = opts || {}
|
||||
imageModalImages.value = (Array.isArray(images) ? images : [images]).map(String)
|
||||
@@ -765,10 +761,6 @@ function triggerBlobDownload(blob: Blob, filename: string) {
|
||||
URL.revokeObjectURL(blobUrl)
|
||||
}
|
||||
|
||||
async function modalDownloadCurrent() {
|
||||
const src = imageModalImages.value[imageModalIndex.value]
|
||||
await downloadImage(src, `原图_${imageModalIndex.value + 1}.png`)
|
||||
}
|
||||
async function modalBatchDownload() {
|
||||
for (let i = 0; i < imageModalImages.value.length; i++) {
|
||||
await downloadImage(imageModalImages.value[i], `image_${i + 1}.png`)
|
||||
|
||||
@@ -128,7 +128,7 @@ const routes = [
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
scrollBehavior(to, _from, savedPosition) {
|
||||
scrollBehavior(_to, _from, savedPosition) {
|
||||
// 返回工具台的 #group-xxx 锚点由组件 onMounted 处理;普通滚动回顶部
|
||||
if (savedPosition) return savedPosition
|
||||
return { top: 0 }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { get, post, put, del, type JavaApiResponse, unwrapJavaResponse } from '../../http.ts'
|
||||
import { buildJavaUrl, JAVA_API_PREFIX } from '../../url.ts'
|
||||
import { JAVA_API_PREFIX } from '../../url.ts'
|
||||
import { getCurrentUserId } from '../../user.ts'
|
||||
import { getTaskProgressCacheTtlMs, getTaskProgressTimeoutMs } from '../../../task-progress-config.ts'
|
||||
import { createTaskProgressRequestCache } from '../../../task-progress-request-cache.ts'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { get, post, put, type JavaApiResponse, unwrapJavaResponse } from '../../http.ts'
|
||||
import { buildJavaUrl, JAVA_API_PREFIX } from '../../url.ts'
|
||||
import { JAVA_API_PREFIX } from '../../url.ts'
|
||||
import { getCurrentUserId } from '../../user.ts'
|
||||
|
||||
export interface ImageVideoDouyinCopyVo {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { get, post, put, del, type JavaApiResponse, unwrapJavaResponse } from '../../http.ts'
|
||||
import { buildJavaUrl, JAVA_API_PREFIX } from '../../url.ts'
|
||||
import { JAVA_API_PREFIX } from '../../url.ts'
|
||||
import { getCurrentUserId } from '../../user.ts'
|
||||
import { getJavaDownloadUrl } from '../../download-url.ts'
|
||||
import { getTaskProgressCacheTtlMs, getTaskProgressTimeoutMs } from '../../../task-progress-config.ts'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { get, post, put, del, type JavaApiResponse, unwrapJavaResponse } from '../../http.ts'
|
||||
import { buildJavaUrl, JAVA_API_PREFIX } from '../../url.ts'
|
||||
import { JAVA_API_PREFIX } from '../../url.ts'
|
||||
import { getCurrentUserId } from '../../user.ts'
|
||||
import { getJavaDownloadUrl } from '../../download-url.ts'
|
||||
import { getTaskProgressCacheTtlMs, getTaskProgressTimeoutMs } from '../../../task-progress-config.ts'
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { get, post, put, del, type JavaApiResponse, unwrapJavaResponse } from '../../http.ts'
|
||||
import { buildJavaUrl, JAVA_API_PREFIX } from '../../url.ts'
|
||||
import { JAVA_API_PREFIX } from '../../url.ts'
|
||||
import { getCurrentUserId } from '../../user.ts'
|
||||
import { getJavaDownloadUrl } from '../../download-url.ts'
|
||||
import { getTaskProgressCacheTtlMs, getTaskProgressTimeoutMs } from '../../../task-progress-config.ts'
|
||||
import { createTaskProgressRequestCache } from '../../../task-progress-request-cache.ts'
|
||||
import { API_ENDPOINTS } from '../../endpoints.ts'
|
||||
import type { QueryAsinCountryAsins } from './query-asin.ts'
|
||||
|
||||
/** 商品风险处理:备选店铺与匹配相关数据结构。 */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { get, post, del, type JavaApiResponse, unwrapJavaResponse } from '../../http.ts'
|
||||
import { buildJavaUrl, JAVA_API_PREFIX } from '../../url.ts'
|
||||
import { JAVA_API_PREFIX } from '../../url.ts'
|
||||
import { getCurrentUserId } from '../../user.ts'
|
||||
import { getJavaDownloadUrl } from '../../download-url.ts'
|
||||
import { getTaskProgressCacheTtlMs, getTaskProgressTimeoutMs } from '../../../task-progress-config.ts'
|
||||
|
||||
@@ -6,18 +6,9 @@ import {
|
||||
getTaskForegroundRefreshDelayMs,
|
||||
} from '../task-progress-config.ts'
|
||||
import { createCategorizedTimers } from '../utils/categorized-timers.ts'
|
||||
import {
|
||||
createTaskPollingBaseline,
|
||||
type TaskPollingBaseline,
|
||||
} from '../task-polling-baseline.ts'
|
||||
import {
|
||||
createProgressResponseCache,
|
||||
type ProgressResponseCache,
|
||||
} from '../progress-response-cache.ts'
|
||||
import {
|
||||
createTaskPollingCoordinator,
|
||||
type TaskPollingCoordinator,
|
||||
} from '../task-polling-coordinator.ts'
|
||||
import { createTaskPollingBaseline, type TaskPollingBaseline } from '../task-polling-baseline.ts'
|
||||
import type { ProgressResponseCache } from '../progress-response-cache.ts'
|
||||
import type { TaskPollingCoordinator } from '../task-polling-coordinator.ts'
|
||||
|
||||
/**
|
||||
* 通用任务进度轮询组合式函数。
|
||||
@@ -86,7 +77,7 @@ export interface TaskProgressLoopOptions<TDetail> {
|
||||
coordinator?: TaskPollingCoordinator
|
||||
}
|
||||
|
||||
export interface TaskProgressLoopHandle<TDetail> {
|
||||
export interface TaskProgressLoopHandle {
|
||||
taskIds: Ref<number[]>
|
||||
taskStatuses: Ref<Record<number, string>>
|
||||
inFlight: Ref<boolean>
|
||||
@@ -135,7 +126,7 @@ function writeIdsToStorage(key: string | undefined, ids: number[]) {
|
||||
|
||||
export function useTaskProgressLoop<TDetail>(
|
||||
options: TaskProgressLoopOptions<TDetail>,
|
||||
): TaskProgressLoopHandle<TDetail> {
|
||||
): TaskProgressLoopHandle {
|
||||
const timers = createCategorizedTimers(`task-progress-loop:${options.scope}`)
|
||||
const isTerminal = options.isTerminal ?? DEFAULT_TERMINAL
|
||||
const intervalMs = options.getIntervalMs ?? getTaskPollIntervalMs
|
||||
|
||||
@@ -12,7 +12,3 @@ export function resolvePageHref(rawHref?: string): string {
|
||||
return rawHref.replace(/^\/new_web_source\/(.+)\.html$/, '/$1')
|
||||
}
|
||||
|
||||
/** 兼容旧引用:SPA 下生产/开发路径一致,恒为 false(页面不再按路径区分桌面/Web) */
|
||||
export function isDesktopClientPage(): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export function createProgressResponseCache<V>(options: ProgressResponseCacheOpt
|
||||
return removed
|
||||
}
|
||||
|
||||
function evictIfNeeded(at: number) {
|
||||
function evictIfNeeded(_at: number) {
|
||||
while (map.size > maxEntries) {
|
||||
const oldest = map.keys().next().value as number | undefined
|
||||
if (oldest == null) break
|
||||
|
||||
@@ -48,7 +48,6 @@ export interface TaskProgressRequestCache<T> {
|
||||
|
||||
export function createTaskProgressRequestCache<T>(options: TaskProgressRequestCacheOptions) {
|
||||
const ttl = options.ttlMs
|
||||
const ttlValue = typeof ttl === 'number' ? ttl : 0
|
||||
if (typeof ttl === 'number') {
|
||||
if (!(ttl > 0)) {
|
||||
throw new Error('ttlMs 必须为正数: ' + ttl)
|
||||
|
||||
Reference in New Issue
Block a user