forked from 77media/video-flow
更新模板故事的样式
This commit is contained in:
parent
b1bdc9423d
commit
6fba02c591
@ -215,147 +215,129 @@ const RenderTemplateStoryMode = ({
|
|||||||
>
|
>
|
||||||
Template Configuration
|
Template Configuration
|
||||||
</h3>
|
</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) => (
|
{selectedTemplate.fillable_content.map((field, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
data-alt={`variable-field-${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">
|
||||||
{/* 图片缩略图 - 显示对应角色的图片 */}
|
<Tooltip
|
||||||
<div className="relative group flex items-center justify-center">
|
title={
|
||||||
<Tooltip title={field.field_name} placement="top">
|
<div className="relative">
|
||||||
<div
|
<input
|
||||||
data-alt={`field-thumbnail-${index}`}
|
type="text"
|
||||||
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"
|
value={field.field_value || ""}
|
||||||
>
|
onChange={(e) =>
|
||||||
<Image
|
updateFillableContentField(
|
||||||
src={
|
field.field_name,
|
||||||
selectedTemplate?.storyRole?.find(
|
e.target.value
|
||||||
(role) => role.role_name === field.field_name
|
)
|
||||||
)?.photo_url || "/assets/empty_video.png"
|
|
||||||
}
|
}
|
||||||
alt={field.field_name}
|
placeholder={`${field.field_description}`}
|
||||||
className="w-full h-full object-cover"
|
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"
|
||||||
preview={{
|
|
||||||
mask: null,
|
|
||||||
maskClassName: "hidden",
|
|
||||||
}}
|
|
||||||
fallback="/assets/empty_video.png"
|
|
||||||
/>
|
/>
|
||||||
|
<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>
|
</div>
|
||||||
</Tooltip>
|
}
|
||||||
|
placement="top"
|
||||||
{/* 上传按钮 - 右上角 */}
|
classNames={{
|
||||||
<Upload
|
root: "max-w-none",
|
||||||
name="fieldImage"
|
}}
|
||||||
showUploadList={false}
|
trigger="hover"
|
||||||
beforeUpload={(file) => {
|
styles={{ root: { zIndex: 1000 } }}
|
||||||
// 验证文件类型
|
>
|
||||||
const isImage = file.type.startsWith("image/");
|
{/* 图片 */}
|
||||||
if (!isImage) {
|
<div
|
||||||
console.error("只能上传图片文件");
|
data-alt={`field-thumbnail-${index}`}
|
||||||
return false;
|
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"
|
||||||
}
|
|
||||||
|
|
||||||
// 验证文件大小 (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);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
|
<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
|
<button
|
||||||
data-alt={`field-upload-button-${index}`}
|
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"
|
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" />
|
<UploadOutlined className="w-3.5 h-3.5" />
|
||||||
</button>
|
</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>
|
</Tooltip>
|
||||||
)}
|
</Upload>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -5,19 +5,25 @@ export function ActionButton({
|
|||||||
isCreating,
|
isCreating,
|
||||||
handleCreateVideo,
|
handleCreateVideo,
|
||||||
icon,
|
icon,
|
||||||
|
width = "w-12",
|
||||||
|
height = "h-12",
|
||||||
|
className = "",
|
||||||
}: {
|
}: {
|
||||||
isCreating: boolean;
|
isCreating: boolean;
|
||||||
handleCreateVideo: () => void;
|
handleCreateVideo: () => void;
|
||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
|
width?: string;
|
||||||
|
height?: string;
|
||||||
|
className?: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="relative group">
|
<div className={`relative group ${className}`}>
|
||||||
<div
|
<div
|
||||||
data-alt="action-button"
|
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}
|
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">
|
<div className="absolute flex items-center justify-center text-white z-[1] opacity-90 rounded-xl inset-0.5 bg-black">
|
||||||
<button
|
<button
|
||||||
name="text"
|
name="text"
|
||||||
@ -26,7 +32,7 @@ export function ActionButton({
|
|||||||
{isCreating ? <Loader2 className="w-5 h-5 animate-spin" /> : icon}
|
{isCreating ? <Loader2 className="w-5 h-5 animate-spin" /> : icon}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user