/** * OmniCard AI - 名片大厅人脉驱动层 */ import { CardContact, OwnerPageData, OwnerPageQuery, } from '../../../common/types.uts'; import { collectHallContact, confirmHallExchange, fetchEventFloorPage, fetchHallContactPage, fetchTeamWallPage, joinHallEvent, } from '../../../common/api.uts'; import { getCurrentLocale, getOmniText } from '../../../common/locale.uts'; export type HallCopyKey = | 'title' | 'subtitle' | 'searchPlaceholder' | 'refresh' | 'exchangePlaceholder' | 'exchange' | 'empty' | 'error' | 'loading' | 'scan' | 'tabContacts' | 'tabTeam' | 'tabEvent' | 'teamEmpty' | 'eventEmpty' | 'eventPlaceholder' | 'eventJoin'; export type HallContactItem = { id?: string | number; peerCardId: number; peerName: string; peerTitle?: string; peerCompany?: string; peerSlug?: string; source?: string; }; export type HallTab = 'contacts' | 'team' | 'event'; export type HallState = { tab: HallTab; list: HallContactItem[]; page: number; size: number; total: number; keyWord: string; exchangeCode: string; eventCode: string; loading: boolean; empty: boolean; errorMessage: string; }; export type LoadHallApi = { fetchHallContactPage: ( query: OwnerPageQuery ) => Promise>; fetchTeamWallPage?: ( query: OwnerPageQuery ) => Promise>; confirmHallExchange?: ( exchangeCode: string ) => Promise<{ status: string; isDualSynced: boolean }>; collectHallContact?: (peerCardId: string | number) => Promise; fetchEventFloorPage?: ( query: OwnerPageQuery ) => Promise>; joinHallEvent?: ( code: string ) => Promise<{ event: { code: string }; member: { cardId: number } }>; }; function isZhLocale(): boolean { return getCurrentLocale() === 'zh-CN'; } export function resolveHallText(key: HallCopyKey): string { const locale = getCurrentLocale(); 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 === 'scan') { return getOmniText(locale, 'card.scan_nfc'); } const zh = isZhLocale(); if (key === 'title') { return zh ? '名片大厅' : 'Card hall'; } if (key === 'subtitle') { return zh ? '已互换和收藏的名片' : 'Exchanged and saved cards'; } if (key === 'searchPlaceholder') { return zh ? '搜索姓名或公司' : 'Search name or company'; } if (key === 'refresh') { return zh ? '刷新人脉' : 'Refresh network'; } if (key === 'exchangePlaceholder') { return zh ? '输入 6 位互换码' : '6-digit exchange code'; } if (key === 'exchange') { return zh ? '确认互换' : 'Confirm exchange'; } if (key === 'tabContacts') { return zh ? '人脉' : 'Network'; } if (key === 'tabTeam') { return zh ? '团队' : 'Team'; } if (key === 'teamEmpty') { return zh ? '还没有同事发布名片' : 'No published teammate cards yet'; } if (key === 'tabEvent') { return zh ? '现场' : 'Floor'; } if (key === 'eventEmpty') { return zh ? '输入活动码加入现场' : 'Enter an event code to join the floor'; } if (key === 'eventPlaceholder') { return zh ? '输入 6 位活动码' : '6-character event code'; } if (key === 'eventJoin') { return zh ? '加入现场' : 'Join floor'; } return ''; } export function toHallItem(row: CardContact): HallContactItem { return { id: row.id, peerCardId: Number(row.peerCardId), peerName: row.peerName != null ? row.peerName : '', peerTitle: row.peerTitle, peerCompany: row.peerCompany, peerSlug: row.peerSlug, source: row.source, }; } export function createHallState(): HallState { return { tab: 'contacts', list: [], page: 1, size: 20, total: 0, keyWord: '', exchangeCode: '', eventCode: '', loading: false, empty: false, errorMessage: '', }; } function readErrorMessage(err: unknown): string { if (err instanceof Error && err.message !== '') { return err.message; } return resolveHallText('error'); } export function buildHallPortalUrl(item: HallContactItem): string { if (item.peerSlug != null && item.peerSlug !== '') { return `/pages/portal/index?slug=${item.peerSlug}`; } return `/pages/portal/index?id=${item.peerCardId}`; } export function openHallPortal(item: HallContactItem): void { uni.navigateTo({ url: buildHallPortalUrl(item), }); } export async function refreshHall( state: HallState, api?: LoadHallApi ): Promise { if (state.loading) { return; } state.loading = true; state.errorMessage = ''; const fetchPage = state.tab === 'event' ? api != null && api.fetchEventFloorPage != null ? api.fetchEventFloorPage : fetchEventFloorPage : state.tab === 'team' ? api != null && api.fetchTeamWallPage != null ? api.fetchTeamWallPage : fetchTeamWallPage : api != null ? api.fetchHallContactPage : fetchHallContactPage; try { const query: OwnerPageQuery = { page: state.page, size: state.size, }; const word = state.keyWord.trim(); if (word !== '') { query.keyWord = word; } const data = await fetchPage(query); const raw = data.list != null ? data.list : []; const list: HallContactItem[] = []; for (let i = 0; i < raw.length; i++) { list.push(toHallItem(raw[i])); } state.list = list; state.total = data.pagination != null ? data.pagination.total : list.length; state.empty = list.length === 0; } catch (err) { state.list = []; state.empty = false; state.errorMessage = readErrorMessage(err); } state.loading = false; } export async function submitHallExchange( state: HallState, api?: LoadHallApi ): Promise { const code = state.exchangeCode.trim(); if (!/^\d{6}$/.test(code)) { state.errorMessage = resolveHallText('exchangePlaceholder'); return false; } const confirm = api != null && api.confirmHallExchange != null ? api.confirmHallExchange : confirmHallExchange; try { await confirm(code); state.exchangeCode = ''; await refreshHall(state, api); return true; } catch (err) { state.errorMessage = readErrorMessage(err); return false; } } export async function collectPeerCard( peerCardId: string | number, api?: LoadHallApi ): Promise { const collect = api != null && api.collectHallContact != null ? api.collectHallContact : collectHallContact; await collect(peerCardId); return true; } export function applyHallTab(state: HallState, tab: HallTab): void { if (tab === 'team') { state.tab = 'team'; } else if (tab === 'event') { state.tab = 'event'; } else { state.tab = 'contacts'; } state.page = 1; state.errorMessage = ''; } export async function submitHallEventJoin( state: HallState, api?: LoadHallApi ): Promise { const code = state.eventCode.trim(); if (!/^[A-Za-z0-9]{6}$/.test(code)) { state.errorMessage = resolveHallText('eventPlaceholder'); return false; } const join = api != null && api.joinHallEvent != null ? api.joinHallEvent : joinHallEvent; try { await join(code); state.eventCode = ''; state.tab = 'event'; await refreshHall(state, api); return true; } catch (err) { state.errorMessage = readErrorMessage(err); return false; } } export const PENDING_COLLECT_KEY = 'omni_pending_collect_card'; export function setPendingCollectCard(cardId: string | number): void { try { uni.setStorageSync(PENDING_COLLECT_KEY, String(cardId)); } catch (_e) {} } export function consumePendingCollectCard(): string { try { const raw = uni.getStorageSync(PENDING_COLLECT_KEY); uni.removeStorageSync(PENDING_COLLECT_KEY); if (raw == null) { return ''; } return String(raw).trim(); } catch (_e) { return ''; } }