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