258 行
7.1 KiB
Plaintext
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 '';
|
|
}
|
|
}
|