forked from 77media/video-flow
样式再调整
This commit is contained in:
parent
a4a41da618
commit
8fa0adfdc4
16
app/ScreenAdapter.tsx
Normal file
16
app/ScreenAdapter.tsx
Normal 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; // 这个组件不渲染任何内容
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
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"
|
||||
/>
|
||||
<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>
|
||||
<Image
|
||||
src={selectedTemplate.image_url[0]}
|
||||
alt={selectedTemplate.name}
|
||||
className="w-4 h-5 !object-contain transition-all duration-500 group-hover:scale-105 group-hover:rotate-1"
|
||||
/>
|
||||
</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(
|
||||
|
||||
@ -12,10 +12,10 @@ 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}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,17 +1,16 @@
|
||||
import { ScriptSlice, ScriptSliceType } from "@/app/service/domain/valueObject";
|
||||
|
||||
export function parseScriptEntity(text: string):ScriptSlice {
|
||||
const scriptSlice = new ScriptSlice(
|
||||
// 生成唯一ID,单次使用即可
|
||||
`${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`,
|
||||
ScriptSliceType.text,
|
||||
text,
|
||||
{}
|
||||
);
|
||||
return scriptSlice;
|
||||
export function parseScriptEntity(text: string): ScriptSlice {
|
||||
const scriptSlice = new ScriptSlice(
|
||||
// 生成唯一ID,单次使用即可
|
||||
`${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`,
|
||||
ScriptSliceType.text,
|
||||
text,
|
||||
{}
|
||||
);
|
||||
return scriptSlice;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description 节流函数,限制函数在指定时间间隔内只执行一次
|
||||
* @param {Function} func - 需要被节流的函数
|
||||
@ -22,16 +21,57 @@ 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>) => {
|
||||
const now = Date.now();
|
||||
if (now - lastCall >= delay) {
|
||||
lastCall = now;
|
||||
func(...args);
|
||||
}
|
||||
let lastCall = 0;
|
||||
return (...args: Parameters<T>) => {
|
||||
const now = Date.now();
|
||||
if (now - lastCall >= delay) {
|
||||
lastCall = now;
|
||||
func(...args);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
/**
|
||||
* 创建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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user