forked from 77media/video-flow
更新模板故事的样式
This commit is contained in:
parent
b1bdc9423d
commit
6fba02c591
@ -215,147 +215,129 @@ const RenderTemplateStoryMode = ({
|
||||
>
|
||||
Template Configuration
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
||||
{selectedTemplate.fillable_content.map((field, index) => (
|
||||
<div
|
||||
key={index}
|
||||
data-alt={`variable-field-${index}`}
|
||||
className="flex items-center gap-4"
|
||||
className="flex flex-col items-center space-y-3"
|
||||
>
|
||||
{/* 字段名称 */}
|
||||
<div className="w-22 flex items-center gap-2">
|
||||
{/* 图片缩略图 - 显示对应角色的图片 */}
|
||||
<div className="relative group flex items-center justify-center">
|
||||
<Tooltip title={field.field_name} placement="top">
|
||||
<div
|
||||
data-alt={`field-thumbnail-${index}`}
|
||||
className="w-16 h-16 rounded-lg overflow-hidden border border-white/20 bg-white/[0.05] flex items-center justify-center cursor-pointer hover:scale-105 transition-all duration-200"
|
||||
>
|
||||
<Image
|
||||
src={
|
||||
selectedTemplate?.storyRole?.find(
|
||||
(role) => role.role_name === field.field_name
|
||||
)?.photo_url || "/assets/empty_video.png"
|
||||
{/* 图片容器 */}
|
||||
<div className="relative group">
|
||||
<Tooltip
|
||||
title={
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
value={field.field_value || ""}
|
||||
onChange={(e) =>
|
||||
updateFillableContentField(
|
||||
field.field_name,
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
alt={field.field_name}
|
||||
className="w-full h-full object-cover"
|
||||
preview={{
|
||||
mask: null,
|
||||
maskClassName: "hidden",
|
||||
}}
|
||||
fallback="/assets/empty_video.png"
|
||||
placeholder={`${field.field_description}`}
|
||||
className="w-[30rem] px-3 py-2 pr-16 bg-white/0 border border-white/10 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500/30 transition-all duration-200 text-sm"
|
||||
/>
|
||||
<div className="absolute right-2 top-1/2 -translate-y-1/2">
|
||||
{/* AI生成按钮 */}
|
||||
<ActionButton
|
||||
isCreating={false}
|
||||
handleCreateVideo={() =>
|
||||
handleFieldBlur(
|
||||
field.field_name,
|
||||
field.field_value || ""
|
||||
)
|
||||
}
|
||||
icon={<Sparkles className="w-4 h-4" />}
|
||||
width="w-8"
|
||||
height="h-8"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
{/* 上传按钮 - 右上角 */}
|
||||
<Upload
|
||||
name="fieldImage"
|
||||
showUploadList={false}
|
||||
beforeUpload={(file) => {
|
||||
// 验证文件类型
|
||||
const isImage = file.type.startsWith("image/");
|
||||
if (!isImage) {
|
||||
console.error("只能上传图片文件");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证文件大小 (5MB)
|
||||
const isLt5M = file.size / 1024 / 1024 < 5;
|
||||
if (!isLt5M) {
|
||||
console.error("图片大小不能超过5MB");
|
||||
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
|
||||
);
|
||||
|
||||
// 调用 AvatarAndAnalyzeFeatures 更新对应角色的图片
|
||||
await AvatarAndAnalyzeFeatures(
|
||||
uploadedUrl,
|
||||
field.field_name
|
||||
);
|
||||
|
||||
onSuccess?.(uploadedUrl);
|
||||
} catch (error) {
|
||||
console.error("字段图片上传失败:", error);
|
||||
onError?.(error as Error);
|
||||
}
|
||||
}}
|
||||
}
|
||||
placement="top"
|
||||
classNames={{
|
||||
root: "max-w-none",
|
||||
}}
|
||||
trigger="hover"
|
||||
styles={{ root: { zIndex: 1000 } }}
|
||||
>
|
||||
{/* 图片 */}
|
||||
<div
|
||||
data-alt={`field-thumbnail-${index}`}
|
||||
className="w-24 h-24 rounded-xl overflow-hidden border border-white/20 bg-white/[0.05] flex items-center justify-center cursor-pointer hover:scale-105 transition-all duration-200"
|
||||
>
|
||||
<Image
|
||||
src={
|
||||
selectedTemplate?.storyRole?.find(
|
||||
(role) => role.role_name === field.field_name
|
||||
)?.photo_url || "/assets/empty_video.png"
|
||||
}
|
||||
alt={field.field_name}
|
||||
className="w-full h-full object-cover"
|
||||
preview={{
|
||||
mask: null,
|
||||
maskClassName: "hidden",
|
||||
}}
|
||||
fallback="/assets/empty_video.png"
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
{/* 角色名称 - 图片下方 */}
|
||||
<div className="text-center mt-2">
|
||||
<span className="text-white text-sm font-medium">
|
||||
{field.field_name}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* 上传按钮 - 右上角 */}
|
||||
<Upload
|
||||
name="fieldImage"
|
||||
showUploadList={false}
|
||||
beforeUpload={(file) => {
|
||||
const isImage = file.type.startsWith("image/");
|
||||
if (!isImage) {
|
||||
console.error("只能上传图片文件");
|
||||
return false;
|
||||
}
|
||||
const isLt5M = file.size / 1024 / 1024 < 5;
|
||||
if (!isLt5M) {
|
||||
console.error("图片大小不能超过5MB");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}}
|
||||
customRequest={async ({ file, onSuccess, onError }) => {
|
||||
try {
|
||||
const fileObj = file as File;
|
||||
const uploadedUrl = await uploadFile(
|
||||
fileObj,
|
||||
(progress) => {
|
||||
console.log(`上传进度: ${progress}%`);
|
||||
}
|
||||
);
|
||||
await AvatarAndAnalyzeFeatures(
|
||||
uploadedUrl,
|
||||
field.field_name
|
||||
);
|
||||
onSuccess?.(uploadedUrl);
|
||||
} catch (error) {
|
||||
console.error("字段图片上传失败:", error);
|
||||
onError?.(error as Error);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Tooltip title="upload your image" placement="top">
|
||||
<button
|
||||
data-alt={`field-upload-button-${index}`}
|
||||
className="absolute -top-1 -right-1 w-6 h-6 bg-blue-500 hover:bg-blue-600 text-white rounded-full flex items-center justify-center transition-all duration-200 opacity-0 group-hover:opacity-100 hover:scale-110 shadow-lg"
|
||||
title="change field image"
|
||||
>
|
||||
<UploadOutlined className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</Upload>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 输入框 */}
|
||||
<div className="flex-1 flex items-center relative">
|
||||
<input
|
||||
type="text"
|
||||
value={field.field_value || ""}
|
||||
onChange={(e) =>
|
||||
updateFillableContentField(
|
||||
field.field_name,
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
onBlur={(e) =>
|
||||
handleFieldBlur(field.field_name, e.target.value)
|
||||
}
|
||||
placeholder={`${field.field_description}`}
|
||||
className="w-full px-3 py-2 pr-10 bg-white/10 border border-white/20 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500/30 transition-all duration-200"
|
||||
/>
|
||||
|
||||
{/* 问号提示 - 直接放在输入框内部右侧 */}
|
||||
{field.field_description && (
|
||||
<Tooltip
|
||||
title={field.field_description}
|
||||
placement="top"
|
||||
classNames={{
|
||||
root: "max-w-xs",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
data-alt={`field-help-${index}`}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 w-5 h-5 rounded-full bg-white/10 border border-white/20 flex items-center justify-center cursor-help hover:bg-white/20 transition-colors duration-200"
|
||||
>
|
||||
<span className="text-white text-xs font-bold">
|
||||
?
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Upload>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@ -5,19 +5,25 @@ export function ActionButton({
|
||||
isCreating,
|
||||
handleCreateVideo,
|
||||
icon,
|
||||
width = "w-12",
|
||||
height = "h-12",
|
||||
className = "",
|
||||
}: {
|
||||
isCreating: boolean;
|
||||
handleCreateVideo: () => void;
|
||||
icon: React.ReactNode;
|
||||
width?: string;
|
||||
height?: string;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="relative group">
|
||||
<div className={`relative group ${className}`}>
|
||||
<div
|
||||
data-alt="action-button"
|
||||
className="relative w-12 h-12 opacity-90 cursor-pointer overflow-hidden rounded-xl bg-black z-10"
|
||||
className={`relative ${width} ${height} opacity-90 cursor-pointer overflow-hidden rounded-xl bg-black z-10`}
|
||||
onClick={isCreating ? undefined : handleCreateVideo}
|
||||
>
|
||||
<div className="absolute z-10 -translate-x-12 group-hover:translate-x-12 transition-all duration-700 h-full w-12 bg-gradient-to-r from-gray-500 to-white/10 opacity-30 -skew-x-12"></div>
|
||||
<div className={`absolute z-10 -translate-x-12 group-hover:translate-x-12 transition-all duration-700 h-full ${width} bg-gradient-to-r from-gray-500 to-white/10 opacity-30 -skew-x-12`}></div>
|
||||
<div className="absolute flex items-center justify-center text-white z-[1] opacity-90 rounded-xl inset-0.5 bg-black">
|
||||
<button
|
||||
name="text"
|
||||
@ -26,7 +32,7 @@ export function ActionButton({
|
||||
{isCreating ? <Loader2 className="w-5 h-5 animate-spin" /> : icon}
|
||||
</button>
|
||||
</div>
|
||||
<div className="absolute duration-1000 group-hover:animate-spin w-full h-12 bg-gradient-to-r from-[rgb(106,244,249)] to-[rgb(199,59,255)] blur-[20px]"></div>
|
||||
<div className={`absolute duration-1000 group-hover:animate-spin w-full ${height} bg-gradient-to-r from-[rgb(106,244,249)] to-[rgb(199,59,255)] blur-[20px]`}></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user