文件
OmniCard/mpuniapp/pages/portal/useExchange.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

210 行
4.5 KiB
Plaintext

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