chore(lesson-preparation): V4 lint + tsc 零错误验证
重构 anchor-migration-banner:使用 key-based remount + lazy initial state 避免 setState in effect,符合 V4 react-hooks/set-state-in-effect 规则。 验证:tsc --noEmit 0 错误;lint 中 V4 引入的错误已全部修复。剩余 6 个 lint 错误为预存无关模块问题(exams/use-exam-preview-tasks react-hooks/refs 3 个、template-picker V5-9 set-state-in-effect 1 个),13 个 warnings 均为预存代码(schedule-dialog、unread-message-badge 等),按计划要求已有的无关模块错误需记录但不要求修复。
This commit is contained in:
@@ -1,28 +1,44 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useLessonPlanEditor } from "../hooks/use-lesson-plan-editor";
|
import { useLessonPlanEditor } from "../hooks/use-lesson-plan-editor";
|
||||||
|
import type { LessonPlanDocument } from "../types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* V4:v3 锚点失效提示 banner。
|
* V4:v3 锚点失效提示 banner。
|
||||||
* 当文档含旧锚点(invalid: true 或含 start/end 字段)且未 dismiss 时显示。
|
* 当文档含旧锚点(invalid: true 或含 start/end 字段)且未 dismiss 时显示。
|
||||||
* dismiss 状态记录到 localStorage(按 planId)。
|
* dismiss 状态记录到 localStorage(按 planId)。
|
||||||
|
*
|
||||||
|
* 通过 key={planId} 实现 planId 变化时重新挂载内部组件,
|
||||||
|
* 从而使用 lazy initial state 读取 localStorage,避免在 effect 中 setState。
|
||||||
*/
|
*/
|
||||||
export function AnchorMigrationBanner() {
|
export function AnchorMigrationBanner() {
|
||||||
const t = useTranslations("lessonPreparation");
|
|
||||||
const doc = useLessonPlanEditor((s) => s.doc);
|
const doc = useLessonPlanEditor((s) => s.doc);
|
||||||
const planId = useLessonPlanEditor((s) => s.planId);
|
const planId = useLessonPlanEditor((s) => s.planId);
|
||||||
const [dismissed, setDismissed] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
return (
|
||||||
if (planId) {
|
<BannerContent
|
||||||
const stored = localStorage.getItem(`lp-legacy-anchor-dismissed-${planId}`);
|
key={planId || "no-plan"}
|
||||||
setDismissed(stored === "true");
|
doc={doc}
|
||||||
} else {
|
planId={planId}
|
||||||
setDismissed(false);
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}, [planId]);
|
|
||||||
|
function BannerContent({
|
||||||
|
doc,
|
||||||
|
planId,
|
||||||
|
}: {
|
||||||
|
doc: LessonPlanDocument;
|
||||||
|
planId: string;
|
||||||
|
}) {
|
||||||
|
const t = useTranslations("lessonPreparation");
|
||||||
|
const [dismissed, setDismissed] = useState<boolean>(() => {
|
||||||
|
if (typeof window === "undefined") return false;
|
||||||
|
if (!planId) return false;
|
||||||
|
return localStorage.getItem(`lp-legacy-anchor-dismissed-${planId}`) === "true";
|
||||||
|
});
|
||||||
|
|
||||||
// 检测是否有旧锚点(invalid: true 或含 start/end 字段)
|
// 检测是否有旧锚点(invalid: true 或含 start/end 字段)
|
||||||
const hasLegacyAnchors = doc.anchors.some(
|
const hasLegacyAnchors = doc.anchors.some(
|
||||||
|
|||||||
Reference in New Issue
Block a user