feat: Improve route handling and dynamic chat navigation in SiderBar

This commit is contained in:
1808837298@qq.com
2025-03-11 14:55:48 +08:00
parent 69db1f1465
commit 2af05c166c
3 changed files with 89 additions and 81 deletions

View File

@@ -1,5 +1,5 @@
import React, { lazy, Suspense, useContext, useEffect } from 'react'; import React, { lazy, Suspense, useContext, useEffect } from 'react';
import { Route, Routes } from 'react-router-dom'; import { Route, Routes, useLocation } from 'react-router-dom';
import Loading from './components/Loading'; import Loading from './components/Loading';
import User from './pages/User'; import User from './pages/User';
import { PrivateRoute } from './components/PrivateRoute'; import { PrivateRoute } from './components/PrivateRoute';
@@ -8,10 +8,8 @@ import LoginForm from './components/LoginForm';
import NotFound from './pages/NotFound'; import NotFound from './pages/NotFound';
import Setting from './pages/Setting'; import Setting from './pages/Setting';
import EditUser from './pages/User/EditUser'; import EditUser from './pages/User/EditUser';
import { getLogo, getSystemName } from './helpers';
import PasswordResetForm from './components/PasswordResetForm'; import PasswordResetForm from './components/PasswordResetForm';
import PasswordResetConfirm from './components/PasswordResetConfirm'; import PasswordResetConfirm from './components/PasswordResetConfirm';
import { UserContext } from './context/User';
import Channel from './pages/Channel'; import Channel from './pages/Channel';
import Token from './pages/Token'; import Token from './pages/Token';
import EditChannel from './pages/Channel/EditChannel'; import EditChannel from './pages/Channel/EditChannel';
@@ -26,10 +24,6 @@ import Pricing from './pages/Pricing/index.js';
import Task from "./pages/Task/index.js"; import Task from "./pages/Task/index.js";
import Playground from './pages/Playground/Playground.js'; import Playground from './pages/Playground/Playground.js';
import OAuth2Callback from "./components/OAuth2Callback.js"; import OAuth2Callback from "./components/OAuth2Callback.js";
import { useTranslation } from 'react-i18next';
import { StatusContext } from './context/Status';
import { setStatusData } from './helpers/data.js';
import { API, showError } from './helpers';
import PersonalSetting from './components/PersonalSetting.js'; import PersonalSetting from './components/PersonalSetting.js';
const Home = lazy(() => import('./pages/Home')); const Home = lazy(() => import('./pages/Home'));
@@ -37,13 +31,15 @@ const Detail = lazy(() => import('./pages/Detail'));
const About = lazy(() => import('./pages/About')); const About = lazy(() => import('./pages/About'));
function App() { function App() {
const location = useLocation();
return ( return (
<> <>
<Routes> <Routes>
<Route <Route
path='/' path='/'
element={ element={
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<Home /> <Home />
</Suspense> </Suspense>
} }
@@ -59,7 +55,7 @@ function App() {
<Route <Route
path='/channel/edit/:id' path='/channel/edit/:id'
element={ element={
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<EditChannel /> <EditChannel />
</Suspense> </Suspense>
} }
@@ -67,7 +63,7 @@ function App() {
<Route <Route
path='/channel/add' path='/channel/add'
element={ element={
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<EditChannel /> <EditChannel />
</Suspense> </Suspense>
} }
@@ -107,7 +103,7 @@ function App() {
<Route <Route
path='/user/edit/:id' path='/user/edit/:id'
element={ element={
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<EditUser /> <EditUser />
</Suspense> </Suspense>
} }
@@ -115,7 +111,7 @@ function App() {
<Route <Route
path='/user/edit' path='/user/edit'
element={ element={
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<EditUser /> <EditUser />
</Suspense> </Suspense>
} }
@@ -123,7 +119,7 @@ function App() {
<Route <Route
path='/user/reset' path='/user/reset'
element={ element={
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<PasswordResetConfirm /> <PasswordResetConfirm />
</Suspense> </Suspense>
} }
@@ -131,7 +127,7 @@ function App() {
<Route <Route
path='/login' path='/login'
element={ element={
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<LoginForm /> <LoginForm />
</Suspense> </Suspense>
} }
@@ -139,7 +135,7 @@ function App() {
<Route <Route
path='/register' path='/register'
element={ element={
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<RegisterForm /> <RegisterForm />
</Suspense> </Suspense>
} }
@@ -147,7 +143,7 @@ function App() {
<Route <Route
path='/reset' path='/reset'
element={ element={
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<PasswordResetForm /> <PasswordResetForm />
</Suspense> </Suspense>
} }
@@ -155,7 +151,7 @@ function App() {
<Route <Route
path='/oauth/github' path='/oauth/github'
element={ element={
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<OAuth2Callback type='github'></OAuth2Callback> <OAuth2Callback type='github'></OAuth2Callback>
</Suspense> </Suspense>
} }
@@ -163,7 +159,7 @@ function App() {
<Route <Route
path='/oauth/linuxdo' path='/oauth/linuxdo'
element={ element={
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<OAuth2Callback type='linuxdo'></OAuth2Callback> <OAuth2Callback type='linuxdo'></OAuth2Callback>
</Suspense> </Suspense>
} }
@@ -172,7 +168,7 @@ function App() {
path='/setting' path='/setting'
element={ element={
<PrivateRoute> <PrivateRoute>
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<Setting /> <Setting />
</Suspense> </Suspense>
</PrivateRoute> </PrivateRoute>
@@ -182,7 +178,7 @@ function App() {
path='/personal' path='/personal'
element={ element={
<PrivateRoute> <PrivateRoute>
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<PersonalSetting /> <PersonalSetting />
</Suspense> </Suspense>
</PrivateRoute> </PrivateRoute>
@@ -192,7 +188,7 @@ function App() {
path='/topup' path='/topup'
element={ element={
<PrivateRoute> <PrivateRoute>
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<TopUp /> <TopUp />
</Suspense> </Suspense>
</PrivateRoute> </PrivateRoute>
@@ -210,7 +206,7 @@ function App() {
path='/detail' path='/detail'
element={ element={
<PrivateRoute> <PrivateRoute>
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<Detail /> <Detail />
</Suspense> </Suspense>
</PrivateRoute> </PrivateRoute>
@@ -220,7 +216,7 @@ function App() {
path='/midjourney' path='/midjourney'
element={ element={
<PrivateRoute> <PrivateRoute>
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<Midjourney /> <Midjourney />
</Suspense> </Suspense>
</PrivateRoute> </PrivateRoute>
@@ -230,7 +226,7 @@ function App() {
path='/task' path='/task'
element={ element={
<PrivateRoute> <PrivateRoute>
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<Task /> <Task />
</Suspense> </Suspense>
</PrivateRoute> </PrivateRoute>
@@ -239,7 +235,7 @@ function App() {
<Route <Route
path='/pricing' path='/pricing'
element={ element={
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<Pricing /> <Pricing />
</Suspense> </Suspense>
} }
@@ -247,7 +243,7 @@ function App() {
<Route <Route
path='/about' path='/about'
element={ element={
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<About /> <About />
</Suspense> </Suspense>
} }
@@ -255,7 +251,7 @@ function App() {
<Route <Route
path='/chat/:id?' path='/chat/:id?'
element={ element={
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<Chat /> <Chat />
</Suspense> </Suspense>
} }
@@ -265,7 +261,7 @@ function App() {
path='/chat2link' path='/chat2link'
element={ element={
<PrivateRoute> <PrivateRoute>
<Suspense fallback={<Loading></Loading>}> <Suspense fallback={<Loading></Loading>} key={location.pathname}>
<Chat2Link /> <Chat2Link />
</Suspense> </Suspense>
</PrivateRoute> </PrivateRoute>

View File

@@ -62,6 +62,25 @@ const iconStyle = (itemKey, selectedKeys) => {
}; };
}; };
// Define routerMap as a constant outside the component
const routerMap = {
home: '/',
channel: '/channel',
token: '/token',
redemption: '/redemption',
topup: '/topup',
user: '/user',
log: '/log',
midjourney: '/midjourney',
setting: '/setting',
about: '/about',
detail: '/detail',
pricing: '/pricing',
task: '/task',
playground: '/playground',
personal: '/personal',
};
const SiderBar = () => { const SiderBar = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [styleState, styleDispatch] = useContext(StyleContext); const [styleState, styleDispatch] = useContext(StyleContext);
@@ -76,6 +95,7 @@ const SiderBar = () => {
const theme = useTheme(); const theme = useTheme();
const setTheme = useSetTheme(); const setTheme = useSetTheme();
const location = useLocation(); const location = useLocation();
const [routerMapState, setRouterMapState] = useState(routerMap);
// 预先计算所有可能的图标样式 // 预先计算所有可能的图标样式
const allItemKeys = useMemo(() => { const allItemKeys = useMemo(() => {
@@ -97,25 +117,6 @@ const SiderBar = () => {
return styles; return styles;
}, [allItemKeys, selectedKeys]); }, [allItemKeys, selectedKeys]);
const routerMap = {
home: '/',
channel: '/channel',
token: '/token',
redemption: '/redemption',
topup: '/topup',
user: '/user',
log: '/log',
midjourney: '/midjourney',
setting: '/setting',
about: '/about',
chat: '/chat',
detail: '/detail',
pricing: '/pricing',
task: '/task',
playground: '/playground',
personal: '/personal',
};
const workspaceItems = useMemo( const workspaceItems = useMemo(
() => [ () => [
{ {
@@ -237,21 +238,24 @@ const SiderBar = () => {
[chatItems, t], [chatItems, t],
); );
useEffect(() => { // Function to update router map with chat routes
const currentPath = location.pathname; const updateRouterMapWithChats = (chats) => {
const matchingKey = Object.keys(routerMap).find(key => routerMap[key] === currentPath); const newRouterMap = { ...routerMap };
if (matchingKey) { if (Array.isArray(chats) && chats.length > 0) {
setSelectedKeys([matchingKey]); for (let i = 0; i < chats.length; i++) {
} else if (currentPath.startsWith('/chat/')) { newRouterMap['chat' + i] = '/chat/' + i;
setSelectedKeys(['chat']); }
} }
}, [location.pathname]);
setRouterMapState(newRouterMap);
return newRouterMap;
};
// Update the useEffect for chat items
useEffect(() => { useEffect(() => {
let chats = localStorage.getItem('chats'); let chats = localStorage.getItem('chats');
if (chats) { if (chats) {
// console.log(chats);
try { try {
chats = JSON.parse(chats); chats = JSON.parse(chats);
if (Array.isArray(chats)) { if (Array.isArray(chats)) {
@@ -263,19 +267,44 @@ const SiderBar = () => {
chat.itemKey = 'chat' + i; chat.itemKey = 'chat' + i;
chat.to = '/chat/' + i; chat.to = '/chat/' + i;
} }
// setRouterMap({ ...routerMap, chat: '/chat/' + i })
chatItems.push(chat); chatItems.push(chat);
} }
setChatItems(chatItems); setChatItems(chatItems);
// Update router map with chat routes
updateRouterMapWithChats(chats);
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);
showError('聊天数据解析失败') showError('聊天数据解析失败')
} }
} }
}, []);
// Update the useEffect for route selection
useEffect(() => {
const currentPath = location.pathname;
let matchingKey = Object.keys(routerMapState).find(key => routerMapState[key] === currentPath);
// Handle chat routes
if (!matchingKey && currentPath.startsWith('/chat/')) {
const chatIndex = currentPath.split('/').pop();
if (!isNaN(chatIndex)) {
matchingKey = 'chat' + chatIndex;
} else {
matchingKey = 'chat';
}
}
// If we found a matching key, update the selected keys
if (matchingKey) {
setSelectedKeys([matchingKey]);
}
}, [location.pathname, routerMapState]);
useEffect(() => {
setIsCollapsed(styleState.siderCollapsed); setIsCollapsed(styleState.siderCollapsed);
}) }, [styleState.siderCollapsed]);
// Custom divider style // Custom divider style
const dividerStyle = { const dividerStyle = {
@@ -322,14 +351,14 @@ const SiderBar = () => {
// 确保在收起侧边栏时有选中的项目,避免不必要的计算 // 确保在收起侧边栏时有选中的项目,避免不必要的计算
if (selectedKeys.length === 0) { if (selectedKeys.length === 0) {
const currentPath = location.pathname; const currentPath = location.pathname;
const matchingKey = Object.keys(routerMap).find(key => routerMap[key] === currentPath); const matchingKey = Object.keys(routerMapState).find(key => routerMapState[key] === currentPath);
if (matchingKey) { if (matchingKey) {
setSelectedKeys([matchingKey]); setSelectedKeys([matchingKey]);
} else if (currentPath.startsWith('/chat/')) { } else if (currentPath.startsWith('/chat/')) {
setSelectedKeys(['chat']); setSelectedKeys(['chat']);
} else { } else {
setSelectedKeys([]); // 默认选中首页 setSelectedKeys(['detail']); // 默认选中首页
} }
} }
}} }}
@@ -338,28 +367,10 @@ const SiderBar = () => {
hoverStyle={navItemHoverStyle} hoverStyle={navItemHoverStyle}
selectedStyle={navItemSelectedStyle} selectedStyle={navItemSelectedStyle}
renderWrapper={({ itemElement, isSubNav, isInSubNav, props }) => { renderWrapper={({ itemElement, isSubNav, isInSubNav, props }) => {
let chats = localStorage.getItem('chats');
if (chats) {
chats = JSON.parse(chats);
if (Array.isArray(chats) && chats.length > 0) {
for (let i = 0; i < chats.length; i++) {
routerMap['chat' + i] = '/chat/' + i;
}
if (chats.length > 1) {
// delete /chat
if (routerMap['chat']) {
delete routerMap['chat'];
}
} else {
// rename /chat to /chat/0
routerMap['chat'] = '/chat/0';
}
}
}
return ( return (
<Link <Link
style={{ textDecoration: 'none' }} style={{ textDecoration: 'none' }}
to={routerMap[props.itemKey]} to={routerMapState[props.itemKey] || routerMap[props.itemKey]}
> >
{itemElement} {itemElement}
</Link> </Link>
@@ -466,7 +477,7 @@ const SiderBar = () => {
<Nav.Footer <Nav.Footer
style={{ style={{
paddingBottom: styleState?.isMobile ? '112px' : '20px', paddingBottom: styleState?.isMobile ? '112px' : '',
}} }}
collapseButton={true} collapseButton={true}
collapseText={(collapsed)=> collapseText={(collapsed)=>

View File

@@ -1,5 +1,6 @@
{ {
"主页": "Home", "主页": "Home",
"文档": "Docs",
"控制台": "Console", "控制台": "Console",
"$%.6f 额度": "$%.6f quota", "$%.6f 额度": "$%.6f quota",
"%d 点额度": "%d point quota", "%d 点额度": "%d point quota",