- shadcn/ui 标准化:废弃纸感令牌,统一 bg-background/text-foreground 等 - Tailwind v4 + @theme inline,移除 tailwind.config.js - React 19 use() + Suspense 流式渲染,首屏骨架秒出 - 三级错误边界:Route → Section → Widget 层层兜底 - 错误上报:useErrorReport → sendBeacon → /api/log mock 端点 - 三层安全边界:L1 角色门禁 / L2 权限点门禁 / L3 数据范围 - 权限位图 base36 压缩:67 权限点 → ~14 字符,JWT 体积减少 ≥ 99% - notify 统一 Toast 封装,禁止业务直接 import sonner - PluginBoundary 替代 PluginLoader(错误边界 + Suspense + Skeleton 三件套) 验证:typecheck 0 错误 / lint 0 错误 / build 6 路由生成成功
81 lines
2.2 KiB
TypeScript
81 lines
2.2 KiB
TypeScript
import { useSyncExternalStore } from "react";
|
||
import type {
|
||
PluginStoreState,
|
||
ThemeMode,
|
||
Locale,
|
||
} from "@edu/shared-ts/contracts";
|
||
|
||
/**
|
||
* usePluginStore - Zustand 全局状态 Hook(portal-shell spec §5.2.2)
|
||
*
|
||
* 封装 Zustand store 的订阅,提供 theme/locale/sidebarCollapsed 状态管理。
|
||
* 此 hook 是通用封装,实际 store 实例由 portal-shell 创建并注入。
|
||
*
|
||
* 设计原则(@edu/hooks):hooks 不直接依赖特定 store 实例,
|
||
* 通过 subscribe/getSnapshot 与外部 store 交互。
|
||
*
|
||
* 关联:portal-shell spec §5.2.2、§9.3
|
||
*/
|
||
|
||
/** Store 实例接口(与 Zustand create() 返回值兼容) */
|
||
export interface PluginStoreInstance extends PluginStoreState {
|
||
subscribe: (listener: () => void) => () => void;
|
||
getState: () => PluginStoreState;
|
||
}
|
||
|
||
/** 全局 store 引用(由 portal-shell 注入) */
|
||
let globalStore: PluginStoreInstance | null = null;
|
||
|
||
/**
|
||
* 注入全局 PluginStore 实例(portal-shell 启动时调用)
|
||
*
|
||
* @example
|
||
* import { usePluginStore as originalStore } from "@/shell/PluginStore";
|
||
* injectPluginStore(originalStore as PluginStoreInstance);
|
||
*/
|
||
export function injectPluginStore(store: PluginStoreInstance): void {
|
||
globalStore = store;
|
||
}
|
||
|
||
/**
|
||
* 读取 PluginStore 全局状态(theme/locale/sidebarCollapsed)
|
||
*
|
||
* @example
|
||
* const { theme, setTheme } = usePluginStore();
|
||
*/
|
||
export function usePluginStore(): PluginStoreState {
|
||
return useSyncExternalStore(
|
||
(listener) => {
|
||
if (!globalStore) return () => {};
|
||
return globalStore.subscribe(listener);
|
||
},
|
||
() => {
|
||
if (!globalStore) {
|
||
return DEFAULT_STATE;
|
||
}
|
||
return globalStore.getState();
|
||
},
|
||
() => DEFAULT_STATE,
|
||
);
|
||
}
|
||
|
||
/** 默认状态(store 未注入时使用) */
|
||
const DEFAULT_STATE: PluginStoreState = {
|
||
theme: "light",
|
||
setTheme: (_theme: ThemeMode) => {
|
||
// store 未注入时的空操作
|
||
},
|
||
locale: "zh-CN",
|
||
setLocale: (_locale: Locale) => {
|
||
// store 未注入时的空操作
|
||
},
|
||
sidebarCollapsed: false,
|
||
toggleSidebar: () => {
|
||
// store 未注入时的空操作
|
||
},
|
||
unreadNotificationIds: [],
|
||
markNotificationsRead: (_ids: string[]) => {
|
||
// store 未注入时的空操作
|
||
},
|
||
};
|