Files
Edu/packages/shared-ts/src/contracts/layout.ts
SpecialX 9cedf0c437 feat(portal-shell): v2.0 P0 shadcn standardization + security + streaming + error handling
- 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 路由生成成功
2026-07-17 16:10:05 +08:00

136 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Layout 与 Slot 配置类型portal-shell spec §4、§6.2
*
* 对应 config-service PluginConfigResponse三层合并后的插件配置
* 类型与 services/config-service 的 GraphQL schema 对齐,
* 通过 apollo-router GraphQL 查询 pluginConfig(userId, role) 获取。
*
* 关联portal-shell spec §4.1 5 种 Layout 模板、§4.2 Slot 系统、§6.2 PluginConfigResponse
*/
import type { Role } from "./plugin.js";
/** Layout 模板信息(对应 config-service LayoutTemplateInfo */
export interface LayoutTemplateInfo {
/** Layout ID'classic' | 'focus' | 'split' | 'triple' | 'canvas' */
layoutId: string;
/** 显示名 */
displayName: string;
/** 描述 */
description: string;
/** 可用 slot 列表,如 ["top", "side", "main"] */
availableSlots: string[];
/** Layout schema JSON 字符串grid 配置等) */
layoutSchemaJson: string;
}
/** Slot 配置(对应 config-service SlotConfig */
export interface SlotConfig {
/** Slot 名称:'top' | 'side' | 'main' | 'main-left' | 'main-right' | 'right' | 'canvas-grid' */
slotName: string;
/** 导航项列表side slot 的导航菜单项) */
navItems: string[];
}
/** 插件放置(三层合并后,对应 config-service PluginPlacement */
export interface PluginPlacement {
/** 插件 ID */
pluginId: string;
/** 插入的 slot 名称 */
slot: string;
/** 显示顺序(升序) */
sortOrder: number;
/** 尺寸 JSON 字符串({colSpan, rowSpan} */
sizeJson: string;
/** 三层合并后的最终 props JSON 字符串 */
propsJson: string;
/** 是否可见 */
isVisible: boolean;
}
/** 插件注册项(对应 config-service PluginRegistryItem */
export interface PluginRegistryItem {
/** 插件 ID */
pluginId: string;
/** 分类:'universal' | 'sidebar' | 'topbar' | 'teacher' | 'student' | 'parent' | 'admin' */
category: string;
/** 版本semver */
version: string;
/** 显示名 */
displayName: string;
/** 描述 */
description: string;
/** 可访问此插件的角色列表 */
requiredRoles: string[];
/** 是否内置插件 */
isBuiltin: boolean;
/** 是否全局启用 */
isActive: boolean;
}
/**
* 三层合并后的插件配置响应(对应 config-service PluginConfigResponse
*
* 由 config-service 合并三层配置后返回:
* Layer 1: plugin_registry系统默认
* Layer 2: role_plugin_mapping角色模板
* Layer 3: user_layout_override用户覆盖
*/
export interface PluginConfigResponse {
/** 当前启用的 Layout 模板 */
activeLayout: LayoutTemplateInfo | null;
/** Slot 配置列表 */
slots: SlotConfig[];
/** 插件放置列表(三层合并后) */
plugins: PluginPlacement[];
/** 插件注册表(所有可用插件) */
registry: PluginRegistryItem[];
}
/** Layout 模板 ID 枚举portal-shell spec §4.1 */
export const LAYOUT_TEMPLATE_IDS = [
"classic",
"focus",
"split",
"triple",
"canvas",
] as const;
/** Layout 模板 ID 类型 */
export type LayoutTemplateId = (typeof LAYOUT_TEMPLATE_IDS)[number];
/** Slot 名称枚举portal-shell spec §4.2 */
export const SLOT_NAMES = [
"top",
"side",
"main",
"main-left",
"main-right",
"right",
"canvas-grid",
] as const;
/** Slot 名称类型 */
export type SlotName = (typeof SLOT_NAMES)[number];
/** 角色-Layout 默认配置admin 配置角色默认模板) */
export interface RoleLayoutDefault {
role: Role;
layoutId: string;
slotOverrides: Record<string, unknown>;
}
/** 用户布局覆盖(用户自定义) */
export interface UserLayoutOverride {
userId: string;
activeLayout: string;
slotOverrides: Record<string, unknown>;
pluginPlacements: Array<{
pluginId: string;
slot: string;
sortOrder: number;
size: { colSpan: number; rowSpan: number };
props: Record<string, unknown>;
}>;
hiddenPlugins: string[];
}