R18-1/R18-3: 接入企业微信入口绑定与免登回退

这个提交包含在:
2026-03-22 08:33:53 +08:00
父节点 2a1ffe66f3
当前提交 215e3967bc
共修改 15 个文件,包含 1394 行新增和 26 行删除
+33 -12
查看文件
@@ -1,7 +1,7 @@
import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { useNavigate, useSearchParams } from "react-router-dom";
import { fetchSsoProviders } from "../api/auth";
import { fetchSsoProviders, fetchWecomConfig } from "../api/auth";
import { mapApiErrorMessage } from "../api/errorMessages";
import { isApiError } from "../api/types";
import { useAuth } from "../auth/AuthContext";
@@ -39,6 +39,12 @@ export function LoginPage() {
staleTime: 60_000,
});
const oidcProvider = (ssoProvidersQuery.data ?? []).find((item) => item.id === "oidc" && item.enabled);
const wecomConfigQuery = useQuery({
queryKey: ["auth", "wecom-config"],
queryFn: () => fetchWecomConfig(),
staleTime: 60_000,
});
const wecomEntry = wecomConfigQuery.data?.enabled ? wecomConfigQuery.data : null;
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
@@ -114,19 +120,34 @@ export function LoginPage() {
</Button>
</form>
{oidcProvider ? (
{oidcProvider || wecomEntry ? (
<div className="space-y-3 border-t border-zinc-200 pt-4">
<p className="text-xs uppercase tracking-wide text-zinc-500">企业统一入口</p>
<Button
className="w-full"
type="button"
variant="secondary"
onClick={() => {
window.location.assign(`${oidcProvider.loginUrl}?returnTo=${encodeURIComponent(redirectTo)}`);
}}
>
使用{oidcProvider.label}
</Button>
{wecomEntry?.loginUrl ? (
<Button
className="w-full"
type="button"
variant="secondary"
onClick={() => {
const loginUrl = wecomEntry.loginUrl ?? "/api/auth/wecom/login";
window.location.assign(loginUrl.replace(/returnTo=%2Fportal$/, `returnTo=${encodeURIComponent(redirectTo)}`));
}}
>
使用企业微信进入工作台
</Button>
) : null}
{oidcProvider ? (
<Button
className="w-full"
type="button"
variant="secondary"
onClick={() => {
window.location.assign(`${oidcProvider.loginUrl}?returnTo=${encodeURIComponent(redirectTo)}`);
}}
>
使用{oidcProvider.label}
</Button>
) : null}
</div>
) : null}
</div>
+29
查看文件
@@ -1,8 +1,22 @@
import { useMutation, useQuery } from "@tanstack/react-query";
import { useSearchParams } from "react-router-dom";
import { fetchWecomConfig, startWecomBind, unbindWecom } from "../api/auth";
import { PageHeader } from "../components/common/PageHeader";
import { useAuth } from "../auth/AuthContext";
import { Button } from "../components/ui/button";
export function ProfilePage() {
const { me, permissions } = useAuth();
const [searchParams] = useSearchParams();
const wecomConfigQuery = useQuery({ queryKey: ["auth", "wecom-config"], queryFn: () => fetchWecomConfig(), staleTime: 60_000 });
const bindMutation = useMutation({
mutationFn: async () => startWecomBind("/settings/profile"),
onSuccess: (payload) => { window.location.assign(payload.loginUrl); },
});
const unbindMutation = useMutation({
mutationFn: () => unbindWecom(),
onSuccess: () => { window.location.reload(); },
});
return (
<div>
@@ -38,6 +52,21 @@ export function ProfilePage() {
</dl>
</section>
<section className="card-std mt-4">
<h3 className="card-title">企业微信绑定</h3>
<div className="space-y-3 text-sm text-zinc-700">
<div>接入状态:<span className="font-medium text-zinc-900">{wecomConfigQuery.data?.enabled ? "已启用" : "未启用"}</span></div>
<div>绑定状态:<span className="font-medium text-zinc-900">{me?.wecomBinding?.externalUserId ?? "未绑定"}</span></div>
{searchParams.get("wecomBind") ? (
<div className="rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-2">绑定结果:{searchParams.get("wecomBind")}</div>
) : null}
<div className="flex flex-wrap gap-2">
<Button type="button" variant="secondary" disabled={!wecomConfigQuery.data?.enabled || bindMutation.isPending} onClick={() => bindMutation.mutate()}>绑定企业微信</Button>
<Button type="button" variant="secondary" disabled={!me?.wecomBinding || unbindMutation.isPending} onClick={() => unbindMutation.mutate()}>解绑企业微信</Button>
</div>
</div>
</section>
<section className="card-std mt-4">
<h3 className="card-title">权限列表</h3>
<div className="flex flex-wrap gap-2">