forked from 77media/video-flow
优化chatbox
This commit is contained in:
parent
f40436492b
commit
13e482ade3
@ -18,225 +18,16 @@ import {
|
|||||||
} from "./types";
|
} from "./types";
|
||||||
import { post } from "@/api/request";
|
import { post } from "@/api/request";
|
||||||
|
|
||||||
// Mock 数据
|
// 空消息 默认展示
|
||||||
const MOCK_MESSAGES: RealApiMessage[] = [
|
const EMPTY_MESSAGES: RealApiMessage[] = [
|
||||||
// 用户发送剧本
|
|
||||||
{
|
{
|
||||||
id: 1,
|
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',
|
role: 'assistant',
|
||||||
content: JSON.stringify([{
|
content: JSON.stringify([{
|
||||||
type: 'text',
|
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,
|
function_name: undefined,
|
||||||
custom_data: undefined,
|
custom_data: undefined,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
@ -494,13 +285,13 @@ export async function fetchMessages(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 转换消息并按时间排序
|
// 转换消息并按时间排序
|
||||||
// if (response.data.messages.length === 0) {
|
if (response.data.messages.length === 0) {
|
||||||
// return {
|
return {
|
||||||
// messages: MOCK_MESSAGES.map(transformMessage),
|
messages: EMPTY_MESSAGES.map(transformMessage),
|
||||||
// hasMore: false,
|
hasMore: false,
|
||||||
// totalCount: 0
|
totalCount: 0
|
||||||
// };
|
};
|
||||||
// }
|
}
|
||||||
return {
|
return {
|
||||||
messages: response.data.messages
|
messages: response.data.messages
|
||||||
.map(transformMessage)
|
.map(transformMessage)
|
||||||
|
|||||||
@ -3,28 +3,46 @@
|
|||||||
import '../pages/style/top-bar.css';
|
import '../pages/style/top-bar.css';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { GradientText } from '@/components/ui/gradient-text';
|
import { GradientText } from '@/components/ui/gradient-text';
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from '@/components/ui/dropdown-menu';
|
|
||||||
import { useTheme } from 'next-themes';
|
import { useTheme } from 'next-themes';
|
||||||
import {
|
import {
|
||||||
Sun,
|
Sun,
|
||||||
Moon,
|
Moon,
|
||||||
User,
|
User,
|
||||||
Settings,
|
Sparkles,
|
||||||
LogOut,
|
LogOut,
|
||||||
Bell,
|
Bell,
|
||||||
PanelsLeftBottom
|
PanelsLeftBottom,
|
||||||
|
Library
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
|
import React, { useRef, useEffect } from 'react';
|
||||||
|
|
||||||
export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onToggleSidebar: () => void }) {
|
export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onToggleSidebar: () => void }) {
|
||||||
const { theme, setTheme } = useTheme();
|
const { theme, setTheme } = useTheme();
|
||||||
const router = useRouter();
|
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 handleAnimationEnd = (event: React.AnimationEvent<HTMLDivElement>) => {
|
||||||
const element = event.currentTarget;
|
const element = event.currentTarget;
|
||||||
@ -99,25 +117,91 @@ export function TopBar({ collapsed, onToggleSidebar }: { collapsed: boolean; onT
|
|||||||
</Button> */}
|
</Button> */}
|
||||||
|
|
||||||
{/* User Menu */}
|
{/* User Menu */}
|
||||||
<DropdownMenu>
|
<div className="relative">
|
||||||
<DropdownMenuTrigger asChild>
|
<Button
|
||||||
<Button variant="ghost" size="sm">
|
ref={buttonRef}
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
|
data-alt="user-menu-trigger"
|
||||||
|
>
|
||||||
<User className="h-4 w-4" />
|
<User className="h-4 w-4" />
|
||||||
<span className="ml-2">User</span>
|
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="end">
|
<AnimatePresence>
|
||||||
<DropdownMenuItem>
|
{isOpen && (
|
||||||
<Settings className="mr-2 h-4 w-4" />
|
<motion.div
|
||||||
Settings
|
ref={menuRef}
|
||||||
</DropdownMenuItem>
|
initial={{ opacity: 0, scale: 0.95, y: -20 }}
|
||||||
<DropdownMenuSeparator />
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||||
<DropdownMenuItem>
|
exit={{ opacity: 0, scale: 0.95, y: -20 }}
|
||||||
<LogOut className="mr-2 h-4 w-4" />
|
transition={{ duration: 0.2 }}
|
||||||
Log out
|
className="absolute right-0 mt-2 w-56 bg-[#1E1E1E] rounded-lg shadow-lg overflow-hidden z-50"
|
||||||
</DropdownMenuItem>
|
data-alt="user-menu-dropdown"
|
||||||
</DropdownMenuContent>
|
>
|
||||||
</DropdownMenu>
|
{/* 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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user