From 45b62ba131616200bcd858a10ccdad0761a1f3c8 Mon Sep 17 00:00:00 2001 From: leijiaping Date: Tue, 27 Oct 2026 15:18:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A4=9A=E6=9C=BA=E6=9E=84=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=99=BB=E5=BD=95=E6=97=B6=E9=80=89=E6=8B=A9=E6=9C=BA?= =?UTF-8?q?=E6=9E=84(600=E4=B8=B4=E6=97=B6token=E4=BA=8C=E6=AC=A1=E7=99=BB?= =?UTF-8?q?=E5=BD=95);=E8=AE=BE=E7=BD=AE=E5=AF=86=E7=A0=81=E9=A1=B5?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=AD=A6=E7=A4=BA=E6=96=87=E6=A1=88(Alert?= =?UTF-8?q?=E6=96=B0=E5=A2=9Ewarning=E6=A0=B7=E5=BC=8F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/user.ts | 13 ++ src/components/init-password.tsx | 11 +- src/components/ui/alert.tsx | 2 + src/routes/login.tsx | 225 ++++++++++++++++++++++++------- 4 files changed, 198 insertions(+), 53 deletions(-) diff --git a/src/apis/user.ts b/src/apis/user.ts index f68779d..3060349 100644 --- a/src/apis/user.ts +++ b/src/apis/user.ts @@ -9,6 +9,19 @@ export const login = (data = {}) => { }); }; +// 多机构用户选择机构后二次登录(/login 返回 600 + 机构列表时使用) +export const loginByOrg = (tempToken: string, orgId: string | number) => { + return http.post( + "/login_token_user", + { tempToken, orgId }, + { + headers: { + "content-type": HTTP_CONTENT_TYPE.APPLICATION_FORM_URLENCODED, + }, + }, + ); +}; + // 登出 export const logout = () => { return http.post("/logout"); diff --git a/src/components/init-password.tsx b/src/components/init-password.tsx index ca9ee12..351ccee 100644 --- a/src/components/init-password.tsx +++ b/src/components/init-password.tsx @@ -13,6 +13,8 @@ import { import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Spinner } from "@/components/ui/spinner"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { TriangleAlert } from "lucide-react"; import { initUserPassword } from "@/apis/user"; import { useMutation } from "@tanstack/react-query"; import { toast } from "sonner"; @@ -69,10 +71,17 @@ export function InitPassword() {
设置密码
+ + + 首次登录请设置新密码 + + 设置成功后即可正常使用系统。密码长度 6-32 位,支持字母、数字和特殊字符 + +
svg]:text-current *:data-[slot=alert-description]:text-destructive/90", + warning: + "text-amber-600 bg-amber-500/10 border-amber-500/30 [&>svg]:text-current *:data-[slot=alert-description]:text-amber-600/90", }, }, defaultVariants: { diff --git a/src/routes/login.tsx b/src/routes/login.tsx index 220239a..c1cfda7 100644 --- a/src/routes/login.tsx +++ b/src/routes/login.tsx @@ -10,12 +10,16 @@ import { FormControl, FormMessage, } from "@/components/ui/form"; +import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { Spinner } from "@/components/ui/spinner"; -import { login } from "@/apis/user"; +import { login, loginByOrg } from "@/apis/user"; import { useMutation } from "@tanstack/react-query"; import { toast } from "sonner"; +import { useState } from "react"; import logo from "@/assets/image/logo.png"; import { useUserStore } from "@/stores/user"; @@ -32,10 +36,18 @@ const formSchema = z.object({ }), }); +type PendingOrg = { + tempToken: string; + orgs: { orgId: number; orgName: string; administrator?: boolean }[]; +}; + const title = import.meta.env.VITE_APP_TITLE; function RouteComponent() { const router = useRouter(); + // 多机构用户:密码校验通过后先选机构再登录 + const [pendingOrg, setPendingOrg] = useState(null); + const [selectedOrgId, setSelectedOrgId] = useState(""); const form = useForm>({ resolver: zodResolver(formSchema), @@ -45,28 +57,64 @@ function RouteComponent() { }, }); + const finishLogin = (res: any) => { + useUserStore.setState({ token: res.data.token }); + router.navigate({ + to: decodeURIComponent( + decodeURIComponent(router.state.location.search.redirect || "/"), + ), + replace: true, + }); + }; + const { mutateAsync, isPending } = useMutation({ - mutationFn: login, - onSuccess(res) { - useUserStore.setState({ token: res.data.token }); - router.navigate({ - to: decodeURIComponent( - decodeURIComponent(router.state.location.search.redirect || "/"), - ), - replace: true, - }); + mutationFn: async (values: any) => { + try { + return await login(values); + } catch (err: any) { + // 多机构用户:600 + 机构列表 + 临时token,进入选择机构步骤 + if (err?.code === "600" && err?.errorData?.orgs?.length) { + return { needSelectOrg: true, ...err.errorData }; + } + throw err; + } + }, + onSuccess(res: any) { + if (res?.needSelectOrg) { + setSelectedOrgId(String(res.orgs[0].orgId)); + setPendingOrg({ tempToken: res.tempToken, orgs: res.orgs }); + return; + } + finishLogin(res); }, }); + const { mutateAsync: selectOrgMutateAsync, isPending: selectIsPending } = + useMutation({ + mutationFn: (params: { tempToken: string; orgId: string }) => + loginByOrg(params.tempToken, params.orgId), + onSuccess(res) { + finishLogin(res); + }, + }); + const onSubmit = (values: z.infer) => { - console.log( - "router.state.location.search.redirect=======", - router.state.location.search.redirect, - ); const promise = mutateAsync({ ...values, userType: 1, }); + toast.promise(promise, { + loading: "登录中...", + success: (res: any) => (res?.needSelectOrg ? "请选择机构" : "登录成功"), + }); + }; + + const onSubmitOrg = () => { + if (!selectedOrgId || !pendingOrg) return; + const promise = selectOrgMutateAsync({ + tempToken: pendingOrg.tempToken, + orgId: selectedOrgId, + }); toast.promise(promise, { loading: "登录中...", success: "登录成功", @@ -85,44 +133,117 @@ function RouteComponent() {
{title}
-
用户登录
- - - ( - - 用户名 - - - - - - )} - /> - ( - - 密码 - - - - - - )} - /> - - - + {pendingOrg ? ( + <> +
选择机构
+
+ 当前账号属于多个机构,请选择要登录的机构 +
+ + {pendingOrg.orgs.map((org) => ( +
setSelectedOrgId(String(org.orgId))} + className={`flex items-center gap-3 rounded-lg border p-3 cursor-pointer transition-colors ${ + selectedOrgId === String(org.orgId) + ? "border-primary bg-primary/10" + : "hover:bg-black/5" + }`} + > + + +
+ ))} +
+
+ + +
+ + ) : ( + <> +
用户登录
+
+ + ( + + 用户名 + + + + + + )} + /> + ( + + 密码 + + + + + + )} + /> + + + + + )}