feat: 新增引导式新手指引 (OnboardingTour) 组件,全站页面接入

This commit is contained in:
OmniAI Developer
2026-06-08 21:30:48 +08:00
parent 1e756808c1
commit 6ed65ca3ee
24 changed files with 1414 additions and 164 deletions
@@ -61,6 +61,9 @@ function CharacterMixPage({
const abortRef = useRef(false);
const taskIdRef = useRef<string | null>(null);
const [isDragging, setIsDragging] = useState(false);
const [isCanvasDragging, setIsCanvasDragging] = useState(false);
const characterInputRef = useRef<HTMLInputElement | null>(null);
const videoInputRef = useRef<HTMLInputElement | null>(null);
useEffect(() => {
return () => {
@@ -262,6 +265,23 @@ function CharacterMixPage({
}
};
const handleCanvasDragOver = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); if (e.dataTransfer.types.includes("Files")) setIsCanvasDragging(true); };
const handleCanvasDragLeave = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); if (e.currentTarget === e.target || !e.currentTarget.contains(e.relatedTarget as Node)) setIsCanvasDragging(false); };
const handleCanvasDrop = (e: DragEvent) => {
e.preventDefault();
e.stopPropagation();
setIsCanvasDragging(false);
handleDrop(e);
};
const handleCanvasClick = () => {
if (!characterPreview) {
characterInputRef.current?.click();
} else if (!videoPreview) {
videoInputRef.current?.click();
}
};
return (
<section className="image-workbench-page character-mix-page" aria-label="角色迁移">
<header className="image-workbench-topbar">
@@ -342,6 +362,7 @@ function CharacterMixPage({
<div className="studio-panel__section-body">
<label className={characterFile ? "studio-upload-slot--filled" : "studio-upload-slot--empty"}>
<input
ref={characterInputRef}
type="file"
accept="image/*"
onChange={(event) => {
@@ -383,6 +404,7 @@ function CharacterMixPage({
<div className="studio-panel__section-body">
<label className={videoFile ? "studio-upload-slot--filled" : "studio-upload-slot--empty"}>
<input
ref={videoInputRef}
type="file"
accept="video/*"
onChange={(event) => {
@@ -441,12 +463,21 @@ function CharacterMixPage({
) : null}
</div>
) : (
<div className="studio-canvas-ghost">
<div
className={`studio-canvas-ghost${isCanvasDragging ? " is-dragging" : ""}`}
onClick={handleCanvasClick}
onDragOver={handleCanvasDragOver}
onDragLeave={handleCanvasDragLeave}
onDrop={handleCanvasDrop}
role="button"
tabIndex={0}
onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") handleCanvasClick(); }}
>
<div className="studio-canvas-ghost__icon">
<SwapOutlined />
</div>
<div className="studio-canvas-ghost__title"></div>
<div className="studio-canvas-ghost__hint"></div>
<div className="studio-canvas-ghost__hint"> (PNG/JPG) (MP4/MOV/AVI)</div>
</div>
)
}