fix: 全站页面保活机制、登录拦截优化、UI修复与功能完善

- 移除未登录全页面拦截,改为浏览自由 + 功能使用时弹窗
- 修复PageTransition退出动画卡死导致黑屏的bug
- CanvasPage添加加载中状态避免首次访问黑屏假死
- 全站7个工具页添加页面保活机制,切页后台任务不中断
- 修复未登录时401误触发"用户已在别处登录"弹窗
- 删除MorePage模板板块、微信登录、EcommerceTemplates/SizeTemplate路由
- 剧本评分接入DashScope qwen3.7-max直连API
- 电商视频生成重构为3阶段可视管线(策划→生成图片→生成视频)
- 电商视频保活增强:异步函数直接写localStorage避免卸载丢失
- Workbench侧边栏移除mode过滤,三模式共用同一对话列表
- 首页更新轮播图/背景视频、按钮跳转修正、文案优化
- AppShell顶栏新增网站备案信息按钮
- 多个页面的terminate/cancel按钮覆盖、单镜头重试、批量保存下载

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
2026-06-03 01:39:06 +08:00
parent 8f57e08004
commit 468d1d27dd
35 changed files with 1263 additions and 441 deletions
+46 -30
View File
@@ -48,6 +48,7 @@ import {
} from "./canvasCommunityPublish";
import { createCanvasAssetRefFromGeneratedResult, persistCanvasGeneratedResultAsset } from "./canvasAssetPersistence";
import { normalizeCanvasWorkflowSchema } from "./canvasWorkflowSchema";
import { createBlankWorkflow } from "../../data/workflows";
import { useCanvasHistory, type CanvasHistorySnapshot } from "./useCanvasHistory";
import { useCanvasKeyboard } from "./useCanvasKeyboard";
import { useCanvasNodeDrag } from "./useCanvasNodeDrag";
@@ -310,7 +311,7 @@ function getCameraMotionPrompt(value: string): string {
}
function CanvasPage({
workflow,
workflow: rawWorkflow,
projectId,
projects = [],
projectsLoaded = true,
@@ -323,6 +324,7 @@ function CanvasPage({
onSaveWorkflow,
onCreateTask,
}: CanvasPageProps) {
const workflow = rawWorkflow || createBlankWorkflow();
const [contextMenu, setContextMenu] = useState<CanvasFloatingMenuPosition | null>(null);
const [nodeMenu, setNodeMenu] = useState<CanvasFloatingMenuPosition | null>(null);
const [textNodeMenu, setTextNodeMenu] = useState<{ left: number; top: number; nodeId: string } | null>(null);
@@ -404,6 +406,7 @@ function CanvasPage({
textGenerationState, imageGenerationState, videoGenerationState,
generationToast, setGenerationToast,
imageGenerationInFlightRef, textGenerationInFlightRef, textGenerationAbortControllersRef,
canvasGenKeepaliveRestoredRef,
setTextGenerationStatus, setImageGenerationStatus, setVideoGenerationStatus,
restoreKeepaliveTasks, resetGenerationState,
} = useCanvasGeneration({ setImageNodes, setVideoNodes });
@@ -524,6 +527,7 @@ function CanvasPage({
const canvasAssets = serverAssets.filter((asset) => asset.imageUrl);
const shouldShowEmptyProjectState =
projectsLoaded && projects.length === 0 && !projectId && workflow.source === "blank" && workflow.nodes.length === 0;
const isWaitingForProjects = isAuthenticated && !projectsLoaded;
const [projectSaveState, setProjectSaveState] = useState<CanvasProjectSaveState>({
status: "idle",
message: "",
@@ -571,10 +575,13 @@ function CanvasPage({
imageNodeIdRef.current = nextImageNodes.length + 1;
videoNodeIdRef.current = nextVideoNodes.length + 1;
// Reset keepalive flag so tasks can be restored for this project
canvasGenKeepaliveRestoredRef.current = false;
if (projectId && isAuthenticated) {
restoreKeepaliveTasks(projectId, nextImageNodes, nextVideoNodes);
}
}, [workflow.id, workflow.nodes, projectId, isAuthenticated]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [workflow.id, workflow.nodes, projectId]);
useEffect(() => {
if (!isAuthenticated) {
@@ -3524,16 +3531,16 @@ function CanvasPage({
return (
<WorkspacePageShell title="画布" fullWidth className="canvas-page page-motion">
<div className={`studio-tool-layout studio-tool-layout--no-top studio-tool-layout--no-left studio-tool-layout--no-right studio-tool-layout--canvas${shouldShowEmptyProjectState ? " studio-tool-layout--canvas-empty" : ""}`}>
<div className={`studio-tool-layout studio-tool-layout--no-top studio-tool-layout--no-left studio-tool-layout--no-right studio-tool-layout--canvas${(shouldShowEmptyProjectState || isWaitingForProjects) ? " studio-tool-layout--canvas-empty" : ""}`}>
<section
className={`studio-canvas${pendingLinkPort ? " is-linking" : ""}${shouldShowEmptyProjectState ? " is-empty-projects" : ""}`}
className={`studio-canvas${pendingLinkPort ? " is-linking" : ""}${(shouldShowEmptyProjectState || isWaitingForProjects) ? " is-empty-projects" : ""}`}
ref={canvasRef}
onAuxClick={shouldShowEmptyProjectState ? undefined : handleCanvasAuxClick}
onContextMenu={shouldShowEmptyProjectState ? (event) => event.preventDefault() : handleCanvasContextMenu}
onMouseDownCapture={shouldShowEmptyProjectState ? undefined : handleCanvasMouseDown}
onDoubleClick={shouldShowEmptyProjectState ? undefined : handleCanvasDoubleClick}
onMouseMove={shouldShowEmptyProjectState ? undefined : handleCanvasMouseMove}
onWheel={shouldShowEmptyProjectState ? undefined : handleCanvasWheel}
onAuxClick={(shouldShowEmptyProjectState || isWaitingForProjects) ? undefined : handleCanvasAuxClick}
onContextMenu={(shouldShowEmptyProjectState || isWaitingForProjects) ? (event) => event.preventDefault() : handleCanvasContextMenu}
onMouseDownCapture={(shouldShowEmptyProjectState || isWaitingForProjects) ? undefined : handleCanvasMouseDown}
onDoubleClick={(shouldShowEmptyProjectState || isWaitingForProjects) ? undefined : handleCanvasDoubleClick}
onMouseMove={(shouldShowEmptyProjectState || isWaitingForProjects) ? undefined : handleCanvasMouseMove}
onWheel={(shouldShowEmptyProjectState || isWaitingForProjects) ? undefined : handleCanvasWheel}
style={{
"--canvas-bg-size": `${24 * canvasViewport.zoom}px`,
"--canvas-bg-x": `${canvasViewport.x}px`,
@@ -3555,7 +3562,7 @@ function CanvasPage({
className="studio-canvas-hidden-input"
onChange={(event) => handleImageFileSelected(event, pendingImagePosition)}
/>
{!shouldShowEmptyProjectState ? (
{(!shouldShowEmptyProjectState || isWaitingForProjects) ? (
<div className="studio-canvas-project-bar" onMouseDown={(event) => event.stopPropagation()}>
<div className="studio-canvas-project-bar__identity">
{projectNameEditing ? (
@@ -3650,7 +3657,7 @@ function CanvasPage({
</button>
</div>
) : null}
{!shouldShowEmptyProjectState && recentProjectsOpen ? (
{(!shouldShowEmptyProjectState || isWaitingForProjects) && recentProjectsOpen ? (
<aside
id="studio-canvas-recent-drawer"
className="studio-canvas-recent-drawer"
@@ -3718,8 +3725,8 @@ function CanvasPage({
zoomOnPinch={false}
zoomOnScroll={false}
proOptions={{ hideAttribution: true }}
onPaneClick={shouldShowEmptyProjectState ? undefined : handlePaneClick}
onPaneContextMenu={shouldShowEmptyProjectState ? undefined : handlePaneContextMenu}
onPaneClick={(shouldShowEmptyProjectState || isWaitingForProjects) ? undefined : handlePaneClick}
onPaneContextMenu={(shouldShowEmptyProjectState || isWaitingForProjects) ? undefined : handlePaneContextMenu}
>
<Background gap={24} color="transparent" className="studio-canvas__background" />
</ReactFlow>
@@ -3731,7 +3738,7 @@ function CanvasPage({
<button type="button" title="放大" onClick={zoomCanvasIn}>+</button>
<button type="button" title="适应视图" onClick={fitCanvasView}></button>
</div>
{shouldShowEmptyProjectState ? (
{(shouldShowEmptyProjectState || isWaitingForProjects) ? (
<div
className="studio-canvas-empty-projects"
role="status"
@@ -3742,21 +3749,30 @@ function CanvasPage({
event.stopPropagation();
}}
>
<strong></strong>
<button
type="button"
className="studio-canvas-empty-projects__button"
onClick={(event) => {
event.stopPropagation();
if (onStartCreate) {
onStartCreate();
return;
}
onOpenLogin();
}}
>
</button>
{isWaitingForProjects ? (
<>
<div className="studio-canvas-loading-spinner" />
<strong></strong>
</>
) : (
<>
<strong></strong>
<button
type="button"
className="studio-canvas-empty-projects__button"
onClick={(event) => {
event.stopPropagation();
if (onStartCreate) {
onStartCreate();
return;
}
onOpenLogin();
}}
>
</button>
</>
)}
</div>
) : null}
{selectionRect ? (