From 676e216fe29f16a2b7a1e0801a52b7544b6fdc2e Mon Sep 17 00:00:00 2001 From: susie-laptop Date: Fri, 11 Apr 2025 12:27:30 -0400 Subject: [PATCH] [dev]ws setting & --- src/App.jsx | 16 +-- src/components/SearchBar.jsx | 38 ++++-- src/layouts/HeaderWithNav.jsx | 4 +- src/services/websocket.js | 23 +++- src/styles/style.scss | 233 +++++++++++++++++++++------------- vite.config.js | 6 +- 6 files changed, 206 insertions(+), 114 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 6263565..5c3f9c3 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -25,14 +25,14 @@ function App() { // 如果用户已认证但WebSocket未连接,则初始化连接 if (user && !isConnected) { - // initWebSocket() - // .then(() => { - // dispatch(setWebSocketConnected(true)); - // console.log('WebSocket connection initialized'); - // }) - // .catch((error) => { - // console.error('Failed to initialize WebSocket connection:', error); - // }); + initWebSocket() + .then(() => { + dispatch(setWebSocketConnected(true)); + console.log('WebSocket connection initialized'); + }) + .catch((error) => { + console.error('Failed to initialize WebSocket connection:', error); + }); } // 组件卸载或用户登出时关闭WebSocket连接 diff --git a/src/components/SearchBar.jsx b/src/components/SearchBar.jsx index c6ae245..8c7bdf3 100644 --- a/src/components/SearchBar.jsx +++ b/src/components/SearchBar.jsx @@ -15,6 +15,7 @@ import SvgIcon from './SvgIcon'; * @param {boolean} props.isSearchLoading - 搜索是否正在加载 * @param {Function} props.onResultClick - 点击搜索结果的回调 * @param {Function} props.onRequestAccess - 申请权限的回调 + * @param {string} props.cornerStyle - 设置圆角风格,可选值: 'rounded'(圆角) 或 'square'(方角) */ const SearchBar = ({ searchKeyword, @@ -22,17 +23,23 @@ const SearchBar = ({ onSearchChange, onSearch, onClearSearch, - placeholder = '搜索...', + placeholder = '搜索知识库...', className = 'w-50', searchResults = [], isSearchLoading = false, onResultClick, onRequestAccess, + cornerStyle = 'rounded', // 默认为圆角 }) => { const [showDropdown, setShowDropdown] = useState(false); const searchRef = useRef(null); const inputRef = useRef(null); + // 计算边框圆角样式类 + const getBorderRadiusClass = () => { + return cornerStyle === 'rounded' ? 'rounded-pill' : 'rounded-0'; + }; + // 处理点击外部关闭下拉框 useEffect(() => { const handleClickOutside = (event) => { @@ -69,11 +76,11 @@ const SearchBar = ({ return (
-
+
{ onClearSearch(); setShowDropdown(false); @@ -91,7 +100,12 @@ const SearchBar = ({ )} -
@@ -99,7 +113,11 @@ const SearchBar = ({ {/* 搜索结果下拉框 - 仅在用户搜索且有结果时显示 */} {showDropdown && (isSearchLoading || searchResults?.length > 0) && ( -
+
{isSearchLoading ? (
@@ -116,7 +134,9 @@ const SearchBar = ({ {searchResults.map((item) => (
{!item.permissions?.can_read && ( -
*/} +
{ } const wsUrl = `${WS_BASE_URL}/ws/notifications?token=${encryptedToken}`; + console.log('WebSocket URL:', wsUrl); socket = new WebSocket(wsUrl); // 连接建立时的处理 socket.onopen = () => { console.log('WebSocket connection established'); + reconnectAttempts = 0; // 连接成功后重置重连计数器 // 订阅通知频道 subscribeToNotifications(); @@ -81,12 +85,19 @@ export const initWebSocket = () => { // 如果不是正常关闭,尝试重连 if (event.code !== 1000) { - reconnectTimer = setTimeout(() => { - console.log('Attempting to reconnect WebSocket...'); - initWebSocket().catch((err) => { - console.error('Failed to reconnect WebSocket:', err); - }); - }, RECONNECT_DELAY); + if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) { + reconnectAttempts++; + console.log(`WebSocket reconnection attempt ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS}`); + + reconnectTimer = setTimeout(() => { + console.log('Attempting to reconnect WebSocket...'); + initWebSocket().catch((err) => { + console.error('Failed to reconnect WebSocket:', err); + }); + }, RECONNECT_DELAY); + } else { + console.log('Maximum reconnection attempts reached. Giving up.'); + } } }; } catch (error) { diff --git a/src/styles/style.scss b/src/styles/style.scss index 72c6af9..b815aa5 100644 --- a/src/styles/style.scss +++ b/src/styles/style.scss @@ -18,17 +18,33 @@ color: inherit; /* Heading styles */ - h1, h2, h3, h4, h5, h6 { + h1, + h2, + h3, + h4, + h5, + h6 { margin-top: 1rem; margin-bottom: 0.75rem; font-weight: 600; } - h1 { font-size: 1.5rem; } - h2 { font-size: 1.35rem; } - h3 { font-size: 1.2rem; } - h4 { font-size: 1.1rem; } - h5, h6 { font-size: 1rem; } + h1 { + font-size: 1.5rem; + } + h2 { + font-size: 1.35rem; + } + h3 { + font-size: 1.2rem; + } + h4 { + font-size: 1.1rem; + } + h5, + h6 { + font-size: 1rem; + } /* Paragraph spacing */ p { @@ -36,19 +52,21 @@ } /* Lists */ - ul, ol { + ul, + ol { padding-left: 1.5rem; margin-bottom: 0.75rem; } /* Code blocks with syntax highlighting */ - pre, pre.prism-code { + pre, + pre.prism-code { margin: 0.5rem 0 !important; padding: 0.75rem !important; border-radius: 0.375rem !important; font-size: 0.85rem !important; line-height: 1.5 !important; - + /* Improve readability on dark background */ code span { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace !important; @@ -91,12 +109,13 @@ width: 100%; margin-bottom: 0.75rem; border-collapse: collapse; - - th, td { + + th, + td { padding: 0.5rem; border: 1px solid #dee2e6; } - + th { background-color: #f8f9fa; } @@ -120,20 +139,20 @@ /* Apply different text colors based on message background */ .bg-dark .markdown-content { color: white; - + code { background-color: rgba(255, 255, 255, 0.1); } - + pre { background-color: rgba(255, 255, 255, 0.1); } - + blockquote { border-left-color: rgba(255, 255, 255, 0.3); color: rgba(255, 255, 255, 0.8); } - + a { color: #8bb9fe; } @@ -142,8 +161,8 @@ .knowledge-card { min-width: 20rem; cursor: pointer; - - .hoverdown:hover .hoverdown-menu{ + + .hoverdown:hover .hoverdown-menu { display: block; color: red; } @@ -161,7 +180,7 @@ gap: 8px; border-radius: 4px; color: $dark; - + &:hover { background-color: $gray-100; } @@ -178,41 +197,41 @@ /* 自定义黑色系开关样式 */ .dark-switch .form-check-input { - border: 1px solid #dee2e6; - background-color: #fff; /* 关闭状态背景色 */ + border: 1px solid #dee2e6; + background-color: #fff; /* 关闭状态背景色 */ } /* 关闭状态滑块 */ .dark-switch .form-check-input:not(:checked) { - background-image: url("data:image/svg+xml,"); + background-image: url("data:image/svg+xml,"); } /* 打开状态 */ .dark-switch .form-check-input:checked { - background-color: #000; /* 打开状态背景色 */ - border-color: #000; + background-color: #000; /* 打开状态背景色 */ + border-color: #000; } /* 打开状态滑块 */ .dark-switch .form-check-input:checked { - background-image: url("data:image/svg+xml,"); + background-image: url("data:image/svg+xml,"); } /* 悬停效果 */ .dark-switch .form-check-input:hover { - filter: brightness(0.9); + filter: brightness(0.9); } /* 禁用状态 */ .dark-switch .form-check-input:disabled { - opacity: 0.5; - background-color: #e9ecef; + opacity: 0.5; + background-color: #e9ecef; } // 通知中心样式 .notification-item { transition: background-color 0.2s ease; - + &:hover { background-color: $gray-100; } @@ -230,48 +249,48 @@ } .dark-pagination .page-link { - color: #000; /* 默认文字颜色 */ + color: #000; /* 默认文字颜色 */ background-color: #fff; /* 默认背景 */ border: 1px solid #dee2e6; /* 边框颜色 */ transition: all 0.3s ease; /* 平滑过渡效果 */ - } - - /* 激活状态 */ - .dark-pagination .page-item.active .page-link { +} + +/* 激活状态 */ +.dark-pagination .page-item.active .page-link { background-color: #000 !important; border-color: #000; color: #fff !important; - } - - /* 悬停状态 */ - .dark-pagination .page-link:hover { +} + +/* 悬停状态 */ +.dark-pagination .page-link:hover { background-color: #f8f9fa; /* 浅灰背景 */ border-color: #adb5bd; - } - - /* 禁用状态 */ - .dark-pagination .page-item.disabled .page-link { +} + +/* 禁用状态 */ +.dark-pagination .page-item.disabled .page-link { color: #6c757d !important; background-color: #e9ecef !important; border-color: #dee2e6; pointer-events: none; opacity: 0.7; - } - - /* 自定义下拉框 */ - .dark-select { +} + +/* 自定义下拉框 */ +.dark-select { border: 1px solid #000 !important; color: #000 !important; - } - - .dark-select:focus { +} + +.dark-select:focus { box-shadow: 0 0 0 0.25rem rgba(0, 0, 0, 0.25); /* 黑色聚焦阴影 */ - } - - /* 下拉箭头颜色 */ - .dark-select { +} + +/* 下拉箭头颜色 */ +.dark-select { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='%23000' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); - } +} /* Code Block Styles */ .code-block-container { @@ -310,7 +329,7 @@ gap: 0.25rem; border-radius: 0.25rem; transition: all 0.2s; - + &:hover { color: rgba(255, 255, 255, 0.9); background-color: rgba(255, 255, 255, 0.1); @@ -333,11 +352,11 @@ /* Markdown fallback styling */ .markdown-fallback { font-size: 0.95rem; - + .text-danger { font-weight: 500; } - + pre { white-space: pre-wrap; word-break: break-word; @@ -350,39 +369,77 @@ /* Streaming message indicator */ .streaming-indicator { - display: inline-flex; - align-items: center; - margin-left: 5px; - - .dot { - width: 6px; - height: 6px; - background-color: #6c757d; - border-radius: 50%; - margin: 0 2px; - animation: pulse 1.5s infinite ease-in-out; - - &.dot1 { - animation-delay: 0s; + display: inline-flex; + align-items: center; + margin-left: 5px; + + .dot { + width: 6px; + height: 6px; + background-color: #6c757d; + border-radius: 50%; + margin: 0 2px; + animation: pulse 1.5s infinite ease-in-out; + + &.dot1 { + animation-delay: 0s; + } + + &.dot2 { + animation-delay: 0.3s; + } + + &.dot3 { + animation-delay: 0.6s; + } } - - &.dot2 { - animation-delay: 0.3s; + + @keyframes pulse { + 0%, + 100% { + transform: scale(0.8); + opacity: 0.5; + } + 50% { + transform: scale(1.2); + opacity: 1; + } } - - &.dot3 { - animation-delay: 0.6s; +} + +// SearchBar component styles +.search-input-group { + border: 1px solid #ced4da; + overflow: hidden; + + &.rounded-pill { + border-radius: 50rem; } - } - - @keyframes pulse { - 0%, 100% { - transform: scale(0.8); - opacity: 0.5; + + .search-input { + border: none; + box-shadow: none; + + &:focus { + box-shadow: none; + } } - 50% { - transform: scale(1.2); - opacity: 1; + + .btn { + border: none; + background-color: transparent; + color: #6c757d; + + &:hover, + &:active, + &:focus { + background-color: transparent; + color: #495057; + } } - } -} \ No newline at end of file + + .search-button { + padding-left: 0.75rem; + padding-right: 0.75rem; + } +} diff --git a/vite.config.js b/vite.config.js index dbe53d6..a0d0787 100644 --- a/vite.config.js +++ b/vite.config.js @@ -6,18 +6,20 @@ export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd()); return { + base: '/', plugins: [react()], build: { - outDir: '../dist', + outDir: 'dist', }, server: { port: env.VITE_PORT, proxy: { '/api': { - target: env.VITE_API_URL || 'http://81.69.223.133:58008', + target: env.VITE_API_URL || 'http://81.69.223.133:8008', changeOrigin: true, }, }, + historyApiFallback: true, }, }; });