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
-
Dedicated Hardworking Integrity Keep One's Word
-
+ // 处理聊天按钮点击事件
+ const handleChatClick = (e) => {
+ e.preventDefault();
+ // 在当前窗口打开链接,而不是新窗口
+ window.location.href = 'https://www.zhaolaw.com/contact';
+ };
+
+ return (
+
+
+

+
+
+
+
北京中兆律师事务所
+
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;
+ }
+ }
+}