forked from 77media/video-flow
报错提示修复,样式优化
This commit is contained in:
parent
2c88b8201b
commit
e405f4bd7d
@ -60,7 +60,11 @@ export interface CreateMovieProjectV2Request {
|
|||||||
/** 类型 */
|
/** 类型 */
|
||||||
genre: string;
|
genre: string;
|
||||||
/** 角色简介数组 */
|
/** 角色简介数组 */
|
||||||
character_briefs: string[];
|
character_briefs: {
|
||||||
|
name:string;
|
||||||
|
image_url:string;
|
||||||
|
character_analysis:Record<string,any>;
|
||||||
|
}[];
|
||||||
/** 语言 */
|
/** 语言 */
|
||||||
language: string;
|
language: string;
|
||||||
/** 图片URL */
|
/** 图片URL */
|
||||||
|
|||||||
@ -30,7 +30,7 @@ request.interceptors.request.use(
|
|||||||
request.interceptors.response.use(
|
request.interceptors.response.use(
|
||||||
(response: AxiosResponse) => {
|
(response: AxiosResponse) => {
|
||||||
// 直接返回响应数据
|
// 直接返回响应数据
|
||||||
if (!response.data?.successful) {
|
if (response.data?.code !=0) {
|
||||||
// TODO 暂时固定报错信息,后续根据后端返回的错误码进行处理
|
// TODO 暂时固定报错信息,后续根据后端返回的错误码进行处理
|
||||||
errorHandle(0);
|
errorHandle(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -426,9 +426,11 @@ export const useImageStoryServiceHook = (): UseImageStoryService => {
|
|||||||
// 从charactersAnalysis中提取whisk_caption字段组成数组
|
// 从charactersAnalysis中提取whisk_caption字段组成数组
|
||||||
const character_briefs = charactersAnalysis.map(char => {
|
const character_briefs = charactersAnalysis.map(char => {
|
||||||
|
|
||||||
const obj = JSON.parse(char.whisk_caption);
|
return {
|
||||||
obj.character_analysis.role_name = char.role_name;
|
name:char.role_name,
|
||||||
return JSON.stringify(obj);
|
image_url:char.crop_url,
|
||||||
|
character_analysis:JSON.parse(char.whisk_caption).character_analysis
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const params: CreateMovieProjectV2Request = {
|
const params: CreateMovieProjectV2Request = {
|
||||||
|
|||||||
@ -142,7 +142,7 @@ export const useTemplateStoryServiceHook = (): UseTemplateStoryService => {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}, 10000);
|
}, 3000);
|
||||||
});
|
});
|
||||||
|
|
||||||
setTemplateStoryList(templates);
|
setTemplateStoryList(templates);
|
||||||
|
|||||||
@ -10,13 +10,13 @@ describe("flow 提示词优化", () => {
|
|||||||
- 身高估计:设定一个具体的值
|
- 身高估计:设定一个具体的值
|
||||||
- 人种:预估这个人的人种。
|
- 人种:预估这个人的人种。
|
||||||
|
|
||||||
2. **详细面容** 必须要给出预测的数值
|
2. **详细面容**:每一段不少于20个词的描述
|
||||||
- 眼睛:描述大小、形状、颜色、间距。
|
- 眼睛。
|
||||||
- 鼻子:描述长度、宽度、形态。
|
- 鼻子
|
||||||
- 嘴巴:描述宽度、形状、颜色。
|
- 嘴巴
|
||||||
- 耳朵:描述大小、形状、位置。
|
- 耳朵
|
||||||
- 脸型:描述整体轮廓、下巴、颧骨。
|
- 脸型
|
||||||
- 额头:描述高度、形状、发际线。
|
- 额头
|
||||||
|
|
||||||
3. **外观特征**: 每一段不少于30个词的描述
|
3. **外观特征**: 每一段不少于30个词的描述
|
||||||
- 肤色:必须给出具体的颜色描述(如“浅米色”、“深棕色”、“橄榄色”等),严禁使用“浅色”、“深色”或“中等”等模糊词汇。
|
- 肤色:必须给出具体的颜色描述(如“浅米色”、“深棕色”、“橄榄色”等),严禁使用“浅色”、“深色”或“中等”等模糊词汇。
|
||||||
|
|||||||
@ -18,7 +18,15 @@ import {
|
|||||||
Sparkles,
|
Sparkles,
|
||||||
Settings,
|
Settings,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { Dropdown, Modal, Tooltip, Upload, Spin, Popconfirm } from "antd";
|
import {
|
||||||
|
Dropdown,
|
||||||
|
Modal,
|
||||||
|
Tooltip,
|
||||||
|
Upload,
|
||||||
|
Spin,
|
||||||
|
Popconfirm,
|
||||||
|
Image,
|
||||||
|
} from "antd";
|
||||||
import { UploadOutlined } from "@ant-design/icons";
|
import { UploadOutlined } from "@ant-design/icons";
|
||||||
import { StoryTemplateEntity } from "@/app/service/domain/Entities";
|
import { StoryTemplateEntity } from "@/app/service/domain/Entities";
|
||||||
import { useImageStoryServiceHook } from "@/app/service/Interaction/ImageStoryService";
|
import { useImageStoryServiceHook } from "@/app/service/Interaction/ImageStoryService";
|
||||||
@ -53,9 +61,12 @@ const RenderTemplateStoryMode = ({
|
|||||||
setActiveRoleIndex,
|
setActiveRoleIndex,
|
||||||
setActiveRoleImage,
|
setActiveRoleImage,
|
||||||
setActiveRoleAudio,
|
setActiveRoleAudio,
|
||||||
clearData
|
clearData,
|
||||||
} = useTemplateStoryServiceHook();
|
} = useTemplateStoryServiceHook();
|
||||||
|
|
||||||
|
// 使用上传文件hook
|
||||||
|
const { uploadFile, isUploading } = useUploadFile();
|
||||||
|
|
||||||
// 本地加载状态,用于 UI 反馈
|
// 本地加载状态,用于 UI 反馈
|
||||||
const [localLoading, setLocalLoading] = useState(0);
|
const [localLoading, setLocalLoading] = useState(0);
|
||||||
|
|
||||||
@ -103,14 +114,23 @@ const RenderTemplateStoryMode = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理角色图片上传
|
// 处理角色图片上传 - 已移至customRequest中处理
|
||||||
const handleRoleImageUpload = (roleIndex: number, file: any) => {
|
// const handleRoleImageUpload = async (roleIndex: number, file: File) => {
|
||||||
if (file && selectedTemplate) {
|
// if (!file || !selectedTemplate) return;
|
||||||
// 模拟上传成功,设置图片URL
|
//
|
||||||
const imageUrl = URL.createObjectURL(file);
|
// try {
|
||||||
setActiveRoleImage(imageUrl);
|
// // 使用真正的文件上传功能
|
||||||
}
|
// const imageUrl = await uploadFile(file, (progress) => {
|
||||||
};
|
// console.log(`上传进度: ${progress}%`);
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// // 上传成功后,更新角色图片
|
||||||
|
// setActiveRoleImage(imageUrl);
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error("图片上传失败:", error);
|
||||||
|
// // 这里可以添加错误提示
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
// 删除角色图片
|
// 删除角色图片
|
||||||
const handleDeleteRoleImage = (roleIndex: number) => {
|
const handleDeleteRoleImage = (roleIndex: number) => {
|
||||||
@ -122,7 +142,6 @@ const RenderTemplateStoryMode = ({
|
|||||||
const templateListRender = () => {
|
const templateListRender = () => {
|
||||||
return (
|
return (
|
||||||
<div className="w-1/3 p-4 border-r border-white/[0.1]">
|
<div className="w-1/3 p-4 border-r border-white/[0.1]">
|
||||||
<h3 className="text-xl font-bold text-white mb-6">Story Templates</h3>
|
|
||||||
<div className="space-y-4 max-h-[700px] overflow-y-auto pr-3 template-list-scroll">
|
<div className="space-y-4 max-h-[700px] overflow-y-auto pr-3 template-list-scroll">
|
||||||
{templateStoryList.map((template, index) => (
|
{templateStoryList.map((template, index) => (
|
||||||
<div
|
<div
|
||||||
@ -148,7 +167,7 @@ const RenderTemplateStoryMode = ({
|
|||||||
};
|
};
|
||||||
// 故事编辑器渲染
|
// 故事编辑器渲染
|
||||||
const storyEditorRender = () => {
|
const storyEditorRender = () => {
|
||||||
return selectedTemplate ? (
|
return selectedTemplate ? (
|
||||||
<div className="relative h-full">
|
<div className="relative h-full">
|
||||||
{/* 模板信息头部 - 增加顶部空间 */}
|
{/* 模板信息头部 - 增加顶部空间 */}
|
||||||
<div className="flex gap-3 py-4 border-b border-white/[0.1] h-[300px]">
|
<div className="flex gap-3 py-4 border-b border-white/[0.1] h-[300px]">
|
||||||
@ -211,25 +230,67 @@ const RenderTemplateStoryMode = ({
|
|||||||
listType="picture-card"
|
listType="picture-card"
|
||||||
className="avatar-uploader [&_.ant-upload-select]:!w-32 [&_.ant-upload-select]:!h-32"
|
className="avatar-uploader [&_.ant-upload-select]:!w-32 [&_.ant-upload-select]:!h-32"
|
||||||
showUploadList={false}
|
showUploadList={false}
|
||||||
action="https://660d2bd96ddfa2943b33731c.mockapi.io/api/upload"
|
beforeUpload={(file) => {
|
||||||
beforeUpload={() => false}
|
// 验证文件类型
|
||||||
onChange={(info) => {
|
const isImage = file.type.startsWith("image/");
|
||||||
if (info.file.status === "done") {
|
if (!isImage) {
|
||||||
handleRoleImageUpload(
|
console.error("只能上传图片文件");
|
||||||
activeRoleIndex,
|
return false;
|
||||||
info.file.originFileObj
|
}
|
||||||
|
|
||||||
|
// 验证文件大小 (5MB)
|
||||||
|
const isLt5M = file.size / 1024 / 1024 < 10;
|
||||||
|
if (!isLt5M) {
|
||||||
|
console.error("图片大小不能超过10MB");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // 允许文件通过验证
|
||||||
|
}}
|
||||||
|
customRequest={async ({ file, onSuccess, onError }) => {
|
||||||
|
try {
|
||||||
|
const fileObj = file as File;
|
||||||
|
console.log(
|
||||||
|
"开始上传图片文件:",
|
||||||
|
fileObj.name,
|
||||||
|
fileObj.type,
|
||||||
|
fileObj.size
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 使用 hook 上传文件到七牛云
|
||||||
|
const uploadedUrl = await uploadFile(
|
||||||
|
fileObj,
|
||||||
|
(progress) => {
|
||||||
|
console.log(`上传进度: ${progress}%`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("图片上传成功,URL:", uploadedUrl);
|
||||||
|
|
||||||
|
// 上传成功后,更新角色图片
|
||||||
|
setActiveRoleImage(uploadedUrl);
|
||||||
|
|
||||||
|
// 调用成功回调
|
||||||
|
onSuccess?.(uploadedUrl);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("图片上传失败:", error);
|
||||||
|
onError?.(error as Error);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{activeRole?.photo_url ? (
|
{activeRole?.photo_url ? (
|
||||||
<div className="relative w-32 h-32 rounded-lg overflow-hidden">
|
<div className="relative w-32 h-32 rounded-lg overflow-hidden">
|
||||||
<img
|
<Image
|
||||||
src={activeRole.photo_url}
|
src={activeRole.photo_url}
|
||||||
alt="Character Portrait"
|
alt="Character Portrait"
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
|
preview={{
|
||||||
|
mask: null,
|
||||||
|
maskClassName: "hidden",
|
||||||
|
}}
|
||||||
|
fallback="/assets/empty_video.png"
|
||||||
/>
|
/>
|
||||||
<button
|
{/* <button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleDeleteRoleImage(activeRoleIndex);
|
handleDeleteRoleImage(activeRoleIndex);
|
||||||
@ -238,7 +299,7 @@ const RenderTemplateStoryMode = ({
|
|||||||
title="Delete Photo"
|
title="Delete Photo"
|
||||||
>
|
>
|
||||||
<Trash2 className="w-2.5 h-2.5" />
|
<Trash2 className="w-2.5 h-2.5" />
|
||||||
</button>
|
</button> */}
|
||||||
<div className="absolute inset-0 bg-black/50 opacity-0 hover:opacity-100 transition-opacity duration-200 flex items-center justify-center">
|
<div className="absolute inset-0 bg-black/50 opacity-0 hover:opacity-100 transition-opacity duration-200 flex items-center justify-center">
|
||||||
<div className="text-white text-center">
|
<div className="text-white text-center">
|
||||||
<UploadOutlined className="w-4 h-4 mb-1" />
|
<UploadOutlined className="w-4 h-4 mb-1" />
|
||||||
@ -248,8 +309,17 @@ const RenderTemplateStoryMode = ({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-32 h-32 flex flex-col items-center justify-center text-white/50 bg-white/[0.05] border border-white/[0.1] rounded-lg hover:bg-white/[0.08] transition-colors">
|
<div className="w-32 h-32 flex flex-col items-center justify-center text-white/50 bg-white/[0.05] border border-white/[0.1] rounded-lg hover:bg-white/[0.08] transition-colors">
|
||||||
<UploadOutlined className="w-6 h-6 mb-1" />
|
{isUploading ? (
|
||||||
<span className="text-xs">Upload Photo</span>
|
<>
|
||||||
|
<Loader2 className="w-6 h-6 mb-1 animate-spin" />
|
||||||
|
<span className="text-xs">上传中...</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<UploadOutlined className="w-6 h-6 mb-1" />
|
||||||
|
<span className="text-xs">Upload Photo</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Upload>
|
</Upload>
|
||||||
@ -280,11 +350,7 @@ const RenderTemplateStoryMode = ({
|
|||||||
Characters
|
Characters
|
||||||
</h4>
|
</h4>
|
||||||
{selectedTemplate.storyRole.map((role, index: number) => (
|
{selectedTemplate.storyRole.map((role, index: number) => (
|
||||||
<Tooltip
|
<Tooltip key={index} title={role.role_name} placement="left">
|
||||||
key={index}
|
|
||||||
title={role.role_name}
|
|
||||||
placement="left"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
data-alt={`character-thumbnail-${index}`}
|
data-alt={`character-thumbnail-${index}`}
|
||||||
className={`w-full aspect-square rounded-lg overflow-hidden border-2 transition-all duration-200 hover:scale-105 ${
|
className={`w-full aspect-square rounded-lg overflow-hidden border-2 transition-all duration-200 hover:scale-105 ${
|
||||||
@ -294,10 +360,15 @@ const RenderTemplateStoryMode = ({
|
|||||||
}`}
|
}`}
|
||||||
onClick={() => setActiveRoleIndex(index)}
|
onClick={() => setActiveRoleIndex(index)}
|
||||||
>
|
>
|
||||||
<img
|
<Image
|
||||||
src={role.photo_url}
|
src={role.photo_url}
|
||||||
alt={role.role_name}
|
alt={role.role_name}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
|
preview={{
|
||||||
|
mask: null,
|
||||||
|
maskClassName: "hidden",
|
||||||
|
}}
|
||||||
|
fallback="/assets/empty_video.png"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@ -305,7 +376,7 @@ const RenderTemplateStoryMode = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className=" absolute bottom-0 right-0">
|
<div className=" absolute -bottom-4 right-0">
|
||||||
<ActionButton
|
<ActionButton
|
||||||
isCreating={localLoading > 0}
|
isCreating={localLoading > 0}
|
||||||
handleCreateVideo={handleConfirm}
|
handleCreateVideo={handleConfirm}
|
||||||
@ -321,7 +392,7 @@ const RenderTemplateStoryMode = ({
|
|||||||
<p className="text-sm">Please try again later</p>
|
<p className="text-sm">Please try again later</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -329,7 +400,7 @@ const RenderTemplateStoryMode = ({
|
|||||||
open={isOpen}
|
open={isOpen}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
// 清空所有选中的内容数据
|
// 清空所有选中的内容数据
|
||||||
clearData()
|
clearData();
|
||||||
onClose();
|
onClose();
|
||||||
}}
|
}}
|
||||||
footer={null}
|
footer={null}
|
||||||
@ -344,7 +415,7 @@ const RenderTemplateStoryMode = ({
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="rounded-2xl">
|
<div className="rounded-2xl min-h-min transition-all duration-700 ease-out">
|
||||||
{/* 弹窗头部 */}
|
{/* 弹窗头部 */}
|
||||||
<div className="flex gap-4 p-4 border-b border-white/[0.1]">
|
<div className="flex gap-4 p-4 border-b border-white/[0.1]">
|
||||||
<h2 className="text-2xl font-bold text-white">
|
<h2 className="text-2xl font-bold text-white">
|
||||||
@ -352,7 +423,7 @@ const RenderTemplateStoryMode = ({
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<GlobalLoad show={isLoading} progress={0}>
|
<GlobalLoad show={isLoading} progress={0}>
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4 pb-4 ">
|
||||||
{templateListRender()}
|
{templateListRender()}
|
||||||
<div className="flex-1">{storyEditorRender()}</div>
|
<div className="flex-1">{storyEditorRender()}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -871,22 +942,22 @@ const PhotoStoryModal = ({
|
|||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-4">
|
||||||
{/* 左侧:图片上传 */}
|
{/* 左侧:图片上传 */}
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<div
|
<div
|
||||||
data-alt="image-upload-area"
|
data-alt="image-upload-area"
|
||||||
className={`w-24 h-24 rounded-lg flex flex-col items-center justify-center transition-all duration-300 cursor-pointer ${
|
className={`w-32 h-32 rounded-lg flex flex-col items-center justify-center transition-all duration-300 cursor-pointer ${
|
||||||
activeImageUrl
|
activeImageUrl
|
||||||
? "border-2 border-white/20 bg-white/[0.05]"
|
? "border-2 border-white/20 bg-white/[0.05]"
|
||||||
: "border-2 border-dashed border-white/20 bg-white/[0.02] hover:border-white/40 hover:bg-white/[0.05] hover:scale-105"
|
: "border-2 border-dashed border-white/20 bg-white/[0.02] hover:border-white/40 hover:bg-white/[0.05] hover:scale-105"
|
||||||
}`}
|
}`}
|
||||||
onClick={handleImageUpload}
|
onClick={handleImageUpload}
|
||||||
>
|
>
|
||||||
{activeImageUrl ? (
|
{activeImageUrl ? (
|
||||||
<div className="relative w-full h-full">
|
<div className="relative w-full h-full">
|
||||||
<img
|
<img
|
||||||
src={activeImageUrl}
|
src={activeImageUrl}
|
||||||
alt="Story inspiration"
|
alt="Story inspiration"
|
||||||
className="w-full h-full object-cover rounded-lg"
|
className="w-full h-full object-cover rounded-lg bg-white/[0.05]"
|
||||||
/>
|
/>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="Clear all content"
|
title="Clear all content"
|
||||||
description="Are you sure you want to clear all content? This action cannot be undone."
|
description="Are you sure you want to clear all content? This action cannot be undone."
|
||||||
@ -932,17 +1003,17 @@ const PhotoStoryModal = ({
|
|||||||
key={`${avatar.name}-${index}`}
|
key={`${avatar.name}-${index}`}
|
||||||
className="flex flex-col items-center"
|
className="flex flex-col items-center"
|
||||||
>
|
>
|
||||||
<div className="relative w-14 h-14 rounded-sm overflow-hidden bg-white/[0.05] border border-white/[0.1] mb-2 group cursor-pointer">
|
<div className="relative w-20 h-20 rounded-sm overflow-hidden bg-white/[0.05] border border-white/[0.1] mb-2 group cursor-pointer">
|
||||||
<img
|
<img
|
||||||
src={avatar.url}
|
src={avatar.url}
|
||||||
alt={avatar.name}
|
alt={avatar.name}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover bg-white/[0.05]"
|
||||||
onError={(e) => {
|
onError={(e) => {
|
||||||
// 如果裁剪的头像加载失败,回退到原图
|
// 如果裁剪的头像加载失败,回退到原图
|
||||||
const target = e.target as HTMLImageElement;
|
const target = e.target as HTMLImageElement;
|
||||||
target.src = activeImageUrl;
|
target.src = activeImageUrl;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/* 删除角色按钮 - 使用Tooltip并调整z-index避免被遮挡 */}
|
{/* 删除角色按钮 - 使用Tooltip并调整z-index避免被遮挡 */}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title="Remove this character from the movie"
|
title="Remove this character from the movie"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user