更新模板故事的样式

This commit is contained in:
海龙 2025-08-26 02:16:41 +08:00
parent b1bdc9423d
commit 6fba02c591
2 changed files with 117 additions and 129 deletions

View File

@ -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>
))}

View File

@ -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>
);