forked from 77media/video-flow
优化chatbox
This commit is contained in:
parent
f40436492b
commit
13e482ade3
@ -18,225 +18,16 @@ import {
|
||||
} from "./types";
|
||||
import { post } from "@/api/request";
|
||||
|
||||
// Mock 数据
|
||||
const MOCK_MESSAGES: RealApiMessage[] = [
|
||||
// 用户发送剧本
|
||||
// 空消息 默认展示
|
||||
const EMPTY_MESSAGES: RealApiMessage[] = [
|
||||
{
|
||||
id: 1,
|
||||
role: 'user',
|
||||
content: JSON.stringify([{
|
||||
type: 'text',
|
||||
content: '我想拍一个关于一个小女孩和她的机器人朋友的故事,故事发生在未来世界。'
|
||||
}]),
|
||||
created_at: '2024-03-20T10:00:00Z',
|
||||
function_name: undefined,
|
||||
custom_data: undefined,
|
||||
status: 'success',
|
||||
intent_type: 'chat'
|
||||
},
|
||||
// 项目初始化
|
||||
{
|
||||
id: 2,
|
||||
role: 'system',
|
||||
content: '我会帮您创建一个温馨感人的科幻短片,讲述人工智能与人类情感的故事。',
|
||||
created_at: '2024-03-20T10:00:10Z',
|
||||
function_name: 'create_project',
|
||||
custom_data: {
|
||||
project_data: {
|
||||
script: '小女孩和机器人朋友的故事'
|
||||
}
|
||||
},
|
||||
status: 'success',
|
||||
intent_type: 'procedure'
|
||||
},
|
||||
// 剧本总结
|
||||
{
|
||||
id: 3,
|
||||
role: 'system',
|
||||
content: '故事概要:在2045年的未来城市,10岁的小女孩艾米丽收到了一个特别的生日礼物——一个具有高度情感智能的机器人伙伴"小星"。随着时间推移,他们建立了深厚的友谊。当小星因能源耗尽即将永久关闭时,艾米丽想尽办法寻找解决方案,最终通过她的坚持和创意,成功为小星找到了新的能源,让这段跨越人机界限的友谊得以延续。',
|
||||
created_at: '2024-03-20T10:01:00Z',
|
||||
function_name: 'generate_script_summary',
|
||||
custom_data: {
|
||||
summary: '一个关于友谊和希望的温暖故事'
|
||||
},
|
||||
status: 'success',
|
||||
intent_type: 'procedure'
|
||||
},
|
||||
// 角色生成 - 艾米丽
|
||||
{
|
||||
id: 4,
|
||||
role: 'system',
|
||||
content: '主角艾米丽的形象已生成',
|
||||
created_at: '2024-03-20T10:02:00Z',
|
||||
function_name: 'generate_character',
|
||||
custom_data: {
|
||||
character_name: '艾米丽',
|
||||
image_path: 'https://picsum.photos/seed/emily/300/400',
|
||||
completed_count: 1,
|
||||
total_count: 2
|
||||
},
|
||||
status: 'success',
|
||||
intent_type: 'procedure'
|
||||
},
|
||||
// 角色生成 - 小星
|
||||
{
|
||||
id: 5,
|
||||
role: 'system',
|
||||
content: '机器人小星的形象已生成',
|
||||
created_at: '2024-03-20T10:03:00Z',
|
||||
function_name: 'generate_character',
|
||||
custom_data: {
|
||||
character_name: '小星',
|
||||
image_path: 'https://picsum.photos/seed/robot/300/400',
|
||||
completed_count: 2,
|
||||
total_count: 2
|
||||
},
|
||||
status: 'success',
|
||||
intent_type: 'procedure'
|
||||
},
|
||||
// 场景生成 - 未来城市
|
||||
{
|
||||
id: 6,
|
||||
role: 'system',
|
||||
content: '未来城市场景设计完成',
|
||||
created_at: '2024-03-20T10:04:00Z',
|
||||
function_name: 'generate_sketch',
|
||||
custom_data: {
|
||||
sketch_name: '未来城市街景',
|
||||
image_path: 'https://picsum.photos/seed/city/600/400',
|
||||
completed_count: 1,
|
||||
total_count: 3
|
||||
},
|
||||
status: 'success',
|
||||
intent_type: 'procedure'
|
||||
},
|
||||
// 场景生成 - 艾米丽的房间
|
||||
{
|
||||
id: 7,
|
||||
role: 'system',
|
||||
content: '艾米丽的未来风格卧室设计完成',
|
||||
created_at: '2024-03-20T10:05:00Z',
|
||||
function_name: 'generate_sketch',
|
||||
custom_data: {
|
||||
sketch_name: '艾米丽的卧室',
|
||||
image_path: 'https://picsum.photos/seed/room/600/400',
|
||||
completed_count: 2,
|
||||
total_count: 3
|
||||
},
|
||||
status: 'success',
|
||||
intent_type: 'procedure'
|
||||
},
|
||||
// 场景生成 - 实验室
|
||||
{
|
||||
id: 8,
|
||||
role: 'system',
|
||||
content: '高科技实验室场景设计完成',
|
||||
created_at: '2024-03-20T10:06:00Z',
|
||||
function_name: 'generate_sketch',
|
||||
custom_data: {
|
||||
sketch_name: '未来实验室',
|
||||
image_path: 'https://picsum.photos/seed/lab/600/400',
|
||||
completed_count: 3,
|
||||
total_count: 3
|
||||
},
|
||||
status: 'success',
|
||||
intent_type: 'procedure'
|
||||
},
|
||||
// 分镜生成 - 相遇
|
||||
{
|
||||
id: 9,
|
||||
role: 'system',
|
||||
content: '第一个分镜:艾米丽收到礼物时的场景',
|
||||
created_at: '2024-03-20T10:07:00Z',
|
||||
function_name: 'generate_shot_sketch',
|
||||
custom_data: {
|
||||
shot_type: '中景',
|
||||
atmosphere: '温馨、期待',
|
||||
key_action: '艾米丽惊喜地打开礼物盒,小星缓缓启动',
|
||||
url: 'https://picsum.photos/seed/shot1/600/400',
|
||||
completed_count: 1,
|
||||
total_count: 3
|
||||
},
|
||||
status: 'success',
|
||||
intent_type: 'procedure'
|
||||
},
|
||||
// 分镜生成 - 危机
|
||||
{
|
||||
id: 10,
|
||||
role: 'system',
|
||||
content: '第二个分镜:小星能源耗尽的场景',
|
||||
created_at: '2024-03-20T10:08:00Z',
|
||||
function_name: 'generate_shot_sketch',
|
||||
custom_data: {
|
||||
shot_type: '特写',
|
||||
atmosphere: '紧张、担忧',
|
||||
key_action: '小星的能源指示灯闪烁微弱,艾米丽神情焦急',
|
||||
url: 'https://picsum.photos/seed/shot2/600/400',
|
||||
completed_count: 2,
|
||||
total_count: 3
|
||||
},
|
||||
status: 'success',
|
||||
intent_type: 'procedure'
|
||||
},
|
||||
// 分镜生成 - 解决
|
||||
{
|
||||
id: 11,
|
||||
role: 'system',
|
||||
content: '第三个分镜:找到新能源解决方案的场景',
|
||||
created_at: '2024-03-20T10:09:00Z',
|
||||
function_name: 'generate_shot_sketch',
|
||||
custom_data: {
|
||||
shot_type: '全景',
|
||||
atmosphere: '欢欣、胜利',
|
||||
key_action: '实验室中艾米丽成功激活新能源,小星重新焕发活力',
|
||||
url: 'https://picsum.photos/seed/shot3/600/400',
|
||||
completed_count: 3,
|
||||
total_count: 3
|
||||
},
|
||||
status: 'success',
|
||||
intent_type: 'procedure'
|
||||
},
|
||||
// 分镜视频生成
|
||||
{
|
||||
id: 11.1,
|
||||
role: 'system',
|
||||
content: '分镜视频生成完成',
|
||||
created_at: '2024-03-20T10:10:00Z',
|
||||
function_name: 'generate_video',
|
||||
custom_data: {
|
||||
prompt_json: {
|
||||
core_atmosphere: '欢欣、胜利',
|
||||
},
|
||||
urls: ['https://cdn.qikongjian.com/faces/1755798635_facefusion_output_1755798635.mp4'],
|
||||
completed_count: 1,
|
||||
total_count: 1
|
||||
},
|
||||
status: 'success',
|
||||
intent_type: 'procedure'
|
||||
},
|
||||
// 用户反馈
|
||||
{
|
||||
id: 12,
|
||||
role: 'user',
|
||||
content: JSON.stringify([{
|
||||
type: 'text',
|
||||
content: '这个故事设计太棒了!特别喜欢艾米丽和小星的互动场景。'
|
||||
}]),
|
||||
created_at: '2024-03-20T10:10:00Z',
|
||||
function_name: undefined,
|
||||
custom_data: undefined,
|
||||
status: 'success',
|
||||
intent_type: 'function_call'
|
||||
},
|
||||
// 助手回复
|
||||
{
|
||||
id: 13,
|
||||
role: 'assistant',
|
||||
content: JSON.stringify([{
|
||||
type: 'text',
|
||||
content: '谢谢您的肯定!我们可以继续优化任何场景或角色设计,您觉得有什么地方需要调整吗?'
|
||||
content: '🌟欢迎来到 MovieFlow 🎬✨\n快把您的创意告诉我吧~💡\n我是您的专属AI小伙伴🤖,可以帮您:\n🎭 生成专属演员形象\n📽️ 搭建场景 & 分镜\n🎞️ 完成整部视频创作\n\n一起开启奇妙的创作之旅吧!❤️'
|
||||
}]),
|
||||
created_at: '2024-03-20T10:10:10Z',
|
||||
created_at: new Date().toISOString(),
|
||||
function_name: undefined,
|
||||
custom_data: undefined,
|
||||
status: 'success',
|
||||
@ -494,13 +285,13 @@ export async function fetchMessages(
|
||||
}
|
||||
|
||||
// 转换消息并按时间排序
|
||||
// if (response.data.messages.length === 0) {
|
||||
// return {
|
||||
// messages: MOCK_MESSAGES.map(transformMessage),
|
||||
// hasMore: false,
|
||||
// totalCount: 0
|
||||
// };
|
||||
// }
|
||||
if (response.data.messages.length === 0) {
|
||||
return {
|
||||
messages: EMPTY_MESSAGES.map(transformMessage),
|
||||
hasMore: false,
|
||||
totalCount: 0
|
||||
};
|
||||
}
|
||||
return {
|
||||
messages: response.data.messages
|
||||
.map(transformMessage)
|
||||
|
||||
@ -3,28 +3,46 @@
|
||||
import '../pages/style/top-bar.css';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { GradientText } from '@/components/ui/gradient-text';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { useTheme } from 'next-themes';
|
||||
import {
|
||||
Sun,
|
||||
Moon,
|
||||
User,
|
||||
Settings,
|
||||
Sparkles,
|
||||
LogOut,
|
||||
Bell,
|
||||
PanelsLeftBottom
|
||||
PanelsLeftBottom,
|
||||
Library
|
||||
} from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
|
||||
export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onToggleSidebar: () => void }) {
|
||||
const { theme, setTheme } = useTheme();
|
||||
const router = useRouter();
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
const currentUser = localStorage.getItem('currentUser');
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
menuRef.current &&
|
||||
!menuRef.current.contains(event.target as Node) &&
|
||||
buttonRef.current &&
|
||||
!buttonRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleAnimationEnd = (event: React.AnimationEvent<HTMLDivElement>) => {
|
||||
const element = event.currentTarget;
|
||||
@ -99,25 +117,91 @@ export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onT
|
||||
</Button> */}
|
||||
|
||||
{/* User Menu */}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="sm">
|
||||
<User className="h-4 w-4" />
|
||||
<span className="ml-2">User</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem>
|
||||
<Settings className="mr-2 h-4 w-4" />
|
||||
Settings
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>
|
||||
<LogOut className="mr-2 h-4 w-4" />
|
||||
Log out
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<div className="relative">
|
||||
<Button
|
||||
ref={buttonRef}
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
data-alt="user-menu-trigger"
|
||||
>
|
||||
<User className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<motion.div
|
||||
ref={menuRef}
|
||||
initial={{ opacity: 0, scale: 0.95, y: -20 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.95, y: -20 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="absolute right-0 mt-2 w-56 bg-[#1E1E1E] rounded-lg shadow-lg overflow-hidden z-50"
|
||||
data-alt="user-menu-dropdown"
|
||||
>
|
||||
{/* User Info */}
|
||||
<div className="p-4">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="h-10 w-10 rounded-full bg-[#1E4D3E] flex items-center justify-center text-white font-semibold">
|
||||
A
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium">admin-live</p>
|
||||
<p className="text-xs text-gray-500">admin-live.com</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* AI Points */}
|
||||
<div className="px-4 py-3 flex items-center justify-between">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Sparkles className="h-4 w-4" />
|
||||
<span className="text-white underline text-sm">100 点数</span>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="text-white border-white hover:bg-white/10 rounded-full px-8"
|
||||
>
|
||||
升级
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Menu Items */}
|
||||
<div className="p-2">
|
||||
<motion.button
|
||||
whileHover={{ backgroundColor: 'rgba(255,255,255,0.1)' }}
|
||||
className="w-full flex items-center space-x-2 px-3 py-2 rounded-md text-sm text-white"
|
||||
onClick={() => router.push('/my-library')}
|
||||
data-alt="my-library-button"
|
||||
>
|
||||
<Library className="h-4 w-4" />
|
||||
<span>我的库</span>
|
||||
</motion.button>
|
||||
|
||||
<motion.button
|
||||
whileHover={{ backgroundColor: 'rgba(255,255,255,0.1)' }}
|
||||
className="w-full flex items-center space-x-2 px-3 py-2 rounded-md text-sm text-white"
|
||||
onClick={() => {
|
||||
// 处理退出登录
|
||||
setIsOpen(false);
|
||||
}}
|
||||
data-alt="logout-button"
|
||||
>
|
||||
<LogOut className="h-4 w-4" />
|
||||
<span>退出账号</span>
|
||||
</motion.button>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="mt-4 px-3 py-2 text-xs text-gray-400 text-center">
|
||||
<div>隐私权和条款 · 许可</div>
|
||||
<div>250819215404 | 2025/8/20 06:00:50</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user