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:
2026-09-08 10:20:30 +08:00
parent e248b6e43a
commit d9e3f34d37
28 changed files with 24 additions and 408 deletions
@@ -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`)
+1 -1
View File
@@ -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
-4
View File
@@ -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)