/** * OmniCard AI (CRMmp) - 无摩擦互换名片留资业务驱动层 * 严格遵循 uts 强类型规范与 AGENTS.md 约束 */ import { LeadEntity, SubmitLeadParams } from '../../common/types.uts'; import { submitLead } from '../../common/api.uts'; export type ExchangeForm = { name: string; company: string; phone: string; email: string; notes: string; }; export type ExchangeState = { form: ExchangeForm; cardId: string; slug: string; submitting: boolean; canSubmit: boolean; errorMessage: string; }; export type ExchangePageOptions = { cardId?: string; slug?: string; id?: string; }; const VISITOR_STORAGE_KEY = 'omni_visitor_id'; /** * 读取本地已有访客标识,不在本页新建 */ function readVisitorId(): string { try { const val = uni.getStorageSync(VISITOR_STORAGE_KEY); return val != null ? String(val) : ''; } catch (e) { return ''; } } /** * 创建互换名片表单初始状态 */ export function createExchangeState(): ExchangeState { return { form: { name: '', company: '', phone: '', email: '', notes: '', }, cardId: '', slug: '', submitting: false, canSubmit: false, errorMessage: '', }; } /** * 解析提交用的名片标识:优先 cardId,其次 slug */ export function resolveExchangeCardId(state: ExchangeState): string { if (state.cardId && state.cardId.trim() !== '') { return state.cardId.trim(); } if (state.slug && state.slug.trim() !== '') { return state.slug.trim(); } return ''; } /** * 从页面 onLoad options 写入名片标识 * 缺少 cardId 与 slug 时 toast 并禁止提交 */ export function applyExchangeOptions( state: ExchangeState, options: ExchangePageOptions | null ): boolean { const cardIdRaw = options != null ? options.cardId || options.id || '' : ''; const slugRaw = options != null ? options.slug || '' : ''; state.cardId = String(cardIdRaw).trim(); state.slug = String(slugRaw).trim(); state.canSubmit = resolveExchangeCardId(state) !== ''; if (!state.canSubmit) { uni.showToast({ title: '缺少名片标识,无法提交', icon: 'none', }); } return state.canSubmit; } /** * 校验姓名 + (WhatsApp/phone 或 email) * 通过返回空字符串 */ export function validateExchangeForm(form: ExchangeForm): string { const name = form.name ? form.name.trim() : ''; if (name === '') { return '请填写姓名'; } const phone = form.phone ? form.phone.trim() : ''; const email = form.email ? form.email.trim() : ''; if (phone === '' && email === '') { return '请填写 WhatsApp 或邮箱'; } return ''; } /** * 清空表单字段,保留名片标识 */ export function resetExchangeForm(state: ExchangeState): void { state.form.name = ''; state.form.company = ''; state.form.phone = ''; state.form.email = ''; state.form.notes = ''; state.errorMessage = ''; } /** * 提交留资:校验通过后调用已有 submitLead */ export async function submitExchange(state: ExchangeState): Promise { const cardId = resolveExchangeCardId(state); if (!state.canSubmit || cardId === '') { uni.showToast({ title: '缺少名片标识,无法提交', icon: 'none', }); return null; } const invalidReason = validateExchangeForm(state.form); if (invalidReason !== '') { uni.showToast({ title: invalidReason, icon: 'none', }); return null; } if (state.submitting) { return null; } const name = state.form.name.trim(); const company = state.form.company.trim(); const phone = state.form.phone.trim(); const email = state.form.email.trim(); const notes = state.form.notes.trim(); const visitorId = readVisitorId(); const payload: SubmitLeadParams = { cardId, name, }; if (company !== '') { payload.company = company; } if (phone !== '') { payload.phone = phone; } if (email !== '') { payload.email = email; } if (notes !== '') { payload.notes = notes; } if (visitorId !== '') { payload.visitorId = visitorId; } payload.source = 'link'; state.submitting = true; state.errorMessage = ''; try { const lead = await submitLead(payload); state.submitting = false; resetExchangeForm(state); uni.showToast({ title: '留资已提交', icon: 'none', }); return lead; } catch (err) { state.submitting = false; if (err instanceof Error) { state.errorMessage = err.message; } else { state.errorMessage = '提交留资失败'; } return null; } }