refactor(crm): 潜客详情与跟进页面深度重构与出海触达优化

这个提交包含在:
wikiboss
2026-09-21 15:10:51 +08:00
父节点 daf385c8e0
当前提交 3c8049f686
共有 3 个文件被更改,包括 1161 次插入 和 158 次删除

查看文件

@@ -88,12 +88,24 @@ type UseLeadDetailModule = {
existing: string | null | undefined,
next: string | null | undefined
) => string;
appendStructuredNote: (
existing: string | null | undefined,
next: string | null | undefined,
attachments?: string[]
) => string;
formatWhatsAppUrl: (phone: string | null | undefined) => string;
generateAiIcebreaker: (
lead: LeadEntity | null,
tracks: RadarTrack[],
locale?: string
) => string;
saveNote: (
input: {
id: string | number;
notes?: string;
next: string;
status?: LeadStatus;
attachments?: string[];
},
api?: SaveNoteApi
) => Promise<LeadEntity>;
@@ -262,6 +274,42 @@ describe('OmniCard AI 名片主潜客详情业务逻辑 (useLeadDetail.uts)', ()
expect(result.notes).toBe('Met at GITEX\nAsked for quote');
expect(result.status).toBe('hot');
});
it('3.3 updateLeadStage 调用 updateOwnerLeadStatus 推进商机阶段', async () => {
const { updateLeadStage } = loadUseLeadDetail() as UseLeadDetailModule & {
updateLeadStage: (
state: Record<string, unknown>,
newStatus: LeadStatus,
api?: SaveNoteApi & {
updateOwnerLeadStatus?: (
id: string | number,
status: LeadStatus
) => Promise<LeadEntity>;
}
) => Promise<boolean>;
};
const lead = buildLead({ status: 'new' });
const state: Record<string, unknown> = {
leadId: 21,
lead,
status: 'new',
saving: false,
errorMessage: '',
};
const updateOwnerLeadStatus = jest.fn(
async (id: string | number, status: LeadStatus) => {
expect(id).toBe(21);
expect(status).toBe('hot');
return buildLead({ id, status });
}
);
const ok = await updateLeadStage(state, 'hot', { updateOwnerLeadStatus });
expect(ok).toBe(true);
expect(state.status).toBe('hot');
expect(state.saving).toBe(false);
expect(updateOwnerLeadStatus).toHaveBeenCalledTimes(1);
});
});
describe('4. resolveLeadDetailText 只增文案', () => {
@@ -272,12 +320,58 @@ describe('OmniCard AI 名片主潜客详情业务逻辑 (useLeadDetail.uts)', ()
expect(resolveLeadDetailText('comingSoon')).toBe('即将提供');
expect(resolveLeadDetailText('uploadHint').length).toBeGreaterThan(0);
expect(resolveLeadDetailText('save').length).toBeGreaterThan(0);
expect(resolveLeadDetailText('stageTitle').length).toBeGreaterThan(0);
expect(resolveLeadDetailText('historyTitle').length).toBeGreaterThan(0);
expect(resolveLeadDetailText('stageUpdated').length).toBeGreaterThan(0);
expect(resolveLeadDetailText('copiedToast').length).toBeGreaterThan(0);
expect(resolveLeadDetailText('whatsappAction').length).toBeGreaterThan(0);
expect(resolveLeadDetailText('saveContactAction').length).toBeGreaterThan(0);
expect(resolveLeadDetailText('generateAiAction').length).toBeGreaterThan(0);
});
});
describe('5. 销售触达与 AI 赋能函数', () => {
it('5.1 formatWhatsAppUrl 清洗国际号码并生成 wa.me 链接', () => {
const { formatWhatsAppUrl } = loadUseLeadDetail();
expect(formatWhatsAppUrl('+971-50-1234567')).toBe('https://wa.me/971501234567');
expect(formatWhatsAppUrl('0086 138-0000-0000')).toBe('https://wa.me/8613800000000');
expect(formatWhatsAppUrl('')).toBe('');
expect(formatWhatsAppUrl(null)).toBe('');
expect(formatWhatsAppUrl(undefined)).toBe('');
});
it('5.2 generateAiIcebreaker 生成个性化破冰销售话术', () => {
const { generateAiIcebreaker } = loadUseLeadDetail();
const lead = buildLead({ name: 'Elena Rostova', company: 'Dubai Global Trading' });
const tracks = [buildTrack({ viewedBlocks: ['case', 'pricing'] })];
const scriptZh = generateAiIcebreaker(lead, tracks, 'zh-CN');
expect(scriptZh).toContain('Elena Rostova');
expect(scriptZh.length).toBeGreaterThan(20);
const scriptEn = generateAiIcebreaker(lead, tracks, 'en-US');
expect(scriptEn).toContain('Elena');
expect(scriptEn.length).toBeGreaterThan(20);
});
it('5.3 appendStructuredNote 结构化拼接备忘与附件链接', () => {
const { appendStructuredNote } = loadUseLeadDetail();
const resWithAttach = appendStructuredNote(
'Initial meeting',
'Follow-up call agreed',
['https://cdn.example.com/card.jpg']
);
expect(resWithAttach).toContain('Initial meeting');
expect(resWithAttach).toContain('Follow-up call agreed');
expect(resWithAttach).toContain('https://cdn.example.com/card.jpg');
});
});
});
describe('OmniCard AI 名片主潜客详情页契约 (leads-detail.uvue)', () => {
it('页面存在,足迹用 list-view,备忘用 textarea,单一主 CTA', () => {
it('页面存在,足迹用 list-view,备忘用 textarea,单一主 CTA,支持 WhatsApp 与 AI 破冰', () => {
expect(fs.existsSync(DETAIL_PAGE)).toBe(true);
expect(fs.existsSync(DETAIL_UTS)).toBe(true);
@@ -294,17 +388,24 @@ describe('OmniCard AI 名片主潜客详情页契约 (leads-detail.uvue)', () =>
expect(pageSource.includes('<textarea')).toBe(true);
expect(pageSource.includes('min-height: 88px')).toBe(true);
expect(pageSource.includes('primary-cta-btn')).toBe(true);
expect(template.includes('WhatsApp')).toBe(false);
expect(pageSource.includes('calc(88px + env(safe-area-inset-bottom, 0px))')).toBe(
true
);
expect(pageSource.includes('lead-status-hot')).toBe(true);
expect(pageSource.includes('#F04438')).toBe(true);
expect(pageSource.includes('aiScript')).toBe(true);
expect(pageSource.includes('comingSoon')).toBe(true);
expect(pageSource.includes('uploadHint')).toBe(true);
expect(pageSource.includes('image-uploader')).toBe(true);
expect(logicSource.includes('async function submitLeadNote')).toBe(true);
expect(
combined.includes('handleWhatsApp') || logicSource.includes('formatWhatsAppUrl')
).toBe(true);
expect(
combined.includes('handleSaveContact') || logicSource.includes('saveContactToDevice')
).toBe(true);
expect(
combined.includes('handleGenerateAiScript') || logicSource.includes('generateAiIcebreaker')
).toBe(true);
expect(
combined.includes('fetchOwnerLeadInfo') || combined.includes('loadDetail')
).toBe(true);