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 路由生成成功
This commit is contained in:
137
scripts/config-seed.sql
Normal file
137
scripts/config-seed.sql
Normal file
@@ -0,0 +1,137 @@
|
||||
-- ============================================================
|
||||
-- config-service 种子数据(v2.1 M8)
|
||||
-- 初始化 28 个内置插件 + 5 种 Layout 模板 + teacher 角色默认映射
|
||||
-- 使用:docker exec -i edu-mysql mysql -uedu -pchangeme next_edu_cloud < scripts/config-seed.sql
|
||||
-- ============================================================
|
||||
|
||||
-- ===== 1. Layout 模板(5 种预置) =====
|
||||
INSERT INTO `config_layout_templates` (`layout_id`, `display_name`, `description`, `available_slots`, `layout_schema`, `is_active`) VALUES
|
||||
('classic', '经典三栏', 'TopBar + SideNav + Main', JSON_ARRAY('top','side','main'), JSON_OBJECT('grid', JSON_OBJECT('rows',1,'cols',1,'areas',JSON_ARRAY(JSON_ARRAY('main')))), TRUE),
|
||||
('focus', '专注模式', '仅 Main 全屏', JSON_ARRAY('main'), JSON_OBJECT('grid', JSON_OBJECT('rows',1,'cols',1,'areas',JSON_ARRAY(JSON_ARRAY('main')))), TRUE),
|
||||
('split', '左右分栏', 'Side + Main', JSON_ARRAY('side','main'), JSON_OBJECT('grid', JSON_OBJECT('rows',1,'cols',2,'areas',JSON_ARRAY(JSON_ARRAY('side','main')))), TRUE),
|
||||
('triple', '三栏布局', 'Side + Main + Right', JSON_ARRAY('side','main','right'), JSON_OBJECT('grid', JSON_OBJECT('rows',1,'cols',3,'areas',JSON_ARRAY(JSON_ARRAY('side','main','right')))), TRUE),
|
||||
('canvas', '自由画布', '可拖拽自由布局', JSON_ARRAY('main'), JSON_OBJECT('grid', JSON_OBJECT('rows',1,'cols',1,'areas',JSON_ARRAY(JSON_ARRAY('main')))), TRUE)
|
||||
ON DUPLICATE KEY UPDATE `display_name` = VALUES(`display_name`);
|
||||
|
||||
-- ===== 2. 插件注册表(28 个内置插件) =====
|
||||
INSERT INTO `config_plugin_registry` (`plugin_id`, `category`, `version`, `display_name`, `description`, `required_roles`, `default_slot`, `default_size`, `default_props`, `is_builtin`, `is_active`) VALUES
|
||||
-- universal(7)
|
||||
('grades-widget', 'universal', '1.0.0', '成绩查询', '查看学生考试成绩', JSON_ARRAY(), 'main', JSON_OBJECT('w',6,'h',4), JSON_OBJECT(), TRUE, TRUE),
|
||||
('homework-widget', 'universal', '1.0.0', '作业列表', '查看作业与提交状态', JSON_ARRAY(), 'main', JSON_OBJECT('w',6,'h',4), JSON_OBJECT(), TRUE, TRUE),
|
||||
('schedule-widget', 'universal', '1.0.0', '课表', '查看本周课表', JSON_ARRAY(), 'main', JSON_OBJECT('w',6,'h',4), JSON_OBJECT(), TRUE, TRUE),
|
||||
('attendance-widget', 'universal', '1.0.0', '考勤', '出勤率统计', JSON_ARRAY(), 'main', JSON_OBJECT('w',4,'h',3), JSON_OBJECT(), TRUE, TRUE),
|
||||
('exams-widget', 'universal', '1.0.0', '考试安排', '即将到来的考试', JSON_ARRAY(), 'main', JSON_OBJECT('w',6,'h',3), JSON_OBJECT(), TRUE, TRUE),
|
||||
('notifications-widget', 'universal', '1.0.0', '通知', '系统通知列表', JSON_ARRAY(), 'main', JSON_OBJECT('w',4,'h',4), JSON_OBJECT(), TRUE, TRUE),
|
||||
('announcements-widget', 'universal', '1.0.0', '公告', '学校公告', JSON_ARRAY(), 'main', JSON_OBJECT('w',6,'h',3), JSON_OBJECT(), TRUE, TRUE),
|
||||
-- sidebar(4)
|
||||
('class-selector', 'sidebar', '1.0.0', '班级选择器', '切换当前班级', JSON_ARRAY(), 'side', JSON_OBJECT('w',2,'h',1), JSON_OBJECT(), TRUE, TRUE),
|
||||
('child-selector', 'sidebar', '1.0.0', '孩子选择器', '家长切换孩子', JSON_ARRAY('parent'), 'side', JSON_OBJECT('w',2,'h',1), JSON_OBJECT(), TRUE, TRUE),
|
||||
('term-switcher', 'sidebar', '1.0.0', '学期切换', '切换学期', JSON_ARRAY(), 'side', JSON_OBJECT('w',2,'h',1), JSON_OBJECT(), TRUE, TRUE),
|
||||
('quick-actions', 'sidebar', '1.0.0', '快捷操作', '常用操作入口', JSON_ARRAY(), 'side', JSON_OBJECT('w',2,'h',2), JSON_OBJECT(), TRUE, TRUE),
|
||||
-- topbar(4)
|
||||
('notification-bell', 'topbar', '1.0.0', '通知铃铛', '顶部通知提醒', JSON_ARRAY(), 'top', JSON_OBJECT('w',1,'h',1), JSON_OBJECT(), TRUE, TRUE),
|
||||
('user-menu', 'topbar', '1.0.0', '用户菜单', '用户头像与下拉菜单', JSON_ARRAY(), 'top', JSON_OBJECT('w',1,'h',1), JSON_OBJECT(), TRUE, TRUE),
|
||||
('global-search', 'topbar', '1.0.0', '全局搜索', '搜索功能', JSON_ARRAY(), 'top', JSON_OBJECT('w',2,'h',1), JSON_OBJECT(), TRUE, TRUE),
|
||||
('locale-switcher', 'topbar', '1.0.0', '语言切换', '中英文切换', JSON_ARRAY(), 'top', JSON_OBJECT('w',1,'h',1), JSON_OBJECT(), TRUE, TRUE),
|
||||
-- teacher(4)
|
||||
('lesson-plan-editor', 'teacher', '1.0.0', '教案编辑器', '编辑教案', JSON_ARRAY('teacher'), 'main', JSON_OBJECT('w',8,'h',6), JSON_OBJECT(), TRUE, TRUE),
|
||||
('question-bank', 'teacher', '1.0.0', '题库管理', '题目管理', JSON_ARRAY('teacher'), 'main', JSON_OBJECT('w',8,'h',6), JSON_OBJECT(), TRUE, TRUE),
|
||||
('textbook-manager', 'teacher', '1.0.0', '教材管理', '教材版本管理', JSON_ARRAY('teacher'), 'main', JSON_OBJECT('w',6,'h',4), JSON_OBJECT(), TRUE, TRUE),
|
||||
('scheduling-rules', 'teacher', '1.0.0', '排课规则', '排课规则配置', JSON_ARRAY('teacher'), 'main', JSON_OBJECT('w',6,'h',4), JSON_OBJECT(), TRUE, TRUE),
|
||||
-- student(4)
|
||||
('error-book', 'student', '1.0.0', '错题本', '错题汇总', JSON_ARRAY('student'), 'main', JSON_OBJECT('w',6,'h',4), JSON_OBJECT(), TRUE, TRUE),
|
||||
('learning-path', 'student', '1.0.0', '学习路径', '个性化学习路径', JSON_ARRAY('student'), 'main', JSON_OBJECT('w',4,'h',4), JSON_OBJECT(), TRUE, TRUE),
|
||||
('elective-selector', 'student', '1.0.0', '选课', '选修课选择', JSON_ARRAY('student'), 'main', JSON_OBJECT('w',6,'h',4), JSON_OBJECT(), TRUE, TRUE),
|
||||
('ai-tutor', 'student', '1.0.0', 'AI 辅导', 'AI 智能辅导', JSON_ARRAY('student'), 'main', JSON_OBJECT('w',4,'h',4), JSON_OBJECT(), TRUE, TRUE),
|
||||
-- parent(2)
|
||||
('child-overview', 'parent', '1.0.0', '孩子概览', '孩子学习概览', JSON_ARRAY('parent'), 'main', JSON_OBJECT('w',6,'h',4), JSON_OBJECT(), TRUE, TRUE),
|
||||
('leave-approval', 'parent', '1.0.0', '请假审批', '审批孩子请假', JSON_ARRAY('parent'), 'main', JSON_OBJECT('w',6,'h',3), JSON_OBJECT(), TRUE, TRUE),
|
||||
-- admin(3)
|
||||
('user-management', 'admin', '1.0.0', '用户管理', '用户账号管理', JSON_ARRAY('admin'), 'main', JSON_OBJECT('w',8,'h',6), JSON_OBJECT(), TRUE, TRUE),
|
||||
('rbac-manager', 'admin', '1.0.0', '权限管理', '角色权限配置', JSON_ARRAY('admin'), 'main', JSON_OBJECT('w',8,'h',6), JSON_OBJECT(), TRUE, TRUE),
|
||||
('plugin-manager', 'admin', '1.0.0', '插件管理', '插件注册与配置', JSON_ARRAY('admin'), 'main', JSON_OBJECT('w',8,'h',6), JSON_OBJECT(), TRUE, TRUE)
|
||||
ON DUPLICATE KEY UPDATE `display_name` = VALUES(`display_name`);
|
||||
|
||||
-- ===== 3. teacher 角色默认 Layout =====
|
||||
INSERT INTO `config_role_layout_default` (`role`, `layout_id`, `slot_overrides`) VALUES
|
||||
('teacher', 'classic', NULL),
|
||||
('student', 'classic', NULL),
|
||||
('parent', 'classic', NULL),
|
||||
('admin', 'classic', NULL)
|
||||
ON DUPLICATE KEY UPDATE `layout_id` = VALUES(`layout_id`);
|
||||
|
||||
-- ===== 4. teacher 角色插件映射(启用 topbar + sidebar + 部分 main) =====
|
||||
INSERT INTO `config_role_plugin_mapping` (`role`, `plugin_id`, `slot`, `sort_order`, `is_enabled`, `widget_props`) VALUES
|
||||
-- topbar
|
||||
('teacher', 'notification-bell', 'top', 1, TRUE, NULL),
|
||||
('teacher', 'global-search', 'top', 2, TRUE, NULL),
|
||||
('teacher', 'locale-switcher', 'top', 3, TRUE, NULL),
|
||||
('teacher', 'user-menu', 'top', 4, TRUE, NULL),
|
||||
-- sidebar
|
||||
('teacher', 'class-selector', 'side', 1, TRUE, NULL),
|
||||
('teacher', 'term-switcher', 'side', 2, TRUE, NULL),
|
||||
('teacher', 'quick-actions', 'side', 3, TRUE, NULL),
|
||||
-- main universal
|
||||
('teacher', 'schedule-widget', 'main', 1, TRUE, NULL),
|
||||
('teacher', 'grades-widget', 'main', 2, TRUE, NULL),
|
||||
('teacher', 'homework-widget', 'main', 3, TRUE, NULL),
|
||||
('teacher', 'exams-widget', 'main', 4, TRUE, NULL),
|
||||
('teacher', 'announcements-widget', 'main', 5, TRUE, NULL),
|
||||
('teacher', 'attendance-widget', 'main', 6, TRUE, NULL),
|
||||
('teacher', 'notifications-widget', 'main', 7, TRUE, NULL),
|
||||
-- main teacher-specific
|
||||
('teacher', 'lesson-plan-editor', 'main', 10, TRUE, NULL),
|
||||
('teacher', 'question-bank', 'main', 11, TRUE, NULL),
|
||||
('teacher', 'textbook-manager', 'main', 12, TRUE, NULL),
|
||||
('teacher', 'scheduling-rules', 'main', 13, TRUE, NULL)
|
||||
ON DUPLICATE KEY UPDATE `is_enabled` = VALUES(`is_enabled`);
|
||||
|
||||
-- ===== 5. student 角色插件映射 =====
|
||||
INSERT INTO `config_role_plugin_mapping` (`role`, `plugin_id`, `slot`, `sort_order`, `is_enabled`, `widget_props`) VALUES
|
||||
('student', 'notification-bell', 'top', 1, TRUE, NULL),
|
||||
('student', 'locale-switcher', 'top', 2, TRUE, NULL),
|
||||
('student', 'user-menu', 'top', 3, TRUE, NULL),
|
||||
('student', 'term-switcher', 'side', 1, TRUE, NULL),
|
||||
('student', 'quick-actions', 'side', 2, TRUE, NULL),
|
||||
('student', 'schedule-widget', 'main', 1, TRUE, NULL),
|
||||
('student', 'grades-widget', 'main', 2, TRUE, NULL),
|
||||
('student', 'homework-widget', 'main', 3, TRUE, NULL),
|
||||
('student', 'exams-widget', 'main', 4, TRUE, NULL),
|
||||
('student', 'announcements-widget', 'main', 5, TRUE, NULL),
|
||||
('student', 'notifications-widget', 'main', 6, TRUE, NULL),
|
||||
('student', 'error-book', 'main', 10, TRUE, NULL),
|
||||
('student', 'learning-path', 'main', 11, TRUE, NULL),
|
||||
('student', 'elective-selector', 'main', 12, TRUE, NULL),
|
||||
('student', 'ai-tutor', 'main', 13, TRUE, NULL)
|
||||
ON DUPLICATE KEY UPDATE `is_enabled` = VALUES(`is_enabled`);
|
||||
|
||||
-- ===== 6. parent 角色插件映射 =====
|
||||
INSERT INTO `config_role_plugin_mapping` (`role`, `plugin_id`, `slot`, `sort_order`, `is_enabled`, `widget_props`) VALUES
|
||||
('parent', 'notification-bell', 'top', 1, TRUE, NULL),
|
||||
('parent', 'locale-switcher', 'top', 2, TRUE, NULL),
|
||||
('parent', 'user-menu', 'top', 3, TRUE, NULL),
|
||||
('parent', 'child-selector', 'side', 1, TRUE, NULL),
|
||||
('parent', 'term-switcher', 'side', 2, TRUE, NULL),
|
||||
('parent', 'grades-widget', 'main', 1, TRUE, NULL),
|
||||
('parent', 'attendance-widget', 'main', 2, TRUE, NULL),
|
||||
('parent', 'announcements-widget', 'main', 3, TRUE, NULL),
|
||||
('parent', 'notifications-widget', 'main', 4, TRUE, NULL),
|
||||
('parent', 'child-overview', 'main', 10, TRUE, NULL),
|
||||
('parent', 'leave-approval', 'main', 11, TRUE, NULL)
|
||||
ON DUPLICATE KEY UPDATE `is_enabled` = VALUES(`is_enabled`);
|
||||
|
||||
-- ===== 7. admin 角色插件映射 =====
|
||||
INSERT INTO `config_role_plugin_mapping` (`role`, `plugin_id`, `slot`, `sort_order`, `is_enabled`, `widget_props`) VALUES
|
||||
('admin', 'notification-bell', 'top', 1, TRUE, NULL),
|
||||
('admin', 'global-search', 'top', 2, TRUE, NULL),
|
||||
('admin', 'locale-switcher', 'top', 3, TRUE, NULL),
|
||||
('admin', 'user-menu', 'top', 4, TRUE, NULL),
|
||||
('admin', 'class-selector', 'side', 1, TRUE, NULL),
|
||||
('admin', 'term-switcher', 'side', 2, TRUE, NULL),
|
||||
('admin', 'quick-actions', 'side', 3, TRUE, NULL),
|
||||
('admin', 'announcements-widget', 'main', 1, TRUE, NULL),
|
||||
('admin', 'notifications-widget', 'main', 2, TRUE, NULL),
|
||||
('admin', 'user-management', 'main', 10, TRUE, NULL),
|
||||
('admin', 'rbac-manager', 'main', 11, TRUE, NULL),
|
||||
('admin', 'plugin-manager', 'main', 12, TRUE, NULL)
|
||||
ON DUPLICATE KEY UPDATE `is_enabled` = VALUES(`is_enabled`);
|
||||
Reference in New Issue
Block a user