文件
OmniCard/mpuniapp/pages/owner/card/useCardEdit.uts

497 行
11 KiB
Plaintext

/**
* OmniCard AI (CRMmp) - 名片主极速编辑业务驱动层
* 拉名片详情,仅提交已填字段,不做 Bento 搭建
*/
import { CardProfile } from '../../../common/types.uts';
import { fetchOwnerCardInfo, updateOwnerCard } from '../../../common/api.uts';
import { getCurrentLocale, getOmniText } from '../../../common/locale.uts';
export type CardEditCopyKey =
| 'heading'
| 'name'
| 'jobTitle'
| 'company'
| 'phone'
| 'email'
| 'whatsapp'
| 'linkedin'
| 'save'
| 'loading'
| 'success'
| 'error'
| 'empty'
| 'missingId'
| 'back'
| 'avatarUpload'
| 'avatarChange'
| 'avatarPlaceholder';
export type CardEditForm = {
id?: string | number | null;
name?: string;
title?: string;
company?: string;
slogan?: string;
avatarUrl?: string;
phone?: string;
email?: string;
whatsapp?: string;
linkedin?: string;
telegram?: string;
twitter?: string;
wechat?: string;
};
export type CardUpdatePayload = {
id: string | number;
name?: string;
title?: string;
company?: string;
slogan?: string;
avatarUrl?: string;
phone?: string;
email?: string;
whatsapp?: string;
linkedin?: string;
telegram?: string;
twitter?: string;
wechat?: string;
};
export type LoadCardApi = {
fetchOwnerCardInfo?: (id: string | number) => Promise<CardProfile>;
};
export type LoadCardResult = {
card: CardProfile | null;
empty: boolean;
errorMessage: string;
};
export type SaveCardApi = {
updateOwnerCard?: (
payload: CardProfile & { id: string | number }
) => Promise<CardProfile>;
};
export type SaveCardResult = {
ok: boolean;
card: CardProfile | null;
errorMessage: string;
};
export type CardEditPageOptions = {
id?: string;
};
export type CardEditState = {
rawId: string;
cardId: number | null;
name: string;
title: string;
company: string;
avatarUrl: string;
phone: string;
email: string;
whatsapp: string;
linkedin: string;
loading: boolean;
saving: boolean;
empty: boolean;
missingId: boolean;
errorMessage: string;
};
function isZhLocale(): boolean {
return getCurrentLocale() === 'zh-CN';
}
/**
* 页面文案:保存 / 社交走词典,字段标签走本地词典
*/
export function resolveCardEditText(key: CardEditCopyKey): string {
const locale = getCurrentLocale();
if (key === 'save') {
return getOmniText(locale, 'common.save');
}
if (key === 'loading') {
return getOmniText(locale, 'common.loading');
}
if (key === 'success') {
return getOmniText(locale, 'common.success');
}
if (key === 'error') {
return getOmniText(locale, 'common.error');
}
if (key === 'empty') {
return getOmniText(locale, 'common.empty');
}
if (key === 'whatsapp') {
return getOmniText(locale, 'portal.whatsapp');
}
if (key === 'linkedin') {
return getOmniText(locale, 'portal.linkedin');
}
const zh = isZhLocale();
if (key === 'heading') {
return zh ? '极速编辑' : 'Quick edit';
}
if (key === 'name') {
return zh ? '姓名' : 'Name';
}
if (key === 'jobTitle') {
return zh ? '头衔' : 'Title';
}
if (key === 'company') {
return zh ? '公司' : 'Company';
}
if (key === 'phone') {
return zh ? '电话' : 'Phone';
}
if (key === 'email') {
return zh ? '邮箱' : 'Email';
}
if (key === 'missingId') {
return zh ? '缺少名片编号' : 'Card ID is missing';
}
if (key === 'back') {
return zh ? '返回名片' : 'Back to card';
}
if (key === 'avatarUpload') {
return zh ? '点击拍照/上传' : 'Upload photo';
}
if (key === 'avatarChange') {
return zh ? '更换头像' : 'Change avatar';
}
if (key === 'avatarPlaceholder') {
return zh ? '上传头像' : 'Upload avatar';
}
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 pickFilled(value: string | null | undefined): string | null {
if (value == null) {
return null;
}
const text = String(value).trim();
if (text === '') {
return null;
}
return text;
}
function assignFilled(
payload: CardUpdatePayload,
key:
| 'name'
| 'title'
| 'company'
| 'slogan'
| 'avatarUrl'
| 'phone'
| 'email'
| 'whatsapp'
| 'linkedin'
| 'telegram'
| 'twitter'
| 'wechat',
value: string | null | undefined
): void {
const filled = pickFilled(value);
if (filled != null) {
payload[key] = filled;
}
}
/**
* 只带已填字段与解析后的 id。缺 id 返回 null
*/
export function buildUpdatePayload(form: CardEditForm): CardUpdatePayload | null {
const id = parseCardId(form.id);
if (id == null) {
return null;
}
const payload: CardUpdatePayload = { id };
assignFilled(payload, 'name', form.name);
assignFilled(payload, 'title', form.title);
assignFilled(payload, 'company', form.company);
assignFilled(payload, 'slogan', form.slogan);
assignFilled(payload, 'avatarUrl', form.avatarUrl);
assignFilled(payload, 'phone', form.phone);
assignFilled(payload, 'email', form.email);
assignFilled(payload, 'whatsapp', form.whatsapp);
assignFilled(payload, 'linkedin', form.linkedin);
assignFilled(payload, 'telegram', form.telegram);
assignFilled(payload, 'twitter', form.twitter);
assignFilled(payload, 'wechat', form.wechat);
return payload;
}
function readErrorMessage(err: unknown): string {
if (err instanceof Error && err.message !== '') {
return err.message;
}
return resolveCardEditText('error');
}
function asUpdatePayload(
payload: CardUpdatePayload
): CardProfile & { id: string | number } {
return payload as CardProfile & { id: string | number };
}
/**
* 拉取名片详情。可注入 fetchOwnerCardInfo。缺 id 不请求
*/
export async function loadCard(
id: string | number | null | undefined,
api?: LoadCardApi
): Promise<LoadCardResult> {
const cardId = parseCardId(id);
if (cardId == null) {
return {
card: null,
empty: true,
errorMessage: '',
};
}
const fetchInfo =
api != null && api.fetchOwnerCardInfo != null
? api.fetchOwnerCardInfo
: fetchOwnerCardInfo;
try {
const card = await fetchInfo(cardId);
return {
card,
empty: false,
errorMessage: '',
};
} catch (err) {
return {
card: null,
empty: true,
errorMessage: readErrorMessage(err),
};
}
}
/**
* 保存名片。可注入 updateOwnerCard。缺 id 不请求
*/
export async function saveCard(
form: CardEditForm,
api?: SaveCardApi
): Promise<SaveCardResult> {
const payload = buildUpdatePayload(form);
if (payload == null) {
return {
ok: false,
card: null,
errorMessage: '',
};
}
const update =
api != null && api.updateOwnerCard != null
? api.updateOwnerCard
: updateOwnerCard;
try {
const card = await update(asUpdatePayload(payload));
return {
ok: true,
card,
errorMessage: '',
};
} catch (err) {
return {
ok: false,
card: null,
errorMessage: readErrorMessage(err),
};
}
}
export function createCardEditState(): CardEditState {
return {
rawId: '',
cardId: null,
name: '',
title: '',
company: '',
avatarUrl: '',
phone: '',
email: '',
whatsapp: '',
linkedin: '',
loading: false,
saving: false,
empty: false,
missingId: false,
errorMessage: '',
};
}
export function applyCardEditOptions(
state: CardEditState,
options: CardEditPageOptions | null
): boolean {
if (options == null || options.id == null) {
state.rawId = '';
state.cardId = null;
state.missingId = true;
state.empty = true;
return false;
}
const parsed = parseCardId(options.id);
state.rawId = String(options.id).trim();
state.cardId = parsed;
state.missingId = parsed == null;
state.empty = parsed == null;
return parsed != null;
}
function readOptionalText(value: string | null | undefined): string {
if (value == null) {
return '';
}
return String(value);
}
function applyCardToState(state: CardEditState, card: CardProfile): void {
if (card.id != null) {
const parsed = parseCardId(card.id);
if (parsed != null) {
state.cardId = parsed;
}
}
state.name = card.name != null ? String(card.name) : '';
state.title = readOptionalText(card.title);
state.company = readOptionalText(card.company);
state.avatarUrl = readOptionalText(card.avatarUrl);
state.phone = readOptionalText(card.phone);
state.email = readOptionalText(card.email);
state.whatsapp = readOptionalText(card.whatsapp);
state.linkedin = readOptionalText(card.linkedin);
}
export function applyLoadResult(
state: CardEditState,
result: LoadCardResult
): void {
if (result.card != null) {
applyCardToState(state, result.card);
state.empty = false;
state.missingId = false;
} else {
state.empty = true;
}
state.errorMessage = result.errorMessage;
}
export function formFromState(state: CardEditState): CardEditForm {
return {
id: state.cardId,
name: state.name,
title: state.title,
company: state.company,
avatarUrl: state.avatarUrl,
phone: state.phone,
email: state.email,
whatsapp: state.whatsapp,
linkedin: state.linkedin,
};
}
export async function refreshCardEdit(
state: CardEditState,
api?: LoadCardApi
): Promise<LoadCardResult> {
if (state.missingId || state.cardId == null) {
const skipped: LoadCardResult = {
card: null,
empty: true,
errorMessage: '',
};
applyLoadResult(state, skipped);
state.missingId = true;
return skipped;
}
if (state.loading) {
return {
card: null,
empty: state.empty,
errorMessage: state.errorMessage,
};
}
state.loading = true;
state.errorMessage = '';
const result = await loadCard(state.cardId, api);
applyLoadResult(state, result);
state.loading = false;
return result;
}
export async function submitCardEdit(
state: CardEditState,
api?: SaveCardApi
): Promise<boolean> {
if (state.cardId == null || state.saving) {
return false;
}
state.saving = true;
state.errorMessage = '';
const result = await saveCard(formFromState(state), api);
if (result.ok && result.card != null) {
applyCardToState(state, result.card);
state.saving = false;
return true;
}
state.saving = false;
state.errorMessage = result.errorMessage;
return false;
}
export function goBackFromCardEdit(): void {
uni.navigateBack({
delta: 1,
fail: () => {
uni.switchTab({
url: '/pages/owner/card/index',
});
},
});
}