update
This commit is contained in:
		
							
								
								
									
										40
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										40
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							@@ -96,6 +96,9 @@ importers:
 | 
				
			|||||||
      events:
 | 
					      events:
 | 
				
			||||||
        specifier: ^3.3.0
 | 
					        specifier: ^3.3.0
 | 
				
			||||||
        version: 3.3.0
 | 
					        version: 3.3.0
 | 
				
			||||||
 | 
					      graphology:
 | 
				
			||||||
 | 
					        specifier: ^0.26.0
 | 
				
			||||||
 | 
					        version: 0.26.0(graphology-types@0.24.8)
 | 
				
			||||||
      highlight.js:
 | 
					      highlight.js:
 | 
				
			||||||
        specifier: ^11.11.1
 | 
					        specifier: ^11.11.1
 | 
				
			||||||
        version: 11.11.1
 | 
					        version: 11.11.1
 | 
				
			||||||
@@ -129,6 +132,9 @@ importers:
 | 
				
			|||||||
      react-toastify:
 | 
					      react-toastify:
 | 
				
			||||||
        specifier: ^11.0.5
 | 
					        specifier: ^11.0.5
 | 
				
			||||||
        version: 11.0.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
 | 
					        version: 11.0.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
 | 
				
			||||||
 | 
					      sigma:
 | 
				
			||||||
 | 
					        specifier: ^3.0.2
 | 
				
			||||||
 | 
					        version: 3.0.2(graphology-types@0.24.8)
 | 
				
			||||||
      tailwind-merge:
 | 
					      tailwind-merge:
 | 
				
			||||||
        specifier: ^3.3.1
 | 
					        specifier: ^3.3.1
 | 
				
			||||||
        version: 3.3.1
 | 
					        version: 3.3.1
 | 
				
			||||||
@@ -1614,6 +1620,19 @@ packages:
 | 
				
			|||||||
  graceful-fs@4.2.11:
 | 
					  graceful-fs@4.2.11:
 | 
				
			||||||
    resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
 | 
					    resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  graphology-types@0.24.8:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-hDRKYXa8TsoZHjgEaysSRyPdT6uB78Ci8WnjgbStlQysz7xR52PInxNsmnB7IBOM1BhikxkNyCVEFgmPKnpx3Q==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  graphology-utils@2.5.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-ckHg8MXrXJkOARk56ZaSCM1g1Wihe2d6iTmz1enGOz4W/l831MBCKSayeFQfowgF8wd+PQ4rlch/56Vs/VZLDQ==}
 | 
				
			||||||
 | 
					    peerDependencies:
 | 
				
			||||||
 | 
					      graphology-types: '>=0.23.0'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  graphology@0.26.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-8SSImzgUUYC89Z042s+0r/vMibY7GX/Emz4LDO5e7jYXhuoWfHISPFJYjpRLUSJGq6UQ6xlenvX1p/hJdfXuXg==}
 | 
				
			||||||
 | 
					    peerDependencies:
 | 
				
			||||||
 | 
					      graphology-types: '>=0.24.0'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  guid-typescript@1.0.9:
 | 
					  guid-typescript@1.0.9:
 | 
				
			||||||
    resolution: {integrity: sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==}
 | 
					    resolution: {integrity: sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2612,6 +2631,9 @@ packages:
 | 
				
			|||||||
  shiki@3.13.0:
 | 
					  shiki@3.13.0:
 | 
				
			||||||
    resolution: {integrity: sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g==}
 | 
					    resolution: {integrity: sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sigma@3.0.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-/BUbeOwPGruiBOm0YQQ6ZMcLIZ6tf/W+Jcm7dxZyAX0tK3WP9/sq7/NAWBxPIxVahdGjCJoGwej0Gdrv0DxlQQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sisteransi@1.0.5:
 | 
					  sisteransi@1.0.5:
 | 
				
			||||||
    resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
 | 
					    resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -4604,6 +4626,17 @@ snapshots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  graceful-fs@4.2.11: {}
 | 
					  graceful-fs@4.2.11: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  graphology-types@0.24.8: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  graphology-utils@2.5.2(graphology-types@0.24.8):
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      graphology-types: 0.24.8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  graphology@0.26.0(graphology-types@0.24.8):
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      events: 3.3.0
 | 
				
			||||||
 | 
					      graphology-types: 0.24.8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  guid-typescript@1.0.9: {}
 | 
					  guid-typescript@1.0.9: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  h3@1.15.4:
 | 
					  h3@1.15.4:
 | 
				
			||||||
@@ -6082,6 +6115,13 @@ snapshots:
 | 
				
			|||||||
      '@shikijs/vscode-textmate': 10.0.2
 | 
					      '@shikijs/vscode-textmate': 10.0.2
 | 
				
			||||||
      '@types/hast': 3.0.4
 | 
					      '@types/hast': 3.0.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sigma@3.0.2(graphology-types@0.24.8):
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      events: 3.3.0
 | 
				
			||||||
 | 
					      graphology-utils: 2.5.2(graphology-types@0.24.8)
 | 
				
			||||||
 | 
					    transitivePeerDependencies:
 | 
				
			||||||
 | 
					      - graphology-types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sisteransi@1.0.5: {}
 | 
					  sisteransi@1.0.5: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sitemap@8.0.0:
 | 
					  sitemap@8.0.0:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,6 +32,7 @@
 | 
				
			|||||||
    "dayjs": "^1.11.18",
 | 
					    "dayjs": "^1.11.18",
 | 
				
			||||||
    "es-toolkit": "^1.40.0",
 | 
					    "es-toolkit": "^1.40.0",
 | 
				
			||||||
    "events": "^3.3.0",
 | 
					    "events": "^3.3.0",
 | 
				
			||||||
 | 
					    "graphology": "^0.26.0",
 | 
				
			||||||
    "highlight.js": "^11.11.1",
 | 
					    "highlight.js": "^11.11.1",
 | 
				
			||||||
    "lodash-es": "^4.17.21",
 | 
					    "lodash-es": "^4.17.21",
 | 
				
			||||||
    "lucide-react": "^0.545.0",
 | 
					    "lucide-react": "^0.545.0",
 | 
				
			||||||
@@ -43,6 +44,7 @@
 | 
				
			|||||||
    "react-dom": "^19.2.0",
 | 
					    "react-dom": "^19.2.0",
 | 
				
			||||||
    "react-resizable-panels": "^3.0.6",
 | 
					    "react-resizable-panels": "^3.0.6",
 | 
				
			||||||
    "react-toastify": "^11.0.5",
 | 
					    "react-toastify": "^11.0.5",
 | 
				
			||||||
 | 
					    "sigma": "^3.0.2",
 | 
				
			||||||
    "tailwind-merge": "^3.3.1",
 | 
					    "tailwind-merge": "^3.3.1",
 | 
				
			||||||
    "three": "^0.180.0",
 | 
					    "three": "^0.180.0",
 | 
				
			||||||
    "wavesurfer.js": "^7.11.0",
 | 
					    "wavesurfer.js": "^7.11.0",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										127
									
								
								web/src/apps/muse/base/graph/sigma/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								web/src/apps/muse/base/graph/sigma/index.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,127 @@
 | 
				
			|||||||
 | 
					import React, { useEffect, useRef, useState } from 'react';
 | 
				
			||||||
 | 
					import Graph from 'graphology';
 | 
				
			||||||
 | 
					import Sigma from 'sigma';
 | 
				
			||||||
 | 
					import { Mark } from '../../../modules/mark';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					  dataSource?: Mark[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const SigmaGraph = (props: Props) => {
 | 
				
			||||||
 | 
					  const { dataSource = [] } = props;
 | 
				
			||||||
 | 
					  const containerRef = useRef<HTMLDivElement>(null);
 | 
				
			||||||
 | 
					  const sigmaRef = useRef<Sigma | null>(null);
 | 
				
			||||||
 | 
					  const graphRef = useRef<Graph | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    if (!containerRef.current) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 创建图实例
 | 
				
			||||||
 | 
					    const graph = new Graph();
 | 
				
			||||||
 | 
					    graphRef.current = graph;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 创建 Sigma 实例
 | 
				
			||||||
 | 
					    const sigma = new Sigma(graph, containerRef.current, {
 | 
				
			||||||
 | 
					      renderLabels: true,
 | 
				
			||||||
 | 
					      labelRenderedSizeThreshold: 8,
 | 
				
			||||||
 | 
					      labelDensity: 8,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    sigmaRef.current = sigma;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return () => {
 | 
				
			||||||
 | 
					      sigma.kill();
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    if (!graphRef.current || !dataSource.length) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const graph = graphRef.current;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 清空现有图数据
 | 
				
			||||||
 | 
					    graph.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 添加节点
 | 
				
			||||||
 | 
					    dataSource.forEach((mark, index) => {
 | 
				
			||||||
 | 
					      graph.addNode(`node-${index}`, {
 | 
				
			||||||
 | 
					        x: Math.random() * 800,
 | 
				
			||||||
 | 
					        y: Math.random() * 600,
 | 
				
			||||||
 | 
					        size: Math.random() * 20 + 5,
 | 
				
			||||||
 | 
					        label: mark.title || `Mark ${index}`,
 | 
				
			||||||
 | 
					        color: `#${Math.floor(Math.random() * 16777215).toString(16)}`,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // // 添加一些随机边(基于数据关系或随机生成)
 | 
				
			||||||
 | 
					    // for (let i = 0; i < Math.min(dataSource.length - 1, 20); i++) {
 | 
				
			||||||
 | 
					    //   const sourceIndex = Math.floor(Math.random() * dataSource.length);
 | 
				
			||||||
 | 
					    //   const targetIndex = Math.floor(Math.random() * dataSource.length);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //   if (sourceIndex !== targetIndex) {
 | 
				
			||||||
 | 
					    //     try {
 | 
				
			||||||
 | 
					    //       graph.addEdge(`node-${sourceIndex}`, `node-${targetIndex}`, {
 | 
				
			||||||
 | 
					    //         size: Math.random() * 5 + 1,
 | 
				
			||||||
 | 
					    //         color: '#999',
 | 
				
			||||||
 | 
					    //       });
 | 
				
			||||||
 | 
					    //     } catch (error) {
 | 
				
			||||||
 | 
					    //       // 边已存在,忽略错误
 | 
				
			||||||
 | 
					    //     }
 | 
				
			||||||
 | 
					    //   }
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 刷新 Sigma 渲染
 | 
				
			||||||
 | 
					    if (sigmaRef.current) {
 | 
				
			||||||
 | 
					      sigmaRef.current.refresh();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [dataSource]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 添加交互事件处理
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    if (!sigmaRef.current) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const sigma = sigmaRef.current;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 节点点击事件
 | 
				
			||||||
 | 
					    const handleNodeClick = (event: any) => {
 | 
				
			||||||
 | 
					      console.log('Node clicked:', event.node);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 节点悬停事件
 | 
				
			||||||
 | 
					    const handleNodeHover = (event: any) => {
 | 
				
			||||||
 | 
					      console.log('Node hovered:', event.node);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sigma.on('clickNode', handleNodeClick);
 | 
				
			||||||
 | 
					    sigma.on('enterNode', handleNodeHover);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return () => {
 | 
				
			||||||
 | 
					      sigma.off('clickNode', handleNodeClick);
 | 
				
			||||||
 | 
					      sigma.off('enterNode', handleNodeHover);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div style={{ width: '100%', height: '100vh', position: 'relative' }}>
 | 
				
			||||||
 | 
					      <div
 | 
				
			||||||
 | 
					        ref={containerRef}
 | 
				
			||||||
 | 
					        style={{
 | 
				
			||||||
 | 
					          width: '100%',
 | 
				
			||||||
 | 
					          height: '100%',
 | 
				
			||||||
 | 
					          background: '#f5f5f5'
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <div style={{
 | 
				
			||||||
 | 
					        position: 'absolute',
 | 
				
			||||||
 | 
					        top: 10,
 | 
				
			||||||
 | 
					        left: 10,
 | 
				
			||||||
 | 
					        background: 'rgba(255,255,255,0.8)',
 | 
				
			||||||
 | 
					        padding: '10px',
 | 
				
			||||||
 | 
					        borderRadius: '4px'
 | 
				
			||||||
 | 
					      }}>
 | 
				
			||||||
 | 
					        <p>节点数量: {dataSource.length}</p>
 | 
				
			||||||
 | 
					        <p>使用鼠标拖拽和缩放来探索图形</p>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
import { useState } from "react";
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
import { Base } from "./table/index";
 | 
					import { Base } from "./table/index";
 | 
				
			||||||
 | 
					import { markService } from "../modules/mark-service";
 | 
				
			||||||
 | 
					import { SigmaGraph } from "./graph/sigma/index";
 | 
				
			||||||
const tabs = [
 | 
					const tabs = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    key: 'table',
 | 
					    key: 'table',
 | 
				
			||||||
@@ -22,15 +23,22 @@ const tabs = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const BaseApp = () => {
 | 
					export const BaseApp = () => {
 | 
				
			||||||
  const [activeTab, setActiveTab] = useState('table');
 | 
					  const [activeTab, setActiveTab] = useState('table');
 | 
				
			||||||
 | 
					  const [dataSource, setDataSource] = useState<any[]>([]);
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    getMarks();
 | 
				
			||||||
 | 
					  }, []);
 | 
				
			||||||
 | 
					  const getMarks = async () => {
 | 
				
			||||||
 | 
					    const marks = await markService.getAllMarks();
 | 
				
			||||||
 | 
					    setDataSource(marks);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  const renderContent = () => {
 | 
					  const renderContent = () => {
 | 
				
			||||||
    switch (activeTab) {
 | 
					    switch (activeTab) {
 | 
				
			||||||
      case 'table':
 | 
					      case 'table':
 | 
				
			||||||
        return <Base />;
 | 
					        return <Base dataSource={dataSource} />;
 | 
				
			||||||
      case 'graph':
 | 
					      case 'graph':
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
          <div className="flex items-center justify-center h-96 text-gray-500">
 | 
					          <div className="w-full h-96">
 | 
				
			||||||
            关系图模块暂未实现
 | 
					            <SigmaGraph dataSource={dataSource} />
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      case 'world':
 | 
					      case 'world':
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,26 @@
 | 
				
			|||||||
import React, { useState, useMemo } from 'react';
 | 
					import React, { useState, useMemo, useEffect } from 'react';
 | 
				
			||||||
import { Table } from './Table';
 | 
					import { Table } from './Table';
 | 
				
			||||||
import { DetailModal } from './DetailModal';
 | 
					import { DetailModal } from './DetailModal';
 | 
				
			||||||
import { mockMarks, Mark } from '../mock/collection';
 | 
					import { Mark } from '../mock/collection';
 | 
				
			||||||
import { TableColumn, ActionButton } from './types';
 | 
					import { TableColumn, ActionButton } from './types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const Base = () => {
 | 
					type Props = {
 | 
				
			||||||
 | 
					  dataSource?: Mark[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export const Base = (props: Props) => {
 | 
				
			||||||
 | 
					  const { dataSource = [] } = props;
 | 
				
			||||||
  const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
 | 
					  const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
 | 
				
			||||||
  const [currentPage, setCurrentPage] = useState(1);
 | 
					  const [currentPage, setCurrentPage] = useState(1);
 | 
				
			||||||
  const [pageSize, setPageSize] = useState(10);
 | 
					  const [pageSize, setPageSize] = useState(10);
 | 
				
			||||||
  const [data, setData] = useState<Mark[]>(mockMarks);
 | 
					  const [data, setData] = useState<Mark[]>([]);
 | 
				
			||||||
  const [detailModalVisible, setDetailModalVisible] = useState(false);
 | 
					  const [detailModalVisible, setDetailModalVisible] = useState(false);
 | 
				
			||||||
  const [currentRecord, setCurrentRecord] = useState<Mark | null>(null);
 | 
					  const [currentRecord, setCurrentRecord] = useState<Mark | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    if (dataSource) {
 | 
				
			||||||
 | 
					      setData(dataSource);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [dataSource]);
 | 
				
			||||||
  // 表格列配置
 | 
					  // 表格列配置
 | 
				
			||||||
  const columns: TableColumn<Mark>[] = [
 | 
					  const columns: TableColumn<Mark>[] = [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -193,7 +202,7 @@ export const Base = () => {
 | 
				
			|||||||
  // 排序处理
 | 
					  // 排序处理
 | 
				
			||||||
  const handleSort = (field: string, order: 'asc' | 'desc' | null) => {
 | 
					  const handleSort = (field: string, order: 'asc' | 'desc' | null) => {
 | 
				
			||||||
    if (!order) {
 | 
					    if (!order) {
 | 
				
			||||||
      setData(mockMarks); // 重置为原始顺序
 | 
					      setData(dataSource); // 重置为原始顺序
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,27 +29,42 @@ export class MarkDB {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // 创建索引以支持查询
 | 
					  // 创建索引以支持查询
 | 
				
			||||||
  async createIndexes() {
 | 
					  async createIndexes() {
 | 
				
			||||||
    // 检查索引是否已存在,而不是尝试创建
 | 
					 | 
				
			||||||
    if (!this.db) {
 | 
					    if (!this.db) {
 | 
				
			||||||
      throw new Error('数据库未初始化');
 | 
					      throw new Error('数据库未初始化');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const transaction = this.db.transaction([STORE_NAME], 'readonly');
 | 
					    try {
 | 
				
			||||||
    const store = transaction.objectStore(STORE_NAME);
 | 
					      // PouchDB 创建索引的正确方式
 | 
				
			||||||
 | 
					      const indexes = [
 | 
				
			||||||
 | 
					        { index: { fields: ['uid'] } },
 | 
				
			||||||
 | 
					        { index: { fields: ['markType'] } },
 | 
				
			||||||
 | 
					        { index: { fields: ['tags'] } },
 | 
				
			||||||
 | 
					        { index: { fields: ['createdAt'] } },
 | 
				
			||||||
 | 
					        { index: { fields: ['title'] } },
 | 
				
			||||||
 | 
					        { index: { fields: ['uid', 'markType'] } },
 | 
				
			||||||
 | 
					        { index: { fields: ['createdAt', 'uid'] } }
 | 
				
			||||||
 | 
					      ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 检查索引是否存在
 | 
					      const results = await Promise.allSettled(
 | 
				
			||||||
    const indexNames = ['uid', 'markType', 'tags', 'createdAt', 'title'];
 | 
					        indexes.map(indexDef => this.db.createIndex(indexDef))
 | 
				
			||||||
    const existingIndexes = Array.from(store.indexNames);
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    console.log('现有索引:', existingIndexes);
 | 
					      // 检查索引创建结果
 | 
				
			||||||
    
 | 
					      results.forEach((result, index) => {
 | 
				
			||||||
    const missingIndexes = indexNames.filter(name => !existingIndexes.includes(name));
 | 
					        if (result.status === 'fulfilled') {
 | 
				
			||||||
    
 | 
					          console.log(`索引 ${index + 1} 创建成功:`, result.value);
 | 
				
			||||||
    if (missingIndexes.length > 0) {
 | 
					 | 
				
			||||||
      console.warn('缺少索引:', missingIndexes);
 | 
					 | 
				
			||||||
      console.log('请删除数据库并重新初始化以创建缺少的索引');
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
      console.log('所有索引都已存在');
 | 
					          // 如果索引已存在,PouchDB 会返回错误,这是正常的
 | 
				
			||||||
 | 
					          if (result.reason?.status !== 409) {
 | 
				
			||||||
 | 
					            console.warn(`索引 ${index + 1} 创建失败:`, result.reason);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      console.log('索引初始化完成');
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.error('创建索引失败:', error);
 | 
				
			||||||
 | 
					      throw error;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,7 +84,7 @@ export const ChatInterface: React.FC = () => {
 | 
				
			|||||||
            <Bot className="w-6 h-6 text-white" />
 | 
					            <Bot className="w-6 h-6 text-white" />
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div>
 | 
					          <div>
 | 
				
			||||||
            <h1 className="text-xl font-semibold text-gray-900">AI 助手</h1>
 | 
					            <h1 className="text-xl font-semibold text-gray-900">智能工作台</h1>
 | 
				
			||||||
            <p className="text-sm text-gray-500">在线 · 随时为您服务</p>
 | 
					            <p className="text-sm text-gray-500">在线 · 随时为您服务</p>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user