From d94a58ebca1c53490f4c62aad7b97a3c3d335a97 Mon Sep 17 00:00:00 2001 From: samanhappy Date: Tue, 15 Apr 2025 19:02:38 +0800 Subject: [PATCH] Refactor server management UI (#9) --- assets/dashboard.png | Bin 20167 -> 35941 bytes assets/dashboard.zh.png | Bin 21842 -> 36540 bytes frontend/src/App.tsx | 404 ++------------------- frontend/src/components/ServerCard.tsx | 12 +- frontend/src/components/layout/Content.tsx | 17 + frontend/src/components/layout/Header.tsx | 61 ++++ frontend/src/components/layout/Sidebar.tsx | 80 ++++ frontend/src/hooks/useServerData.ts | 306 ++++++++++++++++ frontend/src/layouts/MainLayout.tsx | 33 ++ frontend/src/locales/en.json | 37 +- frontend/src/locales/zh.json | 37 +- frontend/src/pages/ChangePasswordPage.tsx | 30 -- frontend/src/pages/Dashboard.tsx | 206 +++++++++++ frontend/src/pages/ServersPage.tsx | 111 ++++++ frontend/src/pages/SettingsPage.tsx | 55 +++ 15 files changed, 964 insertions(+), 425 deletions(-) create mode 100644 frontend/src/components/layout/Content.tsx create mode 100644 frontend/src/components/layout/Header.tsx create mode 100644 frontend/src/components/layout/Sidebar.tsx create mode 100644 frontend/src/hooks/useServerData.ts create mode 100644 frontend/src/layouts/MainLayout.tsx delete mode 100644 frontend/src/pages/ChangePasswordPage.tsx create mode 100644 frontend/src/pages/Dashboard.tsx create mode 100644 frontend/src/pages/ServersPage.tsx create mode 100644 frontend/src/pages/SettingsPage.tsx diff --git a/assets/dashboard.png b/assets/dashboard.png index 09e8924b1dc18bde47fe75e2f1dc34ba1bd67403..42a4865032a266de7c422fa3ed0fe67c92ba7345 100644 GIT binary patch literal 35941 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≠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 literal 20167 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 diff --git a/assets/dashboard.zh.png b/assets/dashboard.zh.png index ad84131cf218b04ce5f92da913731063d50b5356..3edd8a6a100f713cf78a435a4c9eeaa8b1e6904b 100644 GIT binary patch literal 36540 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 OAi&@7THe)L5C1PD;x$76 literal 21842 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%0Ay`PYM% z(eb=LLSu?IcTVcyo1yJ}wj`K9?rQ*YHmjF)u~TW^;s`@4;bY$wP4PMiETwPW%JNQ< z$$``GICQA<6vrim#)-Iz0!^*arV^|HQW z-L!+wp0`Y-vrf#1t7Ooj3Pe51_EUd83K>Jec3Zo$dI@9MWxGV^d73)&)em`RyD#qE zxB?Dk`2_M1rd_RWh!IlCEfdj54rHl@dX3cU7!}Nx^eY-D2FA)5?8)kp{n}%C@m`Ej zlPXS+^^;h(LhfFFX&=S(7)cYWw)4`Ts1dC@?d-!G;pYR+ICG%{d*QryD;K=(*2)F zI^k>8Vgvk$?^T9(K5*}OqqdNT?hkc91G+Wz%)>A`QN#k=-nx6EK(O@SHKOCeen%=_ zp*pDLjc{w-RKqL9oquqc01uDLKUBigNH^(gnX{C?`6*yZ8PBs86;t^uKDg(z=p)%B7V)ju06Q z6Qru4|6>TXeW_77OW0!=wP5$Mgg*Z9pwUhkU%#RJqrkxFhdD`2e-U=4!h!Uyg3G-s zQZFp9W`Q)AZ>p$lH5MYe_5OSC*70T}!kFJ!zGsYl#_YF7y^-rLK~!c|#lDCJL1B#WkE4u)s>$soB+uw*^O7wOx}vD_`_saIw>AOy6vhx zr*=LaoGQ?hc4{Vb{A$ePBP)B&{cTb+s)I`9HAhJ9MY6hBCgLNCRsWX^79O?J#qe;6 z)6M5##rcv!;0?&Y+xFTAY4ba0!I$OqIavJV ztAlLY@9hS~KD_|wQ51r3#?er2r=pYrrhwAta+tz5(kAerY=-lY5rN2t_a!Y^vtV(w z84qr&=PhNs3}K^T=7LhyxyB)ZRSI*j#wnqPY?;)#w1o|6tTB!rMIw2_T~yllblL>d zW*?A+-Xt?gprN~Z6hhB-FFWiijJeH@7|wmB`B>TZB6Lt1t|sUgHG!0Kj4q{ub5w0b(b zJOjPGET%k>k>z<4t`G+)3amPw-Rsk}&%M}tD3nZ5m)z9pL`nEs@R2YU$2Yp)OA{j{Bpo@pz56EU3r`M+* z(o}%Z5Q~{y82)gb@xVHzo4DU8kT5?bLSa`7YqYt6%b2*{j2RY*+0Y%3JJRuHj6373 z_~8vuXFM(4nAIfzlU_{vee+t>emLn^BD!fqET1a;=^Q`1 z^Q*;{gJ>s1HIzDn2PAoatQ)I?>x}y9UoQHDJ>KDYe`(9_bwFkeF5*3jp(dilGRU9y zTEhcANp`b-_4*UMDtj3nX>DnfEKpB_DFRV`;N(YTh()=RAaxeogG^g;d)}%13huNc zml9bguXUm!Wt)d*PtX8?PEBj!5(f(oGQvTDhm|dHeJ&GzA$5VRQ-6b9WqWi}`-j)> zD-RxS?5%9xiFAZ#=k-K3KF<#7S)dk%PPhxeb*4Y_dVRZ@$G%Te6d{10$RKeSVaekT z1CP)tXTz8l7?P2wn@G`(=VH?bXCdH1|3nHQr?oz>UPe8U4S{l%{|o%$|AW_O|AW_k z{)5-g{0CnD?<*qv$I!$XEQrMde^>}SP(FYmmSh;Y^P(C86xY5I0Vh()2mz3>(eWiC zhgQ6?WMm6Wi-0Q|J3(@yd-9Do8NBF)kV||3IxyP3kEH}Wosf3gB!je+qEt&5aC8Dh zZP=IzszHXa7$8>_4nyY|5j@|cK$Lrw>P` znF#e%rSF%0UpWA-*F6pqz%Rfzu$3&2b>S6ZM9dtN&vqI+ zv$uG3nv-yqkU)4#12-$CukP9Z&3L2CP@2(`yNqlA%dQ-5v_b+`c zJ$^=qzgC#=_B7mVR;b(4vkQ36-U26MI-2`xjNlYEJ3zndDf0g4I3APcJ@0r1MGm#&06&}dcr`4 zx&K`LfV>FckUnP~;u{o3zVIsJ1x^*{b2B0?0Dq2u`9(1Sc?aW^(#p_#HN7Za(lugc zI}g|_P?r}0ohP;fWyhT)rMaK`mrT2#jeu0j5j52Jxnn?(mN!OF^*f&$(unYo1}Xaguai|^*7@AA@T z2qsFB8}%Qi7x^m|%zs=W5XV112{s(~`?qQf_rGZRgMZ$V{|`kW|7xNB$@%}{|2oPA z9ITAu@9#aXvNS3+1$~_XAC((oYGsy2pD2h@$Ww#8QAY>vKTv{V4JRC}$43f|h^=?P z(y*53THk$EQ@9C1O}zC>4GK4aUokZd>-fwMKLL(_e?OTn)lD3?*9{{6VsHE%e`h)p zq{oraIm^MyEkBUtPeA2yt1D?Z`xv|v>6kE~bOn~wM`gNR0uxO_hx0sMVxQ_o=V?tx4g)B?NTeA9?v_v5J` zZFK(P+;&+UjU}<9aa7{uWYE7YAY?lGuGecv-uFFOuQ?q1z8!tL&owHtX&_WGQmGvH z2L8^&aoV5>g{HE*2PoEkKA-Vi{@5QHHe@3D#FxBV4KFAd*r~D~cJEKGU;iTB8E6h^ zoC38pM)mi2t1whCO=`v+TkMEPy5eetqL8QKL6ZMph&;o3-}rlsZ2#f05WHR|Rf*sVDDmI#@EO;T^~o9o)pV@aA- z;{wH6NHNw}g5MD*%`|bAYTnZP8u59P%TpFtsOHDb8ZLZ+M zasP@yY8I~CS^t3O$l%7sg&``!MWoH458^R?r169|lj7mR<7$mAZM|OA3dg%BuJC3Z zss$*iw0JzNX5}7d;1JU6?M8D8X&>Tx%~tAe06Ce;7_J~BsHp@QlV`anpHm1Tf9fD8 zZ@s=xtD8%OIiy7@2oTCMSYDk`eAv9X-JJIs+iad%>$5n^4+^C>UcGX4G6wFbJJ0^q zFg0I!kS00z?LL*vhenBK7ZAY@e~;F)#_%vC`*f}Ig3Nbc&HUs_5=<7r-NCUCzp?%J z(1zan^wC=JI%5Av>c*cLme9F?5c1-A{UwQQZmqH!vUyr+Dhz1H_d6C9DvAqiku$=^ z2wxC-dY2F*nI?q$NI72tPjWT;>O=L7k_kc2;CIPKC;XIo)mzo3{`Ly_?@aG=biI@w z8;JI&Y!Mg1{f_3EZ91tU5B;K@_>Jb65ykY~v|aoqQd-g+;zug*;r>k6+do9DOx84W zj&DRv9nrbHm|qdsZE>Y3R&z4c)=*LsSlN($kg`pWS9w=x>hDr!b51wKbd&kw*bwUW zDHgn#^qw7S+6y{V{Dh&ZI%G^%Af<$PS`_0E`{;Y6o-3)5VYIin%sZ)>wBA9n3YnkA z@0O5m4DUj}*A%Dn6UbJ`utey?qL^pDm+2KZ*5C``B7|oi!F7oJsS}CA%`Qp~RJ_dv zMh(NY56AxCYXtv(Cu__XXqA#Arsorg%3n_4&YID=6>0|qDYgFGS z&n!gDSSY>(9u2w$?i~}!1%7`!Mqw~xgKy4H9Uv&Fgs2X5SJAvABVRJ684)ZmE0^BC z><1oII7$4l7MY!5K1hE~fpG8`@ib-VkcgzE{GLvcQ*1QdX|GJ(j6l?37nIsXt|nE) zoEIZ#j6V>-RkbSyg`q-&(OaD0ieve8#bEdsbH&ETNo8}r-Sn(y`?(+bXeMLx^gKVX z^YOjUEbwxoi@3GR8;U_NuqfOUPnxeH`-&074Q31JT$;$y4^aZq#$+BtOcV6Pr@wpN zk~%5pHI6FVa$I?A%DL#4Tk*BFyng}4X#-`_7TuQ=oayA5|fnC9?dNiFh{Zvq02=f81tP4 zFK!N9J)T0%oc8kYA3_cj>TH#?ppCtAS%{^ALaoOXe=LQ$!n?%9JR4&lm0*IoX|!%@ z3I9G~2lL_5$un4O9B#A?r5OUg)FPmnzW+L(^eT{J4C#9?YyTr}=OZI_Hy43$>Ze6G zEZTlHC+%^;FdXPqS?8j$p7m@rYlC$Hsp~q4*67fzOBeo7gE_`zn<&&t5j_6ux&G^{ zEFBfUk0V(R%dd`qx`IL-voYfPC-CZ!jcEqE=Nhrl(hazMDiOtGqY?2E_BiNWFLz0%P=`L>om?BfjEk=Ww%JELiE1OM0Uh2PL8xE<0L!OoIUiyy@#%LLJ95;)__baq#vjZ&WFR=G+I(gO8BCm9Og-M~XZ zhU!=>4^afHJ!7SI0~)L^E!r_WQ_z(=mQgz*#FZ{aH$Kbha+`JZA*!HY^)P9eNroI3 z_Mm`9$$C$oX6iLubg?{=C-6(wLu1ibCSi%u%Q%B8*&K0jQ?UL^-jMJYLryTjyo9N14EC5@xWkzL_O&EnCRg|S0WQ4dPSCX3>lUm%Q!W3_Y(V-t_gXgN?} zWG*)9gT-*et-xij^*b&o&6bgBZ+?tjRnQO85oYNc>+JD0Z#_TycV{^p6AKGg+oXXa zM6DOtQ45K|hLtqcF=LK%TZS;>YtesakO+m&RSexRb6$(bf~32KFYafRIiMSDCz zQ93F+#kJ+>jgw!)L2qWXncAk{;FP-(I*xhf`%6A2wxde@oOV)4H_{{1*5Yj2d#ux4 zuK1MsTRRNVOw=E3u9X)ba(N&9wN$Dqmg)Z9cx|%`_#0pNKYBUypeB}mkIzgJNCHTN zfGA>uvLlNu%ANsHkVPW0Z-OYYDJYBJ0uu%LpB>fZd*nVFuhu3371-{0rktvY&0ZrG!lunIEIOHN-{UwH^M z!t8%suQg1v8rl#&mv!phJUWepPm0o!q&(UM&cL1}15+_N!WH#Z&1uG0ihENCe_RE>>;U5GrvK2f|X5 zA1ywczVC?qgbYQB&pqD`B%4KHrwDO9P_vk*iNb;30H>6h+nH62Djbi@6PZ3*wH;`{ zi08-5ZqWa|rg4YXwL?7wkQ?>ALzCgBV}`h}20x->5ZZdId*K5Eww~9BZUWpsF3OfH zrpv!4J1(p<{G1tiF#!4`z|+K36E?5R;sjxIBPO>Vhyx}M4k|O> z0VBe2Cln^YjRG72;xR_t7=?}_*5F6QP#$Cl5IF>eFac(FF7{W!+I3)n+@b>1$}S=h<@p%x|l@ktC+V0;A!`8XJYlALPzjqt;3_B2pJAuI)yV9D~x{P|h%E8C@5*Xd8w4TgC z@m(|p!UOrE~(C0YjE$k1%m+F*>P zK{3Wa7#kltE8=v2XtpPXekMQq6-@klLdbFcf*^h^6J-j0Zf6`>rH5j1hQiJ#-Ibdi z$qJ9$-Ubf(ND5rUQ?Z2C-4b3{h<>Y9{<%{3w#>9Y7X{>F=*u6V!-3+=+IIUU9}8Ia zaL`uNBIezt^k$YTs5^ThBx5BWd9Nc2R_z2wa7@PaGRVL2$-$DW^=g-4yjL2|Pna;v8@XrJWwrD;y9&Uv3eDsxT*e4y2joMp#32n#1Dz$Xof@hSmi*nmpedpie}S)K_D-iH3ZxxwaVjZjKd zna{%w`|~;;Xz%CwADD2}!x*a%$vmQQzlBf0Pvpps#JYa+0s%S|DVK4gMeMLo38Id> zEsIKMSS@b*>GetQ7sA!@3f94&qCQ~Ud3=s>5q2#?CU5l03xg&8Vfh=)0-KY0_w-sN zQ**58t38!Xx>>2Jqe!DLBi+xj5S`PxErE^sotLkqB-m7klh$b_ctLtfZQ1u%_Pmx% zMNHfx7V6p5dtTk!{Wv*@!D6o_G-~%0BoZZL>yCC#@BYxri-ReHL=HudRSkXjS;=4i zG)3M_>lw1*OMM9hd$*av!zgs z;E`0MMoG8@%#n|K^qzYlZcDn+8|d=3*~M%`?^AdeFQ#dSC{DpVT#nZs3zvPTuoWRF zu%Z8~^Y->8#XS`jh*DJDHKg9ssWFiVzCiHPGTavN`uV3{ri-%nx>&$xr{CdaQ)n=4EV{P&gU~J?M^QW7>NxM>=rIvUY5gg@6V4(m>EJnnOdu>??7yA z50t;kmNaqw21>VVr#q^lf(;#CF%-AM5`OF#I|_qdNgXAKt*-eD1TD(r@x{mTk;(&H zq4Ae{rHbhZSm&sqtd;F&f~AKNn*P)lo-+h>)71$54>wQ-X+?N^J*fA};?YmUB=uS? zxA5K{vgJGbX~&XIyFH#PuS^Jv>=7@n4v(DQU{w@HPMZ3S^~r91}2`RbVlE>|0x8` z=Q={8FVA|{h*Piu8IW`>2uh=T&8AEwwx@jqj|TTh8q8P*Z|=@UG&r5!=a+XF!x3Y? zkEsbIcGrZD8r&skpM8-g!d>n^e@t??CrE%p(;K-v#jLZ=o@xIZM${y>(FOH>)CSx> zUGE*aWSqFjlGXY31Sv=FkT(*Ep9{Yg8m**mrK?+&QxojoFZ_7pk5+HUX-+cj?v-Qz zZec5IUIJ~>`x+xE7$R!YWw{i3SjGdFU$dTl0J5;iO7H*HVTigrfBmg%W=n#*)jnSw zXy2KL4FKz)Q}xHF6gR`s{h7wF?of*!$cnb2GB=ZIsYVeB^tUH$7IS(cwBMAEm_=79e$Zs+wUVZ zqAx@UbaRriGGv zGnp{uGBxpr!}k->rbL1aDR}+~>*MZaX`(28`Vy8;g_ICQ^A!=6`8 z+KT_YXYAz-f}g0tZbO-!tRcpA?Whm?-@C? zylBR-u>`@)o=<%9DT`Ap;p0wa$0f*hoJ!v(1M8&YBs_`yQ7fk(9(R$tN8N)nEu*|+ zITXxK9y|kd?<^#9`S8PP@hNR>eU@`>hFd~Z0_JAti=^iB0*2PqlttIPXUO<72Bw&p zsedf4swnBvnJaS-2<-xxx_ZpFUJFYhPeojlu;sT&!b3cSE#C6ld*K8I=eXQ)2zYtDL>B9 zGJsR3IAri>K~x0IfmMv>usyF-7?#oZ*5p%Y&(X*fAa!c-jm?-%1g4d?kScFua}@pj zG2(Pdh^c8L8ftVmx1(q`VBqn6C>fJkvp@-y^AWAW6qUTn5=oe;*`1wMcLGc}(ICtC z4!T^m@k;?DGUIP?0qwp5P+jW@_z%|H{73uN?%3MBTfO3I?#8u?{hvO49-_kxfc_T@ bo9^4dif{L_xxS7KV84Bshp)iIGv>blhiP#k diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b52b461..71eedf7 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,398 +1,36 @@ -import { useState, useEffect, useRef, useCallback } from 'react' -import { useTranslation } from 'react-i18next' -import { BrowserRouter as Router, Route, Routes, Navigate, useNavigate } from 'react-router-dom' -import { Server, ApiResponse } from './types' -import ServerCard from './components/ServerCard' -import AddServerForm from './components/AddServerForm' -import EditServerForm from './components/EditServerForm' -import LoginPage from './pages/LoginPage' -import ChangePasswordPage from './pages/ChangePasswordPage' -import ProtectedRoute from './components/ProtectedRoute' -import { AuthProvider, useAuth } from './contexts/AuthContext' - -// 配置选项 -const CONFIG = { - // 初始化启动阶段的配置 - startup: { - maxAttempts: 60, // 初始化阶段最大尝试次数 - pollingInterval: 3000 // 初始阶段轮询间隔 (3秒) - }, - // 正常运行阶段的配置 - normal: { - pollingInterval: 10000 // 正常运行时的轮询间隔 (10秒) - } -} - -// Dashboard component that contains the main application -const Dashboard = () => { - const { t } = useTranslation() - const [servers, setServers] = useState([]) - const [error, setError] = useState(null) - const [refreshKey, setRefreshKey] = useState(0) - const [editingServer, setEditingServer] = useState(null) - const [isInitialLoading, setIsInitialLoading] = useState(true) - const [fetchAttempts, setFetchAttempts] = useState(0) - const { auth, logout } = useAuth() - const navigate = useNavigate() - - // 轮询定时器引用 - const intervalRef = useRef(null) - // 保存当前尝试次数,避免依赖循环 - const attemptsRef = useRef(0) - - // 清理定时器 - const clearTimer = () => { - if (intervalRef.current) { - clearInterval(intervalRef.current) - intervalRef.current = null - } - } - - // 开始正常轮询 - const startNormalPolling = useCallback(() => { - // 确保没有其他定时器在运行 - clearTimer() - - const fetchServers = async () => { - try { - const token = localStorage.getItem('mcphub_token'); - const response = await fetch('/api/servers', { - headers: { - 'x-auth-token': token || '' - } - }); - const data = await response.json() - - if (data && data.success && Array.isArray(data.data)) { - setServers(data.data) - } else if (data && Array.isArray(data)) { - setServers(data) - } else { - console.error('Invalid server data format:', data) - setServers([]) - } - - // 重置错误状态 - setError(null) - } catch (err) { - console.error('Error fetching servers during normal polling:', err) - - // 使用友好的错误消息 - if (!navigator.onLine) { - setError(t('errors.network')) - } else if (err instanceof TypeError && ( - err.message.includes('NetworkError') || - err.message.includes('Failed to fetch') - )) { - setError(t('errors.serverConnection')) - } else { - setError(t('errors.serverFetch')) - } - } - } - - // 立即执行一次 - fetchServers() - - // 设置定期轮询 - intervalRef.current = setInterval(fetchServers, CONFIG.normal.pollingInterval) - }, [t]) - - useEffect(() => { - // 重置尝试计数 - if (refreshKey > 0) { - attemptsRef.current = 0; - setFetchAttempts(0); - } - - // 初始化加载阶段的请求函数 - const fetchInitialData = async () => { - try { - const token = localStorage.getItem('mcphub_token'); - const response = await fetch('/api/servers', { - headers: { - 'x-auth-token': token || '' - } - }); - const data = await response.json() - - // 处理API响应中的包装对象,提取data字段 - if (data && data.success && Array.isArray(data.data)) { - setServers(data.data) - setIsInitialLoading(false) - // 初始化成功,开始正常轮询 - startNormalPolling() - return true - } else if (data && Array.isArray(data)) { - // 兼容性处理,如果API直接返回数组 - setServers(data) - setIsInitialLoading(false) - // 初始化成功,开始正常轮询 - startNormalPolling() - return true - } else { - // 如果数据格式不符合预期,设置为空数组 - console.error('Invalid server data format:', data) - setServers([]) - setIsInitialLoading(false) - // 初始化成功但数据为空,开始正常轮询 - startNormalPolling() - return true - } - } catch (err) { - // 增加尝试次数计数,使用 ref 避免触发 effect 重新运行 - attemptsRef.current += 1; - console.error(`Initial loading attempt ${attemptsRef.current} failed:`, err) - - // 更新状态用于显示 - setFetchAttempts(attemptsRef.current) - - // 设置适当的错误消息 - if (!navigator.onLine) { - setError(t('errors.network')) - } else { - setError(t('errors.initialStartup')) - } - - // 如果已超过最大尝试次数,放弃初始化并切换到正常轮询 - if (attemptsRef.current >= CONFIG.startup.maxAttempts) { - console.log('Maximum startup attempts reached, switching to normal polling') - setIsInitialLoading(false) - // 清除初始化的轮询 - clearTimer() - // 切换到正常轮询模式 - startNormalPolling() - } - - return false - } - } - - // 组件挂载时,根据当前状态设置适当的轮询 - if (isInitialLoading) { - // 确保没有其他定时器在运行 - clearTimer() - - // 立即执行一次初始请求 - fetchInitialData() - - // 设置初始阶段的轮询间隔 - intervalRef.current = setInterval(fetchInitialData, CONFIG.startup.pollingInterval) - console.log(`Started initial polling with interval: ${CONFIG.startup.pollingInterval}ms`) - } else { - // 已经初始化完成,开始正常轮询 - startNormalPolling() - } - - // 清理函数 - return () => { - clearTimer() - } - }, [refreshKey, t, isInitialLoading, startNormalPolling]) - - // 手动触发刷新 - const triggerRefresh = () => { - // 清除当前的定时器 - clearTimer() - - // 如果在初始化阶段,重置初始化状态 - if (isInitialLoading) { - setIsInitialLoading(true) - attemptsRef.current = 0 - setFetchAttempts(0) - } - - // refreshKey 的改变会触发 useEffect 再次运行 - setRefreshKey(prevKey => prevKey + 1) - } - - const handleServerAdd = () => { - setRefreshKey(prevKey => prevKey + 1) - } - - const handleServerEdit = (server: Server) => { - // Fetch settings to get the full server config before editing - const token = localStorage.getItem('mcphub_token'); - fetch(`/api/settings`, { - headers: { - 'x-auth-token': token || '' - } - }) - .then(response => response.json()) - .then((settingsData: ApiResponse<{ mcpServers: Record }>) => { - if ( - settingsData && - settingsData.success && - settingsData.data && - settingsData.data.mcpServers && - settingsData.data.mcpServers[server.name] - ) { - const serverConfig = settingsData.data.mcpServers[server.name] - const fullServerData = { - name: server.name, - status: server.status, - tools: server.tools || [], - config: serverConfig, - } - - console.log('Editing server with config:', fullServerData) - setEditingServer(fullServerData) - } else { - console.error('Failed to get server config from settings:', settingsData) - setError(t('server.invalidConfig', { serverName: server.name })) - } - }) - .catch(err => { - console.error('Error fetching server settings:', err) - setError(err instanceof Error ? err.message : String(err)) - }) - } - - const handleEditComplete = () => { - setEditingServer(null) - setRefreshKey(prevKey => prevKey + 1) - } - - const handleServerRemove = async (serverName: string) => { - try { - const token = localStorage.getItem('mcphub_token'); - const response = await fetch(`/api/servers/${serverName}`, { - method: 'DELETE', - headers: { - 'x-auth-token': token || '' - } - }) - const result = await response.json() - - if (!response.ok) { - setError(result.message || t('server.deleteError', { serverName })) - return - } - - setRefreshKey(prevKey => prevKey + 1) - } catch (err) { - setError(t('errors.general') + ': ' + (err instanceof Error ? err.message : String(err))) - } - } - - const handleServerToggle = async (server: Server, enabled: boolean) => { - try { - const token = localStorage.getItem('mcphub_token'); - const response = await fetch(`/api/servers/${server.name}/toggle`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-auth-token': token || '' - }, - body: JSON.stringify({ enabled }), - }); - - const result = await response.json(); - - if (!response.ok) { - console.error('Failed to toggle server:', result); - setError(t('server.toggleError', { serverName: server.name })); - return; - } - - // Update the UI immediately to reflect the change - setRefreshKey(prevKey => prevKey + 1); - } catch (err) { - console.error('Error toggling server:', err); - setError(err instanceof Error ? err.message : String(err)); - } - }; - - const handleLogout = () => { - logout() - navigate('/login') - } - - return ( -
-
- {error && ( -
-
-
-

{t('app.error')}

-

{error}

-
- -
-
- )} - -
-

{t('app.title')}

-
- - - -
-
- {servers.length === 0 ? ( -
-

{t('app.noServers')}

-
- ) : ( -
- {servers.map((server, index) => ( - - ))} -
- )} - {editingServer && ( - setEditingServer(null)} - /> - )} -
-
- ) -} +import React from 'react'; +import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom'; +import { AuthProvider } from './contexts/AuthContext'; +import MainLayout from './layouts/MainLayout'; +import ProtectedRoute from './components/ProtectedRoute'; +import LoginPage from './pages/LoginPage'; +import DashboardPage from './pages/Dashboard'; +import ServersPage from './pages/ServersPage'; +import SettingsPage from './pages/SettingsPage'; function App() { return ( + {/* 公共路由 */} } /> + + {/* 受保护的路由,使用 MainLayout 作为布局容器 */} }> - } /> - } /> + }> + } /> + } /> + } /> + + + {/* 未匹配的路由重定向到首页 */} } /> - ) + ); } -export default App \ No newline at end of file +export default App; \ No newline at end of file diff --git a/frontend/src/components/ServerCard.tsx b/frontend/src/components/ServerCard.tsx index d9e830f..6cc2a49 100644 --- a/frontend/src/components/ServerCard.tsx +++ b/frontend/src/components/ServerCard.tsx @@ -63,12 +63,6 @@ const ServerCard = ({ server, onRemove, onEdit, onToggle }: ServerCardProps) => > {t('server.edit')} -
+ diff --git a/frontend/src/components/layout/Content.tsx b/frontend/src/components/layout/Content.tsx new file mode 100644 index 0000000..faf4890 --- /dev/null +++ b/frontend/src/components/layout/Content.tsx @@ -0,0 +1,17 @@ +import React, { ReactNode } from 'react'; + +interface ContentProps { + children: ReactNode; +} + +const Content: React.FC = ({ children }) => { + return ( +
+
+ {children} +
+
+ ); +}; + +export default Content; \ No newline at end of file diff --git a/frontend/src/components/layout/Header.tsx b/frontend/src/components/layout/Header.tsx new file mode 100644 index 0000000..103ceba --- /dev/null +++ b/frontend/src/components/layout/Header.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; +import { useAuth } from '@/contexts/AuthContext'; + +interface HeaderProps { + onToggleSidebar: () => void; +} + +const Header: React.FC = ({ onToggleSidebar }) => { + const { t } = useTranslation(); + const navigate = useNavigate(); + const { auth, logout } = useAuth(); + + const handleLogout = () => { + logout(); + navigate('/login'); + }; + + return ( +
+
+
+ {/* 侧边栏切换按钮 */} + + + {/* 应用标题 */} +

{t('app.title')}

+
+ + {/* 用户信息和操作 */} +
+ {auth.user && ( + + {t('app.welcomeUser', { username: auth.user.username })} + + )} + +
+ +
+
+
+
+ ); +}; + +export default Header; \ No newline at end of file diff --git a/frontend/src/components/layout/Sidebar.tsx b/frontend/src/components/layout/Sidebar.tsx new file mode 100644 index 0000000..1d227c6 --- /dev/null +++ b/frontend/src/components/layout/Sidebar.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { NavLink, useLocation } from 'react-router-dom'; + +interface SidebarProps { + collapsed: boolean; +} + +interface MenuItem { + path: string; + label: string; + icon: React.ReactNode; +} + +const Sidebar: React.FC = ({ collapsed }) => { + const { t } = useTranslation(); + const location = useLocation(); + + // 菜单项配置 + const menuItems: MenuItem[] = [ + { + path: '/', + label: t('nav.dashboard'), + icon: ( + + + + + ), + }, + { + path: '/servers', + label: t('nav.servers'), + icon: ( + + + + ), + }, + { + path: '/settings', + label: t('nav.settings'), + icon: ( + + + + ), + }, + ]; + + return ( + + ); +}; + +export default Sidebar; \ No newline at end of file diff --git a/frontend/src/hooks/useServerData.ts b/frontend/src/hooks/useServerData.ts new file mode 100644 index 0000000..3dad563 --- /dev/null +++ b/frontend/src/hooks/useServerData.ts @@ -0,0 +1,306 @@ +import { useState, useEffect, useRef, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Server, ApiResponse } from '@/types'; + +// 配置选项 +const CONFIG = { + // 初始化启动阶段的配置 + startup: { + maxAttempts: 60, // 初始化阶段最大尝试次数 + pollingInterval: 3000 // 初始阶段轮询间隔 (3秒) + }, + // 正常运行阶段的配置 + normal: { + pollingInterval: 10000 // 正常运行时的轮询间隔 (10秒) + } +}; + +export const useServerData = () => { + const { t } = useTranslation(); + const [servers, setServers] = useState([]); + const [error, setError] = useState(null); + const [refreshKey, setRefreshKey] = useState(0); + const [isInitialLoading, setIsInitialLoading] = useState(true); + const [fetchAttempts, setFetchAttempts] = useState(0); + + // 轮询定时器引用 + const intervalRef = useRef(null); + // 保存当前尝试次数,避免依赖循环 + const attemptsRef = useRef(0); + + // 清理定时器 + const clearTimer = () => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + }; + + // 开始正常轮询 + const startNormalPolling = useCallback(() => { + // 确保没有其他定时器在运行 + clearTimer(); + + const fetchServers = async () => { + try { + const token = localStorage.getItem('mcphub_token'); + const response = await fetch('/api/servers', { + headers: { + 'x-auth-token': token || '' + } + }); + const data = await response.json(); + + if (data && data.success && Array.isArray(data.data)) { + setServers(data.data); + } else if (data && Array.isArray(data)) { + setServers(data); + } else { + console.error('Invalid server data format:', data); + setServers([]); + } + + // 重置错误状态 + setError(null); + } catch (err) { + console.error('Error fetching servers during normal polling:', err); + + // 使用友好的错误消息 + if (!navigator.onLine) { + setError(t('errors.network')); + } else if (err instanceof TypeError && ( + err.message.includes('NetworkError') || + err.message.includes('Failed to fetch') + )) { + setError(t('errors.serverConnection')); + } else { + setError(t('errors.serverFetch')); + } + } + }; + + // 立即执行一次 + fetchServers(); + + // 设置定期轮询 + intervalRef.current = setInterval(fetchServers, CONFIG.normal.pollingInterval); + }, [t]); + + useEffect(() => { + // 重置尝试计数 + if (refreshKey > 0) { + attemptsRef.current = 0; + setFetchAttempts(0); + } + + // 初始化加载阶段的请求函数 + const fetchInitialData = async () => { + try { + const token = localStorage.getItem('mcphub_token'); + const response = await fetch('/api/servers', { + headers: { + 'x-auth-token': token || '' + } + }); + const data = await response.json(); + + // 处理API响应中的包装对象,提取data字段 + if (data && data.success && Array.isArray(data.data)) { + setServers(data.data); + setIsInitialLoading(false); + // 初始化成功,开始正常轮询 + startNormalPolling(); + return true; + } else if (data && Array.isArray(data)) { + // 兼容性处理,如果API直接返回数组 + setServers(data); + setIsInitialLoading(false); + // 初始化成功,开始正常轮询 + startNormalPolling(); + return true; + } else { + // 如果数据格式不符合预期,设置为空数组 + console.error('Invalid server data format:', data); + setServers([]); + setIsInitialLoading(false); + // 初始化成功但数据为空,开始正常轮询 + startNormalPolling(); + return true; + } + } catch (err) { + // 增加尝试次数计数,使用 ref 避免触发 effect 重新运行 + attemptsRef.current += 1; + console.error(`Initial loading attempt ${attemptsRef.current} failed:`, err); + + // 更新状态用于显示 + setFetchAttempts(attemptsRef.current); + + // 设置适当的错误消息 + if (!navigator.onLine) { + setError(t('errors.network')); + } else { + setError(t('errors.initialStartup')); + } + + // 如果已超过最大尝试次数,放弃初始化并切换到正常轮询 + if (attemptsRef.current >= CONFIG.startup.maxAttempts) { + console.log('Maximum startup attempts reached, switching to normal polling'); + setIsInitialLoading(false); + // 清除初始化的轮询 + clearTimer(); + // 切换到正常轮询模式 + startNormalPolling(); + } + + return false; + } + }; + + // 组件挂载时,根据当前状态设置适当的轮询 + if (isInitialLoading) { + // 确保没有其他定时器在运行 + clearTimer(); + + // 立即执行一次初始请求 + fetchInitialData(); + + // 设置初始阶段的轮询间隔 + intervalRef.current = setInterval(fetchInitialData, CONFIG.startup.pollingInterval); + console.log(`Started initial polling with interval: ${CONFIG.startup.pollingInterval}ms`); + } else { + // 已经初始化完成,开始正常轮询 + startNormalPolling(); + } + + // 清理函数 + return () => { + clearTimer(); + }; + }, [refreshKey, t, isInitialLoading, startNormalPolling]); + + // 手动触发刷新 + const triggerRefresh = () => { + // 清除当前的定时器 + clearTimer(); + + // 如果在初始化阶段,重置初始化状态 + if (isInitialLoading) { + setIsInitialLoading(true); + attemptsRef.current = 0; + setFetchAttempts(0); + } + + // refreshKey 的改变会触发 useEffect 再次运行 + setRefreshKey(prevKey => prevKey + 1); + }; + + // 服务器相关操作 + const handleServerAdd = () => { + setRefreshKey(prevKey => prevKey + 1); + }; + + const handleServerEdit = async (server: Server) => { + try { + // Fetch settings to get the full server config before editing + const token = localStorage.getItem('mcphub_token'); + const response = await fetch(`/api/settings`, { + headers: { + 'x-auth-token': token || '' + } + }); + + const settingsData: ApiResponse<{ mcpServers: Record }> = await response.json(); + + if ( + settingsData && + settingsData.success && + settingsData.data && + settingsData.data.mcpServers && + settingsData.data.mcpServers[server.name] + ) { + const serverConfig = settingsData.data.mcpServers[server.name]; + return { + name: server.name, + status: server.status, + tools: server.tools || [], + config: serverConfig, + }; + } else { + console.error('Failed to get server config from settings:', settingsData); + setError(t('server.invalidConfig', { serverName: server.name })); + return null; + } + } catch (err) { + console.error('Error fetching server settings:', err); + setError(err instanceof Error ? err.message : String(err)); + return null; + } + }; + + const handleServerRemove = async (serverName: string) => { + try { + const token = localStorage.getItem('mcphub_token'); + const response = await fetch(`/api/servers/${serverName}`, { + method: 'DELETE', + headers: { + 'x-auth-token': token || '' + } + }); + const result = await response.json(); + + if (!response.ok) { + setError(result.message || t('server.deleteError', { serverName })); + return false; + } + + setRefreshKey(prevKey => prevKey + 1); + return true; + } catch (err) { + setError(t('errors.general') + ': ' + (err instanceof Error ? err.message : String(err))); + return false; + } + }; + + const handleServerToggle = async (server: Server, enabled: boolean) => { + try { + const token = localStorage.getItem('mcphub_token'); + const response = await fetch(`/api/servers/${server.name}/toggle`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-auth-token': token || '' + }, + body: JSON.stringify({ enabled }), + }); + + const result = await response.json(); + + if (!response.ok) { + console.error('Failed to toggle server:', result); + setError(t('server.toggleError', { serverName: server.name })); + return false; + } + + // Update the UI immediately to reflect the change + setRefreshKey(prevKey => prevKey + 1); + return true; + } catch (err) { + console.error('Error toggling server:', err); + setError(err instanceof Error ? err.message : String(err)); + return false; + } + }; + + return { + servers, + error, + setError, + isLoading: isInitialLoading, + fetchAttempts, + triggerRefresh, + handleServerAdd, + handleServerEdit, + handleServerRemove, + handleServerToggle + }; +}; \ No newline at end of file diff --git a/frontend/src/layouts/MainLayout.tsx b/frontend/src/layouts/MainLayout.tsx new file mode 100644 index 0000000..fa1f49d --- /dev/null +++ b/frontend/src/layouts/MainLayout.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { Outlet } from 'react-router-dom'; +import Header from '@/components/layout/Header'; +import Sidebar from '@/components/layout/Sidebar'; +import Content from '@/components/layout/Content'; + +const MainLayout: React.FC = () => { + // 控制侧边栏展开/折叠状态 + const [sidebarCollapsed, setSidebarCollapsed] = React.useState(false); + + const toggleSidebar = () => { + setSidebarCollapsed(!sidebarCollapsed); + }; + + return ( +
+ {/* 顶部导航 */} +
+ +
+ {/* 侧边导航 */} + + + {/* 主内容区域 */} + + + +
+
+ ); +}; + +export default MainLayout; \ No newline at end of file diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index c050998..79e6fe7 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -7,7 +7,9 @@ "loading": "Loading...", "logout": "Logout", "profile": "Profile", - "changePassword": "Change Password" + "changePassword": "Change Password", + "toggleSidebar": "Toggle Sidebar", + "welcomeUser": "Welcome, {{username}}" }, "auth": { "login": "Login", @@ -51,9 +53,14 @@ "envVars": "Environment Variables", "key": "key", "value": "value", + "enabled": "Enabled", "enable": "Enable", "disable": "Disable", - "remove": "Remove" + "remove": "Remove", + "toggleError": "Failed to toggle server {{serverName}}", + "alreadyExists": "Server {{serverName}} already exists", + "invalidData": "Invalid server data provided", + "notFound": "Server {{serverName}} not found" }, "status": { "online": "Online", @@ -72,6 +79,30 @@ "common": { "processing": "Processing...", "save": "Save", - "cancel": "Cancel" + "cancel": "Cancel", + "refresh": "Refresh" + }, + "nav": { + "dashboard": "Dashboard", + "servers": "Servers", + "settings": "Settings", + "changePassword": "Change Password" + }, + "pages": { + "dashboard": { + "title": "Dashboard", + "totalServers": "Total Servers", + "onlineServers": "Online Servers", + "offlineServers": "Offline Servers", + "connectingServers": "Connecting Servers", + "recentServers": "Recent Servers" + }, + "servers": { + "title": "Servers Management" + }, + "settings": { + "title": "Settings", + "language": "Language" + } } } \ No newline at end of file diff --git a/frontend/src/locales/zh.json b/frontend/src/locales/zh.json index 999a710..2a5e158 100644 --- a/frontend/src/locales/zh.json +++ b/frontend/src/locales/zh.json @@ -7,7 +7,9 @@ "loading": "加载中...", "logout": "退出登录", "profile": "个人资料", - "changePassword": "修改密码" + "changePassword": "修改密码", + "toggleSidebar": "切换侧边栏", + "welcomeUser": "欢迎, {{username}}" }, "auth": { "login": "登录", @@ -51,9 +53,14 @@ "envVars": "环境变量", "key": "键", "value": "值", + "enabled": "已启用", "enable": "启用", "disable": "禁用", - "remove": "移除" + "remove": "移除", + "toggleError": "切换服务器 {{serverName}} 状态失败", + "alreadyExists": "服务器 {{serverName}} 已经存在", + "invalidData": "提供的服务器数据无效", + "notFound": "找不到服务器 {{serverName}}" }, "status": { "online": "在线", @@ -72,6 +79,30 @@ "common": { "processing": "处理中...", "save": "保存", - "cancel": "取消" + "cancel": "取消", + "refresh": "刷新" + }, + "nav": { + "dashboard": "仪表盘", + "servers": "服务器", + "settings": "设置", + "changePassword": "修改密码" + }, + "pages": { + "dashboard": { + "title": "仪表盘", + "totalServers": "服务器总数", + "onlineServers": "在线服务器", + "offlineServers": "离线服务器", + "connectingServers": "连接中服务", + "recentServers": "最近的服务器" + }, + "servers": { + "title": "服务器管理" + }, + "settings": { + "title": "设置", + "language": "语言" + } } } \ No newline at end of file diff --git a/frontend/src/pages/ChangePasswordPage.tsx b/frontend/src/pages/ChangePasswordPage.tsx deleted file mode 100644 index e9c8716..0000000 --- a/frontend/src/pages/ChangePasswordPage.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { useNavigate } from 'react-router-dom'; -import ChangePasswordForm from '../components/ChangePasswordForm'; - -const ChangePasswordPage: React.FC = () => { - const { t } = useTranslation(); - const navigate = useNavigate(); - - const handleSuccess = () => { - setTimeout(() => { - navigate('/'); - }, 2000); - }; - - const handleCancel = () => { - navigate('/'); - }; - - return ( -
-
-

{t('auth.changePassword')}

- -
-
- ); -}; - -export default ChangePasswordPage; \ No newline at end of file diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx new file mode 100644 index 0000000..1cf3f13 --- /dev/null +++ b/frontend/src/pages/Dashboard.tsx @@ -0,0 +1,206 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useServerData } from '@/hooks/useServerData'; +import { ServerStatus } from '@/types'; + +const DashboardPage: React.FC = () => { + const { t } = useTranslation(); + const { servers, error, setError, isLoading } = useServerData(); + + // 计算服务器统计信息 + const serverStats = { + total: servers.length, + online: servers.filter(server => server.status === 'connected').length, + offline: servers.filter(server => server.status === 'disconnected').length, + connecting: servers.filter(server => server.status === 'connecting').length + }; + + // Map status to translation keys + const statusTranslations = { + connected: 'status.online', + disconnected: 'status.offline', + connecting: 'status.connecting' + } + + // 计算各状态百分比(用于仪表板展示) + const getStatusPercentage = (status: ServerStatus) => { + if (servers.length === 0) return 0; + return Math.round((servers.filter(server => server.status === status).length / servers.length) * 100); + }; + + return ( +
+

{t('pages.dashboard.title')}

+ + {error && ( +
+
+
+

{t('app.error')}

+

{error}

+
+ +
+
+ )} + + {isLoading ? ( +
+
+ + + + +

{t('app.loading')}

+
+
+ ) : ( +
+ {/* 服务器总数 */} +
+
+
+ + + +
+
+

{t('pages.dashboard.totalServers')}

+

{serverStats.total}

+
+
+
+ + {/* 在线服务器 */} +
+
+
+ + + +
+
+

{t('pages.dashboard.onlineServers')}

+

{serverStats.online}

+
+
+
+
+
+
+ + {/* 离线服务器 */} +
+
+
+ + + +
+
+

{t('pages.dashboard.offlineServers')}

+

{serverStats.offline}

+
+
+
+
+
+
+ + {/* 连接中服务器 */} +
+
+
+ + + +
+
+

{t('pages.dashboard.connectingServers')}

+

{serverStats.connecting}

+
+
+
+
+
+
+
+ )} + + {/* 最近活动列表 */} + {servers.length > 0 && !isLoading && ( +
+

{t('pages.dashboard.recentServers')}

+
+ + + + + + + + + + + {servers.slice(0, 5).map((server, index) => ( + + + + + + + ))} + +
+ {t('server.name')} + + {t('server.status')} + + {t('server.tools')} + + {t('server.enabled')} +
+ {server.name} + + + {t(statusTranslations[server.status] || server.status)} + + + {server.tools?.length || 0} + + {server.enabled !== false ? ( + + ) : ( + + )} +
+
+
+ )} +
+ ); +}; + +export default DashboardPage; \ No newline at end of file diff --git a/frontend/src/pages/ServersPage.tsx b/frontend/src/pages/ServersPage.tsx new file mode 100644 index 0000000..e3c12a8 --- /dev/null +++ b/frontend/src/pages/ServersPage.tsx @@ -0,0 +1,111 @@ +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Server } from '@/types'; +import ServerCard from '@/components/ServerCard'; +import AddServerForm from '@/components/AddServerForm'; +import EditServerForm from '@/components/EditServerForm'; +import { useServerData } from '@/hooks/useServerData'; + +const ServersPage: React.FC = () => { + const { t } = useTranslation(); + const { + servers, + error, + setError, + isLoading, + handleServerAdd, + handleServerEdit, + handleServerRemove, + handleServerToggle + } = useServerData(); + const [editingServer, setEditingServer] = useState(null); + + const handleEditClick = async (server: Server) => { + const fullServerData = await handleServerEdit(server); + if (fullServerData) { + setEditingServer(fullServerData); + } + }; + + const handleEditComplete = () => { + setEditingServer(null); + }; + + return ( +
+
+

{t('pages.servers.title')}

+
+ + +
+
+ + {error && ( +
+
+
+

{t('app.error')}

+

{error}

+
+ +
+
+ )} + + {isLoading ? ( +
+
+ + + + +

{t('app.loading')}

+
+
+ ) : servers.length === 0 ? ( +
+

{t('app.noServers')}

+
+ ) : ( +
+ {servers.map((server, index) => ( + + ))} +
+ )} + + {editingServer && ( + setEditingServer(null)} + /> + )} +
+ ); +}; + +export default ServersPage; \ No newline at end of file diff --git a/frontend/src/pages/SettingsPage.tsx b/frontend/src/pages/SettingsPage.tsx new file mode 100644 index 0000000..c653569 --- /dev/null +++ b/frontend/src/pages/SettingsPage.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; +import ChangePasswordForm from '@/components/ChangePasswordForm'; + +const SettingsPage: React.FC = () => { + const { t } = useTranslation(); + const navigate = useNavigate(); + + const handlePasswordChangeSuccess = () => { + setTimeout(() => { + navigate('/'); + }, 2000); + }; + + return ( +
+

{t('pages.settings.title')}

+ +
+

{t('auth.changePassword')}

+
+ +
+
+ + {/* 其他设置可以在这里添加 */} +
+

{t('pages.settings.language')}

+
+ + +
+
+
+ ); +}; + +export default SettingsPage; \ No newline at end of file