feat: 菜单管理(侧边栏覆盖层)

管理员可对一/二/三级菜单(含注入的 iframe 轻应用节点)改名、停用/启用、
排序、跨层级移动。静态菜单仍是图标/路由来源,后端只存每节点覆盖项。

后端: /api/menu-overrides(全局共享;开放读、admin 原子替换;≤3 级校验),
新增 persistence/menu_overrides 持久化层(create_all 自动建表)。
前端: 合并逻辑 menu-overrides.ts、MenuManagementPage(@dnd-kit 拖拽+移动到
父级选择器+上下箭头+改名/停用)、左下角设置入口、Header/侧边栏自动生效。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
董宏凯andClaude Opus 4.8 committed 2026-06-16 23:34:12 +08:00
1 parent 8d0759123a
commit 1c8b5b23a1
19 files changed
+1480 -8

No files matched your search

@@ -455,9 +455,15 @@ export function AIWritingPanel({
/>
)}
{/* 技能检索 pending:用户点击瞬间到 awaiting 之间,始终显示 SkillStepsCard 占位
steps.length === 0 时 SkillStepsCard 内部会渲染骨架行,不让用户干等空白 */}
{state.skillSearchPending && (
{/* 技能检索过程实时展示:
① skillSearchPending —— PAUSE-1 用户点"重新搜索/按输入追加"瞬间到 awaiting
之间的占位(steps 为空时 SkillStepsCard 内部渲染骨架行,不让用户干等空白);
② 首次素材收集(researching 且已有 skill_search_step)—— 让"调用了哪个技能 /
检索到多少条 / 检索完成"像智能体问答一样边搜边显示,而不是搜完才在素材确认
卡里一次性看到。素材确认(awaiting_material_confirm)时由 InterventionCard
接管展示同一份 steps,这里 researching 条件自动失效,不会重复。 */}
{(state.skillSearchPending
|| (state.status === 'researching' && state.skillSearchSteps.length > 0)) && (
<div className="px-4 pb-2">
<SkillStepsCard steps={state.skillSearchSteps} inProgress />
</div>
@@ -467,8 +473,11 @@ export function AIWritingPanel({
state.status !== 'writing_draft' &&
!(state.status === 'writing_outline' && (state.outlineStreamingText || state.outlineThinkingText)) &&
!(state.status === 'reviewing' && state.streamingReviewItems.length > 0) &&
/* 技能检索占位卡 / 选择打字机 已经在流式展示,不再叠加 skeleton */
/* 技能检索占位卡 / 选择打字机 / 已流式的技能步骤 已经在展示,不再叠加 skeleton。
(研究阶段一旦有 skill_search_step,就改由上面的 SkillStepsCard 实时呈现进度,
研究词生成阶段 steps 还为空时仍走 skeleton 透传 researchProgress 文案。) */
!state.skillSearchPending &&
!(state.status === 'researching' && state.skillSearchSteps.length > 0) &&
!(state.status === 'researching' && (state.skillSelectStreamingText || state.skillSelectThinkingText)) &&
/* 意图解析流式过程中(LLM 还在吐 JSON)不要显示 skeleton */
!state.intentStreamingText && !state.intentThinkingText && (