- {!hideLogo &&

}
-
- 数联网科创智能体
-
+
setShowVideo(true)}>
+ {!hideLogo &&

}
+
+ 数联网科创智能体
+
+
@@ -252,7 +257,7 @@ export const PlaygroundHistory = () => {
setShow(true)}
+ onClose={() => setShowOptionSidebar(true)}
setMessages={setMessages}
setHistory={setHistory}
setHistoryId={setHistoryId}
@@ -262,6 +267,7 @@ export const PlaygroundHistory = () => {
historyId={historyId}
setSystemPrompt={setSystemPrompt}
temporaryChat={temporaryChat}
+ stopStreamingRequest={stopStreamingRequest}
history={history}
/>
diff --git a/src/components/Option/Sidebar.tsx b/src/components/Option/Sidebar.tsx
index fedbff6..cd3f283 100644
--- a/src/components/Option/Sidebar.tsx
+++ b/src/components/Option/Sidebar.tsx
@@ -33,6 +33,7 @@ type Props = {
setSelectedModel: (model: string) => void
setSelectedSystemPrompt: (prompt: string) => void
setSystemPrompt: (prompt: string) => void
+ stopStreamingRequest: () => void
clearChat: () => void
temporaryChat: boolean
historyId: string
@@ -46,6 +47,7 @@ export const Sidebar = ({
setHistoryId,
setSelectedModel,
setSelectedSystemPrompt,
+ stopStreamingRequest,
clearChat,
historyId,
setSystemPrompt,
@@ -140,6 +142,40 @@ export const Sidebar = ({
}
})
+ const handleHistoryClick = async (chat: any) => {
+ const db = new PageAssitDatabase()
+ const history = await db.getChatHistory(chat.id)
+ setHistoryId(chat.id)
+ setHistory(formatToChatHistory(history))
+ setMessages(formatToMessage(history))
+ stopStreamingRequest()
+ const isLastUsedChatModel =
+ await lastUsedChatModelEnabled()
+ if (isLastUsedChatModel) {
+ const currentChatModel = await getLastUsedChatModel(
+ chat.id
+ )
+ if (currentChatModel) {
+ setSelectedModel(currentChatModel)
+ }
+ }
+ const lastUsedPrompt =
+ await getLastUsedChatSystemPrompt(chat.id)
+ if (lastUsedPrompt) {
+ if (lastUsedPrompt.prompt_id) {
+ const prompt = await getPromptById(
+ lastUsedPrompt.prompt_id
+ )
+ if (prompt) {
+ setSelectedSystemPrompt(lastUsedPrompt.prompt_id)
+ }
+ }
+ setSystemPrompt(lastUsedPrompt.prompt_content)
+ }
+ navigate("/")
+ onClose()
+ }
+
return (
@@ -181,38 +217,7 @@ export const Sidebar = ({
)}
diff --git a/src/components/Option/VideoPlayer/index.tsx b/src/components/Option/VideoPlayer/index.tsx
new file mode 100644
index 0000000..1c38714
--- /dev/null
+++ b/src/components/Option/VideoPlayer/index.tsx
@@ -0,0 +1,410 @@
+import React, { useEffect, useRef, useState } from "react"
+import iodVideo from "@/assets/video.mp4"
+import { useOptionLayoutContext } from "@/components/Layouts/Layout.tsx"
+import {
+ ExpandOutlined,
+ PauseCircleOutlined,
+ PlayCircleOutlined
+} from "@ant-design/icons"
+import logo from "@/assets/logo.png"
+
+const VideoPlayer = () => {
+ const { setShowVideo } = useOptionLayoutContext()
+ const videoRef = useRef
(null)
+ const containerRef = useRef(null)
+ const controlsTimerRef = useRef(null)
+ const mouseMoveTimerRef = useRef(null)
+ const isPlayingRef = useRef(false)
+
+ const [isPlaying, setIsPlaying] = useState(false)
+ const [currentTime, setCurrentTime] = useState(0)
+ const [duration, setDuration] = useState(0)
+ const [volume, setVolume] = useState(1)
+ const [isMuted, setIsMuted] = useState(false)
+ const [showControls, setShowControls] = useState(false)
+ const [isBuffering, setIsBuffering] = useState(false)
+
+ // 更新 isPlayingRef 当状态变化时
+ useEffect(() => {
+ isPlayingRef.current = isPlaying
+ }, [isPlaying])
+
+ // 格式化时间
+ const formatTime = (seconds: number) => {
+ if (isNaN(seconds)) return "00:00"
+ const minutes = Math.floor(seconds / 60)
+ const secs = Math.floor(seconds % 60)
+ return `${minutes.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`
+ }
+
+ // 处理播放/暂停
+ const togglePlayPause = () => {
+ const video = videoRef.current
+ if (!video) return
+
+ if (isPlaying) {
+ video.pause()
+ setIsPlaying(false)
+ } else {
+ video.play().catch((error) => {
+ console.error("播放失败:", error)
+ })
+ setIsPlaying(true)
+ }
+ }
+
+ // 处理音量变化
+ const handleVolumeChange = (e: React.ChangeEvent) => {
+ const newVolume = parseFloat(e.target.value)
+ setVolume(newVolume)
+ if (videoRef.current) {
+ videoRef.current.volume = newVolume
+ setIsMuted(newVolume === 0)
+ }
+ }
+
+ // 切换静音
+ const toggleMute = () => {
+ const newMuted = !isMuted
+ setIsMuted(newMuted)
+ if (videoRef.current) {
+ videoRef.current.muted = newMuted
+ }
+ }
+
+ // 进度条点击处理
+ const handleProgressClick = (e: React.MouseEvent) => {
+ const progressBar = e.currentTarget
+ const clickPosition = e.nativeEvent.offsetX
+ const progressBarWidth = progressBar.offsetWidth
+ if (duration > 0 && videoRef.current) {
+ const newTime = (clickPosition / progressBarWidth) * duration
+ videoRef.current.currentTime = newTime
+ }
+ }
+
+ // 全屏切换
+ const toggleFullscreen = () => {
+ const videoContainer = containerRef.current
+ if (!videoContainer) return
+
+ if (!document.fullscreenElement) {
+ if (videoContainer.requestFullscreen) {
+ videoContainer.requestFullscreen().catch((err) => {
+ console.error("全屏切换失败:", err)
+ })
+ }
+ } else {
+ if (document.exitFullscreen) {
+ document.exitFullscreen()
+ }
+ }
+ }
+
+ const handleEnded = () => {
+ setIsPlaying(false)
+ setShowVideo(false)
+ }
+
+ // 控制栏显示/隐藏 - 与原始HTML版本行为完全一致,添加防抖功能
+ const handleMouseMove = (e: React.MouseEvent) => {
+ // 清除之前的防抖定时器
+ if (mouseMoveTimerRef.current) {
+ clearTimeout(mouseMoveTimerRef.current)
+ }
+
+ // 设置新的防抖定时器
+ mouseMoveTimerRef.current = setTimeout(() => {
+ const container = containerRef.current
+ if (!container) return
+
+ const containerHeight = container.offsetHeight
+ const mouseY = e.clientY - container.getBoundingClientRect().top
+
+ // 如果鼠标在底部150px区域内
+ if (mouseY > containerHeight - 150) {
+ // 清除之前的隐藏定时器
+ if (controlsTimerRef.current) {
+ clearTimeout(controlsTimerRef.current)
+ }
+ // 立即显示控制器
+ setShowControls(true)
+ } else {
+ // 鼠标离开底部区域,设置定时器隐藏控制器
+ if (controlsTimerRef.current) {
+ clearTimeout(controlsTimerRef.current)
+ }
+ controlsTimerRef.current = setTimeout(() => {
+ setShowControls(false)
+ }, 300) // 300ms后隐藏
+ }
+ }, 10) // 10ms 防抖延迟
+ }
+
+ // 鼠标离开整个视频容器时立即隐藏控制器
+ const handleMouseLeave = () => {
+ // 清除防抖定时器
+ if (mouseMoveTimerRef.current) {
+ clearTimeout(mouseMoveTimerRef.current)
+ }
+
+ // 清除控制栏隐藏定时器
+ if (controlsTimerRef.current) {
+ clearTimeout(controlsTimerRef.current)
+ }
+
+ // 立即隐藏控制栏
+ setShowControls(false)
+ }
+
+ // 视频事件处理
+ useEffect(() => {
+ const video = videoRef.current
+ if (!video) return
+
+ const handleLoadedMetadata = () => {
+ setDuration(video.duration)
+ }
+
+ const handleTimeUpdate = () => {
+ setCurrentTime(video.currentTime)
+ }
+
+ const handleWaiting = () => {
+ setIsBuffering(true)
+ }
+
+ const handlePlaying = () => {
+ setIsBuffering(false)
+ setIsPlaying(true)
+ }
+
+ const handlePause = () => {
+ if (!isBuffering) {
+ setIsPlaying(false)
+ }
+ }
+
+
+ video.addEventListener("loadedmetadata", handleLoadedMetadata)
+ video.addEventListener("timeupdate", handleTimeUpdate)
+ video.addEventListener("waiting", handleWaiting)
+ video.addEventListener("playing", handlePlaying)
+ video.addEventListener("pause", handlePause)
+ video.addEventListener("ended", handleEnded)
+
+ // 组件挂载时尝试播放视频
+ const playVideo = async () => {
+ try {
+ await video.play()
+ setIsPlaying(true)
+ } catch (error) {
+ console.error("自动播放失败:", error)
+ }
+ }
+
+ const timer = setTimeout(playVideo, 100)
+
+ return () => {
+ video.removeEventListener("loadedmetadata", handleLoadedMetadata)
+ video.removeEventListener("timeupdate", handleTimeUpdate)
+ video.removeEventListener("waiting", handleWaiting)
+ video.removeEventListener("playing", handlePlaying)
+ video.removeEventListener("pause", handlePause)
+ video.removeEventListener("ended", handleEnded)
+
+ // 清除所有定时器
+ if (controlsTimerRef.current) {
+ clearTimeout(controlsTimerRef.current)
+ }
+ if (mouseMoveTimerRef.current) {
+ clearTimeout(mouseMoveTimerRef.current)
+ }
+ clearTimeout(timer)
+ }
+ }, [])
+
+ // 处理键盘事件
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (!videoRef.current) {
+ return
+ }
+
+ switch (e.code) {
+ case "Space":
+ e.preventDefault()
+ const video = videoRef.current
+ if (!video) return
+
+ if (isPlayingRef.current) {
+ video.pause()
+ setIsPlaying(false)
+ } else {
+ video.play().catch((error) => {
+ console.error("播放失败:", error)
+ })
+ setIsPlaying(true)
+ }
+ break
+ case "ArrowLeft":
+ e.preventDefault()
+ if (videoRef.current) {
+ videoRef.current.currentTime = Math.max(
+ 0,
+ videoRef.current.currentTime - 10
+ )
+ }
+ break
+ case "ArrowRight":
+ e.preventDefault()
+ if (videoRef.current && duration) {
+ videoRef.current.currentTime = Math.min(
+ duration,
+ videoRef.current.currentTime + 10
+ )
+ }
+ break
+ case "ArrowUp":
+ e.preventDefault()
+ setVolume((prev) => {
+ const newVolume = Math.min(1, prev + 0.1)
+ if (videoRef.current) {
+ videoRef.current.volume = newVolume
+ setIsMuted(newVolume === 0)
+ }
+ return newVolume
+ })
+ break
+ case "ArrowDown":
+ e.preventDefault()
+ setVolume((prev) => {
+ const newVolume = Math.max(0, prev - 0.1)
+ if (videoRef.current) {
+ videoRef.current.volume = newVolume
+ setIsMuted(newVolume === 0)
+ }
+ return newVolume
+ })
+ break
+ case "KeyM":
+ e.preventDefault()
+ toggleMute()
+ break
+ case "KeyF":
+ e.preventDefault()
+ toggleFullscreen()
+ break
+ }
+ }
+
+ // 键盘事件监听
+ useEffect(() => {
+ document.addEventListener("keydown", handleKeyDown)
+
+ if (containerRef.current) {
+ containerRef.current.tabIndex = 0
+ }
+
+ return () => {
+ document.removeEventListener("keydown", handleKeyDown)
+ }
+ }, [duration])
+
+ // 计算进度条百分比
+ const progressPercent = duration ? (currentTime / duration) * 100 : 0
+
+ return (
+
+
+
+ {/* 暂停时的遮罩层 */}
+ {!isPlaying && !isBuffering && (
+
+ )}
+
+ {/* 缓冲提示 */}
+ {isBuffering && (
+
+ 缓冲中...
+
+ )}
+
+
+ {/* 控制栏 - 使用与原始HTML相同的类名和行为 */}
+
+
+ {

}
+
+ 数联网科创智能体
+
+
+
+
+
+
+
+
+ {formatTime(currentTime)} /
+ {formatTime(duration)}
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default VideoPlayer
diff --git a/src/libs/export-import.ts b/src/libs/export-import.ts
index 0ec77ce..20f06e6 100644
--- a/src/libs/export-import.ts
+++ b/src/libs/export-import.ts
@@ -60,7 +60,7 @@ export const importPageAssistData = async (file: File) => {
}
if(data?.iod) {
- IodDb.getInstance().insertIodConnection(data.iod)
+ IodDb.getInstance().insertIodConnection(data.iod.connection)
}
message.success("Data imported successfully")
diff --git a/src/routes/option-index.tsx b/src/routes/option-index.tsx
index 47e04ec..42275d7 100644
--- a/src/routes/option-index.tsx
+++ b/src/routes/option-index.tsx
@@ -1,10 +1,11 @@
import OptionLayout from "~/components/Layouts/Layout"
+import IodVideo from "@/components/Option/VideoPlayer/index.tsx"
import { Playground } from "~/components/Option/Playground/Playground"
const OptionIndex = () => {
return (
-
+
)
}