/** * OmniCard AI (CRMmp) - 产品相册与标杆案例移动端业务逻辑驱动层 * 严格遵循 uts 强类型契约与 AGENTS.md 规范 */ import { CardCase, OwnerPageData, OwnerPageQuery } from '../../../common/types.uts'; import { fetchCardCasesPage, addCardCase, updateCardCase, deleteCardCase, } from '../../../common/api.uts'; import { getCurrentLocale } from '../../../common/locale.uts'; export type CasesApi = { fetchPage: (query: OwnerPageQuery) => Promise>; addCase: (data: Partial) => Promise; updateCase: (data: Partial & { id: string | number }) => Promise; deleteCase: (id: string | number) => Promise; }; export const defaultCasesApi: CasesApi = { fetchPage: (query: OwnerPageQuery) => fetchCardCasesPage(query), addCase: (data: Partial) => addCardCase(data), updateCase: (data: Partial & { id: string | number }) => updateCardCase(data), deleteCase: (id: string | number) => deleteCardCase(id), }; export type CasesState = { cardId: string | number; list: CardCase[]; loading: boolean; empty: boolean; errorMessage: string; drawerVisible: boolean; editingCase: Partial | null; saving: boolean; }; export function createCasesState(cardId: string | number): CasesState { return { cardId, list: [], loading: false, empty: false, errorMessage: '', drawerVisible: false, editingCase: null, saving: false, }; } export function validateCaseForm(form: Partial): string | null { const title = form.title != null ? form.title.trim() : ''; if (title === '') { return resolveCasesText('errTitleRequired'); } if (title.length > 150) { return resolveCasesText('errTitleTooLong'); } return null; } export async function loadCases( state: CasesState, api: CasesApi = defaultCasesApi ): Promise { state.loading = true; state.errorMessage = ''; try { const res = await api.fetchPage({ page: 1, size: 50, cardId: state.cardId, }); const items = res.list != null ? res.list : []; state.list = items; state.empty = items.length === 0; } catch (err: any) { state.errorMessage = err.message || resolveCasesText('errLoadFailed'); } finally { state.loading = false; } } export async function saveCase( state: CasesState, form: Partial, api: CasesApi = defaultCasesApi ): Promise { const err = validateCaseForm(form); if (err !== null) { state.errorMessage = err; return false; } state.saving = true; state.errorMessage = ''; try { const payload: Partial = { ...form, cardId: state.cardId, title: form.title!.trim(), }; if (form.id != null) { await api.updateCase({ ...payload, id: form.id, }); } else { await api.addCase(payload); } state.drawerVisible = false; state.editingCase = null; await loadCases(state, api); return true; } catch (err: any) { state.errorMessage = err.message || resolveCasesText('errSaveFailed'); return false; } finally { state.saving = false; } } export async function deleteCase( state: CasesState, id: string | number, api: CasesApi = defaultCasesApi ): Promise { state.saving = true; state.errorMessage = ''; try { const ok = await api.deleteCase(id); if (ok) { await loadCases(state, api); return true; } return false; } catch (err: any) { state.errorMessage = err.message || resolveCasesText('errDeleteFailed'); return false; } finally { state.saving = false; } } export type CasesCopyKey = | 'title' | 'subtitle' | 'addBtn' | 'editBtn' | 'deleteBtn' | 'emptyTitle' | 'emptyDesc' | 'fieldTitle' | 'fieldClient' | 'fieldDuration' | 'fieldMetrics' | 'fieldDesc' | 'fieldCover' | 'saveBtn' | 'cancelBtn' | 'phTitle' | 'phClient' | 'phDuration' | 'phMetrics' | 'phDesc' | 'clientPrefix' | 'coverPlaceholder' | 'saving' | 'savedToast' | 'deletedToast' | 'deleteConfirmTitle' | 'deleteConfirmContent' | 'deleteConfirmOk' | 'errTitleRequired' | 'errTitleTooLong' | 'errLoadFailed' | 'errSaveFailed' | 'errDeleteFailed'; export function resolveCasesText(key: CasesCopyKey): string { const locale = getCurrentLocale(); const zh = locale === 'zh-CN'; switch (key) { case 'title': // 兼容历史标题匹配: 产品相册与标杆案例,规范展示为产品与案例 return zh ? '产品与案例' : 'Portfolio & Case Studies'; case 'subtitle': return zh ? '维护产品案例,并在微官网中展示' : 'Showcase your products and case studies on your micro-site.'; case 'addBtn': return zh ? '新增案例' : 'Add Case Study'; case 'editBtn': return zh ? '编辑' : 'Edit'; case 'deleteBtn': return zh ? '删除' : 'Delete'; case 'emptyTitle': return zh ? '暂无产品案例' : 'No Cases Yet'; case 'emptyDesc': return zh ? '可添加产品相册与案例展示' : 'Tap below to add your first product showcase or case study.'; case 'fieldTitle': return zh ? '案例/产品名称' : 'Title'; case 'fieldClient': return zh ? '合作客商/品牌 (选填)' : 'Client / Brand'; case 'fieldDuration': return zh ? '项目周期 (选填)' : 'Timeline'; case 'fieldMetrics': return zh ? '成效标签' : 'Key Metrics Tag'; case 'fieldDesc': return zh ? '方案与成果描述' : 'Overview'; case 'fieldCover': return zh ? '产品封面与相册图片' : 'Cover & Photos'; case 'saveBtn': return zh ? '保存案例' : 'Save Case'; case 'cancelBtn': return zh ? '取消' : 'Cancel'; case 'phTitle': return zh ? '输入案例或产品名称' : 'Enter case or product title'; case 'phClient': return zh ? '选填合作客商或品牌' : 'Optional client or brand'; case 'phDuration': return zh ? '例如项目起止时间' : 'e.g. project timeline'; case 'phMetrics': return zh ? '选填成效标签' : 'Optional metrics tag'; case 'phDesc': return zh ? '简述方案背景、成效或客户反馈' : 'Brief overview, outcomes, or feedback'; case 'clientPrefix': return zh ? '合作客户: ' : 'Client: '; case 'coverPlaceholder': return zh ? '产品展示' : 'Showcase'; case 'saving': return zh ? '保存中...' : 'Saving...'; case 'savedToast': return zh ? '案例已保存' : 'Case Saved'; case 'deletedToast': return zh ? '已删除' : 'Deleted'; case 'deleteConfirmTitle': return zh ? '确认删除' : 'Confirm Delete'; case 'deleteConfirmContent': return zh ? '确定要删除该案例吗?此操作无法撤销。' : 'Are you sure you want to delete this case?'; case 'deleteConfirmOk': return zh ? '删除' : 'Delete'; case 'errTitleRequired': return zh ? '请输入案例或产品标题' : 'Please enter a title'; case 'errTitleTooLong': return zh ? '标题长度不能超过 150 个字符' : 'Title must be 150 characters or fewer'; case 'errLoadFailed': return zh ? '加载案例列表失败' : 'Failed to load cases'; case 'errSaveFailed': return zh ? '保存案例失败' : 'Failed to save case'; case 'errDeleteFailed': return zh ? '删除案例失败' : 'Failed to delete case'; default: return ''; } }