/** * OmniCard AI (CRMmp) - 名片主个人中心与消息通知配置业务驱动层 * 严格按照 AGENTS.md 规范与 uts 强类型约束 */ import { OmniLocale, PORTAL_LOCALE_STORAGE_KEY, getCurrentLocale, setAppLocale, getOmniText, } from '../../../common/locale.uts'; export { PORTAL_LOCALE_STORAGE_KEY }; export type UserProfileAccount = { name: string; company: string; department: string; avatar: string; }; export type NotificationSettings = { radarPush: boolean; leadNotice: boolean; }; export type ProfileState = { account: UserProfileAccount; notifications: NotificationSettings; locale: OmniLocale; saving: boolean; loading: boolean; errorMessage: string; }; export type StorageApi = { getStorageSync: (key: string) => unknown; setStorageSync: (key: string, value: unknown) => void; }; export const PROFILE_ACCOUNT_STORAGE_KEY = 'omni_owner_profile_account'; export const PROFILE_NOTIFICATIONS_STORAGE_KEY = 'omni_owner_profile_notifications'; function defaultStorage(): StorageApi { return { getStorageSync: (key: string): unknown => { try { if (typeof uni !== 'undefined' && uni.getStorageSync != null) { return uni.getStorageSync(key); } } catch (_e) {} return ''; }, setStorageSync: (key: string, value: unknown): void => { try { if (typeof uni !== 'undefined' && uni.setStorageSync != null) { uni.setStorageSync(key, value); } } catch (err: unknown) { throw err; } }, }; } export function getDefaultAccount(): UserProfileAccount { return { name: 'Alex Vance', company: 'Nexus Tech Global', department: 'Overseas Business Dept', avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&w=256&q=80', }; } export function getDefaultNotifications(): NotificationSettings { return { radarPush: true, leadNotice: true, }; } export function createProfileState(locale?: OmniLocale): ProfileState { const currentLocale = locale != null && locale !== '' ? locale : getCurrentLocale(); return { account: getDefaultAccount(), notifications: getDefaultNotifications(), locale: currentLocale, saving: false, loading: false, errorMessage: '', }; } function safeParseJson(raw: unknown): T | null { if (raw == null || typeof raw !== 'string') { return null; } const trimmed = raw.trim(); if (trimmed === '' || (!trimmed.startsWith('{') && !trimmed.startsWith('['))) { return null; } try { return JSON.parse(trimmed) as T; } catch (_e) { return null; } } /** * 从本地存储加载个人名片主资料与通知设置,具备完整防御性容错 */ export function loadProfileSettings(storage?: StorageApi): ProfileState { const store = storage != null ? storage : defaultStorage(); const state = createProfileState(); try { const rawAccount = store.getStorageSync(PROFILE_ACCOUNT_STORAGE_KEY); const parsedAccount = safeParseJson>(rawAccount); if (parsedAccount != null && typeof parsedAccount === 'object') { const defaultAcc = getDefaultAccount(); const safeName = typeof parsedAccount.name === 'string' && parsedAccount.name.trim() !== '' ? parsedAccount.name.trim() : defaultAcc.name; const safeCompany = typeof parsedAccount.company === 'string' && parsedAccount.company.trim() !== '' ? parsedAccount.company.trim() : defaultAcc.company; const safeDept = typeof parsedAccount.department === 'string' && parsedAccount.department.trim() !== '' ? parsedAccount.department.trim() : defaultAcc.department; const safeAvatar = typeof parsedAccount.avatar === 'string' && parsedAccount.avatar.trim() !== '' ? parsedAccount.avatar.trim() : defaultAcc.avatar; state.account = { name: safeName, company: safeCompany, department: safeDept, avatar: safeAvatar, }; } } catch (_e) { // 降级使用默认账户 state.account = getDefaultAccount(); } try { const rawNotice = store.getStorageSync(PROFILE_NOTIFICATIONS_STORAGE_KEY); const parsedNotice = safeParseJson>(rawNotice); if (parsedNotice != null && typeof parsedNotice === 'object') { const defaultNotif = getDefaultNotifications(); state.notifications = { radarPush: typeof parsedNotice.radarPush === 'boolean' ? parsedNotice.radarPush : defaultNotif.radarPush, leadNotice: typeof parsedNotice.leadNotice === 'boolean' ? parsedNotice.leadNotice : defaultNotif.leadNotice, }; } } catch (_e) { // 降级使用默认通知 state.notifications = getDefaultNotifications(); } state.locale = getCurrentLocale(); return state; } /** * 保存名片主资料与通知设置到本地存储 */ export function saveProfileSettings(state: ProfileState, storage?: StorageApi): boolean { const store = storage != null ? storage : defaultStorage(); try { store.setStorageSync( PROFILE_ACCOUNT_STORAGE_KEY, JSON.stringify(state.account) ); store.setStorageSync( PROFILE_NOTIFICATIONS_STORAGE_KEY, JSON.stringify(state.notifications) ); state.errorMessage = ''; return true; } catch (err: unknown) { const msg = err instanceof Error ? err.message : 'Storage save failed'; state.errorMessage = msg; return false; } } /** * 更新账户资料字段,具备非空与空格修剪防御保护 */ export function updateAccount( state: ProfileState, patch: Partial ): void { if (patch == null) { return; } if (typeof patch.name === 'string' && patch.name.trim() !== '') { state.account.name = patch.name.trim(); } if (typeof patch.company === 'string' && patch.company.trim() !== '') { state.account.company = patch.company.trim(); } if (typeof patch.department === 'string' && patch.department.trim() !== '') { state.account.department = patch.department.trim(); } if (typeof patch.avatar === 'string' && patch.avatar.trim() !== '') { state.account.avatar = patch.avatar.trim(); } } /** * 切换雷达即时推送通知开关,并同步记忆持久化 */ export function toggleRadarPush( state: ProfileState, value?: boolean, storage?: StorageApi ): boolean { const store = storage != null ? storage : defaultStorage(); const nextVal = value !== undefined ? Boolean(value) : !state.notifications.radarPush; state.notifications.radarPush = nextVal; try { store.setStorageSync( PROFILE_NOTIFICATIONS_STORAGE_KEY, JSON.stringify(state.notifications) ); state.errorMessage = ''; } catch (err: unknown) { const msg = err instanceof Error ? err.message : 'Failed to persist radar push'; state.errorMessage = msg; } return nextVal; } /** * 切换新线索提醒开关,并同步记忆持久化 */ export function toggleLeadNotice( state: ProfileState, value?: boolean, storage?: StorageApi ): boolean { const store = storage != null ? storage : defaultStorage(); const nextVal = value !== undefined ? Boolean(value) : !state.notifications.leadNotice; state.notifications.leadNotice = nextVal; try { store.setStorageSync( PROFILE_NOTIFICATIONS_STORAGE_KEY, JSON.stringify(state.notifications) ); state.errorMessage = ''; } catch (err: unknown) { const msg = err instanceof Error ? err.message : 'Failed to persist lead notice'; state.errorMessage = msg; } return nextVal; } /** * 切换首选语言并记忆持久化 */ export function setPreferredLocale( state: ProfileState, locale: string, storage?: StorageApi ): OmniLocale { const safeLocales: OmniLocale[] = [ 'zh-CN', 'en-US', 'ar-SA', 'es-ES', 'de-DE', 'fr-FR', 'ja-JP', ]; let targetLocale: OmniLocale = 'zh-CN'; if (locale != null) { const found = safeLocales.find((l: OmniLocale) => l === locale); if (found != null) { targetLocale = found; } else { const lower = locale.toLowerCase(); if (lower.startsWith('en')) { targetLocale = 'en-US'; } } } state.locale = targetLocale; setAppLocale(targetLocale); if (storage != null) { try { storage.setStorageSync(PORTAL_LOCALE_STORAGE_KEY, targetLocale); } catch (_e) {} } return targetLocale; } /** * 预览个人头像 */ export function previewAccountAvatar(state: ProfileState): void { if (state.account.avatar !== '') { // #ifndef UNI-APP-X-TEST try { if (typeof uni !== 'undefined' && typeof uni.previewImage === 'function') { uni.previewImage({ urls: [state.account.avatar], current: state.account.avatar, }); } } catch (_e) {} // #endif } } /** * 移除个人头像 */ export function removeAccountAvatar(state: ProfileState): void { state.account.avatar = ''; } /** * 计算配额使用百分比 (0-100),防溢出与除零防御 */ export function calculateQuotaPercent(used: number, limit: number): number { if (limit <= 0) { return 100; } if (used <= 0) { return 0; } const pct = Math.round((used / limit) * 100); if (pct > 100) { return 100; } return pct; } /** * 瑞士国际主义多语言文案解析器,无 emoji */ export function resolveProfileText(key: string): string { const locale = getCurrentLocale(); if (key === 'save') { return getOmniText(locale, 'common.save'); } if (key === 'saved') { return getOmniText(locale, 'common.success'); } if (key === 'loading') { return getOmniText(locale, 'common.loading'); } if (key === 'error') { return getOmniText(locale, 'common.error'); } const isZh = locale === 'zh-CN'; switch (key) { case 'title': return isZh ? '个人中心' : 'Profile'; case 'subtitle': return isZh ? '管理名片主信息与系统通知' : 'Manage account and notifications'; case 'accountSection': return isZh ? '基本资料' : 'Account'; case 'nameLabel': return isZh ? '姓名' : 'Name'; case 'companyLabel': return isZh ? '所属企业' : 'Company'; case 'departmentLabel': return isZh ? '部门' : 'Department'; case 'avatarLabel': return isZh ? '头像' : 'Avatar'; case 'tierPro': return isZh ? '商业专业版' : 'Pro Plan'; case 'tierBadge': return 'PRO'; case 'previewPortal': return isZh ? '预览微官网' : 'Preview site'; case 'metricsCards': return isZh ? '活跃名片' : 'Active Cards'; case 'metricsVisitors': return isZh ? '雷达访客' : 'Radar Visitors'; case 'metricsLeads': return isZh ? '沉淀线索' : 'Captured Leads'; case 'notificationsSection': return isZh ? '消息通知' : 'Notifications'; case 'radarPushTitle': return isZh ? '雷达即时推送' : 'Radar Push'; case 'radarPushDesc': return isZh ? '海外客商访问名片时立即通知' : 'Instant alert when visitors view card'; case 'leadNoticeTitle': return isZh ? '新线索提醒' : 'Lead Notice'; case 'leadNoticeDesc': return isZh ? '客商留资或互换名片时实时触达' : 'Real-time notice for new leads'; case 'languageSection': return isZh ? '偏好设置' : 'Preferences'; case 'languageLabel': return isZh ? '界面语言' : 'Display Language'; case 'logout': return isZh ? '退出登录' : 'Log out'; case 'workspaceSection': return isZh ? '智能硬件与团队' : 'Hardware & Team'; case 'cardStudioSection': return isZh ? '名片与微官网工作室' : 'Digital Card Studio'; case 'myCards': return isZh ? '我的数字名片' : 'Digital Cards'; case 'cardEdit': return isZh ? '极速资料维护' : 'Quick Profile'; case 'productCases': return isZh ? '产品相册与案例' : 'Portfolio & Cases'; case 'articleDynamics': return isZh ? '文章与商务动态' : 'Insights & News'; case 'sharePoster': return isZh ? '分享海报' : 'Share Poster'; case 'exchangeHall': return isZh ? '互换大厅' : 'Exchange Hall'; case 'editProfile': return isZh ? '编辑资料' : 'Edit'; case 'nfcSection': return isZh ? 'NFC 智能硬件' : 'NFC Fleet'; case 'nfcScan': return isZh ? 'NFC 贴卡激活' : 'Activate NFC card'; case 'nfcMode': return isZh ? 'NFC 场景切换' : 'NFC scene mode'; case 'nfcSecurity': return isZh ? 'NFC 挂失' : 'Freeze NFC card'; case 'teamBriefing': return isZh ? '展会战报' : 'Team briefing'; case 'quotaSection': return isZh ? '套餐与配额' : 'Plan & Quota'; case 'quotaCards': return isZh ? '名片用量' : 'Card usage'; case 'quotaUpgrade': return isZh ? '已达上限,请升级套餐' : 'Limit reached. Upgrade to continue.'; case 'upgradeQuota': return isZh ? '升级扩容' : 'Upgrade Plan'; case 'planActive': return isZh ? '生效中' : 'Active'; case 'confirmLogoutTitle': return isZh ? '确认退出' : 'Sign Out'; case 'confirmLogoutContent': return isZh ? '确定要退出当前账号吗?' : 'Are you sure you want to sign out?'; default: return key; } }