/** * OmniCard AI (CRMmp) - 名片主移动潜客列表业务驱动层 * 分页拉线索、过滤非法 status、空态与错误 */ import { LeadEntity, LeadStatus, OwnerPageData, OwnerPageQuery, } from '../../common/types.uts'; import { fetchOwnerLeadPage } from '../../common/api.uts'; import { getCurrentLocale, getOmniText } from '../../common/locale.uts'; export type LeadsCopyKey = | 'title' | 'followUp' | 'searchPlaceholder' | 'cta' | 'statusAll' | 'statusNew' | 'statusContacted' | 'statusHot' | 'statusWon' | 'statusArchived' | 'empty' | 'error' | 'loading' | 'score'; export type LoadLeadsQuery = { page: number; size: number; status?: string; keyWord?: string; }; export type LoadLeadsApi = { fetchOwnerLeadPage: ( query: OwnerPageQuery ) => Promise>; }; export type LoadLeadsResult = { list: LeadEntity[]; pagination: { page: number; size: number; total: number; }; empty: boolean; errorMessage: string; }; export type LeadsState = { list: LeadEntity[]; page: number; size: number; total: number; status: string; keyWord: string; loading: boolean; empty: boolean; errorMessage: string; }; export type LeadsPageOptions = { status?: string; keyWord?: string; page?: string; size?: string; }; export const LEAD_STATUS_VALUES: LeadStatus[] = [ 'new', 'contacted', 'hot', 'won', 'archived', ]; function isZhLocale(): boolean { return getCurrentLocale() === 'zh-CN'; } export function parseLeadStatus( raw: string | null | undefined ): LeadStatus | null { if ( raw === 'new' || raw === 'contacted' || raw === 'hot' || raw === 'won' || raw === 'archived' ) { return raw; } return null; } /** * 页面文案:crm.leads / crm.follow_up 与通用词条走词典,状态筛选用本地词典 */ export function resolveLeadsText(key: LeadsCopyKey): string { const locale = getCurrentLocale(); if (key === 'title') { return getOmniText(locale, 'crm.leads'); } if (key === 'followUp') { return getOmniText(locale, 'crm.follow_up'); } if (key === 'empty') { return getOmniText(locale, 'common.empty'); } if (key === 'error') { return getOmniText(locale, 'common.error'); } if (key === 'loading') { return getOmniText(locale, 'common.loading'); } if (key === 'cta') { return getOmniText(locale, 'common.search'); } const zh = isZhLocale(); if (key === 'searchPlaceholder') { return zh ? '搜索姓名或公司' : 'Search name or company'; } if (key === 'statusAll') { return zh ? '全部' : 'All'; } if (key === 'statusNew') { return zh ? '新线索' : 'New'; } if (key === 'statusContacted') { return zh ? '已联系' : 'Contacted'; } if (key === 'statusHot') { return zh ? '高意向' : 'Hot'; } if (key === 'statusWon') { return zh ? '已成交' : 'Won'; } if (key === 'statusArchived') { return zh ? '已归档' : 'Archived'; } if (key === 'score') { return zh ? '意向分' : 'Intent'; } return ''; } export function statusLabel(status: string): string { if (status === 'new') { return resolveLeadsText('statusNew'); } if (status === 'contacted') { return resolveLeadsText('statusContacted'); } if (status === 'hot') { return resolveLeadsText('statusHot'); } if (status === 'won') { return resolveLeadsText('statusWon'); } if (status === 'archived') { return resolveLeadsText('statusArchived'); } return status; } export function formatLeadCompany(company: string | null | undefined): string { if (company == null) { return ''; } return company; } export function sanitizeLeadQuery(query: LoadLeadsQuery): OwnerPageQuery { const next: OwnerPageQuery = { page: query.page, size: query.size, }; const status = parseLeadStatus(query.status); if (status != null) { next.status = status; } if (query.keyWord != null) { const word = query.keyWord.trim(); if (word !== '') { next.keyWord = word; } } return next; } function readErrorMessage(err: unknown): string { if (err instanceof Error && err.message !== '') { return err.message; } return resolveLeadsText('error'); } /** * 拉取潜客分页。可注入 fetchOwnerLeadPage;非法 status 不入请求 */ export async function loadLeads( query: LoadLeadsQuery, api?: LoadLeadsApi ): Promise { const fetchPage = api != null ? api.fetchOwnerLeadPage : fetchOwnerLeadPage; try { const data = await fetchPage(sanitizeLeadQuery(query)); const list = data.list != null ? data.list : []; const pagination = data.pagination != null ? data.pagination : { page: query.page, size: query.size, total: list.length }; return { list, pagination, empty: list.length === 0, errorMessage: '', }; } catch (err) { return { list: [], pagination: { page: query.page, size: query.size, total: 0 }, empty: false, errorMessage: readErrorMessage(err), }; } } export function createLeadsState(): LeadsState { return { list: [], page: 1, size: 20, total: 0, status: '', keyWord: '', loading: false, empty: false, errorMessage: '', }; } export function applyLeadsOptions( state: LeadsState, options: LeadsPageOptions | null ): void { if (options == null) { return; } const status = parseLeadStatus(options.status); state.status = status != null ? status : ''; if (options.keyWord != null) { state.keyWord = options.keyWord; } } export function applyLeadStatusFilter(state: LeadsState, status: string): void { const parsed = parseLeadStatus(status); state.status = parsed != null ? parsed : ''; state.page = 1; } export function applyLoadResult(state: LeadsState, result: LoadLeadsResult): void { state.list = result.list; state.page = result.pagination.page; state.size = result.pagination.size; state.total = result.pagination.total; state.empty = result.empty; state.errorMessage = result.errorMessage; } export async function refreshLeads( state: LeadsState, api?: LoadLeadsApi ): Promise { if (state.loading) { return { list: state.list, pagination: { page: state.page, size: state.size, total: state.total, }, empty: state.empty, errorMessage: state.errorMessage, }; } state.loading = true; state.errorMessage = ''; const result = await loadLeads( { page: state.page, size: state.size, status: state.status, keyWord: state.keyWord, }, api ); applyLoadResult(state, result); state.loading = false; return result; } export function buildLeadDetailUrl(id: string | number): string { return `/pages/owner/leads-detail?id=${id}`; } export function openLeadDetail(id: string | number): void { uni.navigateTo({ url: buildLeadDetailUrl(id), }); }