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:
@@ -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 ? (
|
||||
|
||||
Reference in New Issue
Block a user