forked from wangziqi/gongxue-base
Standards 轴: - 移除 uiArtifacts.ts 的 payloadOf 死代码残留 - AttendanceDevices 残留 any 类型化(补 ClassroomOption.status 字段) - 批量考勤纠错区分业务失败(已结算/无权限)与系统错误, 前端提示精确到两类数量 - Dashboard queryFn 六段重复校验块收敛为 safeValidate 助手 Spec 轴: - 补齐阶段 3.4 A2UI 测试:图表空数据占位、ArtifactErrorBoundary 降级隔离、useSubmissionState/useXCardSurface 单测(7 用例) - 阶段 2.2 补两处引导:教师工作台区分「今日无课」与「未分配班级」、 班级花名册空态带「添加学员」动作 - 契约文档修正 DynamicReview 状态管理描述(多提交点如实说明) aislop 剩余 16 警告均为必要豁免(类型边界/声明式 SQL 配置/既有文件规模)
69 lines
2.2 KiB
TypeScript
69 lines
2.2 KiB
TypeScript
import type {
|
||
AiArtifactSchema,
|
||
AiChartSchema,
|
||
AiChatMessage,
|
||
AiFormSchema,
|
||
AiReviewSchema,
|
||
} from './types';
|
||
|
||
export function mergeById<T extends { id: string }>(
|
||
current: T[] | undefined,
|
||
incoming: T | T[] | undefined,
|
||
): T[] {
|
||
const items = Array.isArray(incoming) ? incoming : incoming ? [incoming] : [];
|
||
if (!items.length) return current ?? [];
|
||
const next = [...(current ?? [])];
|
||
for (const item of items) {
|
||
if (!item || typeof item !== 'object') continue;
|
||
const index = next.findIndex((existing) => existing.id === item.id);
|
||
if (index === -1) {
|
||
next.push(item);
|
||
} else {
|
||
next[index] = item;
|
||
}
|
||
}
|
||
return next;
|
||
}
|
||
|
||
/**
|
||
* 将统一 artifact 归入 uiArtifacts。
|
||
*
|
||
* 注意:不再派发到 legacy 列表(forms/reviews/charts)——渲染层从
|
||
* uiArtifacts 派生,legacy 字段仅保留给历史消息(metadata 中只有
|
||
* a2uiForm/a2uiReview/a2uiChart 的老数据)作兼容读取。
|
||
*/
|
||
export function mergeArtifactIntoMessage(
|
||
message: AiChatMessage,
|
||
artifact: AiArtifactSchema,
|
||
): AiChatMessage {
|
||
message.uiArtifacts = mergeById<AiArtifactSchema>(message.uiArtifacts, artifact);
|
||
return message;
|
||
}
|
||
|
||
/**
|
||
* 从 uiArtifacts 派生 legacy 列表(渲染用)。
|
||
* 仅当 message 上没有显式 legacy 数据(历史消息)时,渲染层回退到 message.forms 等。
|
||
*/
|
||
export function deriveForms(message: AiChatMessage): AiFormSchema[] {
|
||
return (message.uiArtifacts ?? [])
|
||
.filter((artifact) => artifact.type === 'form')
|
||
.map((artifact) => artifact.payload)
|
||
.filter((payload): payload is AiFormSchema => Boolean(payload) && typeof payload === 'object');
|
||
}
|
||
|
||
export function deriveReviews(message: AiChatMessage): AiReviewSchema[] {
|
||
return (message.uiArtifacts ?? [])
|
||
.filter((artifact) => artifact.type === 'review')
|
||
.map((artifact) => artifact.payload)
|
||
.filter(
|
||
(payload): payload is AiReviewSchema => Boolean(payload) && typeof payload === 'object',
|
||
);
|
||
}
|
||
|
||
export function deriveCharts(message: AiChatMessage): AiChartSchema[] {
|
||
return (message.uiArtifacts ?? [])
|
||
.filter((artifact) => artifact.type === 'chart')
|
||
.map((artifact) => artifact.payload)
|
||
.filter((payload): payload is AiChartSchema => Boolean(payload) && typeof payload === 'object');
|
||
}
|