- 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 路由生成成功
136 lines
3.9 KiB
TypeScript
136 lines
3.9 KiB
TypeScript
/**
|
||
* 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[];
|
||
}
|