feat: 模板适配层、易班能力与全局状态

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
zhongyidianandClaude Opus 4.8 committed 2026-08-18 13:39:42 +08:00
1 parent 3995656175
commit d3ae0a8ca7
45 files changed
+6931 -179

No files matched your search

@@ -0,0 +1,57 @@
import { appConfig } from "@/app.config";
import { useSpotlightStore } from "@/stores";
import type {
SpotlightStep,
StartSpotlightOptions,
} from "@/stores/types/spotlight.types";
type DefineSpotlightStep = Omit<SpotlightStep, "target"> & {
target?: SpotlightStep["target"];
};
export interface DefineSpotlightOptions {
id: string;
steps: DefineSpotlightStep[];
showOnce?: boolean;
initialStep?: number;
}
export interface SpotlightController {
id: string;
steps: SpotlightStep[];
start: (options?: Pick<StartSpotlightOptions, "initialStep">) => void;
reset: () => void;
}
function normalizeSteps(steps: DefineSpotlightStep[]): SpotlightStep[] {
return steps.map((step) => ({
...step,
target: step.target ?? { dataGuide: step.id },
}));
}
export function defineSpotlight({
id,
steps,
showOnce = true,
initialStep = 0,
}: DefineSpotlightOptions): SpotlightController {
const normalizedSteps = normalizeSteps(steps);
return {
id,
steps: normalizedSteps,
start: (options) => {
useSpotlightStore.getState().startSpotlight({
courseId: appConfig.course.id,
spotlightId: id,
showOnce,
steps: normalizedSteps,
initialStep: options?.initialStep ?? initialStep,
});
},
reset: () => {
useSpotlightStore.getState().resetSpotlight(appConfig.course.id, id);
},
};
}
@@ -0,0 +1,62 @@
import { useCallback } from "react";
import {
type GuideSpotlightProps,
GuideSpotlight as RegistryGuideSpotlight,
} from "@/components/course-interactions/guide-spotlight";
import { useCourseCanvas } from "@/course-runtime/canvas";
import { useSpotlightStore } from "@/stores";
import { getAppPortalRoot } from "../canvasPortal";
export function GuideSpotlight({
children,
onFinish,
onSkip,
container = getAppPortalRoot,
viewport,
...props
}: Omit<GuideSpotlightProps, "actions" | "state"> & {
onFinish?: () => void;
onSkip?: () => void;
}) {
const {
open,
steps,
current,
nextStep,
prevStep,
finishSpotlight,
skipSpotlight,
} = useSpotlightStore();
const { viewport: canvasViewport } = useCourseCanvas();
const finish = useCallback(() => {
finishSpotlight();
onFinish?.();
}, [finishSpotlight, onFinish]);
const skip = useCallback(() => {
skipSpotlight();
onSkip?.();
}, [onSkip, skipSpotlight]);
return (
<RegistryGuideSpotlight
state={{
open,
steps,
current,
}}
actions={{
next: nextStep,
prev: prevStep,
skip,
finish,
}}
container={container}
viewport={viewport ?? canvasViewport}
{...props}
>
{children}
</RegistryGuideSpotlight>
);
}
@@ -0,0 +1,7 @@
export type {
GuideSpotlightProps,
GuideSpotlightRenderState,
GuideSpotlightTheme,
Rect,
SpotlightLayout,
} from "@/components/course-interactions/guide-spotlight";
@@ -0,0 +1,11 @@
export { GuideSpotlight } from "./GuideSpotlight";
export type {
DefineSpotlightOptions,
SpotlightController,
} from "./GuideSpotlight.helpers";
export { defineSpotlight } from "./GuideSpotlight.helpers";
export type {
GuideSpotlightProps,
GuideSpotlightRenderState,
GuideSpotlightTheme,
} from "./GuideSpotlight.types";