Archived
接入统一鉴权中心(Keycloak,ADR-012);活动模块 L1/L2 运营态模版(历史活动列表+检索+沙箱);数据域与工作台界面改版;个人中心 /me
This commit is contained in:
@@ -11,10 +11,11 @@
|
||||
* 刷新与深链都能完整还原;前端零过程状态。
|
||||
*/
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import type { Session } from '@contracts';
|
||||
import { logoutRoute, sessionRoute } from '@contracts';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import type { Principal } from '@contracts';
|
||||
import { meRoute } from '@contracts';
|
||||
import { call } from '@web-shared/typed-fetch.ts';
|
||||
import { logout as kcLogout } from '@web-shared/auth.ts';
|
||||
import { Ico } from '@web-shared/Ico.tsx';
|
||||
import { TABS, ZM, ZONES, routeLabel } from '@web-shared/gov/gov-route.ts';
|
||||
import type { GovZone, Nav, Route } from '@web-shared/gov/gov-route.ts';
|
||||
@@ -24,14 +25,14 @@ import { useGovObjects, useGovTasks } from '@web-shared/gov/gov-api.ts';
|
||||
import { domainShort, sortDomains } from '@web-shared/gov/domains.ts';
|
||||
import { queryKeys } from './query.ts';
|
||||
import { BlueprintHome } from '@features/gov-blueprint';
|
||||
import { TasksHome, TaskLogPage, TaskPage, WfPage } from '@features/gov-task';
|
||||
import { TasksHome, ActvPage, TaskLogPage, TaskPage, WfPage } from '@features/gov-task';
|
||||
import { RunsHome } from '@features/gov-runs';
|
||||
import { EmpPage, PeopleHome, RolePage } from '@features/gov-staff';
|
||||
import { PkgPage, WorkflowsHome } from '@features/gov-process';
|
||||
import { TriggerPage, TriggerRulesHome } from '@features/gov-trigger';
|
||||
import { ActPage, LibraryHome } from '@features/gov-activity';
|
||||
import { ToolPage, ToolsHome } from '@features/gov-tool';
|
||||
import { DataHome, LedgersHome, ObjPage } from '@features/gov-data';
|
||||
import { DataHome, LedgersHome, ObjPage, RowPage } from '@features/gov-data';
|
||||
import { ArchiveHome } from '@features/gov-archive';
|
||||
import { TestbedHome } from '@features/gov-testbed';
|
||||
import { CockpitHome } from '@features/gov-memory';
|
||||
@@ -65,21 +66,20 @@ const writePlace = (surface: Surface, p: Place): string =>
|
||||
|
||||
export function GovShell({ surface }: { surface: Surface }) {
|
||||
const [place, setPlace] = useState<Place>(() => readPlace(surface));
|
||||
const session = useQuery({ queryKey: queryKeys.session, queryFn: () => call<Session>(sessionRoute) });
|
||||
const me$ = useQuery({ queryKey: queryKeys.me, queryFn: () => call<Principal>(meRoute) });
|
||||
// 运行态的两份数据只在运营中心取:设计中心不看运行数字(纪律),也不必替它要
|
||||
const tasks = useGovTasks({ enabled: surface === 'ops' });
|
||||
const objects = useGovObjects({ enabled: surface === 'ops' });
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
/** 登出后整体清空缓存 —— 治理台看到的是全组织数据,更不能留给下一个人 */
|
||||
const logout = useMutation({
|
||||
mutationFn: () => call<{ ok: boolean }>(logoutRoute),
|
||||
onSuccess: () => {
|
||||
queryClient.clear();
|
||||
// 整页导航:SSE 连接、组件状态、路由位置一并归零 —— 登出不留残影
|
||||
window.location.assign('/');
|
||||
},
|
||||
});
|
||||
/*
|
||||
* 登出(ADR-012):先整体清空缓存 —— 治理台看到的是全组织数据,更不能留给下一个人;
|
||||
* 再走 Keycloak end-session 终结 SSO 会话(`/api/logout` 已退役 410,不再调)。
|
||||
*/
|
||||
const logout = () => {
|
||||
queryClient.clear();
|
||||
void kcLogout();
|
||||
};
|
||||
|
||||
// 浏览器前进 / 后退即导航 —— 路由状态就是应用状态
|
||||
useEffect(() => {
|
||||
@@ -123,7 +123,7 @@ export function GovShell({ surface }: { surface: Surface }) {
|
||||
const { zone, tab, stack } = place;
|
||||
const meta = ZM[zone];
|
||||
const top = stack[stack.length - 1];
|
||||
const me = session.data?.staff;
|
||||
const me = me$.data?.staff;
|
||||
const other: Surface = surface === 'design' ? 'ops' : 'design';
|
||||
|
||||
/** 告警计数:异常工作流 + 非放行终态的任务 —— 只有运营中心管这个 */
|
||||
@@ -141,11 +141,15 @@ export function GovShell({ surface }: { surface: Surface }) {
|
||||
{ label: SURFACE_META[surface].brand, go: () => goZone(zone) },
|
||||
];
|
||||
crumbs.push({ label: meta.title, go: () => go({ zone, tab, stack: [] }) });
|
||||
// 台账区的页签名就是域键 —— 静态表里查不到,呈现走人读短名
|
||||
crumbs.push({
|
||||
label: TABS[zone].find((t) => t.key === tab)?.label ?? (zone === 'ledgers' ? domainShort(effectiveTab) : tab),
|
||||
go: () => go({ zone, tab, stack: [] }),
|
||||
});
|
||||
/* 页签级只在页签栏真出现时摆 —— 单页签的区页签栏收起(下文 g-tabs 同口径),
|
||||
面包屑再摆一级同义页签名就是冗余(2026-08-25 用户定,活动区起) */
|
||||
if (zone === 'ledgers' || TABS[zone].length > 1) {
|
||||
// 台账区的页签名就是域键 —— 静态表里查不到,呈现走人读短名
|
||||
crumbs.push({
|
||||
label: TABS[zone].find((t) => t.key === tab)?.label ?? (zone === 'ledgers' ? domainShort(effectiveTab) : tab),
|
||||
go: () => go({ zone, tab, stack: [] }),
|
||||
});
|
||||
}
|
||||
stack.forEach((r, i) =>
|
||||
crumbs.push({
|
||||
label: routeLabel(r),
|
||||
@@ -193,18 +197,20 @@ export function GovShell({ surface }: { surface: Surface }) {
|
||||
告警<b>{alerts}</b>
|
||||
</button>
|
||||
)}
|
||||
{/* 三面互跳:另一面按授权亮灯,工作台人人可回 */}
|
||||
{canSeeSurface(session.data, other) && (
|
||||
{/* 三面互跳:另一面与工作台都按 ends 亮灯(ADR-012,权限只到端) */}
|
||||
{canSeeSurface(me$.data, other) && (
|
||||
<a className="g-tbtn" href={SURFACE_META[other].path}>
|
||||
<Ico name={other === 'design' ? 'route' : 'gauge'} s={13} />
|
||||
{SURFACE_META[other].brand}
|
||||
</a>
|
||||
)}
|
||||
<a className="g-tbtn" href="/">
|
||||
<Ico name="list-checks" s={13} />
|
||||
工作台
|
||||
</a>
|
||||
<Btn className="g-tbtn" onClick={() => logout.mutate()} busy={logout.isPending}>
|
||||
{(me$.data?.ends ?? []).includes('workbench') && (
|
||||
<a className="g-tbtn" href="/">
|
||||
<Ico name="list-checks" s={13} />
|
||||
工作台
|
||||
</a>
|
||||
)}
|
||||
<Btn className="g-tbtn" onClick={logout}>
|
||||
<Ico name="log-out" s={13} />
|
||||
登出
|
||||
</Btn>
|
||||
@@ -323,10 +329,14 @@ function Detail({ route, nav }: { route: Route; nav: Nav }) {
|
||||
return <TriggerPage id={route.id} />;
|
||||
case 'act':
|
||||
return <ActPage id={route.id} />;
|
||||
case 'actd':
|
||||
return <ActvPage id={route.id} />;
|
||||
case 'tool':
|
||||
return <ToolPage id={route.id} />;
|
||||
case 'obj':
|
||||
return <ObjPage id={route.id} />;
|
||||
return <ObjPage id={route.id} nav={nav} />;
|
||||
case 'row':
|
||||
return <RowPage id={route.id} />;
|
||||
case 'llm':
|
||||
return <LlmPage id={route.id} />;
|
||||
case 'chan':
|
||||
|
||||
Reference in New Issue
Block a user