From 3fe2863ff72ac5c87c7d5b6e1c94564afdaf4753 Mon Sep 17 00:00:00 2001 From: Buer <42402987+MartialBE@users.noreply.github.com> Date: Sat, 6 Apr 2024 19:44:23 +0800 Subject: [PATCH] feat: berry theme update & bug fix (#1282) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ⚡️ improve: delete google fonts * ⚡️ improve: Optimized priority input handling in TableRow component. * 🔖 chore: channel batch add * ✨ feat: add dark mod * ✨ feat: support token limit ip range and models * ✨ feat: add MessagePusher * ✨ feat: add lark login --- web/berry/.prettierrc | 8 + web/berry/public/index.html | 5 - web/berry/src/App.js | 13 +- web/berry/src/assets/fonts/roboto-500.woff2 | Bin 0 -> 11072 bytes web/berry/src/assets/fonts/roboto-700.woff2 | Bin 0 -> 11040 bytes .../src/assets/fonts/roboto-regular.woff2 | Bin 0 -> 11028 bytes web/berry/src/assets/images/icons/lark.svg | 1 + web/berry/src/assets/images/logo-white.svg | 13 + .../src/assets/scss/_themes-vars.module.scss | 10 + web/berry/src/assets/scss/fonts.scss | 32 +++ web/berry/src/assets/scss/style.scss | 1 + web/berry/src/hooks/useLogin.js | 24 +- .../MainLayout/Header/ProfileSection/index.js | 4 +- .../src/layout/MainLayout/Header/index.js | 6 +- .../MainLayout/Sidebar/MenuCard/index.js | 3 +- .../src/layout/MainLayout/Sidebar/index.js | 16 +- .../src/layout/MinimalLayout/Header/index.js | 174 +++++++++--- web/berry/src/layout/MinimalLayout/index.js | 10 +- web/berry/src/routes/OtherRoutes.js | 5 + web/berry/src/store/actions.js | 1 + web/berry/src/store/customizationReducer.js | 8 +- web/berry/src/themes/compStyleOverride.js | 39 ++- web/berry/src/themes/index.js | 59 +++- web/berry/src/themes/palette.js | 2 +- web/berry/src/themes/typography.js | 13 + web/berry/src/ui-component/Logo.js | 6 +- web/berry/src/ui-component/ThemeButton.js | 50 ++++ web/berry/src/ui-component/cards/MainCard.js | 2 +- web/berry/src/ui-component/cards/SubCard.js | 10 +- web/berry/src/utils/chart.js | 3 +- web/berry/src/utils/common.js | 7 + .../views/Authentication/Auth/LarkOAuth.js | 94 +++++++ .../Authentication/AuthForms/AuthLogin.js | 28 +- .../src/views/Authentication/AuthWrapper.js | 2 +- .../src/views/Channel/component/EditModal.js | 101 ++++--- .../src/views/Channel/component/TableRow.js | 61 ++--- .../component/StatisticalLineChartCard.js | 2 +- web/berry/src/views/Profile/index.js | 16 +- .../views/Setting/component/SystemSetting.js | 134 ++++++++- .../src/views/Token/component/EditModal.js | 254 ++++++++++-------- 40 files changed, 936 insertions(+), 281 deletions(-) create mode 100644 web/berry/.prettierrc create mode 100644 web/berry/src/assets/fonts/roboto-500.woff2 create mode 100644 web/berry/src/assets/fonts/roboto-700.woff2 create mode 100644 web/berry/src/assets/fonts/roboto-regular.woff2 create mode 100644 web/berry/src/assets/images/icons/lark.svg create mode 100644 web/berry/src/assets/images/logo-white.svg create mode 100644 web/berry/src/assets/scss/fonts.scss create mode 100644 web/berry/src/ui-component/ThemeButton.js create mode 100644 web/berry/src/views/Authentication/Auth/LarkOAuth.js diff --git a/web/berry/.prettierrc b/web/berry/.prettierrc new file mode 100644 index 0000000000..d5fba07c11 --- /dev/null +++ b/web/berry/.prettierrc @@ -0,0 +1,8 @@ +{ + "bracketSpacing": true, + "printWidth": 140, + "singleQuote": true, + "trailingComma": "none", + "tabWidth": 2, + "useTabs": false +} diff --git a/web/berry/public/index.html b/web/berry/public/index.html index 6f232250ab..abd079e17a 100644 --- a/web/berry/public/index.html +++ b/web/berry/public/index.html @@ -11,11 +11,6 @@ name="description" content="OpenAI 接口聚合管理,支持多种渠道包括 Azure,可用于二次分发管理 key,仅单可执行文件,已打包好 Docker 镜像,一键部署,开箱即用" /> - - diff --git a/web/berry/src/App.js b/web/berry/src/App.js index fc54c632d1..d6422a0f54 100644 --- a/web/berry/src/App.js +++ b/web/berry/src/App.js @@ -1,8 +1,9 @@ -import { useSelector } from 'react-redux'; +import { useEffect } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; import { ThemeProvider } from '@mui/material/styles'; import { CssBaseline, StyledEngineProvider } from '@mui/material'; - +import { SET_THEME } from 'store/actions'; // routing import Routes from 'routes'; @@ -20,8 +21,16 @@ import { SnackbarProvider } from 'notistack'; // ==============================|| APP ||============================== // const App = () => { + const dispatch = useDispatch(); const customization = useSelector((state) => state.customization); + useEffect(() => { + const storedTheme = localStorage.getItem('theme'); + if (storedTheme) { + dispatch({ type: SET_THEME, theme: storedTheme }); + } + }, [dispatch]); + return ( diff --git a/web/berry/src/assets/fonts/roboto-500.woff2 b/web/berry/src/assets/fonts/roboto-500.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2360b721dc266e107240483333017cb941baf4e9 GIT binary patch literal 11072 zcmV-GE5FotPew8T0RR9104qQM4gdfE08}&p04nML0RR9100000000000000000000 z0000QWE+|u9EDy6U;u+45ey3PAlf(!gbDxwHUcCAh!6xI1%m(wAREm^MF(=mH~=<= zhI$m`dNdwI>7c9${;w0bF$D8N>!QLaFcjh_GmF-Vh!YV{QgU*2D{90?XnEtQBM8OM z^xQURUjoFQVtaJI^XfBjoR6Bz!}og6|2j%;9zIyY=JbGSR#x^8{M>qf1uYc@MxIhJ z1V@g@oRqjxIYvuMv{X0{6q_vb6bq12>4A|)I;|anVP@BgAE*MPqQqUvlsIfUV8Jw! z(@B@2dBCC}sIU6}QkIS&!Jh#5hrGRQ%Q3Pr;Q+Y1Kmz+#niKFo7j=>dDR(Yg4@K+C zF%7u1&hB=;Za4X#6x56Y*1IXq!cGA{1WFJ9S2oLZ`$k(QFzGqeniXDmAl|=s)xG%vBLi>>6xpGjagaE3<~T$O z5(i=8(3y!o?~yZyp(T7|HT;b=fpQ8K>=cBGokDFCb6n0oJrWZUiTuqkn`e3*8AO7l z5+q2mdt4uXYio@{*NmI7LQMo-w+tsu+da0jKs05B7so2L*dx zhEs>Tk^|rr zr`}`m=}A4gPwUJ2NB!&&&@c9DL*oQB*?ZzOv`6=@N&cjMxHlOjFgNs0_7n#D4C-iW zIHA>NK)}$5Rx03LOYggb8Wx8E!^2ay9VBGqo5lX|u_cFeX8q%fsnOC4Vc_RJMQN`% zeOa^Z#Xf?k+bSiJG40AyGQ3{=tx@qE!PrKuas1M&Ruyzv-(cys_=U9s%x1dioyL-H z^)>GsAdc+Eb{5Sji``X*t?C={8Fyf7i>3cOY0s4wKrHu($gBGBg@g6DkG(3p`Z`j5 ztyhH`PYk!%HV#01!X3gB^VwJs4J~CRc`T%4Q~R6I;)<>Y-voNU<-j=N8n-(Gnh|#B zz|`>-m>W3eIjM8=)js|OBe$@w8N#DUpX?z8OSK+n{hac*yR|Rk*}+U}6Xv;K4328l zz61J{oy2(AGjvXRa@C8`n@557Y9gt%WX4ZOjxR!uFQiQ|G`pv|K`Bk|8MUF}O(b|^ z6>apQ4}sekzqmpM>uA=s+*s3Co zX=j{ujs!_EWXX}I2v3;`RhQ_}W5AdxGv=(=uw&1WvqD8mlo~K-$gmNk#*CXVY09+c zUU=z$ue`Ht#i}(MHf`CqW7jv|{qWN-2*ntLfyYPLDJlfKBLKl0%p`G*n|Si3oeTo; zkfex@R=h`yFLVraK_AS)(%C9j*v4mevBuF@=ERBxWvs%b!3c(67)D?e#-#VX_W|Z% z0T$t-wA2^NumY>F2Ai-2+pq(>5~I)dU?09p-&*s%fUu7~wyTp+Y;cMn|A(a*AK+t& za;#H{cizD;BTTc60cP*rST7Pc(rL5wvzfG+5RC5~X3l@;z;k)Q-h*%Gh} zE3nGekbdCu6Mi`(ilPV`Vyx3K&;@<4giKh1Wmth#SnC}_4<8%S>~RmZn3%_6lNGWV zY)h~WJFpv!#T8UMPz+^Ifs?~Jtiw92!@5i`FJctY0bS6y%p-`Ux?6`3du%d0w&bu3 zE3gV{(q=)nU>kN|chk|1$4xe|#Xdej1`~lc*Uk-)W!bME-I!@y;q?{pVa789?#k+04+G$m=Gub zj2&~s95Wc^K(oNa3NJe>9Pn_&!U?Ce#q?3P1r;+zKl1LKGG!x-CVHtN28}@(|Bc&( zX;{y_f_UvMR`0B`d+#HhrOI#F8fF`x7@KyXKM=9_@f3_R+~I!k)fYE`P&>YbqmA-k zf+9|k3=}yi3X?O!jx{Xd4{r{HEnFkKqYI($3yCf0U^bd15Z2h)PHcoXn1y+BNCZj- zluR2eM-;eDAXLIl_y{-Q%kt@m3xMD_Do%nTEQAf1I@TaT612>I3}0ahbDaB6lITVP zNB{}A`e3a49~tNmw12$Q12PVudAq`OM#`KU4?jln2m0a1-TmP9w_GqhIJ%MQziH#6 zSY<0xr4e0^UW#YEG7fNHT4z@+F+^ZJGYeaILo+ooSU)#lGHT0ptnhEUmEk_yZ>t|H z03^)i1r8II3!r%AR8k=a!4e=W1dUw)QWp$}chazkI4ZvHzXWG2%Emfo{RQz)Q;x-i z8B2ro&6quz0J;&kZ|!Z-imFDmLLg~zK?#`u@o9$Ah&>g(e*5FEe+YO!At@7a5K0Ml z4?QA^p8!EZgca3?@Ifhpc{FX5Bd)s!U1a?A;Q0t9Q1Y>A*QV7?bR9a~(xn?p1Xj02 zxQgtK9(Uby-vbClE))-bQ2dagpzt{aToqU$*oQYgo&a}Ra>6khBsAo_CjM&Gosa8& z<`Gk;hoR{)R1Z~nljt|mAelfQw~wwjr#)EEZf!Ck&Bd$AhrBX?H4!))ECe=#xgRVW zZDmXZ0*F5y`~?U0B^ksZcLD0EjZTah^rMjYY#}22m~{D9tBnz0F$2@ZsM4yYFs*AyAm1;-47-sRHr@_zO&b1I)fU zKwTWp(<}ECMK%sRE^wW-Ek8aHN&LJSuL9~L z=3+f(J&tv4TJ|yC2IV85&wO8G0Jm{fJJA5iHb5U zSfrE-_t2%Sed07eT|)%5>qkBE3EV~8pdoT5m0#IR10b{APbN`O+P zLOD|GDzd4fE9+w|ECGYu+%kCFRk=)Ky~5{&fAyX97q_Metr zk)#+r_dlJQ@*Egv?Mh(E+|$cqbX6~hX}+lay4i40{b$pvTr#{JYz?TlV>bJG(DPXd zX`wAa+0^%I`>J$FVF$b&70`k{Gd0M>%Zzy)z(GT_1gP+|wtnA1;9+|cj?>+1u&C9X zC_e4Sn!(dIR@9XZa#F{>EuafHeG)?~({wL3X(6aSa0 z{lO0xmyFITWGG2kJW;Qq-F%y5#h2c*YooU>9O(R^WlSjP0;Fd`p4*z4+%9V+T62iq z${O|5Q?{@z;cjl^+hk}`Lw7#(&C{$ROcXQq3cD%3d0wdLOwOlrF;`)Ij3Ni!MD29V zUBDfVts z)|_(SR(;B#L^~dCtxP*VJ?gtXN~jdQj8)KHBd0B_$zwUCjeKrrs%BRPc|hb z{+(SHh&oamz=aLHK~P@ zWRepb_sNYB3+t(Hm{ZRf?v8hEXO>!=y}eCn>KAts_FEl?61NgcR6x=OBUR zs^6qW6J6pIz&sWphv`?sUaxAoc|pu$ReWL^^TrH~QwvFK=K*IFwh5vx1jyD6*V~xS zFD}ci!_}3fCFx6uGQt{dQ6#$0N%=*$Av_1=C7+|+f$o?=Gczw7v#_bqqH4t{KM23c zPQy13``1*D3r1qrR}qI~)bwpF{*~>(#1j9DCdHp|TJdT77L!wj=SlyNMa-*qk)0JM zA{tm0Eo9F{%)p9NJT{8?>79ok=)R)WL5Qw~9pa$dj5y_8y9;A*VqBk{@9y+>9??}87Sa%7v>M^K(0Q=Nq6kRTKBk32KT611 zV|c_Y%U^OL&1{h3WL~Ade6iJKjvl&a8*vFPAmaNWHX=>&DS}qSRPN5y1J)?T#Xpok zn#JkgO+{^A!*vi^J9v#Sk=PtNIbKhTiqsz7%yG7}?}QZF2MR{81Up7lwe)HP`Wj(E z*%{NHE5KJ3Z`B_m|8X8owKN;}$k)n#P2_F8|Dd&;0407?J2vP2MIZO2-r}Cv#o-|E z*|0>VTsXfSbR!(mwB4DSw;M`Gp}25BD#LFTMA=a(>YU?n93^q~vf8qElBOU3A^diSl7vA)qX0|f zl6VEKd-bQEt~?#SNcdS!fk2d{p49&io+g0;LHZNgC3s*sR6^?i(lP0igQ{6^$Ra1r zPnBEN%jp4W23*bY6c-Y_P#9tkRp5P?7XO*#AL;KfyOn+x-xF-nOdmQ9LZN=qgT@^h z+BHJS26sr!j?pSDpnTl@r!Tlaz1n*t;WoQVs?!SDVhjCPjYb?(-hHAWy7dV{Mgtc` zMM+mL?g%I;5Ot}&0k>S}v4nK?_VX#Hxna6^Ucb1JZf>;#tE9-Nnka=;K(YExrcMq^{DPGVz2T5@}B zMiW-cz~YjszJ;!?mVsrAo)z)vaYX=J5J&@t23*ETd) zQ8h5v`@@JLo+k%K5y3LW)4#F$ss%~CK)YpTm|i)4FU{V*$VcCeD5QTeH18Qlbjyq1 zBBm~-WIAeT;aDWeR3&6N)Q;jAAC2TSTX@9;=&!xBQnEBrCz?A0S9hW?trBw=VvScL zE4H+g)Fd)9NbR?oM8Hy_d9FB8f|jJVtrJlOCYRoamwhv%5hQ2@SKHr83knpDW~P3T zNRCz6QjVoD%>duPq@=Zc;{`IO^({`vGr@8#a@Hd(*}k`qbO_{l!nuz}quxh#tarq` zk11G(3SxqxlHEBPXgz!b?cnS49L$+H204KDpQf$>UP)I^Q%zr&tZM+hQIC(myzm&* zk9p!ht8TAv%A+KI<3B6yY<-qON$&Da1wM)h4eUZa)|P*hVOeJ0P-x)hRfnvNC7$Ha8?t6dW|MwW4=X0EDSo2tNhtrqaCRE$QdU`jK zAH9mMW{#}`4bQVDJkS0+p*MC0c7Sj~^0I%ulf)I-h&$w7^!%&Wn1#ob$h(13juL+L zm+x)8o}Aix%^_pz7e{>_ljwgrD)wzeslRZ8BDEIs*AhqZk9#_-*k^N2f z)lV@4m^!&rKUzVfLw^-2ZpRErBBwB z%m7seyVJN+u+x|me2Vm4+>%Ggeewu-3mjXL)XMuG6h;x99Ng@zd|b>K2z`BOU~GQk zPEBEXM3|Z$lEsi$*44R0C}u(XH?8&+rrt;2Chs@5e^GF1y&%#A1b6JWR(=ku_$72*av-{!t zr}mH!SUTeyGBoIMxzhVT%8@vF)D-T`K7aOzaA*?6_v0$&!rkv7RFa!tgmc1IFzT4& zU;7X9qn)`nO9C~TGM%Hu)pTp;Bvx?qH+NZX+O<0RHnJAK-uSJipdg92RL*O%z3*I0 zjq;@KOs$e+(-5o2l1Sai^Y6%->BiKLK~N1&v+BJ+-UC>A$7ENo6{vLj>iPZwEWLcP zszVQq;h~Y2pl|0L;kqJO)KsvZai^lMV8#UMV`9~#mx)o38q2t=S)5e z%V{Uhba8O}0@KCLmwDhwKu#l{bXhZ=_b311e#3?pE`fKv!_9nII z>@yu*H24qZ>zzN1HNl@O*LzqRn(~?@MWpCA8vN#7c$u3q7%i*q#Bo677bY$%E{H;n zO+?s0@kyu)&D+`6n=l+TMnC*Cs{Wpe^2M;6=$E29ftgif+wyD zhz-&A#p>w%U{dYFiP8DRkEQQ&Tb$Kjo9}9@OU(C_N#oX77z_6v1j9W)PT6lzNUCTXV#f@s$iP8Ew znV6jHXr=971{%kBe$SsBdgPnJMt%!8V2Jxfb`CF`WZuQmBX5ROTuOaJ$fyjCY(iQ< zvCTfz7j3wV_Mo(nc~U;IoJ0$MV>+|cw>yu$y^MAU33UjGZHBhf^q5vKI`MN=e4y1g z^c(|l@RlvmFY)%H&r-WPbum4Z0!`|)dVE1h>^-pIT|Ink_wbFVzYCe^@3w_eZUd}I zWu>7FKD2}=g3Bust45_qL^@a03=dQIl_+>htLv(B?;08CA`rQ;4@2eJZ@IkfdP@V- z6TjEb58m~SWA%F@{DOIZA#~9VZycNxNUaS|Pl(m^A>VwoePL(6hI%g~PdoapW<*{X z^&toh42GoMw!@?iFzyApI5_1JMAM_b*S1pAXtkpsjqFX z)R^tHkqk#A*6@8;cr$Z3z?Dz$A^J||OitwVKL`VvN-kMGuIJPr>{e*hXNo?jo&^>&iT z2m`Ql^DO?P_@3eUQ@9=Mjkt$l1%!xswdm{QgnM9g^6%=|;cnj;4$>>Y&M|)b1~&J9 z65rjgPk2Di){0oyqFf_W@4|mF4&P)Nlwy|{_%&>pbw-)&A(%UE? z{BO{ZGH&_2!9&K6uC9Ds8RenkH`*89E#3imG3Cyw$7gwh{r@X3uW(wP(B)%?ul8@= zp+%P7yK!yE?L2tu;GiyIbj(3;P!0#NeT4pi$`da|2aQX>#oNUV=lo)!ud{FQIqFu| zyPHxH>8%RbAFmc#?ON?WzQF!yRR9Z)2EaXP0Fk+yis-+ytKF8}mY-+Pe{U*Eq<2eq zEZrSl=7L8iqvEp@)sLRmFb}kV zrh+F36@_q76cmYiYU)HiMFpasx*AarjM{8Ny|Kni=;Xo49MR9CpKl+XIQ;hDi-{w* zk1mYPmy7oNk1k!pIaA2?D+d&SNRMfS-w{QuQn?;BP%nzB)GS~gq)q1 znVol?R59M?TAG%TnXQ-*8RkFrko@)`#cyiUZ{h*@{X??f1RUcv#=3A=jD0SWNRud( zxbXXeY@uwLY|(eCF>Xla5M34>UH-ZZI(;KO%{DamyKNjppT8b?HM0W&rJ*U|8BWHA zNp|<-?@K@QcPRGnX|yFsX(W3z^GvL4v&~O3NZGMrk!en57NJ%N-hDx~MbiEzeEOo7 zPe+-$S>}s(%6;2atI~FJIUVG*2JxjT3>LGySb*4tETjbMadDadIC>V;Sp1=hVgs0 zdc$$L!+kWLzcdp^`}=f8?%RTMFPDaaX;er;gl|AYSeutjELlxQTC$bHr#&tuwK$u4 z(N);QIsT0K@y1k=K3FPC*}%o4A)}bc77eg^#eChSTocOtEih+iT4*L*+(L_{gsKt_ z$^_KlG_G@%_Ej0EsT-CjWobF6$ZKok9Uv2A>)3Br+yo)YAgQ?gZDEO@e@V&PvT{U!KlRg!S(_&xX9$b_sLVZNa>>HIU-Nc8BeFa_Ds;)pJ*SCdIic3+~7C(1E`GF69aWo&aXY7FqXaS8bx6 zT|ra!Mt{Gz#)|?C-uBL+YaUkD8gh5bdf}pSul|g7>=Adol|}NRtG7o?_5WQtoOi-d z&UcCQb5)2!7uwn5-WVO>-el~aX8@MIJGb&RIy?S-dSW;N5xb@@mo}#Fzr5F^D&ndc z#7{VVo?zaQXOg1d@~UUpr1_MMWdB6W33*prOARS4yS8MLWD}d{QA1OE7n2_JS1@wn z`;%TpFnD23qmSGytJrAy^q6SxPTFb?M$(LbD^pR_+#oy2#^~?~nxS9Im1xuIvwHm-Qgqs_P(#0KE6Be@!6+uJp&%xs zAs{eiqzyEJwfCOyeeKZH)eqLy)6|eM&(O!z+{S}xXp`|uy1FHv_W)m~->kktTd=HaBZQW2B-v{FU&^AGPYe zuOoTT-J4zU3Wt4l`!w8_@9UTayHnpe#piz{(Fx|a9sS_t25LQ}nKdoDoML=(9y zg3|obJ`PUaEa3M#=NKE&92M%~D*JAZiQt-%P$P&-5H3lItE#)I`ba^Qjm_PI%kozw-M%!z2{urLqr z&@i`?P9Y)Q?qR`*11Jl9z1DCfiT6FT0S?@1dLpl8f6am>zyG!Dg#3*I9@w@UEkyxN zVxkkF2jk%65hkQHvo%gWc5Uo`{}cH`q8=8V^gQZWSlz;0vxBl7 zLEPS8%x=7ST<;d+%gL^6t+>b5Lxgs8ZYKBzwZypbN6;%fNP9Q!TNOz-n=IPD4wc3~eoDErVt;UDw0lI)NM{pLj8jR26t_ z*Nz}T`Zw6U-fT5!by~vZ0IbNVDcZ%*QlrC~YN+Yeb;xz)|KGg&0K9owVWw^^M#r^u z)CjUN1VbeSWerfRJ9}*C?B21#jRymC=*+m^d_Usf2kTKyU+%A&c>a4^n`}}?`!c{O z;Q0l>1U&zK6;n9Psj!m60T++PTlpndiI{=ttzz+4Au+|L+8BuTZly3fJjT|z?s0`NMJFpw1H3X?6-TQt3YQL;CA z3iwT?DWIQ31G!3ckZ*%_xLKr&9b%_=OY9=u7LLe25JL_EDgNiAa&C|pbaL1n&2&7; zUqdOWKlGY`gR%_`h1E$mHBwfxNhESw3QuG4i=3UKX>F(=l#-G|uSw}oa*JFD)r`VW z3c1j0q0pZcZicj9vT4Z7N${c!c`eG2IZ=kZb;O*}oPQDGBCjv-g%?70X+l z2toza3mw3vvER-lfE99Se$?pnb7pRCJv|ip+ZeNUz|gwe?vbArSM`>5&dN1v8yM?b zs@>u9Q99NJ*@OIS>qg8hdHXzt2efs2`9)L^&x@${E}z$Uv3_ZT@2A%a0r?C(F%dm==Z?qje8GDrAGA69^SomHINZy_rJ!OTlxX*UXa`Y2Mq&c>Gr{iE z)~^oWLiTK5jY-^`6XWq>*{?>-?bJ3~2d*UF`iL%&E^j|%fqV#_xFa3denyM)F>OdV zD1WuDj9HsX2XGdc1IMG-&v0Fv+`g>mq#7}@_e)4-ySw{K5+4OX5k3{UirC~ z1mM$Q_TA4D|GyX zLRuwWxm~Iea_Tv8Wy!_<4Ett|r?Ja#O1v6gex(`n>;u6_?WRLkS5vRJ(AZ|dBH1Gw z)-Fc7@k2c8Qvb?&POrY%@2Z06b^%Y%O0sb|Tjw#=51C{s)MA6rcZIziMy`}W=j%yT z3wAYLRF!00sd>}&ndg@-)Kq5mitx-PIwR4&FJQ6B=)PX0tHSFw>=)9LdDeI3DE-`o zxgIOD>=S6aj#iu_Wf{A*HOn_CW|a2FN3@xuNL+Y}MnOzq*(=O(B;Gs`urV4O&^qq zC8F|ybT3E%v_+J6lHyY@_w;~FnkHTN_!Uuv!c00(NNM%baxlN_%c7LIkiU|v8Jrpm z|C*?xHWI22Tg}5ILyl80DVvZIWPu8L%wZj^ZJ^unjtWcr84lPmVNdy%eiI~ z-!fQtjhUo*R0)xa(V|o~q4?u0+F}$1(KLta-ku$F$=uaoNEV4Mw=7TGiMM3V7NZ0U z#k?W#?Tc?~d?xm9&*CbnwM$e5$0s8i0#cPlfDtSNU6JF4eIskhD=>UWX zrvp$pg*BqvjTjt&bqbB)5L;(pe2m5;akj11WrBXnQ7^vX)^9YU%RufBGi(-oDgHgoyUF^w-oQ!ZoPhWo83kou z%?hT&UO*})2jL|W3AMysgEDhpwe=Ja4EAPzV(tSE@XW5_WT2~|Wn^CX1s5^NpFHQ) G*P{Yv4gdfE093R904m7<0RR9100000000000000000000 z0000QWE+|u9EDy6U;u+45ey3QAle}dgbDxwHUcCAh!6xI1%iGDARDwr1>=|lY#ab# z_FE~6axqGxC>@j$#s75zH-^ajC{-A8uj302J;$f06~!{K2!{}8qDGs(Y6YcG9w`_w z(3`J|U49TEN^3PFJO>BwsZ52VQ-=~BIdcY%^HFo5f3|ra8=yNHgUQe>q0RAr;OEx+ z+lW$1Fo+Gb5vdUaBx-}+7?QD#F-C<*j*uz=sZe61O(?C0g^?%cHY4iTDzlTn4ZeIRdQI*lj z)y@`;Q3IS_D8&x!h@S=rFnf4qvrKo?w+A}Y{Bz`!0V4&WiS@1z5~mKL+1}Od&W`p# zd!Uu>9`>I0^q@UG96d<}o$5X^U`!AKNzeK>A!HCbNGSdwa^%c$h#aJ$GxLytBEJ-< zfQR3(W=&JMG>Z@LQW}1q4s61U{|sn2oDdFjt0*gi`?=2$JP~Pgt8mBkIs}nHf(;TR zEa`DRezjrFkX@+>A;wI8dkj~X^|>`J5EFHN{Nlo(>;M4tPy`)t62gopM4UK?6e$pC z(jYiE5ZSUJ&dP&m)dA&}e;EV{5CH`QU=VQ-fVZC#%xP}z2nVmYa3&nQ!vlih;Oi5~ zf`gDJAe;rF5IaZ$zyJ>5i0cIU3jubl!vH$Wlr)ddqf`1+J{!;WGi~;qYPx6lpJ|=Z zBYRG->BIfr%<7AMb#7mv7H2O4=k6Lfuqa)$&Q}-XG(4Y27he@3X|wTo``NRGHjXv7 zqmd4BH89!_YThZ80s~H0s-LxX1B|IaTJL)AVrnV1YoqJF*rV(5>#Z4_-zw^??)9~A z*!x_c%8FsdN1kQm*(vs0)V$SOpK)Hbx1mlct~PyW?LKvv)iR*5EWX7@|7oh=+q@p} zXtzr7ZC?C~PPfc&C-N)ek`rsbzGwDwZ-JK1^W37Rdg>0mk>2&x@7S{#jkQ@lG2%_O z*k|d3w??cz3#T1CP+RYftGrdp%DHYpL9~I!3s`@@ott8xI}Aozd(;3kCzg@?KxCT} zyR}%E5*s*hZ_iDZzs>01Em@<@F!QHWyu3R5MdDYaI6Fj11>felwtbV2`=^(||J`21 z6Q5h1Tcc@|>MK!3tEq3TwEiDbQ@hk2%!$qGYMGGL+zx6!tX^R3YMa%E9<_ktnT*XS zY#P6#c(&Bc=(5@!^8wX^-Z$uvd^39K5gpArU_nqWehPK0A;hZ4CS+wvv0TldvB z-{VKC{yhjzwY)i^Hch4%a@jiXz9eAQ=@j<+onyMHf^r75-h4jL}FEx*r?f`YBx7R@`ijb zOO~xzjgPT?^4XRzV>HEjIQNNL3sW!BGNZm4=0dUT<67$wDHkJ7?;zM>*r^@e4V+sZ zJg1&=ZW#0(4XImV|46AGr!m=$I^V-E#b^szq>x$?LDwFqD3W+T3Ra{DQHmm78SQ{0 zFqCKzpzY-1H)CNB5>$qDxGV>b|;=rgLIEWRLo(DI6`?SQ$Z)F5EUv#ogy@-ByB1}hYBLly>#hrdQ_A? z6?T&DWJYN&7v1ndMM5XI;`5`>im z5*nZZjE>?H%Gim)4o?X!y%X}j$i50)hfr4r_aRi2F~mk%ur z+6vx?U;(9+pl)3{_2BB&r{91)vbTDeKEo2XY+&DTPkL!3q zyQ`^#vS~M{zArEUG(fttc3b2ave^^sh!ajY0v3$l6Z`=e_No9Z)#LOy&bdF+GmIrbi*g`^Y z^DY=5Mx){iM_+KTi{)*4y7x_&Sj-@&!=}!GpR292Y;;(1CNOo&V|c~b zG%0c^weJg&u{ekSH;42&f>~k`g>1=b1-I@-|@f_z=G1QhRsB2tUQ&ESIw68 z1N&3n+9{4({tk}IBbuve%3z5SiIr>QiO#O&JW)@RP#wmIny3eAq0N*_9Lu8=#u}o4 z~=DI;#;Bse&OV4C`jB;ZWGHtc9Sy zPQ^>0rMaZfw3pi9ErPS=7Q??3RK@XZMR7a~b>5y}+zv6JHJ{T>5>@ z-LTUTwVrO*gGSA;a^x<#k}~Qx#5OF*=zJOda181sGc~c`m)tNkG~ActuCI4R+ku1i zCc>&LwehsLvVx0%s@!&X8MfNi`2PbHV}N)~G@u&Rv3bZT0grp*F| z7S7NXa3-bpHM}%$2l&Eqr|)y{m#bmqtoSFoBB116{38BE>Lh05okjGCn!jG*ti zp&#pa8HsS_NSbC=PPmT79(=Ktky++&DU}uIvOldEZ+@rp zQstUtHItXrrcedDGIfI;N6kHDx~0GjT*?ehqy*#u}!lIR+)y%|Eysu zXtc$IZ_RU-2&epz`;ij3&a#KbO+Yfbgm7jb2S-D#>(jYIWj?}Jw}`5cKq87`--Di- zg%>;Yl*(O;adL@-B0?siye#dnGnyw9D7-~l6bOYIB{297izNh59cgRBZLxx5iB&jzlw(}L)EZ7)XV$+qSSGO417e|_+_kF|^}{mjfTe&W zvDfB_;>HGpLzPc&BxaJyM>RwZNsVx3Fb*vNuF3`OL|ZWMs}$?h5DFs^S1aIgCjt{v zOKhd8KSpK{;TYL-D;QORd)5q%)Rq7V3ewXZL4^{-5Y*rZVvU=`UL1}n8SlO1FYnF{=gbmisP4&I z6&EEUN}+m7SnNW&`@_fx>=n6XtVTVIWbpJUhYBjy5Bh~IUk~)2ZN|*}vJG(=eTG*t zh)!v5LYA4N0L5JqHqw(aj{b{4JO2R1EsCc^8h@2)UXVl;fhpF~{{Ta*`oy`YW{GVk zbBk8r`;Q~Fj+#Y3PaBEdy8$F_(n$^oC*%d#6aC2DoF#QZ z%#B_wyx$%)w^xIQSlRs~6E3l!wb-~qrelgfUlQY81y0bEc^+r_&v5X^ij8_h10>Y8tDJMhwYenu~ z^23G6x7Bwx7%<7l<+m1Qvt|#LPO@2FQkLqU4;5t9T0~n*+mbu3({Rv?0raWa@UvO7 zj7M%^u{3GOM<~`S)f=ny{b?zXptwd2hdfbliX08eA(v2PYFgxib;nBfrcat))GQmG z{qamDy5b7rT95^qXIWA$u&*L~U3xa1&*B^1rSgnGeEbjTcOYa0nn1Mdd@e@gdvwfq zIdNW2mf)>RA%1?N?UP1ll5 zutstvXInIu=ubppAVxlDsl}W~uKZ;CyYZDF)@!6|c^F};Ym;s;Xv{9v<5dwE3%uOa ziO6XJsOU%aj_-M2Jh+1vc!laqdzbz%PL8j0+;}71aACiV(&5i2#Vrki<^qy4)x=oT zl8BYkot;3mCHhcRKTy;S|&sz`Lq+6Jg*hRUi<4hRPOq6gRm+(`LCkz=l-xsK54=VQWsSSo>ekbTW^h$|J1`i`s0doeS0b0k{);|4Q{PjH;$PrxMHRrx=I zt&cx-Q2T{df9bybsg2r?-81OhOb_&~LACn!d3OdgJX(+ofWx^uGd5hG$OCD4^6@us zy8C$xA0L*RFPpKW2 zfQ%KMmpA?6+I*GbkHz-H-rT#k_FQu5296vb6#pzOo2M=tI z>_4cUYmCA|-fhY|l`RuR?Lf`QdmG4VUi9l%@fGQB8L4CF1UY_JroXcV$(OM9)I5xM(YYwBF~}pl zI4UNh2uyeFP{i+0RWzNPU&nw^`f&!m$z6r6me3b>lkd%>z}vC7gnpW;y9%v|arMW` zNAv$a7gaa&3JLRY4`F+I1c!NhGD2iuP+bDa4laJqr(EdG&U8=-H)pdd##Z>>79@UL zNY5=?C`b^2u3#p;&Pv1_wm!#-HVm8lTNKBXgf@7d3ayQeVOD}OU1xeX{(i`&3*QZf zzUxVwN#{$zb*Aa8lxA4ShI`am(%?FrYtk}Muj{YfQDf=sir6V2!(W~}m|wedjcQfy z>1J6TL>N#4}&Y(sI~H;Eas=2V!LSwHN`kqZq@427$H-gOX8yCPvsp<3=^`HM(NX2jj+3`dZnx^5 z=o$ur$QyTxxL1~tM>el7z3+vSi!p|r=B<0c!=2l1bTf?4q!?0^c+$XjR_ya=kG*W~ zeTXq|L#DY(2YKlCy!fPK`&G2!_q`kYB@(kDV$anr!J@}f*IIjrg~d;?SNcF!0VOtx z4Kzc^mSn5M*f{}8kCq4U`E30BLPXvQvi?Ll*d@TnDIh8YNEX#?^$qVpY1%;e>o5!l zXL2L}x;~%Y>gZku+rpLh$4Z-GbH*(k&zJ4qU8+B~J6%t}+l_YfzcCpv^B(X2j^_O$9FrJI3qGC3LL`8E^U|F(rAXJU8^i7Re~CK%yET%Z zcJ05xWKq&f56Y4!Qwap}4r45u|4UdxctB8ORNNM5IV4eIT($gNReof=i%!4%Z%}p3 zHk$P_mZpc3V(k>BS_$crA7B)D>39OrM4Bt??ae2ED zY_ z4qQ2MD~|~~3hu-R{(-~&S4F?MwB7G}duomybSv zfQ%*3fyDy+I|Pc3vmYezy0EC3O^a-=yLTzK^UtNvka5z=+J!#j1NiIjn+l^K{UN?=|oOmS%(;cyJ_0`8DsWYgpu1%521IjB4+ zN1c69BkXMMv1~~rP?@*)JB1K;LP*rm9sk5tp@SHS{{@b$-u}9bjO;`xel&7GU~!9F zTR;&Q?f<=aY@#+W>aYUv0*^>|Fz3R^vJ&=tCA}elqshLAXJ_08Z;6jb6}M%YPQ!9e z>FYnzNOZ3VJ@*W=%_BCi*3j)$pkP|86C+|raIrMv4j?510vReoXl zOQKqX(zSi3bbM22(H*e6WIQ~(+Z$6ki0ksObM`o+?StTe7jRIoKu}PVHgcj^S)QU8 z89P&oohh(ak`UE@W3u1MM082}P}1Me%4OWvXC@&GuTw*znx(`!>yC%@4U3@67cp^toF&x}~Znz%s7alSFYz&# z@K$Hr3^IKUH{fnZ_K4RD`iUce(ZPmL_E+M4jE=@|CEYG#99h9-RkACT@J(f~@^$J&lJtFgv$1Tns3r_w5w z8n}EEPt%K#PO<+{_;GY>?)z9Z?xdYPm1Jk6uHs5~a+I|9J>_oc@1dEj2i`I5zT#+W zx`PYF@g&HUnF?N+zapde&`8Dnh2tJq6&^K<+WA^R_#VPYxzRl|g^W(%u60zEXa@XI zAcz9md-|!e)I32_Y(^Q3#n?$@Z#roU`9Q^LxCyEq~KB$sepI*Bv`%r#S>iK=# zb@?~bZyRMUzd{;C5vpIHLtsED)Zy9#3qjq0CnrIln75_4in|~3%2_~Y!wMnzL&!0|C%Prt!`BhKcVb&KECracMR)vqo5FI=_92v?IO!bS737)I);2OELy z#!4wksdcMGsP%j`p3#LfBKt7=rZL|!KpJ~H)ZoksxY{E=`2~@CbQ=pUXFSiaBP?!N!&$IiDixKr_rU{;KcDTzf2^SK$~QF%Yy@`#@q*)rkK zD`N*`kCU#pQHMYpPsQ61^-o%nXjYbq)|U6xHSLK6U0V`fqwv)NW)B=IyBiFz&kuxk z56{;&bvBpPc)O9E-TgVhPKXNNxVyO6`?jRFmdS`+XsQ!!s+M=Gk{4|?6z`~zcMxrx zz3O5)nS5ZumkP6YZnzRBM#hL!*IdADw2Ef!kQkjBPiM8ob_J@Ri8LarsdS3ax|6dq zs|zypeD>ITr5vX0Z_i>_0aaJk8T7=Wr!ff^O@R6o`kWm^Z;$5xmd{-sD#&{(y0Vhk zZ2dVERx__jxoGuRy|t0C&H0QhOHTt0b8~__~gudl5S_}+8F8yZ+4we`!jH6hHV znw|W3Q**qAsTIS*ibk}6qm*vq7LoazX}4u}>bLj=#(TpX2CA4ja==!hNIQC|b)A@t zyl_6Vv_3h;+#({_sU+MB#3aMWG0uwFsz)t&v< z57$P;N&ug~PyO&s&5^NIV%@cy=WB1=YU;-Lx`)`?x(0){y^ni{ovk}F5YDA$=r`4t zXj{mTl+IV#xVj+rIAmn&@qwi?BCR2#CVxdv9e40HY7!-$a6p;O5G7Vql$8o`L1xG0-cc_$_7>qM=`1{&;n z)d(fu8_k};qUdYe8>^}{yE4OtnMpF9P62@5wdW5HWLkEni+w0BDO^fQev`HGO>izN z*h-}$l06(}6IS6JVNr6>b>YK_-n0u$>#&Q@O~@r-;iW0;n0()$xPe+QRnx^RW18em zT`5Ehce0s@3)#}@6lJ|oPn0i8fTN6Aena4csZ#U*b}6lU+3&F^X$X8cSA^9`J#2%P z&_BkAXdbQz-|z6({9$@jJ(zq+o&EjTzZHP5UO_SJ+c>#!fDg-i*=ssJU<6O&{92>gPXyI0jG)NE$bO1ycsIuT;6mUI`Q1r zYd}8O5cmH|PSwiU(v0%Q$m4IwW^MjXc4}e}H3RFQWP6pU^UN@2?QCgw=>y55Xe?hf zyOGHVtM5A((ZryKH4(VYHa=lH8KHZ9+!)vkW`VMYa;wK;OzSk+%wH@CuN{& zpJq;ix5x9pMBhLyK@~lDG-eSm|7XDYO-nmXVOq+6B+dMgkZ1{`jmfFIz!@+ zz6zrK)nAQknT4MU-~#g>mV2SKh_S;I7nfAi9px0a5@gc()b{fI?5ONW6#XuW{?wD= zrKjefhc6!TZu|CPZ7S^&?Z6=Y3(Jeh^7cuVtBnjj9a<)ryxE)5li!oYtN;TAN3TsU z7|bIdA2sCm_m^)oM+rwh`p5)}o}BGhdNltiFjIk`02*dks(|Hq8!5Xq6}o)%j>xr@ z+4%~xJn(!|Sct~_`>p5PtYNaN^!f+d--F)sw;tvI!|SsT3tgUzc(7CfEAi~|zF@>5 z>u%qD_vqfbYxsGK@Jx$z(2Zl6+*)2%SeJt&-4-u7rgKKa%U##iP}$9T#&!0>tYtrj zlW?^56DTZc_MA0t>Bb>*4^Bgo-!uF8pA@nF&^ zD#_*)6<#VEtJ^1>O-+mKZA~j|ZiPzSr!Swbi8V1iWbuZS8Ur4iD7q5COe+`+9__%j z>`=gsPqwU2Kz-`KrGc3L2-aef@}j7y@5=gf(_R14^cP)40NW5B1Sj45o2Wa~$VZo# z=sJpE-Is1%@x_Amjn!_61P~`EilpEd3SnHdra{h^0jq9wU^<^}~t=3>m zJ(Q!Gxt1Tm`fbDd$yW=!(A`GGLg!W#FDzKO%Gk<6Pl|VNn<{U(w=1^`aY5Woy2%|f zSMHVj*?WRa=IMF){l)}qQ0 z_^6K2#$^$C(bq1+N99PnnDJhSxp%AGpuS3dy4o{|-00T6y~bu*wvjIqtu`(Y#22j( ztNe_Hc#jXX)4S#&tIL9)oB~cuRm{&eE*0d-UFYzNjH{r$CaJ78?r$AEkf%vk^2shD zAB(m{SLZY~n{wBLuG&dZWL@6N4}}m4hBXmIBDscl32YFN*XZXz3;aV9wt8vk}F(fwR*G5EetCQz6zN8Y3^6Vo( zY`KQ=TJ0UL>HW^XX90jWe-cv$;NwF6zkTA&X+gAnqRS%L9jNy?yCPr+lSY_hJP_Qv4p< zPuxqSjhGWdqDAP$-_ln-)=pvUbnKy%;xw~VRw{_CKOOH!ST$dKZ9}O|k5f~Zn#pkA zu_1zIYbz5}Wf|*mlUyZgYeyY;YjVz$IMSJpCcfNYmfPnKlBvk1i*FjB*mjZ1FHre8 zQs{`S-XQiTQm>#?iy`qSjkIK%DMC|;ai%g(3Szt_n?=kr9xddBgyd*QbD<$GY5E{E z6VQ{xcv{M6jScmcr5ytVCo859k@h8-6t^oe&@J3DnyCb@uIERgsbGY$OqBMzPf}WQiJ!FW_l8Qe3CIu5)(uucmmB-} z#!s2GU3C2m)HW<&lIA$Y@T@P!F9qqL^b>nebk%6POf7-)R+;+q;zB=?)se^G3Xh-d+)2M7Tr6_8?!2LiAP z2&g^q(wkZcgofk{oC(P82w|Qab(9&qqe%%b#9((G2W4?yd41=(=YVY(0Y%TFUMqDNV%HT zcTV~dlFSm+`^SGh2NxgQ4IxKH2%++IPDfx)WPl>_%>m8re#M9yVHBWX5-F7uQqmzc zYE(#$!GMuzK+B*vR4P;~Flk9U{qOf{cK*IAwqjDDC8jfk3dQ5aJ4s&d3sj+0(3+L8 zsJ9Ic@bxqRj&xcxvSgUiN)JRew|CV8t#)Pm?h-cWfCbY?Nhc*m^8`hE-+xD?YzO}N z{^zvdk_cT_S);p6e;ASQsP_Dvno$IIJPP~&ud{4*hrL0Ea1?f#qTRD)xK2@}sH7)p zwEus3j0^TGkw#1M%$%$}i*mpV*ae6x9D9M#U63o}lA%kPlcGu6Wy=u5Kecq0&C62y zr`uBH35js)r1tmjSqQ8Ox4tNr0@=PAmhLV|o7MYc2siA6g8NFuZ(SHmdv&O3N)_8x`9M{L4Id(#c z3z{8YwcEC0vlXzvg8bqL0_S?)r7j1T7=d!hgCLtjg+6M6A3#Kh zpCCF%&NA_QtHgS*UWZ%28iY5ltr+*4b~#@?m?V$uM=|ElJ!Af4XnM`=Q7msz>cweWb83^3quL3f!KN5LMx*6%ltBZT*i@~(q=Qg!5vl1 zQ)EAEqh2>IUwvJtM?k*NIa1o#d0llxZ_&-FC-abLK60?1`tAY}&GI$6N2b_rXV>eD}jo zzx;+!%%?Dv@l!#dM$i&L2)+~|g(n3Oc8e#PHY65)fFgc+Ez|Ec1A!sV!xGl{ZQ5er zD-P{&7M7E-@`fr_qw;v#n7|~aFpU{`IhUC_>OL-$-+r$>Ov4eMbj}Q2WPx9w{ zSbD>Lh1aN?(hx9qHQ#|PjB2VM&<6zQ9AB=Q|4i)VO_7g!A31BVB@ z?$b@6-wp#{i1VrrFfJnDo`nJ{jVH-R0Pdt9%cOX0c-Y|e6&chlx*uW;X zu#KJBqZ#35M}{L_IF=cE2j1fYKH^g#7Eh)jpd3}Gp;++-Z}0|h@J1n!oiQP1FwbvU z65H}PV)rg$@4*Lr#HYnaJDOwcde5;HXq>r4t1sLW8^x%1x%~U;!ImUIXsoDfVl3O}4_6j&?-k5CBzK@)Wn z#u;*0DN;wEN8dh%qJxa1hg@LgFdK<*h#uv{#W7jY<4RRIq2{j=#_W*v)+g4!`^^!G z0tWW5hXdw7j8Me1&vEfOA;d{xNr;W4*hxu{tPpbIrzlQJVxz46RJ50>c&Leky13~H z=bTvRR|H}lfH#<93mBFN&8or@?*Kl-8Y2Q{v2qcYEYGrIZR}A_<-pFBiBzR3A;TPq zTNE*F2FmR3c-=M!Yu;0cXBM$pk|>8|FX3!N1#H@Z_x3i%JD;HUHL?0x-$N(7@NV$q z)rugg1Nc2oY@nQ0k0R3VA#VdkUWIbD!bRZ#HdqmW4{q4uNyM=s#Ci1yZJ@zypjjh? ztvJ|MuoOD*z;AF$MJU-q$+?XvRzi?ec;SZ^{tbSkctHT?QFMZW6?Vgt?2rkOpktl? z>>DijI`5x4)`K8|2>P0Y?bP#w1A+X1>%4;ObNzhWp+upobJMv&5rIHIH`tR1eYXxt zaO1+B*65|;`)O?Ql&RB%Zon|t?|W(%(1B&oqxFOk!M!c43@pGTqY# zSsZRMYIW54)q+64->z_2uzoWr-_)Eg519!TfE9FNWni`k2GqO6TEtG<Q~N6n~jxVQoQ zTuQpEY7;mZ>lVZQ-)8!t3G(j)|G?>=iULBAy8&(YPA4>jXjiCW4H8N9VeOp@-LNV5ZTk??Vc~J#)k1rP4j#3PkW#TCzqLU)M+xKzP(l6$ z>5%fP@pt$n1w@AwBC5hrhn_9idnZ(NXl`EDDkyh6sWYzAf_HkIAZ`2a^ z!pX)wDsSKX6sewh?uA85(xh9qV%3^;FOg-Gy$=bBOrL%6)i>W|`2m4~hsyu391snF zG6DDtEd36wc_cvF9+a;Ex=NJ#m?N;zkX)1sRChE~s@X%8h1O<+a#WilK#in~hS(i1 zF9cqQ1PWt0##;olqXCc_r4*fuL}gS+9Sr*rqBqGfEv+8Ri#G3$=7aP4eNN%C0%sF| zyKaS7xCORs-C&T$5Ja@uRk0bn?nD(`P=C;iPt73J40D7We~~}PrlNy4UL6B z4cJQRrgTD+g$}?jj?JK~v^1`hese3CV?F~P4y&93U&Xx2TuCo$!Z0;VuNbOi^O#G0 z+Kf5)`Bd5jL?*q9+?jVSOP>iDQSJT&i@C?UPgZIU?DvyMnu_JnF1;l6XDb#3gJb|RRe z4Qa5BSNPt$FhSK5sA^mLd^;3Q#lAEn(~=fHXc9)Yi)%<2$e~Vv!kO}3TU!euh@X?R zsZdRZ7ei)3b$CyJTF^!_8W8u#t#dVKa2%Fw^t=o2mQjtV7&*N>n-0xJ6w|19^-&^O zdy+<2kOO<&Iw7xkvtf(#otj*N0tH&`h8f~I=8)lJwGUYewpj6?pxa5DYfdXvr$9-& z@+Xtgxt7E{r-NVrAL&;A4U->6zt8m4PR6JHKR5Mp))Jk3aQy4n#~;KboYQS}fkh>> zJI!6S?X_QRDxwPb(*D!I(80=GzdoWRUFrp(RIlV@HS6ODO8G|@eQq4FG9!T-!d-GV z1Wp)P)Tl2cT-65mN2L%xsd`zQ@oC4$P{^}!u^W19Uc9g>^$&Z>9(CdQxB%cU9NTOT@fB>1y3R? zoG(ZlHTt8LCE@9e=2J@MoE{5hE_nrS-3|93e=?4GMTbyld-N%AXG&RgbDMrb~C02IQt%DIUi z=QN^ayHPZE8g}V_WiAF0EWf$hsJ*U1hqxr}5%OZH+thcQ_F|y!J!n}FH+E~I06A7R zlT~@RGtpfVRy5$zQc3OaRrMDB=z}T$ zyN50fN*=*tgSD7?_5fv4jEz^9dK%%9^O~G1oNNsR( z8@9IK)wKjD0p1PQmV{Up*i3OXb5of?85R>bjyvz!eGjX=vfzHQ3yh#Fp`NS_A(hS# zp<_v1oPEAwT+33L2bv~>Q`#}-7-Hqe;3&L$1Cyu>p}q(CC$?w^c|=Qcs378QNgGSJ z%1)b01+Mo?1*a%+(HtUF^L8EeRp7%J#n4WF&chyed*rc{VZnyUj8ZG`fTj?}`p&5( zL3nYwX0;E+TO-y={?%?h&PN)Sz3SKVt-^2D4EBlYO?WUFge^?*G*3~KfYO!id zn51Q#se83cuhJ{WnJedY*CEWCy=d$6|1d>g*N!$gplbcEG=a7V+=MBwW zRlasnh^eRiaN}Vruf`_iY24!1&3@TQ=(N3x>&N=y+)_vfDIdG#ID#X0je__vTC!mX z!TGRI7siuayEBQlcl7S|p+R{i5udm8P2&2Utc-HzrRcm$+2Z)oGJ*ONjLENi=TeoXzqm0swbr_{;YQ^y zH1#K@Q+chO^TR>lhFOybWt|q#c*6=5(O#8h6E)v;C?KN=$Gno1sdTaaye{d(Mvu!o zMg5+>_`?FVi1rmF5pwuoCRqD;7jfR=P!_=v_bt%Xct5Z11=`0+xa&tYY=XdX= z0oxevv>@i7;Uv$Byt-E@rFYT%{g?ES9$J8ifX|&-VaFj{;Fel`){T?c!c67nbc{f$ z6JWCbm+uAx0ikaa-sa9rPJm_MP0@>d&mQ%%9*sCt5N9+vR5Zf8z$iztr8too?+{D$2VU#wL*BvgHk&h} z865tU@;ct3Zr|&;mic>Cx4dc*{CJe&K|VRkDJh{$ zW?pmje*M*34;uJh6fZUkGGE^v$VtCOib{wLx|kL%<u2hS-$IvK-XWO}7YI zgoTczw2iusr=78dYY=cREkQwdCA{o|eX4<2NO=VF10o^x(AKx=kh_^!NUFg*>&Qyrzw$)fUc5v+7;jt7 z>U1Jdyi~?vf-BCMain{1yaSwk$yw4-a))`x{SV#DgmhtFediQ(R%36zx5o@{9NsD2 z0h*f>jnp9}(N4+%dVH-IPP_&*F9+%^sl3@;kweRT*IGLJ_Vv;kl+pml>(s}z+~fsh z>BJk#Dm63h5$Yv8V?GF{H1SRz0$FDIcgf&Hs~T%kW`0;!SyeV;2KD{f?bq|2Pk2wH zH@twoo-))Fq&S@f|Fc|5D5L$)#@O8Mvr*UBPQTMpqA4Ah#{c&fu++y4E6z+L=TK8G zRb~_*R8Vx^oh_ZUU|N(0o*4T$as2bt#H=mceP+1us@3Flw!b=MY8SV<_6hZT8jOk-@8vIh3hLu>fuv@4$xx}Z!Kj=6R_;Ii zvw9;k>1L>un`Ce^ap3ink>NK_Si@XnE6RVBl>R8lNlhut<@MShNG68|r%{&ED9f2t zkX=tw(yg;}OJv4*uTlzvc5lZ~p8n9@qtI+dw!j>#I=+Gay2bm`xhnLirBPq?%b3_g zW_fY$FtDVC{u!4Qjw_HAjvWWJD-zoE+M3#R+92JPRfM`F?;cwq?;bY{Ugb!nbf&Ro z3fa;n&@V93?_yklBLk^lpX|H&aJI9fzL^+cU|u$?x{iPL5bM9Oe|)m1cTlcT-YUz& zC^vtbaEI$^H0oBN--S!3%+zI161ECMsVmrSLfW}f)0wlA-BD~`*}v0>V#5y{dQNv8 zBR#$&?f1)PwX1=BKRaQmFYev@pPno2*EH8cICIQP?No!jkGkN+zr|}tl)VMj2k{=1 zxJi)-dXvZkeWGjM`|nNA3|RW&+jnEb7sIMNPelk(29n1AuIwl_aj20ivSL{dYm|#5 zmLw3PsYKdAFiD=uB zj;4C14g;1>u9^Jg2`0lshJJWodKX_qi7YaE$xy~ma6@3{@WQaXkjTg+7eLh46`>(w zy`+Kug(@B}PlA#9<$6aAhe^w%5r}%UmLSzjA7Od$>-B?YAD5a> z;`1IJBUZPm_!l3aAuum!eoRwmX{5KYHWer$cl{pbYq7Pob+9$jG#73m=zIKwefRlM zUju$WJj`I;=0g1eUnOO)9~t}JbA6tf$8UVMsj{!Ql9!osr7{=g1?Gh2N9j`0*67egJA?=9?W>7f2=JwvmI6 znCL5?ibZXCzGT~0+KcEf=UYwLhPi__iucFMGsAa-^YHO4fCGkFa8}mpN%R2bX8zsG zezWFztLC2c!n>fB;#nBQV&8#rifA173|hj7!I7GzlMNQBQzySlS8On^Q!{UL& z1cx|c0Py!H_bVPylP>x$r>?1OIY>BsCP-ePY3K-yFJaN~)>E(dh&1q|Uy)&@OM%<{IgRjb32PhWn8cd@^F$eCC)FZ?^liD+C$Jz}KUU&4{a< zM)p6fGB}9U#{XT;q%HAC|KqHQ{3xwm8+*w3waQ_zbkxdB3lT zk4%KbvoZktycr)ALY)~~7sU;r`zs$&nk9*BL2Ojv-gw7#OE4BFQh&3id{5 z;$~^HwOaC;T0(Ip27G$)3B_5p6SN5&$Rq^>BtB^GnTGLU#^AT(1BgRe)qgeOdn2Q62^cU5_S)AAJe*r{UpcKv>BJh=TL;ZTA;&O47gjxK=?JQiDO;d z?y>B-uzP#|eQi+^UAl7>1|Ghj&!7K1d!uLVx-^)6X02g9%`~_q*HmbD^pvTkze^ob z*MFC4Ox5V9`#ECl66OlctxYh`Kgv;ToPi$>!xXQ=XcoH;Y{JAeZQm~ARhRJ8fyJZq zGY9EH>x`9R!J$Xk;-x56Jlci>Szc7AIV?dHzS<;OIt6jXvn*b#uEV3JzPz2jwe+ZS zC?GK|*e|&}0gu+G2Hg)KHK`Bo)g-59B$KnUeJW??f-e`8Bxe*9S4WMF*F+bUrcz4E z125km_pZsMrDiCj;^Ih;#$wjTVnZIr*}=x6Lqf-*?yUj{ckh!eSqQp^P0+oj8?UQ9 zqOBgUeoZ}rMIPf%p@4G_bI&yM8x!>NJ8m+pbfxq&yqeIH8zIwY^%S#0?lO>+?vg6X zwQx849N+tDy^=#gP8Gq-URy1xa~WO@Y*WiS=(W3ygvJVLZXL-(@2nYF^RQWyeu$e= z(%}~SJx2S@2u5Cd=j6@Ov<_ULoA6nB>Dgrw0cY)arJ=-jXg`(WRn_}sUEWq#Q+?xM zUl9G)!^*nWE5!w#E>?~%0hw{3(TiERI5}B*|4VJFJw~{u$$M?{oj_YlwJ@QfzK-u| z%~4YGEYsd-GRbg~X+JyBZEJLYa>8h?*B1PSJFU-KCq*U4`2;sawGrhq;`L1BW$V~| zT9Y%=%X7)9?)%IgV^7;2XXb`jfStOm3#hC`G4bj38esp_dyF)RW_oq?d`(rtZLL*8 zeDzPDp)VFW7x>GYQWD}WX~sxHxta}@C;t&ch})Hf?zmK?kJxG{7@DYhWr575U>&Uv z@W+OIiz@wn3o88l3yOXHii`gVC>WkoQZ_VFIBR4|G%|z}g*L=eVohUBa%pmPBR~IV zoiZJ0sjwxU-beJ-lm4T6{@i(`YEnZXzC1C=*WcgLv?GrOJVh6+jPzBd#COjr8OfZi z_P87ypII60dsfduYj4pBntyXVCGA>5K#jPmiq1I$71_{A#18l-VU=C97zE^$W8m_| z%J3<*QX!^tBc9|$ZYeZ3SlG^%2FQ4;E&Sid3#|XPH~~wCf~S9)JXR&IlXBruciH{| z_u3E|&fm;NR`SeGcl?obcvn$v|3op#Bb)#%S3PS6M=L4^hZ~G)nIm#HN3LDQdbx#K zS-OzCO+DR0EibqRB5rWL5&TXhx<>R5vw83bhLHlj;zJx6NZsn~{_%zB?)KX2#1Ji$ zf??HMii)49ix?3CVO(x7siIU-(9jB#yFdy(B0x0ZV7x5E00&Es2h+Ey)1T zW!T=Id&TS-@2X9>G^;0TZ>?d-eIbK3D#1vmM$d_ah^g`A=5jRKp@3-(!a zRxUpNcF#q@bd?{Q(XDB{=A6?w*j8G--oCVA9S|7n>qiZ|&`+R!>=_K}u#mP;H*L4^ zd$#k=bG?8PiW+f7fI18!~+1bwu4LpWYQXTrF6l>neU(dRcWZ;QQ z<=n%OW#E)wf2%ROVJs=3Y;6FvAKGmVY`H4sN6&Ur4obr$ekgQ^exYOktY*=IoKG4Q z#(UoxE!V7x`@2HP^R*T+EQy;C%xFzOa$)4O7j)%eW2r_UzHvcp%K+VGqK+N6W^ znzPg~6hcx*+m^T^ObS(ji>;_1WM}H zj61>Vo@ySGA>>6C$$ta*{@N$h%-Y9ueRWT>GU^w!??3h|zATW*wLgJU3{qb2*vgBW zi<-+hzH*0vNJNrtT)3lCY(!gZ%s_06k+r0@j$ZjqYW7C~ZcDt#y z9Xweh9fu2uraB)Db5#;Pa#~SG9C)_){Pdj?Q3aHN@QZF!z*;2etM8PIqUH6cfT!Ce zWbgMia7aYuM>4#;J!T;LjIX`}1y#miTqSxuT-4BRLgv)~xBGyc5)0QI*Zx{ z2D&&01=+zNQ6xJ@ER|0zEeE*5dY9@hGZ)E0a^Z5I&%a$HpZl;p;ZPEKwEMxeaBpG0 z+NeX(KMo$lvUkOIWp*WWv0@J%{_Iaa%vVno3A#$3#SUIdNXkqCwM}7arW}=$>98+^ zmP^YG+2`v1?*Esi8@-IV~OPQBf2cpmS1zJ@>&ZR%7Z;d z7&*M=AQiO87Al|bbvNzUJ0T~v&!JYfLT&Dqigs#FX)97yg&WRFM|XFR zQq#~qfS=v?e+bM@1*8NtS2ssty`!=9al0$R>=2wHoxhysEYPfZ&?qUF5))sWM~-Po zz7#kV3fJo}$R9<#O@VUM*DUzHQ6<$-GMs2uODtmwFpRdGco3JZE|MiRaBu`RW_g7 zrIJ+l*lL%P=Tt3CLyMWJ%6Us2EkhGf+sNRUr~z}sZtq8BrBk;JmkwY0_d@lFTmSr> z>B$Q}j;j}I^@~3M8zcMyFe2pl6Xv+bN)H^KB_{sbfBxCEp_~6gf8A2B>%t#AVYlat zUDoXS*bATg>E*E+`@A7DJPwL=qOBeth|*bll>t9>kS>+Zx%L4V|7)f>x$5GtW_XoO zFk2crpucR%QCVof-D-e$D;V&1tahM`KnLY^Z?2nLEZuh6F57K;QoYyU?EeR5|3NbX zj{vHd*Ve{FgI($_sZh|m5o^fJHM5*&B9uS+`)M*u3-i$8_-xF2nBO^hsq1%WgB!83 z++0QD{>Bk^b!&7Z$h*0zIqqLbkipCEARDJFLea{$uL(ET>%~0Vp_*ZR9WV(>-u6z1 zLghv*rkiVg+~1d=W&x$DCyp!tk%PJyd>Z7Q-;C#-jmi0Kud$t_`HDU0(~X8T^b;Z& zLd{B>1Zg|oVBB9W)IHt5)D1*@<%wPwOXR`ZXsZ@DQzr$>;M}v8xTGz^nYzT5Q}akD z3-+Fvvi8`d?fr>W-kQANY|a6kd;Nb72VkdwqYOdzu(xYpvNl*F3;l;^F#gC-)jQK} zxWTafx`1AIEN{LK@?Vo*S76h1a$z~Fec(l<)vI>TuB=AaUe=&r+uNB7UPExp-}gaR z3bo}_5&7clGsW^9=`uL*QEh^SJOJE~UuVt(3Z=3Pb~^K&Q>yzwj52>aN7e{z?AG2# zImb1avD&(AxbsV~9z3x(BiK8IypwQY$Jv5%P46dKjucK*pM#*UH7eZ7L^=(>!OJIK z)T@i9eOxnW`;FS*X8FGg;_|p39h4*S z(U_J|A#M1L8?+~y6nHMqs{vrE{w6q4<#Xip>VCdyrkn+zvxD;7EOHXW8T~)p1j;V> z{@=rv7;Jf;J}w*7&-xpVoQshg0BX#3=@TfvvzQ%JWWj~=pL7k>xZY=k{T1CCp&V6t z+a?(L>BoV#Lm%hzJo^cgkE@U9bQO^i2W7HU@07+wYPMJZ(LZ1ru*2N)2v9ay#l7&l z>A6*Z$M2f}@ZwLr5&*mt7Qg*E@ONRBn!(BJ&gP}H z{%vp7vK;CP4W+#mHHm`mBER{`dwKKOH>8(oVwL1z%VfF=94hie%K>@S7~we<23p_@ z7s#UmX)r-ZvAxy@+?xWT*pw#AYw^=s(h=OY1 zgydtbb~H~FOo^!#&q3fM$Zk@cCY9@`ao$r@TLQQ7l^q}D#BL4^7wI2WjcA}Y|L5xR zINICUTz9WRXmP8tHb0cQN_arS>H6KtIzv#HUDeiHpLJmK^-!r%K5BF0Hw_$b534VW zJ6SwB>M+7|=n^NN{!lnhmE!oNa;)e3 z(dB-|HJ5fVXHwkogSZa~dwS%1Vh9mi*5Z?%B{nEOoIq_bf?gC}4L7Ioxosj_n(SSyf6)Xf`87NR2!a;z^ zMQBK41#mpj?T_H#vOfwJ|6Fu`E(Z3;5)-{Y&OtS+%f$+ljyu62F(f}9-*6!rDQQzJ zq#vxRpXeQE800Czv=R_Q7S|qg3MsA-7qI(fGGAYbY1cipg zAlb7jNJc;FXH4RE3hoXMQI<5NQB0!1Laf`<*IS}7(u8Ef&vPP(u;!OEHq_BGwRA!p ON3B$ub4ks;IsgFgYn-P5 literal 0 HcmV?d00001 diff --git a/web/berry/src/assets/images/icons/lark.svg b/web/berry/src/assets/images/icons/lark.svg new file mode 100644 index 0000000000..239e1bef65 --- /dev/null +++ b/web/berry/src/assets/images/icons/lark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/berry/src/assets/images/logo-white.svg b/web/berry/src/assets/images/logo-white.svg new file mode 100644 index 0000000000..d6289b9af6 --- /dev/null +++ b/web/berry/src/assets/images/logo-white.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/berry/src/assets/scss/_themes-vars.module.scss b/web/berry/src/assets/scss/_themes-vars.module.scss index a470b033eb..661bb6c6cc 100644 --- a/web/berry/src/assets/scss/_themes-vars.module.scss +++ b/web/berry/src/assets/scss/_themes-vars.module.scss @@ -46,11 +46,16 @@ $grey600: #4b5565; $grey700: #364152; $grey900: #121926; +$tableBackground: #f4f6f8; +$tableBorderBottom: #f1f3f4; + // ==============================|| DARK THEME VARIANTS ||============================== // // paper & background $darkBackground: #1a223f; // level 3 $darkPaper: #111936; // level 4 +$darkDivider: rgba(227, 232, 239, 0.2); +$darkSelectedBack : rgba(124, 77, 255, 0.15); // dark 800 & 900 $darkLevel1: #29314f; // level 1 @@ -154,4 +159,9 @@ $darkTextSecondary: #8492c4; darkSecondaryDark: $darkSecondaryDark; darkSecondary200: $darkSecondary200; darkSecondary800: $darkSecondary800; + + darkDivider: $darkDivider; + darkSelectedBack: $darkSelectedBack; + tableBackground: $tableBackground; + tableBorderBottom: $tableBorderBottom; } diff --git a/web/berry/src/assets/scss/fonts.scss b/web/berry/src/assets/scss/fonts.scss new file mode 100644 index 0000000000..c792aab27c --- /dev/null +++ b/web/berry/src/assets/scss/fonts.scss @@ -0,0 +1,32 @@ + +/* roboto-regular */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Roboto'), url('../fonts/roboto-regular.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + } + + /* roboto-500 */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: local('Roboto'), url('../fonts/roboto-500.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + + +/* roboto-700 */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: local('Roboto'), url('../fonts/roboto-700.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + \ No newline at end of file diff --git a/web/berry/src/assets/scss/style.scss b/web/berry/src/assets/scss/style.scss index 17d566e623..5d2d897533 100644 --- a/web/berry/src/assets/scss/style.scss +++ b/web/berry/src/assets/scss/style.scss @@ -1,3 +1,4 @@ +@import 'fonts.scss'; // color variants @import 'themes-vars.module.scss'; diff --git a/web/berry/src/hooks/useLogin.js b/web/berry/src/hooks/useLogin.js index 53626577ba..39d8b40741 100644 --- a/web/berry/src/hooks/useLogin.js +++ b/web/berry/src/hooks/useLogin.js @@ -48,6 +48,28 @@ const useLogin = () => { } }; + const larkLogin = async (code, state) => { + try { + const res = await API.get(`/api/oauth/lark?code=${code}&state=${state}`); + const { success, message, data } = res.data; + if (success) { + if (message === 'bind') { + showSuccess('绑定成功!'); + navigate('/panel'); + } else { + dispatch({ type: LOGIN, payload: data }); + localStorage.setItem('user', JSON.stringify(data)); + showSuccess('登录成功!'); + navigate('/panel'); + } + } + return { success, message }; + } catch (err) { + // 请求失败,设置错误信息 + return { success: false, message: '' }; + } + }; + const wechatLogin = async (code) => { try { const res = await API.get(`/api/oauth/wechat?code=${code}`); @@ -72,7 +94,7 @@ const useLogin = () => { navigate('/'); }; - return { login, logout, githubLogin, wechatLogin }; + return { login, logout, githubLogin, wechatLogin, larkLogin }; }; export default useLogin; diff --git a/web/berry/src/layout/MainLayout/Header/ProfileSection/index.js b/web/berry/src/layout/MainLayout/Header/ProfileSection/index.js index 3e3512546a..e1392dc00a 100644 --- a/web/berry/src/layout/MainLayout/Header/ProfileSection/index.js +++ b/web/berry/src/layout/MainLayout/Header/ProfileSection/index.js @@ -71,8 +71,8 @@ const ProfileSection = () => { alignItems: 'center', borderRadius: '27px', transition: 'all .2s ease-in-out', - borderColor: theme.palette.primary.light, - backgroundColor: theme.palette.primary.light, + borderColor: theme.typography.menuChip.background, + backgroundColor: theme.typography.menuChip.background, '&[aria-controls="menu-list-grow"], &:hover': { borderColor: theme.palette.primary.main, background: `${theme.palette.primary.main}!important`, diff --git a/web/berry/src/layout/MainLayout/Header/index.js b/web/berry/src/layout/MainLayout/Header/index.js index 51d40c7546..8fd9c950c8 100644 --- a/web/berry/src/layout/MainLayout/Header/index.js +++ b/web/berry/src/layout/MainLayout/Header/index.js @@ -7,6 +7,7 @@ import { Avatar, Box, ButtonBase } from '@mui/material'; // project imports import LogoSection from '../LogoSection'; import ProfileSection from './ProfileSection'; +import ThemeButton from 'ui-component/ThemeButton'; // assets import { IconMenu2 } from '@tabler/icons-react'; @@ -37,9 +38,8 @@ const Header = ({ handleLeftDrawerToggle }) => { sx={{ ...theme.typography.commonAvatar, ...theme.typography.mediumAvatar, + ...theme.typography.menuButton, transition: 'all .2s ease-in-out', - background: theme.palette.secondary.light, - color: theme.palette.secondary.dark, '&:hover': { background: theme.palette.secondary.dark, color: theme.palette.secondary.light @@ -55,7 +55,7 @@ const Header = ({ handleLeftDrawerToggle }) => { - + ); diff --git a/web/berry/src/layout/MainLayout/Sidebar/MenuCard/index.js b/web/berry/src/layout/MainLayout/Sidebar/MenuCard/index.js index 16b132319b..dadd3ecab6 100644 --- a/web/berry/src/layout/MainLayout/Sidebar/MenuCard/index.js +++ b/web/berry/src/layout/MainLayout/Sidebar/MenuCard/index.js @@ -36,7 +36,7 @@ import { useNavigate } from 'react-router-dom'; // })); const CardStyle = styled(Card)(({ theme }) => ({ - background: theme.palette.primary.light, + background: theme.typography.menuChip.background, marginBottom: '22px', overflow: 'hidden', position: 'relative', @@ -121,7 +121,6 @@ const MenuCard = () => { /> - {/* */} ); diff --git a/web/berry/src/layout/MainLayout/Sidebar/index.js b/web/berry/src/layout/MainLayout/Sidebar/index.js index e3c6d12d3a..10652ba691 100644 --- a/web/berry/src/layout/MainLayout/Sidebar/index.js +++ b/web/berry/src/layout/MainLayout/Sidebar/index.js @@ -39,7 +39,13 @@ const Sidebar = ({ drawerOpen, drawerToggle, window }) => { - + @@ -48,7 +54,13 @@ const Sidebar = ({ drawerOpen, drawerToggle, window }) => { - + diff --git a/web/berry/src/layout/MinimalLayout/Header/index.js b/web/berry/src/layout/MinimalLayout/Header/index.js index 4f61da6088..feaeb60356 100644 --- a/web/berry/src/layout/MinimalLayout/Header/index.js +++ b/web/berry/src/layout/MinimalLayout/Header/index.js @@ -1,10 +1,30 @@ // material-ui -import { useTheme } from "@mui/material/styles"; -import { Box, Button, Stack } from "@mui/material"; -import LogoSection from "layout/MainLayout/LogoSection"; -import { Link } from "react-router-dom"; -import { useLocation } from "react-router-dom"; -import { useSelector } from "react-redux"; +import { useState } from 'react'; +import { useTheme } from '@mui/material/styles'; +import { + Box, + Button, + Stack, + Popper, + IconButton, + List, + ListItemButton, + Paper, + ListItemText, + Typography, + Divider, + ClickAwayListener +} from '@mui/material'; +import LogoSection from 'layout/MainLayout/LogoSection'; +import { Link } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; +import { useSelector } from 'react-redux'; +import ThemeButton from 'ui-component/ThemeButton'; +import ProfileSection from 'layout/MainLayout/Header/ProfileSection'; +import { IconMenu2 } from '@tabler/icons-react'; +import Transitions from 'ui-component/extended/Transitions'; +import MainCard from 'ui-component/cards/MainCard'; +import { useMediaQuery } from '@mui/material'; // ==============================|| MAIN NAVBAR / HEADER ||============================== // @@ -12,16 +32,26 @@ const Header = () => { const theme = useTheme(); const { pathname } = useLocation(); const account = useSelector((state) => state.account); + const [open, setOpen] = useState(null); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + + const handleOpenMenu = (event) => { + setOpen(open ? null : event.currentTarget); + }; + + const handleCloseMenu = () => { + setOpen(null); + }; return ( <> @@ -31,43 +61,99 @@ const Header = () => { - - - - {account.user ? ( - + + {isMobile ? ( + <> + + + + + ) : ( - + <> + + + + {account.user ? ( + <> + + + + ) : ( + + )} + )} + + + {({ TransitionProps }) => ( + + + + + + + 首页} /> + + + + 关于} /> + + + {account.user ? ( + + 控制台 + + ) : ( + + 登录 + + )} + + + + + + )} + ); }; diff --git a/web/berry/src/layout/MinimalLayout/index.js b/web/berry/src/layout/MinimalLayout/index.js index c2919c6d79..81047fd165 100644 --- a/web/berry/src/layout/MinimalLayout/index.js +++ b/web/berry/src/layout/MinimalLayout/index.js @@ -1,6 +1,6 @@ import { Outlet } from 'react-router-dom'; import { useTheme } from '@mui/material/styles'; -import { AppBar, Box, CssBaseline, Toolbar } from '@mui/material'; +import { AppBar, Box, CssBaseline, Toolbar, Container } from '@mui/material'; import Header from './Header'; import Footer from 'ui-component/Footer'; @@ -22,9 +22,11 @@ const MinimalLayout = () => { flex: 'none' }} > - -
- + + +
+ + diff --git a/web/berry/src/routes/OtherRoutes.js b/web/berry/src/routes/OtherRoutes.js index 085c4add76..58c0b660e6 100644 --- a/web/berry/src/routes/OtherRoutes.js +++ b/web/berry/src/routes/OtherRoutes.js @@ -8,6 +8,7 @@ import MinimalLayout from 'layout/MinimalLayout'; const AuthLogin = Loadable(lazy(() => import('views/Authentication/Auth/Login'))); const AuthRegister = Loadable(lazy(() => import('views/Authentication/Auth/Register'))); const GitHubOAuth = Loadable(lazy(() => import('views/Authentication/Auth/GitHubOAuth'))); +const LarkOAuth = Loadable(lazy(() => import('views/Authentication/Auth/LarkOAuth'))); const ForgetPassword = Loadable(lazy(() => import('views/Authentication/Auth/ForgetPassword'))); const ResetPassword = Loadable(lazy(() => import('views/Authentication/Auth/ResetPassword'))); const Home = Loadable(lazy(() => import('views/Home'))); @@ -48,6 +49,10 @@ const OtherRoutes = { path: '/oauth/github', element: }, + { + path: '/oauth/lark', + element: + }, { path: '/404', element: diff --git a/web/berry/src/store/actions.js b/web/berry/src/store/actions.js index 221e8578e6..f1592d17d7 100644 --- a/web/berry/src/store/actions.js +++ b/web/berry/src/store/actions.js @@ -7,3 +7,4 @@ export const SET_BORDER_RADIUS = '@customization/SET_BORDER_RADIUS'; export const SET_SITE_INFO = '@siteInfo/SET_SITE_INFO'; export const LOGIN = '@account/LOGIN'; export const LOGOUT = '@account/LOGOUT'; +export const SET_THEME = '@customization/SET_THEME'; diff --git a/web/berry/src/store/customizationReducer.js b/web/berry/src/store/customizationReducer.js index bd8e5f00e3..0c10402577 100644 --- a/web/berry/src/store/customizationReducer.js +++ b/web/berry/src/store/customizationReducer.js @@ -9,7 +9,8 @@ export const initialState = { defaultId: 'default', fontFamily: config.fontFamily, borderRadius: config.borderRadius, - opened: true + opened: true, + theme: 'light' }; // ==============================|| CUSTOMIZATION REDUCER ||============================== // @@ -38,6 +39,11 @@ const customizationReducer = (state = initialState, action) => { ...state, borderRadius: action.borderRadius }; + case actionTypes.SET_THEME: + return { + ...state, + theme: action.theme + }; default: return state; } diff --git a/web/berry/src/themes/compStyleOverride.js b/web/berry/src/themes/compStyleOverride.js index b6e87e0182..67a3dd1486 100644 --- a/web/berry/src/themes/compStyleOverride.js +++ b/web/berry/src/themes/compStyleOverride.js @@ -1,5 +1,5 @@ export default function componentStyleOverrides(theme) { - const bgColor = theme.colors?.grey50; + const bgColor = theme.mode === 'dark' ? theme.backgroundDefault : theme.colors?.grey50; return { MuiButton: { styleOverrides: { @@ -12,15 +12,7 @@ export default function componentStyleOverrides(theme) { } } }, - MuiMenuItem: { - styleOverrides: { - root: { - '&:hover': { - backgroundColor: theme.colors?.grey100 - } - } - } - }, //MuiAutocomplete-popper MuiPopover-root + //MuiAutocomplete-popper MuiPopover-root MuiAutocomplete: { styleOverrides: { popper: { @@ -226,12 +218,12 @@ export default function componentStyleOverrides(theme) { MuiTableCell: { styleOverrides: { root: { - borderBottom: '1px solid rgb(241, 243, 244)', + borderBottom: '1px solid ' + theme.tableBorderBottom, textAlign: 'center' }, head: { color: theme.darkTextSecondary, - backgroundColor: 'rgb(244, 246, 248)' + backgroundColor: theme.headBackgroundColor } } }, @@ -239,7 +231,7 @@ export default function componentStyleOverrides(theme) { styleOverrides: { root: { '&:hover': { - backgroundColor: 'rgb(244, 246, 248)' + backgroundColor: theme.headBackgroundColor } } } @@ -247,10 +239,29 @@ export default function componentStyleOverrides(theme) { MuiTooltip: { styleOverrides: { tooltip: { - color: theme.paper, + color: theme.colors.paper, background: theme.colors?.grey700 } } + }, + MuiCssBaseline: { + styleOverrides: ` + .apexcharts-title-text { + fill: ${theme.textDark} !important + } + .apexcharts-text { + fill: ${theme.textDark} !important + } + .apexcharts-legend-text { + color: ${theme.textDark} !important + } + .apexcharts-menu { + background: ${theme.backgroundDefault} !important + } + .apexcharts-gridline, .apexcharts-xaxistooltip-background, .apexcharts-yaxistooltip-background { + stroke: ${theme.divider} !important; + } + ` } }; } diff --git a/web/berry/src/themes/index.js b/web/berry/src/themes/index.js index 6e694aa6f7..addd61f74f 100644 --- a/web/berry/src/themes/index.js +++ b/web/berry/src/themes/index.js @@ -15,19 +15,10 @@ import themeTypography from './typography'; export const theme = (customization) => { const color = colors; - + const options = customization.theme === 'light' ? GetLightOption() : GetDarkOption(); const themeOption = { colors: color, - heading: color.grey900, - paper: color.paper, - backgroundDefault: color.paper, - background: color.primaryLight, - darkTextPrimary: color.grey700, - darkTextSecondary: color.grey500, - textDark: color.grey900, - menuSelected: color.secondaryDark, - menuSelectedBack: color.secondaryLight, - divider: color.grey200, + ...options, customization }; @@ -53,3 +44,49 @@ export const theme = (customization) => { }; export default theme; + +function GetDarkOption() { + const color = colors; + return { + mode: 'dark', + heading: color.darkTextTitle, + paper: color.darkLevel2, + backgroundDefault: color.darkPaper, + background: color.darkBackground, + darkTextPrimary: color.darkTextPrimary, + darkTextSecondary: color.darkTextSecondary, + textDark: color.darkTextTitle, + menuSelected: color.darkSecondaryMain, + menuSelectedBack: color.darkSelectedBack, + divider: color.darkDivider, + borderColor: color.darkBorderColor, + menuButton: color.darkLevel1, + menuButtonColor: color.darkSecondaryMain, + menuChip: color.darkLevel1, + headBackgroundColor: color.darkBackground, + tableBorderBottom: color.darkDivider + }; +} + +function GetLightOption() { + const color = colors; + return { + mode: 'light', + heading: color.grey900, + paper: color.paper, + backgroundDefault: color.paper, + background: color.primaryLight, + darkTextPrimary: color.grey700, + darkTextSecondary: color.grey500, + textDark: color.grey900, + menuSelected: color.secondaryDark, + menuSelectedBack: color.secondaryLight, + divider: color.grey200, + borderColor: color.grey300, + menuButton: color.secondaryLight, + menuButtonColor: color.secondaryDark, + menuChip: color.primaryLight, + headBackgroundColor: color.tableBackground, + tableBorderBottom: color.tableBorderBottom + }; +} diff --git a/web/berry/src/themes/palette.js b/web/berry/src/themes/palette.js index 097685553a..70c7878251 100644 --- a/web/berry/src/themes/palette.js +++ b/web/berry/src/themes/palette.js @@ -5,7 +5,7 @@ export default function themePalette(theme) { return { - mode: 'light', + mode: theme.mode, common: { black: theme.colors?.darkPaper }, diff --git a/web/berry/src/themes/typography.js b/web/berry/src/themes/typography.js index 24bfabb936..f20d87a5fb 100644 --- a/web/berry/src/themes/typography.js +++ b/web/berry/src/themes/typography.js @@ -132,6 +132,19 @@ export default function themeTypography(theme) { width: '44px', height: '44px', fontSize: '1.5rem' + }, + menuButton: { + color: theme.menuButtonColor, + background: theme.menuButton + }, + menuChip: { + background: theme.menuChip + }, + CardWrapper: { + backgroundColor: theme.mode === 'dark' ? theme.colors.darkLevel2 : theme.colors.primaryDark + }, + SubCard: { + border: theme.mode === 'dark' ? '1px solid rgba(227, 232, 239, 0.2)' : '1px solid rgb(227, 232, 239)' } }; } diff --git a/web/berry/src/ui-component/Logo.js b/web/berry/src/ui-component/Logo.js index a34fe8954c..52e61f4f6c 100644 --- a/web/berry/src/ui-component/Logo.js +++ b/web/berry/src/ui-component/Logo.js @@ -1,6 +1,8 @@ // material-ui -import logo from 'assets/images/logo.svg'; +import logoLight from 'assets/images/logo.svg'; +import logoDark from 'assets/images/logo-white.svg'; import { useSelector } from 'react-redux'; +import { useTheme } from '@mui/material/styles'; /** * if you want to use image instead of uncomment following. @@ -14,6 +16,8 @@ import { useSelector } from 'react-redux'; const Logo = () => { const siteInfo = useSelector((state) => state.siteInfo); + const theme = useTheme(); + const logo = theme.palette.mode === 'light' ? logoLight : logoDark; return {siteInfo.system_name}; }; diff --git a/web/berry/src/ui-component/ThemeButton.js b/web/berry/src/ui-component/ThemeButton.js new file mode 100644 index 0000000000..c907c646dd --- /dev/null +++ b/web/berry/src/ui-component/ThemeButton.js @@ -0,0 +1,50 @@ +import { useDispatch, useSelector } from 'react-redux'; +import { SET_THEME } from 'store/actions'; +import { useTheme } from '@mui/material/styles'; +import { Avatar, Box, ButtonBase } from '@mui/material'; +import { IconSun, IconMoon } from '@tabler/icons-react'; + +export default function ThemeButton() { + const dispatch = useDispatch(); + + const defaultTheme = useSelector((state) => state.customization.theme); + + const theme = useTheme(); + + return ( + + + { + let theme = defaultTheme === 'light' ? 'dark' : 'light'; + dispatch({ type: SET_THEME, theme: theme }); + localStorage.setItem('theme', theme); + }} + color="inherit" + > + {defaultTheme === 'light' ? : } + + + + ); +} diff --git a/web/berry/src/ui-component/cards/MainCard.js b/web/berry/src/ui-component/cards/MainCard.js index 8735282c2f..323530276d 100644 --- a/web/berry/src/ui-component/cards/MainCard.js +++ b/web/berry/src/ui-component/cards/MainCard.js @@ -15,7 +15,7 @@ const headerSX = { const MainCard = forwardRef( ( { - border = true, + border = false, boxShadow, children, content = true, diff --git a/web/berry/src/ui-component/cards/SubCard.js b/web/berry/src/ui-component/cards/SubCard.js index 05f9abb70c..a63819a805 100644 --- a/web/berry/src/ui-component/cards/SubCard.js +++ b/web/berry/src/ui-component/cards/SubCard.js @@ -15,8 +15,7 @@ const SubCard = forwardRef( )} @@ -62,7 +61,8 @@ SubCard.propTypes = { secondary: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object]), sx: PropTypes.object, contentSX: PropTypes.object, - title: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object]) + title: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object]), + subTitle: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object]) }; SubCard.defaultProps = { diff --git a/web/berry/src/utils/chart.js b/web/berry/src/utils/chart.js index 4633fe375a..8cf6d8477a 100644 --- a/web/berry/src/utils/chart.js +++ b/web/berry/src/utils/chart.js @@ -40,7 +40,8 @@ export function generateChartOptions(data, unit) { chart: { sparkline: { enabled: true - } + }, + background: 'transparent' }, dataLabels: { enabled: false diff --git a/web/berry/src/utils/common.js b/web/berry/src/utils/common.js index d8dabac36f..947df3bf9c 100644 --- a/web/berry/src/utils/common.js +++ b/web/berry/src/utils/common.js @@ -91,6 +91,13 @@ export async function onGitHubOAuthClicked(github_client_id, openInNewTab = fals } } +export async function onLarkOAuthClicked(lark_client_id) { + const state = await getOAuthState(); + if (!state) return; + let redirect_uri = `${window.location.origin}/oauth/lark`; + window.open(`https://open.feishu.cn/open-apis/authen/v1/index?redirect_uri=${redirect_uri}&app_id=${lark_client_id}&state=${state}`); +} + export function isAdmin() { let user = localStorage.getItem('user'); if (!user) return false; diff --git a/web/berry/src/views/Authentication/Auth/LarkOAuth.js b/web/berry/src/views/Authentication/Auth/LarkOAuth.js new file mode 100644 index 0000000000..88ced5d86f --- /dev/null +++ b/web/berry/src/views/Authentication/Auth/LarkOAuth.js @@ -0,0 +1,94 @@ +import { Link, useNavigate, useSearchParams } from 'react-router-dom'; +import React, { useEffect, useState } from 'react'; +import { showError } from 'utils/common'; +import useLogin from 'hooks/useLogin'; + +// material-ui +import { useTheme } from '@mui/material/styles'; +import { Grid, Stack, Typography, useMediaQuery, CircularProgress } from '@mui/material'; + +// project imports +import AuthWrapper from '../AuthWrapper'; +import AuthCardWrapper from '../AuthCardWrapper'; +import Logo from 'ui-component/Logo'; + +// assets + +// ================================|| AUTH3 - LOGIN ||================================ // + +const LarkOAuth = () => { + const theme = useTheme(); + const matchDownSM = useMediaQuery(theme.breakpoints.down('md')); + + const [searchParams] = useSearchParams(); + const [prompt, setPrompt] = useState('处理中...'); + const { larkLogin } = useLogin(); + + let navigate = useNavigate(); + + const sendCode = async (code, state, count) => { + const { success, message } = await larkLogin(code, state); + if (!success) { + if (message) { + showError(message); + } + if (count === 0) { + setPrompt(`操作失败,重定向至登录界面中...`); + await new Promise((resolve) => setTimeout(resolve, 2000)); + navigate('/login'); + return; + } + count++; + setPrompt(`出现错误,第 ${count} 次重试中...`); + await new Promise((resolve) => setTimeout(resolve, 2000)); + await sendCode(code, state, count); + } + }; + + useEffect(() => { + let code = searchParams.get('code'); + let state = searchParams.get('state'); + sendCode(code, state, 0).then(); + }, []); + + return ( + + + + + + + + + + + + + + + + + + 飞书 登录 + + + + + + + + + {prompt} + + + + + + + + + + ); +}; + +export default LarkOAuth; diff --git a/web/berry/src/views/Authentication/AuthForms/AuthLogin.js b/web/berry/src/views/Authentication/AuthForms/AuthLogin.js index 9420b098e9..bc7a35c0af 100644 --- a/web/berry/src/views/Authentication/AuthForms/AuthLogin.js +++ b/web/berry/src/views/Authentication/AuthForms/AuthLogin.js @@ -35,7 +35,8 @@ import VisibilityOff from '@mui/icons-material/VisibilityOff'; import Github from 'assets/images/icons/github.svg'; import Wechat from 'assets/images/icons/wechat.svg'; -import { onGitHubOAuthClicked } from 'utils/common'; +import Lark from 'assets/images/icons/lark.svg'; +import { onGitHubOAuthClicked, onLarkOAuthClicked } from 'utils/common'; // ============================|| FIREBASE - LOGIN ||============================ // @@ -49,7 +50,7 @@ const LoginForm = ({ ...others }) => { // const [checked, setChecked] = useState(true); let tripartiteLogin = false; - if (siteInfo.github_oauth || siteInfo.wechat_login) { + if (siteInfo.github_oauth || siteInfo.wechat_login || siteInfo.lark_client_id) { tripartiteLogin = true; } @@ -121,6 +122,29 @@ const LoginForm = ({ ...others }) => { )} + {siteInfo.lark_client_id && ( + + + + + + )} ({ - backgroundColor: theme.palette.primary.light + backgroundColor: theme.palette.background.default })); // eslint-disable-next-line diff --git a/web/berry/src/views/Channel/component/EditModal.js b/web/berry/src/views/Channel/component/EditModal.js index cbf411b985..03b4df577e 100644 --- a/web/berry/src/views/Channel/component/EditModal.js +++ b/web/berry/src/views/Channel/component/EditModal.js @@ -21,15 +21,16 @@ import { Container, Autocomplete, FormHelperText, - Checkbox + Switch, + Checkbox, } from "@mui/material"; import { Formik } from "formik"; import * as Yup from "yup"; import { defaultConfig, typeConfig } from "../type/Config"; //typeConfig import { createFilterOptions } from "@mui/material/Autocomplete"; -import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank'; -import CheckBoxIcon from '@mui/icons-material/CheckBox'; +import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank"; +import CheckBoxIcon from "@mui/icons-material/CheckBox"; const icon = ; const checkedIcon = ; @@ -79,6 +80,7 @@ const EditModal = ({ open, channelId, onCancel, onOk }) => { const [inputPrompt, setInputPrompt] = useState(defaultConfig.prompt); const [groupOptions, setGroupOptions] = useState([]); const [modelOptions, setModelOptions] = useState([]); + const [batchAdd, setBatchAdd] = useState(false); const initChannel = (typeValue) => { if (typeConfig[typeValue]?.inputLabel) { @@ -151,7 +153,7 @@ const EditModal = ({ open, channelId, onCancel, onOk }) => { try { let res = await API.get(`/api/channel/models`); const { data } = res.data; - data.forEach(item => { + data.forEach((item) => { if (!item.owned_by) { item.owned_by = "未知"; } @@ -166,7 +168,7 @@ const EditModal = ({ open, channelId, onCancel, onOk }) => { }); setModelOptions( - data.map((model) => { + data.map((model) => { return { id: model.id, group: model.owned_by, @@ -258,7 +260,7 @@ const EditModal = ({ open, channelId, onCancel, onOk }) => { 2 ); } - data.base_url = data.base_url ?? ''; + data.base_url = data.base_url ?? ""; data.is_edit = true; initChannel(data.type); setInitialInput(data); @@ -273,6 +275,7 @@ const EditModal = ({ open, channelId, onCancel, onOk }) => { }, []); useEffect(() => { + setBatchAdd(false); if (channelId) { loadChannel().then(); } else { @@ -340,15 +343,17 @@ const EditModal = ({ open, channelId, onCancel, onOk }) => { }, }} > - {Object.values(CHANNEL_OPTIONS).sort((a, b) => { - return a.text.localeCompare(b.text) - }).map((option) => { - return ( - - {option.text} - - ); - })} + {Object.values(CHANNEL_OPTIONS) + .sort((a, b) => { + return a.text.localeCompare(b.text); + }) + .map((option) => { + return ( + + {option.text} + + ); + })} {touched.type && errors.type ? ( @@ -553,7 +558,12 @@ const EditModal = ({ open, channelId, onCancel, onOk }) => { }} renderOption={(props, option, { selected }) => (
  • - + {option.id}
  • )} @@ -599,20 +609,38 @@ const EditModal = ({ open, channelId, onCancel, onOk }) => { error={Boolean(touched.key && errors.key)} sx={{ ...theme.typography.otherInput }} > - - {inputLabel.key} - - + {!batchAdd ? ( + <> + + {inputLabel.key} + + + + ) : ( + + )} + {touched.key && errors.key ? ( {errors.key} @@ -624,6 +652,19 @@ const EditModal = ({ open, channelId, onCancel, onOk }) => { )} + {channelId === 0 && ( + + setBatchAdd(e.target.checked)} + /> + 批量添加 + + )} { - if (priorityValve === "" || priorityValve === item.priority) { + const handlePriority = async (event) => { + const currentValue = parseInt(event.target.value); + if (isNaN(currentValue) || currentValue === priorityValve) { return; } - await manageChannel(item.id, "priority", priorityValve); + + if (currentValue < 0) { + showError("优先级不能小于 0"); + return; + } + + await manageChannel(item.id, "priority", currentValue); + setPriority(currentValue); }; const handleResponseTime = async () => { @@ -170,9 +170,7 @@ export default function ChannelTableRow({ handle_action={handleResponseTime} /> - - {renderNumber(item.used_quota)} - + {renderNumber(item.used_quota)} - - 优先级 - setPriority(e.target.value)} - sx={{ textAlign: "center" }} - endAdornment={ - - - - - - } - /> - + diff --git a/web/berry/src/views/Dashboard/component/StatisticalLineChartCard.js b/web/berry/src/views/Dashboard/component/StatisticalLineChartCard.js index 9daa951997..e6b46e2530 100644 --- a/web/berry/src/views/Dashboard/component/StatisticalLineChartCard.js +++ b/web/berry/src/views/Dashboard/component/StatisticalLineChartCard.js @@ -12,7 +12,7 @@ import MainCard from 'ui-component/cards/MainCard'; import SkeletonTotalOrderCard from 'ui-component/cards/Skeleton/EarningCard'; const CardWrapper = styled(MainCard)(({ theme }) => ({ - backgroundColor: theme.palette.primary.dark, + ...theme.typography.CardWrapper, color: '#fff', overflow: 'hidden', position: 'relative', diff --git a/web/berry/src/views/Profile/index.js b/web/berry/src/views/Profile/index.js index e06832284a..483e3141c1 100644 --- a/web/berry/src/views/Profile/index.js +++ b/web/berry/src/views/Profile/index.js @@ -12,7 +12,8 @@ import { DialogTitle, DialogContent, DialogActions, - Divider + Divider, + SvgIcon } from '@mui/material'; import Grid from '@mui/material/Unstable_Grid2'; import SubCard from 'ui-component/cards/SubCard'; @@ -20,12 +21,13 @@ import { IconBrandWechat, IconBrandGithub, IconMail } from '@tabler/icons-react' import Label from 'ui-component/Label'; import { API } from 'utils/api'; import { showError, showSuccess } from 'utils/common'; -import { onGitHubOAuthClicked } from 'utils/common'; +import { onGitHubOAuthClicked, onLarkOAuthClicked } from 'utils/common'; import * as Yup from 'yup'; import WechatModal from 'views/Authentication/AuthForms/WechatModal'; import { useSelector } from 'react-redux'; import EmailModal from './component/EmailModal'; import Turnstile from 'react-turnstile'; +import { ReactComponent as Lark } from 'assets/images/icons/lark.svg'; const validationSchema = Yup.object().shape({ username: Yup.string().required('用户名 不能为空').min(3, '用户名 不能小于 3 个字符'), @@ -137,6 +139,9 @@ export default function Profile() { + @@ -205,6 +210,13 @@ export default function Profile() { )} + {status.lark_client_id && !inputs.lark_id && ( + + + + )} + +
    + { + + 用以推送报警信息, + + 点击此处 + + 了解 Message Pusher + + } + > + + + + Message Pusher 推送地址 + + + + + + Message Pusher 访问凭证 + + + + + + + + ; +const checkedIcon = ; +const filter = createFilterOptions(); const validationSchema = Yup.object().shape({ is_edit: Yup.boolean(), - name: Yup.string().required("名称 不能为空"), - remain_quota: Yup.number().min(0, "必须大于等于0"), + name: Yup.string().required('名称 不能为空'), + remain_quota: Yup.number().min(0, '必须大于等于0'), expired_time: Yup.number(), - unlimited_quota: Yup.boolean(), + unlimited_quota: Yup.boolean() }); const originInputs = { is_edit: false, - name: "", + name: '', remain_quota: 0, expired_time: -1, unlimited_quota: false, + subnet: '', + models: [] }; const EditModal = ({ open, tokenId, onCancel, onOk }) => { const theme = useTheme(); const [inputs, setInputs] = useState(originInputs); + const [modelOptions, setModelOptions] = useState([]); const submit = async (values, { setErrors, setStatus, setSubmitting }) => { setSubmitting(true); values.remain_quota = parseInt(values.remain_quota); let res; + let models = values.models.join(','); if (values.is_edit) { - res = await API.put(`/api/token/`, { ...values, id: parseInt(tokenId) }); + res = await API.put(`/api/token/`, { ...values, id: parseInt(tokenId), models: models }); } else { - res = await API.post(`/api/token/`, values); + res = await API.post(`/api/token/`, { ...values, models: models }); } const { success, message } = res.data; if (success) { if (values.is_edit) { - showSuccess("令牌更新成功!"); + showSuccess('令牌更新成功!'); } else { - showSuccess("令牌创建成功,请在列表页面点击复制获取令牌!"); + showSuccess('令牌创建成功,请在列表页面点击复制获取令牌!'); } setSubmitting(false); setStatus({ success: true }); @@ -78,61 +91,55 @@ const EditModal = ({ open, tokenId, onCancel, onOk }) => { const { success, message, data } = res.data; if (success) { data.is_edit = true; + if (data.models === '') { + data.models = []; + } else { + data.models = data.models.split(','); + } setInputs(data); } else { showError(message); } }; + const loadAvailableModels = async () => { + let res = await API.get(`/api/user/available_models`); + const { success, message, data } = res.data; + if (success) { + setModelOptions(data); + } else { + showError(message); + } + }; useEffect(() => { if (tokenId) { loadToken().then(); } else { - setInputs({...originInputs}); + setInputs({ ...originInputs }); } + loadAvailableModels().then(); }, [tokenId]); return ( - + - {tokenId ? "编辑令牌" : "新建令牌"} + {tokenId ? '编辑令牌' : '新建令牌'} - - 注意,令牌的额度仅用于限制令牌本身的最大额度使用量,实际的使用受到账户的剩余额度限制。 - - - {({ - errors, - handleBlur, - handleChange, - handleSubmit, - touched, - values, - setFieldError, - setFieldValue, - isSubmitting, - }) => ( + 注意,令牌的额度仅用于限制令牌本身的最大额度使用量,实际的使用受到账户的剩余额度限制。 + + {({ errors, handleBlur, handleChange, handleSubmit, touched, values, setFieldError, setFieldValue, isSubmitting }) => (
    - + 名称 { name="name" onBlur={handleBlur} onChange={handleChange} - inputProps={{ autoComplete: "name" }} + inputProps={{ autoComplete: 'name' }} aria-describedby="helper-text-channel-name-label" /> {touched.name && errors.name && ( @@ -151,42 +158,99 @@ const EditModal = ({ open, tokenId, onCancel, onOk }) => { )} + + { + const event = { + target: { + name: 'models', + value: value + } + }; + handleChange(event); + }} + onBlur={handleBlur} + // filterSelectedOptions + disableCloseOnSelect + renderInput={(params) => } + filterOptions={(options, params) => { + const filtered = filter(options, params); + const { inputValue } = params; + const isExisting = options.some((option) => inputValue === option); + if (inputValue !== '' && !isExisting) { + filtered.push(inputValue); + } + return filtered; + }} + renderOption={(props, option, { selected }) => ( +
  • + + {option} +
  • + )} + /> + {errors.models ? ( + + {errors.models} + + ) : ( + 请选择允许使用的模型,留空则不进行限制 + )} +
    + + IP 限制 + + {touched.subnet && errors.subnet ? ( + + {errors.subnet} + + ) : ( + + 请输入允许访问的网段,例如:192.168.0.0/24,请使用英文逗号分隔多个网段 + + )} + {values.expired_time !== -1 && ( - - + + { if (newError === null) { - setFieldError("expired_time", null); + setFieldError('expired_time', null); } else { - setFieldError("expired_time", "无效的日期"); + setFieldError('expired_time', '无效的日期'); } }} onChange={(newValue) => { - setFieldValue("expired_time", newValue.unix()); + setFieldValue('expired_time', newValue.unix()); }} slotProps={{ actionBar: { - actions: ["today", "accept"], - }, + actions: ['today', 'accept'] + } }} /> {errors.expired_time && ( - + {errors.expired_time} )} @@ -196,35 +260,22 @@ const EditModal = ({ open, tokenId, onCancel, onOk }) => { checked={values.expired_time === -1} onClick={() => { if (values.expired_time === -1) { - setFieldValue( - "expired_time", - Math.floor(Date.now() / 1000) - ); + setFieldValue('expired_time', Math.floor(Date.now() / 1000)); } else { - setFieldValue("expired_time", -1); + setFieldValue('expired_time', -1); } }} - />{" "} + />{' '} 永不过期 - - - 额度 - + + 额度 - {renderQuotaWithPrompt(values.remain_quota)} - - } + endAdornment={{renderQuotaWithPrompt(values.remain_quota)}} onBlur={handleBlur} onChange={handleChange} aria-describedby="helper-text-channel-remain_quota-label" @@ -232,10 +283,7 @@ const EditModal = ({ open, tokenId, onCancel, onOk }) => { /> {touched.remain_quota && errors.remain_quota && ( - + {errors.remain_quota} )} @@ -243,19 +291,13 @@ const EditModal = ({ open, tokenId, onCancel, onOk }) => { { - setFieldValue("unlimited_quota", !values.unlimited_quota); + setFieldValue('unlimited_quota', !values.unlimited_quota); }} - />{" "} + />{' '} 无限额度 - @@ -273,5 +315,5 @@ EditModal.propTypes = { open: PropTypes.bool, tokenId: PropTypes.number, onCancel: PropTypes.func, - onOk: PropTypes.func, + onOk: PropTypes.func };