Procházet zdrojové kódy

feat: update knowhub frontend

elksmmx před 1 měsícem
rodič
revize
42557a606e

+ 13 - 59
knowhub/frontend/src/components/common/SideDrawer.tsx

@@ -1,6 +1,4 @@
-import { useState, useEffect } from 'react';
-import { createPortal } from 'react-dom';
-import { ChevronRight, PanelLeftClose, X } from 'lucide-react';
+import { X } from 'lucide-react';
 import { cn } from '../../lib/utils';
 import { cn } from '../../lib/utils';
 
 
 interface SideDrawerProps {
 interface SideDrawerProps {
@@ -11,43 +9,23 @@ interface SideDrawerProps {
   width?: string;
   width?: string;
 }
 }
 
 
-export function SideDrawer({ isOpen, onClose, title, children, width = 'w-[650px]' }: SideDrawerProps) {
-  const [mounted, setMounted] = useState(false);
-  const [expanded, setExpanded] = useState(false);
-
-  useEffect(() => {
-    setMounted(true);
-  }, []);
-
-  useEffect(() => {
-    if (isOpen) setExpanded(false);
-  }, [isOpen]);
-
-  if (!mounted) return null;
-
-  return createPortal(
-    <>
-      <div
-        className={cn(
-          "fixed top-0 left-0 h-full bg-white shadow-2xl z-[220] transform transition-all duration-300 ease-in-out overflow-hidden flex flex-col border-r border-slate-200",
-          expanded ? width : "w-0",
-          isOpen && expanded ? "translate-x-0" : "-translate-x-full"
-        )}
-      >
-        <div className="border-b border-slate-100 bg-white sticky top-0 z-10 px-4 py-4">
+export function SideDrawer({ isOpen, onClose, title, children, width = 'w-[360px]' }: SideDrawerProps) {
+  return (
+    <aside
+      className={cn(
+        "shrink-0 h-full overflow-hidden transition-[width,opacity,margin] duration-300 ease-in-out",
+        isOpen ? cn(width, "opacity-100 ml-4") : "w-0 opacity-0 ml-0"
+      )}
+    >
+      <div className="h-full bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden flex flex-col">
+        <div className="border-b border-slate-100 bg-white shrink-0 px-4 py-4">
           <div className="flex justify-between items-center gap-2">
           <div className="flex justify-between items-center gap-2">
-            <button
-              onClick={() => setExpanded(false)}
-              className="p-2 hover:bg-slate-100 rounded-lg text-slate-400 hover:text-slate-600 transition-colors"
-              title="收起"
-            >
-              <PanelLeftClose size={18} />
-            </button>
             <div className="text-lg font-bold text-slate-900 min-w-0 flex-1 truncate">{title}</div>
             <div className="text-lg font-bold text-slate-900 min-w-0 flex-1 truncate">{title}</div>
             <button
             <button
               onClick={onClose}
               onClick={onClose}
               className="p-2 hover:bg-slate-100 rounded-lg text-slate-400 hover:text-slate-600 transition-colors"
               className="p-2 hover:bg-slate-100 rounded-lg text-slate-400 hover:text-slate-600 transition-colors"
               title="关闭"
               title="关闭"
+              type="button"
             >
             >
               <X size={18} />
               <X size={18} />
             </button>
             </button>
@@ -57,30 +35,6 @@ export function SideDrawer({ isOpen, onClose, title, children, width = 'w-[650px
           {children}
           {children}
         </div>
         </div>
       </div>
       </div>
-
-      <button
-        onClick={() => {
-          if (!isOpen) {
-            setExpanded(true);
-          } else {
-            setExpanded((prev) => !prev);
-          }
-        }}
-        className={cn(
-          "fixed left-0 top-24 z-[230] flex items-center gap-1 rounded-r-xl border border-l-0 border-slate-200 bg-white/95 px-2 py-3 shadow-lg backdrop-blur transition-all duration-300 hover:bg-white text-slate-500 hover:text-slate-700",
-          isOpen && expanded ? "-translate-x-full opacity-0 pointer-events-none" : "translate-x-0 opacity-100"
-        )}
-        title={isOpen ? "展开详情页" : "打开详情页"}
-      >
-        <ChevronRight size={16} />
-        <span
-          className="text-[10px] font-bold tracking-widest"
-          style={{ writingMode: 'vertical-rl', textOrientation: 'mixed' }}
-        >
-          详情页
-        </span>
-      </button>
-    </>,
-    document.body
+    </aside>
   );
   );
 }
 }

+ 13 - 11
knowhub/frontend/src/components/dashboard/CategoryTree.tsx

@@ -39,7 +39,9 @@ function HorizontalTreeNode({ node, onSelect, selectedId, level, highlightLeafNa
   const inHighlight = nodeHasHighlightedLeaf(node, highlightLeafNames);
   const inHighlight = nodeHasHighlightedLeaf(node, highlightLeafNames);
   const isLeafHighlighted = highlightLeafNames && highlightLeafNames.has(node.name);
   const isLeafHighlighted = highlightLeafNames && highlightLeafNames.has(node.name);
   const isInSubtreeHighlight = subtreeHighlightNodeIds?.has(String(node.id)) ?? false;
   const isInSubtreeHighlight = subtreeHighlightNodeIds?.has(String(node.id)) ?? false;
+  const isSourceNode = sourceNodeIds?.has(String(node.id)) ?? false;
   const isPatternNode = patternNodeIds?.has(String(node.id)) ?? false;
   const isPatternNode = patternNodeIds?.has(String(node.id)) ?? false;
+  const isAssociatedNode = isSourceNode || isPatternNode;
   const isSelected =
   const isSelected =
     selectedId !== null &&
     selectedId !== null &&
     selectedId !== undefined &&
     selectedId !== undefined &&
@@ -68,7 +70,7 @@ function HorizontalTreeNode({ node, onSelect, selectedId, level, highlightLeafNa
           "flex items-center px-3 py-1.5 rounded-md border shadow-[0_1px_2px_rgba(0,0,0,0.05)] cursor-pointer whitespace-nowrap transition-all z-10 h-[34px] bg-white border-slate-200",
           "flex items-center px-3 py-1.5 rounded-md border shadow-[0_1px_2px_rgba(0,0,0,0.05)] cursor-pointer whitespace-nowrap transition-all z-10 h-[34px] bg-white border-slate-200",
           isSelected
           isSelected
             ? "ring-2 ring-orange-400 ring-offset-2 border-orange-400 shadow-[0_0_0_2px_rgba(251,146,60,0.16)]"
             ? "ring-2 ring-orange-400 ring-offset-2 border-orange-400 shadow-[0_0_0_2px_rgba(251,146,60,0.16)]"
-            : isPatternNode
+            : isAssociatedNode
             ? "ring-2 ring-sky-400 ring-offset-1 border-sky-300 shadow-[0_0_0_2px_rgba(56,189,248,0.16)]"
             ? "ring-2 ring-sky-400 ring-offset-1 border-sky-300 shadow-[0_0_0_2px_rgba(56,189,248,0.16)]"
             : isInSubtreeHighlight
             : isInSubtreeHighlight
             ? "border-sky-300 border-dashed bg-white shadow-none"
             ? "border-sky-300 border-dashed bg-white shadow-none"
@@ -84,29 +86,29 @@ function HorizontalTreeNode({ node, onSelect, selectedId, level, highlightLeafNa
         </div>
         </div>
         <div className="relative flex items-center gap-2 ml-2">
         <div className="relative flex items-center gap-2 ml-2">
           <span
           <span
-            onMouseEnter={(e) => metrics.patternCount > 0 && setHoveredMetric({ key: 'pattern', count: metrics.patternCount, colorClass: 'bg-amber-500 border-amber-500 text-white', x: e.currentTarget.getBoundingClientRect().left + e.currentTarget.getBoundingClientRect().width / 2, y: e.currentTarget.getBoundingClientRect().top - 8 })}
+            onMouseEnter={(e) => metrics.patternCount > 0 && setHoveredMetric({ key: 'pattern', count: metrics.patternCount, colorClass: 'bg-blue-500 border-blue-500 text-white', x: e.currentTarget.getBoundingClientRect().left + e.currentTarget.getBoundingClientRect().width / 2, y: e.currentTarget.getBoundingClientRect().top - 8 })}
             onMouseLeave={() => setHoveredMetric((prev) => (prev?.key === 'pattern' ? null : prev))}
             onMouseLeave={() => setHoveredMetric((prev) => (prev?.key === 'pattern' ? null : prev))}
-            className={cn("w-2.5 h-2.5 rounded-full", metrics.patternCount > 0 ? "bg-amber-500 opacity-100" : "opacity-0")}
+            className={cn("w-2.5 h-2.5 rounded-full", metrics.patternCount > 0 ? "bg-blue-500 opacity-100" : "opacity-0")}
           />
           />
           <span
           <span
-            onMouseEnter={(e) => metrics.reqCount > 0 && setHoveredMetric({ key: 'req', count: metrics.reqCount, colorClass: 'bg-indigo-500 border-indigo-500 text-white', x: e.currentTarget.getBoundingClientRect().left + e.currentTarget.getBoundingClientRect().width / 2, y: e.currentTarget.getBoundingClientRect().top - 8 })}
+            onMouseEnter={(e) => metrics.reqCount > 0 && setHoveredMetric({ key: 'req', count: metrics.reqCount, colorClass: 'bg-cyan-500 border-cyan-500 text-white', x: e.currentTarget.getBoundingClientRect().left + e.currentTarget.getBoundingClientRect().width / 2, y: e.currentTarget.getBoundingClientRect().top - 8 })}
             onMouseLeave={() => setHoveredMetric((prev) => (prev?.key === 'req' ? null : prev))}
             onMouseLeave={() => setHoveredMetric((prev) => (prev?.key === 'req' ? null : prev))}
-            className={cn("w-2.5 h-2.5 rounded-full", metrics.reqCount > 0 ? "bg-indigo-500" : "opacity-0")}
+            className={cn("w-2.5 h-2.5 rounded-full", metrics.reqCount > 0 ? "bg-cyan-500" : "opacity-0")}
           />
           />
           <span
           <span
-            onMouseEnter={(e) => metrics.procCount > 0 && setHoveredMetric({ key: 'proc', count: metrics.procCount, colorClass: 'bg-purple-500 border-purple-500 text-white', x: e.currentTarget.getBoundingClientRect().left + e.currentTarget.getBoundingClientRect().width / 2, y: e.currentTarget.getBoundingClientRect().top - 8 })}
+            onMouseEnter={(e) => metrics.procCount > 0 && setHoveredMetric({ key: 'proc', count: metrics.procCount, colorClass: 'bg-green-500 border-green-500 text-white', x: e.currentTarget.getBoundingClientRect().left + e.currentTarget.getBoundingClientRect().width / 2, y: e.currentTarget.getBoundingClientRect().top - 8 })}
             onMouseLeave={() => setHoveredMetric((prev) => (prev?.key === 'proc' ? null : prev))}
             onMouseLeave={() => setHoveredMetric((prev) => (prev?.key === 'proc' ? null : prev))}
-            className={cn("w-2.5 h-2.5 rounded-full", metrics.procCount > 0 ? "bg-purple-500" : "opacity-0")}
+            className={cn("w-2.5 h-2.5 rounded-full", metrics.procCount > 0 ? "bg-green-500" : "opacity-0")}
           />
           />
           <span
           <span
-            onMouseEnter={(e) => metrics.capCount > 0 && setHoveredMetric({ key: 'cap', count: metrics.capCount, colorClass: 'bg-rose-500 border-rose-500 text-white', x: e.currentTarget.getBoundingClientRect().left + e.currentTarget.getBoundingClientRect().width / 2, y: e.currentTarget.getBoundingClientRect().top - 8 })}
+            onMouseEnter={(e) => metrics.capCount > 0 && setHoveredMetric({ key: 'cap', count: metrics.capCount, colorClass: 'bg-amber-400 border-amber-400 text-white', x: e.currentTarget.getBoundingClientRect().left + e.currentTarget.getBoundingClientRect().width / 2, y: e.currentTarget.getBoundingClientRect().top - 8 })}
             onMouseLeave={() => setHoveredMetric((prev) => (prev?.key === 'cap' ? null : prev))}
             onMouseLeave={() => setHoveredMetric((prev) => (prev?.key === 'cap' ? null : prev))}
-            className={cn("w-2.5 h-2.5 rounded-full", metrics.capCount > 0 ? "bg-rose-500" : "opacity-0")}
+            className={cn("w-2.5 h-2.5 rounded-full", metrics.capCount > 0 ? "bg-amber-400" : "opacity-0")}
           />
           />
           <span
           <span
-            onMouseEnter={(e) => metrics.toolCount > 0 && setHoveredMetric({ key: 'tool', count: metrics.toolCount, colorClass: 'bg-green-500 border-green-500 text-white', x: e.currentTarget.getBoundingClientRect().left + e.currentTarget.getBoundingClientRect().width / 2, y: e.currentTarget.getBoundingClientRect().top - 8 })}
+            onMouseEnter={(e) => metrics.toolCount > 0 && setHoveredMetric({ key: 'tool', count: metrics.toolCount, colorClass: 'bg-orange-500 border-orange-500 text-white', x: e.currentTarget.getBoundingClientRect().left + e.currentTarget.getBoundingClientRect().width / 2, y: e.currentTarget.getBoundingClientRect().top - 8 })}
             onMouseLeave={() => setHoveredMetric((prev) => (prev?.key === 'tool' ? null : prev))}
             onMouseLeave={() => setHoveredMetric((prev) => (prev?.key === 'tool' ? null : prev))}
-            className={cn("w-2.5 h-2.5 rounded-full", metrics.toolCount > 0 ? "bg-green-500" : "opacity-0")}
+            className={cn("w-2.5 h-2.5 rounded-full", metrics.toolCount > 0 ? "bg-orange-500" : "opacity-0")}
           />
           />
         </div>
         </div>
 
 

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 448 - 197
knowhub/frontend/src/pages/Dashboard.tsx


Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů