From 3168daedf4b3ad66ea74532163cc010b9a68b5d9 Mon Sep 17 00:00:00 2001 From: 24kycj <1637269896@qq.com> Date: Tue, 2 Jun 2026 12:16:03 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=88=86=E5=8F=91=E6=94=B6?= =?UTF-8?q?=E9=9B=86=E7=BB=AD=E5=81=9A=E3=80=81=E6=89=93=E5=8D=B0=E6=9C=BA?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E6=9F=A5=E8=AF=A2=E4=B8=8E=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 完成/失败态统一卡位13自动续做,修复主进程失败时 mode 被重置导致续做被拒 - 接入 SAPI_PrinterCheckstatus,Header 轻量刷状态,Init 后一次 GetPrinterInfo - print_flag 取自 soon.flag,path_file 目录级 staging,加密狗与格式化选项对齐 - 失败页并入运行页,移除独立 DistributeFailedView Co-authored-by: Cursor --- app/src/main/ipc/register-handlers.ts | 46 ++++- app/src/main/services/poll-manager.ts | 35 ++-- app/src/main/services/work-dll.service.ts | 29 +++- app/src/main/utils/parse-soon.ts | 15 +- app/src/main/utils/stage-job-payload.ts | 3 - app/src/preload/index.ts | 1 + app/src/renderer/src/api/cardsoon.ts | 10 ++ app/src/renderer/src/components/AppHeader.vue | 9 +- .../src/composables/useAppBootstrap.ts | 6 +- .../src/composables/usePrinterStatus.ts | 33 +++- .../renderer/src/constants/selectOptions.ts | 1 + app/src/renderer/src/router/guards.ts | 80 +++++---- app/src/renderer/src/router/index.ts | 5 - app/src/renderer/src/stores/distributeForm.ts | 10 +- app/src/renderer/src/styles/pages/page4.css | 19 +-- app/src/renderer/src/utils/buildJobConfig.ts | 14 +- .../src/utils/validateJobPreflight.ts | 7 +- .../src/views/DistributeConfigView.vue | 44 ++++- .../src/views/DistributeFailedView.vue | 89 ---------- .../src/views/DistributeRunningView.vue | 159 +++++++++++------- app/src/renderer/src/views/HomeView.vue | 6 +- app/src/shared/card-position.ts | 1 - app/src/shared/gen-task-id.ts | 1 - app/src/shared/printer-info.ts | 21 ++- 24 files changed, 389 insertions(+), 255 deletions(-) delete mode 100644 app/src/renderer/src/views/DistributeFailedView.vue diff --git a/app/src/main/ipc/register-handlers.ts b/app/src/main/ipc/register-handlers.ts index 0732a69..0cac8c2 100644 --- a/app/src/main/ipc/register-handlers.ts +++ b/app/src/main/ipc/register-handlers.ts @@ -5,7 +5,7 @@ import { CS_FAIL, CS_OK } from '../constants' import { assertNotBusy, assertReady, mainAppState } from '../services/app-state' import { configStore } from '../services/config-store' import { startUsbPoll, startJobPoll, stopAllPolls, stopJobPoll, stopUsbPoll, stopCardPositionPoll, startCardPositionPoll, getPollMainWindow, isJobPollActive } from '../services/poll-manager' -import { parsePrinterInfoFromDll, type PrinterStatusSnapshot } from '@shared/printer-info' +import { parsePrinterInfoFromDll, statusTextFromCheckstatus, type PrinterStatusSnapshot } from '@shared/printer-info' import { cleanPathPattern, getDirectorySizeBytes } from '../utils/dir-size' import { getDesignAppPath } from '../services/app-config' import { openDesignApp } from '../services/open-design-app' @@ -83,6 +83,50 @@ export function registerIpcHandlers(): void { } }) + tracedHandle('dll:printer-status', async () => { + if (!mainAppState.initialized) { + const cached = configStore.get('lastPrinterStatus') + return ok({ + statusText: cached?.statusText ?? '未初始化', + fromCache: true + }) + } + try { + assertReady() + const dll = await loadDllModule() + if (!dll.isPrinterCheckstatusApiAvailable()) { + return fail(CS_FAIL, '当前 DLL 不支持打印机状态查询') + } + const code = dll.dllPrinterCheckstatus() + const parsed = statusTextFromCheckstatus(code) + const cached = configStore.get('lastPrinterStatus') + if (parsed.ok) { + const snapshot: PrinterStatusSnapshot = { + ribbonType: cached?.ribbonType ?? '—', + statusText: parsed.statusText, + serialNo: cached?.serialNo ?? '—', + printedCount: cached?.printedCount ?? 0 + } + configStore.set('lastPrinterStatus', snapshot) + return ok({ statusText: parsed.statusText, statusCode: code }) + } + if (cached) { + return ok({ + statusText: parsed.statusText, + fromCache: true, + liveError: parsed.liveError + }) + } + return fail(code, parsed.liveError || parsed.statusText) + } catch (err) { + const cached = configStore.get('lastPrinterStatus') + if (cached) { + return ok({ statusText: cached.statusText, fromCache: true, liveError: String(err) }) + } + return fail(CS_FAIL, String(err)) + } + }) + tracedHandle('dll:printer-reset', async () => { try { assertReady() diff --git a/app/src/main/services/poll-manager.ts b/app/src/main/services/poll-manager.ts index b41751c..f0f7978 100644 --- a/app/src/main/services/poll-manager.ts +++ b/app/src/main/services/poll-manager.ts @@ -95,14 +95,12 @@ export function startJobPoll(id: string): void { log.warn('GetJobStateById query failed', r.queryErrorCode) return } - if (failed || cancelled) { - stopJobPoll(true) - } else if (finished) { + if (failed || cancelled || finished) { stopJobPoll(false) } } catch (e) { log.error('job poll error', e) - stopJobPoll(true) + stopJobPoll(false) } })() }, POLL_INTERVAL_MS) @@ -115,7 +113,7 @@ export function startUsbPoll(): void { usbTimer = setInterval(() => { void pollUsbOnce(gen).catch((e) => { log.error('usb poll error', e) - stopUsbPoll(true) + stopUsbPoll(false) }) }, POLL_INTERVAL_MS) } @@ -156,21 +154,24 @@ async function pollUsbOnce(gen: number): Promise { } } +async function pollCardPositionOnce(): Promise { + try { + const dll = await loadDllModule() + if (!dll.isCardPositionApiAvailable()) return + const r = dll.dllGetPrinterCardPosition() + const tick = { queryCode: r.queryCode, position: r.position } + emitTrace('[poll] card:position-tick', tick) + send('card:position-tick', tick) + } catch (e) { + log.error('card position poll error', e) + } +} + export function startCardPositionPoll(): void { stopCardPositionPoll() + void pollCardPositionOnce() cardTimer = setInterval(() => { - void (async () => { - try { - const dll = await loadDllModule() - if (!dll.isCardPositionApiAvailable()) return - const r = dll.dllGetPrinterCardPosition() - const tick = { queryCode: r.queryCode, position: r.position } - emitTrace('[poll] card:position-tick', tick) - send('card:position-tick', tick) - } catch (e) { - log.error('card position poll error', e) - } - })() + void pollCardPositionOnce() }, POLL_INTERVAL_MS) } diff --git a/app/src/main/services/work-dll.service.ts b/app/src/main/services/work-dll.service.ts index ebe33f2..023d745 100644 --- a/app/src/main/services/work-dll.service.ts +++ b/app/src/main/services/work-dll.service.ts @@ -47,9 +47,12 @@ let SAPI_PrinterMovetousbreader: any = null // eslint-disable-next-line @typescript-eslint/no-explicit-any let SAPI_GetPrinterCardPosition: any = null // eslint-disable-next-line @typescript-eslint/no-explicit-any +let SAPI_PrinterCheckstatus: any = null +// eslint-disable-next-line @typescript-eslint/no-explicit-any let SAPI_UploadFile: any = null let hasRejectApi = false let hasCardPositionApi = false +let hasCheckstatusApi = false let hasCancelApi = false let hasUploadApi = false let hasPrinterInfoEx = false @@ -179,13 +182,23 @@ function loadLibrary(): void { emitTrace('[dll] SAPI_GetPrinterCardPosition not in workDll (optional)') } + try { + SAPI_PrinterCheckstatus = lib.func('int __stdcall SAPI_PrinterCheckstatus()') + hasCheckstatusApi = true + } catch { + SAPI_PrinterCheckstatus = null + hasCheckstatusApi = false + emitTrace('[dll] SAPI_PrinterCheckstatus not in workDll (optional)') + } + log.info('workDll loaded', { upload: hasUploadApi, printerInfoEx: hasPrinterInfoEx, cancel: hasCancelApi, reject: hasRejectApi, usbReader: hasUsbReaderApi, - cardPosition: hasCardPositionApi + cardPosition: hasCardPositionApi, + checkstatus: hasCheckstatusApi }) } @@ -214,6 +227,11 @@ export function isCardPositionApiAvailable(): boolean { return hasCardPositionApi } +export function isPrinterCheckstatusApiAvailable(): boolean { + loadLibrary() + return hasCheckstatusApi +} + export function dllInit(params: InitParams): number { return traceCall( 'SAPI_Init', @@ -336,7 +354,6 @@ export function dllCopyFromUsb(destFolder: string, cardOutput: number): number { export function dllGetUsbCopyState(): { queryCode: number taskStatus: number - /** copy_progress 0-100 */ progress: number } { return traceCall('SAPI_GetUsbCopyState', undefined, () => { @@ -384,3 +401,11 @@ export function dllGetPrinterCardPosition(): { queryCode: number; position: numb return { queryCode, position: position[0] } }) } + +export function dllPrinterCheckstatus(): number { + return traceCall('SAPI_PrinterCheckstatus', undefined, () => { + loadLibrary() + if (!SAPI_PrinterCheckstatus) throw new Error('CHECKSTATUS_API_UNAVAILABLE') + return SAPI_PrinterCheckstatus!() as number + }) +} diff --git a/app/src/main/utils/parse-soon.ts b/app/src/main/utils/parse-soon.ts index e990a7c..07d0c0b 100644 --- a/app/src/main/utils/parse-soon.ts +++ b/app/src/main/utils/parse-soon.ts @@ -11,10 +11,20 @@ export interface ParsedSoonTemplate { frontImageUrl: string backImageUrl: string fields: TemplateFieldRow[] + printFlag: number } const SOON_FIELD_TYPES = new Set([1, 3, 4, 5]) +export function readSoonPrintFlag(raw: Record): number { + const flag = Number(raw.flag) + if (flag === 1 || flag === 2) return flag + const hasBack = + !!String(raw.backDisplayPic ?? '').trim() || + (Array.isArray(raw.backData) && raw.backData.length > 0) + return hasBack ? 2 : 1 +} + function pickArray(obj: Record, key: string): Record[] { const entry = Object.entries(obj).find(([k]) => k.toLowerCase() === key.toLowerCase()) if (!Array.isArray(entry?.[1])) return [] @@ -83,7 +93,8 @@ function parseSoonWorkerDisk(soonPath: string, raw: Record): Pa return { frontImageUrl: toImageUrl(soonPath, frontPic), backImageUrl: toImageUrl(soonPath, backPic), - fields + fields, + printFlag: readSoonPrintFlag(raw) } } @@ -119,7 +130,7 @@ function parseSoonLegacy(soonPath: string, raw: Record): Parsed fields.push({ label: toFieldLabel(name, side), value, originName: name }) }) - return { frontImageUrl, backImageUrl, fields } + return { frontImageUrl, backImageUrl, fields, printFlag: readSoonPrintFlag(raw) } } export function parseSoonTemplate(soonPath: string, raw: Record): ParsedSoonTemplate { diff --git a/app/src/main/utils/stage-job-payload.ts b/app/src/main/utils/stage-job-payload.ts index bcbab85..8f8958b 100644 --- a/app/src/main/utils/stage-job-payload.ts +++ b/app/src/main/utils/stage-job-payload.ts @@ -51,7 +51,6 @@ function stageSoonAssets(taskDir: string, soonSrc: string, soonDest: string): vo } } -/** 保留目录级 path_file,不展开为单文件列表 */ function normalizeCopyPaths(payload: Record): void { if (!Array.isArray(payload.path_file)) { throw new Error('拷贝任务缺少 path_file') @@ -87,7 +86,6 @@ function normalizeCopyPaths(payload: Record): void { payload.path_file = normalized } -/** staging 后按 has_* 裁剪字段,并校验 RestJobEx 必填项 */ function finalizeRestJobPayload(payload: Record): void { const hasCopy = payload.has_copy_task === true const hasPrint = payload.has_print_task === true @@ -119,7 +117,6 @@ function finalizeRestJobPayload(payload: Record): void { } } -/** 通过 SAPI_UploadFile + 本地落盘,准备 RestJobEx 所需路径 */ export function stageJobPayloadJson( json: string, sharedDir: string, diff --git a/app/src/preload/index.ts b/app/src/preload/index.ts index 9753429..4378659 100644 --- a/app/src/preload/index.ts +++ b/app/src/preload/index.ts @@ -4,6 +4,7 @@ const channels = { invoke: [ 'dll:init', 'dll:printer-info', + 'dll:printer-status', 'dll:printer-reset', 'dll:printer-reject', 'dll:printer-error-str', diff --git a/app/src/renderer/src/api/cardsoon.ts b/app/src/renderer/src/api/cardsoon.ts index e434e33..d945c6b 100644 --- a/app/src/renderer/src/api/cardsoon.ts +++ b/app/src/renderer/src/api/cardsoon.ts @@ -14,6 +14,14 @@ export async function dllPrinterInfo(): Promise>> } +export async function dllPrinterStatus(): Promise< + IpcResult<{ statusText: string; statusCode?: number; fromCache?: boolean; liveError?: string }> +> { + return api().invoke('dll:printer-status') as Promise< + IpcResult<{ statusText: string; statusCode?: number; fromCache?: boolean; liveError?: string }> + > +} + export function parsePrinterInfo(json: Record): PrinterStatusDisplay { return parsePrinterInfoFromDll(json) } @@ -119,6 +127,7 @@ export async function fsParseSoon(filePath: string): Promise< frontImageUrl: string backImageUrl: string fields: { label: string; value: string; originName: string }[] + printFlag: number }> > { return api().invoke('fs:parse-soon', filePath) as Promise< @@ -126,6 +135,7 @@ export async function fsParseSoon(filePath: string): Promise< frontImageUrl: string backImageUrl: string fields: { label: string; value: string; originName: string }[] + printFlag: number }> > } diff --git a/app/src/renderer/src/components/AppHeader.vue b/app/src/renderer/src/components/AppHeader.vue index 4270689..ba55641 100644 --- a/app/src/renderer/src/components/AppHeader.vue +++ b/app/src/renderer/src/components/AppHeader.vue @@ -23,14 +23,21 @@ - - - - diff --git a/app/src/renderer/src/views/DistributeRunningView.vue b/app/src/renderer/src/views/DistributeRunningView.vue index 9ac14a0..962df51 100644 --- a/app/src/renderer/src/views/DistributeRunningView.vue +++ b/app/src/renderer/src/views/DistributeRunningView.vue @@ -154,7 +154,9 @@ let usbQueryFailStreak = 0 const displayProgress = computed(() => Math.round(progress.value)) const failedSub = computed(() => - isCollect.value ? '请检查读卡器与卡片后重新尝试' : '请检查设备故障后重新插入数据卡' + isCollect.value + ? '请检查读卡器与卡片,插入备卡位可自动重试' + : '请检查设备故障,插入备卡位可自动重试' ) const statusTitle = computed(() => { @@ -169,7 +171,9 @@ const statusTitle = computed(() => { const statusSub = computed(() => { if (phase.value === 'completed') { - return '请点击返回,或插入数据卡继续下一张' + return isCollect.value + ? '请点击返回,或插入备卡位继续下一张' + : '请点击返回,或插入备卡位自动开始下一张' } if (isCollect.value) { return collectHint.value || '正在从卡片读取并写入导入目录' @@ -192,7 +196,6 @@ function clearPollListeners(): void { unsubCard = null } -/** 停止主进程轮询;resetMode=false 时保留 usbCopying/distributing 会话(完成态续做) */ async function releasePolls(resetMode: boolean): Promise { clearPollListeners() await pollCardPositionStop() @@ -203,48 +206,72 @@ async function releasePolls(resetMode: boolean): Promise { } } -async function enterFailedPhase(errorText = ''): Promise { - if (phase.value === 'failed') return - phase.value = 'failed' - failureStep.value = workflowStep.value - failureErrorText.value = errorText - if (!failureErrorText.value && !isCollect.value) { - try { - const r = await dllPrinterErrorStr(-1) - failureErrorText.value = r.ok && r.data?.text ? r.data.text : '' - } catch { - failureErrorText.value = '' - } - } - await releasePolls(true) - jobStore.clearActiveJob() - appStore.setMode('ready') -} - -async function enterCompletedPhase(): Promise { - if (phase.value === 'completed') return - phase.value = 'completed' - setProgress(100) +async function stopTaskPollsOnly(): Promise { + unsubJob?.() + unsubJob = null + unsubUsb?.() + unsubUsb = null if (isCollect.value) { - workflowStep.value = 3 await pollUsbStop({ resetMode: false }) } else { - workflowStep.value = 4 await pollJobStop({ resetMode: false }) } +} + +async function startCardPositionWatch(): Promise { + await pollCardPositionStop() const cardStarted = await pollCardPositionStart() if (!cardStarted.ok) { notify.warning(cardStarted.message || '无法监控卡位,请手动点击返回或重新提交') } - if (!unsubCard) { - unsubCard = onCardPositionTick((payload) => - applyCardPosition(payload as CardPositionPollPayload) - ) + unsubCard?.() + unsubCard = onCardPositionTick((payload) => + applyCardPosition(payload as CardPositionPollPayload) + ) +} + +async function enterWaitPhase(next: 'completed' | 'failed', errorText = ''): Promise { + if (phase.value !== next) { + phase.value = next + if (next === 'completed') { + setProgress(100) + workflowStep.value = isCollect.value ? 3 : 4 + } else { + failureStep.value = workflowStep.value + failureErrorText.value = errorText + if (!failureErrorText.value && !isCollect.value) { + try { + const r = await dllPrinterErrorStr(-1) + failureErrorText.value = r.ok && r.data?.text ? r.data.text : '' + } catch { + failureErrorText.value = '' + } + } + jobStore.clearActiveJob() + } + } else if (next === 'failed' && errorText) { + failureErrorText.value = errorText } + await stopTaskPollsOnly() + appStore.setMode(isCollect.value ? 'usbCopying' : 'distributing') + lastCardPosition = -1 + await startCardPositionWatch() +} + +async function enterFailedPhase(errorText = ''): Promise { + await enterWaitPhase('failed', errorText) +} + +async function enterCompletedPhase(): Promise { + await enterWaitPhase('completed') +} + +function canAutoResubmit(): boolean { + return phase.value === 'completed' || phase.value === 'failed' } function applyCardPosition(p: CardPositionPollPayload): void { - if (phase.value !== 'completed' || resubmitting) return + if (!canAutoResubmit() || resubmitting) return if (p.queryCode !== 0) return const pos = p.position const enteredPrepare = pos === POSITION_PREPARE && lastCardPosition !== POSITION_PREPARE @@ -254,59 +281,66 @@ function applyCardPosition(p: CardPositionPollPayload): void { } async function resubmitTask(): Promise { - if (resubmitting || phase.value !== 'completed') return + if (resubmitting || !canAutoResubmit()) return + const fromWaitPhase = phase.value resubmitting = true await pollCardPositionStop() + unsubCard?.() + unsubCard = null phase.value = 'running' setProgress(0) workflowStep.value = 1 collectHint.value = '' waitCard.value = false + queryFailStreak = 0 + usbQueryFailStreak = 0 + + const backToWait = async (message?: string): Promise => { + resubmitting = false + if (message) notify.error(message) + if (fromWaitPhase === 'failed') { + await enterFailedPhase(message || failureErrorText.value) + } else { + await enterCompletedPhase() + } + } try { if (isCollect.value) { const dest = collectStore.destPath.trim() if (!dest) { - notify.error('导入目录无效,无法继续') - await enterCompletedPhase() + await backToWait('导入目录无效,无法继续') return } const r = await dllUsbCopy(dest, collectStore.cardOutput, { resubmit: true }) if (!r.ok) { - notify.error(r.message || '重新提交收集任务失败') - await enterCompletedPhase() + await backToWait(r.message || '重新提交收集任务失败') return } usbAwaitNewCycle = true collectHint.value = usbTaskStatusHint(USB_TASK_PREPARING) - if (!unsubUsb) { - unsubUsb = onUsbPollTick((payload) => applyUsbProgress(payload as UsbPollPayload)) - } + unsubUsb = onUsbPollTick((payload) => applyUsbProgress(payload as UsbPollPayload)) return } const err = await validateJobPreflight(formStore) if (err) { notify.warning(err) - await enterCompletedPhase() + await backToWait() return } const created = await createDistributeJob(formStore, { resubmit: true }) if (!created.ok) { - notify.error(created.message) - await enterCompletedPhase() + await backToWait(created.message) return } jobStore.setActiveJob(created.jobId) const started = await pollJobStart(created.jobId) if (!started.ok) { - notify.error(started.message || '启动任务轮询失败') - await enterCompletedPhase() + await backToWait(started.message || '启动任务轮询失败') return } - if (!unsubJob) { - unsubJob = onJobPollTick((payload) => applyJobProgress(payload as JobPollPayload)) - } + unsubJob = onJobPollTick((payload) => applyJobProgress(payload as JobPollPayload)) } finally { resubmitting = false } @@ -354,13 +388,16 @@ function applyUsbProgress(p: UsbPollPayload): void { } usbQueryFailStreak = 0 + if (usbAwaitNewCycle && p.queryCode === 0 && p.taskStatus === USB_TASK_PREPARING) { + usbAwaitNewCycle = false + } + if (p.failed) { collectStore.failCount += 1 void enterFailedPhase(p.errorMessage || usbTaskStatusHint(p.taskStatus)) return } if (p.success) { - if (usbAwaitNewCycle) return collectStore.successCount += 1 workflowStep.value = 3 collectHint.value = usbTaskStatusHint(p.taskStatus) @@ -373,9 +410,6 @@ function applyUsbProgress(p: UsbPollPayload): void { workflowStep.value = p.taskStatus === USB_TASK_COPYING ? 2 : 1 collectHint.value = usbTaskStatusHint(p.taskStatus) setProgress(copyProgress) - if (usbAwaitNewCycle && p.taskStatus === USB_TASK_PREPARING) { - usbAwaitNewCycle = false - } } async function finishDistribute( @@ -384,7 +418,7 @@ async function finishDistribute( ): Promise { if (finishing) return finishing = true - const skipCancel = phase.value === 'completed' + const skipCancel = phase.value === 'completed' || phase.value === 'failed' await releasePolls(true) const id = jobStore.jobId if (id && !skipCancel) { @@ -437,29 +471,36 @@ onMounted(async () => { onUnmounted(() => { if (finishing || phase.value !== 'running') return void releasePolls(true) - if (!isCollect.value && appStore.mode === 'distributing') { + if (isCollect.value && appStore.mode === 'usbCopying') { + appStore.setMode('ready') + } else if (!isCollect.value && appStore.mode === 'distributing') { appStore.setMode('ready') } }) -async function onFailedBack(): Promise { +async function leaveFailedPage(redirect: '/collect' | '/distribute/config' | '/home'): Promise { finishing = true + await releasePolls(true) + appStore.setMode('ready') + await router.push(redirect) +} + +async function onFailedBack(): Promise { if (isCollect.value) { - await router.push('/collect') + await leaveFailedPage('/collect') return } - await router.push('/distribute/config') + await leaveFailedPage('/distribute/config') } async function onFailedReset(): Promise { - finishing = true if (isCollect.value) { collectStore.reset() } else { formStore.reset() jobStore.reset() } - await router.push('/home') + await leaveFailedPage('/home') } async function onReturn(): Promise { diff --git a/app/src/renderer/src/views/HomeView.vue b/app/src/renderer/src/views/HomeView.vue index 4574ad9..c95ba85 100644 --- a/app/src/renderer/src/views/HomeView.vue +++ b/app/src/renderer/src/views/HomeView.vue @@ -49,7 +49,7 @@ diff --git a/app/src/shared/card-position.ts b/app/src/shared/card-position.ts index 2ee50c2..b584ff5 100644 --- a/app/src/shared/card-position.ts +++ b/app/src/shared/card-position.ts @@ -1,2 +1 @@ -/** workDll 卡位:备卡位,检测到后可自动重提任务 */ export const POSITION_PREPARE = 13 diff --git a/app/src/shared/gen-task-id.ts b/app/src/shared/gen-task-id.ts index 13efc90..55a096f 100644 --- a/app/src/shared/gen-task-id.ts +++ b/app/src/shared/gen-task-id.ts @@ -1,4 +1,3 @@ -/** RestJobEx JSON 根字段 task_id */ export function genTaskId(): string { return `T${Date.now()}` } diff --git a/app/src/shared/printer-info.ts b/app/src/shared/printer-info.ts index 43fec5d..e4e1e5f 100644 --- a/app/src/shared/printer-info.ts +++ b/app/src/shared/printer-info.ts @@ -30,6 +30,26 @@ function normalizeStatus(raw: unknown): string { return PRINTER_STATUS_MAP[s] ?? s } +export function statusTextFromCheckstatus(code: number): { + ok: boolean + statusText: string + liveError?: string +} { + if (code === 0) { + return { ok: true, statusText: PRINTER_STATUS_MAP.I ?? '空闲' } + } + if (code > 0 && code < 128) { + const statusText = normalizeStatus(code) + if (statusText !== '—') { + return { ok: true, statusText } + } + } + if (code < 0) { + return { ok: false, statusText: '未连接打印机', liveError: `code=${code}` } + } + return { ok: false, statusText: '—', liveError: `code=${code}` } +} + function snapshotFromRecord(row: Record): PrinterStatusSnapshot { const serial = pickFirst(row, [ 'serial_no', @@ -65,7 +85,6 @@ function snapshotFromRecord(row: Record): PrinterStatusSnapshot } } -/** SAPI_GetPrinterInfoEx(扁平 JSON)与 SAPI_GetPrinterInfo(printerList) */ export function parsePrinterInfoFromDll(json: Record): PrinterStatusSnapshot { const flatSerial = pickFirst(json, ['serial_no', 'SerialNo', 'serialNo', 'szPrinterSerial']) const flatStatus = pickFirst(json, ['printer_status', 'PrinterStatus'])