From d5074a92a28224860216cfa24c52052056886ede Mon Sep 17 00:00:00 2001 From: siujamo Date: Wed, 28 Jan 2026 17:05:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=A4=9A=E5=9B=A0?= =?UTF-8?q?=E7=B4=A0=E8=AE=A4=E8=AF=81=E7=99=BB=E5=BD=95=EF=BC=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E7=9B=B8=E5=85=B3=E7=B1=BB=E5=9E=8B=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/auth/index.ts | 6 +- src/layouts/dashboard-layout/index.tsx | 78 +++++++++++--------------- src/page/login/index.tsx | 38 ++++++------- src/types/entity/index.ts | 1 + src/types/web/response/index.ts | 8 ++- 5 files changed, 62 insertions(+), 69 deletions(-) diff --git a/src/api/auth/index.ts b/src/api/auth/index.ts index 9dbd38e..f3fb84f 100644 --- a/src/api/auth/index.ts +++ b/src/api/auth/index.ts @@ -1,6 +1,6 @@ import webClient from "@/client/web-client" import { HttpStatus } from "@/constant" -import type { CaptchaResponse, UserAuthResponse } from "@/types/web/response" +import type { CaptchaResponse, MfaAuthResponse, UserAuthResponse } from "@/types/web/response" import type { UsernamePasswordLoginRequest } from "@/types/web/request" /** @@ -21,8 +21,8 @@ async function getCaptcha(): Promise { */ async function usernamePasswordLogin( request: UsernamePasswordLoginRequest -): Promise { - const { data } = await webClient.post("/auth/login", request) +): Promise { + const { data } = await webClient.post("/auth/login", request) return data } diff --git a/src/layouts/dashboard-layout/index.tsx b/src/layouts/dashboard-layout/index.tsx index 51d494f..e5360b1 100644 --- a/src/layouts/dashboard-layout/index.tsx +++ b/src/layouts/dashboard-layout/index.tsx @@ -1,31 +1,24 @@ -import React, { useEffect, useMemo, useState } from "react" -import { useNavigate } from "react-router" -import { - App, - Avatar, - Breadcrumb, - Dropdown, - Layout, - Menu, - type MenuProps, - message, - Space, -} from "antd" +import { type ReactNode, useEffect, useMemo, useState } from "react" +import { Link, Navigate, useNavigate } from "react-router" +import { App, Avatar, Breadcrumb, Dropdown, Layout, Menu, type MenuProps, Space } from "antd" import { DownOutlined } from "@ant-design/icons" import { ApplicationLogo } from "@/components/icon" import { useAppDispatch, useAppSelector } from "@/store" import { useAntBreadcrumbs } from "@/hooks" import { logout } from "@/store/auth-slice" import { MenuApi } from "@/api" -import axios, { type AxiosError } from "axios" +import axios from "axios" import type { TreeNode } from "@/types/tree" import type { MenuItem } from "@/types/entity" import { AppUtils } from "@/utils" import type { GeneralErrorResponse } from "@/types/web/response" -const { Header, Footer, Sider, Content } = Layout type AntMenuItem = Required["items"][number] +export interface DashboardLayoutProps { + children: ReactNode +} + function transformMenuData(nodes: TreeNode[]): AntMenuItem[] { if (!nodes || nodes.length === 0) { return [] @@ -37,21 +30,32 @@ function transformMenuData(nodes: TreeNode[]): AntMenuItem[] { const { item, children } = node const hasChildren = children && children.length > 0 - const menuItem: AntMenuItem = { - key: item.code, - label: item.name, - } + if (item.isVisible) { + const menuItem: AntMenuItem = { + key: item.code, + label: item.name, + } - if (hasChildren) { - // Append children - return { ...menuItem, children: transformMenuData(children) } - } + if (item.path) { + if (item.isExternalLink) { + menuItem.extra = + } else { + menuItem.extra = + } + } - return menuItem + if (hasChildren) { + // Append children + return { ...menuItem, children: transformMenuData(children) } + } + + return menuItem + } + return null }) } -export default function DashboardLayout({ children }: { children: React.ReactNode }) { +export default function DashboardLayout({ children }: DashboardLayoutProps) { const { modal, message } = App.useApp() const user = useAppSelector((store) => store.auth.user!) const dispatch = useAppDispatch() @@ -105,7 +109,7 @@ export default function DashboardLayout({ children }: { children: React.ReactNod return ( -
+
{appTitle} @@ -122,32 +126,18 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
-
+ - + { - console.log(`key = ${key}`) - switch (key) { - case "user-mgmt": - void navigate("/users") - break - case "role-mgmt": - void navigate("/roles") - break - case "menu-mgmt": - void navigate("/menus") - break - } - }} /> - + - {children} + {children} diff --git a/src/page/login/index.tsx b/src/page/login/index.tsx index f439e5b..122ac8b 100644 --- a/src/page/login/index.tsx +++ b/src/page/login/index.tsx @@ -1,9 +1,10 @@ import { type MouseEvent, useCallback, useEffect, useState } from "react" import { Link, useNavigate } from "react-router" -import { Form, Input, Button, Card, message, Divider } from "antd" +import { Form, Input, Button, Card, message, Divider, App } from "antd" import dayjs from "dayjs" import type { AxiosError } from "axios" -// import { useMsal } from "@azure/msal-react" +import { GithubFilled } from "@ant-design/icons" +import { useMsal } from "@azure/msal-react" import { AuthApi } from "@/api" import { useAppDispatch, useAppSelector } from "@/store" import { loginSuccess, updateRegistrationEnabled } from "@/store/auth-slice" @@ -18,8 +19,6 @@ import { DiscordFilled, EmailFilled, } from "@/components/icon" - -import { GithubFilled } from "@ant-design/icons" import { fetchRegisterEnabled } from "@/api/auth" import type { UsernamePasswordLoginRequest } from "@/types/web/request" import type { CaptchaResponse, GeneralErrorResponse } from "@/types/web/response" @@ -29,9 +28,7 @@ export default function LoginPage() { const dispatch = useAppDispatch() const navigate = useNavigate() - // const msalContext = useMsal() - - const [messageApi, contextHolder] = message.useMessage() + const { message } = App.useApp() const [form] = Form.useForm() // const [isLoading, setIsLoading] = useState(false) @@ -77,28 +74,33 @@ export default function LoginPage() { console.log("Login values:", values) const loginResponse = await AuthApi.usernamePasswordLogin(values) - if (loginResponse) { + + if (loginResponse == null) { + message.error("登录失败:服务器响应异常。") + return + } + + if ('mfaToken' in loginResponse) { + + } else { dispatch( loginSuccess({ - user: loginResponse.user, - token: loginResponse.accessToken, + user: loginResponse }) ) - messageApi.success("登录成功", dayjs.duration({ seconds: 3 }).asSeconds()) + message.success("登录成功", dayjs.duration({ seconds: 3 }).asSeconds()) await navigate("/") - } else { - messageApi.error("登录失败:服务器响应异常。") } } catch (errorInfo: unknown) { const error = errorInfo as AxiosError console.log(error) - messageApi.error( + message.error( error.response?.data.message ?? "登录失败,请稍后再试", dayjs.duration({ seconds: 3 }).asSeconds() ) } }, - [dispatch, navigate, messageApi] + [dispatch, navigate] ) /** @@ -190,9 +192,7 @@ export default function LoginPage() { } return ( -
- {contextHolder} - +
{/* 背景装饰元素 */}
@@ -275,7 +275,7 @@ export default function LoginPage() { htmlType="submit" block size="large" - className="h-12 rounded-lg font-semibold text-base bg-gradient-to-r from-blue-500 to-purple-600 border-0 hover:from-blue-600 hover:to-purple-700 transition-all duration-300 shadow-lg hover:shadow-xl"> + className="h-12 rounded-lg font-semibold text-base bg-linear-to-r from-blue-500 to-purple-600 border-0 hover:from-blue-600 hover:to-purple-700 transition-all duration-300 shadow-lg hover:shadow-xl"> 登录 diff --git a/src/types/entity/index.ts b/src/types/entity/index.ts index 5abfaf9..a9de055 100644 --- a/src/types/entity/index.ts +++ b/src/types/entity/index.ts @@ -30,6 +30,7 @@ export interface MenuItem { parentId: number | null code: string sort: number + path: string | null isExternalLink: boolean isVisible: boolean status: Status diff --git a/src/types/web/response/index.ts b/src/types/web/response/index.ts index bd20934..6081d11 100644 --- a/src/types/web/response/index.ts +++ b/src/types/web/response/index.ts @@ -14,9 +14,11 @@ export interface PageResponse { pageable: Pageable } -export interface UserAuthResponse { - user: User - accessToken: string +export interface UserAuthResponse extends User { +} + +export interface MfaAuthResponse { + mfaToken: string } export interface UserDetailResponse extends User {