diff --git a/index.html b/index.html index b1e7383..9b9a377 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,175 @@ - + 北京中兆律师事务所 智能知识库 + + + + +
+ + +
+
+ +
+ + + diff --git a/public/OpenDoor.gif b/public/OpenDoor.gif new file mode 100644 index 0000000..61d557e Binary files /dev/null and b/public/OpenDoor.gif differ diff --git a/public/OpenDoor.mp4 b/public/OpenDoor.mp4 new file mode 100644 index 0000000..5edfdbd Binary files /dev/null and b/public/OpenDoor.mp4 differ diff --git a/public/vite.svg b/public/vite.svg deleted file mode 100644 index 40d10df..0000000 --- a/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 6263565..58515d9 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,8 +1,8 @@ +import React, { useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; import { useDispatch, useSelector } from 'react-redux'; import AppRouter from './router/router'; import { checkAuthThunk } from './store/auth/auth.thunk'; -import { useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; import { login } from './store/auth/auth.slice'; import { initWebSocket, closeWebSocket } from './services/websocket'; import { setWebSocketConnected } from './store/notificationCenter/notificationCenter.slice'; @@ -10,33 +10,31 @@ import { setWebSocketConnected } from './store/notificationCenter/notificationCe function App() { const navigate = useNavigate(); const dispatch = useDispatch(); - const { user } = useSelector((state) => state.auth); const { isConnected } = useSelector((state) => state.notificationCenter); // 检查用户认证状态 useEffect(() => { handleCheckAuth(); - }, [dispatch]); + }, []); // 管理WebSocket连接 useEffect(() => { - console.log(user, isConnected); - - // 如果用户已认证但WebSocket未连接,则初始化连接 - if (user && !isConnected) { - // initWebSocket() - // .then(() => { - // dispatch(setWebSocketConnected(true)); - // console.log('WebSocket connection initialized'); - // }) - // .catch((error) => { - // console.error('Failed to initialize WebSocket connection:', error); - // }); + if (user) { + if (!isConnected) { + // 初始化WebSocket连接 + initWebSocket(dispatch); + } + } else { + if (isConnected) { + // 关闭WebSocket连接 + closeWebSocket(); + dispatch(setWebSocketConnected(false)); + } } - // 组件卸载或用户登出时关闭WebSocket连接 return () => { + // 组件卸载时关闭WebSocket连接 if (isConnected) { closeWebSocket(); dispatch(setWebSocketConnected(false)); @@ -55,7 +53,7 @@ function App() { } }; - return ; + return ; } export default App; diff --git a/src/assets/react.svg b/src/assets/react.svg deleted file mode 100644 index 22c3eba..0000000 --- a/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/components/Banner.jsx b/src/components/Banner.jsx index 204607a..c0b151a 100644 --- a/src/components/Banner.jsx +++ b/src/components/Banner.jsx @@ -1,23 +1,29 @@ -import React from "react"; -import bannerImg from "../assets/wmremove-transformed.jpeg"; +import React from 'react'; +import bannerImg from '../assets/wmremove-transformed.jpeg'; +import { Link } from 'react-router-dom'; export const Banner = () => { - return ( -
-
- Beijing Zhong Zhao Law Firm -
-
-
-

Beijing Zhong Zhao Law Firm

-

Dedicated Hardworking Integrity Keep One's Word

- + // 处理聊天按钮点击事件 + const handleChatClick = (e) => { + e.preventDefault(); + // 在当前窗口打开链接,而不是新窗口 + window.location.href = 'https://www.zhaolaw.com/contact'; + }; + + return ( +
+
+ Beijing Zhong Zhao Law Firm +
+
+
+

北京中兆律师事务所

+

Dedicated Hardworking Integrity Keep One's Word

+ + 开始聊天 + +
+
-
-
- ); + ); }; diff --git a/src/main.jsx b/src/main.jsx index cc1e2cf..4ba874f 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -11,8 +11,15 @@ import store, { persistor } from './store/store.js'; import { PersistGate } from 'redux-persist/integration/react'; import Loading from './components/Loading.jsx'; -createRoot(document.getElementById('root')).render( - // +// 获取根元素 +const rootElement = document.getElementById('root'); + +// 定义渲染React应用的函数 +const renderApp = () => { + console.log('React应用开始渲染'); + const root = createRoot(rootElement); + root.render( + // } persistor={persistor}> @@ -20,5 +27,33 @@ createRoot(document.getElementById('root')).render( - // -); + // + ); + console.log('React应用渲染完成'); +}; + +// 协调视频播放和应用加载 +const coordinateAppStart = () => { + // 如果视频已播放完毕,直接显示应用 + if (window.doorAnimationCompleted) { + console.log('视频已播放完毕,显示应用'); + if (window.showReactApp) { + window.showReactApp(); + } else { + // 备用方案:直接修改样式 + if (rootElement) rootElement.style.display = 'block'; + const splash = document.getElementById('root-loading'); + if (splash) splash.style.display = 'none'; + } + } else { + console.log('React应用已准备好,等待视频播放完成'); + // 通知视频播放脚本,React已准备好 + window.reactAppReady = true; + } +}; + +// 预先加载和渲染React应用,但不显示 +renderApp(); + +// 通知协调脚本React应用已准备好 +coordinateAppStart(); diff --git a/src/pages/Chat/Chat.jsx b/src/pages/Chat/Chat.jsx index bd22472..35d1aee 100644 --- a/src/pages/Chat/Chat.jsx +++ b/src/pages/Chat/Chat.jsx @@ -21,6 +21,9 @@ export default function Chat() { const operationStatus = useSelector((state) => state.chat.createSession?.status); const operationError = useSelector((state) => state.chat.createSession?.error); + // 判断是否在知识库选择页面 + const isNewChatView = !chatId; + // 获取聊天记录列表 useEffect(() => { dispatch(fetchChats({ page: 1, page_size: 20 })); @@ -163,6 +166,7 @@ export default function Chat() { onDeleteChat={handleDeleteChat} isLoading={status === 'loading'} hasError={status === 'failed'} + isNewChatView={isNewChatView} />
@@ -171,7 +175,7 @@ export default function Chat() { className='chat-main col-md-9 col-lg-10 p-0' style={{ height: 'calc(100vh - 84px)', overflowY: 'auto' }} > - {!chatId ? : } + {isNewChatView ? : } diff --git a/src/pages/Chat/ChatSidebar.jsx b/src/pages/Chat/ChatSidebar.jsx index 62b68ad..84dd3d1 100644 --- a/src/pages/Chat/ChatSidebar.jsx +++ b/src/pages/Chat/ChatSidebar.jsx @@ -2,7 +2,22 @@ import React, { useState } from 'react'; import { Link, useNavigate, useParams } from 'react-router-dom'; import SvgIcon from '../../components/SvgIcon'; -export default function ChatSidebar({ chatHistory = [], onDeleteChat, isLoading = false, hasError = false }) { +/** + * 聊天侧边栏组件 + * @param {Object} props + * @param {Array} props.chatHistory - 聊天历史记录 + * @param {Function} props.onDeleteChat - 删除聊天的回调 + * @param {boolean} props.isLoading - 是否正在加载 + * @param {boolean} props.hasError - 是否有错误 + * @param {boolean} props.isNewChatView - 是否在选择知识库页面 + */ +export default function ChatSidebar({ + chatHistory = [], + onDeleteChat, + isLoading = false, + hasError = false, + isNewChatView = false +}) { const navigate = useNavigate(); const { chatId, knowledgeBaseId } = useParams(); const [activeDropdown, setActiveDropdown] = useState(null); @@ -61,10 +76,12 @@ export default function ChatSidebar({ chatHistory = [], onDeleteChat, isLoading
diff --git a/src/pages/Chat/NewChat.jsx b/src/pages/Chat/NewChat.jsx index 248a6e4..5b84ff9 100644 --- a/src/pages/Chat/NewChat.jsx +++ b/src/pages/Chat/NewChat.jsx @@ -191,7 +191,7 @@ export default function NewChat() {

选择知识库开始聊天

{selectedDatasetIds.length > 0 && ( -
已选择 {selectedDatasetIds.length} 个知识库
+
已选择 {selectedDatasetIds.length} 个知识库
)}
@@ -221,11 +221,16 @@ export default function NewChat() { return (
handleToggleKnowledgeBase(dataset)} - style={{ opacity: isNavigating && !isSelected ? 0.6 : 1 }} + style={{ + opacity: isNavigating && !isSelected ? 0.6 : 1, + transition: 'all 0.2s ease-in-out', + }} >
diff --git a/src/styles/style.scss b/src/styles/style.scss index a71772a..43bb867 100644 --- a/src/styles/style.scss +++ b/src/styles/style.scss @@ -433,7 +433,7 @@ .banner-container { position: relative; width: 100%; - height: 500px; + height: 360px; overflow: hidden; } @@ -536,3 +536,57 @@ padding-right: 0.75rem; } } + +// Selected card highlight effect for knowledge base selection +.selected-card { + transform: translateY(-3px); + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15) !important; + + &:hover { + transform: translateY(-3px); + } + + .card-title { + color: $primary; + font-weight: 600; + } +} + +// Hover effect for selectable cards +.cursor-pointer.card:not(.selected-card) { + transition: all 0.2s ease-in-out; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08) !important; + border-color: rgba($primary, 0.3) !important; + } +} + +// Splash screen animation styles +.splash-screen { + animation: fadeIn 0.5s ease-in-out; + + &.fade-out { + animation: fadeOut 0.8s ease-in-out forwards; + } + + @keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + + @keyframes fadeOut { + from { + opacity: 1; + } + to { + opacity: 0; + visibility: hidden; + } + } +}