补齐平台订阅、外部登录、CRM 同步与用户端页面;Nginx 转发用户端接口,登录页使用 Google 与 Apple 标志。 Co-authored-by: Cursor <cursoragent@cursor.com>
331 行
8.7 KiB
Plaintext
331 行
8.7 KiB
Plaintext
/**
|
|
* OmniCard AI (CRMmp) - 智能移动销售工作台业务逻辑与状态机 (Sales Workplace Hub)
|
|
* 严格遵循 AGENTS.md 规范与 uts 强类型设计
|
|
*/
|
|
|
|
import {
|
|
CardProfile,
|
|
LeadEntity,
|
|
WorkplaceKpi,
|
|
WorkplaceOverviewResult,
|
|
WorkplaceRadarItem,
|
|
} from '../../../common/types.uts';
|
|
import {
|
|
fetchCardQrcodeSvg,
|
|
fetchOwnerCardPage,
|
|
fetchOwnerLeadPage,
|
|
fetchWorkplaceOverview,
|
|
} from '../../../common/api.uts';
|
|
import { getCurrentLocale } from '../../../common/locale.uts';
|
|
|
|
export interface WorkplaceState {
|
|
loading: boolean;
|
|
errorMessage: string;
|
|
activeCard: CardProfile | null;
|
|
cardList: CardProfile[];
|
|
activeQrSvg: string;
|
|
activeShareUrl: string;
|
|
kpis: WorkplaceKpi;
|
|
hotRadarItem: WorkplaceRadarItem | null;
|
|
recentLeads: LeadEntity[];
|
|
radarOnline: boolean;
|
|
}
|
|
|
|
export function createWorkplaceState(): WorkplaceState {
|
|
return {
|
|
loading: true,
|
|
errorMessage: '',
|
|
activeCard: null,
|
|
cardList: [],
|
|
activeQrSvg: '',
|
|
activeShareUrl: '',
|
|
kpis: {
|
|
tapCount: 0,
|
|
viewCount: 0,
|
|
newLeadCount: 0,
|
|
conversionRate: 0,
|
|
tapDelta: 0,
|
|
viewDelta: 0,
|
|
newLeadDelta: 0,
|
|
},
|
|
hotRadarItem: null,
|
|
recentLeads: [],
|
|
radarOnline: true,
|
|
};
|
|
}
|
|
|
|
export async function loadWorkplaceData(state: WorkplaceState): Promise<void> {
|
|
state.loading = true;
|
|
state.errorMessage = '';
|
|
|
|
try {
|
|
// 1. 并行获取工作台概览、名片列表、最新待跟进线索
|
|
const overviewPromise = fetchWorkplaceOverview(5).then((data: WorkplaceOverviewResult) => {
|
|
state.radarOnline = true;
|
|
return data;
|
|
}).catch(() => {
|
|
state.radarOnline = false;
|
|
const emptyKpi: WorkplaceKpi = {
|
|
tapCount: 0,
|
|
viewCount: 0,
|
|
newLeadCount: 0,
|
|
conversionRate: 0,
|
|
tapDelta: 0,
|
|
viewDelta: 0,
|
|
newLeadDelta: 0,
|
|
};
|
|
const emptyResult: WorkplaceOverviewResult = {
|
|
kpis: emptyKpi,
|
|
radarFeed: [],
|
|
};
|
|
return emptyResult;
|
|
});
|
|
|
|
const cardsPromise = fetchOwnerCardPage({ page: 1, size: 10 }).catch(() => null);
|
|
const leadsPromise = fetchOwnerLeadPage({ page: 1, size: 3 }).catch(() => null);
|
|
|
|
const [overview, cardPage, leadPage] = await Promise.all([
|
|
overviewPromise,
|
|
cardsPromise,
|
|
leadsPromise,
|
|
]);
|
|
|
|
// 2. 赋值 KPI 与雷达高光
|
|
if (overview != null && overview.kpis != null) {
|
|
state.kpis = overview.kpis;
|
|
}
|
|
state.hotRadarItem = null;
|
|
if (overview != null && overview.radarFeed != null && overview.radarFeed.length > 0) {
|
|
state.hotRadarItem = overview.radarFeed[0];
|
|
}
|
|
|
|
if (leadPage != null && leadPage.list != null) {
|
|
state.recentLeads = leadPage.list;
|
|
} else {
|
|
state.recentLeads = [];
|
|
}
|
|
|
|
if (cardPage != null && cardPage.list != null && cardPage.list.length > 0) {
|
|
state.cardList = cardPage.list;
|
|
const published = cardPage.list.find((c: CardProfile) => c.status === 'published');
|
|
state.activeCard = published != null ? published : cardPage.list[0];
|
|
} else {
|
|
state.activeCard = null;
|
|
state.cardList = [];
|
|
state.activeQrSvg = '';
|
|
state.activeShareUrl = '';
|
|
}
|
|
|
|
if (state.activeCard != null) {
|
|
state.activeQrSvg = '';
|
|
state.activeShareUrl = '';
|
|
try {
|
|
const slugOrId = state.activeCard!.slug || String(state.activeCard!.id);
|
|
const qrRes = await fetchCardQrcodeSvg(slugOrId);
|
|
if (qrRes != null && qrRes.svg != null && qrRes.svg !== '') {
|
|
state.activeQrSvg = qrRes.svg;
|
|
} else {
|
|
state.activeQrSvg = '';
|
|
}
|
|
if (qrRes != null && qrRes.url != null && qrRes.url !== '') {
|
|
state.activeShareUrl = qrRes.url;
|
|
} else if (state.activeCard!.url != null && state.activeCard!.url !== '') {
|
|
state.activeShareUrl = state.activeCard!.url!;
|
|
}
|
|
} catch (_e) {
|
|
state.activeQrSvg = '';
|
|
state.activeShareUrl = '';
|
|
}
|
|
} else {
|
|
state.activeQrSvg = '';
|
|
state.activeShareUrl = '';
|
|
}
|
|
if (state.activeCard == null && state.recentLeads.length === 0 && state.radarOnline === false) {
|
|
state.errorMessage = resolveWorkplaceText('workplace.load_error');
|
|
}
|
|
} catch (_err) {
|
|
state.errorMessage = resolveWorkplaceText('workplace.load_error');
|
|
state.activeQrSvg = '';
|
|
state.activeShareUrl = '';
|
|
} finally {
|
|
state.loading = false;
|
|
}
|
|
}
|
|
|
|
export function buildPortalPreviewUrl(card: CardProfile): string {
|
|
if (card.slug != null && card.slug !== '') {
|
|
return `/pages/portal/index?slug=${card.slug}&mode=owner`;
|
|
}
|
|
if (card.id != null) {
|
|
return `/pages/portal/index?id=${card.id}&mode=owner`;
|
|
}
|
|
return '/pages/portal/index?mode=owner';
|
|
}
|
|
|
|
export function openCardPreview(card: CardProfile): void {
|
|
uni.navigateTo({
|
|
url: buildPortalPreviewUrl(card),
|
|
});
|
|
}
|
|
|
|
export function openCardQrcode(card: CardProfile): void {
|
|
const targetId = card.slug || String(card.id || '');
|
|
uni.navigateTo({
|
|
url: `/pages/owner/card/qrcode?id=${targetId}`,
|
|
});
|
|
}
|
|
|
|
export function openLeadDetail(leadId: string | number): void {
|
|
uni.navigateTo({
|
|
url: `/pages/owner/leads-detail?id=${leadId}`,
|
|
});
|
|
}
|
|
|
|
export function openRadarPage(): void {
|
|
uni.switchTab({
|
|
url: '/pages/owner/radar',
|
|
});
|
|
}
|
|
|
|
export function openCardsPage(): void {
|
|
uni.switchTab({
|
|
url: '/pages/owner/card/index',
|
|
});
|
|
}
|
|
|
|
export function openLeadsPage(): void {
|
|
uni.switchTab({
|
|
url: '/pages/owner/leads',
|
|
});
|
|
}
|
|
|
|
export function resolveWorkplaceText(key: string, locale?: string): string {
|
|
const targetLocale = locale != null ? locale : getCurrentLocale();
|
|
const isZh = targetLocale.startsWith('zh');
|
|
|
|
const dict: Record<string, Record<string, string>> = {
|
|
'workplace.title': {
|
|
zh: '销售工作台',
|
|
en: 'Sales Workplace',
|
|
},
|
|
'workplace.greeting': {
|
|
zh: '今日业务概况与线索跟进',
|
|
en: 'Today business overview & lead follow-up',
|
|
},
|
|
'workplace.radar_active': {
|
|
zh: '雷达在线监测中',
|
|
en: 'Radar Live Active',
|
|
},
|
|
'workplace.quick_showcase': {
|
|
zh: '出示名片',
|
|
en: 'Show Card',
|
|
},
|
|
'workplace.view_portal': {
|
|
zh: '预览微官网',
|
|
en: 'View Micro-Site',
|
|
},
|
|
'workplace.copy_link': {
|
|
zh: '复制直链',
|
|
en: 'Copy Link',
|
|
},
|
|
'workplace.link_copied': {
|
|
zh: '名片直链已复制',
|
|
en: 'Card link copied to clipboard',
|
|
},
|
|
'workplace.nfc_active': {
|
|
zh: 'NFC 展会模式就绪',
|
|
en: 'NFC Business Mode Ready',
|
|
},
|
|
'workplace.kpi_title': {
|
|
zh: '今日数据',
|
|
en: 'Today KPIs',
|
|
},
|
|
'workplace.kpi_taps': {
|
|
zh: '触碰/扫码',
|
|
en: 'Taps & Scans',
|
|
},
|
|
'workplace.kpi_visitors': {
|
|
zh: '官网访客数',
|
|
en: 'Visitors',
|
|
},
|
|
'workplace.kpi_leads': {
|
|
zh: '新增线索',
|
|
en: 'New Leads',
|
|
},
|
|
'workplace.kpi_conversion': {
|
|
zh: '意向转化率',
|
|
en: 'Intent Rate',
|
|
},
|
|
'workplace.radar_pulse_title': {
|
|
zh: '实时访客',
|
|
en: 'Live Visitors',
|
|
},
|
|
'workplace.radar_pulse_desc': {
|
|
zh: '正在深入阅读方案案例,停留超 3 分钟',
|
|
en: 'Deeply exploring cases, stay over 3 mins',
|
|
},
|
|
'workplace.radar_action_whatsapp': {
|
|
zh: '立即联系',
|
|
en: 'Contact Now',
|
|
},
|
|
'workplace.leads_todo_title': {
|
|
zh: '今日待办商机',
|
|
en: 'Actionable Leads',
|
|
},
|
|
'workplace.leads_view_all': {
|
|
zh: '查看全部',
|
|
en: 'View All',
|
|
},
|
|
'workplace.ai_tip_title': {
|
|
zh: '跟进建议',
|
|
en: 'Follow-up Suggestion',
|
|
},
|
|
'workplace.ai_tip_desc': {
|
|
zh: '近期重点客户对解决方案案例关注度上升,建议及时跟进。',
|
|
en: 'Prospect engagement on solution cases is rising. Follow-up recommended.',
|
|
},
|
|
'workplace.primary_cta': {
|
|
zh: '出示名片码',
|
|
en: 'Show Card QR',
|
|
},
|
|
'workplace.qr_empty': {
|
|
zh: '暂无二维码',
|
|
en: 'QR code unavailable',
|
|
},
|
|
'workplace.empty_card': {
|
|
zh: '还没有名片',
|
|
en: 'No card yet',
|
|
},
|
|
'workplace.empty_leads': {
|
|
zh: '今天还没有要跟进的线索',
|
|
en: 'No leads to follow up',
|
|
},
|
|
'workplace.empty_radar': {
|
|
zh: '还没有新的访客',
|
|
en: 'No new visitors',
|
|
},
|
|
'workplace.switch_card': {
|
|
zh: '切换',
|
|
en: 'Switch',
|
|
},
|
|
'workplace.published': {
|
|
zh: '已发布',
|
|
en: 'Published',
|
|
},
|
|
'workplace.load_error': {
|
|
zh: '工作台暂时没加载出来',
|
|
en: 'Workplace could not be loaded',
|
|
},
|
|
'workplace.score': {
|
|
zh: '意向',
|
|
en: 'Intent',
|
|
},
|
|
};
|
|
|
|
const item = dict[key];
|
|
if (item != null) {
|
|
return isZh ? item.zh : item.en;
|
|
}
|
|
return key;
|
|
}
|