删除模块 取消打开紫鸟

This commit is contained in:
super
2026-03-31 23:12:46 +08:00
parent bf802c1019
commit b6dd1d7931
8 changed files with 260 additions and 60 deletions

View File

@@ -16,7 +16,6 @@ import webview
import threading import threading
import time import time
import requests import requests
import subprocess
with open("version.txt","w",encoding="utf-8") as file: with open("version.txt","w",encoding="utf-8") as file:
@@ -235,21 +234,6 @@ class WindowAPI:
except Exception as e: except Exception as e:
return {'success': False, 'error': str(e)} return {'success': False, 'error': str(e)}
def open_external_url(self, url):
if not url or not str(url).strip():
return {'success': False, 'error': '链接为空'}
target = str(url).strip()
try:
if sys.platform.startswith('win'):
os.startfile(target)
elif sys.platform == 'darwin':
subprocess.Popen(['open', target])
else:
subprocess.Popen(['xdg-open', target])
return {'success': True}
except Exception as e:
return {'success': False, 'error': str(e)}
def enqueue_json(self, data): def enqueue_json(self, data):
"""保存任务到队列""" """保存任务到队列"""
try: try:
@@ -366,7 +350,7 @@ def main():
) )
api = WindowAPI(window) api = WindowAPI(window)
window.expose(api.close, api.minimize, api.maximize, api.toggle_maximize, generate_images, api.save_image, api.save_image_to_folder, api.select_folder, api.select_brand_xlsx_files, api.select_brand_folder, window.expose(api.close, api.minimize, api.maximize, api.toggle_maximize, generate_images, api.save_image, api.save_image_to_folder, api.select_folder, api.select_brand_xlsx_files, api.select_brand_folder,
api.save_file_from_url, api.save_template_xlsx,api.save_template_zip,api.upload_file_to_java,api.open_external_url,api.enqueue_json) api.save_file_from_url, api.save_template_xlsx,api.save_template_zip,api.upload_file_to_java,api.enqueue_json)
webview.start( webview.start(
debug=True, debug=True,
storage_path=cache_path, storage_path=cache_path,

View File

@@ -7,6 +7,7 @@ import com.nanri.aiimage.modules.deletebrand.model.dto.DeleteBrandTaskBatchReque
import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandHistoryVo; import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandHistoryVo;
import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandRunVo; import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandRunVo;
import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandTaskBatchVo; import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandTaskBatchVo;
import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandTaskDeletionStatusVo;
import com.nanri.aiimage.modules.deletebrand.service.DeleteBrandRunService; import com.nanri.aiimage.modules.deletebrand.service.DeleteBrandRunService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
@@ -83,6 +84,15 @@ public class DeleteBrandRunController {
return ApiResponse.success(deleteBrandRunService.getTaskDetails(request.getTaskIds())); return ApiResponse.success(deleteBrandRunService.getTaskDetails(request.getTaskIds()));
} }
@GetMapping("/tasks/{taskId}/deletion-status")
@Operation(summary = "获取删除品牌任务删除状态", description = "供 Python/插件按 taskId 查询该删除品牌任务是否已被前端删空。")
public ApiResponse<DeleteBrandTaskDeletionStatusVo> getTaskDeletionStatus(
@PathVariable Long taskId,
@Parameter(name = "user_id", description = "当前登录用户 ID", required = true, in = ParameterIn.QUERY)
@RequestParam("user_id") Long userId) {
return ApiResponse.success(deleteBrandRunService.getTaskDeletionStatus(taskId, userId));
}
@PostMapping("/tasks/{taskId}/result") @PostMapping("/tasks/{taskId}/result")
@Operation(summary = "提交删除品牌处理结果", description = "插件/前端分片回传处理结果;后端累计分片并推进进度,全部完成后再执行最终组装与落库。") @Operation(summary = "提交删除品牌处理结果", description = "插件/前端分片回传处理结果;后端累计分片并推进进度,全部完成后再执行最终组装与落库。")
public ApiResponse<Void> submitResult( public ApiResponse<Void> submitResult(

View File

@@ -0,0 +1,17 @@
package com.nanri.aiimage.modules.deletebrand.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "删除品牌任务删除状态响应")
public class DeleteBrandTaskDeletionStatusVo {
@Schema(description = "任务ID")
private Long taskId;
@Schema(description = "该任务的结果记录是否已被全部删除")
private boolean deleted;
@Schema(description = "该任务当前剩余的结果记录数量")
private long remainingResultCount;
}

View File

@@ -23,6 +23,7 @@ import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandLineProgressVo;
import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandPreviewRowVo; import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandPreviewRowVo;
import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandResultItemVo; import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandResultItemVo;
import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandRunVo; import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandRunVo;
import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandTaskDeletionStatusVo;
import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandTaskDetailVo; import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandTaskDetailVo;
import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandTaskItemVo; import com.nanri.aiimage.modules.deletebrand.model.vo.DeleteBrandTaskItemVo;
import com.nanri.aiimage.modules.file.service.LocalFileStorageService; import com.nanri.aiimage.modules.file.service.LocalFileStorageService;
@@ -31,6 +32,8 @@ import com.nanri.aiimage.modules.task.mapper.FileResultMapper;
import com.nanri.aiimage.modules.task.mapper.FileTaskMapper; import com.nanri.aiimage.modules.task.mapper.FileTaskMapper;
import com.nanri.aiimage.modules.task.model.entity.FileResultEntity; import com.nanri.aiimage.modules.task.model.entity.FileResultEntity;
import com.nanri.aiimage.modules.task.model.entity.FileTaskEntity; import com.nanri.aiimage.modules.task.model.entity.FileTaskEntity;
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoShopMatchResultVo;
import com.nanri.aiimage.modules.ziniao.service.ZiniaoShopSwitchService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
@@ -68,7 +71,7 @@ public class DeleteBrandRunService {
private final FileResultMapper fileResultMapper; private final FileResultMapper fileResultMapper;
private final LocalFileStorageService localFileStorageService; private final LocalFileStorageService localFileStorageService;
private final DeleteBrandTaskCacheService deleteBrandTaskCacheService; private final DeleteBrandTaskCacheService deleteBrandTaskCacheService;
private final com.nanri.aiimage.modules.ziniao.service.ZiniaoAuthService ziniaoAuthService; private final ZiniaoShopSwitchService ziniaoShopSwitchService;
private final OssStorageService ossStorageService; private final OssStorageService ossStorageService;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
private final DeleteBrandProgressProperties deleteBrandProgressProperties; private final DeleteBrandProgressProperties deleteBrandProgressProperties;
@@ -96,7 +99,7 @@ public class DeleteBrandRunService {
int parsedSuccessCount = 0; int parsedSuccessCount = 0;
int parsedFailedCount = 0; int parsedFailedCount = 0;
Map<String, DeleteBrandParsedFileCacheDto> parsedPayloadByFileIdentity = new LinkedHashMap<>(); Map<String, DeleteBrandParsedFileCacheDto> parsedPayloadByFileIdentity = new LinkedHashMap<>();
Map<String, com.nanri.aiimage.modules.ziniao.service.ZiniaoAuthService.StoreMatchResult> storeMatchByShopName = new LinkedHashMap<>(); Map<String, ZiniaoShopMatchResultVo> storeMatchByShopName = new LinkedHashMap<>();
for (DeleteBrandSourceFileDto sourceFile : request.getFiles()) { for (DeleteBrandSourceFileDto sourceFile : request.getFiles()) {
DeleteBrandResultItemVo item = new DeleteBrandResultItemVo(); DeleteBrandResultItemVo item = new DeleteBrandResultItemVo();
@@ -117,25 +120,22 @@ public class DeleteBrandRunService {
item.setPreviewRows(parsed.previewRows()); item.setPreviewRows(parsed.previewRows());
try { try {
String normalizedShopName = normalizeShopName(item.getShopName()); String normalizedShopName = ziniaoShopSwitchService.normalizeShopName(item.getShopName());
com.nanri.aiimage.modules.ziniao.service.ZiniaoAuthService.StoreMatchResult matchResult = normalizedShopName.isBlank() ZiniaoShopMatchResultVo matchResult = normalizedShopName.isBlank()
? new com.nanri.aiimage.modules.ziniao.service.ZiniaoAuthService.StoreMatchResult(false, null, null, null, null, null) ? ziniaoShopSwitchService.emptyMatchResult()
: storeMatchByShopName.computeIfAbsent(normalizedShopName, : storeMatchByShopName.computeIfAbsent(normalizedShopName,
ignored -> ziniaoAuthService.matchStoreByNameAcrossStaff(item.getShopName(), null)); ignored -> ziniaoShopSwitchService.matchStoreByNameAcrossStaff(item.getShopName(), null));
item.setMatched(matchResult.matched()); item.setMatched(matchResult.isMatched());
if (matchResult.matched()) { if (matchResult.isMatched()) {
item.setShopId(matchResult.shopId()); item.setShopId(matchResult.getShopId());
item.setPlatform(matchResult.platform()); item.setPlatform(matchResult.getPlatform());
item.setOpenStoreUrl(matchResult.openStoreUrl()); item.setOpenStoreUrl(matchResult.getOpenStoreUrl());
} else { } else {
throw new BusinessException("未匹配到紫鸟店铺,跳过处理"); throw new BusinessException("未匹配到紫鸟店铺,跳过处理");
} }
} catch (BusinessException ex) { } catch (BusinessException ex) {
String message = ex.getMessage(); String message = ex.getMessage();
if (message != null && (message.contains("code=40004") if (ziniaoShopSwitchService.isSkippableMatchError(ex)) {
|| message.contains("userId存在无效的参数值")
|| message.contains("无权限")
|| message.contains("没有权限")) && !message.contains("白名单")) {
log.warn("[ziniao-match] suppressed upstream permission error for {}, msg: {}", item.getShopName(), message); log.warn("[ziniao-match] suppressed upstream permission error for {}, msg: {}", item.getShopName(), message);
item.setMatched(false); item.setMatched(false);
} else { } else {
@@ -301,6 +301,24 @@ public class DeleteBrandRunService {
return vo; return vo;
} }
public DeleteBrandTaskDeletionStatusVo getTaskDeletionStatus(Long taskId, Long userId) {
FileTaskEntity task = fileTaskMapper.selectById(taskId);
if (task == null || !MODULE_TYPE.equals(task.getModuleType()) || !userId.equals(task.getUserId())) {
throw new BusinessException("任务不存在");
}
Long remainingResultCount = fileResultMapper.selectCount(new LambdaQueryWrapper<FileResultEntity>()
.eq(FileResultEntity::getTaskId, taskId)
.eq(FileResultEntity::getModuleType, MODULE_TYPE)
.eq(FileResultEntity::getUserId, userId));
DeleteBrandTaskDeletionStatusVo vo = new DeleteBrandTaskDeletionStatusVo();
vo.setTaskId(taskId);
vo.setRemainingResultCount(remainingResultCount == null ? 0L : remainingResultCount);
vo.setDeleted(vo.getRemainingResultCount() == 0);
return vo;
}
public void deleteHistory(Long resultId, Long userId) { public void deleteHistory(Long resultId, Long userId) {
FileResultEntity entity = fileResultMapper.selectById(resultId); FileResultEntity entity = fileResultMapper.selectById(resultId);
if (entity == null || !MODULE_TYPE.equals(entity.getModuleType()) || !userId.equals(entity.getUserId())) { if (entity == null || !MODULE_TYPE.equals(entity.getModuleType()) || !userId.equals(entity.getUserId())) {
@@ -400,13 +418,6 @@ public class DeleteBrandRunService {
return value.replace(" ", "").trim().toUpperCase(Locale.ROOT); return value.replace(" ", "").trim().toUpperCase(Locale.ROOT);
} }
private String normalizeShopName(String value) {
if (value == null) {
return "";
}
return value.replace("\u3000", " ").trim();
}
private List<CountryColumnPair> resolveCountryPairs(Row titleRow, Row headerRow, DataFormatter formatter) { private List<CountryColumnPair> resolveCountryPairs(Row titleRow, Row headerRow, DataFormatter formatter) {
List<CountryColumnPair> pairs = new ArrayList<>(); List<CountryColumnPair> pairs = new ArrayList<>();
int lastCellNum = Math.max(titleRow.getLastCellNum(), headerRow.getLastCellNum()); int lastCellNum = Math.max(titleRow.getLastCellNum(), headerRow.getLastCellNum());
@@ -876,7 +887,7 @@ public class DeleteBrandRunService {
throw new BusinessException("存在未完成的结果分片: " + parsedFile.getSourceFilename()); throw new BusinessException("存在未完成的结果分片: " + parsedFile.getSourceFilename());
} }
MergedDeleteBrandFile mergedFile = mergeChunks(parsedFile, chunks); MergedDeleteBrandFile mergedFile = mergeChunks(parsedFile, chunks);
File outputFile = buildResultWorkbook(task.getId(), parsedFile, mergedFile); File outputFile = buildResultWorkbookPreserveLayout(task.getId(), parsedFile, mergedFile);
deleteBrandTaskCacheService.saveProgress(task.getId(), Map.of( deleteBrandTaskCacheService.saveProgress(task.getId(), Map.of(
"phase", DeleteBrandTaskCacheService.PHASE_UPLOADING, "phase", DeleteBrandTaskCacheService.PHASE_UPLOADING,
@@ -1032,6 +1043,52 @@ public class DeleteBrandRunService {
} }
} }
private File buildResultWorkbookPreserveLayout(Long taskId,
DeleteBrandParsedFileCacheDto parsedFile,
MergedDeleteBrandFile mergedFile) {
File outputDir = FileUtil.mkdir(FileUtil.file(System.getProperty("java.io.tmpdir"), "delete-brand-result", String.valueOf(taskId)));
File outputFile = buildNamedOutputFile(outputDir, blankToDefault(parsedFile.getSourceFilename(), "delete-brand-result.xlsx"));
try (Workbook workbook = new XSSFWorkbook(); FileOutputStream outputStream = new FileOutputStream(outputFile)) {
Sheet sheet = workbook.createSheet("删除品牌结果");
Row countryRow = sheet.createRow(0);
Row headerRow = sheet.createRow(1);
for (int countryIndex = 0; countryIndex < mergedFile.countries().size(); countryIndex++) {
DeleteBrandProcessedCountryDto country = mergedFile.countries().get(countryIndex);
int asinColumnIndex = countryIndex * 2;
int statusColumnIndex = asinColumnIndex + 1;
createTextCell(countryRow, asinColumnIndex, country.getCountry());
createTextCell(headerRow, asinColumnIndex, "删除ASIN");
createTextCell(headerRow, statusColumnIndex, "状态");
List<DeleteBrandCountryResultItemDto> items = country.getItems();
if (items == null) {
continue;
}
for (int itemIndex = 0; itemIndex < items.size(); itemIndex++) {
DeleteBrandCountryResultItemDto item = items.get(itemIndex);
Row row = sheet.getRow(itemIndex + 2);
if (row == null) {
row = sheet.createRow(itemIndex + 2);
}
createTextCell(row, asinColumnIndex, item.getAsin());
createTextCell(row, statusColumnIndex, item.getStatus());
}
}
for (int i = 0; i < mergedFile.countries().size() * 2; i++) {
sheet.autoSizeColumn(i);
}
workbook.write(outputStream);
return outputFile;
} catch (Exception ex) {
throw new BusinessException("生成删除品牌结果文件失败: " + parsedFile.getSourceFilename());
}
}
private void createTextCell(Row row, int index, String value) { private void createTextCell(Row row, int index, String value) {
Cell cell = row.createCell(index); Cell cell = row.createCell(index);
cell.setCellValue(value == null ? "" : value); cell.setCellValue(value == null ? "" : value);

View File

@@ -0,0 +1,30 @@
package com.nanri.aiimage.modules.ziniao.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "紫鸟店铺匹配结果")
public class ZiniaoShopMatchResultVo {
@Schema(description = "是否匹配成功")
private boolean matched;
@Schema(description = "店铺ID")
private String shopId;
@Schema(description = "店铺名称")
private String shopName;
@Schema(description = "平台")
private String platform;
@Schema(description = "匹配到的员工 userId")
private Long matchedUserId;
@Schema(description = "打开店铺链接")
private String openStoreUrl;
}

View File

@@ -0,0 +1,53 @@
package com.nanri.aiimage.modules.ziniao.service;
import com.nanri.aiimage.common.exception.BusinessException;
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoShopMatchResultVo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class ZiniaoShopSwitchService {
private final ZiniaoAuthService ziniaoAuthService;
public ZiniaoShopMatchResultVo matchStoreByNameAcrossStaff(String targetShopName, Long preferUserId) {
String normalizedShopName = normalizeShopName(targetShopName);
if (normalizedShopName.isBlank()) {
return emptyMatchResult();
}
ZiniaoAuthService.StoreMatchResult result = ziniaoAuthService.matchStoreByNameAcrossStaff(normalizedShopName, preferUserId);
return new ZiniaoShopMatchResultVo(
result.matched(),
result.shopId(),
result.shopName(),
result.platform(),
result.matchedUserId(),
result.openStoreUrl()
);
}
public ZiniaoShopMatchResultVo emptyMatchResult() {
return new ZiniaoShopMatchResultVo(false, null, null, null, null, null);
}
public String normalizeShopName(String value) {
if (value == null) {
return "";
}
return value.replace("\u3000", " ").trim();
}
public boolean isSkippableMatchError(BusinessException ex) {
String message = ex == null ? null : ex.getMessage();
if (message == null || message.isBlank()) {
return false;
}
return (message.contains("code=40004")
|| message.contains("userId\u5b58\u5728\u65e0\u6548\u7684\u53c2\u6570\u503c")
|| message.contains("\u65e0\u6743\u9650")
|| message.contains("\u6ca1\u6709\u6743\u9650"))
&& !message.contains("\u767d\u540d\u5355");
}
}

View File

@@ -249,6 +249,9 @@ const queuePayloadText = ref('')
const taskDetails = ref<Record<number, DeleteBrandTaskDetailVo>>({}) const taskDetails = ref<Record<number, DeleteBrandTaskDetailVo>>({})
const pollTimer = ref<number | null>(null) const pollTimer = ref<number | null>(null)
const pollingInFlight = ref(false) const pollingInFlight = ref(false)
const chainStarted = ref(false)
const activeItemKey = ref('')
const autoAdvancing = ref(false)
const displayPaths = computed(() => selectedPaths.value.slice(0, 10)) const displayPaths = computed(() => selectedPaths.value.slice(0, 10))
const currentSectionItems = computed(() => { const currentSectionItems = computed(() => {
@@ -517,6 +520,7 @@ function scheduleNextPoll(immediate = false) {
let stateChanged = false let stateChanged = false
try { try {
await refreshTaskDetails(taskIds) await refreshTaskDetails(taskIds)
await maybeAutoAdvance()
for (const taskId of taskIds) { for (const taskId of taskIds) {
if (isTaskTerminal(taskId)) { if (isTaskTerminal(taskId)) {
removeSessionTaskFromStorage(taskId) removeSessionTaskFromStorage(taskId)
@@ -766,34 +770,38 @@ function isPythonQueueBusy() {
return false return false
} }
async function triggerItemRun(item: DeleteBrandResultItem) { function getItemKey(item: DeleteBrandResultItem) {
if (item.resultId != null) return `result:${item.resultId}`
return `task:${item.taskId || 0}:${item.sourceFilename || ''}`
}
function findNextAutoRunnableItem() {
return currentSectionItems.value.find((item) => {
if (!item.openStoreUrl) return false
if (getItemKey(item) === activeItemKey.value) return false
return getQueueStatus(item) === '未入队'
})
}
async function runItem(item: DeleteBrandResultItem, options?: { auto?: boolean }) {
if (isPythonQueueBusy()) { if (isPythonQueueBusy()) {
if (!options?.auto) {
ElMessage.warning('当前存在正在处理的任务,请等待其完成后再推送下一条!') ElMessage.warning('当前存在正在处理的任务,请等待其完成后再推送下一条!')
return }
return false
} }
const api = getPywebviewApi() const api = getPywebviewApi()
if (item.openStoreUrl) {
if (api?.open_external_url) {
const openResult = await api.open_external_url(item.openStoreUrl)
if (!openResult?.success) {
ElMessage.error(openResult?.error || '打开紫鸟失败')
return
}
} else {
window.open(item.openStoreUrl, '_self')
}
}
const taskId = item.taskId const taskId = item.taskId
if (!taskId) return if (!taskId) return false
const sessionTask = sessionTasks.value.find(t => t.taskId === taskId) const sessionTask = sessionTasks.value.find(t => t.taskId === taskId)
if (!sessionTask) return if (!sessionTask) return false
if (!api?.enqueue_json) { if (!api?.enqueue_json) {
ElMessage.error('当前环境未启用 pywebview enqueue_json浏览器环境不会推送') ElMessage.error('当前环境未启用 pywebview enqueue_json浏览器环境不会推送')
return return false
} }
const payload = { const payload = {
@@ -811,18 +819,60 @@ async function triggerItemRun(item: DeleteBrandResultItem) {
if (pushResult?.success) { if (pushResult?.success) {
qpr = `已单独推送文件 ${item.sourceFilename},当前队列长度:${pushResult.queue_size ?? '-'}` qpr = `已单独推送文件 ${item.sourceFilename},当前队列长度:${pushResult.queue_size ?? '-'}`
// Mark the item as pushed
const sessionItem = sessionTask.items.find(i => i.resultId === item.resultId || i.sourceFilename === item.sourceFilename) const sessionItem = sessionTask.items.find(i => i.resultId === item.resultId || i.sourceFilename === item.sourceFilename)
if (sessionItem) { if (sessionItem) {
(sessionItem as any)._pushed = true (sessionItem as any)._pushed = true
} }
activeItemKey.value = getItemKey(item)
saveSessionTask(taskId, sessionTask.items, qpr, queuePayloadText.value) saveSessionTask(taskId, sessionTask.items, qpr, queuePayloadText.value)
ensurePolling(true) ensurePolling(true)
} else { } else {
qpr = `推送到 Python 队列失败:${pushResult?.error || '未知错误'}` qpr = `推送到 Python 队列失败:${pushResult?.error || '未知错误'}`
} }
queuePushResult.value = qpr queuePushResult.value = qpr
return Boolean(pushResult?.success)
}
async function maybeAutoAdvance() {
if (!chainStarted.value || autoAdvancing.value) return
if (isPythonQueueBusy()) return
const currentKey = activeItemKey.value
if (currentKey) {
const currentItem = currentSectionItems.value.find(item => getItemKey(item) === currentKey)
if (currentItem) {
const status = getQueueStatus(currentItem)
if (status === '已入队' || status === '处理中') {
return
}
}
}
const nextItem = findNextAutoRunnableItem()
if (!nextItem) {
activeItemKey.value = ''
chainStarted.value = false
return
}
autoAdvancing.value = true
try {
const started = await runItem(nextItem, { auto: true })
if (!started) {
chainStarted.value = false
}
} finally {
autoAdvancing.value = false
}
}
async function triggerItemRun(item: DeleteBrandResultItem) {
chainStarted.value = true
const started = await runItem(item)
if (!started) {
chainStarted.value = false
activeItemKey.value = ''
}
} }
function openShop(url: string) { function openShop(url: string) {

View File

@@ -21,7 +21,6 @@ export interface PywebviewApi {
save_template_xlsx?: () => Promise<{ success: boolean; path?: string; error?: string }> save_template_xlsx?: () => Promise<{ success: boolean; path?: string; error?: string }>
save_template_zip?: () => Promise<{ success: boolean; path?: string; error?: string }> save_template_zip?: () => Promise<{ success: boolean; path?: string; error?: string }>
enqueue_json?: (data: unknown) => Promise<{ success: boolean; queue_size?: number; error?: string }> enqueue_json?: (data: unknown) => Promise<{ success: boolean; queue_size?: number; error?: string }>
open_external_url?: (url: string) => Promise<{ success: boolean; error?: string }>
} }
declare global { declare global {