Compare commits
2 Commits
de3d196e11
...
91eaa37283
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91eaa37283 | ||
|
|
3f85b0ff78 |
98
.dockerignore
Normal file
98
.dockerignore
Normal file
@@ -0,0 +1,98 @@
|
||||
node_modules
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Git
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
# Docker
|
||||
Dockerfile*
|
||||
docker-compose*
|
||||
.dockerignore
|
||||
|
||||
# Environment files (keep only production)
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Dependency directories
|
||||
jspm_packages/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
|
||||
# Storybook build outputs
|
||||
.out
|
||||
.storybook-out
|
||||
|
||||
# Temporary folders
|
||||
tmp/
|
||||
temp/
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
6
.env.production
Normal file
6
.env.production
Normal file
@@ -0,0 +1,6 @@
|
||||
# VITE_API_BASE_URL = http://150.158.121.95
|
||||
VITE_API_BASE_URL = http://154.9.253.114:9380
|
||||
|
||||
VITE_RSA_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArq9XTUSeYr2+N1h3Afl/z8Dse/2yD0ZGrKwx+EEEcdsBLca9Ynmx3nIB5obmLlSfmskLpBo0UACBmB5rEjBp2Q2f3AG3Hjd4B+gNCG6BDaawuDlgANIhGnaTLrIqWrrcm4EMzJOnAOI1fgzJRsOOUEfaS318Eq9OVO3apEyCCt0lOQK6PuksduOjVxtltDav+guVAA068NrPYmRNabVKRNLJpL8w4D44sfth5RvZ3q9t+6RTArpEtc5sh5ChzvqPOzKGMXW83C95TxmXqpbK6olN4RevSfVjEAgCydH6HN6OhtOQEcnrU97r9H0iZOWwbw3pVrZiUkuRD1R56Wzs2wIDAQAB
|
||||
-----END PUBLIC KEY-----"
|
||||
52
Dockerfile
52
Dockerfile
@@ -1,12 +1,60 @@
|
||||
FROM node:20-alpine
|
||||
# 多阶段构建 - 构建阶段
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 复制包管理文件
|
||||
COPY package.json pnpm-lock.yaml ./
|
||||
|
||||
# 安装 pnpm 和依赖
|
||||
RUN npm install -g pnpm && pnpm install
|
||||
|
||||
# 复制源代码
|
||||
COPY . .
|
||||
|
||||
# 构建生产版本
|
||||
RUN pnpm build
|
||||
|
||||
# 生产阶段 - nginx
|
||||
FROM nginx:alpine AS production
|
||||
|
||||
# 复制自定义 nginx 配置
|
||||
COPY <<EOF /etc/nginx/conf.d/default.conf
|
||||
server {
|
||||
listen 5173;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# 处理 SPA 路由
|
||||
location / {
|
||||
try_files \$uri \$uri/ /index.html;
|
||||
}
|
||||
|
||||
# 静态资源缓存
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# 安全头
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# Gzip 压缩
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
|
||||
}
|
||||
EOF
|
||||
|
||||
# 从构建阶段复制构建产物
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 5173
|
||||
|
||||
CMD ["pnpm", "dev"]
|
||||
# 启动 nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
28
docker-compose.yml
Normal file
28
docker-compose.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
teres-frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
target: production
|
||||
ports:
|
||||
- "3000:80"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
restart: unless-stopped
|
||||
|
||||
# 开发环境服务(可选)
|
||||
teres-frontend-dev:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
ports:
|
||||
- "5173:5173"
|
||||
volumes:
|
||||
- .:/app
|
||||
- /app/node_modules
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
profiles:
|
||||
- dev
|
||||
@@ -16,6 +16,7 @@
|
||||
"@mui/material": "^7.3.4",
|
||||
"@mui/x-data-grid": "^8.14.0",
|
||||
"@mui/x-date-pickers": "^8.14.0",
|
||||
"@xyflow/react": "^12.8.6",
|
||||
"ahooks": "^3.9.5",
|
||||
"axios": "^1.12.2",
|
||||
"dayjs": "^1.11.18",
|
||||
|
||||
236
pnpm-lock.yaml
generated
236
pnpm-lock.yaml
generated
@@ -26,6 +26,9 @@ importers:
|
||||
'@mui/x-date-pickers':
|
||||
specifier: ^8.14.0
|
||||
version: 8.14.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@mui/material@7.3.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@7.3.3(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(dayjs@1.11.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@xyflow/react':
|
||||
specifier: ^12.8.6
|
||||
version: 12.8.6(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
ahooks:
|
||||
specifier: ^3.9.5
|
||||
version: 3.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@@ -92,7 +95,7 @@ importers:
|
||||
version: 19.2.1(@types/react@19.2.2)
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^5.0.4
|
||||
version: 5.0.4(vite@7.1.9(@types/node@24.7.1))
|
||||
version: 5.0.4(vite@7.1.9(@types/node@24.7.1)(terser@5.44.0))
|
||||
eslint:
|
||||
specifier: ^9.36.0
|
||||
version: 9.37.0
|
||||
@@ -113,7 +116,7 @@ importers:
|
||||
version: 8.46.0(eslint@9.37.0)(typescript@5.9.3)
|
||||
vite:
|
||||
specifier: ^7.1.7
|
||||
version: 7.1.9(@types/node@24.7.1)
|
||||
version: 7.1.9(@types/node@24.7.1)(terser@5.44.0)
|
||||
|
||||
packages:
|
||||
|
||||
@@ -478,6 +481,9 @@ packages:
|
||||
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@jridgewell/source-map@0.3.11':
|
||||
resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5':
|
||||
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||
|
||||
@@ -792,6 +798,24 @@ packages:
|
||||
'@types/babel__traverse@7.28.0':
|
||||
resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
|
||||
|
||||
'@types/d3-color@3.1.3':
|
||||
resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
|
||||
|
||||
'@types/d3-drag@3.0.7':
|
||||
resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==}
|
||||
|
||||
'@types/d3-interpolate@3.0.4':
|
||||
resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
|
||||
|
||||
'@types/d3-selection@3.0.11':
|
||||
resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==}
|
||||
|
||||
'@types/d3-transition@3.0.9':
|
||||
resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==}
|
||||
|
||||
'@types/d3-zoom@3.0.8':
|
||||
resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==}
|
||||
|
||||
'@types/estree@1.0.8':
|
||||
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
||||
|
||||
@@ -891,6 +915,15 @@ packages:
|
||||
peerDependencies:
|
||||
vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
|
||||
|
||||
'@xyflow/react@12.8.6':
|
||||
resolution: {integrity: sha512-SksAm2m4ySupjChphMmzvm55djtgMDPr+eovPDdTnyGvShf73cvydfoBfWDFllooIQ4IaiUL5yfxHRwU0c37EA==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
|
||||
'@xyflow/system@0.0.70':
|
||||
resolution: {integrity: sha512-PpC//u9zxdjj0tfTSmZrg3+sRbTz6kop/Amky44U2Dl51sxzDTIUfXMwETOYpmr2dqICWXBIJwXL2a9QWtX2XA==}
|
||||
|
||||
acorn-jsx@5.3.2:
|
||||
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
||||
peerDependencies:
|
||||
@@ -950,6 +983,9 @@ packages:
|
||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||
hasBin: true
|
||||
|
||||
buffer-from@1.1.2:
|
||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||
|
||||
call-bind-apply-helpers@1.0.2:
|
||||
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -965,6 +1001,9 @@ packages:
|
||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
classcat@5.0.5:
|
||||
resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==}
|
||||
|
||||
clsx@2.1.1:
|
||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -980,6 +1019,9 @@ packages:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
commander@2.20.3:
|
||||
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
|
||||
|
||||
concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
@@ -1004,6 +1046,44 @@ packages:
|
||||
csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
d3-color@3.1.0:
|
||||
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-dispatch@3.0.1:
|
||||
resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-drag@3.0.0:
|
||||
resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-ease@3.0.1:
|
||||
resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-interpolate@3.0.1:
|
||||
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-selection@3.0.0:
|
||||
resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-timer@3.0.1:
|
||||
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-transition@3.0.1:
|
||||
resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
d3-selection: 2 - 3
|
||||
|
||||
d3-zoom@3.0.0:
|
||||
resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
dayjs@1.11.18:
|
||||
resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==}
|
||||
|
||||
@@ -1601,10 +1681,17 @@ packages:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
source-map-support@0.5.21:
|
||||
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
|
||||
|
||||
source-map@0.5.7:
|
||||
resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
source-map@0.6.1:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
strip-json-comments@3.1.1:
|
||||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1620,6 +1707,11 @@ packages:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
terser@5.44.0:
|
||||
resolution: {integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@@ -1738,6 +1830,21 @@ packages:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
zustand@4.5.7:
|
||||
resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==}
|
||||
engines: {node: '>=12.7.0'}
|
||||
peerDependencies:
|
||||
'@types/react': '>=16.8'
|
||||
immer: '>=9.0.6'
|
||||
react: '>=16.8'
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
immer:
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
|
||||
zustand@5.0.8:
|
||||
resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
@@ -2102,6 +2209,12 @@ snapshots:
|
||||
|
||||
'@jridgewell/resolve-uri@3.1.2': {}
|
||||
|
||||
'@jridgewell/source-map@0.3.11':
|
||||
dependencies:
|
||||
'@jridgewell/gen-mapping': 0.3.13
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
optional: true
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.31':
|
||||
@@ -2358,6 +2471,27 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/types': 7.28.4
|
||||
|
||||
'@types/d3-color@3.1.3': {}
|
||||
|
||||
'@types/d3-drag@3.0.7':
|
||||
dependencies:
|
||||
'@types/d3-selection': 3.0.11
|
||||
|
||||
'@types/d3-interpolate@3.0.4':
|
||||
dependencies:
|
||||
'@types/d3-color': 3.1.3
|
||||
|
||||
'@types/d3-selection@3.0.11': {}
|
||||
|
||||
'@types/d3-transition@3.0.9':
|
||||
dependencies:
|
||||
'@types/d3-selection': 3.0.11
|
||||
|
||||
'@types/d3-zoom@3.0.8':
|
||||
dependencies:
|
||||
'@types/d3-interpolate': 3.0.4
|
||||
'@types/d3-selection': 3.0.11
|
||||
|
||||
'@types/estree@1.0.8': {}
|
||||
|
||||
'@types/js-cookie@3.0.6': {}
|
||||
@@ -2479,7 +2613,7 @@ snapshots:
|
||||
'@typescript-eslint/types': 8.46.0
|
||||
eslint-visitor-keys: 4.2.1
|
||||
|
||||
'@vitejs/plugin-react@5.0.4(vite@7.1.9(@types/node@24.7.1))':
|
||||
'@vitejs/plugin-react@5.0.4(vite@7.1.9(@types/node@24.7.1)(terser@5.44.0))':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.4
|
||||
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4)
|
||||
@@ -2487,10 +2621,33 @@ snapshots:
|
||||
'@rolldown/pluginutils': 1.0.0-beta.38
|
||||
'@types/babel__core': 7.20.5
|
||||
react-refresh: 0.17.0
|
||||
vite: 7.1.9(@types/node@24.7.1)
|
||||
vite: 7.1.9(@types/node@24.7.1)(terser@5.44.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@xyflow/react@12.8.6(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@xyflow/system': 0.0.70
|
||||
classcat: 5.0.5
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
zustand: 4.5.7(@types/react@19.2.2)(react@18.3.1)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
|
||||
'@xyflow/system@0.0.70':
|
||||
dependencies:
|
||||
'@types/d3-drag': 3.0.7
|
||||
'@types/d3-interpolate': 3.0.4
|
||||
'@types/d3-selection': 3.0.11
|
||||
'@types/d3-transition': 3.0.9
|
||||
'@types/d3-zoom': 3.0.8
|
||||
d3-drag: 3.0.0
|
||||
d3-interpolate: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
d3-zoom: 3.0.0
|
||||
|
||||
acorn-jsx@5.3.2(acorn@8.15.0):
|
||||
dependencies:
|
||||
acorn: 8.15.0
|
||||
@@ -2566,6 +2723,9 @@ snapshots:
|
||||
node-releases: 2.0.23
|
||||
update-browserslist-db: 1.1.3(browserslist@4.26.3)
|
||||
|
||||
buffer-from@1.1.2:
|
||||
optional: true
|
||||
|
||||
call-bind-apply-helpers@1.0.2:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
@@ -2580,6 +2740,8 @@ snapshots:
|
||||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
|
||||
classcat@5.0.5: {}
|
||||
|
||||
clsx@2.1.1: {}
|
||||
|
||||
color-convert@2.0.1:
|
||||
@@ -2592,6 +2754,9 @@ snapshots:
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
|
||||
commander@2.20.3:
|
||||
optional: true
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
|
||||
convert-source-map@1.9.0: {}
|
||||
@@ -2616,6 +2781,42 @@ snapshots:
|
||||
|
||||
csstype@3.1.3: {}
|
||||
|
||||
d3-color@3.1.0: {}
|
||||
|
||||
d3-dispatch@3.0.1: {}
|
||||
|
||||
d3-drag@3.0.0:
|
||||
dependencies:
|
||||
d3-dispatch: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
|
||||
d3-ease@3.0.1: {}
|
||||
|
||||
d3-interpolate@3.0.1:
|
||||
dependencies:
|
||||
d3-color: 3.1.0
|
||||
|
||||
d3-selection@3.0.0: {}
|
||||
|
||||
d3-timer@3.0.1: {}
|
||||
|
||||
d3-transition@3.0.1(d3-selection@3.0.0):
|
||||
dependencies:
|
||||
d3-color: 3.1.0
|
||||
d3-dispatch: 3.0.1
|
||||
d3-ease: 3.0.1
|
||||
d3-interpolate: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
d3-timer: 3.0.1
|
||||
|
||||
d3-zoom@3.0.0:
|
||||
dependencies:
|
||||
d3-dispatch: 3.0.1
|
||||
d3-drag: 3.0.0
|
||||
d3-interpolate: 3.0.1
|
||||
d3-selection: 3.0.0
|
||||
d3-transition: 3.0.1(d3-selection@3.0.0)
|
||||
|
||||
dayjs@1.11.18: {}
|
||||
|
||||
debug@4.4.3:
|
||||
@@ -3189,8 +3390,17 @@ snapshots:
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
source-map-support@0.5.21:
|
||||
dependencies:
|
||||
buffer-from: 1.1.2
|
||||
source-map: 0.6.1
|
||||
optional: true
|
||||
|
||||
source-map@0.5.7: {}
|
||||
|
||||
source-map@0.6.1:
|
||||
optional: true
|
||||
|
||||
strip-json-comments@3.1.1: {}
|
||||
|
||||
stylis@4.2.0: {}
|
||||
@@ -3201,6 +3411,14 @@ snapshots:
|
||||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
terser@5.44.0:
|
||||
dependencies:
|
||||
'@jridgewell/source-map': 0.3.11
|
||||
acorn: 8.15.0
|
||||
commander: 2.20.3
|
||||
source-map-support: 0.5.21
|
||||
optional: true
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
dependencies:
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
@@ -3251,7 +3469,7 @@ snapshots:
|
||||
|
||||
uuid@13.0.0: {}
|
||||
|
||||
vite@7.1.9(@types/node@24.7.1):
|
||||
vite@7.1.9(@types/node@24.7.1)(terser@5.44.0):
|
||||
dependencies:
|
||||
esbuild: 0.25.10
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
@@ -3262,6 +3480,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/node': 24.7.1
|
||||
fsevents: 2.3.3
|
||||
terser: 5.44.0
|
||||
|
||||
void-elements@3.1.0: {}
|
||||
|
||||
@@ -3277,6 +3496,13 @@ snapshots:
|
||||
|
||||
yocto-queue@0.1.0: {}
|
||||
|
||||
zustand@4.5.7(@types/react@19.2.2)(react@18.3.1):
|
||||
dependencies:
|
||||
use-sync-external-store: 1.6.0(react@18.3.1)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
react: 18.3.1
|
||||
|
||||
zustand@5.0.8(@types/react@19.2.2)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)):
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import knowledgeService from '@/services/knowledge_service';
|
||||
import type { IKnowledge, IKnowledgeResult, IParserConfig } from '@/interfaces/database/knowledge';
|
||||
import type { IKnowledge, IKnowledgeGraph, IKnowledgeResult } from '@/interfaces/database/knowledge';
|
||||
import type { IFetchKnowledgeListRequestParams } from '@/interfaces/request/knowledge';
|
||||
|
||||
/**
|
||||
@@ -200,6 +200,12 @@ export const useKnowledgeDetail = (kbId: string) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const [knowledgeGraph, setKnowledgeGraph] = useState<IKnowledgeGraph | null>(null);
|
||||
|
||||
const showKnowledgeGraph = useMemo(() => {
|
||||
return knowledgeGraph !== null && Object.keys(knowledgeGraph?.graph || {}).length > 0;
|
||||
}, [knowledgeGraph]);
|
||||
|
||||
const fetchKnowledgeDetail = useCallback(async () => {
|
||||
if (!kbId) return;
|
||||
|
||||
@@ -223,15 +229,41 @@ export const useKnowledgeDetail = (kbId: string) => {
|
||||
}
|
||||
}, [kbId]);
|
||||
|
||||
const fetchKnowledgeGraph = useCallback(async () => {
|
||||
if (!kbId) return;
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const response = await knowledgeService.getKnowledgeGraph(kbId);
|
||||
|
||||
if (response.data.code === 0) {
|
||||
setKnowledgeGraph(response.data.data);
|
||||
} else {
|
||||
throw new Error(response.data.message || '获取知识库图失败');
|
||||
}
|
||||
} catch (err: any) {
|
||||
const errorMessage = err.response?.data?.message || err.message || '获取知识库图失败';
|
||||
setError(errorMessage);
|
||||
console.error('Failed to fetch knowledge graph:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [kbId]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchKnowledgeDetail();
|
||||
}, [fetchKnowledgeDetail]);
|
||||
fetchKnowledgeGraph();
|
||||
}, [fetchKnowledgeDetail, fetchKnowledgeGraph]);
|
||||
|
||||
return {
|
||||
knowledge,
|
||||
knowledgeGraph,
|
||||
loading,
|
||||
error,
|
||||
refresh: fetchKnowledgeDetail,
|
||||
showKnowledgeGraph,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// import type { AgentCategory } from '@/constants/agent';
|
||||
import type { Edge, Node } from '@xyflow/react';
|
||||
import type { IReference, Message } from './chat';
|
||||
|
||||
export interface ICategorizeItem {
|
||||
name: string;
|
||||
description?: string;
|
||||
@@ -30,11 +34,6 @@ export interface ISwitchForm {
|
||||
no: string;
|
||||
}
|
||||
|
||||
|
||||
import type { AgentCategory } from '@/constants/agent';
|
||||
import type { Edge, Node } from '@xyflow/react';
|
||||
import type { IReference, Message } from './chat';
|
||||
|
||||
export type DSLComponents = Record<string, IOperator>;
|
||||
|
||||
export interface DSL {
|
||||
@@ -157,7 +156,7 @@ export interface IAgentForm {
|
||||
delay_after_error: number;
|
||||
visual_files_var: string;
|
||||
max_rounds: number;
|
||||
exception_method: Nullable<'comment' | 'go'>;
|
||||
// exception_method: Nullable<'comment' | 'go'>;
|
||||
exception_comment: any;
|
||||
exception_goto: any;
|
||||
tools: Array<{
|
||||
@@ -275,5 +274,5 @@ export interface IPipeLineListRequest {
|
||||
keywords?: string;
|
||||
orderby?: string;
|
||||
desc?: boolean;
|
||||
canvas_category?: AgentCategory;
|
||||
// canvas_category?: AgentCategory;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MessageType } from '@/constants/chat';
|
||||
// import { MessageType } from '@/constants/chat';
|
||||
|
||||
export interface PromptConfig {
|
||||
empty_response: string;
|
||||
@@ -89,7 +89,7 @@ export interface IConversation {
|
||||
|
||||
export interface Message {
|
||||
content: string;
|
||||
role: MessageType;
|
||||
// role: MessageType;
|
||||
doc_ids?: string[];
|
||||
prompt?: string;
|
||||
id?: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Edge, Node } from '@xyflow/react';
|
||||
import type { Edge, Node } from '@xyflow/react';
|
||||
import type { IReference, Message } from './chat';
|
||||
|
||||
export type DSLComponents = Record<string, IOperator>;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export enum McpServerType {
|
||||
Sse = 'sse',
|
||||
StreamableHttp = 'streamable-http',
|
||||
}
|
||||
// export enum McpServerType {
|
||||
// Sse = 'sse',
|
||||
// StreamableHttp = 'streamable-http',
|
||||
// }
|
||||
|
||||
export interface IMcpServerVariable {
|
||||
key: string;
|
||||
@@ -12,7 +12,7 @@ export interface IMcpServerInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
url: string;
|
||||
server_type: McpServerType;
|
||||
// server_type: McpServerType;
|
||||
description?: string;
|
||||
variables?: IMcpServerVariable[];
|
||||
headers: Map<string, string>;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IExportedMcpServer } from '@/interfaces/database/mcp';
|
||||
// import { IExportedMcpServer } from '@/interfaces/database/mcp';
|
||||
|
||||
export interface ITestMcpRequestBody {
|
||||
server_type: string;
|
||||
@@ -9,8 +9,8 @@ export interface ITestMcpRequestBody {
|
||||
}
|
||||
|
||||
export interface IImportMcpServersRequestBody {
|
||||
mcpServers: Record<
|
||||
string,
|
||||
Pick<IExportedMcpServer, 'type' | 'url' | 'authorization_token'>
|
||||
>;
|
||||
// mcpServers: Record<
|
||||
// string,
|
||||
// Pick<IExportedMcpServer, 'type' | 'url' | 'authorization_token'>
|
||||
// >;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ const MetricValue = styled(Typography)(({ theme }) => ({
|
||||
lineHeight: 1.2,
|
||||
}));
|
||||
|
||||
const TrendIndicator = styled(Box)<{ trend: 'up' | 'down' }>(({ trend, theme }) => ({
|
||||
const TrendIndicator = styled(Box)<{ trend: string }>(({ trend, theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '0.25rem',
|
||||
@@ -245,7 +245,7 @@ const Dashboard: React.FC = () => {
|
||||
|
||||
{/* 关键指标卡片 */}
|
||||
<Grid container spacing={3} sx={{ mb: 3 }}>
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<MetricCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
@@ -270,7 +270,7 @@ const Dashboard: React.FC = () => {
|
||||
</MetricCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<MetricCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
@@ -295,7 +295,7 @@ const Dashboard: React.FC = () => {
|
||||
</MetricCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<MetricCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
@@ -320,7 +320,7 @@ const Dashboard: React.FC = () => {
|
||||
</MetricCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<MetricCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
@@ -362,7 +362,7 @@ const Dashboard: React.FC = () => {
|
||||
</Button>
|
||||
</Box>
|
||||
<KnowledgeGridView
|
||||
knowledgeBases={mockKnowledgeBases}
|
||||
knowledgeBases={mockKnowledgeBases as any}
|
||||
maxItems={3}
|
||||
showSeeAll={true}
|
||||
onSeeAll={handleSeeAllKnowledgeBases}
|
||||
@@ -372,7 +372,7 @@ const Dashboard: React.FC = () => {
|
||||
|
||||
{/* 系统状态 */}
|
||||
<Grid container spacing={3} sx={{ mb: 3 }}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Grid size={{xs:12,md:6}}>
|
||||
<Card sx={{ border: '1px solid #E5E5E5' }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" fontWeight={600} mb={2}>
|
||||
@@ -403,7 +403,7 @@ const Dashboard: React.FC = () => {
|
||||
</Card>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={6}>
|
||||
<Grid size={{xs:12,md:6}}>
|
||||
<Card sx={{ border: '1px solid #E5E5E5' }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" fontWeight={600} mb={2}>
|
||||
@@ -479,15 +479,6 @@ const Dashboard: React.FC = () => {
|
||||
</TableContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 用户数据调试组件 - 仅在开发环境显示 */}
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<Card sx={{ border: '1px solid #E5E5E5', mt: 3 }}>
|
||||
<CardContent>
|
||||
<UserDataDebug />
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -193,7 +193,7 @@ const MCP: React.FC = () => {
|
||||
<>
|
||||
{/* 状态概览 */}
|
||||
<Grid container spacing={3} sx={{ mb: 3 }}>
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<StatusCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
@@ -211,7 +211,7 @@ const MCP: React.FC = () => {
|
||||
</StatusCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<StatusCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
@@ -229,7 +229,7 @@ const MCP: React.FC = () => {
|
||||
</StatusCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<StatusCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
@@ -247,7 +247,7 @@ const MCP: React.FC = () => {
|
||||
</StatusCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<StatusCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
@@ -359,7 +359,7 @@ const MCP: React.FC = () => {
|
||||
</Alert>
|
||||
<Grid container spacing={2}>
|
||||
{mockMCPServers.map((server) => (
|
||||
<Grid item xs={12} md={6} key={server.id}>
|
||||
<Grid size={{xs:12,md:6}} key={server.id}>
|
||||
<Card variant="outlined">
|
||||
<CardContent>
|
||||
<Typography variant="subtitle1" fontWeight={600} mb={1}>
|
||||
@@ -389,7 +389,7 @@ const MCP: React.FC = () => {
|
||||
|
||||
{tabValue === 2 && (
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Grid size={{xs:12,md:6}}>
|
||||
<Card sx={{ border: '1px solid #E5E5E5' }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" fontWeight={600} mb={2}>
|
||||
@@ -418,7 +418,7 @@ const MCP: React.FC = () => {
|
||||
</Card>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={6}>
|
||||
<Grid size={{xs:12,md:6}}>
|
||||
<Card sx={{ border: '1px solid #E5E5E5' }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" fontWeight={600} mb={2}>
|
||||
|
||||
@@ -226,7 +226,7 @@ const ModelsResources: React.FC = () => {
|
||||
<>
|
||||
{/* 模型概览卡片 */}
|
||||
<Grid container spacing={3} sx={{ mb: 3 }}>
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<ResourceCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
@@ -244,7 +244,7 @@ const ModelsResources: React.FC = () => {
|
||||
</ResourceCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<ResourceCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
@@ -262,7 +262,7 @@ const ModelsResources: React.FC = () => {
|
||||
</ResourceCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<ResourceCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
@@ -280,7 +280,7 @@ const ModelsResources: React.FC = () => {
|
||||
</ResourceCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<ResourceCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
@@ -385,7 +385,7 @@ const ModelsResources: React.FC = () => {
|
||||
<>
|
||||
{/* 资源概览卡片 */}
|
||||
<Grid container spacing={3} sx={{ mb: 3 }}>
|
||||
<Grid item xs={12} sm={6} md={4}>
|
||||
<Grid size={{xs:12,sm:6,md:4}}>
|
||||
<ResourceCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
@@ -403,7 +403,7 @@ const ModelsResources: React.FC = () => {
|
||||
</ResourceCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={4}>
|
||||
<Grid size={{xs:12,sm:6,md:4}}>
|
||||
<ResourceCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
@@ -421,7 +421,7 @@ const ModelsResources: React.FC = () => {
|
||||
</ResourceCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} sm={6} md={4}>
|
||||
<Grid size={{xs:12,sm:6,md:4}}>
|
||||
<ResourceCard>
|
||||
<CardContent>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
|
||||
@@ -155,7 +155,7 @@ const PipelineConfig: React.FC = () => {
|
||||
</PageHeader>
|
||||
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Grid size={{xs:12,md:6}}>
|
||||
<ConfigCard>
|
||||
<CardContent>
|
||||
<Typography variant="h6" fontWeight={600} mb={2}>
|
||||
@@ -223,7 +223,7 @@ const PipelineConfig: React.FC = () => {
|
||||
</ConfigCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={6}>
|
||||
<Grid size={{xs:12,md:6}}>
|
||||
<ConfigCard>
|
||||
<CardContent>
|
||||
<Typography variant="h6" fontWeight={600} mb={2}>
|
||||
@@ -283,7 +283,7 @@ const PipelineConfig: React.FC = () => {
|
||||
</ConfigCard>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Grid size={{xs:12,md:6}}>
|
||||
<ConfigCard>
|
||||
<CardContent>
|
||||
<Typography variant="h6" fontWeight={600} mb={2}>
|
||||
@@ -291,7 +291,7 @@ const PipelineConfig: React.FC = () => {
|
||||
</Typography>
|
||||
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<Box textAlign="center" p={2} bgcolor="#F8F9FA" borderRadius="6px">
|
||||
<Typography variant="h4" color="primary" fontWeight={600}>
|
||||
1,234
|
||||
@@ -301,7 +301,7 @@ const PipelineConfig: React.FC = () => {
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<Box textAlign="center" p={2} bgcolor="#F8F9FA" borderRadius="6px">
|
||||
<Typography variant="h4" color="success.main" fontWeight={600}>
|
||||
98.5%
|
||||
@@ -311,7 +311,7 @@ const PipelineConfig: React.FC = () => {
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<Box textAlign="center" p={2} bgcolor="#F8F9FA" borderRadius="6px">
|
||||
<Typography variant="h4" color="warning.main" fontWeight={600}>
|
||||
2.3s
|
||||
@@ -321,7 +321,7 @@ const PipelineConfig: React.FC = () => {
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} md={3}>
|
||||
<Grid size={{xs:12,sm:6,md:3}}>
|
||||
<Box textAlign="center" p={2} bgcolor="#F8F9FA" borderRadius="6px">
|
||||
<Typography variant="h4" color="info.main" fontWeight={600}>
|
||||
156
|
||||
|
||||
351
src/pages/knowledge/components/KnowledgeGraphView.tsx
Normal file
351
src/pages/knowledge/components/KnowledgeGraphView.tsx
Normal file
@@ -0,0 +1,351 @@
|
||||
import React, { useMemo, useCallback } from 'react';
|
||||
import {
|
||||
ReactFlow,
|
||||
type Node,
|
||||
type Edge,
|
||||
Controls,
|
||||
Background,
|
||||
useNodesState,
|
||||
useEdgesState,
|
||||
ConnectionMode,
|
||||
Panel,
|
||||
Handle,
|
||||
Position,
|
||||
} from '@xyflow/react';
|
||||
import '@xyflow/react/dist/style.css';
|
||||
import { Box, Typography, Alert, Chip, Card, CardContent, Tooltip, Avatar } from '@mui/material';
|
||||
import type { IKnowledgeGraph } from '@/interfaces/database/knowledge';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
interface KnowledgeGraphProps {
|
||||
knowledgeGraph?: IKnowledgeGraph | null;
|
||||
}
|
||||
|
||||
// 根据实体类型获取节点颜色
|
||||
const getNodeColor = (entityType?: string): string => {
|
||||
const colorMap: Record<string, string> = {
|
||||
PERSON: '#FF6B6B', // 红色 - 人物
|
||||
ORGANIZATION: '#4ECDC4', // 青色 - 组织
|
||||
LOCATION: '#45B7D1', // 蓝色 - 地点
|
||||
EVENT: '#96CEB4', // 绿色 - 事件
|
||||
CONCEPT: '#FFEAA7', // 黄色 - 概念
|
||||
CATEGORY: '#DDA0DD', // 紫色 - 分类
|
||||
TECHNOLOGY: '#98D8C8', // 薄荷绿 - 技术
|
||||
PRODUCT: '#F7DC6F', // 金黄色 - 产品
|
||||
SERVICE: '#BB8FCE', // 淡紫色 - 服务
|
||||
};
|
||||
return colorMap[entityType || 'CONCEPT'] || '#74B9FF';
|
||||
};
|
||||
|
||||
// 自定义节点组件
|
||||
const CustomNode = ({ data }: { data: any }) => {
|
||||
const nodeColor = getNodeColor(data.entity_type);
|
||||
const nodeSize = Math.max(80, Math.min(140, (data.pagerank || 0.1) * 500));
|
||||
|
||||
// 获取节点首字母作为Avatar显示
|
||||
const getInitials = (name: string) => {
|
||||
if (!name) return '?';
|
||||
const words = name.trim().split(/\s+/);
|
||||
if (words.length === 1) {
|
||||
return words[0].charAt(0).toUpperCase();
|
||||
}
|
||||
return words.slice(0, 2).map(word => word.charAt(0).toUpperCase()).join('');
|
||||
};
|
||||
|
||||
const tooltipContent = (
|
||||
<Box sx={{ p: 1, maxWidth: 300 }}>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 'bold', mb: 1 }}>
|
||||
{data.label || data.name || 'Unknown'}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ mb: 0.5 }}>
|
||||
<strong>类型:</strong> {data.entity_type || 'Unknown'}
|
||||
</Typography>
|
||||
{data.description && (
|
||||
<Typography variant="body2">
|
||||
<strong>描述:</strong> {data.description}
|
||||
</Typography>
|
||||
)}
|
||||
{data.pagerank && (
|
||||
<Typography variant="caption" sx={{ display: 'block', mt: 1 }}>
|
||||
PageRank: {data.pagerank.toFixed(4)}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
title={tooltipContent}
|
||||
placement="top"
|
||||
arrow
|
||||
enterDelay={500}
|
||||
leaveDelay={200}
|
||||
>
|
||||
<Card
|
||||
sx={{
|
||||
width: nodeSize,
|
||||
height: nodeSize,
|
||||
borderRadius: '50%',
|
||||
border: '3px solid #fff',
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
|
||||
cursor: 'pointer',
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: nodeColor,
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
'&:hover': {
|
||||
transform: 'scale(1.1)',
|
||||
boxShadow: '0 6px 20px rgba(0,0,0,0.25)',
|
||||
zIndex: 10,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{/* React Flow Handle 组件 */}
|
||||
<Handle
|
||||
type="target"
|
||||
position={Position.Top}
|
||||
style={{ background: '#555', opacity: 0 }}
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Bottom}
|
||||
style={{ background: '#555', opacity: 0 }}
|
||||
/>
|
||||
|
||||
<CardContent
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
p: 1,
|
||||
'&:last-child': { pb: 1 },
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={{
|
||||
color: '#fff',
|
||||
fontWeight: 'bold',
|
||||
fontSize: Math.max(10, nodeSize * 0.08),
|
||||
textAlign: 'center',
|
||||
lineHeight: 1.1,
|
||||
wordBreak: 'break-word',
|
||||
textShadow: '1px 1px 2px rgba(0,0,0,0.7)',
|
||||
maxWidth: '90%',
|
||||
overflow: 'hidden',
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
}}
|
||||
>
|
||||
{data.label || data.name || 'Unknown'}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const nodeTypes = {
|
||||
custom: CustomNode,
|
||||
};
|
||||
|
||||
const KnowledgeGraphView: React.FC<KnowledgeGraphProps> = ({ knowledgeGraph }) => {
|
||||
// 转换数据格式为 React Flow 所需的格式
|
||||
const { nodes: initialNodes, edges: initialEdges } = useMemo(() => {
|
||||
const graphData = knowledgeGraph?.graph || {};
|
||||
if (!graphData?.nodes || !graphData?.edges) {
|
||||
return { nodes: [], edges: [] };
|
||||
}
|
||||
|
||||
// 转换节点数据
|
||||
// @ts-ignore
|
||||
const nodes: Node[] = graphData.nodes.map((node, index) => {
|
||||
console.log(`节点 ${index}:`, node);
|
||||
console.log(`节点ID: ${node.id}`);
|
||||
|
||||
const encodeId = encodeURIComponent(String(node.id));
|
||||
|
||||
const n: Node = {
|
||||
id: encodeId,
|
||||
type: 'custom',
|
||||
position: {
|
||||
x: Math.random() * 800,
|
||||
y: Math.random() * 600,
|
||||
},
|
||||
data: {
|
||||
label: node.entity_name || node.id,
|
||||
entity_type: node.entity_type,
|
||||
pagerank: node.pagerank,
|
||||
description: node.description,
|
||||
...node,
|
||||
},
|
||||
};
|
||||
return n
|
||||
});
|
||||
|
||||
// 转换边数据
|
||||
// @ts-ignore
|
||||
const edges: Edge[] = graphData.edges.map((edge, index) => {
|
||||
console.log(`边 ${index}:`, edge);
|
||||
console.log(`src_id: ${edge.src_id}, tgt_id: ${edge.tgt_id}`);
|
||||
// 检查source和target是否存在
|
||||
if (!edge.src_id || !edge.tgt_id) {
|
||||
console.warn(`边 ${index} 缺少src_id或tgt_id:`, edge);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查对应的节点是否存在
|
||||
const sourceExists = nodes.some(node => node.id === encodeURIComponent(edge.src_id));
|
||||
const targetExists = nodes.some(node => node.id === encodeURIComponent(edge.tgt_id));
|
||||
|
||||
if (!sourceExists || !targetExists) {
|
||||
console.warn(`边 ${index} 的节点不存在: source=${sourceExists}, target=${targetExists}`, edge);
|
||||
return null;
|
||||
}
|
||||
|
||||
const weight = Number(edge.weight) || 1;
|
||||
const strokeWidth = Math.max(1, Math.min(3, weight));
|
||||
|
||||
const sourceId = encodeURIComponent(String(edge.src_id));
|
||||
const targetId = encodeURIComponent(String(edge.tgt_id));
|
||||
|
||||
const edgeObj: Edge = {
|
||||
id: `${sourceId}-${targetId}`,
|
||||
source: sourceId,
|
||||
target: targetId,
|
||||
type: 'simplebezier',
|
||||
// animated: weight > 5,
|
||||
style: {
|
||||
strokeWidth,
|
||||
stroke: '#99ADD1',
|
||||
},
|
||||
// label: edge.description ? edge.description.substring(0, 50) + '...' : '',
|
||||
labelStyle: {
|
||||
fontSize: '10px',
|
||||
fill: '#666',
|
||||
},
|
||||
data: {
|
||||
weight: edge.weight,
|
||||
description: edge.description,
|
||||
keywords: edge.keywords,
|
||||
},
|
||||
};
|
||||
return edgeObj;
|
||||
})
|
||||
// @ts-ignore
|
||||
.filter(edge => edge != null); // 过滤掉null值
|
||||
return { nodes, edges };
|
||||
}, [knowledgeGraph]);
|
||||
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||
|
||||
console.log('转换后的节点:', nodes);
|
||||
console.log('转换后的边:', edges);
|
||||
// 节点点击事件
|
||||
const onNodeClick = useCallback((event: React.MouseEvent, node: Node) => {
|
||||
console.log('节点点击:', node.data);
|
||||
}, []);
|
||||
|
||||
// 边点击事件
|
||||
const onEdgeClick = useCallback((event: React.MouseEvent, edge: Edge) => {
|
||||
console.log('边点击:', edge.data);
|
||||
}, []);
|
||||
|
||||
if (!knowledgeGraph || initialNodes.length === 0) {
|
||||
return (
|
||||
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', alignItems: 'center', height: 400 }}>
|
||||
<Alert severity="info">暂无知识图谱数据</Alert>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%', height: '600px', border: '1px solid #e0e0e0', borderRadius: 1 }}>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onNodeClick={onNodeClick}
|
||||
onEdgeClick={onEdgeClick}
|
||||
nodeTypes={nodeTypes}
|
||||
connectionMode={ConnectionMode.Loose}
|
||||
fitView
|
||||
fitViewOptions={{ padding: 0.2 }}
|
||||
minZoom={0.1}
|
||||
maxZoom={2}
|
||||
>
|
||||
<Background />
|
||||
<Controls />
|
||||
|
||||
{/* 图例面板 */}
|
||||
<Panel position="top-right">
|
||||
<Box sx={{
|
||||
backgroundColor: 'rgba(255,255,255,0.9)',
|
||||
p: 2,
|
||||
borderRadius: 1,
|
||||
minWidth: 200,
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.1)'
|
||||
}}>
|
||||
<Typography variant="subtitle2" sx={{ mb: 1, fontWeight: 'bold' }}>
|
||||
图例
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>
|
||||
{['PERSON', 'ORGANIZATION', 'CATEGORY', 'TECHNOLOGY'].map((type) => (
|
||||
<Box key={type} sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: 16,
|
||||
height: 16,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: getNodeColor(type),
|
||||
border: '1px solid #fff',
|
||||
}}
|
||||
/>
|
||||
<Typography variant="caption">{type}</Typography>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
</Panel>
|
||||
|
||||
{/* 统计信息面板 */}
|
||||
<Panel position="top-left">
|
||||
<Box sx={{
|
||||
backgroundColor: 'rgba(255,255,255,0.9)',
|
||||
p: 2,
|
||||
borderRadius: 1,
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.1)'
|
||||
}}>
|
||||
<Typography variant="subtitle2" sx={{ mb: 1, fontWeight: 'bold' }}>
|
||||
图谱统计
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
|
||||
<Chip
|
||||
label={`节点: ${nodes.length}`}
|
||||
size="small"
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
/>
|
||||
<Chip
|
||||
label={`边: ${edges.length}`}
|
||||
size="small"
|
||||
color="secondary"
|
||||
variant="outlined"
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Panel>
|
||||
</ReactFlow>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default KnowledgeGraphView;
|
||||
@@ -18,9 +18,10 @@ import {
|
||||
CardContent,
|
||||
Divider,
|
||||
Chip,
|
||||
Tabs,
|
||||
Tab,
|
||||
} from '@mui/material';
|
||||
import { type GridRowSelectionModel } from '@mui/x-data-grid';
|
||||
import knowledgeService from '@/services/knowledge_service';
|
||||
import type { IKnowledge, IKnowledgeFile } from '@/interfaces/database/knowledge';
|
||||
import type { IDocumentInfoFilter } from '@/interfaces/database/document';
|
||||
import FileUploadDialog from '@/components/FileUploadDialog';
|
||||
@@ -28,16 +29,16 @@ import KnowledgeInfoCard from './components/KnowledgeInfoCard';
|
||||
import DocumentListComponent from './components/DocumentListComponent';
|
||||
import FloatingActionButtons from './components/FloatingActionButtons';
|
||||
import KnowledgeBreadcrumbs from './components/KnowledgeBreadcrumbs';
|
||||
import KnowledgeGraphView from './components/KnowledgeGraphView';
|
||||
import { useDocumentList, useDocumentOperations } from '@/hooks/document-hooks';
|
||||
import { RUNNING_STATUS_KEYS } from '@/constants/knowledge';
|
||||
import { useKnowledgeDetail } from '@/hooks/knowledge-hooks';
|
||||
|
||||
function KnowledgeBaseDetail() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// 状态管理
|
||||
const [knowledgeBase, setKnowledgeBase] = useState<IKnowledge | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [searchKeyword, setSearchKeyword] = useState('');
|
||||
const [rowSelectionModel, setRowSelectionModel] = useState<GridRowSelectionModel>({
|
||||
@@ -46,11 +47,13 @@ function KnowledgeBaseDetail() {
|
||||
});
|
||||
const [uploadDialogOpen, setUploadDialogOpen] = useState(false);
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
const [testingDialogOpen, setTestingDialogOpen] = useState(false);
|
||||
const [configDialogOpen, setConfigDialogOpen] = useState(false);
|
||||
|
||||
const [processDetailsDialogOpen, setProcessDetailsDialogOpen] = useState(false);
|
||||
const [selectedFileDetails, setSelectedFileDetails] = useState<IKnowledgeFile | null>(null);
|
||||
|
||||
// 标签页状态
|
||||
const [currentTab, setCurrentTab] = useState(0);
|
||||
|
||||
// 轮询相关状态
|
||||
const pollingIntervalRef = useRef<any>(null);
|
||||
const [isPolling, setIsPolling] = useState(false);
|
||||
@@ -80,25 +83,9 @@ function KnowledgeBaseDetail() {
|
||||
error: operationError,
|
||||
} = useDocumentOperations();
|
||||
|
||||
// 获取知识库详情
|
||||
const fetchKnowledgeDetail = async () => {
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await knowledgeService.getKnowledgeDetail({ kb_id: id });
|
||||
|
||||
if (response.data.code === 0) {
|
||||
setKnowledgeBase(response.data.data);
|
||||
} else {
|
||||
setError(response.data.message || '获取知识库详情失败');
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.response?.data?.message || err.message || '获取知识库详情失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
const { knowledge: knowledgeBase, refresh: fetchKnowledgeDetail, loading, showKnowledgeGraph, knowledgeGraph } = useKnowledgeDetail(id || '');
|
||||
|
||||
console.log('showKnowledgeGraph:', showKnowledgeGraph, knowledgeGraph);
|
||||
|
||||
// 删除文件
|
||||
const handleDeleteFiles = async () => {
|
||||
@@ -291,42 +278,104 @@ function KnowledgeBaseDetail() {
|
||||
{/* 知识库信息卡片 */}
|
||||
<KnowledgeInfoCard knowledgeBase={knowledgeBase} />
|
||||
|
||||
{/* 文件列表组件 */}
|
||||
<DocumentListComponent
|
||||
files={files}
|
||||
loading={filesLoading}
|
||||
searchKeyword={searchKeyword}
|
||||
onSearchChange={setSearchKeyword}
|
||||
onReparse={(fileIds) => handleReparse(fileIds)}
|
||||
onDelete={(fileIds) => {
|
||||
console.log('删除文件:', fileIds);
|
||||
setRowSelectionModel({
|
||||
type: 'include',
|
||||
ids: new Set(fileIds)
|
||||
});
|
||||
setDeleteDialogOpen(true);
|
||||
}}
|
||||
onUpload={() => setUploadDialogOpen(true)}
|
||||
onRefresh={() => {
|
||||
refreshFiles();
|
||||
fetchKnowledgeDetail();
|
||||
}}
|
||||
rowSelectionModel={rowSelectionModel}
|
||||
onRowSelectionModelChange={(newModel) => {
|
||||
console.log('新的选择模型:', newModel);
|
||||
setRowSelectionModel(newModel);
|
||||
}}
|
||||
total={total}
|
||||
page={currentPage}
|
||||
pageSize={pageSize}
|
||||
onPageChange={setCurrentPage}
|
||||
onPageSizeChange={setPageSize}
|
||||
onRename={handleRename}
|
||||
onChangeStatus={handleChangeStatus}
|
||||
onCancelRun={handleCancelRun}
|
||||
onViewDetails={handleViewDetails}
|
||||
onViewProcessDetails={handleViewProcessDetails}
|
||||
/>
|
||||
{/* 标签页组件 - 仅在showKnowledgeGraph为true时显示 */}
|
||||
{showKnowledgeGraph ? (
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<Tabs
|
||||
value={currentTab}
|
||||
onChange={(event, newValue) => setCurrentTab(newValue)}
|
||||
sx={{ borderBottom: 1, borderColor: 'divider' }}
|
||||
>
|
||||
<Tab label="Documents" />
|
||||
<Tab label="Graph" />
|
||||
</Tabs>
|
||||
|
||||
{/* Document List 标签页内容 */}
|
||||
{currentTab === 0 && (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<DocumentListComponent
|
||||
files={files}
|
||||
loading={filesLoading}
|
||||
searchKeyword={searchKeyword}
|
||||
onSearchChange={setSearchKeyword}
|
||||
onReparse={(fileIds) => handleReparse(fileIds)}
|
||||
onDelete={(fileIds) => {
|
||||
console.log('删除文件:', fileIds);
|
||||
setRowSelectionModel({
|
||||
type: 'include',
|
||||
ids: new Set(fileIds)
|
||||
});
|
||||
setDeleteDialogOpen(true);
|
||||
}}
|
||||
onUpload={() => setUploadDialogOpen(true)}
|
||||
onRefresh={() => {
|
||||
refreshFiles();
|
||||
fetchKnowledgeDetail();
|
||||
}}
|
||||
rowSelectionModel={rowSelectionModel}
|
||||
onRowSelectionModelChange={(newModel) => {
|
||||
console.log('新的选择模型:', newModel);
|
||||
setRowSelectionModel(newModel);
|
||||
}}
|
||||
total={total}
|
||||
page={currentPage}
|
||||
pageSize={pageSize}
|
||||
onPageChange={setCurrentPage}
|
||||
onPageSizeChange={setPageSize}
|
||||
onRename={handleRename}
|
||||
onChangeStatus={handleChangeStatus}
|
||||
onCancelRun={handleCancelRun}
|
||||
onViewDetails={handleViewDetails}
|
||||
onViewProcessDetails={handleViewProcessDetails}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Graph 标签页内容 */}
|
||||
{currentTab === 1 && (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<KnowledgeGraphView knowledgeGraph={knowledgeGraph} />
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
) : (
|
||||
/* 原有的文件列表组件 - 当showKnowledgeGraph为false时显示 */
|
||||
<DocumentListComponent
|
||||
files={files}
|
||||
loading={filesLoading}
|
||||
searchKeyword={searchKeyword}
|
||||
onSearchChange={setSearchKeyword}
|
||||
onReparse={(fileIds) => handleReparse(fileIds)}
|
||||
onDelete={(fileIds) => {
|
||||
console.log('删除文件:', fileIds);
|
||||
setRowSelectionModel({
|
||||
type: 'include',
|
||||
ids: new Set(fileIds)
|
||||
});
|
||||
setDeleteDialogOpen(true);
|
||||
}}
|
||||
onUpload={() => setUploadDialogOpen(true)}
|
||||
onRefresh={() => {
|
||||
refreshFiles();
|
||||
fetchKnowledgeDetail();
|
||||
}}
|
||||
rowSelectionModel={rowSelectionModel}
|
||||
onRowSelectionModelChange={(newModel) => {
|
||||
console.log('新的选择模型:', newModel);
|
||||
setRowSelectionModel(newModel);
|
||||
}}
|
||||
total={total}
|
||||
page={currentPage}
|
||||
pageSize={pageSize}
|
||||
onPageChange={setCurrentPage}
|
||||
onPageSizeChange={setPageSize}
|
||||
onRename={handleRename}
|
||||
onChangeStatus={handleChangeStatus}
|
||||
onCancelRun={handleCancelRun}
|
||||
onViewDetails={handleViewDetails}
|
||||
onViewProcessDetails={handleViewProcessDetails}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 浮动操作按钮 */}
|
||||
<FloatingActionButtons
|
||||
@@ -359,88 +408,6 @@ function KnowledgeBaseDetail() {
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
{/* 检索测试对话框 */}
|
||||
<Dialog open={testingDialogOpen} onClose={() => setTestingDialogOpen(false)} maxWidth="md" fullWidth>
|
||||
<DialogTitle>检索测试</DialogTitle>
|
||||
<DialogContent>
|
||||
<Stack spacing={3} sx={{ mt: 1 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="测试查询"
|
||||
placeholder="输入要测试的查询内容..."
|
||||
multiline
|
||||
rows={3}
|
||||
/>
|
||||
<Stack direction="row" spacing={2}>
|
||||
<TextField
|
||||
label="返回结果数量"
|
||||
type="number"
|
||||
defaultValue={5}
|
||||
sx={{ width: 150 }}
|
||||
/>
|
||||
<TextField
|
||||
label="相似度阈值"
|
||||
type="number"
|
||||
defaultValue={0.7}
|
||||
inputProps={{ min: 0, max: 1, step: 0.1 }}
|
||||
sx={{ width: 150 }}
|
||||
/>
|
||||
</Stack>
|
||||
<Box sx={{ minHeight: 200, border: '1px solid #e0e0e0', borderRadius: 1, p: 2 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
测试结果将在这里显示...
|
||||
</Typography>
|
||||
</Box>
|
||||
</Stack>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setTestingDialogOpen(false)}>关闭</Button>
|
||||
<Button variant="contained">开始测试</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
{/* 配置设置对话框 */}
|
||||
<Dialog open={configDialogOpen} onClose={() => setConfigDialogOpen(false)} maxWidth="sm" fullWidth>
|
||||
<DialogTitle>配置设置</DialogTitle>
|
||||
<DialogContent>
|
||||
<Stack spacing={3} sx={{ mt: 1 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="知识库名称"
|
||||
defaultValue={knowledgeBase?.name}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="描述"
|
||||
multiline
|
||||
rows={3}
|
||||
defaultValue={knowledgeBase?.description}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="语言"
|
||||
defaultValue={knowledgeBase?.language}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="嵌入模型"
|
||||
defaultValue={knowledgeBase?.embd_id}
|
||||
disabled
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="解析器"
|
||||
defaultValue={knowledgeBase?.parser_id}
|
||||
disabled
|
||||
/>
|
||||
</Stack>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setConfigDialogOpen(false)}>取消</Button>
|
||||
<Button variant="contained">保存</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
{/* 文档详情对话框 */}
|
||||
<Dialog
|
||||
open={processDetailsDialogOpen}
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true,
|
||||
|
||||
Reference in New Issue
Block a user