文件
OmniCard/mpuniapp/pages/owner/nfc/useScan.uts
wikiboss eec259a3a7 feat: 完成全系统30个功能点开发与E2E全链路验收 (8轮调度32个子代理闭环)
- 模块一: 出海案例作品集、博客动态发布、人名发音语音条、RTL阿语多语言切换
- 模块二: AI顾问人设配置、行业术语库润色、RAG切片沙盒调试、流式会话与意向留资卡片
- 模块三: 全球访客雷达大屏、多维加权意向算分、外部CRM对接推送、团队公海池流转
- 模块四: 移动端NFC安全挂失、NFC动态路由规则配置、动态防伪验签、展会制卡Zip批量导出
- 模块五: 组织架构与名片分配、移动端个人中心设置、离职交接与历史线索迁移、安全审计日志
- 模块六: Stripe国际信用卡订阅Webhook、独立二级域名与SSL检测、坐席配额预警、多币种发票
- 模块七: 纸质名片OCR解析入库、国际商务日程RFC 5545 ICS日历、微官网离线SWR预加载、展会实时排行榜大屏
- 模块八: 全球出海SEO OpenGraph元标签注入与社交爬虫探测、30点端到端全链路业务流集成测试套件
2026-09-20 23:47:47 +08:00

288 行
6.9 KiB
Plaintext

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

/**
* 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<NFCRecord> {
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<NFCRecord | null> {
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;
}
}