diff --git a/src/lib/rbac.tsx b/src/lib/rbac.tsx index eb8955e..da6f641 100644 --- a/src/lib/rbac.tsx +++ b/src/lib/rbac.tsx @@ -70,15 +70,17 @@ export const NAV: NavGroup[] = [ // 按当前身份角色过滤导航 export function navForRole(role: string | null): NavGroup[] { const { isAuthed } = useAuth(); - // 平台管理方:恒显示完整运营管理导航(角色暂未加载时也按 operator 视作,避免「只见园区管理」) - if (isAuthed && !isParkUser()) { - const r = role || "operator"; + // 平台管理方:持平台令牌即恒显示完整运营管理导航。 + // 不再叠加 !isParkUser() —— 避免「本会话曾登录过园区端/残留园区凭据」导致登录后只见园区管理, + // 只有刷新/重新拉取身份后才恢复。平台令牌存在即平台优先。 + if (isAuthed) { + const r = role === "operator" ? role : "operator"; return NAV.map((g) => ({ ...g, items: g.items.filter((i) => !i.roles || i.roles.includes(r)), })).filter((g) => g.items.length > 0); } - // 园区端(园区账密登录,无平台角色):只显示园区管理(本园区) + // 园区端(园区账密登录,无平台令牌):只显示园区管理(本园区) if (isParkUser()) { return [{ title: "园区管理", items: [{ to: "/park", label: "园区管理", icon: Building2 }] }]; } diff --git a/src/lib/useUrlTab.ts b/src/lib/useUrlTab.ts new file mode 100644 index 0000000..2e127a3 --- /dev/null +++ b/src/lib/useUrlTab.ts @@ -0,0 +1,31 @@ +import * as React from "react"; +import { useSearchParams } from "react-router-dom"; + +/** + * URL 驱动的 Tab 状态:把当前选中项同步到 ``?tab=``,刷新/回退均保持。 + * key 默认「tab」,可用 ``param`` 覆盖(如一页内多个 Tab 组)。 + * + * 用法: + * const [tab, setTab] = useUrlTab("screens"); + * ... + */ +export function useUrlTab(defaultKey: string, param = "tab"): [string, (v: string) => void] { + const [searchParams, setSearchParams] = useSearchParams(); + const current = searchParams.get(param) || defaultKey; + + const setTab = React.useCallback( + (v: string) => { + setSearchParams( + (prev) => { + const next = new URLSearchParams(prev); + next.set(param, v); + return next; + }, + { replace: true }, + ); + }, + [setSearchParams, param], + ); + + return [current, setTab]; +} diff --git a/src/pages/compute/index.tsx b/src/pages/compute/index.tsx index d41a1c9..1b44f85 100644 --- a/src/pages/compute/index.tsx +++ b/src/pages/compute/index.tsx @@ -7,6 +7,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Loader2 } from "lucide-react"; import { computeApi } from "@/api/resources"; +import { useUrlTab } from "@/lib/useUrlTab"; import ChannelsPage from "./channels"; import StatsPage from "./stats"; import ModelsPage from "./models"; @@ -35,7 +36,7 @@ const TABS = [ ]; export default function ComputePage() { - const [tab, setTab] = React.useState("overview"); + const [tab, setTab] = useUrlTab("overview", "tab"); return (
diff --git a/src/pages/park/index.tsx b/src/pages/park/index.tsx index 4663f93..645dbfe 100644 --- a/src/pages/park/index.tsx +++ b/src/pages/park/index.tsx @@ -3,6 +3,7 @@ import { Card, CardContent } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { ParkTenantProvider, useParkTenant } from "./tenant-context"; +import { useUrlTab } from "@/lib/useUrlTab"; import { TenantTab } from "./tenant-tab"; import { ScreensTab } from "./screens-tab"; import { ScreenTab } from "./screen-tab"; @@ -35,13 +36,14 @@ function TenantBar() { function ParkPageInner() { const { tenantId, isParkMode } = useParkTenant(); + const [tab, setTab] = useUrlTab(isParkMode ? "screens" : "tenant"); return (
- + {!isParkMode && 园区创建} 屏幕管理