样式再调整

This commit is contained in:
海龙 2025-08-26 19:47:38 +08:00
parent a4a41da618
commit 8fa0adfdc4
5 changed files with 122 additions and 46 deletions

16
app/ScreenAdapter.tsx Normal file
View File

@ -0,0 +1,16 @@
'use client'
import { useEffect } from 'react';
import { createScreenAdapter } from '@/utils/tools';
export function ScreenAdapter() {
useEffect(() => {
// 页面加载时应用
window.addEventListener('load', createScreenAdapter)
// 窗口大小改变时重新应用
window.addEventListener('resize', createScreenAdapter)
}, []);
return null; // 这个组件不渲染任何内容
}

View File

@ -2,6 +2,9 @@ import './globals.css';
import type { Metadata } from 'next';
import { Providers } from '@/components/providers';
import { ConfigProvider, theme } from 'antd';
import { useEffect } from 'react';
import { createScreenAdapter } from '@/utils/tools';
import { ScreenAdapter } from './ScreenAdapter';
export const metadata: Metadata = {
title: 'AI Movie Flow - Create Amazing Videos with AI',
@ -29,6 +32,7 @@ export default function RootLayout({
}}
>
<Providers>
<ScreenAdapter />
{children}
</Providers>
</ConfigProvider>

View File

@ -84,7 +84,9 @@ const RenderTemplateStoryMode = ({
// 本地加载状态,用于 UI 反馈
const [localLoading, setLocalLoading] = useState(0);
// 控制输入框显示状态
const [inputVisible, setInputVisible] = useState<{ [key: string]: boolean }>({});
const [inputVisible, setInputVisible] = useState<{ [key: string]: boolean }>(
{}
);
const router = useRouter();
// 组件挂载时获取模板列表
useEffect(() => {
@ -98,15 +100,18 @@ const RenderTemplateStoryMode = ({
const handleClickOutside = (event: MouseEvent) => {
const target = event.target as Element;
// 检查是否点击了输入框相关的元素
if (!target.closest('.ant-tooltip') && !target.closest('[data-alt*="field-ai-button"]')) {
if (
!target.closest(".ant-tooltip") &&
!target.closest('[data-alt*="field-ai-button"]')
) {
// 关闭所有打开的输入框
setInputVisible({});
}
};
document.addEventListener('mousedown', handleClickOutside);
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
@ -164,7 +169,7 @@ const RenderTemplateStoryMode = ({
const templateListRender = () => {
return (
<div className="w-1/3 p-4 border-r border-white/[0.1]">
<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 template-list-scroll">
{templateStoryList.map((template, index) => (
<div
key={template.id}
@ -195,17 +200,11 @@ const RenderTemplateStoryMode = ({
<div className="flex gap-3 py-4 border-b border-white/[0.1] h-[300px]">
{/* 左侧图片 */}
<div className="w-1/4">
<div
data-alt="template-preview-image"
className="relative w-full aspect-square rounded-xl overflow-hidden group cursor-pointer"
>
<img
<Image
src={selectedTemplate.image_url[0]}
alt={selectedTemplate.name}
className="w-full h-full object-contain transition-all duration-500 group-hover:scale-105 group-hover:rotate-1"
className="w-4 h-5 !object-contain transition-all duration-500 group-hover:scale-105 group-hover:rotate-1"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/40 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
</div>
</div>
{/* 右侧信息 - 增加文本渲染空间 */}
@ -217,7 +216,7 @@ const RenderTemplateStoryMode = ({
{selectedTemplate.name}
</h2>
<div
className="flex-1 overflow-y-auto max-h-96 pr-3"
className="flex-1 overflow-y-auto max-h-96 "
style={{
scrollbarWidth: "thin",
scrollbarColor: "rgba(156,163,175,0.2) rgba(0,0,0,0)",
@ -235,7 +234,7 @@ const RenderTemplateStoryMode = ({
{/* 变量字段填写区域 */}
{selectedTemplate?.fillable_content &&
selectedTemplate.fillable_content.length > 0 && (
<div className="p-4 border-t border-white/10">
<div className="pt-2 border-t border-white/10">
<h3
data-alt="variables-section-title"
className="text-lg font-semibold text-white mb-4"
@ -275,7 +274,10 @@ const RenderTemplateStoryMode = ({
field.field_name,
field.value || ""
);
setInputVisible(prev => ({ ...prev, [field.field_name]: false }));
setInputVisible((prev) => ({
...prev,
[field.field_name]: false,
}));
}}
icon={<Sparkles className="w-4 h-4" />}
width="w-8"
@ -289,14 +291,19 @@ const RenderTemplateStoryMode = ({
root: "max-w-none",
}}
open={inputVisible[field.field_name]}
onOpenChange={(visible) => setInputVisible(prev => ({ ...prev, [field.field_name]: visible }))}
onOpenChange={(visible) =>
setInputVisible((prev) => ({
...prev,
[field.field_name]: visible,
}))
}
trigger="contextMenu"
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"
className="w-24 h-24 rounded-xl overflow-hidden border border-white/10 bg-white/0 flex items-center justify-center cursor-pointer hover:scale-105 transition-all duration-200"
>
<Image
src={
@ -328,7 +335,12 @@ const RenderTemplateStoryMode = ({
<Tooltip title="AI generate image" placement="top">
<button
data-alt={`field-ai-button-${index}`}
onClick={() => setInputVisible(prev => ({ ...prev, [field.field_name]: !prev[field.field_name] }))}
onClick={() =>
setInputVisible((prev) => ({
...prev,
[field.field_name]: !prev[field.field_name],
}))
}
className="w-6 h-6 bg-purple-500 hover:bg-purple-600 text-white rounded-full flex items-center justify-center transition-all duration-200 hover:scale-110 shadow-lg"
>
<Sparkles className="w-3.5 h-3.5" />
@ -352,7 +364,11 @@ const RenderTemplateStoryMode = ({
}
return true;
}}
customRequest={async ({ file, onSuccess, onError }) => {
customRequest={async ({
file,
onSuccess,
onError,
}) => {
try {
const fileObj = file as File;
const uploadedUrl = await uploadFile(

View File

@ -12,7 +12,7 @@ export function DashboardLayout({ children }: DashboardLayoutProps) {
const [sidebarCollapsed, setSidebarCollapsed] = useState(true);
return (
<div className="min-h-screen bg-background">
<div className="min-h-screen bg-background" id="app">
<Sidebar collapsed={sidebarCollapsed} onToggle={setSidebarCollapsed} />
<TopBar collapsed={sidebarCollapsed} onToggleSidebar={() => setSidebarCollapsed(!sidebarCollapsed)} />
{children}

View File

@ -11,7 +11,6 @@ export function parseScriptEntity(text: string):ScriptSlice {
return scriptSlice;
}
/**
* @description
* @param {Function} func -
@ -22,9 +21,12 @@ export function parseScriptEntity(text: string):ScriptSlice {
* const throttledFn = throttle(() => { console.log('触发'); }, 1000);
* window.addEventListener('resize', throttledFn);
*/
export function throttle<T extends (...args: any[]) => any>(func: T, delay: number=100): (...args: Parameters<T>) => void {
if (typeof delay !== 'number' || delay < 0) {
throw new Error('throttle: 第二个参数必须是非负数');
export function throttle<T extends (...args: any[]) => any>(
func: T,
delay: number = 100
): (...args: Parameters<T>) => void {
if (typeof delay !== "number" || delay < 0) {
throw new Error("throttle: 第二个参数必须是非负数");
}
let lastCall = 0;
return (...args: Parameters<T>) => {
@ -35,3 +37,41 @@ export function throttle<T extends (...args: any[]) => any>(func: T, delay: numb
}
};
}
/**
* 1920x1080屏幕适配的CSS样式
*
*/
export function createScreenAdapter(): void {
// 获取当前窗口尺寸
const currentWidth = window.innerWidth;
const currentHeight = window.innerHeight;
// 计算缩放比例 (1920x1080)
const wScale = currentWidth / 1920;
const hScale = currentHeight / 1080;
// 检查app节点是否存在
const app = document.getElementById("app");
if (!app) {
console.error("未找到app节点");
return;
}
// 创建样式元素
const style = document.createElement("style");
// 设置CSS样式
style.textContent = `
body {
#app {
transform-origin: top left;
transform: scale(${wScale}, ${hScale});
width: 1920px;
height: 1080px;
}
}
`;
// 将样式添加到head
document.head.appendChild(style);
}