From dd42cff668bac30f501ab33a579f0ada8a68a470 Mon Sep 17 00:00:00 2001 From: Gavan <994259213@qq.com> Date: Fri, 25 Jul 2025 19:17:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=94=A8=E6=88=B7=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=92=8C=E7=AE=A1=E7=90=86=E5=91=98=E7=99=BB=E5=BD=95=E5=90=88?= =?UTF-8?q?=E4=B8=80=E8=B5=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/api-templates/http-client.ejs | 6 +- ui/src/api/httpClient.ts | 6 +- ui/src/components/header/index.tsx | 2 +- ui/src/main.tsx | 14 +- ui/src/pages/login/index.tsx | 389 +++++++++++++++++++---------- ui/src/router.tsx | 10 +- 6 files changed, 277 insertions(+), 150 deletions(-) diff --git a/ui/api-templates/http-client.ejs b/ui/api-templates/http-client.ejs index 92efaad..3efb6ab 100644 --- a/ui/api-templates/http-client.ejs +++ b/ui/api-templates/http-client.ejs @@ -45,9 +45,9 @@ const redirectToLogin = () => { const redirectAfterLogin = encodeURIComponent(location.href); const search = `redirect=${redirectAfterLogin}`; const pathname = location.pathname.startsWith('/user') - ? '/user/login' - : '/login'; - window.location.href = `${pathname}?${search}`; + ? '/login' + : '/login/admin'; + window.location.href = `${pathname}`; }; type ExtractDataProp = T extends { data?: infer U } ? U : never diff --git a/ui/src/api/httpClient.ts b/ui/src/api/httpClient.ts index e1ca65f..7cae705 100644 --- a/ui/src/api/httpClient.ts +++ b/ui/src/api/httpClient.ts @@ -65,9 +65,9 @@ const redirectToLogin = () => { const redirectAfterLogin = encodeURIComponent(location.href); const search = `redirect=${redirectAfterLogin}`; const pathname = location.pathname.startsWith("/user") - ? "/user/login" - : "/login"; - window.location.href = `${pathname}?${search}`; + ? "/login" + : "/login/admin"; + window.location.href = `${pathname}`; }; type ExtractDataProp = T extends { data?: infer U } ? U : never; diff --git a/ui/src/components/header/index.tsx b/ui/src/components/header/index.tsx index 678f3ae..cdfc741 100644 --- a/ui/src/components/header/index.tsx +++ b/ui/src/components/header/index.tsx @@ -17,7 +17,7 @@ const Header = () => { await postAdminLogout(); } message.success('退出登录成功'); - navigate(pathname.startsWith('/user') ? '/user/login' : '/login'); + navigate(pathname.startsWith('/user') ? '/login/user' : '/login/admin'); }; return ( { const getUser = () => { setLoading(true); - if (location.pathname.startsWith('/user')) { + if ( + location.pathname.startsWith('/user') || + location.pathname === '/login' || + location.pathname === '/login/user' + ) { return getUserProfile() .then((res) => { setUser(res); - if (location.pathname.startsWith('/user/login')) { + if (location.pathname.startsWith('/login')) { onGotoRedirect('user'); } }) .finally(() => { setLoading(false); }); - } else if (!location.pathname.startsWith('/auth')) { + } else if ( + !location.pathname.startsWith('/auth') || + !location.pathname.startsWith('/user') || + location.pathname === '/login/admin' + ) { return getAdminProfile() .then((res) => { setUser(res); diff --git a/ui/src/pages/login/index.tsx b/ui/src/pages/login/index.tsx index cce67dd..b6f2801 100644 --- a/ui/src/pages/login/index.tsx +++ b/ui/src/pages/login/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useMemo } from 'react'; import { Box, Button, @@ -6,20 +6,24 @@ import { Typography, Container, Paper, - Alert, CircularProgress, Grid2 as Grid, InputAdornment, IconButton, + Stack, + Divider, } from '@mui/material'; +import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { postAdminLogin } from '@/api/Admin'; import { useForm, Controller } from 'react-hook-form'; -import { useNavigate } from 'react-router-dom'; import { styled } from '@mui/material/styles'; -import { ConstsLoginSource } from '@/api/types'; -import { Icon } from '@c-x/ui'; +import { ConstsLoginSource, DomainSetting } from '@/api/types'; +import { Icon, CusTabs } from '@c-x/ui'; import Logo from '@/assets/images/logo.png'; import { getRedirectUrl } from '@/utils'; +import { getGetSetting } from '@/api/Admin'; +import { postLogin, getUserOauthSignupOrIn } from '@/api/User'; +import { useRequest } from 'ahooks'; // @ts-ignore import { AestheticFluidBg } from '@/assets/jsm/AestheticFluidBg.module.js'; @@ -57,10 +61,24 @@ interface LoginFormData { password: string; } +type TabType = 'user' | 'admin'; + const LoginPage = () => { + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + const { tab: tabParam } = useParams(); const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); const [showPassword, setShowPassword] = useState(false); + const [tab, setTab] = useState((tabParam as TabType) || 'user'); + const { data: loginSetting = {} as DomainSetting } = + useRequest(getGetSetting); + const { custom_oauth = {}, dingtalk_oauth = {} } = loginSetting; + + useEffect(() => { + if (tabParam) { + setTab(tabParam as TabType); + } + }, [tabParam]); const { control, @@ -79,17 +97,80 @@ const LoginPage = () => { // 处理登录表单提交 const onSubmit = async (data: LoginFormData) => { setLoading(true); - setError(null); - try { - await loginUser(data); - const redirectUrl = getRedirectUrl(); - window.location.href = redirectUrl.href; - } catch (err) { - setError(err instanceof Error ? err.message : '登录失败,请重试'); - } finally { - setLoading(false); + if (tab === 'admin') { + try { + await loginUser(data); + const redirectUrl = getRedirectUrl(); + window.location.href = redirectUrl.href; + } finally { + setLoading(false); + } } + if (tab === 'user') { + try { + // 用户登录 + await postLogin({ + ...data, + source: ConstsLoginSource.LoginSourceBrowser, + }); + const redirectUrl = getRedirectUrl('user'); + window.location.href = redirectUrl.href; + } finally { + setLoading(false); + } + } + }; + + const oauthEnable = useMemo(() => { + return ( + (loginSetting.custom_oauth?.enable || + loginSetting.dingtalk_oauth?.enable) && + tab === 'user' + ); + }, [loginSetting, tab]); + + const disablePasswordLogin = useMemo(() => { + return loginSetting.disable_password_login && tab === 'user'; + }, [loginSetting, tab]); + + const onOauthLogin = (platform: 'dingtalk' | 'custom') => { + const redirectUrl = getRedirectUrl('user'); + getUserOauthSignupOrIn({ + platform, + redirect_url: redirectUrl.href, + source: ConstsLoginSource.LoginSourceBrowser, + }).then((res) => { + if (res.url) { + window.location.href = res.url; + } + }); + }; + + const oauthLogin = () => { + return ( + + + 使用其他方式登录 + + {dingtalk_oauth.enable && ( + onOauthLogin('dingtalk')} + > + + + )} + {custom_oauth.enable && ( + onOauthLogin('custom')} + > + + + )} + + ); }; useEffect(() => { @@ -124,131 +205,169 @@ const LoginPage = () => { > Monkey Code - - 请输入您的账号和密码 - - - - - ( - - ), - }, - }} - /> - )} - /> - + { + setTab(value); + navigate(`/login/${value}?${searchParams.toString()}`); + }} + sx={{ + width: '100%', + mb: 4, + height: 40, + border: 'none', + padding: '4px', + '.MuiTab-root': { + width: '50%', + height: 32, + fontSize: 14, + '&.Mui-selected': { + color: 'text.primary', + fontWeight: 500, + }, + }, + '.MuiTabs-scroller': { + height: 32, + }, + '.MuiTabs-indicator': { + borderRadius: '10px', + height: 32, + backgroundColor: '#fff', + }, + bgcolor: 'rgba(255, 255, 255, 0.2)', + borderRadius: '10px', + }} + /> - - ( - - ), - endAdornment: ( - - setShowPassword(!showPassword)} - edge='end' - disabled={loading} - size='small' - > - {showPassword ? ( - - ) : ( - - )} - - - ), - }, - }} - /> - )} - /> - + {!disablePasswordLogin && ( + + + + ( + + ), + }, + }} + /> + )} + /> + - - + + ( + + ), + endAdornment: ( + + setShowPassword(!showPassword)} + edge='end' + disabled={loading} + size='small' + > + {showPassword ? ( + + ) : ( + + )} + + + ), + }, + }} + /> + )} + /> + + + + + - - + + )} + {oauthEnable && oauthLogin()} ); diff --git a/ui/src/router.tsx b/ui/src/router.tsx index 44d237d..070a230 100644 --- a/ui/src/router.tsx +++ b/ui/src/router.tsx @@ -123,12 +123,12 @@ const routerConfig = [ path: '/auth', element: , }, + // { + // path: '/user/login', + // element: , + // }, { - path: '/user/login', - element: , - }, - { - path: '/login', + path: '/login/:tab?', element: , }, ];