P0-1 跨模块直查修复:publish-service 不再直查 examQuestions 表,新增 exams/data-access.addExamQuestions 接口,复用 classes/data-access.getStudentIdsByClassIds P0-2 i18n 接入:新增 zh-CN/en 翻译文件,注册 lessonPreparation 命名空间,17 个组件改造为 useTranslations/getTranslations P1 纯函数抽取:lib/document-migration.ts(类型守卫替代 as 断言)、lib/node-summary.ts(翻译函数注入)、lib/rf-mappers.ts P1 错误边界+骨架屏:新增 LessonPlanErrorBoundary 和 4 个 Skeleton 组件 P1 Block 注册表:新增 config/block-registry.tsx(BlockRenderer 组件),node-edit-panel 重构为配置驱动渲染 P1 其他修复:exercise-block 改用 router.refresh(),node-editor/lesson-node 复用 lib/ 纯函数 架构图同步:更新 004 和 005 文档 Refs: docs/architecture/audit/lesson-preparation-audit-report.md
60 lines
1.6 KiB
TypeScript
60 lines
1.6 KiB
TypeScript
"use client";
|
|
|
|
import { Component, type ReactNode, type ErrorInfo } from "react";
|
|
import { Button } from "@/shared/components/ui/button";
|
|
|
|
interface Props {
|
|
children: ReactNode;
|
|
fallback?: ReactNode;
|
|
/** 错误时的回调,用于上报埋点 */
|
|
onError?: (error: Error, info: ErrorInfo) => void;
|
|
}
|
|
|
|
interface State {
|
|
hasError: boolean;
|
|
error: Error | null;
|
|
}
|
|
|
|
/**
|
|
* 备课模块错误边界。
|
|
* 包裹独立数据区块(版本抽屉/题库选择器/知识点选择器/发布对话框),
|
|
* 单个区块异常不影响整页。
|
|
*/
|
|
export class LessonPlanErrorBoundary extends Component<Props, State> {
|
|
constructor(props: Props) {
|
|
super(props);
|
|
this.state = { hasError: false, error: null };
|
|
}
|
|
|
|
static getDerivedStateFromError(error: Error): State {
|
|
return { hasError: true, error };
|
|
}
|
|
|
|
componentDidCatch(error: Error, info: ErrorInfo): void {
|
|
if (this.props.onError) {
|
|
this.props.onError(error, info);
|
|
}
|
|
}
|
|
|
|
handleRetry = (): void => {
|
|
this.setState({ hasError: false, error: null });
|
|
};
|
|
|
|
render(): ReactNode {
|
|
if (this.state.hasError) {
|
|
if (this.props.fallback) return this.props.fallback;
|
|
return (
|
|
<div className="flex flex-col items-center justify-center p-8 gap-3 text-center">
|
|
<p className="text-sm text-on-surface-variant">
|
|
{this.state.error?.message ?? "区块加载失败"}
|
|
</p>
|
|
<Button variant="outline" size="sm" onClick={this.handleRetry}>
|
|
重试
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|
|
return this.props.children;
|
|
}
|
|
}
|