/** * OmniCard AI (CRMmp) - 名片主极速编辑业务驱动层 * 拉名片详情,仅提交已填字段,不做 Bento 搭建 */ import { CardProfile } from '../../../common/types.uts'; import { fetchOwnerCardInfo, updateOwnerCard } from '../../../common/api.uts'; import { getCurrentLocale, getOmniText } from '../../../common/locale.uts'; export type CardEditCopyKey = | 'heading' | 'name' | 'jobTitle' | 'company' | 'phone' | 'email' | 'whatsapp' | 'linkedin' | 'save' | 'loading' | 'success' | 'error' | 'empty' | 'missingId' | 'back' | 'avatarUpload' | 'avatarChange' | 'avatarPlaceholder'; export type CardEditForm = { id?: string | number | null; name?: string; title?: string; company?: string; slogan?: string; avatarUrl?: string; phone?: string; email?: string; whatsapp?: string; linkedin?: string; telegram?: string; twitter?: string; wechat?: string; }; export type CardUpdatePayload = { id: string | number; name?: string; title?: string; company?: string; slogan?: string; avatarUrl?: string; phone?: string; email?: string; whatsapp?: string; linkedin?: string; telegram?: string; twitter?: string; wechat?: string; }; export type LoadCardApi = { fetchOwnerCardInfo?: (id: string | number) => Promise; }; export type LoadCardResult = { card: CardProfile | null; empty: boolean; errorMessage: string; }; export type SaveCardApi = { updateOwnerCard?: ( payload: CardProfile & { id: string | number } ) => Promise; }; export type SaveCardResult = { ok: boolean; card: CardProfile | null; errorMessage: string; }; export type CardEditPageOptions = { id?: string; }; export type CardEditState = { rawId: string; cardId: number | null; name: string; title: string; company: string; avatarUrl: string; phone: string; email: string; whatsapp: string; linkedin: string; loading: boolean; saving: boolean; empty: boolean; missingId: boolean; errorMessage: string; }; function isZhLocale(): boolean { return getCurrentLocale() === 'zh-CN'; } /** * 页面文案:保存 / 社交走词典,字段标签走本地词典 */ export function resolveCardEditText(key: CardEditCopyKey): string { const locale = getCurrentLocale(); if (key === 'save') { return getOmniText(locale, 'common.save'); } if (key === 'loading') { return getOmniText(locale, 'common.loading'); } if (key === 'success') { return getOmniText(locale, 'common.success'); } if (key === 'error') { return getOmniText(locale, 'common.error'); } if (key === 'empty') { return getOmniText(locale, 'common.empty'); } if (key === 'whatsapp') { return getOmniText(locale, 'portal.whatsapp'); } if (key === 'linkedin') { return getOmniText(locale, 'portal.linkedin'); } const zh = isZhLocale(); if (key === 'heading') { return zh ? '极速编辑' : 'Quick edit'; } if (key === 'name') { return zh ? '姓名' : 'Name'; } if (key === 'jobTitle') { return zh ? '头衔' : 'Title'; } if (key === 'company') { return zh ? '公司' : 'Company'; } if (key === 'phone') { return zh ? '电话' : 'Phone'; } if (key === 'email') { return zh ? '邮箱' : 'Email'; } if (key === 'missingId') { return zh ? '缺少名片编号' : 'Card ID is missing'; } if (key === 'back') { return zh ? '返回名片' : 'Back to card'; } if (key === 'avatarUpload') { return zh ? '点击拍照/上传' : 'Upload photo'; } if (key === 'avatarChange') { return zh ? '更换头像' : 'Change avatar'; } if (key === 'avatarPlaceholder') { return zh ? '上传头像' : 'Upload avatar'; } return ''; } /** * 解析为正整数。非正整数返回 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 pickFilled(value: string | null | undefined): string | null { if (value == null) { return null; } const text = String(value).trim(); if (text === '') { return null; } return text; } function assignFilled( payload: CardUpdatePayload, key: | 'name' | 'title' | 'company' | 'slogan' | 'avatarUrl' | 'phone' | 'email' | 'whatsapp' | 'linkedin' | 'telegram' | 'twitter' | 'wechat', value: string | null | undefined ): void { const filled = pickFilled(value); if (filled != null) { payload[key] = filled; } } /** * 只带已填字段与解析后的 id。缺 id 返回 null */ export function buildUpdatePayload(form: CardEditForm): CardUpdatePayload | null { const id = parseCardId(form.id); if (id == null) { return null; } const payload: CardUpdatePayload = { id }; assignFilled(payload, 'name', form.name); assignFilled(payload, 'title', form.title); assignFilled(payload, 'company', form.company); assignFilled(payload, 'slogan', form.slogan); assignFilled(payload, 'avatarUrl', form.avatarUrl); assignFilled(payload, 'phone', form.phone); assignFilled(payload, 'email', form.email); assignFilled(payload, 'whatsapp', form.whatsapp); assignFilled(payload, 'linkedin', form.linkedin); assignFilled(payload, 'telegram', form.telegram); assignFilled(payload, 'twitter', form.twitter); assignFilled(payload, 'wechat', form.wechat); return payload; } function readErrorMessage(err: unknown): string { if (err instanceof Error && err.message !== '') { return err.message; } return resolveCardEditText('error'); } function asUpdatePayload( payload: CardUpdatePayload ): CardProfile & { id: string | number } { return payload as CardProfile & { id: string | number }; } /** * 拉取名片详情。可注入 fetchOwnerCardInfo。缺 id 不请求 */ export async function loadCard( id: string | number | null | undefined, api?: LoadCardApi ): Promise { const cardId = parseCardId(id); if (cardId == null) { return { card: null, empty: true, errorMessage: '', }; } const fetchInfo = api != null && api.fetchOwnerCardInfo != null ? api.fetchOwnerCardInfo : fetchOwnerCardInfo; try { const card = await fetchInfo(cardId); return { card, empty: false, errorMessage: '', }; } catch (err) { return { card: null, empty: true, errorMessage: readErrorMessage(err), }; } } /** * 保存名片。可注入 updateOwnerCard。缺 id 不请求 */ export async function saveCard( form: CardEditForm, api?: SaveCardApi ): Promise { const payload = buildUpdatePayload(form); if (payload == null) { return { ok: false, card: null, errorMessage: '', }; } const update = api != null && api.updateOwnerCard != null ? api.updateOwnerCard : updateOwnerCard; try { const card = await update(asUpdatePayload(payload)); return { ok: true, card, errorMessage: '', }; } catch (err) { return { ok: false, card: null, errorMessage: readErrorMessage(err), }; } } export function createCardEditState(): CardEditState { return { rawId: '', cardId: null, name: '', title: '', company: '', avatarUrl: '', phone: '', email: '', whatsapp: '', linkedin: '', loading: false, saving: false, empty: false, missingId: false, errorMessage: '', }; } export function applyCardEditOptions( state: CardEditState, options: CardEditPageOptions | null ): boolean { if (options == null || options.id == null) { state.rawId = ''; state.cardId = null; state.missingId = true; state.empty = true; return false; } const parsed = parseCardId(options.id); state.rawId = String(options.id).trim(); state.cardId = parsed; state.missingId = parsed == null; state.empty = parsed == null; return parsed != null; } function readOptionalText(value: string | null | undefined): string { if (value == null) { return ''; } return String(value); } function applyCardToState(state: CardEditState, card: CardProfile): void { if (card.id != null) { const parsed = parseCardId(card.id); if (parsed != null) { state.cardId = parsed; } } state.name = card.name != null ? String(card.name) : ''; state.title = readOptionalText(card.title); state.company = readOptionalText(card.company); state.avatarUrl = readOptionalText(card.avatarUrl); state.phone = readOptionalText(card.phone); state.email = readOptionalText(card.email); state.whatsapp = readOptionalText(card.whatsapp); state.linkedin = readOptionalText(card.linkedin); } export function applyLoadResult( state: CardEditState, result: LoadCardResult ): void { if (result.card != null) { applyCardToState(state, result.card); state.empty = false; state.missingId = false; } else { state.empty = true; } state.errorMessage = result.errorMessage; } export function formFromState(state: CardEditState): CardEditForm { return { id: state.cardId, name: state.name, title: state.title, company: state.company, avatarUrl: state.avatarUrl, phone: state.phone, email: state.email, whatsapp: state.whatsapp, linkedin: state.linkedin, }; } export async function refreshCardEdit( state: CardEditState, api?: LoadCardApi ): Promise { if (state.missingId || state.cardId == null) { const skipped: LoadCardResult = { card: null, empty: true, errorMessage: '', }; applyLoadResult(state, skipped); state.missingId = true; return skipped; } if (state.loading) { return { card: null, empty: state.empty, errorMessage: state.errorMessage, }; } state.loading = true; state.errorMessage = ''; const result = await loadCard(state.cardId, api); applyLoadResult(state, result); state.loading = false; return result; } export async function submitCardEdit( state: CardEditState, api?: SaveCardApi ): Promise { if (state.cardId == null || state.saving) { return false; } state.saving = true; state.errorMessage = ''; const result = await saveCard(formFromState(state), api); if (result.ok && result.card != null) { applyCardToState(state, result.card); state.saving = false; return true; } state.saving = false; state.errorMessage = result.errorMessage; return false; } export function goBackFromCardEdit(): void { uni.navigateBack({ delta: 1, fail: () => { uni.switchTab({ url: '/pages/owner/card/index', }); }, }); }