文件
OmniCard/mpuniapp/pages/owner/useRadar.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

320 行
7.1 KiB
Plaintext

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

/**
* OmniCard AI (CRMmp) - 名片主移动实时雷达业务驱动层
* 分页拉取访客轨迹,解析列表 / 空态 / 错误文案
*/
import { OwnerPageData, OwnerPageQuery, RadarTrack } from '../../common/types.uts';
import { fetchOwnerRadarPage } from '../../common/api.uts';
import { getCurrentLocale, getOmniText } from '../../common/locale.uts';
export type RadarCopyKey =
| 'title'
| 'desc'
| 'cardIdLabel'
| 'cardIdPlaceholder'
| 'refresh'
| 'empty'
| 'error'
| 'stay'
| 'depth';
export type LoadTracksInput = {
cardId?: string | number | null;
page?: number;
size?: number;
};
export type RadarFetchApi = {
fetchOwnerRadarPage: (query: OwnerPageQuery) => Promise<OwnerPageData<RadarTrack>>;
};
export type LoadTracksResult = {
list: RadarTrack[];
empty: boolean;
errorMessage: string;
page: number;
size: number;
total: number;
};
export type RadarState = {
cardIdRaw: string;
page: number;
size: number;
total: number;
loading: boolean;
empty: boolean;
errorMessage: string;
tracks: RadarTrack[];
};
export type RadarPageOptions = {
cardId?: string;
id?: string;
};
const DEFAULT_PAGE = 1;
const DEFAULT_SIZE = 20;
function isZhLocale(): boolean {
return getCurrentLocale() === 'zh-CN';
}
/**
* 页面文案:标题与轨迹走词典,其余中英本地词典
*/
export function resolveRadarText(key: RadarCopyKey): string {
const locale = getCurrentLocale();
if (key === 'title') {
return getOmniText(locale, 'radar.online_visitor');
}
if (key === 'desc') {
return getOmniText(locale, 'radar.visitor_trace');
}
if (key === 'empty') {
return getOmniText(locale, 'common.empty');
}
if (key === 'error') {
return getOmniText(locale, 'common.error');
}
const zh = isZhLocale();
if (key === 'cardIdLabel') {
return zh ? '名片编号' : 'Card ID';
}
if (key === 'cardIdPlaceholder') {
return zh ? '可选,输入名片编号' : 'Optional card ID';
}
if (key === 'refresh') {
return zh ? '刷新轨迹' : 'Refresh trail';
}
if (key === 'stay') {
return zh ? '停留' : 'Stay';
}
if (key === 'depth') {
return zh ? '滚动深度' : 'Scroll depth';
}
return '';
}
/**
* 解析为正整数。非正整数返回 null
*/
export function parseCardId(raw: string | number | null | undefined): number | null {
if (raw == null) {
return null;
}
if (typeof raw === 'number') {
if (raw <= 0) {
return null;
}
if (Math.floor(raw) !== raw) {
return null;
}
return raw;
}
const text = String(raw).trim();
if (text === '') {
return null;
}
if (!/^[1-9]\d*$/.test(text)) {
return null;
}
return Number(text);
}
function normalizePage(raw: number | null | undefined): number {
if (raw == null || !Number.isFinite(raw) || raw < 1) {
return DEFAULT_PAGE;
}
return Math.floor(raw);
}
function normalizeSize(raw: number | null | undefined): number {
if (raw == null || !Number.isFinite(raw) || raw < 1) {
return DEFAULT_SIZE;
}
return Math.floor(raw);
}
/**
* 停留时长:不足 1 秒用 ms,否则保留一位小数的秒
*/
export function formatStay(ms: number | null | undefined): string {
if (ms == null || !Number.isFinite(ms) || ms <= 0) {
return '0ms';
}
const safe = Math.round(ms);
if (safe < 1000) {
return `${safe}ms`;
}
return `${(safe / 1000).toFixed(1)}s`;
}
/**
* 滚动深度:纠偏到 0~100 后以百分比展示
*/
export function formatDepth(depth: number | null | undefined): string {
if (depth == null || !Number.isFinite(depth)) {
return '0%';
}
const clamped = Math.min(100, Math.max(0, Math.round(depth)));
return `${clamped}%`;
}
/**
* 国家 / 城市一行展示
*/
export function formatLocation(country?: string, city?: string): string {
const countryText = country != null ? String(country).trim() : '';
const cityText = city != null ? String(city).trim() : '';
if (countryText !== '' && cityText !== '') {
return `${countryText} / ${cityText}`;
}
if (countryText !== '') {
return countryText;
}
if (cityText !== '') {
return cityText;
}
return '';
}
function readErrorMessage(err: unknown): string {
if (err instanceof Error && err.message !== '') {
return err.message;
}
return resolveRadarText('error');
}
function parseTrackList(payload: OwnerPageData<RadarTrack> | null | undefined): RadarTrack[] {
if (payload == null || payload.list == null) {
return [];
}
if (!Array.isArray(payload.list)) {
return [];
}
return payload.list;
}
/**
* 拉取雷达分页。可注入 fetchOwnerRadarPage,缺省走 common/api
*/
export async function loadTracks(
input: LoadTracksInput,
api?: RadarFetchApi
): Promise<LoadTracksResult> {
const page = normalizePage(input.page);
const size = normalizeSize(input.size);
const query: OwnerPageQuery = {
page,
size,
};
const cardId = parseCardId(input.cardId);
if (cardId != null) {
query.cardId = cardId;
}
const fetchPage =
api != null && api.fetchOwnerRadarPage != null
? api.fetchOwnerRadarPage
: fetchOwnerRadarPage;
try {
const payload = await fetchPage(query);
const list = parseTrackList(payload);
const pagination =
payload != null && payload.pagination != null
? payload.pagination
: { page, size, total: list.length };
return {
list,
empty: list.length === 0,
errorMessage: '',
page: normalizePage(pagination.page),
size: normalizeSize(pagination.size),
total:
pagination.total != null && Number.isFinite(pagination.total)
? Math.max(0, pagination.total)
: list.length,
};
} catch (err) {
return {
list: [],
empty: true,
errorMessage: readErrorMessage(err),
page,
size,
total: 0,
};
}
}
export function createRadarState(): RadarState {
return {
cardIdRaw: '',
page: DEFAULT_PAGE,
size: DEFAULT_SIZE,
total: 0,
loading: false,
empty: true,
errorMessage: '',
tracks: [],
};
}
export function applyRadarOptions(state: RadarState, options: RadarPageOptions | null): void {
if (options == null) {
return;
}
const cardRaw =
options.cardId != null && options.cardId !== ''
? options.cardId
: options.id != null
? options.id
: '';
if (cardRaw !== '') {
state.cardIdRaw = String(cardRaw).trim();
}
}
/**
* 页面主 CTA:刷新当前页轨迹
*/
export async function refreshTracks(
state: RadarState,
api?: RadarFetchApi
): Promise<LoadTracksResult> {
if (state.loading) {
return {
list: state.tracks,
empty: state.empty,
errorMessage: state.errorMessage,
page: state.page,
size: state.size,
total: state.total,
};
}
state.loading = true;
state.errorMessage = '';
const result = await loadTracks(
{
cardId: state.cardIdRaw,
page: state.page,
size: state.size,
},
api
);
state.loading = false;
state.tracks = result.list;
state.empty = result.empty;
state.errorMessage = result.errorMessage;
state.page = result.page;
state.size = result.size;
state.total = result.total;
return result;
}