diff --git a/components/home/lovart-home.tsx b/components/home/lovart-home.tsx index e11fbb1bf..bbe8b66a3 100644 --- a/components/home/lovart-home.tsx +++ b/components/home/lovart-home.tsx @@ -335,6 +335,8 @@ export function LovartHome({ const [isLoggingIn, setIsLoggingIn] = useState(false); const [showImageEditor, setShowImageEditor] = useState(false); const [editorImageUrl, setEditorImageUrl] = useState(""); + const [tutorialDialogOpen, setTutorialDialogOpen] = useState(false); + const [currentTutorialVideo, setCurrentTutorialVideo] = useState(null); const [typedPreviewText, setTypedPreviewText] = useState(""); const [previewCursorVisible, setPreviewCursorVisible] = useState(true); const projectSectionRef = useRef(null); @@ -576,6 +578,31 @@ export function LovartHome({ .sort((a, b) => a.order - b.order), [navLinksData?.links] ); + const tutorialVideos = useMemo(() => { + const configured = (tutorialVideosData?.videos || []) + .filter((video) => video.isEnabled !== false && Boolean(video.videoUrl)) + .sort((a, b) => a.order - b.order); + if (configured.length > 0) { + return configured; + } + + const fallbackUrl = String(siteSettingsData?.tutorialVideoUrl || "").trim(); + if (!fallbackUrl) { + return [] as TutorialVideo[]; + } + + return [ + { + id: "fallback-tutorial", + title: "功能教程", + videoUrl: fallbackUrl, + coverUrl: null, + description: "默认教程视频", + order: 0, + isEnabled: true, + }, + ]; + }, [siteSettingsData?.tutorialVideoUrl, tutorialVideosData?.videos]); const canUseGoogleLogin = Boolean( authSettingsData?.isGoogleEnabled && authSettingsData?.isGoogleConfigured @@ -913,24 +940,13 @@ export function LovartHome({ setEditorImageUrl(""); }; - const handleOpenTutorial = () => { - if (!isAuthenticated) { - openLoginDialog(); - return; - } - - const firstTutorial = - tutorialVideosData?.videos - ?.filter((video) => video.isEnabled !== false && video.videoUrl) - ?.sort((a, b) => a.order - b.order)?.[0] || null; - const tutorialUrl = firstTutorial?.videoUrl || siteSettingsData?.tutorialVideoUrl || ""; - - if (!tutorialUrl) { + const handleOpenTutorial = (video: TutorialVideo) => { + if (!video?.videoUrl) { toast.info("暂无教程视频"); return; } - - window.open(tutorialUrl, "_blank", "noopener,noreferrer"); + setCurrentTutorialVideo(video); + setTutorialDialogOpen(true); }; const handleCreateDulupayOrder = async (packageId: string) => { @@ -1290,6 +1306,7 @@ export function LovartHome({ type="button" onClick={openNewCanvas} className="flex h-[52px] w-[52px] items-center justify-center rounded-full border border-black/10 bg-white shadow" + title="新建项目" >
@@ -1301,6 +1318,7 @@ export function LovartHome({ type="button" className="flex h-10 w-10 items-center justify-center rounded-full bg-black/5" onClick={() => window.scrollTo({ top: 0, behavior: "smooth" })} + title="首页" > @@ -1308,6 +1326,7 @@ export function LovartHome({ type="button" className="flex h-10 w-10 items-center justify-center rounded-full hover:bg-black/5" onClick={openAllProjectsInPage} + title="项目列表" > @@ -1315,6 +1334,7 @@ export function LovartHome({ type="button" className="flex h-10 w-10 items-center justify-center rounded-full hover:bg-black/5" onClick={() => toast.info("个人主页模块后续开放")} + title="个人主页" > @@ -1322,18 +1342,63 @@ export function LovartHome({ type="button" className="flex h-10 w-10 items-center justify-center rounded-full hover:bg-black/5" onClick={() => toast.info("帮助中心后续开放")} + title="帮助中心" > {siteSettingsData?.tutorialEnabled ? ( - + + + + + +
视频教程
+
+ {tutorialVideos.length > 0 ? ( + tutorialVideos.map((video) => ( + + )) + ) : ( +
暂无教程视频
+ )} +
+
+
) : null} @@ -1737,9 +1803,46 @@ export function LovartHome({ imageUrl={editorImageUrl} onConfirm={handleImageEditorConfirm} onCancel={handleImageEditorCancel} + onHomeClick={handleImageEditorCancel} /> ) : null} + { + setTutorialDialogOpen(open); + if (!open) { + setCurrentTutorialVideo(null); + } + }} + > + + + + {currentTutorialVideo?.title || "功能教程"} + + +
+ {currentTutorialVideo?.videoUrl ? ( +
+ {currentTutorialVideo?.description ? ( +
+ {currentTutorialVideo.description} +
+ ) : null} +
+
+ {loginDialogOpen ? (
void; onCancel: () => void; + onHomeClick?: () => void; initialResizeMode?: boolean; // 是否直接进入尺寸调整模式 } @@ -48,7 +49,7 @@ const COLORS = [ const LINE_WIDTHS = [2, 4, 6, 8]; -export function ImageAnnotator({ imageUrl, onConfirm, onCancel, initialResizeMode = false }: ImageAnnotatorProps) { +export function ImageAnnotator({ imageUrl, onConfirm, onCancel, onHomeClick, initialResizeMode = false }: ImageAnnotatorProps) { const canvasRef = useRef(null); const containerRef = useRef(null); const [ctx, setCtx] = useState(null); @@ -1384,7 +1385,13 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel, initialResizeMod e.stopPropagation()} + onClick={(e) => { + e.stopPropagation(); + if (onHomeClick) { + e.preventDefault(); + onHomeClick(); + } + }} > {siteLogoDark || siteLogo ? ( <>