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

258 行
7.1 KiB
Plaintext

/**
* OmniCard AI (CRMmp) - 文章动态中心移动端业务逻辑驱动层
* 严格遵循 uts 强类型契约与 AGENTS.md 规范
*/
import { CardArticle, OwnerPageData, OwnerPageQuery } from '../../../common/types.uts';
import {
fetchCardArticlesPage,
addCardArticle,
updateCardArticle,
deleteCardArticle,
} from '../../../common/api.uts';
import { getCurrentLocale, getOmniText } from '../../../common/locale.uts';
export type ArticlesApi = {
fetchPage: (query: OwnerPageQuery) => Promise<OwnerPageData<CardArticle>>;
addArticle: (data: Partial<CardArticle>) => Promise<CardArticle>;
updateArticle: (data: Partial<CardArticle> & { id: string | number }) => Promise<CardArticle>;
deleteArticle: (id: string | number) => Promise<boolean>;
};
export const defaultArticlesApi: ArticlesApi = {
fetchPage: (query: OwnerPageQuery) => fetchCardArticlesPage(query),
addArticle: (data: Partial<CardArticle>) => addCardArticle(data),
updateArticle: (data: Partial<CardArticle> & { id: string | number }) => updateCardArticle(data),
deleteArticle: (id: string | number) => deleteCardArticle(id),
};
export type ArticlesState = {
cardId: string | number;
list: CardArticle[];
loading: boolean;
empty: boolean;
errorMessage: string;
drawerVisible: boolean;
editingArticle: Partial<CardArticle> | null;
saving: boolean;
};
export function createArticlesState(cardId: string | number): ArticlesState {
return {
cardId,
list: [],
loading: false,
empty: false,
errorMessage: '',
drawerVisible: false,
editingArticle: null,
saving: false,
};
}
export function extractArticleSummary(content?: string, maxLen: number = 120): string {
if (content == null || content.trim() === '') {
return '';
}
const plain = content
.replace(/<[^>]+>/g, '') // 剥离 HTML
.replace(/!\[.*?\]\(.*?\)/g, '') // 剥离 Markdown 图片
.replace(/\[(.*?)\]\(.*?\)/g, '$1') // 提取 Markdown 链接文本
.replace(/[#*`_~>-]/g, '') // 移除 Markdown 常用标记
.replace(/\s+/g, ' ') // 压缩多余空格
.trim();
return plain.length > maxLen ? plain.slice(0, maxLen) : plain;
}
export function validateArticleForm(form: Partial<CardArticle>): string | null {
const title = form.title != null ? form.title.trim() : '';
if (title === '') {
return '请输入文章或动态标题';
}
const content = form.content != null ? form.content.trim() : '';
if (content === '') {
return '请输入文章正文内容';
}
return null;
}
export async function loadArticles(
state: ArticlesState,
api: ArticlesApi = defaultArticlesApi
): Promise<void> {
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 || '加载文章列表失败';
} finally {
state.loading = false;
}
}
export async function saveArticle(
state: ArticlesState,
form: Partial<CardArticle>,
api: ArticlesApi = defaultArticlesApi
): Promise<boolean> {
const err = validateArticleForm(form);
if (err !== null) {
state.errorMessage = err;
return false;
}
state.saving = true;
state.errorMessage = '';
try {
const summary = (form.summary != null && form.summary!.trim() !== '')
? form.summary!.trim()
: extractArticleSummary(form.content);
const payload: Partial<CardArticle> = {
...form,
cardId: state.cardId,
title: form.title!.trim(),
summary,
content: form.content!.trim(),
isPublished: form.isPublished !== undefined ? form.isPublished : true,
};
if (form.id != null) {
await api.updateArticle({
...payload,
id: form.id,
});
} else {
await api.addArticle(payload);
}
state.drawerVisible = false;
state.editingArticle = null;
await loadArticles(state, api);
return true;
} catch (err: any) {
state.errorMessage = err.message || '保存文章失败';
return false;
} finally {
state.saving = false;
}
}
export async function toggleArticlePublish(
state: ArticlesState,
article: CardArticle,
api: ArticlesApi = defaultArticlesApi
): Promise<boolean> {
if (article.id == null) {
return false;
}
state.saving = true;
state.errorMessage = '';
try {
const targetStatus = !article.isPublished;
await api.updateArticle({
id: article.id,
cardId: article.cardId,
isPublished: targetStatus,
});
article.isPublished = targetStatus;
return true;
} catch (err: any) {
state.errorMessage = err.message || '切换发布状态失败';
return false;
} finally {
state.saving = false;
}
}
export async function deleteArticle(
state: ArticlesState,
id: string | number,
api: ArticlesApi = defaultArticlesApi
): Promise<boolean> {
state.saving = true;
state.errorMessage = '';
try {
const ok = await api.deleteArticle(id);
if (ok) {
await loadArticles(state, api);
return true;
}
return false;
} catch (err: any) {
state.errorMessage = err.message || '删除文章失败';
return false;
} finally {
state.saving = false;
}
}
export type ArticlesCopyKey =
| 'title'
| 'subtitle'
| 'publishBtn'
| 'editBtn'
| 'deleteBtn'
| 'emptyTitle'
| 'emptyDesc'
| 'statusPublished'
| 'statusDraft'
| 'fieldTitle'
| 'fieldSummary'
| 'fieldCover'
| 'fieldContent'
| 'fieldPublish'
| 'saveBtn'
| 'cancelBtn';
export function resolveArticlesText(key: ArticlesCopyKey): string {
const locale = getCurrentLocale();
const zh = locale === 'zh-CN';
switch (key) {
case 'title':
return zh ? '文章与商务动态' : 'Insights & Articles';
case 'subtitle':
return zh ? '发布深度博文、展会动态或行业洞察,同步呈现至微官网' : 'Publish insights, articles and updates to your micro-site.';
case 'publishBtn':
return zh ? '发布新动态' : 'New Article';
case 'editBtn':
return zh ? '编辑' : 'Edit';
case 'deleteBtn':
return zh ? '删除' : 'Delete';
case 'emptyTitle':
return zh ? '暂无文章动态' : 'No Articles Yet';
case 'emptyDesc':
return zh ? '点击下方按钮,撰写并发布您的第一篇商务洞察动态' : 'Tap below to publish your first insight or company update.';
case 'statusPublished':
return zh ? '已发布' : 'Published';
case 'statusDraft':
return zh ? '草稿' : 'Draft';
case 'fieldTitle':
return zh ? '文章标题' : 'Title';
case 'fieldSummary':
return zh ? '摘要导读 (选填)' : 'Summary';
case 'fieldCover':
return zh ? '封面图片' : 'Cover Image';
case 'fieldContent':
return zh ? '文章正文 (支持富文本与 Markdown)' : 'Content';
case 'fieldPublish':
return zh ? '公开发布至微官网' : 'Publish to Micro-site';
case 'saveBtn':
return zh ? '确认发布' : 'Publish';
case 'cancelBtn':
return zh ? '取消' : 'Cancel';
default:
return '';
}
}