feat: update knowledge workspace and menu configuration

This commit is contained in:
吉鑫升 committed 2026-08-18 15:23:14 +08:00
1 parent 64a5305bbf
commit 8af4fed89d
28 files changed
+1250 -219

No files matched your search

@@ -78,7 +78,9 @@ export async function downloadMarkdownDocxFromBackend(options: {
}),
});
if (response.status === 404) {
// Older/offline gateways mount /api/writing but do not provide export/docx;
// FastAPI then answers 404 or 405. The browser converter is feature-equivalent.
if (response.status === 404 || response.status === 405) {
await downloadMarkdownAsDocx(markdown, options.title?.trim() || "document", options.title);
writeYLog({
optType: OptType.export,
@@ -0,0 +1,72 @@
import type { MenuItem } from "./sidebar-menu";
export type CompactMenuPlacement = "sidebar" | "settings";
export interface CompactMenuOverride {
nodeId: string;
placement: CompactMenuPlacement;
sortOrder: number;
label?: string | null;
}
export interface CompactMenuItem extends MenuItem {
compactPlacement: CompactMenuPlacement;
compactOrder: number;
}
function firstRoutePath(items?: MenuItem[]): string | undefined {
for (const item of items ?? []) {
if (item.path) return item.path;
const nested = firstRoutePath(item.children);
if (nested) return nested;
}
return undefined;
}
function isNavigable(item: MenuItem): boolean {
return Boolean(item.path ?? firstRoutePath(item.children));
}
export function applyCompactMenuOverrides(
items: MenuItem[],
overrides: CompactMenuOverride[],
): { sidebarItems: CompactMenuItem[]; settingsItems: CompactMenuItem[] } {
const byId = new Map(overrides.map((override) => [override.nodeId, override]));
const enhanced = items.filter(isNavigable).map((item, index) => {
const override = byId.get(item.id);
return {
...item,
label: override?.label != null && override.label !== "" ? override.label : item.label,
compactPlacement: override?.placement ?? "sidebar",
compactOrder: override?.sortOrder ?? index,
};
});
const sortItems = (list: CompactMenuItem[]) =>
list.slice().sort((a, b) => a.compactOrder - b.compactOrder || a.id.localeCompare(b.id));
return {
sidebarItems: sortItems(enhanced.filter((item) => item.compactPlacement === "sidebar")),
settingsItems: sortItems(enhanced.filter((item) => item.compactPlacement === "settings")),
};
}
export function diffCompactMenuOverrides(items: CompactMenuEditorItem[]): CompactMenuOverride[] {
return items.flatMap((item, index) => {
const renamed = item.label !== item.defaultLabel;
const moved = item.compactPlacement !== "sidebar";
const reordered = index !== item.defaultIndex || item.compactOrder !== item.defaultIndex;
if (!renamed && !moved && !reordered) return [];
return {
nodeId: item.id,
placement: item.compactPlacement,
sortOrder: index,
label: renamed ? item.label : null,
};
});
}
export interface CompactMenuEditorItem extends CompactMenuItem {
defaultLabel: string;
defaultIndex: number;
}
@@ -250,6 +250,12 @@ export const sidebarMenuItems: MenuItem[] = [
},
{ id: "mgmt-skills", label: "技能管理", icon: Sparkles, path: "/page/strategy/skills" },
{ id: "mgmt-memory", label: "记忆管理", icon: Brain, path: "/page/strategy/memory" },
{
id: "resource-markdown-docx-editor",
label: "md转word",
icon: FileText,
path: "/page/workspace/knowledge/markdown-editor",
},
{
id: "mgmt-tasks",
label: "定时任务",
@@ -313,16 +319,10 @@ export const sidebarMenuItems: MenuItem[] = [
// },
{
id: "resource-knowledge-native",
label: "问答梳理",
label: "知识库管理",
icon: BookOpen,
path: "/page/workspace/knowledge",
},
{
id: "resource-markdown-docx-editor",
label: "Markdown 转 Word",
icon: FileText,
path: "/page/workspace/knowledge/markdown-editor",
},
{
id: "resource-knowledge-base",
label: "知识库资源管理",
@@ -69,6 +69,14 @@ function desensitizeLabels(items: MenuItem[], fn: (text: string) => string): Men
}));
}
function useDesensitizeLabel() {
const { data: words = [] } = useSensitiveWords();
return useMemo(
() => (words.length > 0 ? makeDesensitizer(wordsToMap(words)) : desensitize),
[words],
);
}
/**
* 返回合并了 iframe 轻应用、套用了后端「菜单覆盖层」(改名/停用/排序/层级)、
* 按 admin 角色过滤、并对标签做**展示层脱敏**后的菜单。
@@ -82,11 +90,7 @@ function desensitizeLabels(items: MenuItem[], fn: (text: string) => string): Men
export function useSidebarMenuItems(isAdmin: boolean): MenuItem[] {
const { data: iframeApps = [] } = useLightApps({ appType: "iframe", enabledOnly: true });
const { data: overrides = [] } = useMenuOverrides();
const { data: words = [] } = useSensitiveWords();
const desensitizeLabel = useMemo(
() => (words.length > 0 ? makeDesensitizer(wordsToMap(words)) : desensitize),
[words],
);
const desensitizeLabel = useDesensitizeLabel();
return useMemo(
() =>
desensitizeLabels(