/** * OmniCard AI (CRMmp) - 名片主 NFC 贴卡激活业务驱动层 * H5 无天线走手动 UID;APP startScan 为占位,调不到原生 NFC 时回退手动输入 */ import { NFCRecord } from '../../../common/types.uts'; import { bindNfcDevice, activateNfcDevice } from '../../../common/api.uts'; import { getCurrentLocale, getOmniText } from '../../../common/locale.uts'; export type ScanCopyKey = | 'title' | 'desc' | 'uidLabel' | 'uidPlaceholder' | 'cardIdLabel' | 'cardIdPlaceholder' | 'cta' | 'empty' | 'uidRequired' | 'cardIdRequired' | 'nfcUnavailable' | 'success' | 'error' | 'retry'; export type ScanState = { uid: string; cardIdRaw: string; deviceCardId: number | null; submitting: boolean; scanning: boolean; manualInput: boolean; errorMessage: string; empty: boolean; record: NFCRecord | null; }; export type ScanPageOptions = { cardId?: string; uid?: string; id?: string; }; export type ActivateByTapInput = { uid: string; cardId?: string | number | null; deviceCardId?: string | number | null; }; function isZhLocale(): boolean { return getCurrentLocale() === 'zh-CN'; } /** * 页面文案:能复用 locale.uts 的走词典,缺 key 时中英本地词典 */ export function resolveScanText(key: ScanCopyKey): string { const locale = getCurrentLocale(); if (key === 'title') { return getOmniText(locale, 'card.scan_nfc'); } if (key === 'success') { return getOmniText(locale, 'common.success'); } if (key === 'error') { return getOmniText(locale, 'common.error'); } if (key === 'empty') { return getOmniText(locale, 'common.empty'); } const zh = isZhLocale(); if (key === 'desc') { return zh ? '贴近实体卡,或手动填写芯片 UID 完成绑定' : 'Hold the card to this phone, or enter the chip UID to bind it'; } if (key === 'uidLabel') { return zh ? '芯片 UID' : 'Chip UID'; } if (key === 'uidPlaceholder') { return zh ? '输入芯片 UID' : 'Enter chip UID'; } if (key === 'cardIdLabel') { return zh ? '名片编号' : 'Card ID'; } if (key === 'cardIdPlaceholder') { return zh ? '输入名片编号' : 'Enter card ID'; } if (key === 'cta') { return zh ? '激活并绑定' : 'Activate and bind'; } if (key === 'uidRequired') { return zh ? '请填写芯片 UID' : 'Enter the chip UID'; } if (key === 'cardIdRequired') { return zh ? '请填写名片编号' : 'Enter a card ID'; } if (key === 'nfcUnavailable') { return zh ? '当前无法读取 NFC,请手动填写 UID' : 'NFC is unavailable. Enter the UID instead'; } if (key === 'retry') { return zh ? '重试' : 'Try again'; } return ''; } /** * 去空。空串视为非法 UID */ export function parseUid(raw: string | number | null | undefined): string { if (raw == null) { return ''; } return String(raw).trim(); } /** * 解析为正整数。非正整数返回 null */ export function parseCardId(raw: string | number | null | undefined): number | null { if (raw == null) { return null; } if (typeof raw === 'number') { if (raw <= 0) { return null; } if (Math.floor(raw) !== raw) { return null; } return raw; } const text = String(raw).trim(); if (text === '') { return null; } if (!/^[1-9]\d*$/.test(text)) { return null; } return Number(text); } function readErrorMessage(err: unknown): string { if (err instanceof Error && err.message !== '') { return err.message; } return resolveScanText('error'); } /** * 贴卡激活:有入参 cardId 则 bind;设备已有 cardId 且入参为空则 activate */ export async function activateByTap( input: ActivateByTapInput, existing?: { cardId?: string | number | null } | null ): Promise { const uid = parseUid(input.uid); if (uid === '') { throw new Error(resolveScanText('uidRequired')); } const incomingCardId = parseCardId(input.cardId); const fromInputDevice = parseCardId(input.deviceCardId); const fromExisting = existing != null ? parseCardId(existing.cardId) : null; const deviceCardId = fromInputDevice != null ? fromInputDevice : fromExisting; try { if (incomingCardId == null && deviceCardId != null) { return await activateNfcDevice({ uid }); } if (incomingCardId != null) { return await bindNfcDevice({ uid, cardId: incomingCardId }); } throw new Error(resolveScanText('cardIdRequired')); } catch (err) { throw new Error(readErrorMessage(err)); } } export function createScanState(): ScanState { return { uid: '', cardIdRaw: '', deviceCardId: null, submitting: false, scanning: false, manualInput: true, errorMessage: '', empty: true, record: null, }; } export function applyScanOptions(state: ScanState, options: ScanPageOptions | null): void { if (options == null) { return; } if (options.uid != null) { state.uid = parseUid(options.uid); } const cardRaw = options.cardId != null && options.cardId !== '' ? options.cardId : options.id != null ? options.id : ''; if (cardRaw !== '') { state.cardIdRaw = String(cardRaw).trim(); } state.empty = parseUid(state.uid) === ''; } /** * APP 原生贴卡占位。调不到天线时回退手动输入,禁止伪造成功 */ export function startScan(state: ScanState): boolean { // #ifdef APP // Native NFC antenna is not wired in this skeleton. // #endif state.scanning = false; state.manualInput = true; state.record = null; if (parseUid(state.uid) === '') { state.empty = true; state.errorMessage = resolveScanText('nfcUnavailable'); } return false; } export function resolveStatusLine(state: ScanState): string { if (state.errorMessage !== '') { return state.errorMessage; } if (state.empty && parseUid(state.uid) === '') { return resolveScanText('empty'); } return ''; } /** * 页面主 CTA:校验后走 activateByTap,失败写入一句错误 */ export async function submitScan(state: ScanState): Promise { if (state.submitting) { return null; } const uid = parseUid(state.uid); if (uid === '') { state.empty = true; state.errorMessage = resolveScanText('uidRequired'); return null; } state.uid = uid; state.empty = false; state.submitting = true; state.errorMessage = ''; try { const record = await activateByTap( { uid, cardId: state.cardIdRaw, deviceCardId: state.deviceCardId, }, { cardId: state.deviceCardId } ); state.submitting = false; state.record = record; if (record.cardId != null) { const bound = parseCardId(record.cardId); if (bound != null) { state.deviceCardId = bound; } } return record; } catch (err) { state.submitting = false; state.record = null; state.errorMessage = readErrorMessage(err); return null; } }