Explorar el Código

fix process board

guantao hace 2 semanas
padre
commit
8ee590e832
Se han modificado 2 ficheros con 68 adiciones y 28 borrados
  1. 64 28
      knowhub/frontend/src/pages/Workflows.tsx
  2. 4 0
      knowhub/frontend/src/services/api.ts

+ 64 - 28
knowhub/frontend/src/pages/Workflows.tsx

@@ -68,14 +68,26 @@ function parseSteps(body: string | any[] | object): WorkflowStep[] {
 }
 }
 
 
 export function Workflows() {
 export function Workflows() {
-  const [strategies, setStrategies] = useState<any[]>([]);
-  const [capabilities, setCapabilities] = useState<Record<string, any>>({});
-  const [isLoading, setIsLoading] = useState(true);
+  const getCache = () => {
+    try { const c = localStorage.getItem('workflows_cache_v1'); return c ? JSON.parse(c) : null; } catch(e) { return null; }
+  };
+  const saveCache = (newData: any) => {
+    try {
+      const c = localStorage.getItem('workflows_cache_v1');
+      const parsed = c ? JSON.parse(c) : {};
+      localStorage.setItem('workflows_cache_v1', JSON.stringify({ ...parsed, ...newData }));
+    } catch(e) {}
+  };
+  
+  const [strategies, setStrategies] = useState<any[]>(() => getCache()?.strategies || []);
+  const [capabilities, setCapabilities] = useState<Record<string, any>>(() => getCache()?.capabilities || {});
+  const [isLoading, setIsLoading] = useState(() => !(getCache()?.strategies?.length > 0));
   const [isLoadingMore, setIsLoadingMore] = useState(false);
   const [isLoadingMore, setIsLoadingMore] = useState(false);
   const [offset, setOffset] = useState(0);
   const [offset, setOffset] = useState(0);
   const [hasMore, setHasMore] = useState(true);
   const [hasMore, setHasMore] = useState(true);
-  const [totalCount, setTotalCount] = useState<number>(0);
-  const [activeCount, setActiveCount] = useState<number>(0);
+  const [totalCount, setTotalCount] = useState<number>(() => getCache()?.totalCount || 0);
+  const [activeCount, setActiveCount] = useState<number>(() => getCache()?.activeCount || 0);
+  const [searchQuery, setSearchQuery] = useState("");
   const LIMIT = 50;
   const LIMIT = 50;
   
   
   const loadStrategies = (currentOffset: number, isInit = false) => {
   const loadStrategies = (currentOffset: number, isInit = false) => {
@@ -84,13 +96,19 @@ export function Workflows() {
     getStrategies(LIMIT, currentOffset).then(stratRes => {
     getStrategies(LIMIT, currentOffset).then(stratRes => {
        if (stratRes.total !== undefined) {
        if (stratRes.total !== undefined) {
          setTotalCount(stratRes.total);
          setTotalCount(stratRes.total);
+         saveCache({ totalCount: stratRes.total });
        }
        }
        
        
        const fetched = stratRes.strategies || stratRes.results || (Array.isArray(stratRes) ? stratRes : []);
        const fetched = stratRes.strategies || stratRes.results || (Array.isArray(stratRes) ? stratRes : []);
        if (isInit) {
        if (isInit) {
          setStrategies(fetched);
          setStrategies(fetched);
+         saveCache({ strategies: fetched });
        } else {
        } else {
-         setStrategies(prev => [...prev, ...fetched]);
+         setStrategies(prev => {
+           const newStrats = [...prev, ...fetched];
+           saveCache({ strategies: newStrats.slice(0, 50) });
+           return newStrats;
+         });
        }
        }
        if (fetched.length < LIMIT || (stratRes.total && currentOffset + fetched.length >= stratRes.total)) {
        if (fetched.length < LIMIT || (stratRes.total && currentOffset + fetched.length >= stratRes.total)) {
          setHasMore(false);
          setHasMore(false);
@@ -109,6 +127,7 @@ export function Workflows() {
         capMap[c.capability_id || c.id || c.capability_name || c.name] = c;
         capMap[c.capability_id || c.id || c.capability_name || c.name] = c;
       });
       });
       setCapabilities(capMap);
       setCapabilities(capMap);
+      saveCache({ capabilities: capMap });
       loadStrategies(0, true);
       loadStrategies(0, true);
       
       
       // Calculate active count without downloading all bodies
       // Calculate active count without downloading all bodies
@@ -119,6 +138,7 @@ export function Workflows() {
       ]).then(results => {
       ]).then(results => {
         const sum = results.reduce((acc, curr) => acc + (curr.total || 0), 0);
         const sum = results.reduce((acc, curr) => acc + (curr.total || 0), 0);
         setActiveCount(sum);
         setActiveCount(sum);
+        saveCache({ activeCount: sum });
       }).catch(console.error);
       }).catch(console.error);
     }).catch(console.error);
     }).catch(console.error);
   }, []);
   }, []);
@@ -142,6 +162,18 @@ export function Workflows() {
         <StatCard title="覆盖能力" value={Object.keys(capabilities).length} subtext="关联的底层原子能力" icon={Cpu} iconBgColor="bg-amber-50" iconColor="text-amber-600" />
         <StatCard title="覆盖能力" value={Object.keys(capabilities).length} subtext="关联的底层原子能力" icon={Cpu} iconBgColor="bg-amber-50" iconColor="text-amber-600" />
       </div>
       </div>
 
 
+      <div className="bg-white p-4 rounded-2xl border border-slate-100 shadow-sm relative">
+        <div className="relative flex-1 max-w-xl">
+           <Search size={16} className="absolute left-4 top-1/2 -translate-y-1/2 text-slate-400" />
+           <input 
+             value={searchQuery}
+             onChange={e => setSearchQuery(e.target.value)}
+             placeholder="模糊匹配工序名称或描述..."
+             className="w-full bg-slate-50 border border-slate-200 text-sm rounded-xl pl-10 pr-4 py-2.5 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-all font-medium text-slate-700 placeholder:font-normal"
+           />
+        </div>
+      </div>
+
       <div className="space-y-6">
       <div className="space-y-6">
         {isLoading && (
         {isLoading && (
            <div className="p-12 text-center text-slate-400 font-bold flex flex-col items-center gap-3">
            <div className="p-12 text-center text-slate-400 font-bold flex flex-col items-center gap-3">
@@ -156,12 +188,28 @@ export function Workflows() {
            </div>
            </div>
         )}
         )}
         
         
-        {strategies.map(strategy => {
-          const steps = parseSteps(strategy.body);
-          // If no specific capability is listed for the steps but the strategy has capabilities overall,
-          // maybe the UI shouldn't imply them for every step, but the user requested: 
-          // "最右侧展示每个步骤的原子能力", so we use `step.capabilities`.
-          const stratCaps = strategy.capability_ids || [];
+        {(() => {
+          const filteredStrategies = strategies.filter(s => {
+            if (!searchQuery) return true;
+            const q = searchQuery.toLowerCase();
+            return (s.name || '').toLowerCase().includes(q) || (s.description || '').toLowerCase().includes(q);
+          });
+
+          if (!isLoading && strategies.length > 0 && filteredStrategies.length === 0) {
+            return (
+               <div className="p-12 text-center border-2 border-dashed border-slate-200 rounded-3xl bg-slate-50/50">
+                  <Search size={32} className="mx-auto mb-3 text-slate-300" />
+                  <p className="text-slate-500 font-bold">未找到匹配的工序</p>
+               </div>
+            );
+          }
+
+          return filteredStrategies.map(strategy => {
+            const steps = parseSteps(strategy.body);
+            // If no specific capability is listed for the steps but the strategy has capabilities overall,
+            // maybe the UI shouldn't imply them for every step, but the user requested: 
+            // "最右侧展示每个步骤的原子能力", so we use `step.capabilities`.
+            const stratCaps = strategy.capability_ids || [];
 
 
           return (
           return (
             <div key={strategy.id} className="bg-white rounded-3xl border border-slate-200 shadow-sm overflow-hidden hover:border-indigo-300 transition-colors duration-300">
             <div key={strategy.id} className="bg-white rounded-3xl border border-slate-200 shadow-sm overflow-hidden hover:border-indigo-300 transition-colors duration-300">
@@ -169,25 +217,12 @@ export function Workflows() {
                 <div>
                 <div>
                    <div className="flex items-center gap-3 mb-2">
                    <div className="flex items-center gap-3 mb-2">
                      <h2 className="text-lg font-black text-slate-900">{strategy.name || '未命名工序'}</h2>
                      <h2 className="text-lg font-black text-slate-900">{strategy.name || '未命名工序'}</h2>
-                     <StatusBadge status={strategy.status} />
-                     <span className="text-[11px] font-mono text-slate-400 bg-white px-2 py-0.5 rounded border border-slate-100">ID: {strategy.id?.substring(0,8)}</span>
+                     <span className="text-[11px] font-mono text-slate-400 bg-white px-2 py-0.5 rounded border border-slate-100">ID: {strategy.id}</span>
                    </div>
                    </div>
                    <p className="text-sm text-slate-600 leading-relaxed max-w-3xl">
                    <p className="text-sm text-slate-600 leading-relaxed max-w-3xl">
                      {strategy.description || '无详细描述'}
                      {strategy.description || '无详细描述'}
                    </p>
                    </p>
                 </div>
                 </div>
-                
-                {stratCaps.length > 0 && (
-                  <div className="flex flex-col items-end gap-2 shrink-0">
-                     <span className="text-[10px] font-black text-slate-400 uppercase tracking-wider">关联全量能力</span>
-                     <div className="flex flex-wrap gap-1 justify-end max-w-[200px]">
-                       {stratCaps.slice(0, 3).map((cid: string) => (
-                         <div key={cid} className="w-2 h-2 rounded-full bg-emerald-500" title={capabilities[cid]?.capability_name || capabilities[cid]?.name || cid} />
-                       ))}
-                       {stratCaps.length > 3 && <span className="text-[10px] text-slate-400 font-bold px-1">+{stratCaps.length - 3}</span>}
-                     </div>
-                  </div>
-                )}
               </div>
               </div>
 
 
               <div className="p-6 bg-white">
               <div className="p-6 bg-white">
@@ -247,8 +282,9 @@ export function Workflows() {
               </div>
               </div>
             </div>
             </div>
           );
           );
-        })}
-        {hasMore && strategies.length > 0 && (
+        });
+        })()}
+        {hasMore && strategies.length > 0 && !searchQuery && (
           <div className="flex justify-center pt-4">
           <div className="flex justify-center pt-4">
             <button
             <button
               onClick={handleLoadMore}
               onClick={handleLoadMore}

+ 4 - 0
knowhub/frontend/src/services/api.ts

@@ -62,6 +62,10 @@ export const searchCapabilities = async (q: string, top_k = 20) => {
   return fetchWithCache(`/capability/search?q=${encodeURIComponent(q)}&top_k=${top_k}`);
   return fetchWithCache(`/capability/search?q=${encodeURIComponent(q)}&top_k=${top_k}`);
 };
 };
 
 
+export const searchStrategies = async (q: string, top_k = 20) => {
+  return fetchWithCache(`/strategy/search?q=${encodeURIComponent(q)}&top_k=${top_k}`);
+};
+
 export const searchTools = async (q: string, status?: string, top_k = 20) => {
 export const searchTools = async (q: string, status?: string, top_k = 20) => {
   const params = new URLSearchParams({ q, top_k: top_k.toString() });
   const params = new URLSearchParams({ q, top_k: top_k.toString() });
   if (status) params.append('status', status);
   if (status) params.append('status', status);