feat: 多页面拖拽上传、滚动条精简、UI优化

- 剧本评测/分辨率提升/数字人/角色迁移/图片工作台/去水印/电商:新增外部拖拽文件上传
- 电商:爆款图复刻上传框支持拖拽+大滚动条,短视频/模特图/详情图滚动条精简回退
- 图片工作台:右侧输出面板移至左侧提示词上方,删除局部重绘遮罩/结果框
- 数字人:生成按钮改为「开始生成」
- 局部重绘:编辑遮罩→编辑页面
- 对话框生成器:新增对话/视频模式、模型/速度/深度选择按钮
- 视频时长默认改为5秒
- 工具箱页面空状态logo统一绿底亮色图标
- 多处CSS滚动条和布局优化
This commit is contained in:
OmniAI Developer
2026-06-05 18:01:55 +08:00
parent 8fbb2ec95e
commit 5b87594e36
22 changed files with 1796 additions and 195 deletions
@@ -17,7 +17,7 @@ import {
ThunderboltOutlined,
UserOutlined,
} from "@ant-design/icons";
import { useCallback, useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useRef, useState, type DragEvent } from "react";
import { aiGenerationClient } from "../../api/aiGenerationClient";
import { uploadAssetWithProgress } from "../../api/uploadWithProgress";
import { waitForTask } from "../../api/taskSubscription";
@@ -95,6 +95,7 @@ function DigitalHumanPage({
const activeTaskIdRef = useRef(activeTaskId);
activeTaskIdRef.current = activeTaskId;
const keepaliveRestoredRef = useRef(false);
const [isDragging, setIsDragging] = useState(false);
useEffect(() => {
return () => {
@@ -146,6 +147,28 @@ function DigitalHumanPage({
setNotice("已取消");
}, [activeTaskId]);
const handleDragOver = (e: DragEvent) => { e.preventDefault(); if (e.dataTransfer?.types?.includes("Files")) setIsDragging(true); };
const handleDragLeave = (e: DragEvent) => { e.preventDefault(); if (!e.currentTarget.contains(e.relatedTarget as Node)) setIsDragging(false); };
const handleDrop = (e: DragEvent) => {
e.preventDefault();
setIsDragging(false);
const file = e.dataTransfer?.files?.[0];
if (!file) return;
if (file.type.startsWith("image/")) {
if (imagePreview) URL.revokeObjectURL(imagePreview);
setImageName(file.name);
setImageFile(file);
setImagePreview(URL.createObjectURL(file));
setNotice(`已拖放参考图 ${file.name}`);
} else if (file.type.startsWith("audio/")) {
if (audioPreview) URL.revokeObjectURL(audioPreview);
setAudioName(file.name);
setAudioFile(file);
setAudioPreview(URL.createObjectURL(file));
setNotice(`已拖放音频 ${file.name}`);
}
};
const handleDownloadResult = async () => {
if (!resultVideoUrl || isDownloadingResult) return;
setIsDownloadingResult(true);
@@ -417,7 +440,17 @@ function DigitalHumanPage({
<StudioToolLayout
noTop
leftPanel={
<>
<div
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
style={{ position: "relative" }}
>
{isDragging ? (
<div style={{ position: "absolute", inset: 0, zIndex: 100, display: "flex", alignItems: "center", justifyContent: "center", background: "rgba(0,0,0,0.55)", border: "2px dashed var(--primary, #4a9eff)", borderRadius: 12, pointerEvents: "none" }}>
<span style={{ fontSize: 18, color: "#fff", fontWeight: 600 }}></span>
</div>
) : null}
<div className="studio-panel__section">
<div className="studio-panel__section-head">
<span className="studio-panel__section-title"></span>
@@ -490,7 +523,7 @@ function DigitalHumanPage({
{audioPreview ? <audio src={audioPreview} controls className="studio-audio-preview" /> : null}
</div>
</div>
</>
</div>
}
canvas={
resultVideoUrl ? (
@@ -558,7 +591,7 @@ function DigitalHumanPage({
</div>
<button type="button" className="studio-generate-btn" onClick={() => void handleCreateTask()} disabled={isCreating || !imageFile || !audioFile}>
<PlayCircleOutlined />
{isCreating ? "生成中..." : "提交 wan2.2-s2v"}
{isCreating ? "生成中..." : "开始生成"}
</button>
{isCreating && (
<button type="button" className="studio-generate-btn digital-human-cancel-btn" onClick={handleCancel} aria-label="取消生成任务">