3 Commits 0b087baf6a ... f18ca568fd

Author SHA1 Message Date
  Ryuiso f18ca568fd 忽略.claude 文件 1 month ago
  Ryuiso f9455df8a9 修复 SCSS 变量引用和样式微调 1 month ago
  Ryuiso 1becaf89fc LESS → SCSS 迁移完成 1 month ago
87 changed files with 1666 additions and 15429 deletions
  1. 1 0
      .gitignore
  2. 55 1
      jk-rag-platform/.claude/settings.local.json
  3. 1 0
      jk-rag-platform/package.json
  4. 1 1
      jk-rag-platform/src/components/404/style.scss
  5. 0 618
      jk-rag-platform/src/components/chat/index.tsx.bak
  6. 0 0
      jk-rag-platform/src/components/chat/welcome.scss
  7. 0 84
      jk-rag-platform/src/components/chat/welcome.tsx.bak
  8. 0 415
      jk-rag-platform/src/components/common/AppCard/index.less.rewrite-backup
  9. 124 124
      jk-rag-platform/src/components/common/AppCard/index.scss
  10. 0 321
      jk-rag-platform/src/components/common/AppCard/index.tsx.rewrite-backup
  11. 32 32
      jk-rag-platform/src/components/common/FilterBar/index.scss
  12. 6 6
      jk-rag-platform/src/components/common/FilterDrawer/index.scss
  13. 33 33
      jk-rag-platform/src/components/common/GuideTips/index.scss
  14. 2 2
      jk-rag-platform/src/components/common/HeroBanner/index.scss
  15. 0 135
      jk-rag-platform/src/components/common/StatsGrid/index.less
  16. 135 0
      jk-rag-platform/src/components/common/StatsGrid/index.scss
  17. 1 1
      jk-rag-platform/src/main.tsx
  18. 0 175
      jk-rag-platform/src/pages/appCenter/appPlazaList/style.less.bak
  19. 6 6
      jk-rag-platform/src/pages/appCenter/appPlazaList/style.scss
  20. 0 134
      jk-rag-platform/src/pages/appCenter/categoryApps/style.less.bak
  21. 6 6
      jk-rag-platform/src/pages/appCenter/categoryApps/style.scss
  22. 0 0
      jk-rag-platform/src/pages/appCenter/mobile/MobileH5.scss
  23. 0 0
      jk-rag-platform/src/pages/appCenter/mobile/components/AppPlaza.scss
  24. 0 0
      jk-rag-platform/src/pages/appCenter/mobile/styles/mobile.scss
  25. 0 130
      jk-rag-platform/src/pages/deepseek/dataExport/index.tsx.bak
  26. 0 0
      jk-rag-platform/src/pages/deepseek/dataExport/style.scss
  27. 0 184
      jk-rag-platform/src/pages/deepseek/evaluationTool/datasetManagement/list/index.tsx.bak
  28. 0 163
      jk-rag-platform/src/pages/deepseek/evaluationTool/evaluationTask/components/evaluationTaskHistory.tsx.bak
  29. 0 269
      jk-rag-platform/src/pages/deepseek/evaluationTool/evaluationTask/list/index.tsx.bak
  30. 3 3
      jk-rag-platform/src/pages/home/style.scss
  31. 0 116
      jk-rag-platform/src/pages/knowledgeLib/detail/components/InfoModal.tsx.bak
  32. 0 305
      jk-rag-platform/src/pages/knowledgeLib/detail/components/InfoModalSetting.tsx.bak
  33. 0 1573
      jk-rag-platform/src/pages/knowledgeLib/detail/components/MdModal.tsx.bak
  34. 0 358
      jk-rag-platform/src/pages/knowledgeLib/detail/components/QuoteModal.tsx.bak
  35. 0 669
      jk-rag-platform/src/pages/knowledgeLib/detail/components/UploadModal.tsx.bak
  36. 0 437
      jk-rag-platform/src/pages/knowledgeLib/detail/components/prevewSlice.tsx.bak
  37. 0 0
      jk-rag-platform/src/pages/knowledgeLib/detail/components/style.scss
  38. 0 364
      jk-rag-platform/src/pages/knowledgeLib/detail/drawerIndex.tsx.bak
  39. 0 680
      jk-rag-platform/src/pages/knowledgeLib/detail/index.tsx.bak
  40. 9 9
      jk-rag-platform/src/pages/knowledgeLib/detail/style.scss
  41. 34 34
      jk-rag-platform/src/pages/knowledgeLib/list/KnowledgeDrawer.scss
  42. 0 365
      jk-rag-platform/src/pages/knowledgeLib/list/index.tsx.backup.final
  43. 0 381
      jk-rag-platform/src/pages/knowledgeLib/list/index.tsx.backup.v2
  44. 0 381
      jk-rag-platform/src/pages/knowledgeLib/list/index.tsx.backup.v3
  45. 0 51
      jk-rag-platform/src/pages/knowledgeLib/list/style.less.backup.final
  46. 11 11
      jk-rag-platform/src/pages/knowledgeLib/list/style.scss
  47. 2 2
      jk-rag-platform/src/pages/knowledgeLib/revisionTool/components/reviseDrawer.scss
  48. 0 547
      jk-rag-platform/src/pages/knowledgeLib/revisionTool/components/reviseDrawer.tsx.bak
  49. 0 344
      jk-rag-platform/src/pages/knowledgeLib/revisionTool/components/revisionHistory.tsx.bak
  50. 0 163
      jk-rag-platform/src/pages/knowledgeLib/revisionTool/list/index.tsx.bak
  51. 42 42
      jk-rag-platform/src/pages/layout/components/header.scss
  52. 0 185
      jk-rag-platform/src/pages/layout/components/sidebar.less.bak
  53. 51 51
      jk-rag-platform/src/pages/layout/components/sidebar.scss
  54. 0 399
      jk-rag-platform/src/pages/layout/style.less.bak
  55. 34 34
      jk-rag-platform/src/pages/layout/style.scss
  56. 0 57
      jk-rag-platform/src/pages/login/style.less.bak
  57. 9 9
      jk-rag-platform/src/pages/login/style.scss
  58. 7 7
      jk-rag-platform/src/pages/otherApps/otherAppList/style.scss
  59. 6 6
      jk-rag-platform/src/pages/otherApps/style.scss
  60. 57 57
      jk-rag-platform/src/pages/questionAnswer/form/DrawerForm.scss
  61. 0 331
      jk-rag-platform/src/pages/questionAnswer/form/Step1Basic.backup.tsx
  62. 0 157
      jk-rag-platform/src/pages/questionAnswer/form/VipSelector.less
  63. 157 0
      jk-rag-platform/src/pages/questionAnswer/form/VipSelector.scss
  64. 52 52
      jk-rag-platform/src/pages/questionAnswer/form/style.scss
  65. 3 3
      jk-rag-platform/src/pages/questionAnswer/info/index.tsx
  66. 20 20
      jk-rag-platform/src/pages/questionAnswer/info/style.scss
  67. 0 679
      jk-rag-platform/src/pages/questionAnswer/list/index.tsx.bak
  68. 0 221
      jk-rag-platform/src/pages/questionAnswer/list/style.less.bak
  69. 1 1
      jk-rag-platform/src/pages/questionAnswer/list/style.scss
  70. 0 0
      jk-rag-platform/src/pages/system/apiKey/style.scss
  71. 0 188
      jk-rag-platform/src/pages/system/audit/components/InfoModal.tsx.bak
  72. 0 1602
      jk-rag-platform/src/pages/system/audit/components/PreviewModal.tsx.bak
  73. 0 193
      jk-rag-platform/src/pages/system/audit/components/auditHistory.tsx.bak
  74. 24 24
      jk-rag-platform/src/pages/system/audit/components/style.scss
  75. 0 228
      jk-rag-platform/src/pages/system/audit/index.tsx.bak
  76. 0 0
      jk-rag-platform/src/pages/system/audit/style.scss
  77. 0 130
      jk-rag-platform/src/pages/system/contentManagement/index.tsx.bak
  78. 0 0
      jk-rag-platform/src/pages/system/contentManagement/style.scss
  79. 0 142
      jk-rag-platform/src/pages/system/usageStatistics/index.tsx.bak
  80. 0 0
      jk-rag-platform/src/pages/system/usageStatistics/style.scss
  81. 0 0
      jk-rag-platform/src/pages/universalChat/styles/index.scss
  82. 0 198
      jk-rag-platform/src/router.tsx.bak
  83. 139 181
      jk-rag-platform/src/styles/README.md
  84. 168 168
      jk-rag-platform/src/styles/global.scss
  85. 0 427
      jk-rag-platform/src/styles/variables.less
  86. 431 0
      jk-rag-platform/src/styles/variables.scss
  87. 3 4
      jk-rag-platform/vite.config.ts

+ 1 - 0
.gitignore

@@ -19,3 +19,4 @@ env/.env.development
 
 .cursor
 .history
+.claude

+ 55 - 1
jk-rag-platform/.claude/settings.local.json

@@ -2,7 +2,61 @@
   "permissions": {
     "allow": [
       "Bash(ls -la /Users/misasagi/Git/jkec-xiaozhi-v2/jk-rag-platform/*.md)",
-      "Bash(curl -s http://localhost:5173)"
+      "Bash(curl -s http://localhost:5173)",
+      "Bash(npm install:*)",
+      "Bash(cd:*)",
+      "Bash(sed -i '' 's/@primary-color:/\\\\$primary-color:/g; s/@primary-light:/\\\\$primary-light:/g; s/@primary-dark:/\\\\$primary-dark:/g; s/@primary-lightest:/\\\\$primary-lightest:/g; s/@primary-gradient-start:/\\\\$primary-gradient-start:/g; s/@primary-gradient-end:/\\\\$primary-gradient-end:/g' variables.scss)",
+      "Bash(sed -i '' 's/@success-color:/\\\\$success-color:/g; s/@success-light:/\\\\$success-light:/g; s/@success-dark:/\\\\$success-dark:/g' variables.scss)",
+      "Bash(sed -i '' 's/@warning-color:/\\\\$warning-color:/g; s/@warning-light:/\\\\$warning-light:/g; s/@warning-dark:/\\\\$warning-dark:/g' variables.scss)",
+      "Bash(sed -i '' 's/@error-color:/\\\\$error-color:/g; s/@error-light:/\\\\$error-light:/g; s/@error-dark:/\\\\$error-dark:/g' variables.scss)",
+      "Bash(sed -i '' 's/@info-color:/\\\\$info-color:/g; s/@info-light:/\\\\$info-light:/g; s/@info-dark:/\\\\$info-dark:/g' variables.scss)",
+      "Bash(sed -i '' 's/@chat-primary-start:/\\\\$chat-primary-start:/g; s/@chat-primary-end:/\\\\$chat-primary-end:/g; s/@chat-accent:/\\\\$chat-accent:/g' variables.scss)",
+      "Bash(sed -i '' 's/@text-primary:/\\\\$text-primary:/g; s/@text-secondary:/\\\\$text-secondary:/g; s/@text-hint:/\\\\$text-hint:/g; s/@text-disabled:/\\\\$text-disabled:/g' variables.scss)",
+      "Bash(sed -i '' 's/@bg-primary:/\\\\$bg-primary:/g; s/@bg-secondary:/\\\\$bg-secondary:/g; s/@bg-tertiary:/\\\\$bg-tertiary:/g; s/@bg-hover:/\\\\$bg-hover:/g; s/@bg-active:/\\\\$bg-active:/g' variables.scss)",
+      "Bash(sed -i '' 's/@spacing-1:/\\\\$spacing-1:/g; s/@spacing-2:/\\\\$spacing-2:/g; s/@spacing-3:/\\\\$spacing-3:/g; s/@spacing-4:/\\\\$spacing-4:/g; s/@spacing-5:/\\\\$spacing-5:/g; s/@spacing-6:/\\\\$spacing-6:/g; s/@spacing-8:/\\\\$spacing-8:/g; s/@spacing-10:/\\\\$spacing-10:/g; s/@spacing-12:/\\\\$spacing-12:/g; s/@spacing-16:/\\\\$spacing-16:/g' variables.scss)",
+      "Bash(sed -i '' 's/@spacing-xs:/\\\\$spacing-xs:/g; s/@spacing-sm:/\\\\$spacing-sm:/g; s/@spacing-md:/\\\\$spacing-md:/g; s/@spacing-lg:/\\\\$spacing-lg:/g; s/@spacing-xl:/\\\\$spacing-xl:/g; s/@spacing-2xl:/\\\\$spacing-2xl:/g' variables.scss)",
+      "Bash(sed -i '' 's/@radius-sm:/\\\\$radius-sm:/g; s/@radius-md:/\\\\$radius-md:/g; s/@radius-lg:/\\\\$radius-lg:/g; s/@radius-xl:/\\\\$radius-xl:/g; s/@radius-2xl:/\\\\$radius-2xl:/g; s/@radius-full:/\\\\$radius-full:/g' variables.scss)",
+      "Bash(sed -i '' 's/@border-radius-sm:/\\\\$border-radius-sm:/g; s/@border-radius-md:/\\\\$border-radius-md:/g; s/@border-radius-lg:/\\\\$border-radius-lg:/g; s/@border-radius-xl:/\\\\$border-radius-xl:/g; s/@border-radius-2xl:/\\\\$border-radius-2xl:/g; s/@border-radius-full:/\\\\$border-radius-full:/g' variables.scss)",
+      "Bash(sed -i '' 's/@shadow-sm:/\\\\$shadow-sm:/g; s/@shadow-md:/\\\\$shadow-md:/g; s/@shadow-lg:/\\\\$shadow-lg:/g; s/@shadow-xl:/\\\\$shadow-xl:/g; s/@shadow-glow:/\\\\$shadow-glow:/g; s/@shadow-inner:/\\\\$shadow-inner:/g' variables.scss)",
+      "Bash(sed -i '' 's/@font-xs:/\\\\$font-xs:/g; s/@font-sm:/\\\\$font-sm:/g; s/@font-base:/\\\\$font-base:/g; s/@font-md:/\\\\$font-md:/g; s/@font-lg:/\\\\$font-lg:/g; s/@font-xl:/\\\\$font-xl:/g; s/@font-2xl:/\\\\$font-2xl:/g; s/@font-3xl:/\\\\$font-3xl:/g; s/@font-4xl:/\\\\$font-4xl:/g' variables.scss)",
+      "Bash(sed -i '' 's/@font-weight-normal:/\\\\$font-weight-normal:/g; s/@font-weight-medium:/\\\\$font-weight-medium:/g; s/@font-weight-semibold:/\\\\$font-weight-semibold:/g; s/@font-weight-bold:/\\\\$font-weight-bold:/g' variables.scss)",
+      "Bash(sed -i '' 's/@line-height-tight:/\\\\$line-height-tight:/g; s/@line-height-base:/\\\\$line-height-base:/g; s/@line-height-relaxed:/\\\\$line-height-relaxed:/g; s/@line-height-loose:/\\\\$line-height-loose:/g' variables.scss)",
+      "Bash(sed -i '' 's/@font-family-base:/\\\\$font-family-base:/g; s/@font-family-mono:/\\\\$font-family-mono:/g' variables.scss)",
+      "Bash(sed -i '' 's/@animation-fast:/\\\\$animation-fast:/g; s/@animation-base:/\\\\$animation-base:/g; s/@animation-slow:/\\\\$animation-slow:/g' variables.scss)",
+      "Bash(sed -i '' 's/@easing-default:/\\\\$easing-default:/g; s/@easing-in:/\\\\$easing-in:/g; s/@easing-out:/\\\\$easing-out:/g; s/@easing-in-out:/\\\\$easing-in-out:/g' variables.scss)",
+      "Bash(sed -i '' 's/@transition-base:/\\\\$transition-base:/g; s/@transition-fast:/\\\\$transition-fast:/g; s/@transition-slow:/\\\\$transition-slow:/g' variables.scss)",
+      "Bash(sed -i '' 's/@screen-sm:/\\\\$screen-sm:/g; s/@screen-md:/\\\\$screen-md:/g; s/@screen-lg:/\\\\$screen-lg:/g; s/@screen-xl:/\\\\$screen-xl:/g; s/@screen-2xl:/\\\\$screen-2xl:/g' variables.scss)",
+      "Bash(sed -i '' 's/@breakpoint-xs:~/\\\\$breakpoint-xs:~/g; s/@breakpoint-sm:~/\\\\$breakpoint-sm:~/g; s/@breakpoint-md:~/\\\\$breakpoint-md:~/g; s/@breakpoint-lg:~/\\\\$breakpoint-lg:~/g; s/@breakpoint-xl:~/\\\\$breakpoint-xl:~/g' variables.scss)",
+      "Bash(sed -i '' 's/@header-height:/\\\\$header-height:/g; s/@sidebar-width:/\\\\$sidebar-width:/g; s/@sidebar-collapsed-width:/\\\\$sidebar-collapsed-width:/g; s/@sidebar-width-md:/\\\\$sidebar-width-md:/g; s/@logo-size:/\\\\$logo-size:/g' variables.scss)",
+      "Bash(sed -i '' 's/@search-height:/\\\\$search-height:/g; s/@breadcrumb-height:/\\\\$breadcrumb-height:/g' variables.scss)",
+      "Bash(sed -i '' 's/@card-padding:/\\\\$card-padding:/g; s/@card-radius:/\\\\$card-radius:/g; s/@card-icon-size:/\\\\$card-icon-size:/g; s/@card-btn-size:/\\\\$card-btn-size:/g; s/@card-play-btn-size:/\\\\$card-play-btn-size:/g' variables.scss)",
+      "Bash(sed -i '' 's/@z-index-base:/\\\\$z-index-base:/g; s/@z-index-dropdown:/\\\\$z-index-dropdown:/g; s/@z-index-sticky:/\\\\$z-index-sticky:/g; s/@z-index-fixed:/\\\\$z-index-fixed:/g; s/@z-index-modal-backdrop:/\\\\$z-index-modal-backdrop:/g; s/@z-index-modal:/\\\\$z-index-modal:/g; s/@z-index-popover:/\\\\$z-index-popover:/g; s/@z-index-tooltip:/\\\\$z-index-tooltip:/g; s/@z-index-notification:/\\\\$z-index-notification:/g; s/@z-index-max:/\\\\$z-index-max:/g' variables.scss)",
+      "Bash(sed -i '' 's/@opacity-disabled:/\\\\$opacity-disabled:/g; s/@opacity-disabled-light:/\\\\$opacity-disabled-light:/g; s/@opacity-overlay:/\\\\$opacity-overlay:/g; s/@opacity-hover:/\\\\$opacity-hover:/g; s/@opacity-active:/\\\\$opacity-active:/g' variables.scss)",
+      "Bash(sed -i '' 's/@icon-xs:/\\\\$icon-xs:/g; s/@icon-sm:/\\\\$icon-sm:/g; s/@icon-md:/\\\\$icon-md:/g; s/@icon-lg:/\\\\$icon-lg:/g; s/@icon-xl:/\\\\$icon-xl:/g; s/@icon-2xl:/\\\\$icon-2xl:/g; s/@icon-3xl:/\\\\$icon-3xl:/g' variables.scss)",
+      "Bash(sed -i '' 's/@sidebar-bg:/\\\\$sidebar-bg:/g; s/@sidebar-text-color:/\\\\$sidebar-text-color:/g; s/@sidebar-hover-bg:/\\\\$sidebar-hover-bg:/g; s/@sidebar-active-color:/\\\\$sidebar-active-color:/g' variables.scss)",
+      "Bash(sed -i '' 's/@border-color:/\\\\$border-color:/g; s/@text-placeholder:/\\\\$text-placeholder:/g' variables.scss)",
+      "Bash(sed -i '' 's/@primary-color-hover:/\\\\$primary-color-hover:/g; s/@ease-base:/\\\\$ease-base:/g' variables.scss)",
+      "Bash(sed -i '' 's/@icon-bg-blue:/\\\\$icon-bg-blue:/g; s/@icon-bg-indigo:/\\\\$icon-bg-indigo:/g; s/@icon-bg-teal:/\\\\$icon-bg-teal:/g; s/@icon-bg-purple:/\\\\$icon-bg-purple:/g; s/@icon-bg-rose:/\\\\$icon-bg-rose:/g; s/@icon-bg-cyan:/\\\\$icon-bg-cyan:/g; s/@icon-bg-amber:/\\\\$icon-bg-amber:/g' variables.scss)",
+      "Bash(sed -i '' 's/@tag-bg-slate:/\\\\$tag-bg-slate:/g; s/@tag-text-slate:/\\\\$tag-text-slate:/g; s/@tag-bg-blue:/\\\\$tag-bg-blue:/g; s/@tag-text-blue:/\\\\$tag-text-blue:/g; s/@tag-bg-indigo:/\\\\$tag-bg-indigo:/g; s/@tag-text-indigo:/\\\\$tag-text-indigo:/g; s/@tag-bg-teal:/\\\\$tag-bg-teal:/g; s/@tag-text-teal:/\\\\$tag-text-teal:/g; s/@tag-bg-purple:/\\\\$tag-bg-purple:/g; s/@tag-text-purple:/\\\\$tag-text-purple:/g; s/@tag-bg-rose:/\\\\$tag-bg-rose:/g; s/@tag-text-rose:/\\\\$tag-text-rose:/g; s/@tag-bg-cyan:/\\\\$tag-bg-cyan:/g; s/@tag-text-cyan:/\\\\$tag-text-cyan:/g; s/@tag-bg-amber:/\\\\$tag-bg-amber:/g; s/@tag-text-amber:/\\\\$tag-text-amber:/g' variables.scss)",
+      "Bash(sed -i '' 's/@font-size-base:/\\\\$font-size-base:/g; s/@font-size-xs:/\\\\$font-size-xs:/g; s/@font-size-sm:/\\\\$font-size-sm:/g; s/@font-size-md:/\\\\$font-size-md:/g; s/@font-size-lg:/\\\\$font-size-lg:/g; s/@font-size-xl:/\\\\$font-size-xl:/g' variables.scss)",
+      "Bash(sed -i '' 's/@border-radius-base:/\\\\$border-radius-base:/g' variables.scss)",
+      "Bash(sed -i '' 's/@theme-text-color:/\\\\$theme-text-color:/g' variables.scss)",
+      "Bash(sed -i '' 's/@text-primary/$text-primary/g; s/@success-color/$success-color/g; s/@warning-color/$warning-color/g; s/@error-color/$error-color/g; s/@font-4xl/$font-4xl/g' global.scss)",
+      "Bash(sed -i '' 's/@font-3xl/$font-3xl/g; s/@font-2xl/$font-2xl/g; s/@font-lg/$font-lg/g; s/@font-base/$font-base/g' global.scss)",
+      "Bash(sed -i '' 's/@line-height-tight/$line-height-tight/g; s/@line-height-base/$line-height-base/g; s/@text-secondary/$text-secondary/g' global.scss)",
+      "Bash(sed -i '' 's/@bg-tertiary/$bg-tertiary/g; s/@text-disabled/$text-disabled/g' global.scss)",
+      "Bash(sed -i '' 's/@spacing-1/$spacing-1/g; s/@spacing-6/$spacing-6/g; s/@font-sm/$font-sm/g; s/@font-weight-bold/$font-weight-bold/g' global.scss)",
+      "Bash(sed -i '' 's/@spacing-lg/$spacing-lg/g; s/@icon-xl/$icon-xl/g; s/@shadow-md/$shadow-md/g' global.scss)",
+      "Bash(sed -i '' 's/@icon-lg/$icon-lg/g; s/@spacing-8/$spacing-8/g; s/@radius-xl/$radius-xl/g; s/@transition-base/$transition-base/g' global.scss)",
+      "Bash(sed -i '' 's/@search-height/$search-height/g; s/@bg-hover/$bg-hover/g; s/@text-primary/$text-primary/g' global.scss)",
+      "Bash(sed -i '' 's/@icon-bg-blue/$icon-bg-blue/g; s/@font-2xl/$font-2xl/g; s/@spacing-lg/$spacing-lg/g' global.scss)",
+      "Bash(sed -i '' 's/@shadow-sm/$shadow-sm/g; s/@border-base/$border-base/g; s/@bg-secondary/$bg-secondary/g' global.scss)",
+      "Bash(sed -i '' 's/@radius-lg/$radius-lg/g; s/@spacing-4/$spacing-4/g' global.scss)",
+      "Bash(sed -i '' 's/@header-height/$header-height/g; s/@screen-xl/$screen-xl/g; s/@screen-lg/$screen-lg/g' global.scss)",
+      "Bash(sed -i '' 's/@screen-md/$screen-md/g; s/@spacing-10/$spacing-10/g' global.scss)",
+      "Bash(sed -i '' 's/@primary-light/$primary-light/g; s/@primary-dark/$primary-dark/g' global.scss)",
+      "Bash(git commit:*)",
+      "Bash(git add:*)"
     ]
   }
 }

+ 1 - 0
jk-rag-platform/package.json

@@ -48,6 +48,7 @@
     "@vitejs/plugin-react": "^4.3.0",
     "autoprefixer": "^10.4.21",
     "less": "^4.2.0",
+    "sass": "^1.98.0",
     "typescript": "^5.7.0",
     "vite": "^7.1.11"
   }

+ 1 - 1
jk-rag-platform/src/components/404/style.less → jk-rag-platform/src/components/404/style.scss

@@ -17,7 +17,7 @@
     &-text {
         font-size: 16px;
         font-weight: bold;
-        color: @primary-color;
+        color: $primary-color;
         margin-bottom: 20px;
     }
 }

+ 0 - 618
jk-rag-platform/src/components/chat/index.tsx.bak

@@ -1,618 +0,0 @@
-import React, { useEffect, useRef, useState } from 'react';
-import { Button, GetProp, GetRef, Popover, Space, Spin, Tooltip, message } from 'antd';
-import {
-    CloseOutlined,
-    CloudUploadOutlined,
-    CommentOutlined,
-    OpenAIFilled,
-    PlusOutlined,
-    RedoOutlined,
-    UserOutlined,
-    PaperClipOutlined
-} from '@ant-design/icons';
-import {
-    Attachments,
-    type AttachmentsProps,
-    Bubble,
-    Conversations,
-    Sender,
-    Suggestion,
-    useXAgent,
-    useXChat,
-} from '@ant-design/x';
-import Welcome from './welcome';
-import type { Conversation } from '@ant-design/x/es/conversations';
-import { createStyles } from 'antd-style';
-import dayjs from 'dayjs';
-import ReactMarkdown from 'react-markdown';
-import avatarSrc from '@/assets/public/logo.png';
-
-const useWorkareaStyle = createStyles(({ token, css }) => {
-    return {
-        copilotWrapper: css`
-      height: 100%;
-      display: flex;
-    `,
-        workarea: css`
-      flex: 1;
-      background: ${token.colorBgLayout};
-      display: flex;
-      flex-direction: column;
-    `,
-        workareaHeader: css`
-      box-sizing: border-box;
-      height: 52px;
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-      padding: 0 48px 0 28px;
-      border-bottom: 1px solid ${token.colorBorder};
-    `,
-        headerTitle: css`
-      font-weight: 600;
-      font-size: 15px;
-      color: ${token.colorText};
-      display: flex;
-      align-items: center;
-      gap: 8px;
-    `,
-        headerButton: css`
-      background-image: linear-gradient(78deg, #8054f2 7%, #3895da 95%);
-      border-radius: 12px;
-      height: 24px;
-      width: 93px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      color: #fff;
-      cursor: pointer;
-      font-size: 12px;
-      font-weight: 600;
-      transition: all 0.3s;
-      &:hover {
-        opacity: 0.8;
-      }
-    `,
-        workareaBody: css`
-      flex: 1;
-      padding: 16px;
-      background: ${token.colorBgContainer};
-      border-radius: 16px;
-      min-height: 0;
-    `,
-        bodyContent: css`
-      overflow: auto;
-      height: 100%;
-      padding-right: 10px;
-    `,
-        bodyText: css`
-      color: ${token.colorText};
-      padding: 8px;
-    `,
-    };
-});
-// createStyles 返回一个 hook,使用 design token 生成样式对象
-const useCopilotStyle = createStyles(({ token, css }) => {
-    return {
-        copilotChat: css`
-      display: flex;
-      flex-direction: column;
-      background: ${token.colorBgContainer};
-      color: ${token.colorText};
-    `,
-        // chatHeader 样式
-        chatHeader: css`
-      height: 52px;
-      box-sizing: border-box;
-      border-bottom: 1px solid ${token.colorBorder};
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-      padding: 0 10px 0 16px;
-    `,
-        headerTitle: css`
-      font-weight: 600;
-      font-size: 15px;
-    `,
-        headerButton: css`
-      font-size: 18px;
-    `,
-        conversations: css`
-      width: 300px;
-      .ant-conversations-list {
-        padding-inline-start: 0;
-      }
-    `,
-        // chatList 样式
-        chatList: css`
-      overflow: auto;
-      padding-block: 16px;
-      flex: 1;
-    `,
-        chatWelcome: css`
-      margin-inline: 16px;
-      padding: 12px 16px;
-      border-radius: 2px 12px 12px 12px;
-      background: ${token.colorBgTextHover};
-      margin-bottom: 16px;
-    `,
-        loadingMessage: css`
-      background-image: linear-gradient(90deg, #ff6b23 0%, #af3cb8 31%, #53b6ff 89%);
-      background-size: 100% 2px;
-      background-repeat: no-repeat;
-      background-position: bottom;
-    `,
-        // chatSend 样式
-        chatSend: css`
-      padding: 12px;
-    `,
-        sendAction: css`
-      display: flex;
-      align-items: center;
-      margin-bottom: 12px;
-      gap: 8px;
-    `,
-        speechButton: css`
-      font-size: 18px;
-      color: ${token.colorText} !important;
-    `,
-    };
-});
-// 消息气泡的数据类型:
-// - role: 消息的角色,通常为 'user' 或 'assistant'
-// - content: 文本内容(可能包含 markdown)
-type BubbleDataType = {
-    role: string;
-    content: string;
-};
-
-// MOCK_SESSION_LIST: 用于展示会话历史(仅示例/初始占位)
-const MOCK_SESSION_LIST = [
-    {
-        key: '5',
-        label: 'New session',
-        group: 'Today',
-    },
-    {
-        key: '4',
-        label: 'What has Ant Design X upgraded?',
-        group: 'Today',
-    },
-    {
-        key: '3',
-        label: 'New AGI Hybrid Interface',
-        group: 'Today',
-    },
-    {
-        key: '2',
-        label: 'How to quickly install and import components?',
-        group: 'Yesterday',
-    },
-    {
-        key: '1',
-        label: 'What is Ant Design X?',
-        group: 'Yesterday',
-    },
-];
-
-// MOCK_SUGGESTIONS: Suggestion 组件的数据来源
-// 用于输入框的快速建议/模板,可包含子菜单和图标
-const MOCK_SUGGESTIONS = [
-    { label: 'Write a report', value: 'report' },
-    { label: 'Draw a picture', value: 'draw' },
-    {
-        label: 'Check some knowledge',
-        value: 'knowledge',
-        icon: <OpenAIFilled />,
-        children: [
-            { label: 'About React', value: 'react' },
-            { label: 'About Ant Design', value: 'antd' },
-        ],
-    },
-];
-
-// 生成中占位文本
-const AGENT_PLACEHOLDER = 'Generating content, please wait...';
-
-// CopilotProps: 组件传入的参数
-// - copilotOpen: 控制 Copilot 是否可见
-// - setCopilotOpen: 切换 Copilot 可见性的回调
-// - appId: 当前应用/会话的上下文 ID,会传给后端
-interface CopilotProps {
-    copilotOpen: boolean;
-    setCopilotOpen: (open: boolean) => void;
-    appId: string,
-    infoDetail: any;
-    saveConfig: () => Promise<boolean>;
-}
-
-const Copilot = (props: CopilotProps) => {
-    const { copilotOpen, setCopilotOpen, infoDetail,saveConfig } = props;
-    const { styles } = useCopilotStyle();
-
-    // Attachments 组件的引用,用于编程式上传文件
-    const attachmentsRef = useRef<GetRef<typeof Attachments>>(null);
-    // 保存当前请求对应的 AbortController,方便取消请求
-    const abortController = useRef<AbortController | null>(null);
-
-    // ==================== State ====================
-
-    const [messageHistory, setMessageHistory] = useState<Record<string, any>>({});
-
-    const [sessionList, setSessionList] = useState<Conversation[]>(MOCK_SESSION_LIST);
-    const [curSession, setCurSession] = useState(sessionList[0].key);
-
-    const [attachmentsOpen, setAttachmentsOpen] = useState(false);
-    const [files, setFiles] = useState<GetProp<AttachmentsProps, 'items'>>([]);
-
-    const [inputValue, setInputValue] = useState('');
-
-    // useXAgent 用于创建一个可复用的请求代理(agent)
-    // 可以在这里统一传入 baseURL、headers(比如 Authorization: Bearer <token>)
-    // 例如:useXAgent({ baseURL, headers: { Authorization: `Bearer ${token}` } })
-    const [agent] = useXAgent<BubbleDataType>({
-        // baseURL: props.appId?'/api/deepseek/api/chat':'/api/deepseek/api/preChat',
-        baseURL: '/api/deepseek/api/preChat',
-    });
-
-    // loading 表示 agent 是否正在进行请求
-    const loading = agent.isRequesting();
-
-    // useXChat 管理流式消息和请求生命周期:
-    // - messages: 当前要渲染的消息数组
-    // - onRequest: 发起一次对话请求的函数(会返回/管理流式更新)
-    // - setMessages: 可手动替换消息数组(比如清空或恢复历史)
-    const { messages, onRequest, setMessages } = useXChat({
-        agent,
-        // requestFallback:当请求失败或被取消时,提供回退消息
-        requestFallback: (_, { error }) => {
-            if (error.name === 'AbortError') {
-                return {
-                    content: 'Request is aborted',
-                    role: 'assistant',
-                };
-            }
-            return {
-                content: 'Request failed, please try again!',
-                role: 'assistant',
-            };
-        },
-        // transformMessage:将后端返回的分片/原始格式转换为 UI 可渲染的消息对象
-        transformMessage: (info) => {
-            // console.log(info, '回复消息');
-
-            const { originMessage, chunk } = info || {};
-
-            let currentContent = '';
-
-            try {
-                if (chunk?.data) {
-                    const message = JSON.parse(chunk?.data);
-                    // message.event 用于判断是否结束分片
-                    if (message.event !== 'finish') {
-                        currentContent = message?.data || '';
-                    }
-                }
-            } catch (error) {
-                console.error(error);
-            }
-
-            const content = `${originMessage?.content || ''}${currentContent}`;
-
-            return {
-                content: content,
-                role: 'assistant',
-            };
-
-        },
-        // expose 内部的 AbortController,方便外部取消请求
-        resolveAbortController: (controller) => {
-            abortController.current = controller;
-        },
-    });
-
-    // ==================== Event ====================
-
-    // 用户提交消息:把消息内容传给 onRequest 发起对话请求
-    // 可在这里传入 headers(如 token)作为单次请求的鉴权方式
-    const handleUserSubmit = (val: string,infoDetail?:any) => {
-        // console.log('User submitted:', val);
-        // if(props.appId){
-        //     onRequest({
-        //         message: { content: val, role: 'user' },
-        //         appId: props.appId,
-        //         // 可选扩展字段示例:
-        //         // headers: { Authorization: `Bearer ${token}` },
-        //         request_id: undefined,
-        //         returnType: undefined,
-        //         knowledge_ids: undefined,
-        //         document_ids: undefined
-        //     });
-        // }else{
-            onRequest({
-                message: { content: val, role: 'user' },
-                // appId: props.appId,
-                // 可选扩展字段示例:
-                // headers: { Authorization: `Bearer ${token}` },
-                jkApplicationParams:infoDetail,
-                request_id: undefined,
-                returnType: undefined,
-                knowledge_ids: undefined,
-                document_ids: undefined
-            });
-        // }
-
-        // 如果当前会话为 New session,则用用户第一条消息作为会话标题示例
-        if (sessionList.find((i) => i.key === curSession)?.label === 'New session') {
-            setSessionList(
-                sessionList.map((i) => (i.key !== curSession ? i : { ...i, label: val?.slice(0, 20) })),
-            );
-        }
-    };
-
-    // 处理粘贴文件(从剪贴板粘贴文件)
-    // 将 FileList 转为数组再上传,兼容不同的 TS 设置
-    const onPasteFile = (_: File, files: FileList) => {
-        Array.from(files).forEach((file) => {
-            attachmentsRef.current?.upload(file as File);
-        });
-        setAttachmentsOpen(true);
-    };
-
-    // ==================== Nodes ====================
-    const chatHeader = (
-        <div className={styles.chatHeader}>
-            <div className={styles.headerTitle}>✨ AI Copilot</div>
-            <Space size={0}>
-                <Button
-                    type="text"
-                    icon={<PlusOutlined />}
-                    onClick={() => {
-                        if (agent.isRequesting()) {
-                            message.error(
-                                'Message is Requesting, you can create a new conversation after request done or abort it right now...',
-                            );
-                            return;
-                        }
-
-                        if (messages?.length) {
-                            const timeNow = dayjs().valueOf().toString();
-                            abortController.current?.abort();
-                            // The abort execution will trigger an asynchronous requestFallback, which may lead to timing issues.
-                            // In future versions, the sessionId capability will be added to resolve this problem.
-                            setTimeout(() => {
-                                setSessionList([
-                                    { key: timeNow, label: 'New session', group: 'Today' },
-                                    ...sessionList,
-                                ]);
-                                setCurSession(timeNow);
-                                setMessages([]);
-                            }, 100);
-                        } else {
-                            message.error('It is now a new conversation.');
-                        }
-                    }}
-                    className={styles.headerButton}
-                />
-                <Popover
-                    placement="bottom"
-                    styles={{ body: { padding: 0, maxHeight: 600 } }}
-                    content={
-                        <Conversations
-                            items={sessionList?.map((i) =>
-                                i.key === curSession ? { ...i, label: `[current] ${i.label}` } : i,
-                            )}
-                            activeKey={curSession}
-                            groupable
-                            onActiveChange={async (val) => {
-                                abortController.current?.abort();
-                                // The abort execution will trigger an asynchronous requestFallback, which may lead to timing issues.
-                                // In future versions, the sessionId capability will be added to resolve this problem.
-                                setTimeout(() => {
-                                    setCurSession(val);
-                                    setMessages(messageHistory?.[val] || []);
-                                }, 100);
-                            }}
-                            styles={{ item: { padding: '0 8px' } }}
-                            className={styles.conversations}
-                        />
-                    }
-                >
-                    <Button type="text" icon={<CommentOutlined />} className={styles.headerButton} />
-                </Popover>
-                <Button
-                    type="text"
-                    icon={<CloseOutlined />}
-                    onClick={() => setCopilotOpen(false)}
-                    className={styles.headerButton}
-                />
-            </Space>
-        </div>
-    );
-    const chatList = (
-        <div className={styles.chatList}>
-            {messages?.length ? (
-                /** 消息列表 */
-                <Bubble.List
-                    style={{ height: '100%', paddingInline: 16 }}
-                    items={messages?.map((i) => ({
-                        ...i.message,
-                        classNames: {
-                            content: i.status === 'loading' ? styles.loadingMessage : '',
-                        },
-                        typing: i.status === 'loading' ? { step: 5, interval: 20, suffix: <>💗</> } : false,
-                        content: <ReactMarkdown>{i.message.content}</ReactMarkdown>,
-                        avatar: i.message.role !== 'user' ? <img style={{ width: 30, height: 30 }} src={avatarSrc} /> : <UserOutlined style={{ fontSize: 24, color: '#1890ff' }} />,
-                    }))}
-                    roles={{
-                        assistant: {
-                            placement: 'start',
-                            // footer: (
-                            //     <div style={{ display: 'flex' }}>
-                            //         <Button type="text" size="small" icon={<ReloadOutlined />} />
-                            //         <Button type="text" size="small" icon={<CopyOutlined />} />
-                            //         <Button type="text" size="small" icon={<LikeOutlined />} />
-                            //         <Button type="text" size="small" icon={<DislikeOutlined />} />
-                            //     </div>
-                            // ),
-                            loadingRender: () => (
-                                <Space>
-                                    <Spin size="small" />
-                                    {AGENT_PLACEHOLDER}
-                                </Space>
-                            ),
-                        },
-                        user: { placement: 'end' },
-                    }}
-                />
-            ) : (
-                /** 没有消息时的 welcome */
-                // <Welcome
-                //     variant="borderless"
-                //     title="预览调试"
-                //     description="请输入问题进行预览调试"
-                //     className={styles.chatWelcome}
-                // />
-                <Welcome infoDetail={infoDetail} saveConfig={saveConfig} handleUserSubmit={handleUserSubmit}></Welcome>
-            )}
-        </div>
-    );
-    const sendHeader = (
-        <Sender.Header
-            title="Upload File"
-            styles={{ content: { padding: 0 } }}
-            open={attachmentsOpen}
-            onOpenChange={setAttachmentsOpen}
-            forceRender
-        >
-            <Attachments
-                ref={attachmentsRef}
-                beforeUpload={() => false}
-                items={files}
-                onChange={({ fileList }) => setFiles(fileList)}
-                placeholder={(type) =>
-                    type === 'drop'
-                        ? { title: 'Drop file here' }
-                        : {
-                            icon: <CloudUploadOutlined />,
-                            title: 'Upload files',
-                            description: 'Click or drag files to this area to upload',
-                        }
-                }
-            />
-        </Sender.Header>
-    );
-    const chatSender = (
-        <div className={styles.chatSend}>
-            {/** 输入框 */}
-            <Suggestion items={MOCK_SUGGESTIONS} onSelect={(itemVal) => setInputValue(`[${itemVal}]:`)}>
-                {({ onTrigger, onKeyDown }) => (
-                    <Sender
-                        loading={loading}
-                        value={inputValue}
-                        onChange={(v) => {
-                            onTrigger(v === '/');
-                            setInputValue(v);
-                        }}
-                        onSubmit={ async() => {
-                             const result = await saveConfig();
-                            if(result){
-                                handleUserSubmit(inputValue,result);
-                                setInputValue('');
-                            }
-                        }}
-                        onCancel={() => {
-                            abortController.current?.abort();
-                        }}
-                        allowSpeech={true}
-                        placeholder="请输入"
-                        onKeyDown={onKeyDown}
-                        header={sendHeader}
-                        prefix={
-                            <img style={{ width: 30, height: 30 }} src={avatarSrc} />
-                            // <Button
-                            //     type="text"
-                            //     icon={<PaperClipOutlined style={{ fontSize: 18 }} />}
-                            //     onClick={() => setAttachmentsOpen(!attachmentsOpen)}
-                            // />
-                        }
-                        onPasteFile={onPasteFile}
-                        actions={(_, info) => {
-                            const { SendButton, LoadingButton, SpeechButton, ClearButton } = info.components;
-                            return (
-                                <div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
-                                    <SpeechButton className={styles.speechButton} />
-                                    <Tooltip title={loading ? '停止' : '发送'}>
-                                        {loading ? <LoadingButton type="default" /> : <SendButton type="primary" />}
-                                    </Tooltip>
-                                    <Tooltip title="重新开始">
-                                        <Button
-                                            type="primary"
-                                            shape="circle"
-                                            icon={<RedoOutlined />}
-                                            onClick={() => {
-                                                const timeNow = dayjs().valueOf().toString();
-                                                abortController.current?.abort();
-
-                                                // 确保异步操作完成后清空消息
-                                                setTimeout(() => {
-                                                    setSessionList([
-                                                        { key: timeNow, label: 'New session', group: 'Today' },
-                                                        ...sessionList,
-                                                    ]);
-                                                    setCurSession(timeNow);
-                                                    setMessages([]);
-                                                }, 100);
-                                            }}
-                                        />
-                                    </Tooltip>
-                                </div>
-                            );
-                        }}
-                    />
-                )}
-            </Suggestion>
-        </div>
-    );
-
-    useEffect(() => {
-        // history mock
-        if (messages?.length) {
-            setMessageHistory((prev) => ({
-                ...prev,
-                [curSession]: messages,
-            }));
-        }
-    }, [messages]);
-
-    return (
-        <div className={styles.copilotChat} style={{ width: copilotOpen ? '100%' : 0 }}>
-            {/** 对话区 - header */}
-            {/* {chatHeader} */}
-            {/** 对话区 - 消息列表 */}
-            {chatList}
-            {/** 对话区 - 输入框 */}
-            {chatSender}
-        </div>
-    );
-};
-
-
-const Chat: React.FC<{ appId: string,infoDetail?: any,saveConfig: () => Promise<any> }> = (props) => {
-    const { styles: workareaStyles } = useWorkareaStyle();
-    const [copilotOpen, setCopilotOpen] = useState(true);
-    const { infoDetail } = props;  
-    return (
-        <div className={workareaStyles.copilotWrapper}>
-            <Copilot
-                copilotOpen={copilotOpen}
-                infoDetail={infoDetail}
-                setCopilotOpen={setCopilotOpen}
-                appId={props.appId}
-                saveConfig={props.saveConfig}
-            />
-        </div>
-    );
-};
-
-export default Chat;

+ 0 - 0
jk-rag-platform/src/components/chat/welcome.less → jk-rag-platform/src/components/chat/welcome.scss


+ 0 - 84
jk-rag-platform/src/components/chat/welcome.tsx.bak

@@ -1,84 +0,0 @@
-import { useEffect, useState } from 'react';
-import './welcome.less';
-import avatarSrc from '@/assets/public/logo.png';
-import logoSrc from '@/assets/public/logo.png';
-import { nanoid } from "nanoid";
-import store from './store';
-import { RightOutlined } from "@ant-design/icons";
-
-interface Props {
-    infoDetail: {
-        questionlist: string[],
-        detail: {
-            name: string,
-            desc: string,
-        },
-    };
-    handleUserSubmit: (input: string,infoDetail: any) => void;
-    saveConfig: () => Promise<any>
-}
-
-const Welcome: React.FC<Props> = (props) => {
-    const { infoDetail: { detail, questionlist }, handleUserSubmit, saveConfig } = props;
-    const { state, fetchPresetsApi } = store();
-    useEffect(() => {
-
-    }, []);
-
-
-    return (
-        <div className='welcome-container'>
-            <div className='title'>
-                <div style={{ display: 'flex', justifyContent: 'center' }}>
-                    <img style={{ width: 50, height: 50 }} src={avatarSrc} />
-                </div>
-                <h3 style={{ textAlign: 'center' }}>
-                    {/* {getAppName()} */}
-                    {detail?.name}
-                </h3>
-                <p style={{ textAlign: 'center' }} className='px-10'>
-                    {/* {getDesc()} */}
-                    {detail?.desc}
-                </p>
-
-                <div className='flex items-center justify-center flex-col mt-18 w-full px-4'>
-                    <p className='pb-[20px]'>我猜您可能想问:</p>
-                    {
-                        questionlist?.map((item: any, index: any) => {
-                            return (
-                                <div
-                                    className='w-[300px]'
-                                    style={{
-                                        padding: '10px',
-                                        marginBottom: '10px',
-                                        border: '1px solid #e6e8f1',
-                                        borderRadius: '10px',
-                                        fontSize: '13px',
-                                        display: 'flex',
-                                        justifyContent: 'space-between',
-                                        alignItems: 'center',
-                                        cursor: 'pointer'
-                                    }}
-                                    onClick={async () => {
-                                        const result = await saveConfig();
-                                        if (result) {
-                                            handleUserSubmit(item.question, result);
-                                        }
-                                    }}
-                                    key={item.id}
-                                >
-                                    <div>
-                                        {item.question}
-                                    </div>
-                                    <RightOutlined />
-                                </div>
-                            )
-                        })
-                    }
-                </div>
-            </div>
-        </div>
-    )
-}
-
-export default Welcome;

+ 0 - 415
jk-rag-platform/src/components/common/AppCard/index.less.rewrite-backup

@@ -1,415 +0,0 @@
-// 导入全局样式变量
-@import '@/styles/variables.less';
-
-// ===== AppCard 应用卡片样式 =====
-// 使用全局变量,符合设计规范 v3.0
-
-.app-card {
-    background: #FFFFFF;
-    border: 1px solid @border-base;
-    border-radius: @radius-xl;
-    padding: @spacing-4;
-    cursor: pointer;
-    transition: @transition-base;
-    position: relative;
-    overflow: hidden;
-    min-height: 260px;
-    display: flex;
-    flex-direction: column;
-
-    &:hover {
-        transform: translateY(-@spacing-1);
-        box-shadow: @shadow-lg;
-        border-color: fade(@primary-color, 30%);
-
-        .card-actions {
-            opacity: 1;
-            transform: translateY(0);
-        }
-
-        .card-hover-actions {
-            opacity: 1;
-            transform: translateY(0);
-        }
-
-        .card-title {
-            color: @primary-color;
-        }
-    }
-}
-
-// 热门标识 - 缩小尺寸,被操作按钮遮盖
-.card-hot-badge {
-    position: absolute;
-    top: @spacing-3;
-    right: @spacing-3;
-    padding: 2px 6px;
-    background: linear-gradient(135deg, #F97316 0%, #EA580C 100%);
-    color: #FFFFFF;
-    border-radius: @radius-full;
-    font-size: 9px;
-    font-weight: @font-weight-bold;
-    text-transform: uppercase;
-    letter-spacing: 0.05em;
-    box-shadow: 0 2px 8px rgba(249, 115, 22, 0.3);
-    z-index: 5;  // 较低层级,被操作按钮完全遮盖
-}
-
-// 状态标签
-.card-status-badge {
-    position: absolute;
-    top: @spacing-4;
-    left: @spacing-4;
-    padding: @spacing-1 @spacing-2;
-    color: #FFFFFF;
-    border-radius: @radius-full;
-    font-size: @font-xs;
-    font-weight: @font-weight-semibold;
-    z-index: 10;
-}
-
-.card-header {
-    display: flex;
-    justify-content: flex-start;  // 左对齐,因为操作按钮已移出
-    align-items: flex-start;
-    margin-bottom: @spacing-3;
-    gap: @spacing-3;
-}
-
-// 创建者信息区域 - 三行文字与图标高度一致(56px)
-.card-creator {
-    display: flex;
-    align-items: center;
-    flex: 1;
-    min-width: 0;
-
-    .card-creator-info {
-        display: flex;
-        flex-direction: column;
-        gap: 0;
-        line-height: 1.2;
-
-        .card-creator-label {
-            font-size: 9px;
-            color: @text-hint;
-            text-transform: uppercase;
-            letter-spacing: 0.05em;
-            font-weight: @font-weight-medium;
-            line-height: 1.2;
-        }
-
-        .card-creator-name {
-            font-size: @font-sm;
-            color: @text-secondary;
-            font-weight: @font-weight-medium;
-            white-space: nowrap;
-            overflow: hidden;
-            text-overflow: ellipsis;
-            line-height: 1.2;
-        }
-
-        // 项目名称 - 单行省略
-        .card-pro-name {
-            font-size: 11px;
-            color: @text-hint;
-            white-space: nowrap;
-            overflow: hidden;
-            text-overflow: ellipsis;
-            line-height: 1.2;
-            max-width: 180px;
-        }
-    }
-}
-
-.card-icon-wrapper {
-    width: 56px;
-    height: 56px;
-    border-radius: @radius-xl;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    padding: 2px;
-    flex-shrink: 0;
-
-    &.bg-blue { background: @icon-bg-blue; }
-    &.bg-indigo { background: @icon-bg-indigo; }
-    &.bg-teal { background: @icon-bg-teal; }
-    &.bg-purple { background: @icon-bg-purple; }
-    &.bg-rose { background: @icon-bg-rose; }
-    &.bg-cyan { background: @icon-bg-cyan; }
-    &.bg-amber { background: @icon-bg-amber; }
-    &.bg-green { background: #F0FDF4; }
-    &.bg-orange { background: #FFF7ED; }
-
-    .card-icon {
-        width: 100%;
-        height: 100%;
-        object-fit: contain;
-    }
-
-    .iconify {
-        font-size: @icon-3xl;
-    }
-}
-
-// 操作按钮 - 悬停显示(在 Hot Tag 上层,完全遮盖)
-.card-actions {
-    position: absolute;
-    top: @spacing-2;  // 与 Hot Tag 相同位置
-    right: @spacing-2;
-    display: flex;
-    gap: @spacing-1;
-    opacity: 0;
-    transform: translateY(0);
-    transition: @transition-base;
-    z-index: 15;  // 高于 Hot Tag 的 z-index: 5
-
-    .card-action-btn {
-        width: 36px;
-        height: 36px;
-        border-radius: @radius-lg;
-        background: @bg-tertiary;
-        border: none;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        color: @text-secondary;
-        cursor: pointer;
-        transition: @transition-base;
-
-        &:hover {
-            background: @border-base;
-            color: @primary-color;
-            transform: scale(1.05);
-        }
-
-        .iconify {
-            font-size: @icon-lg;
-        }
-    }
-}
-
-.card-title {
-    font-size: @font-lg;
-    font-weight: @font-weight-semibold;
-    color: @text-primary;
-    margin: 0;
-    padding: 0;
-    margin-bottom: @spacing-2;
-    transition: @transition-fast;
-    line-height: @line-height-tight;
-
-    .app-card:hover & {
-        color: @primary-color;
-    }
-}
-
-.card-description {
-    font-size: @font-sm;
-    color: @text-secondary;
-    line-height: @line-height-base;
-    margin-bottom: @spacing-3;
-    display: -webkit-box;
-    -webkit-line-clamp: 2;
-    -webkit-box-orient: vertical;
-    overflow: hidden;
-    min-height: 39px;
-}
-
-.card-tags {
-    display: flex;
-    flex-wrap: wrap;
-    gap: @spacing-1;
-    margin-bottom: @spacing-3;
-}
-
-.card-tag {
-    padding: 3px 10px;
-    border-radius: @radius-md;
-    font-size: @font-xs;
-    font-weight: @font-weight-bold;
-    text-transform: uppercase;
-    letter-spacing: 0.05em;
-    white-space: nowrap;
-
-    &.tag-slate { background: @tag-bg-slate; color: @tag-text-slate; }
-    &.tag-blue { background: @tag-bg-blue; color: @tag-text-blue; }
-    &.tag-indigo { background: @tag-bg-indigo; color: @tag-text-indigo; }
-    &.tag-teal { background: @tag-bg-teal; color: @tag-text-teal; }
-    &.tag-purple { background: @tag-bg-purple; color: @tag-text-purple; }
-    &.tag-rose { background: @tag-bg-rose; color: @tag-text-rose; }
-    &.tag-cyan { background: @tag-bg-cyan; color: @tag-text-cyan; }
-    &.tag-amber { background: @tag-bg-amber; color: @tag-text-amber; }
-}
-
-// 统计信息和创建时间
-.card-meta {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    margin-top: 0;
-
-    .card-meta-left {
-        display: flex;
-        gap: @spacing-3;
-    }
-
-    .card-meta-right {
-        margin-left: auto;
-    }
-
-    .card-meta-item {
-        display: flex;
-        align-items: center;
-        gap: @spacing-1;
-        font-size: @font-sm;
-        color: @text-hint;
-
-        .iconify {
-            font-size: @icon-md;
-        }
-    }
-}
-
-// 底部认证信息
-.card-footer-info {
-    display: flex;
-    align-items: center;
-    gap: @spacing-2;
-    flex-wrap: wrap;
-    padding-top: @spacing-3;
-    margin-top: auto;
-}
-
-.card-certification {
-    display: flex;
-    align-items: center;
-    gap: @spacing-1;
-    font-size: @font-xs;
-    color: @text-hint;
-
-    .iconify {
-        font-size: @icon-md;
-        color: @success-color;
-    }
-}
-
-// 部门信息
-.card-department {
-    display: flex;
-    align-items: center;
-    gap: @spacing-1;
-    font-size: @font-xs;
-    color: @text-secondary;
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    max-width: 200px;
-
-    .iconify {
-        font-size: @icon-md;
-    }
-}
-
-// 可见性标签
-.card-visible-tag {
-    font-size: @font-xs;
-    font-weight: @font-weight-semibold;
-    padding: 2px 8px;
-    border-radius: @radius-sm;
-    background: @bg-tertiary;
-}
-
-// 悬停操作按钮层
-.card-hover-actions {
-    position: absolute;
-    bottom: 0;
-    left: 0;
-    right: 0;
-    display: flex;
-    gap: @spacing-2;
-    padding: @spacing-4;
-    background: linear-gradient(to top, rgba(255, 255, 255, 0.98) 0%, rgba(255, 255, 255, 0.95) 100%);
-    border-top: 1px solid @border-light;
-    opacity: 0;
-    transform: translateY(@spacing-2);
-    transition: @transition-base;
-    pointer-events: none;
-
-    .app-card:hover & {
-        opacity: 1;
-        transform: translateY(0);
-        pointer-events: auto;
-    }
-
-    // 更多操作按钮
-    .card-operation-btn {
-        flex: 1;
-        height: 44px;
-        background: @bg-tertiary;
-        border: 1px solid @border-base;
-        border-radius: @radius-lg;
-        font-size: @font-md;
-        font-weight: @font-weight-medium;
-        color: @text-primary;
-        transition: @transition-base;
-
-        &:hover {
-            background: @border-base;
-            border-color: @border-dark;
-            color: @text-primary;
-        }
-
-        .iconify {
-            font-size: @icon-lg;
-        }
-    }
-
-    // 立即使用按钮
-    .card-use-btn {
-        flex: 1;
-        height: 44px;
-        background: linear-gradient(135deg, @primary-color 0%, @primary-light 100%);
-        border: none;
-        border-radius: @radius-lg;
-        font-size: @font-md;
-        font-weight: @font-weight-semibold;
-        color: #FFFFFF;
-        box-shadow: 0 4px 12px fade(@primary-color, 30%);
-        transition: @transition-base;
-
-        &:hover {
-            background: linear-gradient(135deg, @primary-dark 0%, @primary-color 100%);
-            box-shadow: 0 6px 16px fade(@primary-color, 40%);
-            transform: translateY(-@spacing-1);
-        }
-
-        &:active {
-            transform: translateY(0);
-            box-shadow: 0 2px 8px fade(@primary-color, 20%);
-        }
-
-        .iconify {
-            font-size: @icon-lg;
-        }
-    }
-}
-
-// 卡片网格布局
-.app-card-grid {
-    display: grid;
-    grid-template-columns: repeat(4, 1fr);
-    gap: @spacing-6;
-
-    @media (max-width: @screen-xl) {
-        grid-template-columns: repeat(3, 1fr);
-    }
-
-    @media (max-width: @screen-lg) {
-        grid-template-columns: repeat(2, 1fr);
-    }
-
-    @media (max-width: @screen-md) {
-        grid-template-columns: repeat(1, 1fr);
-    }
-}

+ 124 - 124
jk-rag-platform/src/components/common/AppCard/index.less → jk-rag-platform/src/components/common/AppCard/index.scss

@@ -1,16 +1,16 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // ===== AppCard 应用卡片样式 =====
 // 使用全局变量,符合设计规范 v3.0
 
 .app-card {
     background: #FFFFFF;
-    border: 1px solid @border-base;
-    border-radius: @radius-xl;
-    padding: @spacing-4;
+    border: 1px solid $border-base;
+    border-radius: $radius-xl;
+    padding: $spacing-4;
     cursor: pointer;
-    transition: @transition-base;
+    transition: $transition-base;
     position: relative;
     overflow: hidden;
     min-height: 280px;
@@ -18,9 +18,9 @@
     flex-direction: column;
 
     &:hover {
-        transform: translateY(-@spacing-1);
-        box-shadow: @shadow-lg;
-        border-color: fade(@primary-color, 30%);
+        transform: translateY(-$spacing-1);
+        box-shadow: $shadow-lg;
+        border-color: fade($primary-color, 30%);
 
         .card-actions {
             opacity: 1;
@@ -33,7 +33,7 @@
         }
 
         .card-title {
-            color: @primary-color;
+            color: $primary-color;
         }
     }
 }
@@ -41,14 +41,14 @@
 // 热门标识 - 缩小尺寸,被操作按钮遮盖
 .card-hot-badge {
     position: absolute;
-    top: @spacing-3;
-    right: @spacing-3;
+    top: $spacing-3;
+    right: $spacing-3;
     padding: 2px 6px;
     background: linear-gradient(135deg, #F97316 0%, #EA580C 100%);
     color: #FFFFFF;
-    border-radius: @radius-full;
+    border-radius: $radius-full;
     font-size: 9px;
-    font-weight: @font-weight-bold;
+    font-weight: $font-weight-bold;
     text-transform: uppercase;
     letter-spacing: 0.05em;
     box-shadow: 0 2px 8px rgba(249, 115, 22, 0.3);
@@ -58,13 +58,13 @@
 // 状态标签
 .card-status-badge {
     position: absolute;
-    top: @spacing-4;
-    left: @spacing-4;
+    top: $spacing-4;
+    left: $spacing-4;
     padding: 2px 6px;
     color: #FFFFFF;
-    border-radius: @radius-full;
+    border-radius: $radius-full;
     font-size: 9px;
-    font-weight: @font-weight-semibold;
+    font-weight: $font-weight-semibold;
     z-index: 10;
 }
 
@@ -73,13 +73,13 @@
     display: flex;
     justify-content: flex-start;
     align-items: flex-start;
-    margin-bottom: @spacing-3;
-    gap: @spacing-3;
+    margin-bottom: $spacing-3;
+    gap: $spacing-3;
 }
 
 // 创建者信息区域 - 三行文字与图标高度一致(56px)
-// 计算:@font-xs + @font-base + @font-sm = 9px + 13px + 12px = 34px 文字 
-//      + @spacing-1 × 2 = 8px 间距 + 行高余量 ≈ 56px
+// 计算:$font-xs + $font-base + $font-sm = 9px + 13px + 12px = 34px 文字 
+//      + $spacing-1 × 2 = 8px 间距 + 行高余量 ≈ 56px
 .card-creator {
     display: flex;
     align-items: center;
@@ -89,37 +89,37 @@
     .card-creator-info {
         display: flex;
         flex-direction: column;
-        gap: @spacing-1;  // 行间距 4px
-        line-height: @line-height-tight;
+        gap: $spacing-1;  // 行间距 4px
+        line-height: $line-height-tight;
 
         .card-creator-label {
-            font-size: @font-xs;  // 9px
-            color: @text-hint;
+            font-size: $font-xs;  // 9px
+            color: $text-hint;
             text-transform: uppercase;
             letter-spacing: 0.05em;
-            font-weight: @font-weight-medium;
-            line-height: @line-height-tight;
+            font-weight: $font-weight-medium;
+            line-height: $line-height-tight;
         }
 
         .card-creator-name {
-            font-size: @font-base;  // 13px
-            color: @text-secondary;
-            font-weight: @font-weight-semibold;
+            font-size: $font-base;  // 13px
+            color: $text-secondary;
+            font-weight: $font-weight-semibold;
             white-space: nowrap;
             overflow: hidden;
             text-overflow: ellipsis;
-            line-height: @line-height-tight;
+            line-height: $line-height-tight;
         }
 
         // 项目名称 - 单行省略
         .card-pro-name {
-            font-size: @font-sm;  // 12px
-            color: @text-hint;
+            font-size: $font-sm;  // 12px
+            color: $text-hint;
             white-space: nowrap;
             overflow: hidden;
             text-overflow: ellipsis;
             max-width: 180px;
-            line-height: @line-height-tight;
+            line-height: $line-height-tight;
         }
     }
 }
@@ -128,20 +128,20 @@
 .card-icon-wrapper {
     width: 56px;
     height: 56px;
-    border-radius: @radius-xl;
+    border-radius: $radius-xl;
     display: flex;
     align-items: center;
     justify-content: center;
     padding: 2px;
     flex-shrink: 0;
 
-    &.bg-blue { background: @icon-bg-blue; }
-    &.bg-indigo { background: @icon-bg-indigo; }
-    &.bg-teal { background: @icon-bg-teal; }
-    &.bg-purple { background: @icon-bg-purple; }
-    &.bg-rose { background: @icon-bg-rose; }
-    &.bg-cyan { background: @icon-bg-cyan; }
-    &.bg-amber { background: @icon-bg-amber; }
+    &.bg-blue { background: $icon-bg-blue; }
+    &.bg-indigo { background: $icon-bg-indigo; }
+    &.bg-teal { background: $icon-bg-teal; }
+    &.bg-purple { background: $icon-bg-purple; }
+    &.bg-rose { background: $icon-bg-rose; }
+    &.bg-cyan { background: $icon-bg-cyan; }
+    &.bg-amber { background: $icon-bg-amber; }
     &.bg-green { background: #F0FDF4; }
     &.bg-orange { background: #FFF7ED; }
 
@@ -152,69 +152,69 @@
     }
 
     .iconify {
-        font-size: @icon-3xl;
+        font-size: $icon-3xl;
     }
 }
 
 // 操作按钮 - 悬停显示(在 Hot Tag 上层,完全遮盖)
 .card-actions {
     position: absolute;
-    top: @spacing-2;
-    right: @spacing-2;
+    top: $spacing-2;
+    right: $spacing-2;
     display: flex;
-    gap: @spacing-1;
+    gap: $spacing-1;
     opacity: 0;
     transform: translateY(0);
-    transition: @transition-base;
+    transition: $transition-base;
     z-index: 15;
 
     .card-action-btn {
         width: 36px;
         height: 36px;
-        border-radius: @radius-lg;
-        background: @bg-tertiary;
+        border-radius: $radius-lg;
+        background: $bg-tertiary;
         border: none;
         display: flex;
         align-items: center;
         justify-content: center;
-        color: @text-secondary;
+        color: $text-secondary;
         cursor: pointer;
-        transition: @transition-base;
+        transition: $transition-base;
 
         &:hover {
-            background: @border-base;
-            color: @primary-color;
+            background: $border-base;
+            color: $primary-color;
             transform: scale(1.05);
         }
 
         .iconify {
-            font-size: @icon-lg;
+            font-size: $icon-lg;
         }
     }
 }
 
 // 应用名称
 .card-title {
-    font-size: @font-lg;
-    font-weight: @font-weight-semibold;
-    color: @text-primary;
+    font-size: $font-lg;
+    font-weight: $font-weight-semibold;
+    color: $text-primary;
     margin: 0;
     padding: 0;
-    margin-bottom: @spacing-2;
-    transition: @transition-fast;
-    line-height: @line-height-tight;
+    margin-bottom: $spacing-2;
+    transition: $transition-fast;
+    line-height: $line-height-tight;
 
     .app-card:hover & {
-        color: @primary-color;
+        color: $primary-color;
     }
 }
 
 // 应用描述
 .card-description {
-    font-size: @font-sm;
-    color: @text-secondary;
-    line-height: @line-height-base;
-    margin-bottom: @spacing-2;
+    font-size: $font-sm;
+    color: $text-secondary;
+    line-height: $line-height-base;
+    margin-bottom: $spacing-2;
     display: -webkit-box;
     -webkit-line-clamp: 2;
     -webkit-box-orient: vertical;
@@ -226,27 +226,27 @@
 .card-tags {
     display: flex;
     flex-wrap: wrap;
-    gap: @spacing-1;
-    margin-bottom: @spacing-2;
+    gap: $spacing-1;
+    margin-bottom: $spacing-2;
 }
 
 .card-tag {
     padding: 3px 10px;
-    border-radius: @radius-md;
-    font-size: @font-xs;
-    font-weight: @font-weight-bold;
+    border-radius: $radius-md;
+    font-size: $font-xs;
+    font-weight: $font-weight-bold;
     text-transform: uppercase;
     letter-spacing: 0.05em;
     white-space: nowrap;
 
-    &.tag-slate { background: @tag-bg-slate; color: @tag-text-slate; }
-    &.tag-blue { background: @tag-bg-blue; color: @tag-text-blue; }
-    &.tag-indigo { background: @tag-bg-indigo; color: @tag-text-indigo; }
-    &.tag-teal { background: @tag-bg-teal; color: @tag-text-teal; }
-    &.tag-purple { background: @tag-bg-purple; color: @tag-text-purple; }
-    &.tag-rose { background: @tag-bg-rose; color: @tag-text-rose; }
-    &.tag-cyan { background: @tag-bg-cyan; color: @tag-text-cyan; }
-    &.tag-amber { background: @tag-bg-amber; color: @tag-text-amber; }
+    &.tag-slate { background: $tag-bg-slate; color: $tag-text-slate; }
+    &.tag-blue { background: $tag-bg-blue; color: $tag-text-blue; }
+    &.tag-indigo { background: $tag-bg-indigo; color: $tag-text-indigo; }
+    &.tag-teal { background: $tag-bg-teal; color: $tag-text-teal; }
+    &.tag-purple { background: $tag-bg-purple; color: $tag-text-purple; }
+    &.tag-rose { background: $tag-bg-rose; color: $tag-text-rose; }
+    &.tag-cyan { background: $tag-bg-cyan; color: $tag-text-cyan; }
+    &.tag-amber { background: $tag-bg-amber; color: $tag-text-amber; }
 }
 
 // 统计信息
@@ -258,7 +258,7 @@
 
     .card-meta-left {
         display: flex;
-        gap: @spacing-3;
+        gap: $spacing-3;
     }
 
     .card-meta-right {
@@ -268,12 +268,12 @@
     .card-meta-item {
         display: flex;
         align-items: center;
-        gap: @spacing-1;
-        font-size: @font-sm;
-        color: @text-hint;
+        gap: $spacing-1;
+        font-size: $font-sm;
+        color: $text-hint;
 
         .iconify {
-            font-size: @icon-md;
+            font-size: $icon-md;
         }
     }
 }
@@ -282,47 +282,47 @@
 .card-footer-info {
     display: flex;
     align-items: center;
-    gap: @spacing-2;
+    gap: $spacing-2;
     flex-wrap: wrap;
-    padding-top: @spacing-2;
+    padding-top: $spacing-2;
     margin-top: auto;
 }
 
 .card-certification {
     display: flex;
     align-items: center;
-    gap: @spacing-1;
-    font-size: @font-xs;
-    color: @text-hint;
+    gap: $spacing-1;
+    font-size: $font-xs;
+    color: $text-hint;
 
     .iconify {
-        font-size: @icon-md;
-        color: @success-color;
+        font-size: $icon-md;
+        color: $success-color;
     }
 }
 
 .card-department {
     display: flex;
     align-items: center;
-    gap: @spacing-1;
-    font-size: @font-xs;
-    color: @text-secondary;
+    gap: $spacing-1;
+    font-size: $font-xs;
+    color: $text-secondary;
     white-space: nowrap;
     overflow: hidden;
     text-overflow: ellipsis;
     max-width: 200px;
 
     .iconify {
-        font-size: @icon-md;
+        font-size: $icon-md;
     }
 }
 
 .card-visible-tag {
-    font-size: @font-xs;
-    font-weight: @font-weight-semibold;
+    font-size: $font-xs;
+    font-weight: $font-weight-semibold;
     padding: 2px 8px;
     border-radius: @radius-sm;
-    background: @bg-tertiary;
+    background: $bg-tertiary;
 }
 
 // 悬停操作按钮层
@@ -334,12 +334,12 @@
     display: flex;
     justify-content: space-between;  // 两端对齐
     align-items: center;
-    padding: @spacing-4;
+    padding: $spacing-4;
     background: linear-gradient(to top, rgba(255, 255, 255, 0.98) 0%, rgba(255, 255, 255, 0.95) 100%);
-    border-top: 1px solid @border-light;
+    border-top: 1px solid $border-light;
     opacity: 0;
-    transform: translateY(@spacing-2);
-    transition: @transition-base;
+    transform: translateY($spacing-2);
+    transition: $transition-base;
     pointer-events: none;
 
     .app-card:hover & {
@@ -353,26 +353,26 @@
         width: auto;
         min-width: 100px;
         height: 40px;
-        background: @bg-tertiary;
-        border: 1px solid @border-base;
-        border-radius: @radius-lg;
-        font-size: @font-md;
-        font-weight: @font-weight-medium;
-        color: @text-primary;
-        transition: @transition-base;
-        margin-right: @spacing-2;  // 与立即使用按钮的间距
+        background: $bg-tertiary;
+        border: 1px solid $border-base;
+        border-radius: $radius-lg;
+        font-size: $font-md;
+        font-weight: $font-weight-medium;
+        color: $text-primary;
+        transition: $transition-base;
+        margin-right: $spacing-2;  // 与立即使用按钮的间距
         display: inline-flex;
         align-items: center;
         justify-content: center;
 
         &:hover {
-            background: @border-base;
-            border-color: @border-dark;
-            color: @text-primary;
+            background: $border-base;
+            border-color: $border-dark;
+            color: $text-primary;
         }
 
         .iconify {
-            font-size: @icon-lg;
+            font-size: $icon-lg;
             display: inline-flex;
             align-items: center;
         }
@@ -383,31 +383,31 @@
         width: auto;
         min-width: 120px;
         height: 40px;
-        background: linear-gradient(135deg, @primary-color 0%, @primary-light 100%);
+        background: linear-gradient(135deg, $primary-color 0%, $primary-light 100%);
         border: none;
-        border-radius: @radius-lg;
-        font-size: @font-md;
-        font-weight: @font-weight-semibold;
+        border-radius: $radius-lg;
+        font-size: $font-md;
+        font-weight: $font-weight-semibold;
         color: #FFFFFF;
-        box-shadow: 0 4px 12px fade(@primary-color, 30%);
-        transition: @transition-base;
+        box-shadow: 0 4px 12px fade($primary-color, 30%);
+        transition: $transition-base;
         display: inline-flex;
         align-items: center;
         justify-content: center;
 
         &:hover {
-            background: linear-gradient(135deg, @primary-dark 0%, @primary-color 100%);
-            box-shadow: 0 6px 16px fade(@primary-color, 40%);
-            transform: translateY(-@spacing-1);
+            background: linear-gradient(135deg, $primary-dark 0%, $primary-color 100%);
+            box-shadow: 0 6px 16px fade($primary-color, 40%);
+            transform: translateY(-$spacing-1);
         }
 
         &:active {
             transform: translateY(0);
-            box-shadow: 0 2px 8px fade(@primary-color, 20%);
+            box-shadow: 0 2px 8px fade($primary-color, 20%);
         }
 
         .iconify {
-            font-size: @icon-lg;
+            font-size: $icon-lg;
             display: inline-flex;
             align-items: center;
         }

+ 0 - 321
jk-rag-platform/src/components/common/AppCard/index.tsx.rewrite-backup

@@ -1,321 +0,0 @@
-import * as React from 'react';
-import { Dropdown, MenuProps, Button } from 'antd';
-import './index.less';
-
-export interface AppCardProps {
-    id: string;
-    name: string;
-    description: string;
-    icon?: string;
-    iconImage?: string;
-    iconBgColor?: 'blue' | 'indigo' | 'teal' | 'purple' | 'rose' | 'cyan' | 'amber' | 'green' | 'orange';
-    creator?: string;
-    creatorId?: string;
-    proName?: string;  // 项目名称
-    tags?: Array<{
-        label: string;
-        color: 'slate' | 'blue' | 'indigo' | 'teal' | 'purple' | 'rose' | 'cyan' | 'amber';
-    }>;
-    certification?: string;
-    isHot?: boolean;
-    viewCount?: number;
-    favoriteCount?: number;
-    createTime?: string;
-    status?: 'draft' | 'pending' | 'approved' | 'rejected';
-    department?: string;
-    visible?: 'public' | 'private' | 'vip';
-    appId?: string;
-    isCollect?: boolean;
-    isCreator?: boolean;
-
-    // 显示控制
-    showCreator?: boolean;
-    showActions?: boolean;
-    showCertification?: boolean;
-    showTags?: boolean;
-    showHot?: boolean;
-    showViewCount?: boolean;
-    showFavoriteCount?: boolean;
-    showCreateTime?: boolean;
-    showStatus?: boolean;
-    showDepartment?: boolean;
-    showVisible?: boolean;
-    showOperations?: boolean;
-
-    // 回调函数
-    onPlay?: () => void;
-    onShare?: () => void;
-    onFavorite?: () => void;
-    onEdit?: () => void;
-    onDelete?: () => void;
-    onView?: () => void;
-    onApi?: () => void;
-}
-
-const AppCard: React.FC<AppCardProps> = (props) => {
-    const {
-        id,
-        name,
-        description,
-        icon,
-        iconImage,
-        iconBgColor = 'blue',
-        creator,
-        creatorId,
-        proName,
-        tags = [],
-        certification,
-        isHot,
-        viewCount,
-        favoriteCount,
-        createTime,
-        status,
-        department,
-        visible,
-        appId,
-        isCollect = false,
-        isCreator = false,
-        showCreator = true,
-        showActions = true,
-        showCertification = true,
-        showTags = true,
-        showHot = true,
-        showViewCount = false,
-        showFavoriteCount = false,
-        showCreateTime = false,
-        showStatus = false,
-        showDepartment = false,
-        showVisible = false,
-        showOperations = false,
-        onPlay,
-        onShare,
-        onFavorite,
-        onEdit,
-        onDelete,
-        onView,
-        onApi,
-    } = props;
-
-    // 构建操作菜单项
-    const operationItems: MenuProps['items'] = React.useMemo(() => {
-        const items: any[] = [];
-        
-        if (onApi) {
-            items.push({
-                key: 'api',
-                label: 'API 调用',
-                icon: <span className="iconify" data-icon="solar:code-square-linear"></span>,
-                onClick: () => onApi?.(),
-            });
-        }
-        
-        if (isCreator && onEdit) {
-            items.push({
-                key: 'edit',
-                label: '编辑',
-                icon: <span className="iconify" data-icon="solar:pen-linear"></span>,
-                onClick: () => onEdit?.(),
-            });
-        }
-        
-        if (isCreator && onDelete) {
-            items.push({
-                key: 'delete',
-                label: '删除',
-                icon: <span className="iconify" data-icon="solar:trash-bin-linear"></span>,
-                danger: true,
-                onClick: () => onDelete?.(),
-            });
-        }
-        
-        return items;
-    }, [onApi, onEdit, onDelete, isCreator]);
-
-    const statusConfig = {
-        draft: { label: '草稿', color: '#9CA3AF' },
-        pending: { label: '审核中', color: '#F59E0B' },
-        approved: { label: '已通过', color: '#10B981' },
-        rejected: { label: '已拒绝', color: '#EF4444' },
-    };
-
-    const visibleConfig = {
-        public: { label: '公开', color: '#1D4ED8' },
-        private: { label: '私有', color: '#6B7280' },
-        vip: { label: 'VIP', color: '#F59E0B' },
-    };
-
-    const handleCollectClick = () => {
-        onFavorite?.();
-    };
-
-    const handleShareClick = () => {
-        onShare?.();
-    };
-
-    return (
-        <div className='app-card'>
-            {showHot && isHot && (
-                <span className='card-hot-badge'>Hot</span>
-            )}
-
-            {showStatus && status && (
-                <span
-                    className='card-status-badge'
-                    style={{ background: statusConfig[status].color }}
-                >
-                    {statusConfig[status].label}
-                </span>
-            )}
-
-            {/* 操作按钮 - 悬停显示(在 Hot Tag 上层) */}
-            {showActions && (
-                <div className='card-actions'>
-                    <button
-                        className='card-action-btn'
-                        onClick={(e) => {
-                            e.stopPropagation();
-                            handleShareClick();
-                        }}
-                        title='分享'
-                    >
-                        <span className='iconify' data-icon='solar:share-linear'></span>
-                    </button>
-                    <button
-                        className='card-action-btn'
-                        onClick={(e) => {
-                            e.stopPropagation();
-                            handleCollectClick();
-                        }}
-                        title={isCollect ? '取消收藏' : '收藏'}
-                    >
-                        {isCollect ? (
-                            <span className='iconify' data-icon='solar:star-bold' style={{ color: '#F5E663' }}></span>
-                        ) : (
-                            <span className='iconify' data-icon='solar:star-linear'></span>
-                        )}
-                    </button>
-                </div>
-            )}
-
-            <div className='card-header'>
-                <div className={`card-icon-wrapper bg-${iconBgColor}`}>
-                    {iconImage ? (
-                        <img alt={name} className='card-icon' src={iconImage} />
-                    ) : icon ? (
-                        <span className='iconify' data-icon={icon}></span>
-                    ) : null}
-                </div>
-
-                {showCreator && creator && (
-                    <div className='card-creator'>
-                        <div className='card-creator-info'>
-                            <span className='card-creator-label'>创建者</span>
-                            <span className='card-creator-name'>{creator}</span>
-                            {proName && (
-                                <span className='card-pro-name' title={proName}>{proName}</span>
-                            )}
-                        </div>
-                    </div>
-                )}
-            </div>
-
-            <h5 className='card-title'>{name}</h5>
-
-            <p className='card-description'>{description}</p>
-
-            {showTags && tags.length > 0 && (
-                <div className='card-tags'>
-                    {tags.map((tag, index) => (
-                        <span key={index} className={`card-tag tag-${tag.color}`}>
-                            {tag.label}
-                        </span>
-                    ))}
-                </div>
-            )}
-
-            {(showViewCount || showFavoriteCount || showCreateTime) && (
-                <div className='card-meta'>
-                    <div className='card-meta-left'>
-                        {showViewCount && viewCount !== undefined && (
-                            <span className='card-meta-item'>
-                                <span className='iconify' data-icon='solar:eye-linear'></span>
-                                <span>{viewCount}</span>
-                            </span>
-                        )}
-                        {showFavoriteCount && favoriteCount !== undefined && (
-                            <span className='card-meta-item'>
-                                <span className='iconify' data-icon='solar:heart-linear'></span>
-                                <span>{favoriteCount}</span>
-                            </span>
-                        )}
-                    </div>
-                    {showCreateTime && createTime && (
-                        <div className='card-meta-right'>
-                            <span className='card-meta-item'>
-                                <span className='iconify' data-icon='solar:calendar-linear'></span>
-                                <span>{createTime}</span>
-                            </span>
-                        </div>
-                    )}
-                </div>
-            )}
-
-            {showCertification && (
-                <div className='card-footer-info'>
-                    {certification && (
-                        <div className='card-certification'>
-                            <span className='iconify' data-icon='solar:verified-check-bold'></span>
-                            <span>{certification}</span>
-                        </div>
-                    )}
-                    {showDepartment && department && (
-                        <div className='card-department'>
-                            <span className='iconify' data-icon='solar:buildings-bold'></span>
-                            <span>{department}</span>
-                        </div>
-                    )}
-                    {showVisible && visible && (
-                        <span 
-                            className='card-visible-tag'
-                            style={{ color: visibleConfig[visible].color }}
-                        >
-                            {visibleConfig[visible].label}
-                        </span>
-                    )}
-                </div>
-            )}
-
-            {showOperations && (
-                <div className='card-hover-actions'>
-                    <Dropdown 
-                        menu={{ items: operationItems }} 
-                        trigger={['click']}
-                        placement='topLeft'
-                        className='card-operation-dropdown'
-                    >
-                        <Button 
-                            className='card-operation-btn'
-                            icon={<span className='iconify' data-icon='solar:menu-dots-linear'></span>}
-                            size='large'
-                        >
-                            更多操作
-                        </Button>
-                    </Dropdown>
-                    <Button 
-                        className='card-use-btn'
-                        icon={<span className='iconify' data-icon='solar:arrow-right-linear'></span>}
-                        size='large'
-                        onClick={(e) => {
-                            e.stopPropagation();
-                            onView?.();
-                        }}
-                    >
-                        立即使用
-                    </Button>
-                </div>
-            )}
-        </div>
-    );
-};
-
-export default AppCard;

+ 32 - 32
jk-rag-platform/src/components/common/FilterBar/index.less → jk-rag-platform/src/components/common/FilterBar/index.scss

@@ -2,13 +2,13 @@
 // 使用全局变量,符合设计规范 v3.6
 // 注意:全局 .filter-bar 已移除 border-bottom,此处定义组件特定样式
 
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 .filter-bar {
     display: flex;
     justify-content: space-between;
     align-items: center;
-    padding: @spacing-3 0;  // 12px 0 - 更紧凑
+    padding: $spacing-3 0;  // 12px 0 - 更紧凑
 
     // Tab 样式覆盖
     .ant-tabs {
@@ -16,25 +16,25 @@
             margin: 0;
             
             .ant-tabs-tab {
-                padding: @spacing-2 @spacing-3;  // 8px 12px - 更紧凑
-                font-size: @font-md;  // 13px
-                color: @text-secondary;
-                transition: @transition-base;
+                padding: $spacing-2 $spacing-3;  // 8px 12px - 更紧凑
+                font-size: $font-md;  // 13px
+                color: $text-secondary;
+                transition: $transition-base;
                 
                 &:hover {
-                    color: @text-primary;
+                    color: $text-primary;
                 }
                 
                 &.ant-tabs-tab-active {
                     .ant-tabs-tab-btn {
-                        color: @primary-color;
-                        font-weight: @font-weight-semibold;
+                        color: $primary-color;
+                        font-weight: $font-weight-semibold;
                     }
                 }
             }
             
             .ant-tabs-ink-bar {
-                background: @primary-color;
+                background: $primary-color;
             }
         }
     }
@@ -45,10 +45,10 @@
         align-items: center;
         width: 300px;
         height: 36px;  // 36px - 与 Tab 高度统一
-        background: @bg-secondary;
-        border: 1px solid @border-base;
-        border-radius: @radius-lg;  // 8px
-        transition: all 0.2s @easing-default;
+        background: $bg-secondary;
+        border: 1px solid $border-base;
+        border-radius: $radius-lg;  // 8px
+        transition: all 0.2s $easing-default;
         position: relative;
         overflow: hidden;
         
@@ -60,13 +60,13 @@
             background: transparent !important;
             
             input {
-                font-size: @font-md;  // 13px
-                color: @text-primary;
-                padding: 0 @spacing-3;  // 12px 左右内边距
+                font-size: $font-md;  // 13px
+                color: $text-primary;
+                padding: 0 $spacing-3;  // 12px 左右内边距
                 height: 100%;
                 
                 &::placeholder {
-                    color: @text-hint;
+                    color: $text-hint;
                 }
             }
         }
@@ -81,15 +81,15 @@
             background: transparent;
             border: none;
             border-left: 1px solid transparent;
-            color: @text-secondary;
+            color: $text-secondary;
             font-size: 15px;
             cursor: pointer;
-            transition: all 0.2s @easing-default;
+            transition: all 0.2s $easing-default;
             flex-shrink: 0;
             
             &:hover:not(:disabled) {
-                color: @primary-color;
-                background: fade(@primary-color, 5%);
+                color: $primary-color;
+                background: fade($primary-color, 5%);
             }
             
             &:disabled {
@@ -100,27 +100,27 @@
         
         // 聚焦状态
         &.focused {
-            border-color: @primary-color;
-            box-shadow: 0 0 0 2px fade(@primary-color, 20%);
+            border-color: $primary-color;
+            box-shadow: 0 0 0 2px fade($primary-color, 20%);
             
             .search-button {
-                color: @primary-color;
+                color: $primary-color;
             }
         }
         
         // 加载状态
         &.loading {
             .search-button {
-                color: @text-hint;
+                color: $text-hint;
             }
         }
         
         // 悬停状态
         &:hover:not(.focused):not(.loading) {
-            border-color: @primary-color;
+            border-color: $primary-color;
             
             .search-button {
-                color: @primary-color;
+                color: $primary-color;
             }
         }
     }
@@ -130,7 +130,7 @@
 @media (max-width: 768px) {
     .filter-bar {
         flex-direction: column;
-        gap: @spacing-3;  // 12px
+        gap: $spacing-3;  // 12px
         align-items: stretch;
         
         .ant-tabs {
@@ -138,8 +138,8 @@
             
             .ant-tabs-nav {
                 .ant-tabs-tab {
-                    padding: @spacing-2 @spacing-3;  // 8px 12px
-                    font-size: @font-sm;  // 11px
+                    padding: $spacing-2 $spacing-3;  // 8px 12px
+                    font-size: $font-sm;  // 11px
                 }
             }
         }
@@ -149,7 +149,7 @@
             
             .search-input {
                 input {
-                    font-size: @font-base;  // 12px
+                    font-size: $font-base;  // 12px
                 }
             }
         }

+ 6 - 6
jk-rag-platform/src/components/common/FilterDrawer/index.less → jk-rag-platform/src/components/common/FilterDrawer/index.scss

@@ -1,10 +1,10 @@
 .filter-drawer {
     .filter-content {
-        padding: @spacing-4 0;
+        padding: $spacing-4 0;
     }
 
     .filter-section {
-        margin-bottom: @spacing-4;  // 16px - 紧凑间距
+        margin-bottom: $spacing-4;  // 16px - 紧凑间距
 
         &:last-child {
             margin-bottom: 0;
@@ -12,10 +12,10 @@
     }
 
     .filter-title {
-        font-size: @font-lg;
-        font-weight: @font-weight-semibold;
-        color: @text-primary;
-        margin-bottom: @spacing-3;
+        font-size: $font-lg;
+        font-weight: $font-weight-semibold;
+        color: $text-primary;
+        margin-bottom: $spacing-3;
     }
 
     .filter-tags {

+ 33 - 33
jk-rag-platform/src/components/common/GuideTips/index.less → jk-rag-platform/src/components/common/GuideTips/index.scss

@@ -1,11 +1,11 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // GuideTips 引导提示组件 - 遵循全局样式规则
 .guide-tips-wrapper {
     padding: 0;
     margin: 0;
-    margin-bottom: @spacing-4;  // 与下一行的间距:16px
+    margin-bottom: $spacing-4;  // 与下一行的间距:16px
 
     // 当紧跟在 .list-header.with-tips 后面时,确保无上边距
     // 使用独立选择器避免嵌套导致的样式重复
@@ -17,8 +17,8 @@
         display: flex;
         align-items: center;
         justify-content: space-between;
-        margin-bottom: @spacing-2;
-        padding: @spacing-2 0;
+        margin-bottom: $spacing-2;
+        padding: $spacing-2 0;
 
         &:hover {
             // 只让灯泡图标有轻微效果,文字颜色不变
@@ -31,28 +31,28 @@
         .guide-tips-title {
             display: flex;
             align-items: center;
-            font-size: @font-sm;
-            color: @text-primary;
+            font-size: $font-sm;
+            color: $text-primary;
             // 移除文字颜色过渡,保持静态
 
             .guide-tips-icon {
-                font-size: @icon-lg;
+                font-size: $icon-lg;
                 color: #FAAD14;
-                margin-right: @spacing-2;
+                margin-right: $spacing-2;
                 transition: transform 0.2s ease, filter 0.2s ease;
             }
         }
 
         .guide-tips-close {
-            color: @text-hint;
+            color: $text-hint;
             cursor: pointer;
             display: flex;
             align-items: center;
-            font-size: @icon-lg;
-            transition: @transition-base;
+            font-size: $icon-lg;
+            transition: $transition-base;
 
             &:hover {
-                color: @primary-color;
+                color: $primary-color;
             }
 
             .guide-tips-arrow {
@@ -70,42 +70,42 @@
     // 全局卡片样式优化
     .guide-tips-card {
         background: #FFFFFF;
-        border-radius: @radius-lg;
-        box-shadow: @shadow-sm;
-        border: 1px solid @border-light;
-        transition: @transition-base;
+        border-radius: $radius-lg;
+        box-shadow: $shadow-sm;
+        border: 1px solid $border-light;
+        transition: $transition-base;
         height: 100%;
 
         &:hover {
-            box-shadow: @shadow-md;
+            box-shadow: $shadow-md;
             // 移除边框颜色变化,保持静态
         }
 
         // 卡片头部
         .guide-tips-card-title {
-            font-size: @font-md;
-            font-weight: @font-weight-semibold;
-            color: @text-primary;
-            margin-bottom: @spacing-2;
-            line-height: @line-height-tight;  // 1.1
+            font-size: $font-md;
+            font-weight: $font-weight-semibold;
+            color: $text-primary;
+            margin-bottom: $spacing-2;
+            line-height: $line-height-tight;  // 1.1
         }
 
         // 卡片内容
         .guide-tips-card-description {
-            font-size: @font-sm;
-            color: @text-secondary;
-            line-height: @line-height-base;  // 1.3
+            font-size: $font-sm;
+            color: $text-secondary;
+            line-height: $line-height-base;  // 1.3
             flex: 1;
         }
 
         // Step 编号
         .guide-tips-card-step {
-            font-size: @font-sm;
-            color: @primary-color;
-            font-weight: @font-weight-semibold;
-            margin-top: @spacing-2;
+            font-size: $font-sm;
+            color: $primary-color;
+            font-weight: $font-weight-semibold;
+            margin-top: $spacing-2;
             text-align: right;
-            line-height: @line-height-tight;  // 1.1
+            line-height: $line-height-tight;  // 1.1
         }
     }
 
@@ -128,7 +128,7 @@
 }
 
 // 响应式适配 - 遵循全局断点
-@media (max-width: @screen-md) {
+@media (max-width: $screen-md) {
     .guide-tips-wrapper {
         .guide-tips-row {
             margin: 0;
@@ -136,11 +136,11 @@
     }
 }
 
-@media (max-width: @screen-sm) {
+@media (max-width: $screen-sm) {
     .guide-tips-wrapper {
         .guide-tips-header {
             .guide-tips-title {
-                font-size: @font-xs;
+                font-size: $font-xs;
             }
         }
     }

+ 2 - 2
jk-rag-platform/src/components/common/HeroBanner/index.less → jk-rag-platform/src/components/common/HeroBanner/index.scss

@@ -101,11 +101,11 @@
     }
 
     &.primary {
-        background: @primary-color;
+        background: $primary-color;
         color: #FFFFFF;
 
         &:hover {
-            background: @primary-light;
+            background: $primary-light;
         }
     }
 

+ 0 - 135
jk-rag-platform/src/components/common/StatsGrid/index.less

@@ -1,135 +0,0 @@
-// StatsGrid 统计卡片网格 - 紧凑布局 v3.9
-// 遵循全局样式规范,使用全局变量
-
-@import '@/styles/variables.less';
-
-.stats-grid {
-    display: grid;
-    grid-template-columns: repeat(4, 1fr);
-    gap: @spacing-4;  // 16px - 紧凑间距
-    margin-bottom: @spacing-4;  // 16px - 紧凑间距
-
-    // 平板及以下:2 列
-    @media (max-width: @screen-lg) {
-        grid-template-columns: repeat(2, 1fr);
-    }
-
-    // 移动端:保持 2 列(不变成 1 列)
-    @media (max-width: @screen-md) {
-        grid-template-columns: repeat(2, 1fr);
-        gap: @spacing-3;  // 12px - 更紧凑
-    }
-}
-
-.stat-card {
-    background: @bg-secondary;
-    padding: @spacing-4;  // 16px - 紧凑内边距
-    border-radius: @radius-xl;  // 12px
-    border: 1px solid @border-light;
-    transition: @transition-base;
-
-    &:hover {
-        box-shadow: @shadow-md;
-        transform: translateY(-@spacing-1);
-    }
-}
-
-.stat-card-header {
-    display: flex;
-    justify-content: space-between;
-    align-items: flex-start;
-    margin-bottom: @spacing-2;  // 8px - 紧凑间距
-}
-
-.stat-card-icon {
-    width: @icon-2xl;  // 40px
-    height: @icon-2xl;
-    border-radius: @radius-lg;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-
-    &.bg-blue { 
-        background: @icon-bg-blue; 
-    }
-    &.bg-indigo { 
-        background: @icon-bg-indigo; 
-    }
-    &.bg-orange { 
-        background: @icon-bg-amber; 
-    }
-    &.bg-green { 
-        background: @success-light; 
-    }
-
-    .iconify {
-        font-size: @icon-lg;  // 18px - 紧凑图标
-
-        &.icon-blue { color: @primary-color; }
-        &.icon-indigo { color: @info-dark; }
-        &.icon-orange { color: @warning-color; }
-        &.icon-green { color: @success-color; }
-    }
-}
-
-.stat-card-trend {
-    font-size: @font-xs;  // 8px - 紧凑文字
-    font-weight: @font-weight-semibold;
-    line-height: @line-height-tight;
-
-    &.text-green { color: @success-color; }
-    &.text-blue { color: @info-color; }
-    &.text-orange { color: @warning-dark; }
-    &.text-gray { color: @text-hint; }
-}
-
-.stat-card-label {
-    font-size: @font-sm;  // 11px - 紧凑文字
-    color: @text-secondary;
-    margin-bottom: @spacing-1;  // 4px
-    line-height: @line-height-tight;
-}
-
-.stat-card-value {
-    font-size: @font-2xl;  // 16px - 紧凑数值
-    font-weight: @font-weight-bold;
-    color: @text-primary;
-    margin: 0;
-    line-height: @line-height-tight;
-
-    small {
-        font-size: @font-sm;  // 11px
-        font-weight: @font-weight-normal;
-        color: @text-hint;
-        margin-left: @spacing-1;
-    }
-}
-
-// ===== 移动端优化(≤768px)=====
-@media (max-width: @screen-md) {
-    .stat-card {
-        padding: @spacing-3;  // 12px - 更紧凑
-
-        .stat-card-icon {
-            width: @icon-xl;  // 36px - 更小图标
-            height: @icon-xl;
-            border-radius: @radius-md;  // 6px
-
-            .iconify {
-                font-size: @icon-md;  // 16px
-            }
-        }
-
-        .stat-card-trend {
-            font-size: @font-xs;  // 8px
-        }
-
-        .stat-card-label {
-            font-size: @font-xs;  // 8px
-        }
-
-        .stat-card-value {
-            font-size: @font-xl;  // 15px
-        }
-    }
-}

+ 135 - 0
jk-rag-platform/src/components/common/StatsGrid/index.scss

@@ -0,0 +1,135 @@
+// StatsGrid 统计卡片网格 - 紧凑布局 v3.9
+// 遵循全局样式规范,使用全局变量
+
+@import '@/styles/variables.scss';';
+
+.stats-grid {
+    display: grid;
+    grid-template-columns: repeat(4, 1fr);
+    gap: $spacing-4;  // 16px - 紧凑间距
+    margin-bottom: $spacing-4;  // 16px - 紧凑间距
+
+    // 平板及以下:2 列
+    @media (max-width: $screen-lg) {
+        grid-template-columns: repeat(2, 1fr);
+    }
+
+    // 移动端:保持 2 列(不变成 1 列)
+    @media (max-width: $screen-md) {
+        grid-template-columns: repeat(2, 1fr);
+        gap: $spacing-3;  // 12px - 更紧凑
+    }
+}
+
+.stat-card {
+    background: $bg-secondary;
+    padding: $spacing-4;  // 16px - 紧凑内边距
+    border-radius: $radius-xl;  // 12px
+    border: 1px solid $border-light;
+    transition: $transition-base;
+
+    &:hover {
+        box-shadow: $shadow-md;
+        transform: translateY(-$spacing-1);
+    }
+}
+
+.stat-card-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: flex-start;
+    margin-bottom: $spacing-2;  // 8px - 紧凑间距
+}
+
+.stat-card-icon {
+    width: $icon-2xl;  // 40px
+    height: $icon-2xl;
+    border-radius: $radius-lg;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    &.bg-blue { 
+        background: $icon-bg-blue; 
+    }
+    &.bg-indigo { 
+        background: $icon-bg-indigo; 
+    }
+    &.bg-orange { 
+        background: $icon-bg-amber; 
+    }
+    &.bg-green { 
+        background: $success-light; 
+    }
+
+    .iconify {
+        font-size: $icon-lg;  // 18px - 紧凑图标
+
+        &.icon-blue { color: $primary-color; }
+        &.icon-indigo { color: $info-dark; }
+        &.icon-orange { color: $warning-color; }
+        &.icon-green { color: $success-color; }
+    }
+}
+
+.stat-card-trend {
+    font-size: $font-xs;  // 8px - 紧凑文字
+    font-weight: $font-weight-semibold;
+    line-height: $line-height-tight;
+
+    &.text-green { color: $success-color; }
+    &.text-blue { color: $info-color; }
+    &.text-orange { color: $warning-dark; }
+    &.text-gray { color: $text-hint; }
+}
+
+.stat-card-label {
+    font-size: $font-sm;  // 11px - 紧凑文字
+    color: $text-secondary;
+    margin-bottom: $spacing-1;  // 4px
+    line-height: $line-height-tight;
+}
+
+.stat-card-value {
+    font-size: $font-2xl;  // 16px - 紧凑数值
+    font-weight: $font-weight-bold;
+    color: $text-primary;
+    margin: 0;
+    line-height: $line-height-tight;
+
+    small {
+        font-size: $font-sm;  // 11px
+        font-weight: $font-weight-normal;
+        color: $text-hint;
+        margin-left: $spacing-1;
+    }
+}
+
+// ===== 移动端优化(≤768px)=====
+@media (max-width: $screen-md) {
+    .stat-card {
+        padding: $spacing-3;  // 12px - 更紧凑
+
+        .stat-card-icon {
+            width: $icon-xl;  // 36px - 更小图标
+            height: $icon-xl;
+            border-radius: $radius-md;  // 6px
+
+            .iconify {
+                font-size: $icon-md;  // 16px
+            }
+        }
+
+        .stat-card-trend {
+            font-size: $font-xs;  // 8px
+        }
+
+        .stat-card-label {
+            font-size: $font-xs;  // 8px
+        }
+
+        .stat-card-value {
+            font-size: $font-xl;  // 15px
+        }
+    }
+}

+ 1 - 1
jk-rag-platform/src/main.tsx

@@ -2,7 +2,7 @@ import { createRoot } from 'react-dom/client';
 import App from '@/App';
 import dayjs from 'dayjs';
 import 'dayjs/locale/zh-cn';
-import '@/styles/global.less';
+import '@/styles/global.scss';
 import '@/styles/tailwind.css';
 import '@fortawesome/fontawesome-free/css/all.min.css';
 

+ 0 - 175
jk-rag-platform/src/pages/appCenter/appPlazaList/style.less.bak

@@ -1,175 +0,0 @@
-// 导入全局样式变量
-@import '@/styles/variables.less';
-
-// 导入全局公共组件样式
-@import '@/styles/components.less';
-
-.app-plaza-list {
-    padding: @spacing-6 @spacing-8;
-    min-height: calc(100vh - @header-height);
-    background: @bg-primary;
-}
-
-.filter-bar {
-    display: flex;
-    justify-content: space-between;
-    align-items: flex-end;
-    margin-bottom: @spacing-8;
-    padding-bottom: @spacing-6;
-    border-bottom: 1px solid @border-base;
-
-    &-title {
-        h3 {
-            font-size: @font-2xl;
-            font-weight: @font-weight-bold;
-            color: @text-primary;
-            margin: 0 0 4px 0;
-        }
-
-        p {
-            font-size: @font-md;
-            color: @text-secondary;
-            margin: 0;
-        }
-    }
-
-    &-actions {
-        display: flex;
-        align-items: center;
-        gap: @spacing-3;
-    }
-}
-
-.sort-buttons {
-    display: flex;
-    border: 1px solid #E5E7EB;
-    border-radius: @radius-xl;
-    overflow: hidden;
-
-    .sort-btn {
-        padding: 8px 16px;
-        background: @bg-secondary;
-        border: none;
-        border-right: 1px solid #E5E7EB;
-        font-size: @font-md;
-        font-weight: 500;
-        color: @text-secondary;
-        cursor: pointer;
-        transition: all 0.2s ease;
-
-        &:last-child {
-            border-right: none;
-        }
-
-        &:hover {
-            background: @bg-primary;
-            color: @text-primary;
-        }
-
-        &.active {
-            background: @icon-bg-blue;
-            color: @primary-color;
-            font-weight: 600;
-        }
-    }
-}
-
-.filter-btn {
-    width: @search-height;
-    height: @search-height;
-    border: 1px solid #BFDBFE;
-    background: @icon-bg-blue;
-    border-radius: @radius-xl;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    color: @primary-color;
-    cursor: pointer;
-    transition: all 0.2s ease;
-
-    &:hover {
-        background: #DBEAFE;
-        border-color: #93C5FD;
-    }
-
-    .iconify {
-        font-size: 20px;
-    }
-}
-
-@media (max-width: 1024px) {
-    .app-plaza-list {
-        padding: 16px 24px;
-    }
-
-    .filter-bar {
-        flex-direction: column;
-        align-items: flex-start;
-        gap: 16px;
-
-        &-actions {
-            width: 100%;
-            justify-content: space-between;
-        }
-    }
-}
-
-@media (max-width: 768px) {
-    .app-plaza-list {
-        padding: 16px;
-    }
-
-    .sort-buttons {
-        flex-wrap: wrap;
-
-        .sort-btn {
-            flex: 1;
-            min-width: 100px;
-        }
-    }
-}
-
-// 分页器样式
-.pagination-container {
-    padding: 24px 0;
-    display: flex;
-    justify-content: flex-end;
-
-    .ant-pagination {
-        .ant-pagination-item {
-            border-radius: 8px;
-            border-color: #E5E7EB;
-            transition: all 0.2s ease;
-
-            &:hover {
-                border-color: @primary-color;
-                transform: translateY(-2px);
-            }
-
-            &.ant-pagination-item-active {
-                background: #1D4ED8;
-                border-color: @primary-color;
-
-                a {
-                    color: #FFFFFF;
-                }
-            }
-        }
-
-        .ant-pagination-prev,
-        .ant-pagination-next {
-            .ant-pagination-item-link {
-                border-radius: 8px;
-                border-color: #E5E7EB;
-                transition: all 0.2s ease;
-            }
-
-            &:hover {
-                .ant-pagination-item-link {
-                    border-color: @primary-color;
-                    color: @primary-color;
-                }
-            }
-        }
-    }
-}

+ 6 - 6
jk-rag-platform/src/pages/appCenter/appPlazaList/style.less → jk-rag-platform/src/pages/appCenter/appPlazaList/style.scss

@@ -1,5 +1,5 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // 应用广场列表页面样式
 // 说明:
@@ -9,20 +9,20 @@
 // 本文件仅保留页面特定的响应式样式
 
 .app-plaza-list {
-    padding: @spacing-4 @spacing-6;
-    min-height: calc(100vh - @header-height);
-    background: @bg-primary;
+    padding: $spacing-4 $spacing-6;
+    min-height: calc(100vh - $header-height);
+    background: $bg-primary;
 }
 
 // 响应式适配
 @media (max-width: 1024px) {
     .app-plaza-list {
-        padding: @spacing-3 @spacing-4;
+        padding: $spacing-3 $spacing-4;
     }
 }
 
 @media (max-width: 768px) {
     .app-plaza-list {
-        padding: @spacing-4;
+        padding: $spacing-4;
     }
 }

+ 0 - 134
jk-rag-platform/src/pages/appCenter/categoryApps/style.less.bak

@@ -1,134 +0,0 @@
-// 导入全局样式变量
-@import '@/styles/variables.less';
-
-// 导入全局公共组件样式
-@import '@/styles/components.less';
-
-.category-apps-list {
-    padding: @spacing-6 @spacing-8;
-    min-height: calc(100vh - @header-height);
-    background: @bg-primary;
-}
-
-
-@media (max-width: 1024px) {
-    .category-apps-list {
-        padding: 16px 24px;
-    }
-
-    .filter-bar {
-        flex-direction: column;
-        align-items: flex-start;
-        gap: 16px;
-
-        &-actions {
-            width: 100%;
-            justify-content: space-between;
-        }
-    }
-}
-
-@media (max-width: 768px) {
-    .category-apps-list {
-        padding: 16px;
-    }
-}
-
-// 分页器样式
-.pagination-container {
-    padding: 24px 0;
-    display: flex;
-    justify-content: flex-end;
-
-    .ant-pagination {
-        .ant-pagination-item {
-            border-radius: 8px;
-            border-color: #E5E7EB;
-            transition: all 0.2s ease;
-
-            &:hover {
-                border-color: #1D4ED8;
-                transform: translateY(-2px);
-            }
-
-            &.ant-pagination-item-active {
-                background: #1D4ED8;
-                border-color: #1D4ED8;
-
-                a {
-                    color: #FFFFFF;
-                }
-            }
-        }
-
-        .ant-pagination-prev,
-        .ant-pagination-next {
-            .ant-pagination-item-link {
-                border-radius: 8px;
-                border-color: #E5E7EB;
-                transition: all 0.2s ease;
-            }
-
-            &:hover {
-                .ant-pagination-item-link {
-                    border-color: #1D4ED8;
-                    color: #1D4ED8;
-                }
-            }
-        }
-    }
-}
-
-// 排序按钮样式
-.sort-buttons {
-    display: flex;
-    border: 1px solid #E5E7EB;
-    border-radius: 12px;
-    overflow: hidden;
-
-    .sort-btn {
-        padding: 8px 16px;
-        background: #FFFFFF;
-        border: none;
-        border-right: 1px solid #E5E7EB;
-        font-size: 14px;
-        font-weight: 500;
-        color: #6B7280;
-        cursor: pointer;
-        transition: all 0.2s ease;
-
-        &:last-child {
-            border-right: none;
-        }
-
-        &:hover {
-            background: #F9FAFB;
-            color: #1F2937;
-        }
-
-        &.active {
-            background: #EFF6FF;
-            color: #1D4ED8;
-            font-weight: 600;
-        }
-    }
-}
-
-// 筛选按钮样式
-.filter-btn {
-    width: 40px;
-    height: 40px;
-    border: 1px solid #BFDBFE;
-    background: #EFF6FF;
-    border-radius: 12px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    cursor: pointer;
-    transition: all 0.2s ease;
-
-    .iconify {
-        font-size: 18px;
-        color: #1D4ED8;
-    }
-}

+ 6 - 6
jk-rag-platform/src/pages/appCenter/categoryApps/style.less → jk-rag-platform/src/pages/appCenter/categoryApps/style.scss

@@ -1,5 +1,5 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // 分类应用列表页面样式
 // 说明:
@@ -9,20 +9,20 @@
 // 本文件仅保留页面特定的响应式样式
 
 .category-apps-list {
-    padding: @spacing-4 @spacing-6;
-    min-height: calc(100vh - @header-height);
-    background: @bg-primary;
+    padding: $spacing-4 $spacing-6;
+    min-height: calc(100vh - $header-height);
+    background: $bg-primary;
 }
 
 // 响应式适配
 @media (max-width: 1024px) {
     .category-apps-list {
-        padding: @spacing-3 @spacing-4;
+        padding: $spacing-3 $spacing-4;
     }
 }
 
 @media (max-width: 768px) {
     .category-apps-list {
-        padding: @spacing-4;
+        padding: $spacing-4;
     }
 }

+ 0 - 0
jk-rag-platform/src/pages/appCenter/mobile/MobileH5.less → jk-rag-platform/src/pages/appCenter/mobile/MobileH5.scss


+ 0 - 0
jk-rag-platform/src/pages/appCenter/mobile/components/AppPlaza.less → jk-rag-platform/src/pages/appCenter/mobile/components/AppPlaza.scss


+ 0 - 0
jk-rag-platform/src/pages/appCenter/mobile/styles/mobile.less → jk-rag-platform/src/pages/appCenter/mobile/styles/mobile.scss


+ 0 - 130
jk-rag-platform/src/pages/deepseek/dataExport/index.tsx.bak

@@ -1,130 +0,0 @@
-import * as React from 'react';
-
-import { Table, TableColumnsType, TablePaginationConfig, Modal } from 'antd';
-import { DownloadOutlined } from '@ant-design/icons';
-import Search from './components/Search';
-import dayjs from 'dayjs';
-import store from './store';
-import { Record } from './types';
-import './style.less';
-
-const DataExport: React.FC = () => {
-    const {
-        state,
-        onClickSearch,
-        onClickReset,
-        onChangePagination,
-        onClickDownload,
-        init,
-        reset
-    } = store;
-    const {
-        listLoading,
-        list,
-        page
-    } = state;
-
-    React.useEffect(() => {
-        init();
-        return () => reset();
-    }, []);
-
-    const columns: TableColumnsType<Record> = [
-        {
-            title: '序号',
-            dataIndex: 'index',
-            width: 80,
-            render: (text, record, index) => {
-                return index + 1;
-            }
-        },
-        {
-            title: '聊天标题',
-            dataIndex: 'dialog_name',
-        },
-        {
-            title: '消息长度',
-            dataIndex: 'length',
-            render: (text) => {
-                return text + '条';
-            }
-        },
-        {
-            title: '聊天时间',
-            dataIndex: 'create_time',
-            width: 200,
-            render: (text) => {
-                if (text) {
-                    return dayjs(text).format('YYYY-MM-DD HH:mm:ss');
-                } else {
-                    return '--';
-                }
-            }
-        },
-        {
-            title: '操作',
-            dataIndex: 'operation',
-            width: 80,
-            fixed: 'right',
-            render: (text, record) => {
-                return (
-                    <a
-                        onClick={() => {
-                            Modal.confirm({
-                                title: '导出',
-                                content: '确定导出Excel吗?',
-                                onOk: async () => {
-                                    await onClickDownload(record.id, record.dialog_name);
-                                }
-                            });
-                        }}
-                    >
-                        <DownloadOutlined />
-                    </a >
-                )
-            }
-        }
-    ];
-
-    const paginationConfig: TablePaginationConfig = {
-        // 显示数据总量
-        showTotal: (total: number) => {
-            return `共 ${total} 条`;
-        },
-        // 展示分页条数切换
-        showSizeChanger: true,
-        // 指定每页显示条数
-        pageSizeOptions: ['10', '20', '50', '100'],
-        // 快速跳转至某页
-        showQuickJumper: true,
-        current: page.pageNum,
-        pageSize: page.pageSize,
-        total: page.total,
-        onChange: async (page, pageSize) => {
-            await onChangePagination(page, pageSize);
-        },
-    };
-
-    return (
-        <div className='dataExport'>
-            <div className='dataExport-search'>
-                <Search
-                    onClickSearch={onClickSearch}
-                    onClickReset={onClickReset}
-                />
-            </div>
-            <div className='dataExport-table'>
-                <Table
-                    scroll={{ x: 'max-content' }}
-                    rowKey={(record) => record.id}
-                    loading={listLoading}
-                    columns={columns}
-                    dataSource={list}
-                    pagination={paginationConfig}
-                />
-            </div>
-        </div>
-    );
-};
-
-export default observer(DataExport);

+ 0 - 0
jk-rag-platform/src/pages/deepseek/dataExport/style.less → jk-rag-platform/src/pages/deepseek/dataExport/style.scss


+ 0 - 184
jk-rag-platform/src/pages/deepseek/evaluationTool/datasetManagement/list/index.tsx.bak

@@ -1,184 +0,0 @@
-import React, { useEffect, useMemo, useState } from 'react';
-import { Table, Pagination, Modal, Space, Button, TablePaginationConfig, Input, Radio, message } from 'antd';
-import type { ColumnsType } from 'antd/es/table';
-
-import { SearchOutlined, BulbOutlined, CloseOutlined } from '@ant-design/icons';
-import LocalStorage from '@/LocalStorage';
-
-import store from './store';
-import DataManagementMode from '../components/dataManagementMode';
-import ExampleModel from '../components/exampleModel';
-
-import { deleteDatasetApi } from '@/apis';
-
-interface KnowledgeRecord {
-  name: string;
-  knowledgeId: string | number;
-  length: string | number; // 使用空间
-  appNames: string; // 关联空间
-  documentSize: number; // 文件数量
-  createTime: string; // 创建时间
-  updateTime: string; // 更新时间
-  status: string | number; // 状态
-}
-
-
-const RevisionToolList: React.FC = () => {
-  const [loading, setLoading] = useState(false);
-
-  const { state, init, handleNameChange, onChangePagination, handleModeChange } = store;
-
-  const { reviseList, name } = state
-
-
-
-  useEffect(() => {
-    // 模拟加载状态
-    setLoading(true);
-    const t = setTimeout(() => setLoading(false), 250);
-    return () => clearTimeout(t);
-  }, [state.page]);
-
-  const [deletedatasetManagement, setDeletedatasetManagement] = useState<boolean>(false); // 是否删除数据集
-  const [downloaddatasetManagement, setDownloaddatasetManagement] = useState<boolean>(false); // 下载数据集
-  const [userInfoAll, setUserInfoAll] = React.useState<any>({});
-
-  useEffect(() => {
-    init();
-
-
-    setDeletedatasetManagement(LocalStorage.getStatusFlag('evaluationTool:datasetManagement:del'));
-    setDownloaddatasetManagement(LocalStorage.getStatusFlag('evaluationTool:datasetManagement:download'));
-    setUserInfoAll(LocalStorage.getUserInfo());
-
-
-
-
-
-    const handleEvaluationTaskCreate = (e: any) => {
-      if (e.detail && e.detail.platform === 'Management') {
-        console.log('监听到创建数据集事件');
-        setOpenDataManagementMode(true);
-      }
-    }
-    window.addEventListener('datasetManagement', handleEvaluationTaskCreate as EventListener);
-
-    return () => {
-      window.removeEventListener('datasetManagement', handleEvaluationTaskCreate as EventListener);
-    };
-  }, []);
-
-  const [showGuide, setShowGuide] = React.useState<boolean>(() => localStorage.getItem('revisionToolGuideHidden') !== 'true');
-  const hideGuide = () => { localStorage.setItem('revisionToolGuideHidden', 'true'); setShowGuide(false); };
-  const [record, setRecord] = useState<KnowledgeRecord>({} as KnowledgeRecord);
-  const columns: ColumnsType<KnowledgeRecord> = [
-    {
-      title: '数据集名称', dataIndex: 'name', width: 220,
-      render: (text, record: any) => {
-        return <div>
-          <div style={{ fontWeight: 600 }} className='cursor-pointer'>{text}</div>
-        </div>
-      },
-    },
-    { title: '描述', dataIndex: 'description' },
-    {
-      title: '创建人', dataIndex: 'nickName', width: 120,
-    },
-    { title: '创建时机', dataIndex: 'createTime', width: 190 },
-    // { title: '更新时间', dataIndex: 'updateTime', width: 140 },
-    {
-      title: '操作',
-      dataIndex: 'status',
-      align: 'center',
-      width: 120,
-      render: (val, record: any) => (
-        <p className='flex'>
-          {(downloaddatasetManagement || userInfoAll.id === record.createBy) && <Button type="link" onClick={() => { window.open(record.url) }}>下载</Button>}
-          {(deletedatasetManagement || userInfoAll.id === record.createBy) && <Button type="link" style={{ color: '#ff4d4f', paddingLeft: 0 }} onClick={() => {
-            Modal.confirm({
-              title: '删除',
-              content: `确定删除吗?`,
-              okType: 'danger',
-              onOk: async () => {
-                const res: any = await deleteDatasetApi(record.id);
-                if (res.code === 200) {
-                  message.success('删除成功');
-                  init();
-                }
-              }
-            });
-          }}>删除</Button>}
-        </p>
-      ),
-    }
-  ]
-
-  const paginationConfig: TablePaginationConfig = {
-    // 显示数据总量
-    showTotal: (total: number) => {
-      return `共 ${total} 条`;
-    },
-    // 展示分页条数切换
-    showSizeChanger: true,
-    // 指定每页显示条数
-    pageSizeOptions: ['10', '20', '50', '100'],
-    // 快速跳转至某页
-    showQuickJumper: true,
-    current: state.page.page,
-    pageSize: state.page.size,
-    total: state.page.total,
-    onChange: async (page, pageSize) => {
-      await onChangePagination(page, pageSize);
-    },
-  };
-  const [openDataManagementMode, setOpenDataManagementMode] = useState(false);
-  const [ExampleModelVisible, setExampleModelVisible] = useState(false);
-  const [title, setTitle] = useState('');
-  return (
-    <div style={{ padding: 20, background: '#fff' }}>
-      {showGuide && <> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
-        <div style={{ fontSize: 13, color: '#333', display: 'flex', alignItems: 'center', gap: 6 }}>
-          <BulbOutlined style={{ color: '#faad14' }} />
-          参考数据模版及数据格式要求,准备并上传您的评测数据。提供和您实际场景相关的问题以评估RAG或模型的效果和场景匹配性。<span className='text-primary cursor-pointer'
-            onClick={() => {
-              setExampleModelVisible(true);
-            }}
-          >查看示例数据</span>
-        </div>
-        {/* <CloseOutlined onClick={hideGuide} style={{ color: '#999', cursor: 'pointer' }} /> */}
-      </div></>}
-      <div className='mb-2'>
-        <Radio.Group onChange={(e) => {
-          handleModeChange(e.target.value)
-        }} value={state.createBy} style={{ marginBottom: 8 }}>
-          <Radio.Button value="">全部</Radio.Button>
-          <Radio.Button value={state.myCreateBy}>我创建的</Radio.Button>
-        </Radio.Group>
-        <Space.Compact style={{ width: '300px', float: 'right' }}>
-          <Input
-            placeholder='请输入数据集名称'
-            value={name}
-            onChange={(e) => handleNameChange(e.target.value)}
-          />
-          <Button type="primary" onClick={() => { init() }}><SearchOutlined /></Button>
-        </Space.Compact>
-      </div>
-      {/* 表格 */}
-      <Table
-        columns={columns}
-        dataSource={reviseList}
-        rowKey="id"
-        loading={loading}
-        pagination={paginationConfig}
-      />
-      {/* 修订工具弹窗 */}
-      {openDataManagementMode && <DataManagementMode openDataManagementMode={openDataManagementMode} onCancel={() => { setOpenDataManagementMode(false); init(); }} />}
-      {/* 示例数据文件弹窗 */}
-      {<ExampleModel visible={ExampleModelVisible} onClose={() => {
-        setExampleModelVisible(false);
-      }} />}
-    </div>
-  );
-};
-
-export default observer(RevisionToolList);

+ 0 - 163
jk-rag-platform/src/pages/deepseek/evaluationTool/evaluationTask/components/evaluationTaskHistory.tsx.bak

@@ -1,163 +0,0 @@
-import * as React from 'react';
-
-import { Table, TableColumnsType, TablePaginationConfig, Drawer, Row, Col, Input, Button, Spin } from 'antd';
-import { StepForwardOutlined } from '@ant-design/icons';
-import dayjs from 'dayjs';
-import store from './evaluationTaskHistoryStore';
-import { Record } from '../types';
-// import InfoModal from './InfoModal';
-// import PreviewModal from './PreviewModal';
-// import LocalStorage from '@/LocalStorage';
-
-
-interface AuditHistoryProps {
-    open: boolean;
-    onClose: () => void;
-    record?: Record;
-}
-
-const AuditHistory: React.FC<AuditHistoryProps> = ({ open, onClose, record }) => {
-    const {
-        state,
-        init,
-        onChangePagination,
-        onSetName
-    } = store;
-    const {
-        listLoading,
-        list,
-        infoModalId,
-        infoModalOpen,
-        page
-    } = state;
-
-    const [drawerFlag, setDrawerFlag] = React.useState<boolean>(false);
-    const [drawerData, setDrawerData] = React.useState<any>({});
-    React.useEffect(() => {
-        if (open) {
-            // const userInfo = LocalStorage.getUserInfo();
-            // const userId = (userInfo?.id ?? '').toString();
-            onSetName(record?.taskName || '');
-            init();
-        }
-    }, [open]);
-    const columns: TableColumnsType<any> = [
-        {
-            title: '任务名称/ID', dataIndex: 'taskId', width: 220,
-            render: (text, record: any) => {
-                return <div>
-                    <div style={{ fontWeight: 600 }}>{record.taskName}</div>
-                    <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>ID: {record?.taskId}
-                    </div>
-                </div>
-            },
-        },
-        {
-            title: '状态', dataIndex: 'status', width: 120,
-            render: (text) => {
-                let color = '#52c41a';
-                let statusText = '';
-                if (text === '0') {
-                    color = '';
-                    statusText = '未执行';
-                } else if (text === '1') {
-                    return <Spin size="small" />;
-                } else if (text === '2') {
-                    color = '#52c41a';
-                    statusText = '成功';
-                } else if (text === '3') {
-                    color = '';
-                    statusText = '失败';
-                }
-                return <span style={{ color }}>{statusText}</span>;
-            }
-        },
-        {
-            title: '模型服务', dataIndex: 'model', width: 120
-        },
-        { title: '类型', dataIndex: 'evaluationType', width: 140 },
-        { title: '数据集', dataIndex: 'datesetName', width: 140 },
-        { title: '结果打分', dataIndex: 'resultStore', width: 140 },
-        { title: '创建时间', dataIndex: 'createTime', width: 140 },
-        { title: '开始时间', dataIndex: 'startTime', width: 140 },
-        { title: '结束时间', dataIndex: 'endTime', width: 140 },
-        {
-            title: '操作',
-            dataIndex: 'status',
-            align: 'center',
-            width: 120,
-            fixed: 'right',
-            render: (val, record: any) => {
-                if (record.status === '3') {
-                    return (<p className='cursor-pointer' onClick={() => {
-                    }}>
-                        下载
-                    </p>)
-                } else {
-                    return (<p className='text-primary cursor-pointer' onClick={() => {
-                        window.open(record.fullUrl)
-                    }}>
-                        下载
-                    </p>)
-                }
-            },
-        }
-    ];
-    const paginationConfig: TablePaginationConfig = {
-        // 显示数据总量
-        showTotal: (total: number) => {
-            return `共 ${total} 条`;
-        },
-        // 展示分页条数切换
-        showSizeChanger: true,
-        // 指定每页显示条数
-        pageSizeOptions: ['10', '20', '50', '100'],
-        // 快速跳转至某页
-        showQuickJumper: true,
-        current: page.pageNum,
-        pageSize: page.pageSize,
-        total: page.total,
-        onChange: async (page, pageSize) => {
-            await onChangePagination(page, pageSize);
-        },
-    };
-    const [kbName, setKbName] = React.useState<string>('');
-    const handleSearch = async () => {
-        init();
-    }
-    return (
-        <Drawer
-            title="评测历史"
-            width="80%"
-            open={open}
-            onClose={onClose}
-        >
-            {/* <div style={{ marginBottom: 12 }}>
-                <Row gutter={12} align="middle">
-                    <Col span={6}>
-                        <Input placeholder="名称" value={kbName} onChange={(e) => {
-                            setKbName(e.target.value)
-                            onSetName(e.target.value)
-                        }} />
-                    </Col>
-                    <Col>
-                        <Button type="primary" onClick={handleSearch}>搜索</Button>
-                    </Col>
-                </Row>
-            </div> */}
-            <div className='knowledgeLibList'>
-                <div className='knowledgeLibList-table'>
-                    <Table
-                        scroll={{ x: 'max-content' }}
-                        rowKey={(record) => record.createTime}
-                        loading={listLoading}
-                        columns={columns}
-                        dataSource={list}
-                        pagination={paginationConfig}
-                    />
-                </div>
-            </div>
-        </Drawer>
-    );
-}
-export default observer(AuditHistory);

+ 0 - 269
jk-rag-platform/src/pages/deepseek/evaluationTool/evaluationTask/list/index.tsx.bak

@@ -1,269 +0,0 @@
-import React, { useEffect, useMemo, useState } from 'react';
-import { Table, Pagination, Space, Button, TablePaginationConfig, Input, Modal, message, Spin } from 'antd';
-import type { ColumnsType } from 'antd/es/table';
-
-import { SearchOutlined, BulbOutlined, CloseOutlined,LayoutOutlined,DeleteOutlined,RedoOutlined,RadarChartOutlined } from '@ant-design/icons';
-import LocalStorage from '@/LocalStorage';
-
-import store from './store';
-import Step from '@/components/step';
-import CreateTaskMode from '../components/CreateTaskMode';
-import IndicatorRag from '../components/indicatorRag';
-import EvaluationTaskHistory from '../components/evaluationTaskHistory';
-import { deleteEvaluationApi, beginEvaluationApi } from '@/apis';
-import { Tooltip } from 'antd/lib';
-
-interface KnowledgeRecord {
-  knowledgeName: string;
-  knowledgeId: string | number;
-  length: string | number; // 使用空间
-  appNames: string; // 关联空间
-  documentSize: number; // 文件数量
-  createTime: string; // 创建时间
-  updateTime: string; // 更新时间
-  status: string | number; // 状态
-}
-
-
-const RevisionToolList: React.FC = () => {
-  const [loading, setLoading] = useState(false);
-
-  const { state, init, handleNameChange, onChangePagination } = store;
-
-  const { reviseList, knowledgeName } = state
-
-
-
-  useEffect(() => {
-    // 模拟加载状态
-    setLoading(true);
-    const t = setTimeout(() => setLoading(false), 250);
-    return () => clearTimeout(t);
-  }, [state.page]);
-
-  const [deleteEvaluationTask, setDeleteEvaluationTask] = useState<boolean>(false); // 是否删除评测任务
-  const [startEvaluationTask, setStartEvaluationTask] = useState<boolean>(false); // 是否开始评测任务
-  const [prewEvaluationTask, setPrewEvaluationTask] = useState<boolean>(false); // 是否雷达图预览
-  const [userInfoAll, setUserInfoAll] = React.useState<any>({});
-
-  const [openEvaluationTaskHistory,setOpenEvaluationTaskHistory] = useState<boolean>(false);
-
-  useEffect(() => {
-    init();
-
-    setDeleteEvaluationTask(LocalStorage.getStatusFlag('evaluationTool:evaluationTask:del'));
-    setStartEvaluationTask(LocalStorage.getStatusFlag('evaluationTool:evaluationTask:start'));
-    setPrewEvaluationTask(LocalStorage.getStatusFlag('evaluationTool:evaluationTask:prew'));
-    setUserInfoAll(LocalStorage.getUserInfo());
-    const handleEvaluationTaskCreate = (e: any) => {
-      if (e.detail && e.detail.platform === 'Task') {
-        setOpenCreateTaskMode(true);
-      }
-    }
-
-    const handlEvaluationTaskHistoryCreate = (e: any) => {
-      if (e.detail && e.detail.platform === 'evaluationTaskHistory') {
-        setOpenEvaluationTaskHistory(true);
-      }
-    }
-
-
-
-    window.addEventListener('evaluationTask', handleEvaluationTaskCreate as EventListener);
-    window.addEventListener('evaluationTaskHistory', handlEvaluationTaskHistoryCreate as EventListener);
-
-    return () => {
-      window.removeEventListener('evaluationTask', handleEvaluationTaskCreate as EventListener);
-      window.removeEventListener('evaluationTaskHistory', handlEvaluationTaskHistoryCreate as EventListener);
-    };
-  }, []);
-
-  const steps = [
-    {
-      title: '创建评测任务',
-      description: '上传本地数据集,选择需要评测的RAG或模型,进行自动评测;',
-      number: 1,
-    },
-    {
-      title: '配置打分模型',
-      description: '选择自动打分的模型,设置不同的评估维度;',
-      number: 2,
-    },
-    {
-      title: '查看评测报告',
-      description: '生成多维度的评测报告,查看详细评分、能力指标、评估日志。',
-      number: 3,
-    }
-  ]
-  const [showGuide, setShowGuide] = React.useState<boolean>(() => localStorage.getItem('revisionToolGuideHidden') !== 'true');
-  const hideGuide = () => { localStorage.setItem('revisionToolGuideHidden', 'true'); setShowGuide(false); };
-  const [record, setRecord] = useState<KnowledgeRecord>({} as KnowledgeRecord);
-  const [openIndicatorRag, setOpenIndicatorRag] = useState<boolean>(false);
-
-  const onBeginEvaluationApi = async (record: any) => {
-    const res: any = await beginEvaluationApi({ ...record });
-    if (res.code === 200) {
-      message.success('评测任务启动成功');
-      init();
-    }
-  }
-
-
-  const columns: ColumnsType<KnowledgeRecord> = [
-    {
-      title: '任务名称/ID', dataIndex: 'taskId', width: 220,
-      render: (text, record: any) => {
-        return <div>
-          {(prewEvaluationTask || userInfoAll.id === record.createBy) ? <div style={{ fontWeight: 600 }} className='cursor-pointer text-primary' onClick={() => {
-            setOpenIndicatorRag(true)
-            setRecord(record)
-          }} >{record.taskName}</div> : <div style={{ fontWeight: 600 }}>{record.taskName}</div>}
-          <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>ID: {record?.taskId}
-          </div>
-        </div>
-      },
-    },
-    {
-      title: '状态', dataIndex: 'status', width: 120,
-      render: (text) => {
-        let color = '#52c41a';
-        let statusText = '';
-        if (text === '0') {
-          color = '';
-          statusText = '未执行';
-        } else if (text === '1') {
-          return <Spin size="small" />;
-        } else if (text === '2') {
-          color = '#52c41a';
-          statusText = '成功';
-        } else if (text === '3') {
-          color = '';
-          statusText = '失败';
-        }
-        return <span style={{ color }}>{statusText}</span>;
-      }
-    },
-    {
-      title: '模型服务', dataIndex: 'model', width: 120
-    },
-    { title: '类型', dataIndex: 'evaluationType', width: 140 },
-    { title: '数据集', dataIndex: 'datesetName', width: 140 },
-    { title: '结果打分', dataIndex: 'resultStore', width: 140 },
-    { title: '创建时间', dataIndex: 'createTime', width: 140 },
-    { title: '开始时间', dataIndex: 'startTime', width: 140 },
-    { title: '结束时间', dataIndex: 'endTime', width: 140 },
-    {
-      title: '操作',
-      dataIndex: 'status',
-      align: 'center',
-      width: 120,
-      render: (val, record: any) => (
-        <p className='flex items-center justify-center'>
-          {(prewEvaluationTask || userInfoAll.id === record.createBy) && <Tooltip title="详情">
-            <Button 
-            type="link"
-            style={{ paddingLeft: 0 }}
-            onClick={()=>{
-             setOpenIndicatorRag(true)
-            setRecord(record)
-          }} icon={<RadarChartOutlined />}/>
-          </Tooltip>}
-          { (record.status === '2'||record.status === '3') &&<Tooltip title="查看评测历史">
-            <Button 
-            type="link"
-            style={{ paddingLeft: 0 }}
-            onClick={()=>{
-            setOpenEvaluationTaskHistory(true);
-            setRecord(record)
-          }} icon={<LayoutOutlined />}/>
-          </Tooltip>}
-          {(startEvaluationTask || userInfoAll.id === record.createBy) && record.status === '0' && <Button type="link" onClick={async () => {
-            onBeginEvaluationApi(record)
-          }}>开始</Button>}
-          {(startEvaluationTask || userInfoAll.id === record.createBy) && record.status !== '0'&&record.status !== '1' && <Tooltip title="重新评测"><Button type="link" icon={<RedoOutlined />} onClick={() => { onBeginEvaluationApi(record) }}></Button></Tooltip>}
-          {(deleteEvaluationTask || userInfoAll.id === record.createBy) && <Button type="link"
-           icon={<DeleteOutlined />}
-           style={{ color: '#ff4d4f', paddingLeft: 0 }}
-            onClick={() => {
-              Modal.confirm({
-                title: '提示',
-                content: `确定删除【${record.taskName}】任务吗?`,
-                okType: 'danger',
-                onOk: async () => {
-                  const res: any = await deleteEvaluationApi(record.taskId);
-                  if (res.code === 200) {
-                    message.success('删除成功');
-                    init();
-                  }
-                }
-              });
-            }} ></Button>}
-
-        </p>
-      ),
-    }
-  ]
-
-  const paginationConfig: TablePaginationConfig = {
-    // 显示数据总量
-    showTotal: (total: number) => {
-      return `共 ${total} 条`;
-    },
-    // 展示分页条数切换
-    showSizeChanger: true,
-    // 指定每页显示条数
-    pageSizeOptions: ['10', '20', '50', '100'],
-    // 快速跳转至某页
-    showQuickJumper: true,
-    current: state.page.page,
-    pageSize: state.page.size,
-    total: state.page.total,
-    onChange: async (page, pageSize) => {
-      await onChangePagination(page, pageSize);
-    },
-  };
-  const [openCreateTaskMode, setOpenCreateTaskMode] = useState(false);
-  const [title, setTitle] = useState('');
-  return (
-    <div style={{ padding: 20, background: '#fff' }}>
-      {showGuide && <> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
-        <div style={{ fontSize: 13, color: '#333', display: 'flex', alignItems: 'center', gap: 6 }}>
-          <BulbOutlined style={{ color: '#faad14' }} />
-          提示: 开始评测模型
-        </div>
-        <CloseOutlined onClick={hideGuide} style={{ color: '#999', cursor: 'pointer' }} />
-      </div>
-        <Step steps={steps} className='mb-3' /></>}
-      <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 12 }} className='items-center'>
-        <p style={{ margin: 0 }}>任务管理</p>
-        <Space.Compact style={{ width: '300px' }}>
-          <Input
-            placeholder='请输入任务名称'
-            value={knowledgeName}
-            onChange={(e) => handleNameChange(e.target.value)}
-          />
-          <Button type="primary" onClick={() => { init() }}><SearchOutlined /></Button>
-        </Space.Compact>
-      </div>
-      {/* 表格 */}
-      <Table
-        columns={columns}
-        dataSource={reviseList}
-        rowKey="taskId"
-        loading={loading}
-        pagination={paginationConfig}
-      />
-      {/* 修订工具弹窗 */}
-      {/* { <ReviseDrawer openDrawer={openDrawer} record={record} title={title} onClose={() => setOpenDrawer(false)} />} */}
-      <CreateTaskMode openCreateTaskMode={openCreateTaskMode} onCancel={() => { setOpenCreateTaskMode(false), init() }}></CreateTaskMode>
-      <IndicatorRag open={openIndicatorRag} record={record} onCancel={() => {
-        setOpenIndicatorRag(false);
-      }}></IndicatorRag>
-      <EvaluationTaskHistory open={openEvaluationTaskHistory}  record={record} onClose={() => {
-        setOpenEvaluationTaskHistory(false);
-      }}></EvaluationTaskHistory>
-    </div>
-  );
-};
-
-export default observer(RevisionToolList);

+ 3 - 3
jk-rag-platform/src/pages/home/style.less → jk-rag-platform/src/pages/home/style.scss

@@ -1,5 +1,5 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // Home 首页样式
 // 说明:使用全局变量,避免硬编码
@@ -7,6 +7,6 @@
 // 卡片背景渐变 - 使用企业蓝色系
 .card-bg-primary {
     background: linear-gradient(135deg, 
-        fade(@primary-color, 5%) 0%, 
-        fade(@primary-color, 1%) 100%);
+        fade($primary-color, 5%) 0%, 
+        fade($primary-color, 1%) 100%);
 }

+ 0 - 116
jk-rag-platform/src/pages/knowledgeLib/detail/components/InfoModal.tsx.bak

@@ -1,116 +0,0 @@
-import * as React from 'react';
-import { Modal, Spin, Form, Input, Select, message } from 'antd';
-import { apis, ModifyDocumentApiParams } from '@/apis';
-
-const FormItem = Form.Item;
-const { Option } = Select;
-
-interface Props {
-    id: string,
-    open: boolean,
-    onClickConfirm: (id: string, data: ModifyDocumentApiParams) => Promise<any>,
-    onClickCancel: () => void,
-};
-
-const InfoModal: React.FC<Props> = (props: Props) => {
-    const {
-        id,
-        open,
-        onClickConfirm,
-        onClickCancel
-    } = props;
-
-    const [form] = Form.useForm();
-
-    const [loading, setLoading] = React.useState<boolean>(false);
-
-    // 获取知识详情
-    const fetchDetail = async () => {
-        try {
-            const res = await apis.fetchTakaiDocumentDetailLibApi(props.id);
-            const { id, name, knowledge_type, custom_separator, sentence_size } = res.data;
-            form.setFieldsValue({
-                id: id,
-                name: name,
-                knowledge_type: knowledge_type,
-                custom_separator: custom_separator,
-                sentence_size: sentence_size,
-            });
-        } catch (error: any) {
-            message.error(error.msg);
-        }
-    };
-
-    const init = async () => {
-        setLoading(true);
-        if (props.id) {
-            await fetchDetail();
-        }
-        setLoading(false);
-    };
-
-    React.useEffect(() => {
-        init();
-    }, []);
-
-    // 点击确定
-    const handleClickConfirm = () => {
-        form.validateFields().then(async (values) => {
-            const data = { ...values };
-            if(values.knowledge_type === '5'){
-                data.custom_separator = [ '"\\n"'];
-                data.sentence_size = 300;
-            }else{
-                data.sentence_size = 20;
-                data.custom_separator = [ '"\\n"'];
-            }
-            console.log(data, 'data');
-            await onClickConfirm(props.id, data);
-        }).catch((error) => {
-            console.error(error);
-        });
-    };
-
-    return (
-        <Modal
-            width={500}
-            title='重命名'
-            maskClosable={false}
-            centered={true}
-            open={open}
-            onOk={handleClickConfirm}
-            onCancel={onClickCancel}
-        >
-            <Spin spinning={loading}>
-                <Form form={form} layout='vertical'>
-                    <FormItem
-                        label='文件名称'
-                        name='name'
-                    >
-                        <Input placeholder='请输入知识库名称' />
-                    </FormItem>
-                    {/* <FormItem
-                        label='知识类型'
-                        name='knowledge_type'
-                        rules={[{ required: true, message: '知识类型不能为空' }]}
-                    >
-                        <Select
-                            style={{ width: '100%' }}
-                            placeholder='请选择知识类型'
-                            allowClear={true}
-                        >
-                            <Option value='1'>1:文章知识:支持pdf,url,docx</Option>
-                            <Option value='2'>2:问答知识-文档:支持pdf,url,docx</Option>
-                            <Option value='3'>3:问答知识-表格:支持xlsx</Option>
-                            <Option value='4'>4:商品库-表格:支持xlsx</Option>
-                            <Option value='5'>5:自定义:支持pdf,url,docx</Option>
-                        </Select>
-                    </FormItem> */}
-                    <div style={{ width: '100%', height: 10 }}></div>
-                </Form>
-            </Spin>
-        </Modal>
-    );
-};
-
-export default InfoModal;

+ 0 - 305
jk-rag-platform/src/pages/knowledgeLib/detail/components/InfoModalSetting.tsx.bak

@@ -1,305 +0,0 @@
-import * as React from 'react';
-import { Modal, Spin, Form, Input, Select, message, Checkbox, GetProp, Tooltip } from 'antd';
-import { InboxOutlined, QuestionCircleOutlined } from '@ant-design/icons';
-
-import { apis, ModifyDocumentSettingApiParams } from '@/apis';
-
-import useAppState from '@/store';
-const { getAppState } = useAppState();
-const { TextArea } = Input;
-
-const FormItem = Form.Item;
-const { Option } = Select;
-
-
-interface Props {
-    id: string,
-    open: boolean,
-    record?: any,
-    onClickConfirm: (id: string, data: ModifyDocumentSettingApiParams) => Promise<any>,
-    onClickCancel: () => void,
-};
-
-const InfoModalSetting: React.FC<Props> = (props: Props) => {
-    const {
-        id,
-        open,
-        onClickConfirm,
-        onClickCancel,
-        record
-    } = props;
-
-    const [form] = Form.useForm();
-
-    const [loading, setLoading] = React.useState<boolean>(false);
-
-    const [isVisibleSlice, setIsVisibleSlice] = React.useState(false);
-    const [titleName, setTitleName] = React.useState<string>('');
-
-    const [standardClass, setStandardClass] = React.useState<any>([]);// 选择分类-下拉框
-    const [parsingTypeList, setParsingTypeList] = React.useState<any>([]);// 解析工具
-    const [parsingTypeListBackup, setParsingTypeListBackup] = React.useState<any[]>([]);// 解析工具-下拉选项-备份数据
-
-    const [splittingOldList, setSplittingOldList] = React.useState<any[]>([]); // 旧的切分规则列表
-    const [splittingTypeList, setSplittingTypeList] = React.useState<any>([]); // 选择切分规则
-    const [standardClassification, setStandardClassification] = React.useState<string>(''); // 选择分类设置
-    const onChangeF = (value: string) => {
-        if (value === '0') {
-            setIsVisibleSlice(!isVisibleSlice);
-        } else {
-            setIsVisibleSlice(false);
-        }
-    };
-    // 获取解析工具
-    const fetchAppstandardClass = async () => {
-        try {
-            const res = await apis.fetchTakaiAppTypeList('parsing_type');
-            const list = res.data.map((item: any) => {
-                return {
-                    label: item.dictLabel,
-                    value: item.dictValue,
-                }
-            });
-            form.setFieldsValue({
-                customSeparator: record?.customSeparator,
-            })
-
-            setParsingTypeList(list);
-            setParsingTypeListBackup(list);
-        } catch (error: any) {
-            console.error(error);
-        }
-    }
-    // 获取向默认切分规则
-    const fetchsplittingType = async () => {
-        try {
-            const res = await apis.fetchTakaiAppTypeList('splitting_type');
-            const list = res.data.map((item: any) => {
-                return {
-                    label: item.dictLabel,
-                    value: item.dictValue,
-                    remark: item.remark,
-                }
-            });
-            if (record.customSeparator === '0') {
-                setIsVisibleSlice(!isVisibleSlice);
-            }
-            if (record.parsingType === '2') {
-                const newList = list.filter((item: any) => item.value !== '3');
-                setSplittingTypeList(newList);
-            }else{
-                setSplittingTypeList(list);
-            }
-            setSplittingOldList(list);
-        } catch (error: any) {
-            console.error(error);
-        }
-    }
-    const init = async () => {
-        setLoading(true);
-        if (props.id) {
-            fetchsplittingType()
-            fetchAppstandardClass()
-            
-        }
-        setLoading(false);
-        const res: any = await getAppState();
-        const reslist = res.filter((item: any) => {
-            if (record?.standardClassification === '0' || record?.standardClassification === '1') {
-                if (item.value === '0' || item.value === '1') {
-                    return item
-                }
-            }
-            if (record?.standardClassification === '3') {
-                if (item.value === '3') {
-                    return item
-                }
-            }
-            if (record?.standardClassification === '2') {
-                if (item.value === '2') {
-                    return item
-                }
-            }
-        })
-        setStandardClass(reslist);
-    };
-
-    React.useEffect(() => {
-        init();
-        form.setFieldsValue({
-            parsingType: record?.parsingType,
-            customSeparator: record?.customSeparator,
-            standardClassification: record?.standardClassification,
-            sliceValue: record?.sliceValue,
-            name: record?.name,
-            remark: record?.remark,
-        })
-        setStandardClassification(record?.standardClassification);
-    }, []);
-
-    // 点击确定
-    const handleClickConfirm = () => {
-        form.validateFields().then(async (values) => {
-            const data = { ...record, ...values, konowledge_id: record.knowledgeId };
-            console.log(loading, 'loading');
-            await onClickConfirm(props.id, data);
-        }).catch((error) => {
-            console.error(error);
-        })
-    };
-
-    return (
-        <Modal
-            width={600}
-            title='知识配置'
-            maskClosable={false}
-            centered={true}
-            open={open}
-            onOk={handleClickConfirm}
-            onCancel={onClickCancel}
-            confirmLoading={loading}
-        >
-            <Spin spinning={loading}>
-                <Form form={form} layout='vertical'>
-                    <FormItem
-                        label='分类设置'
-                        name='standardClassification'
-                    >
-                        <Select
-                            style={{ width: '100%' }}
-                            allowClear={true}
-                            optionLabelProp="label"
-                            placeholder='请选择分类设置'
-                            onChange={(e) => {
-                                // 切换分类时,清空已选本地文件與 OSS 列表
-                                setStandardClassification(e);
-                                if (e === '0') {
-                                    const newSplittingList = splittingOldList.filter((item: any) => item.value === '3');
-                                    setSplittingTypeList(newSplittingList);
-                                    form.setFieldsValue({
-                                        customSeparator: newSplittingList[0]?.value,
-                                    });
-
-                                    const newParsingTypeList = parsingTypeListBackup.filter((item: any) => item.value === '0');
-                                    setParsingTypeList(newParsingTypeList);
-                                    form.setFieldsValue({
-                                        parsingType: newParsingTypeList[0]?.value,
-                                    })
-                                } else {
-                                    setSplittingTypeList(splittingOldList);
-                                    setParsingTypeList(parsingTypeListBackup);
-                                }
-                                if (e === '1') {
-                                    // 通用规范
-                                    form.setFieldsValue({
-                                        customSeparator: '2',
-                                    });
-                                }
-                                form.setFieldsValue({
-                                    name: '',
-                                });
-                            }}
-                        >
-                            {
-                                standardClass.map((item: any, index: any) => {
-                                    return <Option value={item.value} key={index} label={item.label}>
-                                        {item.label}
-                                        <Tooltip title={item.remark} >
-                                            <QuestionCircleOutlined style={{ color: '#FF9800', marginLeft: '3px' }} />
-                                        </Tooltip>
-                                    </Option>
-                                })
-                            }
-                        </Select>
-                    </FormItem>
-
-                    {(standardClassification === '0' || standardClassification === '1') && <><FormItem
-                        label='解析工具'
-                        name='parsingType'
-                    >
-                        {/* <p>{standardClass}</p> */}
-                        <Select
-                            style={{ width: '100%' }}
-                            placeholder='请选择解析设置'
-                            allowClear={true}
-                            onChange={(e) => {
-                                if (e === '2') {
-                                    const newSplittingList = splittingOldList.filter((item: any) => item.value !== '3');
-                                    setSplittingTypeList(newSplittingList);
-                                    form.setFieldsValue({
-                                        customSeparator: newSplittingList[0]?.value,
-                                    });
-                                } else {
-                                    setSplittingTypeList(splittingOldList);
-                                }
-                            }}
-                        >
-                            {
-                                parsingTypeList.map((item: any, index: any) => {
-                                    return <Option value={item.value} key={index}>
-                                        {item.label}
-                                    </Option>
-                                })
-                            }
-                        </Select>
-                    </FormItem>
-                        <FormItem
-                            label='切片设置'
-                            name='customSeparator'
-                        >
-                            <Select
-                                style={{ width: '100%' }}
-                                allowClear={true}
-                                placeholder='请选择切片设置'
-                                onChange={onChangeF}
-                                optionLabelProp="label"
-                            >
-                                {
-                                    splittingTypeList.map((item: any, index: any) => {
-                                        return <Option value={item.value} key={index} label={item.label}>
-                                            {item.label}
-                                            <Tooltip title={item.remark} >
-                                                <QuestionCircleOutlined style={{ color: '#FF9800', marginLeft: '3px' }} />
-                                            </Tooltip>
-                                        </Option>
-                                    })
-                                }
-                            </Select>
-                        </FormItem></>}
-                    {isVisibleSlice &&
-                        <FormItem
-                            label='分隔符'
-                            name='sliceValue'
-                            rules={[{ required: true, message: '自定义切片设置不能为空' }]}
-                        >
-                            <Input max={1024}
-                            />
-                        </FormItem>
-                    }
-                    {standardClassification === '3' && <><FormItem
-                        label='文件名称'
-                        name='name'
-                        rules={[{ required: true, message: '文件名称不能为空', whitespace: true }]}
-                    >
-                        <Input placeholder='请输入文件名称' />
-                    </FormItem>
-                        <FormItem
-                            label='描述'
-                            name='remark'
-                            rules={[{ required: true, message: '描述不能为空', whitespace: true }]}
-                        >
-                            <TextArea
-                                placeholder='请输入描述信息'
-                                showCount={true}
-                                rows={6}
-                                maxLength={500}
-                            />
-                        </FormItem></>}
-                    <div style={{ width: '100%', height: 10 }}></div>
-                </Form>
-            </Spin>
-        </Modal>
-    );
-};
-
-export default InfoModalSetting;

+ 0 - 1573
jk-rag-platform/src/pages/knowledgeLib/detail/components/MdModal.tsx.bak

@@ -1,1573 +0,0 @@
-import React, {
-  useCallback,
-  useEffect,
-  useMemo,
-  useRef,
-  useState,
-} from 'react';
-import {
-  Button,
-  Empty,
-  Modal,
-  Drawer,
-  Space,
-  Spin,
-  Typography,
-  Input,
-  message,
-  Upload,
-  UploadProps,
-  Checkbox,
-  Tooltip,
-  Popconfirm,
-  Image,
-  Tabs
-} from 'antd';
-import { SearchOutlined, MenuFoldOutlined, MenuUnfoldOutlined, FileTextOutlined, DeleteOutlined, QuestionOutlined, QuestionCircleOutlined, FullscreenOutlined } from '@ant-design/icons';
-import MarkdownIt from 'markdown-it';
-
-import { apis } from '@/apis';
-import config, { getHeaders } from '@/apis/config';
-import './style.less'
-import { Document, Page, pdfjs } from 'react-pdf';
-import workerSrc from '@/assets/pdf.worker.min.js?url';
-import 'react-pdf/dist/Page/TextLayer.css';
-import 'react-pdf/dist/Page/AnnotationLayer.css';
-import { Console } from 'console';
-pdfjs.GlobalWorkerOptions.workerSrc = workerSrc;
-// pdfjs.GlobalWorkerOptions.workerSrc = new URL(
-//   'pdfjs-dist/build/pdf.worker.min.mjs',
-//   import.meta.url,
-// ).toString();
-import zHuifu from '@/assets/public/z-huifu.svg'
-import zJiexi from '@/assets/public/z-jiexi.svg'
-import zQiefen from '@/assets/public/z-qiefen.svg'
-import zXinzeng from '@/assets/public/z-xinzeng.svg'
-import zHebing from '@/assets/public/z-hebing.svg'
-import zTuozhan from '@/assets/public/z-tuozhan.svg'
-import zDuoban from '@/assets/public/z-duoban.svg'
-import tubing from '@/assets/public/tubing.png'
-import rfq from '@/assets/public/rfq.png'
-const { TextArea } = Input;
-
-const marked = new MarkdownIt({ html: true, typographer: true });
-
-type KnowledgeRecord = {
-  knowledgeId: string;
-  name: string;
-  length?: number;
-  wordNum?: number;
-  documentSize?: number;
-  markUrl?: string;
-  previewUrl?: string;
-  url?: string;
-  [key: string]: any;
-  status?: string;
-  standardClassification?: string
-  suffix: string
-};
-
-interface ModuleItem {
-  id: string;
-  content: string;
-  mdContent: string;
-  sliceId?: string;
-  saveLoading?: boolean;
-  delLoading?: boolean;
-  mediaList?: any[];
-  uploading?: boolean;
-}
-
-const initialModules: ModuleItem[] = [
-  {
-    id: 'm1',
-    content:
-      '### 新增切片',
-    mdContent: '### 新增切片'
-  }
-];
-interface MdModalProps {
-  open: boolean;
-  detailDocument?: any;
-  knowledgeDetail?: any;
-  onCancel: (type?: number) => void;
-}
-
-import store from './store';
-
-
-
-
-const LazyPage = React.memo(
-    ({
-        pageNumber,
-        width,
-        containerRef,
-    }: {
-        pageNumber: number;
-        width: number;
-        containerRef: React.RefObject<HTMLDivElement>;
-    }) => {
-        const elRef = React.useRef<HTMLDivElement | null>(null);
-        const [visible, setVisible] = React.useState(false);
-
-        React.useEffect(() => {
-            const el = elRef.current;
-            const root = containerRef.current;
-            if (!el || !root) return;
-
-            const io = new IntersectionObserver(
-                entries => {
-                    entries.forEach(e => e.isIntersecting && setVisible(true));
-                },
-                { root, rootMargin: '400px' }
-            );
-
-            io.observe(el);
-            return () => io.disconnect();
-        }, [containerRef]);
-
-        return (
-            <div ref={elRef} style={{ background: '#fff' }}>
-                {visible ? (
-                    <Page
-                        pageNumber={pageNumber}
-                        // width={width}
-                        renderTextLayer={false}
-                        renderAnnotationLayer={false}
-                        className='pdf-page'
-                    />
-                ) : (
-                    <div
-                        style={{
-                            width: '100%',
-                            height: Math.floor(width * 1.3),
-                            background: '#fafafa',
-                        }}
-                    />
-                )}
-            </div>
-        );
-    }
-);
-
-
-
-
-const MdModal: React.FC<MdModalProps> = (props) => {
-
-  const {
-    state,
-    onSetStarts
-  } = store;
-  useEffect(() => {
-    loadKnowledgeList(1, '');
-  }, [state.starts])
-
-  const { open, onCancel, detailDocument, knowledgeDetail } = props;
-  const [documents, setDocuments] = useState<KnowledgeRecord[]>([]);
-  const [selectedDoc, setSelectedDoc] = useState<KnowledgeRecord | null>(null); // 当前选中的文件
-  const selectedRefDoc = useRef<KnowledgeRecord | null>(null); // 当前选中的文件
-  const [pagination, setPagination] = useState({
-    pageNum: 1,
-    pageSize: 20,
-    total: 0,
-  });
-  const [listLoading, setListLoading] = useState(false);
-  const [sliceLoading, setSliceLoading] = useState(true);
-  const [pdfLoading, setPdfLoading] = useState(true);
-  const listLoadingRef = useRef(false);
-  const [hasMore, setHasMore] = useState(true);
-  const listContainerRef = useRef<HTMLDivElement>(null);
-  // 切片列表
-  const [modules, setModules] = useState<ModuleItem[]>([]);
-  const [oneModules, setOneModules] = useState('');
-  // 默认选中第一条数据
-  const [activeId, setActiveId] = useState<string | null>(initialModules[0]?.id);
-  const [fromEditor, setFromEditor] = useState(true);
-  const editorContainerRef = useRef<HTMLDivElement>(null);
-  const previewContainerRef = useRef<HTMLDivElement>(null);
-  const [keyword, setKeyword] = useState('');
-  const [isListCollapsed, setIsListCollapsed] = useState(false);// 文档列表是否展开
-  const [isPDfCollapsed, setIsPDfCollapsed] = useState(false);// PDF预览是否展开
-  const [delSliceIds, setDelSliceIds] = useState<string[]>([]);
-  const [selectedModuleIds, setSelectedModuleIds] = useState<string[]>([]); // 用于合并的多选模块
-
-  const [pdfBuffer, setPdfBuffer] = useState<any>(null);
-  const [numPages, setNumPages] = useState<number>(0);
-  // 全屏预览状态与缩放
-  const [pdfFullVisible, setPdfFullVisible] = useState<boolean>(false);
-  const [pdfFullScale, setPdfFullScale] = useState<number>(1.5);
-
-  const [currentDocumentId, setCurrentDocumentId] = useState<string | null>(null);
-  const pdfScrollRef = useRef<HTMLDivElement | null>(null);
-  const [pageWidth, setPageWidth] = useState<number>(600);
-
-  // 替换渲染数据
-  const customRender = (text: string, mdImgUrlList: any[]) => {
-    // 比如:把 "我是图片" 替换成一张图片
-    mdImgUrlList?.forEach(item => {
-      text = text.replace(new RegExp(item.originText, 'g'), `<img src="${item.mediaUrl}" alt="${item.originText}" />`);
-    })
-    return text;
-  }
-  // 处理切片数据
-  const sliceDataProcess = (data: any[]) => {
-    data.forEach((item: any) => {
-      item.content = item.sliceText
-      item.id = item.sliceId,
-        item.mdContent = customRender(item.sliceText, item.mediaList)
-    })
-    setModules(data || [])
-  }
-  // 获取切片列表
-  const onGetSliceListByDocumentId = async (item: any) => {
-    const res: any = await apis.getSliceListByDocumentId(item.documentId)
-    if (res.code === 200 && res.data) {
-      if (res.data.length > 0) {
-        res.data.forEach((item: any) => {
-          item.saveLoading = false;
-          item.delLoading = false;
-          item.uploading = false;
-        })
-        sliceDataProcess(res.data)
-      } else {
-        setModules(initialModules)
-      }
-      setSliceLoading(false);
-    }
-  }
-  // 获取MD原文数据
-  const onGetMarkDownSliceByDocumentId = async (documentId: string) => {
-    const res: any = await apis.getMarkDodnSliceByDocumentId(documentId);
-    if (res.code === 200 && res.data) {
-      setOneModules(res.data[0] || '');
-    }
-  }
-
-  // 获取文档详情
-  const [customSeparator, setCustomSeparator] = useState<string>('');
-  const onFetchDocumentDetailLibApi = async (documentId: string) => {
-    const res: any = await apis.fetchTakaiDocumentDetailLibApi(documentId);
-    if (res.code === 200 && res.data) {
-      setQaChecked(res.data.qaChecked || false);
-      setRelatedQuestionsEnabled(res.data.relatedQuestionsEnabled || false);
-      setSummaryGenerationEnabled(res.data.summaryGenerationEnabled || false);
-      setParentGenerationEnabled(res.data.parentGenerationEnabled || false);
-      setCustomSeparator(res.data.customSeparator || '');
-    }
-  }
-  // 获取PDF
-  const onExportPdfStream = async (item: any) => {
-    setModules([]) // 清空切片
-    setPdfBuffer(null);// 清空PDF预览
-    setTabKey('slices') // 清空智能切片模式
-    setSelectedModuleIds([]); // 清空合并切片以及需要删除的切片
-    setSliceLoading(true)
-    setSelectedDoc(item);
-    selectedRefDoc.current = item;
-    setCurrentDocumentId(item.documentId);
-    setPdfLoading(false);
-    if (item?.suffix !== 'md') {
-      setPdfLoading(true);
-      const ids = [item.documentId]
-      const blob = await apis.exportPdfStream(ids)
-      setPdfLoading(false);
-      const url = URL.createObjectURL(blob);
-      setPdfBuffer(url);
-    }
-
-    onGetSliceListByDocumentId(item)
-    onGetMarkDownSliceByDocumentId(item.documentId);
-    onFetchDocumentDetailLibApi(item.documentId);
-  }
-      // 计算渲染宽度以降低每页 canvas 分辨率开销(与 prevewSlice 行为一致)
-    useEffect(() => {
-      const update = () => {
-        const el = pdfScrollRef.current;
-        if (el) {
-          const w = el.clientWidth - 20;
-          setPageWidth(Math.max(200, w));
-        }
-      };
-      update();
-      window.addEventListener('resize', update);
-      return () => window.removeEventListener('resize', update);
-    }, []);
-
-  //PDF渲染列表
-  const renderPdfPreview = () => {
-    function onLoadSuccess({ numPages }: { numPages: number }) {
-      setNumPages(numPages);
-    }
-    if (!pdfBuffer) {
-      return (
-        <div className="flex h-full items-center justify-center">
-          <Empty description="请选择左侧的文档" image={Empty.PRESENTED_IMAGE_SIMPLE} />
-        </div>
-      );
-    }
-
-    return (
-      <div>
-        <style>{`
-        .react-pdf__Page__canvas {
-          width: 100% !important;
-          padding-left:0 !important;
-          height: auto !important;
-        }
-        .react-pdf__Page__textContent,.annotationLayer { 
-          padding:0; 
-          margin:0;
-          width: 100% !important;
-          padding-left:0 !important;
-          height: auto !important;
-         }
-      `}</style>
-          {pdfBuffer && (
-            <Document file={pdfBuffer} 
-              renderMode='canvas'
-              onLoadSuccess={onLoadSuccess}
-              loading={<div className="text-center py-8">加载中...</div>}
-            >
-              {Array.from(new Array(numPages), (_, idx) => (
-              //    <Page key={idx} pageNumber={idx + 1}
-              //   renderAnnotationLayer={false}
-              //   renderTextLayer={true}
-              // />
-                <LazyPage key={`page-${idx}`} pageNumber={idx + 1} width={Math.floor(pageWidth)} containerRef={pdfScrollRef} />
-              ))}
-            </Document>
-          )}
-      </div>)
-  }
-  // 获取文档列表
-  const loadKnowledgeList = useCallback(
-    async (targetPage = 1, searchKeyword?: string) => {
-      if (listLoadingRef.current) return;
-      listLoadingRef.current = true;
-      setListLoading(true);
-      try {
-        const res: any = await apis.sliceDocumentList({
-          pageNum: targetPage,
-          pageSize: pagination.pageSize,
-          knowledge_id: knowledgeDetail.knowledgeId,
-          name: searchKeyword !== undefined ? searchKeyword : ''
-        });
-        const rows = res?.rows || [];
-        setDocuments((prev) => {
-          if (targetPage === 1) {
-            const firstDoc = selectedDoc || detailDocument;
-            if (firstDoc && firstDoc.documentId) {
-              const isExist = rows.filter((item: any) => item.documentId !== firstDoc.documentId);
-              return [firstDoc, ...isExist];
-            }
-            return rows;
-          } else {
-            return [...prev, ...rows.filter((row: KnowledgeRecord) => prev.every((p) => p.knowledgeId !== row.knowledgeId))]
-          }
-        });
-        setPagination((prev) => ({
-          ...prev,
-          pageNum: targetPage,
-          total: res?.total || 0,
-        }));
-        setHasMore(targetPage * pagination.pageSize < (res?.total || 0));
-      } catch (error: any) {
-        message.error(error?.msg || '获取文档列表失败');
-      } finally {
-        listLoadingRef.current = false;
-        setListLoading(false);
-      }
-    },
-    [pagination.pageSize, detailDocument, selectedDoc]
-  );
-
-  useEffect(() => {
-    loadKnowledgeList(1, '');
-    onExportPdfStream(detailDocument);
-  }, [detailDocument]);
-  // 列表滚动加载更多
-  const handleListScroll = useCallback(() => {
-    const container = listContainerRef.current;
-    if (
-      !container ||
-      listLoadingRef.current ||
-      !hasMore ||
-      documents.length === 0
-    ) {
-      return;
-    }
-    const nearBottom =
-      container.scrollTop + container.clientHeight >=
-      container.scrollHeight - 40;
-    if (nearBottom) {
-      loadKnowledgeList(pagination.pageNum + 1);
-    }
-  }, [documents.length, hasMore, loadKnowledgeList, pagination.pageNum]);
-
-  useEffect(() => {
-    const container = listContainerRef.current;
-    if (!container) return;
-    container.addEventListener('scroll', handleListScroll);
-    return () => container.removeEventListener('scroll', handleListScroll);
-  }, [handleListScroll, isListCollapsed]);
-  // 最后两列滚动
-  const scrollToElement = (
-    containerRef: React.RefObject<HTMLDivElement>,
-    element: HTMLElement | null
-  ) => {
-    if (!containerRef.current || !element) return;
-    const containerTop = containerRef.current.getBoundingClientRect().top;
-    const elemTop = element.getBoundingClientRect().top;
-    containerRef.current.scrollBy({
-      top: elemTop - containerTop - 16,
-      behavior: 'smooth',
-    });
-  };
-
-  const setActiveModule = (id: string, editorSide: boolean) => {
-    setActiveId(id);
-    setFromEditor(editorSide);
-  };
-
-  useEffect(() => {
-    if (!activeId) return;
-    const targetEditorModule = editorContainerRef.current?.querySelector(
-      `[data-id="${activeId}"]`
-    ) as HTMLElement | null;
-    const targetPreviewModule = previewContainerRef.current?.querySelector(
-      `[data-id="${activeId}"]`
-    ) as HTMLElement | null;
-    if (fromEditor && targetPreviewModule) {
-      scrollToElement(previewContainerRef, targetPreviewModule);
-    }
-    if (!fromEditor && targetEditorModule) {
-      scrollToElement(editorContainerRef, targetEditorModule);
-    }
-  }, [activeId, fromEditor]);
-  // 输入框选中操作
-  const handleContentChange = (id: string, newContent: string, newMediaList?: any) => {
-    setModules((prev) =>
-      prev.map((module: any) => {
-        if (module.id === id) {
-          if (newMediaList) {
-            return { ...module, content: newContent, mdContent: customRender(newContent, newMediaList || module.mediaList), mediaList: newMediaList || module.mediaList }
-          } else {
-            return { ...module, content: newContent, mdContent: customRender(newContent, module.mediaList) }
-          }
-        } else {
-          return module
-        }
-      }));
-  };
-  const setSaveLoadingChange = (id: string, flag: boolean) => {
-    setModules((prev) =>
-      prev.map((module: any) => {
-        if (module.id === id) {
-          return { ...module, saveLoading: flag }
-        } else {
-          return module
-        }
-      }));
-  };
-  const setdelLoadingChange = (id: string, flag: boolean) => {
-    setModules((prev) =>
-      prev.map((module: any) => {
-        if (module.id === id) {
-          return { ...module, delLoading: flag }
-        } else {
-          return module
-        }
-      }));
-  };
-  // 恢复内容
-  const handleRestore = (item: any) => {
-    handleContentChange(item.id, item.sliceText);
-  };
-
-  // 新增模块
-  const handleAddModule = (currentId?: string) => {
-    let createdModule: ModuleItem | any = null;
-    setModules((prev) => {
-      const currentIndex = currentId
-        ? prev.findIndex((module) => module.id === currentId)
-        : prev.length - 1;
-      const nextIndex = currentIndex >= 0 ? currentIndex + 1 : prev.length;
-      createdModule = {
-        id: `m${Date.now()}`,
-        content: '### 新增切片',
-        mdContent: '### 新增切片',
-      };
-      const nextModules = [...prev];
-      nextModules.splice(nextIndex, 0, createdModule);
-      return nextModules;
-    });
-    if (createdModule) {
-      setActiveModule(createdModule.id, true);
-    }
-  };
-  // 合并模块
-  const handleMergeModule = (currentModule: any) => {
-    if (!currentModule) return;
-
-    // 如果没有选中任何模块,提示用户
-    if (selectedModuleIds.length === 0) {
-      message.warning('请先勾选要合并的模块');
-      return;
-    }
-
-    // 获取所有需要合并的模块(包括当前模块),按照在modules数组中的顺序
-    const modulesToMerge = modules.filter(m =>
-      m.id === currentModule.id || selectedModuleIds.includes(m.id)
-    );
-
-    if (modulesToMerge.length <= 1) {
-      message.warning('至少需要选择一个其他模块进行合并');
-      return;
-    }
-
-    // 按照从上到下的顺序合并内容
-    const mergedContent = modulesToMerge.map(m => m.content).join('\n');
-
-    // 合并所有模块的 mediaList
-    const allMediaLists = modulesToMerge
-      .map(m => m.mediaList || [])
-      .flat();
-
-    // 去重 mediaList(根据 originText 或其他唯一标识)
-    const uniqueMediaList = allMediaLists.filter((item, index, self) =>
-      index === self.findIndex(t => t.originText === item.originText)
-    );
-
-    // 收集要删除的 sliceIds
-    const idsToDelete = modulesToMerge
-      .filter(m => m.id !== currentModule.id && m.sliceId)
-      .map(m => m.sliceId!);
-
-    // 更新 modules:保留当前模块并更新其内容,删除其他被合并的模块
-    setModules((prev) => {
-      const next = prev.filter(m =>
-        m.id === currentModule.id || !selectedModuleIds.includes(m.id)
-      );
-
-      // 更新当前模块的内容
-      return next.map(m => {
-        if (m.id === currentModule.id) {
-          return {
-            ...m,
-            content: mergedContent,
-            mdContent: customRender(mergedContent, uniqueMediaList),
-            mediaList: uniqueMediaList.length > 0 ? uniqueMediaList : undefined
-          };
-        }
-        return m;
-      });
-    });
-
-    // 添加到删除列表
-    if (idsToDelete.length > 0) {
-      setDelSliceIds((prev) => [...prev, ...idsToDelete]);
-    }
-
-    // 清空选中状态
-    setSelectedModuleIds([]);
-
-    message.success(`已合并 ${modulesToMerge.length} 个模块`);
-  };
-
-  // 切分模块
-  const handleSplitModule = (module?: any) => {
-    const target = module || modules.find((m) => m.id === activeId);
-    if (!target) return;
-
-    const text = (target.content || '').toString();
-    const pos = Math.max(0, Math.min(cursorEndPosition || 0, text.length));
-
-    // 如果位置在开头或结尾,则不进行切分
-    if (pos === 0 || pos === text.length) {
-      message.warning('当前位置不能切分内容');
-      return;
-    }
-
-    const firstPart = text.slice(0, pos);
-    const secondPart = text.slice(pos);
-
-    // 尝试保留 mediaList(如果有),这里简单复制到两个片段,确保渲染时可用
-    const mediaList = target.mediaList ? [...target.mediaList] : undefined;
-
-    const newModule = {
-      id: `m${Date.now()}`,
-      content: secondPart,
-      mdContent: customRender(secondPart, mediaList || []),
-      saveLoading: false,
-      delLoading: false,
-      mediaList: mediaList ? [...mediaList] : undefined,
-    } as any;
-
-    setModules((prev) => {
-      const idx = prev.findIndex((m) => m.id === target.id);
-      if (idx === -1) return prev;
-      const next = [...prev];
-      // 更新当前为第一段
-      next[idx] = { ...next[idx], content: firstPart, mdContent: customRender(firstPart, mediaList || []) };
-      // 在其后插入新模块(第二段)
-      next.splice(idx + 1, 0, newModule);
-      return next;
-    });
-
-    // 将焦点切到新创建的模块(编辑端)
-    setActiveModule(newModule.id, true);
-    // 重置光标位置
-    setCursorEndPosition(0);
-  };
-  // 保存
-  const handleSaveModule = async (item: any, index: number) => {
-    if (!item.content || item.content.trim() === '') {
-      return message.warning('切片内容不能为空');
-    }
-    const params = {
-      ...item,
-      sliceText: item.content,
-      index: index + 1,
-    }
-    setSaveLoadingChange(item.id, true);
-    try {
-      if (item.sliceId) {
-        const res: any = await apis.setupdateSliceInfo(params);
-        if (res.code === 200) {
-          message.success('修改成功');
-        }
-      }
-      if (!item.sliceId) {
-        params['documentId'] = detailDocument.documentId;
-        params['knowledgeId'] = detailDocument.knowledgeId;
-        const res: any = await apis.addSlice(params);
-        if (res.code === 200) {
-          message.success('保存成功');
-        }
-      }
-      setSaveLoadingChange(item.id, false);
-    } catch (error: any) {
-      setSaveLoadingChange(item.id, false);
-    }
-  };
-  // 删除模块
-  const handleDeleteModule = async (item: any) => {
-    if (item.sliceId) {
-      setdelLoadingChange(item.id, true);
-      const res = await apis.deleteTakaiSlice(item.sliceId, item.knowledgeId, item.documentId);
-      setdelLoadingChange(item.id, false);
-      if (res.code === 200) {
-        setModules((prev) => {
-          if (prev.length === 1) {
-            message.warning('至少保留一个模块');
-            return prev;
-          }
-          const filtered = prev.filter((module) => module.id !== item.id);
-          if (activeId === item.id && filtered.length) {
-            setActiveModule(filtered[0].id, true);
-          }
-          return filtered;
-        });
-        setDelSliceIds((prev) => {
-          const delIds = [...prev];
-          if (item && item.sliceId) {
-            delIds.push(item.sliceId);
-          }
-          return delIds;
-        })
-      }
-    } else {
-      setModules((prev) => {
-        if (prev.length === 1) {
-          message.warning('至少保留一个模块');
-          return prev;
-        }
-        const filtered = prev.filter((module) => module.id !== item.id);
-        if (activeId === item.id && filtered.length) {
-          setActiveModule(filtered[0].id, true);
-        }
-        return filtered;
-      });
-      setDelSliceIds((prev) => {
-        const delIds = [...prev];
-        if (item && item.sliceId) {
-          delIds.push(item.sliceId);
-        }
-        return delIds;
-      })
-    }
-  };
-  // 切换模块选中状态
-  const handleToggleModuleSelection = (moduleId: string) => {
-    setSelectedModuleIds((prev) => {
-      if (prev.includes(moduleId)) {
-        return prev.filter(id => id !== moduleId);
-      } else {
-        return [...prev, moduleId];
-      }
-    });
-  };
-
-  // 处理markdown编辑器
-  const [cursorEndPosition, setCursorEndPosition] = React.useState<number>(0);
-  const [SliceDetail, setSliceDetail] = React.useState<any>(null);
-  const [upload, setUpload] = React.useState<boolean>(false);
-
-  const upDateButton = (module?: any) => {
-    // 上传图片配置
-    const uploadImageConfig: UploadProps = {
-      name: 'file',
-      action: `/api/resource/oss/upload`,
-      method: 'POST',
-      headers: getHeaders(),
-      accept: ['.png', '.jpg', '.jpeg'].join(','),
-      multiple: true,
-      maxCount: 5,
-      showUploadList: false,
-    };
-    return (
-      <Upload
-        {...uploadImageConfig}
-        onChange={async (info) => {
-          const file = info.file;
-          // 上传中,设置模块 uploading 状态
-          if (file.status === 'uploading' && module) {
-            setModules((prev) => prev.map((m) => (m.id === module.id ? { ...m, uploading: true } : m)));
-          }
-          const insertToSliceText = (item: any) => {
-            const slice_text = SliceDetail?.content || '';
-            // 获取当前光标位置
-            const position = cursorEndPosition;
-
-            let newValue = '';
-
-            if (!slice_text) {
-              newValue = item.originText;
-            } else {
-              newValue = slice_text.slice(0, position) + item.originText + slice_text.slice(position);
-            }
-            const falgImages = SliceDetail.mediaList ? [...SliceDetail.mediaList, item] : [item];
-            handleContentChange(SliceDetail.id, newValue, falgImages)
-            // form.setFieldsValue({ slice_text: newValue });
-          }
-          if (file.status === 'done') {// 上传成功
-            const { code, msg, data } = file.response;
-            if (code === 200) {
-              const res: any = await apis.uploadSliceImage(detailDocument, {
-                ...data,
-                name: data.fileName,
-              });
-              if (res.code === 200) {
-                insertToSliceText(res.data)
-                // 上传完成,清除 uploading 状态
-                if (module) {
-                  setModules((prev) => prev.map((m) => (m.id === module.id ? { ...m, uploading: false } : m)));
-                }
-                message.success('上传成功');
-              }
-            } else {
-              message.error(msg);
-            }
-          } else if (file.status === 'error') {// 上传失败
-            // 上传失败,清除 uploading 状态
-            if (module) {
-              setModules((prev) => prev.map((m) => (m.id === module.id ? { ...m, uploading: false } : m)));
-            }
-            message.error('上传失败');
-          }
-        }}
-      >
-        <Tooltip title="解析图片">
-          <Button size='small' style={{ border: 'none', padding: 0 }} loading={module?.uploading}>
-            {/* 解析图片 */}
-            <Image
-              width={23}
-              height={23}
-              src={zJiexi}
-              preview={false}
-              className='cursor-pointer'
-              onClick={() => handleRestore(module)}
-            />
-          </Button>
-        </Tooltip>
-      </Upload>
-    )
-  }
-  // 渲染编辑模块
-  const renderEditorModules = () => {
-    const textAreaProps = {
-      autoSize: { minRows: 4, maxRows: 20 },
-      className:
-        '!bg-white !border-none !shadow-none !p-0 !text-sm !leading-6 focus:!shadow-none',
-    };
-
-    return modules.map((module: any, index) => (
-      <div
-        key={module.id}
-        data-id={module.id}
-        className={`rounded-2xl mb-[10px] border border-transparent p-4 bg-white transition-all duration-200 relative
-         
-           ${activeId === module.id
-            ? 'border-blue-300 ring-2 ring-blue-200'
-            : 'hover:border-blue-200'
-          }`}
-      >
-        {/* 左上角废弃*/}
-        {module.revisionStatus === '0' && <div className="absolute top-[-0px] right-4 z-10">
-          <Tooltip title='已废弃'>
-            <Image width={16} height={16} src={rfq} preview={false} className='cursor-pointer ml-2' />
-          </Tooltip>
-        </div>}
-        <div className={`relative w-full h-full ${module.revisionStatus === '0' ? 'pointer-events-none cursor-not-allowed' : ''}`}>
-          {/* 右上角多选框 */}
-          {module.revisionStatus !== '0' && <div className="absolute top-[-15px] right-1 z-10">
-            <Checkbox
-              checked={selectedModuleIds.includes(module.id)}
-              onChange={() => handleToggleModuleSelection(module.id)}
-              onClick={(e) => e.stopPropagation()}
-            />
-          </div>}
-          <TextArea
-            {...textAreaProps}
-            value={module.content}
-            onChange={(e) => handleContentChange(module.id, e.target.value)}
-            onFocus={() => setActiveModule(module.id, true)}
-            onBlur={(e: React.FocusEvent<HTMLTextAreaElement>) => {
-              const ta = e.target as HTMLTextAreaElement;
-              const start = ta.selectionStart;
-              const end = ta.selectionEnd;
-              setCursorEndPosition(end);
-              setSliceDetail(module)
-              // setCursor({ start, end });
-              // console.log('blur cursor', start, end, ta.value);
-            }}
-          />
-          {activeId === module.id && (
-            <Space size={[8, 8]} wrap className="mt-3 w-full flex justify-end">
-              {/* 恢复初始 */}
-              {module.sliceId &&
-                <Tooltip title="恢复初始内容">
-                  <Image
-                    width={23}
-                    height={23}
-                    src={zHuifu}
-                    preview={false}
-                    className='cursor-pointer'
-                    onClick={() => handleRestore(module)}
-                  />
-                </Tooltip>
-              }
-              {upDateButton(module)}
-              <Tooltip title="切分模块">
-                <Button
-                  style={{ border: 'none', padding: 0 }}
-                  size="small"
-                  type="primary"
-                  ghost
-                  onClick={() => handleSplitModule(module)}
-                >
-                  <Image src={zQiefen} width={23} height={23} preview={false} className='cursor-pointer' />
-                </Button>
-              </Tooltip>
-              <Tooltip title="合并模块(请先勾选要合并的模块)">
-                <Button
-                  style={{ border: 'none', padding: 0 }}
-                  size="small"
-                  type="primary"
-                  ghost
-                  onClick={() => handleMergeModule(module)}
-                >
-                  <Image src={zHebing} width={23} height={23} preview={false} className='cursor-pointer' />
-                </Button>
-              </Tooltip>
-              <Tooltip title="新增模块">
-                <Button
-                  size="small"
-                  type="primary"
-                  style={{ border: 'none', padding: 0 }}
-                  ghost
-                  onClick={() => handleAddModule(module.id)}
-                >
-                  <Image src={zXinzeng} width={23} height={23} preview={false} className='cursor-pointer' />
-                </Button>
-              </Tooltip>
-              <Button
-                size="small"
-                loading={module.delLoading}
-                danger
-                onClick={() => handleDeleteModule(module)}
-              >
-                删除
-              </Button>
-              <Button
-                size="small"
-                type="primary"
-                loading={module.saveLoading}
-                onClick={() => handleSaveModule(module, index)}
-              >
-                保存
-              </Button>
-              <div className="absolute bottom-0 left-0 z-10">
-                <Popconfirm
-                  placement="topLeft"
-                  showCancel={false}
-                  title={
-                    <div style={{ maxWidth: 360 }}>
-                      <div style={{ marginBottom: 8 }}>
-                        <div style={{ fontWeight: 600 }}>生成QA问答对</div>
-                        <div style={{ fontSize: 12, color: '#666' }}>
-                          {(() => {
-                            const trySources = [
-                              module?.qa,
-                              module?.mode?.qa,
-                              knowledgeDetail?.mode?.qa,
-                              detailDocument?.mode?.qa,
-                              module?.question,
-                            ];
-                            let qaRaw: any = null;
-                            for (const s of trySources) {
-                              if (s) {
-                                qaRaw = s;
-                                break;
-                              }
-                            }
-                            if (!qaRaw) return '暂无问答';
-                            let qaList: any[] = [];
-                            if (typeof qaRaw === 'string') {
-                              try {
-                                qaList = JSON.parse(qaRaw);
-                              } catch (e) {
-                                return qaRaw;
-                              }
-                            } else if (Array.isArray(qaRaw)) {
-                              qaList = qaRaw;
-                            } else if (qaRaw && typeof qaRaw === 'object') {
-                              qaList = qaRaw.qa || qaRaw.list || [];
-                            }
-                            if (!qaList || qaList.length === 0) return '暂无问答';
-                            return (
-                              <div>
-                                {qaList.slice(0, 5).map((item: any, idx: number) => (
-                                  <div key={idx} style={{ marginBottom: 6 }}>
-                                    <div style={{ fontWeight: 500 }}>{item.question}</div>
-                                    <div style={{ fontSize: 12, color: '#444' }}>{item.answer}</div>
-                                  </div>
-                                ))}
-                                {qaList.length > 5 && <div style={{ fontSize: 12, color: '#999' }}>...共{qaList.length}条</div>}
-                              </div>
-                            );
-                          })()}
-                        </div>
-                      </div>
-                      <div style={{ marginBottom: 8 }}>
-                        <div style={{ fontWeight: 600 }}>生成关联问题</div>
-                        <div style={{ fontSize: 12, color: '#666' }}>{module?.question || '暂无关联问题'}</div>
-                      </div>
-                      <div>
-                        <div style={{ fontWeight: 600 }}>生成摘要</div>
-                        <div style={{ fontSize: 12, color: '#666' }}>{module?.summary || '暂无摘要'}</div>
-                      </div>
-                    </div>
-                  }
-                  okText="关闭"
-                  onConfirm={() => { }}
-                >
-                  <Image
-                    width={16}
-                    height={16}
-                    src={zTuozhan}
-                    preview={false}
-                    className='cursor-pointer'
-                  />
-                </Popconfirm>
-                {module.revisionStatus === '1' &&
-                  <Popconfirm
-                    placement="topLeft"
-                    showCancel={false}
-                    title={
-                      <div style={{ maxWidth: 500 }}>
-                        <div style={{ marginBottom: 8 }}>
-                          <div style={{ fontWeight: 600 }}>{module.documentName}</div>
-                          <div style={{ marginTop: 6, marginRight: 18 }}>
-                            <div style={{
-                              background: '#f5f7ff',
-                              padding: '10px',
-                              borderRadius: 6,
-                              border: '1px solid rgba(23,70,161,0.06)',
-                              fontSize: 13,
-                              color: '#222',
-                              lineHeight: '1.6',
-                              whiteSpace: 'pre-wrap'
-                            }}>
-                              {module.revisionSliceText}
-                            </div>
-                          </div>
-                        </div>
-                        <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'flex-end', marginTop: 10, alignItems: 'flex-end' }}>
-                          <div style={{ fontSize: 12, color: '#666' }}>修订人:{module?.revisionName}</div>
-                          <div style={{ fontSize: 12, color: '#666' }}>修订时间:{module?.revisionTime}</div>
-                        </div>
-                      </div>
-                    }
-                    okText="关闭"
-                    onConfirm={() => { }}
-                  >
-                    <Image width={16} height={16} src={tubing} preview={false} className='cursor-pointer ml-2' />
-                  </Popconfirm>
-                }
-              </div>
-            </Space>
-          )}
-        </div>
-
-      </div>
-    ));
-  };
-  // 渲染编辑模块-MD 
-  const renderMdEditorModules = () => {
-    const textAreaProps = {
-      autoSize: { minRows: 4, maxRows: 25 },
-      className:
-        '!bg-white !border-none !shadow-none !p-0 !text-sm !leading-6 focus:!shadow-none',
-    };
-    return (
-      <div
-        className={`rounded-2xl mb-[10px] border border-transparent bg-white p-2 transition-all duration-200 relative 
-          border-blue-300 ring-2 ring-blue-200`}
-      >
-        <TextArea
-          {...textAreaProps}
-          value={oneModules}
-          onChange={(e) => { setOneModules(e.target.value) }}
-        />
-      </div>
-    )
-  }
-  // 渲染预览模块
-  const renderPreviewDocument = () => {
-    return (
-      <div className="overflow-hidden rounded-2xl bg-white w-full">
-        <style>{`
-          .markdown-preview img,table {
-            max-width: 100%;
-            width: 100% !important;
-            height: auto;
-            display: block;
-          }
-        `}</style>
-        {modules.map((module, index) => (
-          <div
-            key={module.id}
-            data-id={module.id}
-            className={`relative px-3 py-2 text-sm leading-7 transition-all duration-200 cursor-pointer ${index !== modules.length - 1 ? 'border-b border-gray-100' : ''
-              } ${activeId === module.id
-                ? 'bg-blue-50  border-l-blue-500 shadow-sm scale-[1]'
-                : 'hover:bg-gray-50'
-              }`}
-            onClick={() => setActiveModule(module.id, false)}
-          >
-            <Typography
-              className="text-sm leading-7 text-gray-800 markdown-preview"
-              dangerouslySetInnerHTML={{
-                __html: marked.render(module.mdContent),
-              }}
-            />
-          </div>
-        ))}
-      </div>
-    );
-  };
-  //渲染预览模块-Md
-  const renderMdPreviewDocument = () => {
-    return (
-      <div className="overflow-hidden rounded-2xl bg-white w-full">
-        <style>{`
-          .markdown-preview img,table {
-            max-width: 100%;
-            width: 100% !important;
-            height: auto;
-            display: block;
-          }
-         .markdown-preview  table{
-            border-collapse: collapse;
-            width: 100%;
-          }
-         .markdown-preview  th, .markdown-preview  td{
-           border: 1px solid #ddd;
-         }
-        `}</style>
-        <div
-          className={`relative px-3 py-2 text-sm leading-7 transition-all duration-200 cursor-pointer border-b border-gray-100 bg-blue-50  border-l-blue-500 shadow-sm`}
-        >
-          <Typography
-            className="text-sm leading-7 text-gray-800 markdown-preview"
-            dangerouslySetInnerHTML={{
-              __html: marked.render(oneModules),
-            }}
-          />
-        </div>
-      </div>
-    );
-  };
-
-
-  const handleSearch = () => {
-    setHasMore(true);
-    loadKnowledgeList(1, keyword);
-  };
-  const [qaChecked, setQaChecked] = useState(false);  // 生成QA问答对
-  const [relatedQuestionsEnabled, setRelatedQuestionsEnabled] = useState(false); // 生成关联问题
-  const [summaryGenerationEnabled, setSummaryGenerationEnabled] = useState(false); // 生成摘要
-  const [parentGenerationEnabled, setParentGenerationEnabled] = useState(false); // 开启父子切片召回
-  const [allLoding, setAllLoding] = useState(false);
-  const mdUpdateSliceList = async () => {
-    const params = {
-      documentId: selectedDoc?.documentId,
-      markDownText: encodeURIComponent(oneModules),
-      knowledgeId: detailDocument?.knowledgeId,
-    };
-    try {
-      message.info({
-        duration: 2,
-        content: '正在保存Md切片信息...',
-      });
-      setTimeout(() => {
-        onCancel();
-      }, 1500)
-      const res: any = await apis.editMarkDownFileApi(params);
-      if (res.code === 200) {
-        // message.success('保存成功');
-        const timestampMs = Date.now();
-        onSetStarts(timestampMs)
-        loadKnowledgeList(1, '');
-        onCancel(1);
-      } else {
-        message.error(res.msg || '保存失败');
-      }
-    } catch (error: any) {
-
-    } finally {
-      setAllLoding(false);
-    }
-  }
-  const updateSliceInfoList = async () => {
-    // 当保存时把当前文档状态设为 '4'(加载中),成功后设为 '5'
-    const targetId = selectedDoc?.documentId || detailDocument?.documentId;
-    // 记录原始状态以便错误时恢复
-    let prevStatus: any = null;
-    if (targetId) {
-      setDocuments((prev) => {
-        return prev.map((d) => {
-          if (d.documentId === targetId) {
-            prevStatus = d.status;
-            return { ...d, status: '4' };
-          }
-          return d;
-        });
-      });
-      // 同步更新 selectedDoc 的状态(如果当前选中)
-      if (selectedDoc && selectedDoc.documentId === targetId) {
-        setSelectedDoc({ ...selectedDoc, status: '4' });
-      }
-    }
-
-    setAllLoding(true);
-    if (tabKey === 'md') {
-      // 仅在切片模式下执行保存逻辑
-      mdUpdateSliceList();
-      return;
-    }
-
-    const params = {
-      documentId: selectedDoc?.documentId,
-      knowledgeId: detailDocument?.knowledgeId,
-      sliceList: modules.map((item, index) => ({
-        ...item,
-        sliceText: item.content,
-        index: index + 1,
-      })),
-      delSliceIds,
-      qaChecked: qaChecked,
-      relatedQuestionsEnabled: relatedQuestionsEnabled,
-      summaryGenerationEnabled: summaryGenerationEnabled,
-      parentGenerationEnabled: parentGenerationEnabled,
-    };
-    try {
-      message.info({
-        duration: 2,
-        content: '正在保存切片信息...',
-      });
-      setTimeout(() => {
-        onCancel();
-      }, 1000)
-      const res: any = await apis.updateSliceInfoList(params);
-      setAllLoding(false);
-      if (res.code === 200) {
-        const timestampMs = Date.now();
-        onSetStarts(timestampMs)
-        // 更新状态为 '5' 表示已完成
-        loadKnowledgeList(1, '');
-        if (targetId) {
-          setDocuments((prev) => prev.map((d) => (d.documentId === targetId ? { ...d, status: '5' } : d)));
-          if (selectedDoc && selectedDoc.documentId === targetId) {
-            setSelectedDoc({ ...selectedDoc, status: '5' });
-          }
-        }
-        // message.success('保存成功');
-        const resdata = res.data;
-        onCancel(1);
-        if (selectedRefDoc.current?.documentId === resdata?.documentId) {
-          // console.log('selectedDoc--targetId--',selectedRefDoc.current?.documentId,'--resdata?.documentId',resdata?.documentId)
-          // onCancel();
-        }
-      } else {
-        // 恢复原始状态
-        if (targetId) {
-          setDocuments((prev) => prev.map((d) => (d.documentId === targetId ? { ...d, status: prevStatus } : d)));
-          if (selectedDoc && selectedDoc.documentId === targetId) {
-            setSelectedDoc({ ...selectedDoc, status: prevStatus });
-          }
-        }
-        message.error(res.msg || '保存失败');
-      }
-    } catch (error: any) {
-      // 恢复原始状态
-      if (targetId) {
-        setDocuments((prev) => prev.map((d) => (d.documentId === targetId ? { ...d, status: prevStatus } : d)));
-        if (selectedDoc && selectedDoc.documentId === targetId) {
-          setSelectedDoc({ ...selectedDoc, status: prevStatus });
-        }
-      }
-      setAllLoding(false);
-    }
-
-  }
-  const getOldSliceListByDocumentId = async () => {
-    const res: any = await apis.getOldSliceListByDocumentId(selectedDoc?.documentId)
-    if (res.code === 200 && res.data) {
-      sliceDataProcess(res.data)
-    }
-  }
-  const [tabKey, setTabKey] = useState<string>('slices');
-  // 批量删除
-  const onAllDelete = () => {
-    Modal.confirm({
-      title: '将要删除多条选中的切片,是否确认',
-      okText: '确认',
-      cancelText: '取消',
-      onOk: async () => {
-        const res: any = await apis.deleteSliceListApi(selectedDoc?.knowledgeId, selectedDoc?.documentId, {
-          sliceIds: selectedModuleIds,
-        });
-        if (res.code === 200) {
-          message.success('已删除所选切片');
-          setSelectedModuleIds([]);
-          setModules((pre) => {
-            return pre.filter((m: any) => !selectedModuleIds.includes(m.id));
-          });
-        }
-      },
-    });
-  }
-  // 如果是有Md编辑器推出的时候加个提示
-  const onMdCancel = () => {
-    if (selectedDoc?.standardClassification !== '2') {
-      Modal.confirm({
-        title: '提示',
-        content: '退出后,当前未保存的编辑行为都将失效,是否确认退出',
-        okText: '确认',
-        okType: 'danger',
-        cancelText: '取消',
-        onOk: async () => {
-          onCancel();
-        }
-      });
-    } else {
-      onCancel()
-    }
-  }
-  const [isPreviewCollapsed, setIsPreviewCollapsed] = useState(false); // 控制预览区域是否收起
-
-  return (
-    <>
-      <Modal
-        title="知识文档处理"
-        open={open}
-        onCancel={onMdCancel}
-        footer={
-          <div className="flex justify-end gap-3 items-center">
-            {tabKey === 'slices' && <>
-              <p className='font-bold'>拓展功能:</p>
-              <Checkbox checked={qaChecked} onChange={(e) => setQaChecked(e.target.checked)}>生成QA问答对</Checkbox>
-              <Checkbox checked={relatedQuestionsEnabled} onChange={(e) => setRelatedQuestionsEnabled(e.target.checked)}>生成关联问题</Checkbox>
-              <Checkbox checked={summaryGenerationEnabled} onChange={(e) => setSummaryGenerationEnabled(e.target.checked)}>生成摘要</Checkbox>
-              {customSeparator === '3' && <Checkbox checked={parentGenerationEnabled} onChange={(e) => setParentGenerationEnabled(e.target.checked)}>开启父子切片召回</Checkbox>}
-            </>}
-            <Button onClick={onMdCancel}>取消</Button>
-            {<Button type="primary" loading={selectedDoc?.status === '4'} onClick={() => updateSliceInfoList()}>
-              保存
-            </Button>}
-          </div>
-        }
-        width="95%"
-        maskClosable={false}
-        style={{ top: 24 }}
-        className="md-modal [&_.ant-modal-body]:p-0"
-      >
-        <div className="flex h-[85vh] gap-4 bg-gray-50 p-4">
-          {/* 文档列表 */}
-          <div
-            className={`flex flex-col rounded-2xl bg-white shadow-md transition-all duration-0 ${isListCollapsed ? 'w-14' : 'w-72'
-              }`}
-          >
-            <div
-              className={`flex items-center border-b border-gray-200 ${isListCollapsed ? 'justify-center px-0 py-4' : 'justify-between px-4 py-3'
-                }`}
-            >
-              {!isListCollapsed && (
-                <Tooltip title={knowledgeDetail ? knowledgeDetail.name : '文档列表'}>
-                  <Typography.Title level={5} className="mb-0! text-base font-semibold mt-0 overflow-hidden text-ellipsis whitespace-nowrap max-w-[400px]">
-                    {knowledgeDetail ? knowledgeDetail.name : '文档列表'}
-                  </Typography.Title>
-                </Tooltip>
-              )}
-              <Button
-                type="text"
-                size="small"
-                icon={isListCollapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
-                onClick={() => setIsListCollapsed((prev) => !prev)}
-              />
-            </div>
-            {isListCollapsed ? (
-              <div className="flex flex-1 items-center justify-center text-[11px] tracking-[0.4em] text-gray-400 [writing-mode:vertical-rl]">
-                文档列表
-              </div>
-            ) : (
-              <>
-                <div className="px-4 pt-3">
-                  <Space.Compact className="w-full" size="small">
-                    <Input
-                      placeholder="请输入文档名称"
-                      size='middle'
-                      value={keyword}
-                      onChange={(e) => {
-                        setKeyword(e.target.value);
-                      }}
-                      onPressEnter={handleSearch}
-                    />
-                    <Button
-                      type="primary"
-                      size="middle"
-                      icon={<SearchOutlined />}
-                      onClick={handleSearch}
-                    />
-                  </Space.Compact>
-                </div>
-                <div
-                  className="flex-1 space-y-2 overflow-y-auto px-4 pb-4 pt-3"
-                  ref={listContainerRef}
-                >
-                  {documents.map((doc) => (
-                    <div
-                      key={doc.documentId}
-                      className={`rounded-lg border border-transparent bg-gray-50 p-2.5 text-sm transition-all cursor-pointer hover:border-blue-300 hover:bg-blue-50 ${selectedDoc?.documentId === doc.documentId
-                        ? 'border-blue-400 ring-1 ring-blue-300'
-                        : ''
-                        }`}
-                      onClick={() => { onExportPdfStream(doc); }}
-                    >
-                      <div className="font-medium text-gray-900 text-xs flex items-center">
-                        <FileTextOutlined style={{ color: selectedDoc?.documentId === doc.documentId ? '#1677ff' : '' }} />
-                        <div className="ml-2 flex-1 min-w-0 truncate" title={doc.name}>{doc.name}</div>
-                        {doc?.status === '4' && (
-                          <div className="ml-2 flex-none">
-                            <Spin size="small" />
-                          </div>
-                        )}
-                      </div>
-                    </div>
-                  ))}
-                  {listLoading && (
-                    <div className="flex items-center justify-center gap-2 py-4 text-xs text-gray-500">
-                      <Spin size="small" /> <span>加载中...</span>
-                    </div>
-                  )}
-                  {!listLoading && documents.length === 0 && (
-                    <div className="py-8">
-                      <Empty description="暂无文档" image={Empty.PRESENTED_IMAGE_SIMPLE} />
-                    </div>
-                  )}
-                  {!hasMore && documents.length > 0 && (
-                    <div className="py-2 text-center text-xs text-gray-400">没有更多了</div>
-                  )}
-                </div>
-              </>
-            )}
-          </div>
-
-          {/* PDF预览 isPDfCollapsed */}
-          {selectedDoc?.suffix !== 'md' && <div className={`flex flex-col rounded-2xl bg-white shadow-md transition-all duration-0 ${isPDfCollapsed ? 'w-14' : 'flex-1'}`}>
-            <div className="px-5 pt-4 pb-2 flex items-center justify-between">
-              {!isPDfCollapsed && <Tooltip title={selectedDoc ? selectedDoc.name : 'PDF 预览'}>
-                <Typography.Title level={5} className="mb-0! text-base font-semibold mt-0 overflow-hidden text-ellipsis whitespace-nowrap max-w-[300px]">
-                  {selectedDoc ? selectedDoc.name : 'PDF 预览'}
-                </Typography.Title>
-              </Tooltip>}
-              <p>
-                {!isPDfCollapsed && (
-                  <Button
-                    type="text"
-                    size="small"
-                    icon={<FullscreenOutlined />}
-                    onClick={() => setPdfFullVisible(true)}
-                  />
-                )}
-                <Button
-                  type="text"
-                  size="small"
-                  icon={isPDfCollapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
-                  onClick={() => setIsPDfCollapsed((prev) => !prev)}
-                />
-              </p>
-            </div>
-            {isPDfCollapsed ? <div className="flex flex-1 items-center justify-center text-[11px] tracking-[0.4em] text-gray-400 [writing-mode:vertical-rl]">
-              PDF列表
-            </div> : <div className="flex flex-1 overflow-hidden pb-5 pt-3">
-              <div ref={pdfScrollRef} className="h-full w-full overflow-y-auto">
-                <div className="h-full min-h-[240px]">
-                  <Spin tip="pdf加载中..." spinning={pdfLoading} wrapperClassName="h-full flex justify-center">
-                    <div className="h-full w-full">
-                      {renderPdfPreview()}
-                    </div>
-                  </Spin>
-                </div>
-              </div>
-            </div>}
-          </div>}
-
-          {/* 智能切片 */}
-          <div className="flex flex-1 flex-col rounded-2xl bg-white shadow-md">
-            <div className="flex justify-between px-5 pb-2 pt-4 md_center">
-
-              <Tabs
-                activeKey={tabKey}
-                onChange={(key) => setTabKey(key as 'slices' | 'md')}
-                size="small"
-                items={selectedDoc?.standardClassification === '2' || selectedDoc?.customSeparator === '0' || selectedDoc?.customSeparator === '1' ? [
-                  { key: 'slices', label: '智能切片' },
-
-                ] : [{ key: 'slices', label: '智能切片' }, {
-                  key: 'md', label:
-                    <div>Md编辑器 <Tooltip title="markdown编辑仅支持最小段落切片(标准规范)和按标题段落切片"> <QuestionCircleOutlined /> </Tooltip> </div>
-                },]}
-              />
-              <div>
-                {tabKey === 'slices' && <Button size="small" disabled={!activeId} onClick={() => {
-                  Modal.confirm({
-                    title: '将放弃对切片的所有修改,恢复到初始状态,是否确认',
-                    okText: '确定',
-                    cancelText: '取消',
-                    onOk: () => {
-                      getOldSliceListByDocumentId();
-                    },
-                  })
-                }}>
-                  恢复全部
-                </Button>}
-                {tabKey === 'slices' && <Button size="small" disabled={!selectedModuleIds.length} className='ml-2'
-                  icon={<DeleteOutlined style={{ color: selectedModuleIds.length ? '#eb2f96' : '' }} />}
-                  onClick={() => {
-                    onAllDelete();
-                  }}>
-                </Button>}
-              </div>
-            </div>
-            {<div
-              className="flex-1 space-y-4 overflow-y-auto px-3 pb-5 pt-3"
-              ref={editorContainerRef}
-            >
-              <div className="h-full min-h-[240px] w-full">
-                <Spin tip="切片加载中..." spinning={sliceLoading} wrapperClassName="h-full w-full flex justify-center">
-                  <div className="h-full w-full">
-                    {tabKey === 'slices' && renderEditorModules()}
-                    {tabKey === 'md' && renderMdEditorModules()}
-                  </div>
-                </Spin>
-              </div>
-            </div>}
-          </div>
-
-          {/* 文档预览 */}
-          {
-            <div className={`flex flex-col rounded-2xl bg-white shadow-md transition-all duration-0 ${isPreviewCollapsed ? 'w-14' : 'flex-1'}`}>
-              <div className={`flex items-center border-b border-gray-200 ${isPreviewCollapsed ? 'justify-center px-0 py-4' : 'justify-between px-4 py-3'
-                }`}  >
-                {!isPreviewCollapsed && <Typography.Title level={5} className="mb-0! text-base font-semibold mt-0">
-                  文档预览
-                </Typography.Title>}
-                <Button
-                  type="text"
-                  size="small"
-                  icon={<MenuUnfoldOutlined />}
-                  onClick={() => setIsPreviewCollapsed((prev) => !prev)}
-                />
-              </div>
-              {isPreviewCollapsed ? <div className="flex flex-col items-center justify-center rounded-2xl bg-white shadow-md w-14 h-full">
-                <div className="text-[11px] tracking-[0.4em] text-gray-400 [writing-mode:vertical-rl] mt-4">
-                  文档预览
-                </div>
-              </div>
-                :
-                <div
-                  className="flex-1 overflow-y-auto px-3 pb-5 pt-3"
-                  ref={previewContainerRef}
-                >
-                  <div className="h-full min-h-[240px]">
-                    <Spin tip="预览文档加载中..." spinning={sliceLoading} wrapperClassName="h-full flex justify-center">
-                      <div className="h-full w-full">
-                        {tabKey === 'slices' && renderPreviewDocument()}
-                        {tabKey === 'md' && renderMdPreviewDocument()}
-                      </div>
-                    </Spin>
-                  </div>
-                </div>}
-            </div>}
-        </div>
-      </Modal>
-      {/* 全屏 PDF 预览(改为左侧 Drawer,宽度 40%) */}
-      <Drawer
-        title={selectedDoc ? selectedDoc.name : 'PDF 预览'}
-        open={pdfFullVisible}
-        onClose={() => setPdfFullVisible(false)}
-        placement="left"
-        width="55%"
-        footer={
-          <div className="flex items-center gap-2">
-            <Button size="small" onClick={() => setPdfFullScale((s) => Math.max(0.5, +(s - 0.25).toFixed(2)))}>-</Button>
-            <div className="text-sm">{Math.round(pdfFullScale * 100)}%</div>
-            <Button size="small" onClick={() => setPdfFullScale((s) => Math.min(3, +(s + 0.25).toFixed(2)))}>+</Button>
-            <Button size="small" onClick={() => { setPdfFullScale(1.5); setPdfFullVisible(false); }}>关闭</Button>
-          </div>
-        }
-      >
-        <div style={{ height: '100%', overflow: 'auto' }}>
-          {pdfBuffer ? (
-            <Document file={pdfBuffer} onLoadSuccess={({ numPages }) => setNumPages(numPages)}>
-              {Array.from(new Array(numPages), (_, i) => (
-                <div key={i} style={{ display: 'flex', justifyContent: 'center', marginBottom: 12 }}>
-                  <Page pageNumber={i + 1} scale={pdfFullScale} renderAnnotationLayer={false} renderTextLayer={true} />
-                </div>
-              ))}
-            </Document>
-          ) : (
-            <div className="flex h-full items-center justify-center">暂无 PDF</div>
-          )}
-        </div>
-      </Drawer>
-    </>
-  );
-};
-
-export default observer(MdModal); 

+ 0 - 358
jk-rag-platform/src/pages/knowledgeLib/detail/components/QuoteModal.tsx.bak

@@ -1,358 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import { Button, Modal, Table, TablePaginationConfig, TableColumnsType, Typography, message, Tabs, Input } from 'antd';
-import type { TableProps } from 'antd';
-import { apis } from '@/apis';
-import { Record } from '../types';
-import dayjs from 'dayjs';
-import useAppState from '@/store';
-
-import PrevewSlice from './prevewSlice';
-
-interface QuoteModalProps {
-  open: boolean;
-  knowledgeDetail: any;
-  onCancel: () => void;
-}
-
-const QuoteModal: React.FC<QuoteModalProps> = ({ open, knowledgeDetail, onCancel }) => {
-  const [list, setList] = useState<Record[]>([]); // 示例数据
-  const [page, setPage] = useState<{ page: number; size: number; total: number }>({
-    page: 1,
-    size: 10,
-    total: 0,
-  });
-  const [titleName, setTitleName] = useState<string>('');
-  const [appStateList, setAppStateList] = useState<any[]>([]);
-  const [activeTabKey, setActiveTabKey] = useState<string>('');
-  const [searchKeyword, setSearchKeyword] = useState<string>('');
-  const { getAppState } = useAppState();
-  const [openDrawer, setOpenDrawer] = useState<boolean>(false);
-  const [selectedRecord, setSelectedRecord] = useState<any>(null);
-  const columns: TableColumnsType<Record> = [
-    {
-      title: '序号',
-      dataIndex: 'index',
-      width: 80,
-      render: (text, record, index) => {
-        return index + 1;
-      }
-    },
-    {
-      title: '文件名',
-      dataIndex: 'name',
-      width: 300,
-      sorter: (a, b) => a.name.localeCompare(b.name),
-      render: (text, record) => {
-        return (
-          `${text}`
-        )
-      }
-    },
-    {
-      title: '所在知识库',
-      dataIndex: 'knowledgeName',
-      width: 300,
-      render: (text, record) => {
-        return (
-          `${text}`
-        )
-      }
-    },
-    {
-      title: '创建人',
-      dataIndex: 'nickName',
-      width: 300,
-      render: (text, record) => {
-        return (
-          `${text}`
-        )
-      }
-    },
-    {
-      title: '文件大小',
-      dataIndex: 'length',
-      width: 100,
-      render: (text) => {
-        if (text) {
-          const size = (text / 1024 / 1024).toFixed(2);
-          return `${size} M`;
-        } else {
-          return '--'
-        }
-      }
-    },
-    {
-      title: '字符数量',
-      dataIndex: 'wordNum',
-      width: 100,
-      render: (text) => {
-        if (text) {
-          return `${text}`;
-        } else {
-          return '--'
-        }
-      }
-    },
-    {
-      title: '切片数量',
-      dataIndex: 'sliceTotal',
-      width: 100,
-      render: (text) => {
-        if (text) {
-          return `${text}`;
-        } else {
-          return '--';
-        }
-      }
-    },
-    {
-      title: '上传时间',
-      dataIndex: 'createTime',
-      width: 180,
-      render: (text) => {
-        if (text) {
-          return dayjs(text).format('YYYY-MM-DD HH:mm:ss');
-        } else {
-          return '--';
-        }
-      }
-    },
-    {
-      title: '更新时间',
-      dataIndex: 'updateTime',
-      width: 180,
-      render: (text) => {
-        if (text) {
-          return dayjs(text).format('YYYY-MM-DD HH:mm:ss');
-        } else {
-          return '--';
-        }
-      }
-    },
-    {
-      title: '操作',
-      dataIndex: 'operation',
-      width: 180,
-      fixed: 'right',
-      render: (text, record: any) => {
-        return (
-          <>
-            <a
-              style={{ marginRight: 16 }}
-              onClick={() => {
-                setOpenDrawer(true);
-                setSelectedRecord(record);
-              }}
-            >
-              查看切片
-            </a>
-          </>
-        )
-      }
-    }
-  ];
-  const [allLoding, setAllLoding] = useState<boolean>(false);
-  const handleOk = async () => {
-    if (documentids.length === 0) {
-      Modal.warning({
-        title: '提示',
-        content: '请选择要引用的文档',
-      });
-      return;
-    }
-    setAllLoding(true);
-    try {
-      const falgDocumentids = documentids.map((item: any) => {
-        return item
-      })
-      // 先调用效验接口
-      const checkRes: any = await apis.checkQuoteFileApi({
-        documentIds: falgDocumentids,
-        knowledgeId: knowledgeDetail.knowledgeId,
-      })
-      const checkResData = checkRes.data || [];
-      if (!checkResData?.length) {
-        await onImportDocumentList(falgDocumentids);
-      } else {
-        setAllLoding(false);
-        Modal.confirm({
-          title: '提示',
-          content: checkResData.map((item: any) => {
-            return <div key={item.ossId}>文件“{item.newName}”已存在,知识库中已存在的文件名是“{item.oldName}”</div>
-          }),
-          onOk: async () => {
-            setAllLoding(true);
-            // 过滤掉已存在的文件(根据 ossId)
-            const filtered = falgDocumentids.filter((f: any) => !checkResData.some((d: any) => d.documentId === f));
-
-            if (!filtered || filtered.length === 0) {
-              // 所有文件都已存在,提示并结束
-              message.warning('选中文件均已存在,请重新选择。');
-              setAllLoding(false);
-              return;
-            }
-
-            // 构建参数并继续上传剩余文件
-            // const nextParams = {
-            //   ...values,
-            //   parsingType: values.parsingType,
-            //   standardClassification,
-            //   fileList: filtered.map((item: any) => ({
-            //     ...item,
-            //     suffix: (item.fileName || item.name || '').split('.').pop(),
-            //   })),
-            // };
-            await onImportDocumentList(filtered);
-          },
-          onCancel: () => {
-            setAllLoding(false);
-          }
-        });
-      }
-      return
-    } catch (error) {
-      setAllLoding(false);
-    }
-    onCancel();
-  };
-
-  const onImportDocumentList = async (documentids:any) => {
-    const res: any = await apis.importDocumentList({
-      documentIds: documentids,
-      knowledgeId: knowledgeDetail.knowledgeId,
-    });
-    if (res?.code === 200) {
-      message.success('引用成功');
-    }
-    setAllLoding(false);
-  }
-
-
-  const paginationConfig: TablePaginationConfig = {
-    // 显示数据总量
-    showTotal: (total: number) => {
-      return `共 ${total} 条`;
-    },
-    // 展示分页条数切换
-    showSizeChanger: true,
-    // 指定每页显示条数
-    pageSizeOptions: ['10', '20', '50', '100'],
-    // 快速跳转至某页
-    showQuickJumper: true,
-    current: page.page,
-    pageSize: page.size,
-    total: page.total,
-    onChange: async (page, pageSize) => {
-      setPage((prevState) => ({
-        ...prevState,
-        page: page,
-        size: pageSize,
-      }));
-      onGetQuoteDocumentList(page, pageSize);
-    },
-  };
-  const [listLoading, setListLoading] = useState<boolean>(false);
-  const [documentids, setdocumentids] = React.useState<string[]>([]); // 选择的文档ID数组
-  const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
-
-  // 示例数据和列定义
-  const rowSelection: TableProps<Record>['rowSelection'] = {
-    selectedRowKeys,
-    onChange: (selectedRowKeys: React.Key[], selectedRows: Record[]) => {
-      setSelectedRowKeys(selectedRowKeys);
-      const flagids = selectedRows.map((item) => {
-        return item.documentId
-      })
-      setdocumentids(flagids)
-    },
-  };
-  const onGetQuoteDocumentList = async (pageNum: number = 1, pageSize: number = 10, keyword?: string, name: string = '') => {
-    const params: any = {
-      knowledge_id: knowledgeDetail.knowledgeId,
-      standardClassification: keyword,
-      name,
-      pageNum,
-      pageSize,
-    };
-    const res: any = await apis.getQuoteDocumentList(params);
-    if (res?.code === 200) {
-      setList(res.rows || [])
-      setPage((prev) => ({ ...prev, total: res.total || prev.total }));
-    }
-  }
-  const init = async () => {
-    const res: any = await getAppState();
-    const list = res || [];
-    setAppStateList([{
-      label: '全部',
-      value: '',
-    }, ...list]);
-    // 初始tab key
-    const initKey = String((knowledgeDetail.standardClassification ?? (list[0] && list[0].value)) || '');
-    setActiveTabKey('');
-    // 设置显示名称
-    const match = list.find((it: any) => String(it.value) === String(initKey));
-    if (match) setTitleName(match.label);
-    // 首次请求
-    onGetQuoteDocumentList(1, page.size, activeTabKey, '');
-  }
-  useEffect(() => {
-    init();
-  }, [])
-  return (
-    <>
-      <Modal
-        title="引用知识文库"
-        width={'90%'}
-        open={open}
-        onCancel={onCancel}
-        footer={<div className="flex justify-end gap-3 items-center">
-          <Button onClick={onCancel}>取消</Button>
-          <Button type="primary" loading={allLoding} onClick={() => handleOk()}>
-            保存
-          </Button>
-        </div>}
-      >
-        <div className="flex items-center justify-between">
-          <Tabs
-            activeKey={activeTabKey}
-            onChange={(key) => {
-              setActiveTabKey(String(key));
-              const match = appStateList.find((it: any) => String(it.value) === String(key));
-              if (match) setTitleName(match.label);
-              // 切换 tab 时重载列表
-              onGetQuoteDocumentList(1, page.size, key, '');
-              setdocumentids([]); // 清空已选择的文档ID
-              setSelectedRowKeys([])
-            }}
-            items={appStateList.map((it: any) => ({ key: String(it.value), label: it.label }))}
-          />
-          <Input.Search
-            placeholder="请输入文件名搜索"
-            allowClear
-            enterButton
-            style={{ width: 260 }}
-            value={searchKeyword}
-            onChange={(e) => setSearchKeyword(e.target.value)}
-            onSearch={(value) => {
-              // 搜索时重新拉取第一页
-              onGetQuoteDocumentList(1, page.size, activeTabKey, value);
-            }}
-          />
-        </div>
-        <Table
-          scroll={{ x: 'max-content' }}
-          rowSelection={{ type: 'checkbox', ...rowSelection }}
-          rowKey={(record) => record.documentId}
-          loading={listLoading}
-          columns={columns}
-          dataSource={list}
-          pagination={paginationConfig}
-        />
-      </Modal>
-      <PrevewSlice openDrawer={openDrawer} onClose={() => setOpenDrawer(false)} title="预览切片" record={selectedRecord} />
-    </>
-  );
-};
-
-export default QuoteModal;

+ 0 - 669
jk-rag-platform/src/pages/knowledgeLib/detail/components/UploadModal.tsx.bak

@@ -1,669 +0,0 @@
-import * as React from 'react';
-import { Modal, Form, Input, Select, Upload, UploadProps, message, Spin, Tooltip, Button } from 'antd';
-import { InboxOutlined, QuestionCircleOutlined } from '@ant-design/icons';
-import { apis } from '@/apis';
-import { getHeaders } from '@/apis/config';
-import { RecordKonwledge } from '../types'
-const FormItem = Form.Item;
-const { Option } = Select;
-const { TextArea } = Input;
-const { Dragger } = Upload;
-
-interface Props {
-    open: boolean;
-    knowledgeId: string;
-    knowledgeDetail: RecordKonwledge;
-    onCancel: () => void;
-    onSuccess: () => void;
-}
-
-const UploadModal: React.FC<Props> = (props: Props) => {
-    const { open, knowledgeId, onCancel, onSuccess, knowledgeDetail } = props;
-    const [form] = Form.useForm();
-    const [uploadLoading, setUploadLoading] = React.useState<boolean>(false);
-    const [standardClass, setStandardClass] = React.useState<any[]>([]);// 选择分类-下拉选项
-    const [parsingTypeList, setParsingTypeList] = React.useState<any[]>([]);// 解析工具-下拉选项
-    const [parsingTypeListBackup, setParsingTypeListBackup] = React.useState<any[]>([]);// 解析工具-下拉选项-备份数据
-    const [standardClassification, setStandardClassification] = React.useState<string>() // 选择分类
-    const [parsingType, setParsingType] = React.useState<string>() // 选择解析工具
-    const [splittingOldList, setSplittingOldList] = React.useState<any[]>([]); // 旧的切分规则列表
-    const [splittingType, setSplittingType] = React.useState<any>([]); // 选择切分规则 
-    const [fileOssList, setFileOssList] = React.useState<any>([]); // 选中的列表OSS信息
-    const [oldFileList, setOldFileList] = React.useState<any>([]); // 选中的本地文件列表
-    const [oldfileName, setOldfileName] = React.useState<any>(''); // 选中的本地文件的信息
-    const [isVisibleSlice, setIsVisibleSlice] = React.useState(false);
-    const [formatb, setFormatb] = React.useState('md,txt,pdf,doc,docx,odt,rtf,ods,ppt,pptx,odp,jpg,png,jpeg'); // 默认是通用
-    const oldFileListRef = React.useRef(oldFileList);
-
-    React.useEffect(() => {
-        oldFileListRef.current = oldFileList;
-    }, [oldFileList]);
-    
-    // 获取解析工具
-    const fetchParsingTypeList = async () => {
-        try {
-            const res = await apis.fetchTakaiAppTypeList('parsing_type');
-            const list = res.data.map((item: any) => {
-                return {
-                    label: item.dictLabel,
-                    value: item.dictValue,
-                }
-            });
-            setParsingTypeList(list);
-            setParsingTypeListBackup(list);
-            form.setFieldsValue({
-                parsingType: list[0]?.value
-            })
-        } catch (error: any) {
-            console.error(error);
-        }
-    }
-    // 获取向默认切分规则
-    const fetchsplittingType = async () => {
-        try {
-            const res = await apis.fetchTakaiAppTypeList('splitting_type');
-            const list = res.data.map((item: any) => {
-                return {
-                    label: item.dictLabel,
-                    value: item.dictValue,
-                    remark: item.remark,
-                }
-            });
-            setSplittingType(list);
-            setSplittingOldList(list);
-            form.setFieldsValue({
-                customSeparator: '2',
-                sliceValue: knowledgeDetail.sliceValue,
-            })
-
-            if (knowledgeDetail.splittingType === '0') {
-                setIsVisibleSlice(!isVisibleSlice);
-            }
-        } catch (error: any) {
-            console.error(error);
-        }
-    }
-    const onChangeF = (value: string) => {
-        if (value === '0') {
-            setIsVisibleSlice(!isVisibleSlice);
-        } else {
-            setIsVisibleSlice(false);
-        }
-    };
-    // 批量删除oss文件
-    const onDeleteOssFileApi = async (ossIds: string) => {
-        try {
-            const res: any = await apis.deleteOssFileApi(ossIds);
-            if (res.code !== 200) {
-                message.error('删除oss文件失败');
-            }
-        } catch (error) {
-            console.error('删除oss文件失败:', error);
-        }
-    };
-    // 手动上传单个文件
-    const uploadOneFile = (fileItem: any) => {
-        return new Promise((resolve, reject) => {
-            if (fileItem.response && fileItem.response.code === 200) {
-                resolve(fileItem.response.data);
-                return;
-            }
-            const formData = new FormData();
-            formData.append('file', fileItem.originFileObj);
-
-            const xhr = new XMLHttpRequest();
-            xhr.open('POST', '/api/resource/oss/upload', true);
-            const headers: any = getHeaders();
-            for (const key in headers) {
-                xhr.setRequestHeader(key, headers[key]);
-            }
-
-            xhr.upload.onprogress = (event) => {
-                if (event.lengthComputable) {
-                    const percent = parseInt(((event.loaded / event.total) * 100).toFixed(0));
-                    setOldFileList((prev: any[]) => prev.map(f => {
-                        if (f.uid === fileItem.uid) {
-                            return { ...f, percent, status: 'uploading' };
-                        }
-                        return f;
-                    }));
-                }
-            };
-
-            xhr.onload = () => {
-                if (xhr.status === 200) {
-                    const response = JSON.parse(xhr.responseText);
-                    if (response.code === 200) {
-                        setOldFileList((prev: any[]) => prev.map(f => {
-                            if (f.uid === fileItem.uid) {
-                                return { ...f, status: 'done', percent: 100, response };
-                            }
-                            return f;
-                        }));
-                        resolve(response.data);
-                    } else {
-                        setOldFileList((prev: any[]) => prev.map(f => {
-                            if (f.uid === fileItem.uid) {
-                                return { ...f, status: 'error' };
-                            }
-                            return f;
-                        }));
-                        message.error(response.msg || '上传失败');
-                        reject(response.msg);
-                    }
-                } else {
-                    setOldFileList((prev: any[]) => prev.map(f => {
-                        if (f.uid === fileItem.uid) {
-                            return { ...f, status: 'error' };
-                        }
-                        return f;
-                    }));
-                    message.error('上传失败');
-                    reject('Http Error');
-                }
-            };
-
-            xhr.onerror = () => {
-                setOldFileList((prev: any[]) => prev.map(f => {
-                    if (f.uid === fileItem.uid) {
-                        return { ...f, status: 'error' };
-                    }
-                    return f;
-                }));
-                message.error('网络错误');
-                reject('Network Error');
-            };
-
-            xhr.send(formData);
-        });
-    };
-
-    // 上传组件的参数
-    const uploadProps: UploadProps = {
-        name: 'file',
-        // 根据选择的分类决定是否允许多选与最大数量:分类为 '3' 时只允许单文件,其余允许最多 5 个
-        multiple: standardClassification !== '3',
-        maxCount: standardClassification === '3' ? 1 : 5,
-        headers: getHeaders(),
-        // 使用受控 fileList,便于自定义已上传文件列表的展示和管理
-        fileList: oldFileList,
-        beforeUpload(file, fileList) {
-            const maxCount = standardClassification === '3' ? 1 : 5;
-            const currentCount = (oldFileList || []).length;
-            const incomingCount = fileList?.length || 1;
-            if (currentCount + incomingCount > maxCount) {
-                message.error(`最多只能上传 ${maxCount} 个文件`);
-                return Upload.LIST_IGNORE;
-            }
-            const allowedExtensions = formatb ? formatb.split(',') : ['*'];
-            const nextFormatb = ['exe', 'msi', 'bat', 'cmd', 'com', 'sh', 'run', 'bin', 'app', 'dmg', 'pkg', 'jar', 'py'];
-            // 检查文件类型
-            const fileExt: string = file.name.split('.').pop()?.toLowerCase() || '';
-            if(nextFormatb.includes(fileExt)){
-                message.error(`不支持 ${fileExt} 格式的文件上传-12`);
-                return Upload.LIST_IGNORE;
-            }
-            if(formatb === '*'){
-                // 支持所有格式
-            }else if (!fileExt || !allowedExtensions.includes(fileExt)) {
-                message.error(`不支持 ${fileExt} 格式的文件上传`);
-                return Upload.LIST_IGNORE;
-            }
-
-            // 检查文件大小
-            const fileSizeMB = file.size / 1024 / 1024;
-            if (fileSizeMB > 50) {
-                message.error('单个文件不能大于50M');
-                return Upload.LIST_IGNORE;
-            }
-
-            if (['jpg', 'png', 'jpeg'].includes(fileExt!) && fileSizeMB > 5) {
-                message.error('单张图片不能大于5M');
-                return Upload.LIST_IGNORE;
-            }
-
-            let totalSize = fileSizeMB;
-            for (const f of oldFileList) {
-                totalSize += (f.size / 1024 / 1024);
-            }
-            if (totalSize > 150) {
-                message.error('文件总大小超过150M');
-                return Upload.LIST_IGNORE;
-            }
-
-            // 阻止自动上传
-            return false;
-        },
-        onChange(info) {
-            setOldFileList(info.fileList);
-            if (standardClassification === '3' && info.fileList.length > 0) {
-                const latest = info.fileList[info.fileList.length - 1];
-                if (latest && latest.originFileObj) {
-                    const nameParts = latest.name.split('.');
-                    const nameWithoutExt = nameParts.length > 1 ? nameParts.slice(0, -1).join('.') : latest.name;
-                    form.setFieldsValue({
-                        name: nameWithoutExt
-                    });
-                    setOldfileName(latest.name);
-                }
-            }
-        },
-        onDrop(e) {
-            console.log('Dropped files', e.dataTransfer.files);
-        },
-        onRemove(file) {
-            // 先从本地受控 fileList 中移除
-            setOldFileList((prev: any[]) => (prev || []).filter((f) => f.uid !== file.uid));
-            // 如果该文件已上传并携带后端返回数据,则尝试删除 OSS 记录
-            const resFile = file?.response?.data;
-            if (resFile?.ossId) {
-                onDeleteOssFileApi(`${resFile.ossId}`);
-                setFileOssList((pre: any[]) => (pre || []).filter((item: any) => item.ossId !== resFile.ossId));
-            }
-            return true;
-        }
-
-    };
-    // 获取选择分类
-    const fetchAppstandardClass = async () => {
-        try {
-            const res = await apis.fetchTakaiAppTypeList('standard_classification');
-            const list = res.data.map((item: any) => {
-                return {
-                    label: item.dictLabel,
-                    value: item.dictValue,
-                    remark: item.remark,
-                }
-            });
-            setStandardClass(list);
-            form.setFieldsValue({
-                standardClassification: list[1]?.value
-            })
-            setStandardClassification(list[1]?.value)
-        } catch (error: any) {
-            console.error(error);
-        }
-    }
-    React.useEffect(() => {
-        if (open) {
-            form.resetFields();
-            fetchParsingTypeList();
-            fetchsplittingType();
-            fetchAppstandardClass();
-        }
-    }, [open]);
-
-    // 点击确定
-    const handleClickConfirm = () => {
-        form.validateFields().then(async (values) => {
-            // 这里可以处理表单数据的提交
-            const maxCount = standardClassification === '3' ? 1 : 5;
-            if (!oldFileList || oldFileList.length === 0) {
-                message.warning('请选择文件')
-                return
-            }
-            if (oldFileList.length > maxCount) {
-                message.warning(`最多只能上传 ${maxCount} 个文件`)
-                return
-            }
-
-            setUploadLoading(true);
-
-            try {
-                // 上传所有文件
-                const uploadResults = await Promise.all(oldFileList.map((file: any) => uploadOneFile(file)));
-
-                // 构造 ossItems
-                let ossItems = oldFileList.map((f: any, index: number) => {
-                    const data = uploadResults[index];
-                    const nameParts = f.name.split('.');
-                    return {
-                        uid: f.uid,
-                        name: nameParts.length > 1 ? nameParts.slice(0, -1).join('.') : f.name.split('.')[0],
-                        ...data
-                    };
-                });
-
-                // 过滤掉已经在过程中被删除的文件
-                const currentUids = oldFileListRef.current?.map((f: any) => f.uid) || [];
-                const removedItems = ossItems.filter((item: any) => !currentUids.includes(item.uid));
-                ossItems = ossItems.filter((item: any) => currentUids.includes(item.uid));
-                removedItems.forEach((resFile: any) =>{
-                    if (resFile.ossId)
-                    onDeleteOssFileApi(`${resFile.ossId}`);
-                })
-
-                setFileOssList(ossItems);
-
-                const params = {
-                    ...values,
-                    parsingType: values.parsingType,
-                    standardClassification,
-                    fileList: ossItems.map((item: any) => ({
-                        ...item,
-                        suffix: (item.fileName || item.name || '').split('.').pop(),
-                    })),
-                }
-
-                // checkFileApi 检查文件是否存在 如果已存在就提示 未存在成功
-                const checkParams = {
-                    ...values,
-                    parsingType: values.parsingType,
-                    standardClassification,
-                    ossParms: ossItems,
-                    knowledgeId: knowledgeId
-                }
-
-                const checkRes: any = await apis.checkFileApi(checkParams);
-                const checkResData = checkRes.data || [];
-
-                if (!checkResData?.length) {
-                    await onUploadDocumentApi(params);
-                    setUploadLoading(false);
-                } else {
-                    setUploadLoading(false);
-                    Modal.confirm({
-                        title: '提示',
-                        content: checkResData.map((item: any) => {
-                            return <div key={item.ossId}>文件“{item.newName}”已存在,知识库中已存在的文件名是“{item.oldName}”</div>
-                        }),
-                        onOk: async () => {
-                            setUploadLoading(true);
-                            // 过滤掉已存在的文件(根据 ossId)
-                            const filtered = ossItems.filter((f: any) => !checkResData.some((d: any) => d.ossId === f.ossId));
-
-                            if (!filtered || filtered.length === 0) {
-                                // 所有文件都已存在,提示并结束
-                                message.warning('选中文件均已存在知识库,未执行上传。');
-                                setUploadLoading(false);
-                                return;
-                            }
-
-                            // 构建参数并继续上传剩余文件
-                            const nextParams = {
-                                ...values,
-                                parsingType: values.parsingType,
-                                standardClassification,
-                                fileList: filtered.map((item: any) => ({
-                                    ...item,
-                                    suffix: (item.fileName || item.name || '').split('.').pop(),
-                                })),
-                            };
-                            await onUploadDocumentApi(nextParams);
-                            setUploadLoading(false);
-                        },
-                        onCancel: () => {
-                            setUploadLoading(false);
-                        }
-                    });
-                }
-            } catch (error) {
-                setUploadLoading(false);
-                console.error(error);
-            }
-        }).catch((error) => {
-            console.error(error);
-        });
-    };
-    const onUploadDocumentApi = async (params: any) => {
-        const res = await apis.uploadDocumentApi(knowledgeId, params)
-
-        if (res.code === 200) {
-            // 上传逻辑已在上传组件的 onChange 中处理
-            message.success('操作成功');
-            onCancel();
-            onSuccess();
-            form.resetFields();
-        }
-    }
-    // 点击取消
-    const handleClickCancel = () => {
-        form.resetFields();
-        onCancel();
-    };
-    return (
-        <Modal
-            width={600}
-            title='上传知识文件'
-            maskClosable={false}
-            centered={true}
-            open={open}
-            onCancel={handleClickCancel}
-            footer={
-                <div>
-                    <Button
-                        className="ant-btn ant-btn-default"
-                        onClick={handleClickCancel}
-                        style={{ marginRight: 8 }}
-                    >
-                        取消
-                    </Button>
-                    <Button
-                        className="ant-btn ant-btn-primary"
-                        onClick={handleClickConfirm}
-                        loading={uploadLoading}
-                    >
-                        确定
-                    </Button>
-                </div>
-            }
-        >
-            <Spin spinning={false}>
-                <Form form={form} layout='vertical'>
-                    <FormItem
-                        label='选择分类'
-                        name='standardClassification'
-                        rules={[{ required: true, message: '请选择分类' }]}
-                    >
-                        <Select
-                            style={{ width: '100%' }}
-                            placeholder='请选择分类'
-                            optionLabelProp="label"
-                            allowClear={true}
-                            value={standardClassification}
-                            onChange={(e) => {
-                                // form.setFieldsValue({
-                                //     standardClassification: e,
-                                // })
-                                let nextFormatb = 'md,txt,pdf,doc,docx,odt,rtf,ods,ppt,pptx,odp';
-
-                                if(e === '0') {
-                                    nextFormatb = 'md,txt,pdf,doc,docx,odt,rtf,ods,ppt,pptx,odp';
-                                }
-                                if(e === '1') {
-                                    nextFormatb = 'md,txt,pdf,doc,docx,odt,rtf,ods,ppt,pptx,odp,jpg,png,jpeg';
-                                }
-                                if(e === '2') {
-                                    nextFormatb = 'xlsx';
-                                }
-                                if(e === '3') {
-                                    // nextFormatb = 'md,txt,pdf,jpg,png,jpeg,doc,docx,odt,rtf,xls,xlsx,ppt,pptx,odp';
-                                    nextFormatb = '*';
-                                }
-                                // 切换分类时,判断是新的类型是否支持当前选中的这些文件 如果不支持就给提示让手动去清除
-                                const allowedExtensions = nextFormatb.split(',');
-                                const invalidFiles = oldFileList.filter((file: any) => {
-                                    const fileExt = file.name.split('.').pop()?.toLowerCase();
-                                    return fileExt && !allowedExtensions.includes(fileExt);
-                                });
-                                if (invalidFiles.length > 0) {
-                                    // 还原Select选中值
-                                    form.setFieldsValue({
-                                        standardClassification: standardClassification
-                                    });
-                                    Modal.confirm({
-                                        title: '提示',
-                                        content: invalidFiles.map((item: any) => {
-                                            return <div key={item.uid}>文件“{item.name}”,与所选分类不匹配</div>
-                                        }),
-                                        onOk: () => {
-                                            
-                                        }
-                                    })
-                                    return;
-                                }
-
-                                setStandardClassification(e);
-                                const oldfileOssIds = fileOssList.map((item: any) => item.ossId).join(',');
-                                if (oldfileOssIds) {
-                                    onDeleteOssFileApi(oldfileOssIds);
-                                }
-                                // setOldFileList([]);
-                                // setFileOssList([]);
-                                setOldfileName('');
-                                if (e === '0') {
-                                    // 标准规范
-                                    const newSplittingList = splittingOldList.filter((item: any) => item.value === '3');
-                                    setSplittingType(newSplittingList);
-                                    form.setFieldsValue({
-                                        customSeparator: newSplittingList[0]?.value,
-                                    });
-
-                                    const newParsingTypeList = parsingTypeListBackup.filter((item: any) => item.value === '0');
-                                    setParsingTypeList(newParsingTypeList);
-
-                                    form.setFieldsValue({
-                                        parsingType: newParsingTypeList[0]?.value,
-                                    })
-                                } else {
-                                    setSplittingType(splittingOldList);
-                                    // parsingTypeListBackup
-                                    setParsingTypeList(parsingTypeListBackup);
-                                }
-                                if (e === '1') {
-                                    // 通用规范
-                                    form.setFieldsValue({
-                                        customSeparator: '2',
-                                    });
-                                }
-                                setFormatb(nextFormatb);
-                                // 清空文件名称
-                                form.setFieldsValue({
-                                    name: '',
-                                });
-                            }}
-                        >
-                            {
-                                standardClass.map((item, index) => {
-                                    return <Option value={item.value} key={index} label={item.label}>
-                                        {item.label}
-                                        <Tooltip title={item.remark} >
-                                            <QuestionCircleOutlined style={{ color: '#FF9800', marginLeft: '3px' }} />
-                                        </Tooltip>
-                                    </Option>
-                                })
-                            }
-                        </Select>
-                    </FormItem>
-                    <div style={{ marginBottom: 20, width: '100%', minHeight: '150px' }}>
-                        <Dragger {...uploadProps}>
-                            <p className="ant-upload-drag-icon">
-                                <InboxOutlined />
-                            </p>
-                            <p>
-                                点击上传,或拖放文件到此处
-                            </p>
-                            {standardClassification==='3'?<p className="ant-upload-hint">
-                                支持常规日程工作中的大部分文件格式
-                            </p>:
-                            <p className="ant-upload-hint">
-                                支持文件格式{formatb}
-                                ,<br/>单个文件 ≤ 50M
-                            </p>}
-                        </Dragger>
-                    </div>
-                    {(standardClassification === '0' || standardClassification === '1') &&
-                        <><FormItem
-                            label='解析工具'
-                            name='parsingType'
-                            rules={[{ required: true, message: '请解析工具' }]}
-                        >
-                            <Select
-                                style={{ width: '100%' }}
-                                placeholder='请解析工具'
-                                allowClear={true}
-                                onChange={(e) => {
-                                    setParsingType(e)
-                                    if (e === '2') {
-                                        const newSplittingList = splittingOldList.filter((item: any) => item.value !== '3');
-                                        setSplittingType(newSplittingList);
-                                        form.setFieldsValue({
-                                            customSeparator: newSplittingList[0]?.value,
-                                        });
-                                    } else {
-                                        setSplittingType(splittingOldList);
-                                    }
-                                }}
-                            >
-                                {
-                                    parsingTypeList.map((item, index) => {
-                                        return <Option value={item.value} key={index}>
-                                            {item.label}
-                                        </Option>
-                                    })
-                                }
-                            </Select>
-                        </FormItem>
-                            <FormItem
-                                label='默认切分规则'
-                                name='customSeparator'
-                                rules={[{ required: true, message: '默认切分规则不能为空' }]}
-                            >
-                                {
-                                    <Select
-                                        style={{ width: '100%' }}
-                                        placeholder='请选择默认切分规则'
-                                        allowClear={true}
-                                        onChange={onChangeF}
-                                        optionLabelProp="label"
-                                    >
-                                        {
-                                            splittingType.map((item: any, index: any) => {
-                                                return <Option value={item.value} key={index} label={item.label}>
-                                                    {item.label}
-                                                    <Tooltip title={item.remark} >
-                                                        <QuestionCircleOutlined style={{ color: '#FF9800', marginLeft: '3px' }} />
-                                                    </Tooltip>
-                                                </Option>
-                                            })
-                                        }
-                                    </Select>
-                                }
-                            </FormItem></>}
-                    {isVisibleSlice &&
-                        <FormItem
-                            label='分隔符'
-                            name='sliceValue'
-                            rules={[{ required: true, message: '自定义切片设置不能为空' }]}
-                        >
-                            <Input max={1024}
-                            />
-                        </FormItem>
-                    }
-                    {standardClassification === '3' && <><FormItem
-                        label='文件名称'
-                        name='name'
-                        rules={[{ required: true, message: '文件名称不能为空', whitespace: true }]}
-                    >
-                        <Input placeholder='请输入文件名称' />
-                    </FormItem>
-                        <FormItem
-                            label='描述'
-                            name='remark'
-                            rules={[{ required: true, message: '描述不能为空', whitespace: true }]}
-                        >
-                            <TextArea
-                                placeholder='请输入描述信息'
-                                showCount={true}
-                                rows={6}
-                                maxLength={500}
-                            />
-                        </FormItem></>}
-                </Form>
-            </Spin>
-        </Modal>
-    );
-};
-
-export default UploadModal;
-

+ 0 - 437
jk-rag-platform/src/pages/knowledgeLib/detail/components/prevewSlice.tsx.bak

@@ -1,437 +0,0 @@
-// 查看切片
-import React, { useEffect, useMemo, useState, useRef, useLayoutEffect } from 'react';
-
-import { Drawer, Row, Col, Input, Button, List, Typography, Select, Spin, Image, message } from 'antd';
-import type { DrawerProps, RadioChangeEvent } from 'antd';
-import { SnippetsOutlined } from '@ant-design/icons';
-import MarkdownIt from 'markdown-it';
-import { fetchReviseToolAllList, fetchReviseToolSliceList, apis } from '@/apis';
-import '../../revisionTool/components/reviseDrawer.less'
-import { Document, Page, pdfjs } from 'react-pdf';
-
-interface ReviseDrawerProps {
-    openDrawer: boolean;
-    onClose: () => void;
-    title: string;
-    record: any;
-}
-// 设置 pdf.worker(使用 CDN 作为回退)
-try {
-    pdfjs.GlobalWorkerOptions.workerSrc = (pdfjs.GlobalWorkerOptions.workerSrc || '') || '//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js';
-} catch (e) {
-    // ignore
-}
-
-// =================== PDF 子组件(文件级,最重要) ===================
-
-const LazyPage = React.memo(
-    ({
-        pageNumber,
-        width,
-        containerRef,
-    }: {
-        pageNumber: number;
-        width: number;
-        containerRef: React.RefObject<HTMLDivElement>;
-    }) => {
-        const elRef = React.useRef<HTMLDivElement | null>(null);
-        const [visible, setVisible] = React.useState(false);
-
-        React.useEffect(() => {
-            const el = elRef.current;
-            const root = containerRef.current;
-            if (!el || !root) return;
-
-            const io = new IntersectionObserver(
-                entries => {
-                    entries.forEach(e => e.isIntersecting && setVisible(true));
-                },
-                { root, rootMargin: '400px' }
-            );
-
-            io.observe(el);
-            return () => io.disconnect();
-        }, [containerRef]);
-
-        return (
-            <div ref={elRef} style={{ background: '#fff' }}>
-                {visible ? (
-                    <Page
-                        pageNumber={pageNumber}
-                        // width={width}
-                        renderTextLayer={false}
-                        renderAnnotationLayer={false}
-                        className='pdf-page'
-                    />
-                ) : (
-                    <div
-                        style={{
-                            width: '100%',
-                            height: Math.floor(width * 1.3),
-                            background: '#fafafa',
-                        }}
-                    />
-                )}
-            </div>
-        );
-    }
-);
-
-const PdfViewer = React.memo(
-    ({
-        file,
-        numPages,
-        pageWidth,
-        scale,
-        onLoadSuccess,
-        containerRef,
-    }: {
-        file: string;
-        numPages: number;
-        pageWidth: number;
-        scale: number;
-        onLoadSuccess: any;
-        containerRef: React.RefObject<HTMLDivElement>;
-    }) => {
-        return (
-            <Document
-                // renderMode="canvas"
-                file={file}
-                onLoadSuccess={onLoadSuccess}
-            >
-                {Array.from({ length: numPages }).map((_, idx) => (
-                    <LazyPage
-                        key={idx}
-                        pageNumber={idx + 1}
-                        width={Math.floor(pageWidth * scale)}
-                        containerRef={containerRef}
-                    />
-                ))}
-            </Document>
-        );
-    },
-    (prev, next) =>
-        prev.file === next.file &&
-        prev.numPages === next.numPages &&
-        prev.pageWidth === next.pageWidth &&
-        prev.scale === next.scale
-);
-
-const ReviseDrawer: React.FC<ReviseDrawerProps> = (props: ReviseDrawerProps) => {
-    const { openDrawer, onClose, title, record } = props;
-    const [placement, setPlacement] = useState<DrawerProps['placement']>('right');
-
-    const onGetSliceListByDocumentId = async (documentId: string) => {
-        try {
-            const res: any = await apis.getSliceListByDocumentId(documentId);
-            if (res && res.data && Array.isArray(res.data)) {
-                const results = res.data;
-                results.forEach((item: any) => {
-                    item.sliceText = customRender(item.sliceText, item.mediaList)
-                })
-                setRightSlices(results);
-            } else {
-                return [];
-            }
-        } catch (err) {
-            return [];
-        }
-    };
-
-    useEffect(() => {
-        if (openDrawer) {
-            console.log('预览切片record', record);
-            onGetSliceListByDocumentId(record.documentId);
-        }
-    }, [openDrawer]);
-
-    const [leftSearch, setLeftSearch] = useState('');// 左边搜索
-    const [rightSearch, setRightSearch] = useState(''); // 右边搜索
-    const [leftSpliceSearch, setLeftSpliceSearch] = useState('');// 左边切片搜索
-    const [rightSpliceSearch, setRightSpliceSearch] = useState('');// 右边切片搜索
-    const [revisionOptions, setRevisionOptions] = useState<any[]>([]);
-    const [revisionLoading, setRevisionLoading] = useState(false);
-    const [selectedRevision, setSelectedRevision] = useState<string | undefined>(undefined);
-    const [leftDocumentId, setLeftDocumentId] = useState(''); // 左边选中的文档ID
-    const [rightDocumentId, setRightDocumentId] = useState(''); // 右边选中的文档ID
-    const [leftOptions, setLeftOptions] = useState<any[]>([]);// 左边数据
-    const [rightOptions, setRightOptions] = useState<any[]>([]);// 右边数据
-    const [leftLoading, setLeftLoading] = useState(false);// 左边加载状态
-    const [rightLoading, setRightLoading] = useState(false);// 右边加载状态
-    const [selectedStandard, setSelectedStandard] = useState<string | null>(null);
-    const [leftSlices, setLeftSlices] = useState<string[]>([]);// 左边切片列表
-    const [rightSlices, setRightSlices] = useState<string[]>([]);// 右边切片列表
-    const [leftSlicesLoading, setLeftSlicesLoading] = useState(false);// 左边切片加载状态
-    const [rightSlicesLoading, setRightSlicesLoading] = useState(false);// 右边切片加载状态
-    const [pdfBlobUrl, setPdfBlobUrl] = useState<string | null>(null);
-    const [pdfLoading, setPdfLoading] = useState(false);
-    const [largeFile, setLargeFile] = useState(false);
-    const [pageNumber, setPageNumber] = useState<number>(1);
-    const [scale, setScale] = useState<number>(1.0);
-    const containerRef = useRef<HTMLDivElement | null>(null);
-    const [pageWidth, setPageWidth] = useState<number>(600);
-    const marked = new MarkdownIt({ html: true, typographer: true });
-    useEffect(() => {
-        if (leftDocumentId) {
-            onFetchReviseToolSliceList(leftDocumentId, 'left');
-        }
-    }, [leftDocumentId]);
-    useEffect(() => {
-        if (rightDocumentId) {
-            onFetchReviseToolSliceList(rightDocumentId, 'right');
-        }
-    }, [rightDocumentId]);
-    const onChange = (e: RadioChangeEvent) => {
-        setPlacement(e.target.value);
-    };
-
-    const onFetchTakaiAppTypeListApi = async () => {
-        setRevisionLoading(true);
-        try {
-            const res: any = await apis.fetchTakaiAppTypeList('revision_status');
-            if (res && res.data && Array.isArray(res.data)) {
-                const opts = res.data.map((it: any) => ({ label: it.dictLabel, value: it.dictValue }));
-                setSelectedRevision(opts[0].value);
-                setRevisionOptions(opts);
-            } else {
-                setRevisionOptions([]);
-            }
-        } catch (err) {
-            setRevisionOptions([]);
-        } finally {
-            setRevisionLoading(false);
-        }
-    };
-    useEffect(() => {
-        onFetchTakaiAppTypeListApi();
-    }, []);
-    const customRender = (text: string, mdImgUrlList: any[]) => {
-        // 比如:把 "我是图片" 替换成一张图片
-        mdImgUrlList?.forEach(item => {
-            text = text.replace(new RegExp(item.originText, 'g'), `<img src="${item.mediaUrl}" alt="${item.originText}" />`);
-        })
-        return text;
-    }
-    // 获取知识库修订工具切片列表
-    const onFetchReviseToolSliceList = async (documentId: string, side?: 'left' | 'right', sliceText?: string) => {
-        setLeftSlicesLoading(side === 'left' ? true : leftSlicesLoading);
-        setRightSlicesLoading(side === 'right' ? true : rightSlicesLoading);
-        // Implement the function to fetch revise tool slice list
-        const res: any = await fetchReviseToolSliceList({ documentId: documentId || '', sliceText: sliceText || '' });
-        // 假设接口返回结构为 { code: 200, data: [ { sliceContent }, ... ] }
-        if (res && res.data && Array.isArray(res.data)) {
-            const results = res.data;
-            results.forEach((item: any) => {
-                item.sliceText = customRender(item.sliceText, item.mediaList)
-            })
-            if (side === 'left') {
-                setLeftSlicesLoading(false);
-                setLeftSlices(results);
-            }
-            else if (side === 'right') {
-                setRightSlicesLoading(false);
-                setRightSlices(results);
-
-            }
-        } else {
-            if (side === 'left')
-                setLeftSlices([]);
-            else if (side === 'right')
-                setRightSlices([]);
-        }
-    }
-    const [LeftActiveId, setLeftActiveId] = useState<string | null>(null);
-    const [RightActiveId, setRightActiveId] = useState<string | null>(null);
-    const [subLoading, setSubLoading] = useState(false);
-    const onAllClose = () => {
-        setLeftActiveId(null);
-        setRightActiveId(null);
-        setLeftSearch('');
-        setRightSearch('');
-        setLeftSpliceSearch('');
-        setRightSpliceSearch('');
-        setLeftDocumentId('');
-        setRightDocumentId('');
-        setLeftOptions([]);
-        setRightOptions([]);
-        setLeftSlices([]);
-        setRightSlices([]);
-        onClose();
-    }
-    const [numPages, setNumPages] = useState<number>(0);
-    function onLoadSuccess({ numPages }: { numPages: number }) {
-        setNumPages(numPages);
-        setPageNumber(1);
-    }
-
-
-
-    // 测量容器宽度以计算 Page 渲染宽度
-    useLayoutEffect(() => {
-        const update = () => {
-            const el = containerRef.current;
-            if (el) {
-                const w = el.clientWidth - 20; // 留白
-                setPageWidth(Math.max(200, w));
-            }
-        };
-        update();
-        window.addEventListener('resize', update);
-        return () => window.removeEventListener('resize', update);
-    }, []);
-
-    // Fetch PDF as blob to avoid browser auto-download from Content-Disposition
-    useEffect(() => {
-        let cancelled = false;
-        let currentBlobUrl: string | null = null;
-        const fetchBlob = async () => {
-            if (!record?.pdfUrl) {
-                setPdfBlobUrl(null);
-                return;
-            }
-            try {
-                // 尝试先用 HEAD 获取文件大小,若大文件则跳过下载 blob,改用原始 URL 由 pdf.js 处理流式加载
-                const headResp = await fetch(record.pdfUrl, { method: 'HEAD', mode: 'cors' });
-                if (headResp && headResp.ok) {
-                    const len = headResp.headers.get('Content-Length');
-                    if (len) {
-                        const size = parseInt(len, 10);
-                        const threshold = 10 * 1024 * 1024; // 10MB
-                        if (!Number.isNaN(size) && size > threshold) {
-                            setLargeFile(true);
-                            // 不下载 blob,直接让 Document 使用原始 URL(支持 range 请求的服务器会更快)
-                            setPdfBlobUrl(null);
-                            return;
-                        }
-                    }
-                }
-
-                setPdfLoading(true);
-                const res = await fetch(record.pdfUrl, { method: 'GET', mode: 'cors' });
-                if (!res.ok) throw new Error('fetch failed');
-                const blob = await res.blob();
-                currentBlobUrl = URL.createObjectURL(blob);
-                if (!cancelled) {
-                    setPdfBlobUrl(prev => {
-                        if (prev) URL.revokeObjectURL(prev);
-                        return currentBlobUrl;
-                    });
-                } else {
-                    if (currentBlobUrl) URL.revokeObjectURL(currentBlobUrl);
-                }
-            } catch (err) {
-                console.error('fetch pdf blob error', err);
-                setPdfBlobUrl(null);
-            } finally {
-                if (!cancelled) setPdfLoading(false);
-            }
-        };
-        fetchBlob();
-        return () => {
-            cancelled = true;
-            if (currentBlobUrl) URL.revokeObjectURL(currentBlobUrl);
-        };
-    }, [record?.pdfUrl]);
-    return (
-        <Drawer
-            title={title}
-            placement={placement}
-            closable={true}
-            onClose={onAllClose}
-            open={openDrawer}
-            width={'90%'}
-        >
-            <div className="flex gap-4 h-full">
-                {/* Left column */}
-                <div className="w-1/2 border-r pr-4">
-                    <Spin spinning={leftSlicesLoading || pdfLoading} className="h-full">
-                        <div className="flex flex-col h-full">
-                            <div className="mb-2 flex items-center justify-end">
-                                <div className="flex items-center gap-2">
-                                    <Button size="small" onClick={() => setScale(s => Math.max(0.5, s - 0.1))}>-</Button>
-                                    <span className="text-sm">{Math.round(scale * 100)}%</span>
-                                    <Button size="small" onClick={() => setScale(s => Math.min(2, s + 0.1))}>+</Button>
-                                    <Button size="small" onClick={() => { if (record?.pdfUrl) window.open(record.pdfUrl, '_blank'); }}>在新窗口打开</Button>
-                                </div>
-                            </div>
-                            <div
-                                ref={containerRef}
-                                className="overflow-y-auto"
-                                style={{ maxHeight: 'calc(100vh - 130px)' }}
-                            >
-                                {pdfLoading ? (
-                                    <div className="text-center py-8">PDF 加载中...</div>
-                                ) : record?.pdfUrl ? (
-                                    (pdfBlobUrl || record.pdfUrl) ? (
-                                        <PdfViewer
-                                            file={pdfBlobUrl || record.pdfUrl}
-                                            numPages={numPages}
-                                            pageWidth={pageWidth}
-                                            scale={scale}
-                                            onLoadSuccess={onLoadSuccess}
-                                            containerRef={containerRef}
-                                        />
-                                    ) : (
-                                        <div className="text-center py-8">
-                                            无法生成预览,
-                                            <Button
-                                                type="link"
-                                                onClick={() => {
-                                                    if (record?.pdfUrl) window.open(record.pdfUrl, '_blank');
-                                                }}
-                                            >
-                                                在新窗口打开
-                                            </Button>
-                                        </div>
-                                    )
-                                ) : (
-                                    <div className="text-center text-gray-500 mt-10">未找到 PDF</div>
-                                )}
-                            </div>
-
-                        </div>
-                    </Spin>
-                </div>
-
-                {/* Right column (same as left) */}
-                <div className="w-1/2">
-                    <Spin spinning={rightSlicesLoading} className='h-full'>
-                        {rightSlices.length > 0 ? <div className="overflow-y-auto" style={{ maxHeight: 'calc(100vh - 130px)' }}>
-                            {rightSlices.map((module: any, index) => (
-                                <div
-                                    key={`r-${index}`}
-                                    className={`relative px-3 py-2 text-sm leading-7 transition-all duration-200 cursor-pointer 
-                                        ${index !== rightSlices.length - 1 ? 'border-b border-gray-100' : ''
-                                        }`}
-                                    onClick={() => setRightActiveId(module.sliceId)}
-                                >
-                                    {module.sliceText && (() => {
-                                        const html = marked.render(module.sliceText || '');
-                                        const parts: any[] = [];
-                                        const imgRegex = /<img[^>]*src=["']([^"']+)["'][^>]*>/g;
-                                        let lastIndex = 0;
-                                        let match: RegExpExecArray | null;
-                                        while ((match = imgRegex.exec(html)) !== null) {
-                                            const idx = match.index;
-                                            const textSeg = html.substring(lastIndex, idx);
-                                            if (textSeg) parts.push(<div key={`rt-${index}-${lastIndex}`} dangerouslySetInnerHTML={{ __html: textSeg }} />);
-                                            const url = match[1];
-                                            parts.push(<Image key={`rimg-${index}-${idx}`} src={url} preview={{}} style={{ maxWidth: '100%' }} />);
-                                            lastIndex = idx + match[0].length;
-                                        }
-                                        const rest = html.substring(lastIndex);
-                                        if (rest) parts.push(<div key={`rt-last-${index}`} dangerouslySetInnerHTML={{ __html: rest }} />);
-                                        return <div className="text-sm leading-7 text-gray-800 markdown-preview">{parts}</div>;
-                                    })()}
-                                </div>
-                            ))}
-                        </div> : <div className="text-center text-gray-500 mt-10">暂无数据</div>}
-                    </Spin>
-
-                </div>
-            </div>
-        </Drawer>
-
-    )
-}
-export default observer(ReviseDrawer);

+ 0 - 0
jk-rag-platform/src/pages/knowledgeLib/detail/components/style.less → jk-rag-platform/src/pages/knowledgeLib/detail/components/style.scss


+ 0 - 364
jk-rag-platform/src/pages/knowledgeLib/detail/drawerIndex.tsx.bak

@@ -1,364 +0,0 @@
-import * as React from 'react';
-import { generatePath, useParams, useLocation } from 'react-router-dom';
-
-
-import config, { getHeaders } from '@/apis/config';
-
-import {
-  Button,
-  Table,
-  TableColumnsType,
-  Modal,
-  TablePaginationConfig,
-  Upload,
-  UploadProps,
-  message,
-  Spin
-} from 'antd';
-import { EditOutlined, DeleteOutlined, InboxOutlined, PlusOutlined, ArrowLeftOutlined } from '@ant-design/icons';
-import InfoModal from './components/InfoModal';
-import InfoModalSetting from './components/InfoModalSetting';
-import router from '@/router';
-import { Record } from './types';
-import dayjs from 'dayjs';
-import axios from 'axios';
-import LocalStorage from '@/LocalStorage';
-import store from './store';
-import './style.less';
-
-const { Dragger } = Upload;
-interface Props{
-    drawerItem: any;
-}
-
-const KnowledgeLibInfo : React.FC<Props> = ({drawerItem}:Props) => {
-  const params = {
-    knowledgeId: drawerItem.value,
-    ...drawerItem,
-  }
-
-  const {
-    state,
-    init,
-    onClickModify,
-    onClickDelete,
-    onChangePagination,
-    onClickDocumentDetail,
-    infoModalOnClickConfirm,
-    infoModalOnClickCancel,
-    infoModalSettingOnClickConfirm,
-    infoModalSettingOnClickCancel,
-    onClickSettings,
-    reset
-  } = store;
-  const {
-    knowledge_id,
-    listLoading,
-    page,
-    list,
-    infoModalOpen,
-    infoModalId,
-    infoModalSettingOpen,
-    infoModalSettingId,
-    knowledgeDetail,
-  } = state;
-  const location = useLocation();
-  const [ uploadLoading, setUploadLoading ] = React.useState( false );
-
-
-  const [ fileList, setFileList ] = React.useState<any[]>( [] );
-  const [ uploading, setUploading ] = React.useState( false );
-
-  const [ sListFlag, setSListFlag ] = React.useState<boolean>();
-  const [ cUpdateFlag, setCUpdateFlag ] = React.useState<boolean>();
-  const [ detailFlag, setDetailFlag ] = React.useState<boolean>();
-  const [ deleteFlag, setDeleteFlag ] = React.useState<boolean>();
-  const [ createFlag, setCreateFlag ] = React.useState<boolean>();
-  const [userInfoAll, setUserInfoAll] = React.useState<any>({});
-
-  const props : UploadProps = {
-    name: 'files',
-    multiple: true,
-    action: '/api/deepseek/api/uploadDocument/' + params.knowledgeId,
-    headers: getHeaders(),
-    beforeUpload( file, fileList ) {
-      setUploadLoading( true );
-      // const allowedExtensions = ['md', 'txt', 'pdf', 'jpg', 'png', 'jpeg', 'docx', 'xlsx', 'pptx', 'eml', 'csv', 'tar', 'gz', 'bz2', 'zip', 'rar', 'jar'];
-
-      const allowedExtensions = [ 'txt', 'pdf', 'jpg', 'png', 'jpeg', 'doc', 'docx', 'ppt', 'pptx' ];
-
-      // 检查文件类型
-      for ( const file of fileList ) {
-        const fileExt = file.name.split( '.' ).pop()?.toLowerCase();
-        if ( !fileExt || !allowedExtensions.includes( fileExt ) ) {
-          message.error( `不支持 ${ fileExt } 格式的文件上传` );
-          setUploadLoading( false );
-          return Upload.LIST_IGNORE;
-        }
-      }
-
-      // 检查文件大小
-      let totalSize = 0;
-      for ( const file of fileList ) {
-        const fileExt = file.name.split( '.' ).pop()?.toLowerCase();
-        const fileSizeMB = file.size / 1024 / 1024;
-
-        if ( fileSizeMB > 30 ) {
-          message.error( '单个文件不能大于30M' );
-          setUploadLoading( false );
-          return Upload.LIST_IGNORE;
-        }
-
-        if ( [ 'jpg', 'png', 'jpeg' ].includes( fileExt! ) && fileSizeMB > 5 ) {
-          message.error( '单张图片不能大于5M' );
-          setUploadLoading( false );
-          return Upload.LIST_IGNORE;
-        }
-
-        totalSize += fileSizeMB;
-      }
-
-      if ( totalSize > 125 ) {
-        message.error( '文件总大小超过125M' );
-        setUploadLoading( false );
-        return Upload.LIST_IGNORE;
-      }
-
-    },
-    onChange( info ) {
-      const { status } = info.file;
-
-      if ( status !== 'uploading' ) {
-        console.log( status, 'status--uploading' );
-      }
-      if ( status === 'done' ) {
-        console.log( status, 'status--done' );
-        console.info( info.file.response, 'info.file.response.data' );
-        if ( info.file.response.code === 200 && info.file.response.data === 1 ) {
-          message.success( `${ info.file.name } file uploaded successfully.` );
-          init( params.knowledgeId );
-        }
-        setUploadLoading( false );
-      } else if ( status === 'error' ) {
-        console.log( status, 'status--error' );
-        message.error( `${ info.file.name } file upload failed.` );
-        setUploadLoading( false );
-      }
-    },
-    onDrop( e ) {
-      console.log( 'Dropped files', e.dataTransfer.files );
-    },
-  };
-
-  const handleUpload = async () => {
-    if ( fileList.length === 0 ) return;
-
-    setUploading( true );
-    const formData = new FormData();
-
-    // 添加所有文件
-    fileList.forEach( file => {
-      if ( file.originFileObj ) {
-        formData.append( 'files', file.originFileObj );
-      }
-    } );
-
-    try {
-      const res = await axios.post( '/api/deepseek/api/uploadDocument/' + params.knowledgeId, formData, {
-        headers: { 'Content-Type': 'multipart/form-data' }
-      } );
-
-      message.success( `${ fileList.length }个文件上传成功` );
-      setFileList( [] );
-    } catch ( err ) {
-      message.error( '上传失败' );
-    } finally {
-      setUploading( false );
-    }
-  };
-
-  React.useEffect( () => {
-    init( params.knowledgeId );
-    const cList = LocalStorage.getStatusFlag( 'deepseek:slice:list' );
-    setSListFlag( cList );
-    const cDetail = LocalStorage.getStatusFlag( 'deepseek:config:update' );
-    setCUpdateFlag( cDetail );
-    const detail = LocalStorage.getStatusFlag( 'deepseek:document:detail' );
-    setDetailFlag( detail );
-    const deleteF = LocalStorage.getStatusFlag( 'deepseek:document:delete' );
-    setDeleteFlag( deleteF );
-    const createF = LocalStorage.getStatusFlag( 'deepseek:document:create' );
-    setCreateFlag( createF );
-    setUserInfoAll(LocalStorage.getUserInfo());
-    return () => reset();
-  }, [params.knowledgeId] );
-
-  const columns : TableColumnsType<Record> = [
-    {
-      title: '序号',
-      dataIndex: 'index',
-      width: 80,
-      render: ( text, record, index ) => {
-        return index + 1;
-      }
-    },
-    {
-      title: '文件名',
-      dataIndex: 'name',
-      width: 300,
-      sorter: (a, b) => a.name.localeCompare(b.name),
-      render: ( text, record ) => {
-        return (
-            `${ text }`
-        )
-      }
-    },
-    {
-      title: '文件大小',
-      dataIndex: 'length',
-      width: 100,
-      render: ( text ) => {
-        if ( text ) {
-          const size = ( text / 1024 / 1024 ).toFixed( 2 );
-          return `${ size } M`;
-        } else {
-          return '--'
-        }
-      }
-    },
-    {
-      title: '字符数量',
-      dataIndex: 'wordNum',
-      width: 100,
-      render: ( text ) => {
-        if ( text ) {
-          return `${ text }`;
-        } else {
-          return '--'
-        }
-      }
-    },
-    {
-      title: '分段',
-      dataIndex: 'sliceTotal',
-      width: 100,
-      render: ( text ) => {
-        if ( text ) {
-          return `${ text }`;
-        } else {
-          return '--';
-        }
-      }
-    },
-    {
-      title: '上传时间',
-      dataIndex: 'createTime',
-      width: 180,
-      render: ( text ) => {
-        if ( text ) {
-          return dayjs( text ).format( 'YYYY-MM-DD HH:mm:ss' );
-        } else {
-          return '--';
-        }
-      }
-    },
-    {
-      title: '更新时间',
-      dataIndex: 'updateTime',
-      width: 180,
-      render: ( text ) => {
-        if ( text ) {
-          return dayjs( text ).format( 'YYYY-MM-DD HH:mm:ss' );
-        } else {
-          return '--';
-        }
-      }
-    },
-    {
-      title: '操作',
-      dataIndex: 'operation',
-      width: 80,
-      fixed: 'right',
-      render: ( text, record:any ) => {
-        return (
-            <>
-              {
-                  <a
-                      style={ { marginRight: 16 } }
-                      onClick={ () => {
-                        // window.location= record.url
-                        window.open(`${record.url}`, '_blank');
-                        // window.open(`https://10.1.28.14:9000/deepseek-doc/%E6%95%B0%E5%AD%97%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86%E5%B9%B3%E5%8F%B0%E7%94%A8%E6%88%B7%E6%93%8D%E4%BD%9C%E6%89%8B%E5%86%8C_%E9%83%A8%E5%88%863.pdf`, '_blank');
-                      } }
-                  >
-                    查看
-                  </a>
-              }
-            </>
-        )
-      }
-    }
-  ];
-
-  const paginationConfig : TablePaginationConfig = {
-    // 显示数据总量
-    showTotal: ( total : number ) => {
-      return `共 ${ total } 条`;
-    },
-    // 展示分页条数切换
-    showSizeChanger: true,
-    // 指定每页显示条数
-    pageSizeOptions: [ '10', '20', '50', '100' ],
-    // 快速跳转至某页
-    showQuickJumper: true,
-    current: page.page,
-    pageSize: page.size,
-    total: page.total,
-    onChange: async ( page, pageSize ) => {
-      await onChangePagination( page, pageSize );
-    },
-  };
-
-  return (
-      <div className='knowledgeLibInfo'>
-        <Spin spinning={ uploadLoading || listLoading }>
-          {
-              <>
-                <div className='knowledgeLibInfo-table'>
-                  <Table
-                      scroll={ { x: 'max-content' } }
-                      rowKey={ ( record ) => record.documentId }
-                      loading={ listLoading }
-                      columns={ columns }
-                      dataSource={ list }
-                      pagination={ paginationConfig }
-                  />
-                </div>
-
-                {
-                    infoModalOpen &&
-                    <InfoModal
-                        id={ infoModalId }
-                        open={ infoModalOpen }
-                        onClickConfirm={ infoModalOnClickConfirm }
-                        onClickCancel={ infoModalOnClickCancel }
-                    />
-                }
-
-                {
-                    infoModalSettingOpen &&
-                    <InfoModalSetting
-                        id={ infoModalSettingId }
-                        open={ infoModalSettingOpen }
-                        onClickConfirm={ infoModalSettingOnClickConfirm }
-                        onClickCancel={ infoModalSettingOnClickCancel }
-                    />
-                }
-              </>
-          }
-        </Spin>
-      </div>
-  );
-};
-
-export default observer( KnowledgeLibInfo );

+ 0 - 680
jk-rag-platform/src/pages/knowledgeLib/detail/index.tsx.bak

@@ -1,680 +0,0 @@
-import * as React from 'react';
-import { generatePath, useParams, useLocation, useNavigate } from 'react-router-dom';
-
-import PrevewSlice from './components/prevewSlice';
-
-import config, { getHeaders } from '@/apis/config';
-
-import {
-  Button,
-  Table,
-  TableColumnsType,
-  Modal,
-  TablePaginationConfig,
-  Upload,
-  UploadProps,
-  message,
-  Tooltip,
-  Spin,
-  Input,
-  Space
-} from 'antd';
-import type { TableProps } from 'antd';
-
-import { EditOutlined, DeleteOutlined, InboxOutlined, PlusOutlined, ArrowLeftOutlined, EyeOutlined, ExportOutlined, FileTextOutlined, ArrowDownOutlined, SearchOutlined } from '@ant-design/icons';
-import InfoModal from './components/InfoModal';
-import InfoModalSetting from './components/InfoModalSetting';
-import UploadModal from './components/UploadModal';
-import MdModal from './components/MdModal'
-import QuoteModal from './components/QuoteModal'
-import router from '@/router';
-import { Record } from './types';
-import dayjs from 'dayjs';
-import axios from 'axios';
-import LocalStorage from '@/LocalStorage';
-import store from './store';
-import './style.less';
-import { apis } from '@/apis';
-import { useLayoutStore } from '@/pages/layout/store';
-const { Dragger } = Upload;
-
-const KnowledgeLibInfo: React.FC = () => {
-  const navigate = useNavigate()
-  const {
-    handleNameChange,
-    state,
-    init,
-    onClickModify,
-    onClickDelete,
-    onChangePagination,
-    onClickDocumentDetail,
-    infoModalOnClickConfirm,
-    infoModalOnClickCancel,
-    infoModalSettingOnClickConfirm,
-    infoModalSettingOnClickCancel,
-    onClickSettings,
-    reset,
-    onDeleteTakaiDocumentLibApi,
-    onExportDocumentSliceExport
-  } = store;
-  const { onSetRoouterName } = layoutStore
-  const {
-    knowledge_id,
-    listLoading,
-    page,
-    list,
-    processingList, // 处理中的状态
-    infoModalOpen,
-    infoModalId,
-    infoModalSettingOpen,
-    infoModalSettingId,
-    knowledgeDetail,
-  } = state;
-  const location = useLocation();
-  const [uploadLoading, setUploadLoading] = React.useState(false);
-
-  const params = useParams();
-
-  const [mdModalOpen, setMdModalOpen] = React.useState(false); // 切片弹窗
-  const [quoteModalOpen, setQuoteModalOpen] = React.useState(false); // 引用知识库弹窗
-  const [detailDocument, setDetailDocument] = React.useState<any>({}); // 切片详情
-  const [userInfoAll, setUserInfoAll] = React.useState<any>({});
-  const [uploadModalOpen, setUploadModalOpen] = React.useState<boolean>(false);
-  const [record, setRecord] = React.useState<any>({});
-  const [deletingIds, setDeletingIds] = React.useState<string[]>([]);
-  const [standardClass, setStandardClass] = React.useState<any[]>([]);// 分类-下拉选项
-  const [parsingTypeList, setParsingTypeList] = React.useState<any[]>([]);// 解析工具-下拉选项
-  const [splittingType, setSplittingType] = React.useState<any>([]); // 选择切分规则 
-  // 获取选择分类
-  const fetchAppstandardClass = async () => {
-    try {
-      const res = await apis.fetchTakaiAppTypeList('standard_classification');
-      const list = res.data.map((item: any) => {
-        return {
-          label: item.dictLabel,
-          value: item.dictValue,
-        }
-      });
-      setStandardClass(list);
-    } catch (error: any) {
-      console.error(error);
-    }
-  }
-  // 获取解析工具
-  const fetchParsingTypeList = async () => {
-    try {
-      const res = await apis.fetchTakaiAppTypeList('parsing_type');
-      const list = res.data.map((item: any) => {
-        return {
-          label: item.dictLabel,
-          value: item.dictValue,
-        }
-      });
-      setParsingTypeList(list);
-    } catch (error: any) {
-      console.error(error);
-    }
-  }
-  // 获取向默认切分规则
-  const fetchsplittingType = async () => {
-    try {
-      const res = await apis.fetchTakaiAppTypeList('splitting_type');
-      const list = res.data.map((item: any) => {
-        return {
-          label: item.dictLabel,
-          value: item.dictValue,
-        }
-      });
-      setSplittingType(list);
-    } catch (error: any) {
-      console.error(error);
-    }
-  }
-  const [cUpdateFlag, setCUpdateFlag] = React.useState<boolean>();
-  const [deleteFlag, setDeleteFlag] = React.useState<boolean>(); // 删除权限
-  const [createFlag, setCreateFlag] = React.useState<boolean>();
-  const [updateDoc, setupdateDoc] = React.useState<boolean>(false); // 上传知识文件权限
-  const [quoteDoc, setQuoteDoc] = React.useState<boolean>(false); // 引用知识文件权限
-  const [exportSlice, setExportSlice] = React.useState<boolean>(false); // 导出切片权限
-  const [exportMd, setExportMd] = React.useState<boolean>(false); // 导出Md权限
-  const [exportOr, setExportOr] = React.useState<boolean>(false); // 导出原文件权限
-  const [slicPre, setSlicePre] = React.useState<boolean>(false); // 查看切片
-  const [configuration, setConfiguration] = React.useState<boolean>(false); // 配置权限
-  const [editName, setEditName] = React.useState<boolean>(false); // 编辑名称
-  React.useEffect(() => {
-    fetchAppstandardClass();
-    fetchParsingTypeList();
-    fetchsplittingType();
-    init(params.knowledgeId);
-    setupdateDoc(LocalStorage.getStatusFlag('knowledgeLib:detail:update'));
-    setQuoteDoc(LocalStorage.getStatusFlag('knowledgeLib:detail:quote'));
-    setExportSlice(LocalStorage.getStatusFlag('knowledgeLib:detail:exportSlice'));
-    setExportMd(LocalStorage.getStatusFlag('knowledgeLib:detail:exportMd'));
-    setExportOr(LocalStorage.getStatusFlag('knowledgeLib:detail:exportOr'));
-    setSlicePre(LocalStorage.getStatusFlag('knowledgeLib:detail:slice'));
-    setConfiguration(LocalStorage.getStatusFlag('knowledgeLib:detail:configuration'));
-    setEditName(LocalStorage.getStatusFlag('knowledgeLib:detail:editName'));
-
-
-    const cDetail = LocalStorage.getStatusFlag('deepseek:config:update');
-    setCUpdateFlag(cDetail);
-    const deleteF = LocalStorage.getStatusFlag('knowledgeLib:detail:del');
-    setDeleteFlag(deleteF);
-    const createF = LocalStorage.getStatusFlag('deepseek:document:create');
-    setCreateFlag(createF);
-    setUserInfoAll(LocalStorage.getUserInfo());
-    return () => reset();
-  }, []);
-
-  React.useEffect(() => {
-    // console.log('knowledgeDetail',knowledgeDetail)
-    if (knowledgeDetail.name) {
-      onSetRoouterName(knowledgeDetail.name)
-    }
-  }, [knowledgeDetail])
-
-  const [documentids, setdocumentids] = React.useState<string[]>([]);
-  const rowSelection: TableProps<Record>['rowSelection'] = {
-    onChange: (selectedRowKeys: React.Key[], selectedRows: Record[]) => {
-      const flagids = selectedRows.map((item) => {
-        return item.documentId
-      })
-      setdocumentids(flagids)
-    },
-  };
-
-    const [openDrawer, setOpenDrawer] = React.useState<boolean>(false);
-
-
-  const columns: TableColumnsType<Record> = [
-    {
-      title: '序号',
-      dataIndex: 'index',
-      width: 80,
-      render: (text, record, index) => {
-        return index + 1;
-      }
-    },
-    {
-      title: '文件名',
-      dataIndex: 'name',
-      width: 300,
-      sorter: (a, b) => a.name.localeCompare(b.name),
-      render: (text, record) => {
-        return (
-          `${text}`
-        )
-      }
-    },
-    {
-      title: '文件类型',
-      dataIndex: 'suffix',
-      width: 100,
-      render: (text, record) => {
-        return (
-          `${text}`
-        )
-      }
-    },
-    {
-      title: '状态',
-      dataIndex: 'status',
-      width: 180,
-      render: (text) => {
-        if (text === '4') {
-          return <Spin size="small" />;
-        } else if (text === '5' || text === '1') {
-          return '已完成';
-        } else if (text === '6' || text === '2') {
-          return '处理失败';
-        }
-      }
-    },
-    {
-      title: '文件大小',
-      dataIndex: 'length',
-      width: 100,
-      render: (text) => {
-        if (text) {
-          const size = (text / 1024 / 1024).toFixed(2);
-          return `${size} M`;
-        } else {
-          return '--'
-        }
-      }
-    },
-    {
-      title: '分类',
-      dataIndex: 'standardClassification',
-      width: 100,
-      render: (text) => {
-        if (text) {
-          standardClass.forEach((item: any) => {
-            if (item.value === text) {
-              text = item.label
-            }
-          });
-          return `${text}`;
-        } else {
-          return '--'
-        }
-      }
-    },
-    {
-      title: '解析工具',
-      dataIndex: 'parsingType',
-      width: 100,
-      render: (text) => {
-        if (text) {
-          parsingTypeList.forEach((item: any) => {
-            if (item.value === text) {
-              text = item.label
-            }
-          });
-          return `${text}`;
-        } else {
-          return '--'
-        }
-      }
-    },
-    {
-      title: '切分规则',
-      dataIndex: 'customSeparator',
-      width: 100,
-      render: (text) => {
-        if (text) {
-          splittingType.forEach((item: any) => {
-            if (item.value === text) {
-              text = item.label
-            }
-          });
-          return `${text}`;
-        } else {
-          return '--'
-        }
-      }
-    },
-    {
-      title: '字符数量',
-      dataIndex: 'wordNum',
-      width: 100,
-      render: (text) => {
-        if (text) {
-          return `${text}`;
-        } else {
-          return '--'
-        }
-      }
-    },
-    {
-      title: '切片数量',
-      dataIndex: 'sliceTotal',
-      width: 100,
-      render: (text) => {
-        if (text) {
-          return `${text}`;
-        } else {
-          return '--';
-        }
-      }
-    },
-    {
-      title: '上传时间',
-      dataIndex: 'createTime',
-      width: 180,
-      render: (text) => {
-        if (text) {
-          return dayjs(text).format('YYYY-MM-DD HH:mm:ss');
-        } else {
-          return '--';
-        }
-      }
-    },
-    {
-      title: '更新时间',
-      dataIndex: 'updateTime',
-      width: 180,
-      render: (text) => {
-        if (text) {
-          return dayjs(text).format('YYYY-MM-DD HH:mm:ss');
-        } else {
-          return '--';
-        }
-      }
-    },
-    {
-      title: '操作',
-      dataIndex: 'operation',
-      width: 180,
-      fixed: 'right',
-      render: (text, record: any) => {
-        const isProcessing = record.status === '4';
-        return (
-          <>
-            {
-              <a
-                style={{
-                  marginRight: 16,
-                  color: isProcessing ? '#d9d9d9' : undefined,
-                  cursor: isProcessing ? 'not-allowed' : 'pointer'
-                }}
-                onClick={() => {
-                  setOpenDrawer(true);
-                  setDetailDocument(record);
-                }}
-              >
-                查看
-              </a>
-            }
-            {
-              (record.standardClassification !== '3' && (slicPre || userInfoAll.id === record.createBy)) && <a
-                style={{
-                  marginRight: 16,
-                  color: isProcessing ? '#d9d9d9' : undefined,
-                  cursor: isProcessing ? 'not-allowed' : 'pointer'
-                }}
-                onClick={() => {
-                  if (isProcessing) return;
-                  // const path = generatePath('/knowledge/knowledgeLib/:knowledgeId/:createBy/slice/:documentId/:embeddingId', {
-                  //   knowledgeId: params.knowledgeId as string,
-                  //   createBy: params.createBy as string,
-                  //   documentId: record.documentId,
-                  //   embeddingId: state.knowledgeDetail.embeddingId,
-                  // });
-                  // router.navigate({ pathname: path }, { state: { createBy: record.createBy } });
-                  setMdModalOpen(true);
-                  setDetailDocument(record);
-                }}
-              >
-                切片
-              </a>
-            }
-            {
-              (configuration || userInfoAll.id === record.createBy) && <a
-                style={{
-                  marginRight: 16,
-                  color: isProcessing ? '#d9d9d9' : undefined,
-                  cursor: isProcessing ? 'not-allowed' : 'pointer'
-                }}
-                onClick={() => {
-                  if (isProcessing) return;
-                  onClickSettings(record.documentId);
-                  setRecord(record)
-                }}
-              >
-                配置
-              </a>
-            }
-            {
-              (editName || userInfoAll.id === record.createBy) &&
-              <a
-                style={{
-                  marginRight: 16,
-                  color: isProcessing ? '#d9d9d9' : undefined,
-                  cursor: isProcessing ? 'not-allowed' : 'pointer'
-                }}
-                onClick={() => {
-                  if (isProcessing) return;
-                  onClickModify(record.documentId);
-                }}>
-                <EditOutlined />
-              </a>
-            }
-            {
-              (deleteFlag || userInfoAll.id === record.createBy) &&
-              <Button
-                type="link"
-                danger
-                className='p-0 mr-0'
-                onClick={() => {
-                  if (isProcessing) return;
-                  Modal.confirm({
-                    title: '删除',
-                    content: `确定删除知识文件:${record.name}吗?`,
-                    okType: 'danger',
-                    onOk: async () => {
-                      try {
-                        setDeletingIds((prev) => [...prev, record.documentId]);
-                        const userInfo = LocalStorage.getUserInfo();
-                        await onClickDelete(record.documentId);
-                      } finally {
-                        setDeletingIds((prev) => prev.filter((id) => id !== record.documentId));
-                      }
-                    }
-                  });
-                }}
-                loading={deletingIds.includes(record.documentId)}
-                disabled={isProcessing || deletingIds.includes(record.documentId)}
-                icon={<DeleteOutlined />}
-              />
-            }
-          </>
-        )
-      }
-    }
-  ];
-
-  const paginationConfig: TablePaginationConfig = {
-    // 显示数据总量
-    showTotal: (total: number) => {
-      return `共 ${total} 条`;
-    },
-    // 展示分页条数切换
-    showSizeChanger: true,
-    // 指定每页显示条数
-    pageSizeOptions: ['10', '20', '50', '100'],
-    // 快速跳转至某页
-    showQuickJumper: true,
-    current: page.page,
-    pageSize: page.size,
-    total: page.total,
-    onChange: async (page, pageSize) => {
-      await onChangePagination(page, pageSize);
-    },
-  };
-  const progressBar = () => {
-    return processingList.map((item: any, index: number) => {
-      return <div key={index} className='knowledgeLibInfo-progress'>
-        <div className="task-card bg-[#FAFAFA] rounded-xl py-2 px-5 card-shadow task-item fade-in" data-status="processing" data-name="市场调研报告需求.docx" data-date="2023-06-15 09:32" data-project-id="1" data-project-name="电商平台重构" data-project-color="#165DFF">
-          <div className="flex flex-wrap justify-between items-start mb-2">
-            <div>
-              <div className="flex items-center">
-                <p className="font-semibold text-[14px]" style={{ marginRight: 16 }}>{item?.name}</p>
-              </div>
-              <p className="text-[12px] text-gray-500">上传于:{item?.createTime}</p>
-            </div>
-            {item?.status === '0' && <span className="bg-blue-100 text-primary text-xs font-medium px-2.5 py-0.5 rounded-full flex items-center mt-1">
-              <i className="fa fa-spinner fa-spin mr-1"></i> 处理中
-            </span>}
-            {item?.status === '2' && <span className="bg-blue-100 text-primary text-xs font-medium px-2.5 py-0.5 rounded-full flex items-center mt-1">
-              处理失败
-            </span>}
-            {item?.status === '3' && <span className="bg-blue-100 text-primary text-xs font-medium px-2.5 py-0.5 rounded-full flex items-center mt-1">
-              待处理
-            </span>}
-            {item?.progress === 100 && <span className="bg-green-100 text-success text-xs font-medium px-2.5 py-0.5 rounded-full flex items-center mt-1">
-              <i className="fa fa-check mr-1"></i> 已完成
-            </span>}
-          </div>
-          <div className="mb-1">
-            <div className="flex justify-between text-[12px] mb-1">
-              <span>处理进度</span>
-              <span id="progressText-1">{item?.progress}%</span>
-            </div>
-            <div className="w-full bg-gray-200 rounded-full h-2.5">
-              <div className="progress-bar bg-blue-500 h-2.5 rounded-full" style={{ width: `${item?.progress}%` }} id="progressBar-1"></div>
-            </div>
-            <div className="flex justify-between text-[11px] mt-1">
-              <span className='text-error'>{item?.errorMessage}</span>
-              <p>
-                <EyeOutlined className='mr-2 cursor-pointer' onClick={() => {
-                  window.open(item?.url)
-                }} />
-
-                {(deleteFlag || userInfoAll.id === item.createBy) && <Button
-                  type="link"
-                  danger
-                  className='p-0'
-                  onClick={() => {
-                    Modal.confirm({
-                      title: '删除',
-                      content: `确定删除知识文件:${item.name}吗?`,
-                      okType: 'danger',
-                      onOk: async () => {
-                        try {
-                          setDeletingIds((prev) => [...prev, item.documentId]);
-                          await onDeleteTakaiDocumentLibApi(item.documentId)
-                        } finally {
-                          setDeletingIds((prev) => prev.filter((id) => id !== item.documentId));
-                        }
-                      }
-                    });
-                  }}
-                  loading={deletingIds.includes(item.documentId)}
-                  disabled={deletingIds.includes(item.documentId)}
-                  icon={<DeleteOutlined />}
-                />}
-              </p>
-            </div>
-          </div>
-        </div>
-      </div>
-    })
-  }
-  return (
-    <div className='knowledgeLibInfo'>
-      <Spin spinning={false}>
-        {
-          <>
-            <div className='knowledgeLibInfo-operation'>
-              <Button
-                type='primary'
-                icon={<ArrowLeftOutlined />} onClick={() => {
-                  navigate({ pathname: '/knowledge/knowledgeLib' });
-                }}>
-                返回
-              </Button>
-              {
-                // ((createFlag && userInfoAll.id === 1) || userInfoAll.id === params?.createBy) &&
-                <p>
-                  {updateDoc && <Button
-                    icon={<PlusOutlined />}
-                    onClick={() => setUploadModalOpen(true)}
-                  >
-                    上传知识文件
-                  </Button>}
-                  {quoteDoc && <Button
-                    type='primary'
-                    className='ml-3'
-                    onClick={() => setQuoteModalOpen(true)}
-                  >
-                    引用知识文件
-                  </Button>}</p>
-              }
-            </div>
-            {/* 进度条 */}
-            <div>
-              {progressBar()}
-            </div>
-            <div className='knowledgeLibInfo-operation' style={{ justifyContent: 'flex-end' }}>
-              <Space.Compact style={{ width: '300px' }} className='mr-3'>
-                <Input
-                  placeholder='请输入文件名称进行搜索'
-                  value={state.name}
-                  onChange={(e) => handleNameChange(e.target.value)}
-                />
-                <Button type="primary" onClick={() => { onClickDocumentDetail() }}><SearchOutlined /></Button>
-              </Space.Compact>
-              {
-                // ((userInfoAll.id === 1) || userInfoAll.id === params?.createBy) &&
-                <p>
-                  {exportSlice && <Tooltip title="导出切片">
-                    <Button
-                      icon={<ExportOutlined />}
-                      onClick={() =>
-                        onExportDocumentSliceExport(documentids, 1)
-                      }
-                    >
-                    </Button>
-                  </Tooltip>}
-                  {exportMd && <Tooltip title="导出MD文件">
-                    <Button
-                      className='ml-3'
-                      icon={<FileTextOutlined />}
-                      onClick={() =>
-                        onExportDocumentSliceExport(documentids, 2)
-                      }
-                    >
-                    </Button>
-                  </Tooltip>}
-                  {exportOr && <Tooltip title="导出原文件">
-                    <Button
-                      className='ml-3'
-                      icon={<ArrowDownOutlined />}
-                      onClick={() =>
-                        onExportDocumentSliceExport(documentids, 3)
-                      }
-                    >
-                    </Button>
-                  </Tooltip>}
-                </p>
-              }
-            </div>
-            <div className='knowledgeLibInfo-table'>
-              <Table
-                scroll={{ x: 'max-content' }}
-                rowSelection={{ type: 'checkbox', ...rowSelection }}
-                rowKey={(record) => record.documentId}
-                loading={listLoading}
-                columns={columns}
-                dataSource={list}
-                pagination={paginationConfig}
-              />
-            </div>
-            {/* 上传知识文件 */}
-            {(uploadModalOpen||knowledgeDetail?.createBy === userInfoAll.id) && <UploadModal
-              open={uploadModalOpen}
-              knowledgeId={params.knowledgeId || ''}
-              knowledgeDetail={knowledgeDetail}
-              onCancel={() => setUploadModalOpen(false)}
-              onSuccess={() => {
-                init(params.knowledgeId, 2);
-              }}
-            />}
-            {
-              (infoModalOpen||knowledgeDetail?.createBy === userInfoAll.id) &&
-              <InfoModal
-                id={infoModalId}
-                open={infoModalOpen}
-                onClickConfirm={infoModalOnClickConfirm}
-                onClickCancel={infoModalOnClickCancel}
-              />
-            }
-
-            {
-              infoModalSettingOpen &&
-              <InfoModalSetting
-                id={infoModalSettingId}
-                open={infoModalSettingOpen}
-                record={record}
-                onClickConfirm={infoModalSettingOnClickConfirm}
-                onClickCancel={infoModalSettingOnClickCancel}
-              />
-            }
-          </>
-        }
-      </Spin>
-      {mdModalOpen && <MdModal open={mdModalOpen} detailDocument={detailDocument} knowledgeDetail={knowledgeDetail} onCancel={(type?: number) => {
-        init(params.knowledgeId, 2, '0');
-        if (!type) {
-          setMdModalOpen(false)
-        }
-      }}></MdModal>}
-      {quoteModalOpen && <QuoteModal open={quoteModalOpen} knowledgeDetail={knowledgeDetail} onCancel={() => { init(params.knowledgeId, 2); setQuoteModalOpen(false) }}></QuoteModal>}
-      <PrevewSlice openDrawer={openDrawer} onClose={() => setOpenDrawer(false)} title={detailDocument?.name || '预览切片'} record={detailDocument} />
-      
-    </div>
-  );
-};
-
-export default KnowledgeLibInfo;

+ 9 - 9
jk-rag-platform/src/pages/knowledgeLib/detail/style.less → jk-rag-platform/src/pages/knowledgeLib/detail/style.scss

@@ -7,31 +7,31 @@
 .ant-typography {
     h1 {
         font-size: @font-4xl;   // 20px - 使用全局变量
-        color: @text-primary;   // 使用全局变量
+        color: $text-primary;   // 使用全局变量
         margin: 2px 0;
     }
 
     h2 {
-        font-size: @font-3xl;   // 18px
-        color: @text-primary;
+        font-size: $font-3xl;   // 18px
+        color: $text-primary;
         margin: 2px 0;
     }
 
     h3 {
-        font-size: @font-2xl;   // 16px
-        color: @text-primary;
+        font-size: $font-2xl;   // 16px
+        color: $text-primary;
         margin: 2px 0;
     }
 
     h4 {
-        font-size: @font-lg;    // 14px
-        color: @text-primary;
+        font-size: $font-lg;    // 14px
+        color: $text-primary;
         margin: 2px 0;
     }
 
     h5, h6 {
-        font-size: @font-base;  // 12px
-        color: @text-primary;
+        font-size: $font-base;  // 12px
+        color: $text-primary;
         margin: 2px 0;
     }
 }

+ 34 - 34
jk-rag-platform/src/pages/knowledgeLib/list/KnowledgeDrawer.less → jk-rag-platform/src/pages/knowledgeLib/list/KnowledgeDrawer.scss

@@ -1,5 +1,5 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // ===== 知识库 Drawer 样式 =====
 // 说明:只定义页面特定的样式,不覆盖 Ant Design 和全局样式
@@ -9,32 +9,32 @@
     // Drawer 内部表单容器
     .drawer-form-container {
         height: 100%;
-        padding: @spacing-4;  // 16px - 紧凑间距
+        padding: $spacing-4;  // 16px - 紧凑间距
         overflow-y: auto;
 
         // 区块标题 - 页面特定样式
         .icon-select-section {
             display: flex;
             align-items: center;
-            gap: @spacing-4;
-            padding: @spacing-4;
-            background: @bg-tertiary;
-            border-radius: @radius-xl;
-            margin-bottom: @spacing-4;
+            gap: $spacing-4;
+            padding: $spacing-4;
+            background: $bg-tertiary;
+            border-radius: $radius-xl;
+            margin-bottom: $spacing-4;
 
             .section-icon {
                 width: 48px;
                 height: 48px;
-                border-radius: @radius-lg;
-                background: fade(@primary-color, 10%);
+                border-radius: $radius-lg;
+                background: fade($primary-color, 10%);
                 display: flex;
                 align-items: center;
                 justify-content: center;
                 flex-shrink: 0;
 
                 .iconify {
-                    font-size: @icon-2xl;  // 24px - 使用全局变量
-                    color: @primary-color;
+                    font-size: $icon-2xl;  // 24px - 使用全局变量
+                    color: $primary-color;
                 }
             }
 
@@ -42,59 +42,59 @@
                 flex: 1;
 
                 .section-title {
-                    font-size: @font-lg;
-                    font-weight: @font-weight-semibold;
-                    color: @text-primary;
-                    margin-bottom: @spacing-1;
+                    font-size: $font-lg;
+                    font-weight: $font-weight-semibold;
+                    color: $text-primary;
+                    margin-bottom: $spacing-1;
                 }
 
                 .section-desc {
-                    font-size: @font-sm;
-                    color: @text-hint;
-                    line-height: @line-height-base;
+                    font-size: $font-sm;
+                    color: $text-hint;
+                    line-height: $line-height-base;
                 }
             }
         }
 
         // 分割线 - 页面特定样式
         .section-divider {
-            margin: @spacing-6 0 @spacing-5 0;
+            margin: $spacing-6 0 $spacing-5 0;
 
             &::before {
                 content: '';
                 display: inline-block;
                 width: 4px;
                 height: 16px;
-                background: @primary-color;
+                background: $primary-color;
                 border-radius: 2px;
-                margin-right: @spacing-2;
+                margin-right: $spacing-2;
                 vertical-align: middle;
             }
 
             span {
-                font-size: @font-lg;
-                font-weight: @font-weight-semibold;
-                color: @text-primary;
+                font-size: $font-lg;
+                font-weight: $font-weight-semibold;
+                color: $text-primary;
             }
         }
 
         // 表单样式优化 - 使用全局变量
         .ant-form {
             .ant-form-item {
-                margin-bottom: @spacing-4;
+                margin-bottom: $spacing-4;
 
                 .ant-form-item-label {
                     > label {
-                        font-size: @font-md;
-                        font-weight: @font-weight-medium;
-                        color: @text-primary;
+                        font-size: $font-md;
+                        font-weight: $font-weight-medium;
+                        color: $text-primary;
                     }
                 }
 
                 .ant-form-item-extra {
-                    font-size: @font-sm;
-                    color: @text-hint;
-                    margin-top: @spacing-1;
+                    font-size: $font-sm;
+                    color: $text-hint;
+                    margin-top: $spacing-1;
                 }
             }
         }
@@ -109,14 +109,14 @@
                 text-align: center;
                 height: 44px;
                 line-height: 42px;
-                border-radius: @radius-md;
+                border-radius: $radius-md;
 
                 &:first-child {
-                    border-radius: @radius-md 0 0 @radius-md;
+                    border-radius: $radius-md 0 0 $radius-md;
                 }
 
                 &:last-child {
-                    border-radius: 0 @radius-md @radius-md 0;
+                    border-radius: 0 $radius-md $radius-md 0;
                 }
 
                 &::before {

+ 0 - 365
jk-rag-platform/src/pages/knowledgeLib/list/index.tsx.backup.final

@@ -1,365 +0,0 @@
-import * as React from 'react';
-import { useNavigate } from 'react-router-dom';
-import { Button, Table, TableColumnsType, TablePaginationConfig, Input, Space, Tabs, message, Tooltip } from 'antd';
-import { PlusOutlined, SearchOutlined, EditFilled, FileWordFilled, FileMarkdownFilled, DeleteFilled } from '@ant-design/icons';
-import { GuideTips, FilterBar } from '@/components/common';
-import InfoModal from './components/InfoModal';
-import { useKnowledgeLibListStore } from './store';
-import { Record } from './types';
-import './style.less';
-import LocalStorage from '@/LocalStorage';
-import dayjs from 'dayjs';
-// 导入 GuideTips 配置
-import { getGuideTipsConfig } from '@/config/guideTips';
-
-const KnowledgeLibList: React.FC = () => {
-    const navigate = useNavigate();
-    
-    const {
-        listLoading,
-        list,
-        infoModalId,
-        infoModalOpen,
-        page,
-        setListLoading,
-        setList,
-        setInfoModalId,
-        setInfoModalOpen,
-        setPage,
-        fetchKnowledgeLibList,
-        onCreateKnowledgeLib,
-        onModifyKnowledgeLib,
-        onDeleteKnowledgeLib,
-        onInfoModalOpenChange,
-        init,
-        reset,
-    } = useKnowledgeLibListStore();
-
-    const [activeTab, setActiveTab] = React.useState('all'); // 'all' | 'mine'
-    const [searchName, setSearchName] = React.useState('');
-    const [currentPage, setCurrentPage] = React.useState(1);
-    const [pageSize, setPageSize] = React.useState(10);
-    
-    const userInfoAll = LocalStorage.getUserInfo();
-    const listFlag = LocalStorage.getStatusFlag('knowledge:knowledgeLib:list');
-    const updateFlag = LocalStorage.getStatusFlag('knowledge:knowledgeLib:update');
-    const downloadOriginal = LocalStorage.getStatusFlag('knowledge:knowledgeLib:downloadOriginal');
-    const downloadMd = LocalStorage.getStatusFlag('knowledge:knowledgeLib:downloadMd');
-
-    // 初始化加载数据
-    React.useEffect(() => {
-        init();
-    }, [init]);
-
-    // 监听创建事件
-    React.useEffect(() => {
-        const handleCreate = () => {
-            onCreateKnowledgeLibClick();
-        };
-        window.addEventListener('knowledgeLibCreate', handleCreate as EventListener);
-        return () => {
-            window.removeEventListener('knowledgeLibCreate', handleCreate as EventListener);
-        };
-    }, []);
-
-    // 处理函数
-    const onCreateKnowledgeLibClick = () => {
-        setInfoModalId('');
-        setInfoModalOpen(true);
-        message.info('创建知识库');
-    };
-
-    const onClickModify = (record: Record) => {
-        setInfoModalId(record.knowledgeId);
-        setInfoModalOpen(true);
-        message.info('编辑知识库');
-    };
-
-    const infoModalOnClickConfirm = () => {
-        const data = {
-            name: '测试知识库',
-            description: '测试描述',
-        };
-        
-        if (infoModalId) {
-            onModifyKnowledgeLib(infoModalId, data);
-        } else {
-            onCreateKnowledgeLib(data);
-        }
-    };
-
-    const infoModalOnClickCancel = () => {
-        setInfoModalOpen(false);
-        setInfoModalId('');
-    };
-
-    const onClickDelete = (record: Record) => {
-        message.loading('删除中...', 0);
-        setTimeout(() => {
-            onDeleteKnowledgeLib(record.knowledgeId);
-            message.success('删除成功');
-        }, 500);
-    };
-
-    const onClickDownload = (record: Record) => {
-        // 下载原始文档
-        message.info(`下载原始文档:${record.name}`);
-        // TODO: 调用原始文档下载 API
-    };
-
-    const onClickDownloadMd = (record: Record) => {
-        // 下载 MD 文档
-        message.info(`下载 MD 文档:${record.name}`);
-        // TODO: 调用 MD 文档下载 API
-    };
-
-    // 过滤数据
-    const filteredList = list.filter(item => {
-        const matchSearch = !searchName || item.name.toLowerCase().includes(searchName.toLowerCase());
-        // TAB 过滤:全部 | 我创建的 | 我可以维护的
-        let matchTab = true;
-        if (activeTab === 'mine') {
-            // 我创建的
-            matchTab = userInfoAll?.id === item.createBy;
-        } else if (activeTab === 'maintainer') {
-            // 我可以维护的(创建者或维护者)
-            matchTab = userInfoAll?.id === item.createBy ||
-                      (item.maintainers && item.maintainers.includes(userInfoAll?.id));
-        }
-        return matchSearch && matchTab;
-    });
-
-    // 分页
-    const paginatedList = filteredList.slice((currentPage - 1) * pageSize, currentPage * pageSize);
-
-    // 分页处理
-    const handlePageChange = (page: number, size: number) => {
-        setCurrentPage(page);
-        setPageSize(size);
-        setPage({ pageNum: page, pageSize: size, total: filteredList.length });
-    };
-
-    // 表格列定义 - 匹配实际 API 字段(紧凑布局)
-    const columns: TableColumnsType<Record> = [
-        {
-            title: '序号',
-            dataIndex: 'index',
-            width: 60,  // 更紧凑
-            align: 'center',
-            render: (_text, _record, index) => {
-                return ((currentPage - 1) * pageSize) + index + 1;
-            }
-        },
-        {
-            title: '知识库名称',
-            dataIndex: 'name',
-            width: 180,  // 更紧凑
-            render: (text, record) => {
-                return (
-                    <div>
-                        <p
-                            className='text-primary cursor-pointer'
-                            style={{ fontWeight: 600, fontSize: '@font-base' }}
-                            onClick={() => {
-                                navigate(`/knowledge/knowledgeLib/${record.knowledgeId}/${record.createBy}`);
-                            }}
-                        >
-                            {text}
-                        </p>
-                        <div style={{ color: '#999', fontSize: 11, marginTop: 2 }}>
-                            ID: {record.knowledgeId}
-                        </div>
-                    </div>
-                );
-            }
-        },
-        {
-            title: '使用空间',
-            dataIndex: 'length',
-            width: 90,  // 更紧凑
-            align: 'center',
-            render: (text) => text || '--',
-        },
-        {
-            title: '字符数量',
-            dataIndex: 'wordNum',
-            width: 100,  // 更紧凑
-            align: 'center',
-            render: (text) => text && text !== '--' ? text.toLocaleString() : '--',
-        },
-        {
-            title: '文件数量',
-            dataIndex: 'documentSize',
-            width: 80,  // 更紧凑
-            align: 'center',
-            render: (text) => text && text !== '--' ? text : '--',
-        },
-        {
-            title: '切片数量',
-            dataIndex: 'sliceCount',
-            width: 80,  // 更紧凑
-            align: 'center',
-            render: (text) => text && text !== '--' ? text.toLocaleString() : '--',
-        },
-        {
-            title: '是否公开',
-            dataIndex: 'isOpen',
-            width: 80,  // 更紧凑
-            align: 'center',
-            render: (value) => value === 1 ? '公开' : '私有',
-        },
-        {
-            title: '创建时间',
-            dataIndex: 'createTime',
-            width: 160,  // 更紧凑
-            align: 'center',
-            render: (text) => text && text !== '--' ? dayjs(text).format('YYYY-MM-DD HH:mm:ss') : '--',
-        },
-        {
-            title: '更新时间',
-            dataIndex: 'updateTime',
-            width: 160,  // 更紧凑
-            align: 'center',
-            render: (text) => text && text !== '--' ? dayjs(text).format('YYYY-MM-DD HH:mm:ss') : '--',
-        },
-        {
-            title: '操作',
-            key: 'action',
-            width: 160,  // 适配 Ant Design 图标
-            fixed: 'right',
-            align: 'center',
-            render: (_text, record) => (
-                <Space size={0}>  {/* 无间距,更紧凑 */}
-                    {/* 编辑按钮 */}
-                    <Tooltip title="编辑" placement="top">
-                        <Button
-                            type="text"
-                            size="large"
-                            onClick={() => onClickModify(record)}
-                            className="action-btn"
-                            icon={<EditFilled />}
-                        />
-                    </Tooltip>
-
-                    {/* 下载原文档按钮 */}
-                    <Tooltip title="下载原文档" placement="top">
-                        <Button
-                            type="text"
-                            size="large"
-                            onClick={() => onClickDownload(record)}
-                            className="action-btn"
-                            icon={<FileWordFilled />}
-                        />
-                    </Tooltip>
-
-                    {/* 下载 MD 文档按钮 */}
-                    <Tooltip title="下载 MD 文档" placement="top">
-                        <Button
-                            type="text"
-                            size="large"
-                            onClick={() => onClickDownloadMd(record)}
-                            className="action-btn"
-                            icon={<FileMarkdownFilled />}
-                        />
-                    </Tooltip>
-
-                    {/* 删除按钮 */}
-                    <Tooltip title="删除" placement="top">
-                        <Button
-                            type="text"
-                            size="large"
-                            danger
-                            onClick={() => onClickDelete(record)}
-                            className="action-btn"
-                            icon={<DeleteFilled />}
-                        />
-                    </Tooltip>
-                </Space>
-            )
-        },
-    ];
-
-    // 获取 GuideTips 配置
-    const guideTipsConfig = getGuideTipsConfig('knowledgeLib');
-
-    return (
-        <div className="page-container">
-            {/* 标题区域 */}
-            <div className="list-header with-tips">
-                <div className='list-header-title'>
-                    <h1>知识库管理</h1>
-                    <p>管理您的所有知识库</p>
-                </div>
-                <div className='list-header-actions'>
-                    <Button
-                        type='primary'
-                        icon={<PlusOutlined />}
-                        onClick={onCreateKnowledgeLibClick}
-                    >
-                        创建
-                    </Button>
-                </div>
-            </div>
-
-            {/* Tips 提示组件 */}
-            {guideTipsConfig && (
-                <GuideTips
-                    visible={true}
-                    title={guideTipsConfig.title}
-                    steps={guideTipsConfig.steps}
-                />
-            )}
-
-            {/* 筛选区域 - 使用 FilterBar 组件 */}
-            <FilterBar
-                tabs={[
-                    { key: 'all', label: '全部' },
-                    { key: 'mine', label: '我创建的' },
-                    { key: 'maintainer', label: '我可以维护的' },
-                ]}
-                activeTab={activeTab}
-                onTabChange={setActiveTab}
-                searchPlaceholder="请输入知识库名称"
-                searchValue={searchName}
-                onSearchChange={setSearchName}
-                onSearch={async () => {
-                    // 模拟异步搜索
-                    await new Promise(resolve => setTimeout(resolve, 500));
-                    message.info('搜索功能开发中');
-                }}
-                searchWidth={300}
-            />
-
-            {/* 表格区域 */}
-            <div className="content-section">
-                <Table
-                    rowKey="knowledgeId"
-                    loading={listLoading}
-                    columns={columns}
-                    dataSource={paginatedList}
-                    pagination={{
-                        current: currentPage,
-                        pageSize: pageSize,
-                        total: filteredList.length,
-                        onChange: handlePageChange,
-                        showSizeChanger: true,
-                        showQuickJumper: true,
-                        showTotal: (total) => `共 ${total} 条`,
-                        pageSizeOptions: ['10', '20', '50', '100'],
-                    }}
-                    scroll={{ x: 1200 }}
-                />
-            </div>
-
-            {/* 创建/编辑弹窗 */}
-            <InfoModal
-                id={infoModalId}
-                open={infoModalOpen}
-                onClickConfirm={infoModalOnClickConfirm}
-                onClickCancel={infoModalOnClickCancel}
-            />
-        </div>
-    );
-};
-
-export default KnowledgeLibList;

+ 0 - 381
jk-rag-platform/src/pages/knowledgeLib/list/index.tsx.backup.v2

@@ -1,381 +0,0 @@
-import * as React from 'react';
-import { useNavigate } from 'react-router-dom';
-import { Button, Table, TableColumnsType, TablePaginationConfig, Input, Space, Tabs, message, Tooltip } from 'antd';
-import { PlusOutlined, SearchOutlined } from '@ant-design/icons';
-import { GuideTips, FilterBar } from '@/components/common';
-import InfoModal from './components/InfoModal';
-import { useKnowledgeLibListStore } from './store';
-import { Record } from './types';
-import './style.less';
-import LocalStorage from '@/LocalStorage';
-import dayjs from 'dayjs';
-// 导入 GuideTips 配置
-import { getGuideTipsConfig } from '@/config/guideTips';
-
-const KnowledgeLibList: React.FC = () => {
-    const navigate = useNavigate();
-    
-    const {
-        listLoading,
-        list,
-        infoModalId,
-        infoModalOpen,
-        page,
-        setListLoading,
-        setList,
-        setInfoModalId,
-        setInfoModalOpen,
-        setPage,
-        fetchKnowledgeLibList,
-        onCreateKnowledgeLib,
-        onModifyKnowledgeLib,
-        onDeleteKnowledgeLib,
-        onInfoModalOpenChange,
-        init,
-        reset,
-    } = useKnowledgeLibListStore();
-
-    const [activeTab, setActiveTab] = React.useState('all'); // 'all' | 'mine'
-    const [searchName, setSearchName] = React.useState('');
-    const [currentPage, setCurrentPage] = React.useState(1);
-    const [pageSize, setPageSize] = React.useState(10);
-    
-    const userInfoAll = LocalStorage.getUserInfo();
-    const listFlag = LocalStorage.getStatusFlag('knowledge:knowledgeLib:list');
-    const updateFlag = LocalStorage.getStatusFlag('knowledge:knowledgeLib:update');
-    const downloadOriginal = LocalStorage.getStatusFlag('knowledge:knowledgeLib:downloadOriginal');
-    const downloadMd = LocalStorage.getStatusFlag('knowledge:knowledgeLib:downloadMd');
-
-    // 初始化加载数据
-    React.useEffect(() => {
-        init();
-    }, [init]);
-
-    // 监听创建事件
-    React.useEffect(() => {
-        const handleCreate = () => {
-            onCreateKnowledgeLibClick();
-        };
-        window.addEventListener('knowledgeLibCreate', handleCreate as EventListener);
-        return () => {
-            window.removeEventListener('knowledgeLibCreate', handleCreate as EventListener);
-        };
-    }, []);
-
-    // 处理函数
-    const onCreateKnowledgeLibClick = () => {
-        setInfoModalId('');
-        setInfoModalOpen(true);
-        message.info('创建知识库');
-    };
-
-    const onClickModify = (record: Record) => {
-        setInfoModalId(record.knowledgeId);
-        setInfoModalOpen(true);
-        message.info('编辑知识库');
-    };
-
-    const infoModalOnClickConfirm = () => {
-        const data = {
-            name: '测试知识库',
-            description: '测试描述',
-        };
-        
-        if (infoModalId) {
-            onModifyKnowledgeLib(infoModalId, data);
-        } else {
-            onCreateKnowledgeLib(data);
-        }
-    };
-
-    const infoModalOnClickCancel = () => {
-        setInfoModalOpen(false);
-        setInfoModalId('');
-    };
-
-    const onClickDelete = (record: Record) => {
-        message.loading('删除中...', 0);
-        setTimeout(() => {
-            onDeleteKnowledgeLib(record.knowledgeId);
-            message.success('删除成功');
-        }, 500);
-    };
-
-    const onClickDownload = (record: Record) => {
-        // 下载原始文档
-        message.info(`下载原始文档:${record.name}`);
-        // TODO: 调用原始文档下载 API
-    };
-
-    const onClickDownloadMd = (record: Record) => {
-        // 下载 MD 文档
-        message.info(`下载 MD 文档:${record.name}`);
-        // TODO: 调用 MD 文档下载 API
-    };
-
-    // 过滤数据
-    const filteredList = list.filter(item => {
-        const matchSearch = !searchName || item.name.toLowerCase().includes(searchName.toLowerCase());
-        // TAB 过滤:全部 | 我创建的 | 我可以维护的
-        let matchTab = true;
-        if (activeTab === 'mine') {
-            // 我创建的
-            matchTab = userInfoAll?.id === item.createBy;
-        } else if (activeTab === 'maintainer') {
-            // 我可以维护的(创建者或维护者)
-            matchTab = userInfoAll?.id === item.createBy ||
-                      (item.maintainers && item.maintainers.includes(userInfoAll?.id));
-        }
-        return matchSearch && matchTab;
-    });
-
-    // 分页
-    const paginatedList = filteredList.slice((currentPage - 1) * pageSize, currentPage * pageSize);
-
-    // 分页处理
-    const handlePageChange = (page: number, size: number) => {
-        setCurrentPage(page);
-        setPageSize(size);
-        setPage({ pageNum: page, pageSize: size, total: filteredList.length });
-    };
-
-    // 表格列定义 - 匹配实际 API 字段(紧凑布局)
-    const columns: TableColumnsType<Record> = [
-        {
-            title: '序号',
-            dataIndex: 'index',
-            width: 60,  // 更紧凑
-            align: 'center',
-            render: (_text, _record, index) => {
-                return ((currentPage - 1) * pageSize) + index + 1;
-            }
-        },
-        {
-            title: '知识库名称',
-            dataIndex: 'name',
-            width: 180,  // 更紧凑
-            render: (text, record) => {
-                return (
-                    <div>
-                        <p
-                            className='text-primary cursor-pointer'
-                            style={{ fontWeight: 600, fontSize: '@font-base' }}
-                            onClick={() => {
-                                navigate(`/knowledge/knowledgeLib/${record.knowledgeId}/${record.createBy}`);
-                            }}
-                        >
-                            {text}
-                        </p>
-                        <div style={{ color: '#999', fontSize: 11, marginTop: 2 }}>
-                            ID: {record.knowledgeId}
-                        </div>
-                    </div>
-                );
-            }
-        },
-        {
-            title: '使用空间',
-            dataIndex: 'length',
-            width: 90,  // 更紧凑
-            align: 'center',
-            render: (text) => text || '--',
-        },
-        {
-            title: '字符数量',
-            dataIndex: 'wordNum',
-            width: 100,  // 更紧凑
-            align: 'center',
-            render: (text) => text && text !== '--' ? text.toLocaleString() : '--',
-        },
-        {
-            title: '文件数量',
-            dataIndex: 'documentSize',
-            width: 80,  // 更紧凑
-            align: 'center',
-            render: (text) => text && text !== '--' ? text : '--',
-        },
-        {
-            title: '切片数量',
-            dataIndex: 'sliceCount',
-            width: 80,  // 更紧凑
-            align: 'center',
-            render: (text) => text && text !== '--' ? text.toLocaleString() : '--',
-        },
-        {
-            title: '是否公开',
-            dataIndex: 'isOpen',
-            width: 80,  // 更紧凑
-            align: 'center',
-            render: (value) => value === 1 ? '公开' : '私有',
-        },
-        {
-            title: '创建时间',
-            dataIndex: 'createTime',
-            width: 160,  // 更紧凑
-            align: 'center',
-            render: (text) => text && text !== '--' ? dayjs(text).format('YYYY-MM-DD HH:mm:ss') : '--',
-        },
-        {
-            title: '更新时间',
-            dataIndex: 'updateTime',
-            width: 160,  // 更紧凑
-            align: 'center',
-            render: (text) => text && text !== '--' ? dayjs(text).format('YYYY-MM-DD HH:mm:ss') : '--',
-        },
-        {
-            title: '操作',
-            key: 'action',
-            width: 160,  // 进一步缩减宽度
-            fixed: 'right',
-            align: 'center',
-            render: (_text, record) => (
-                <Space size={12}>
-                    {/* 编辑按钮 */}
-                    <Tooltip title="编辑" placement="top">
-                        <Button
-                            type="text"
-                            size="small"
-                            onClick={() => onClickModify(record)}
-                            className="action-btn"
-                        >
-                            <span
-                                className="iconify"
-                                data-icon="solar:pen-bold"
-                            />
-                        </Button>
-                    </Tooltip>
-
-                    {/* 下载原文档按钮 */}
-                    <Tooltip title="下载原文档" placement="top">
-                        <Button
-                            type="text"
-                            size="small"
-                            onClick={() => onClickDownload(record)}
-                            className="action-btn"
-                        >
-                            <span
-                                className="iconify"
-                                data-icon="solar:download-square-bold"
-                            />
-                        </Button>
-                    </Tooltip>
-
-                    {/* 下载 MD 文档按钮 */}
-                    <Tooltip title="下载 MD 文档" placement="top">
-                        <Button
-                            type="text"
-                            size="small"
-                            onClick={() => onClickDownloadMd(record)}
-                            className="action-btn"
-                        >
-                            <span
-                                className="iconify"
-                                data-icon="solar:file-text-bold"
-                            />
-                        </Button>
-                    </Tooltip>
-
-                    {/* 删除按钮 */}
-                    <Tooltip title="删除" placement="top">
-                        <Button
-                            type="text"
-                            size="small"
-                            danger
-                            onClick={() => onClickDelete(record)}
-                            className="action-btn"
-                        >
-                            <span
-                                className="iconify"
-                                data-icon="solar:trash-bin-trash-bold"
-                            />
-                        </Button>
-                    </Tooltip>
-                </Space>
-            )
-        },
-    ];
-
-    // 获取 GuideTips 配置
-    const guideTipsConfig = getGuideTipsConfig('knowledgeLib');
-
-    return (
-        <div className="page-container">
-            {/* 标题区域 */}
-            <div className="list-header with-tips">
-                <div className='list-header-title'>
-                    <h1>知识库管理</h1>
-                    <p>管理您的所有知识库</p>
-                </div>
-                <div className='list-header-actions'>
-                    <Button
-                        type='primary'
-                        icon={<PlusOutlined />}
-                        onClick={onCreateKnowledgeLibClick}
-                    >
-                        创建
-                    </Button>
-                </div>
-            </div>
-
-            {/* Tips 提示组件 */}
-            {guideTipsConfig && (
-                <GuideTips
-                    visible={true}
-                    title={guideTipsConfig.title}
-                    steps={guideTipsConfig.steps}
-                />
-            )}
-
-            {/* 筛选区域 - 使用 FilterBar 组件 */}
-            <FilterBar
-                tabs={[
-                    { key: 'all', label: '全部' },
-                    { key: 'mine', label: '我创建的' },
-                    { key: 'maintainer', label: '我可以维护的' },
-                ]}
-                activeTab={activeTab}
-                onTabChange={setActiveTab}
-                searchPlaceholder="请输入知识库名称"
-                searchValue={searchName}
-                onSearchChange={setSearchName}
-                onSearch={async () => {
-                    // 模拟异步搜索
-                    await new Promise(resolve => setTimeout(resolve, 500));
-                    message.info('搜索功能开发中');
-                }}
-                searchWidth={300}
-            />
-
-            {/* 表格区域 */}
-            <div className="content-section">
-                <Table
-                    rowKey="knowledgeId"
-                    loading={listLoading}
-                    columns={columns}
-                    dataSource={paginatedList}
-                    pagination={{
-                        current: currentPage,
-                        pageSize: pageSize,
-                        total: filteredList.length,
-                        onChange: handlePageChange,
-                        showSizeChanger: true,
-                        showQuickJumper: true,
-                        showTotal: (total) => `共 ${total} 条`,
-                        pageSizeOptions: ['10', '20', '50', '100'],
-                    }}
-                    scroll={{ x: 1200 }}
-                />
-            </div>
-
-            {/* 创建/编辑弹窗 */}
-            <InfoModal
-                id={infoModalId}
-                open={infoModalOpen}
-                onClickConfirm={infoModalOnClickConfirm}
-                onClickCancel={infoModalOnClickCancel}
-            />
-        </div>
-    );
-};
-
-export default KnowledgeLibList;

+ 0 - 381
jk-rag-platform/src/pages/knowledgeLib/list/index.tsx.backup.v3

@@ -1,381 +0,0 @@
-import * as React from 'react';
-import { useNavigate } from 'react-router-dom';
-import { Button, Table, TableColumnsType, TablePaginationConfig, Input, Space, Tabs, message, Tooltip } from 'antd';
-import { PlusOutlined, SearchOutlined } from '@ant-design/icons';
-import { GuideTips, FilterBar } from '@/components/common';
-import InfoModal from './components/InfoModal';
-import { useKnowledgeLibListStore } from './store';
-import { Record } from './types';
-import './style.less';
-import LocalStorage from '@/LocalStorage';
-import dayjs from 'dayjs';
-// 导入 GuideTips 配置
-import { getGuideTipsConfig } from '@/config/guideTips';
-
-const KnowledgeLibList: React.FC = () => {
-    const navigate = useNavigate();
-    
-    const {
-        listLoading,
-        list,
-        infoModalId,
-        infoModalOpen,
-        page,
-        setListLoading,
-        setList,
-        setInfoModalId,
-        setInfoModalOpen,
-        setPage,
-        fetchKnowledgeLibList,
-        onCreateKnowledgeLib,
-        onModifyKnowledgeLib,
-        onDeleteKnowledgeLib,
-        onInfoModalOpenChange,
-        init,
-        reset,
-    } = useKnowledgeLibListStore();
-
-    const [activeTab, setActiveTab] = React.useState('all'); // 'all' | 'mine'
-    const [searchName, setSearchName] = React.useState('');
-    const [currentPage, setCurrentPage] = React.useState(1);
-    const [pageSize, setPageSize] = React.useState(10);
-    
-    const userInfoAll = LocalStorage.getUserInfo();
-    const listFlag = LocalStorage.getStatusFlag('knowledge:knowledgeLib:list');
-    const updateFlag = LocalStorage.getStatusFlag('knowledge:knowledgeLib:update');
-    const downloadOriginal = LocalStorage.getStatusFlag('knowledge:knowledgeLib:downloadOriginal');
-    const downloadMd = LocalStorage.getStatusFlag('knowledge:knowledgeLib:downloadMd');
-
-    // 初始化加载数据
-    React.useEffect(() => {
-        init();
-    }, [init]);
-
-    // 监听创建事件
-    React.useEffect(() => {
-        const handleCreate = () => {
-            onCreateKnowledgeLibClick();
-        };
-        window.addEventListener('knowledgeLibCreate', handleCreate as EventListener);
-        return () => {
-            window.removeEventListener('knowledgeLibCreate', handleCreate as EventListener);
-        };
-    }, []);
-
-    // 处理函数
-    const onCreateKnowledgeLibClick = () => {
-        setInfoModalId('');
-        setInfoModalOpen(true);
-        message.info('创建知识库');
-    };
-
-    const onClickModify = (record: Record) => {
-        setInfoModalId(record.knowledgeId);
-        setInfoModalOpen(true);
-        message.info('编辑知识库');
-    };
-
-    const infoModalOnClickConfirm = () => {
-        const data = {
-            name: '测试知识库',
-            description: '测试描述',
-        };
-        
-        if (infoModalId) {
-            onModifyKnowledgeLib(infoModalId, data);
-        } else {
-            onCreateKnowledgeLib(data);
-        }
-    };
-
-    const infoModalOnClickCancel = () => {
-        setInfoModalOpen(false);
-        setInfoModalId('');
-    };
-
-    const onClickDelete = (record: Record) => {
-        message.loading('删除中...', 0);
-        setTimeout(() => {
-            onDeleteKnowledgeLib(record.knowledgeId);
-            message.success('删除成功');
-        }, 500);
-    };
-
-    const onClickDownload = (record: Record) => {
-        // 下载原始文档
-        message.info(`下载原始文档:${record.name}`);
-        // TODO: 调用原始文档下载 API
-    };
-
-    const onClickDownloadMd = (record: Record) => {
-        // 下载 MD 文档
-        message.info(`下载 MD 文档:${record.name}`);
-        // TODO: 调用 MD 文档下载 API
-    };
-
-    // 过滤数据
-    const filteredList = list.filter(item => {
-        const matchSearch = !searchName || item.name.toLowerCase().includes(searchName.toLowerCase());
-        // TAB 过滤:全部 | 我创建的 | 我可以维护的
-        let matchTab = true;
-        if (activeTab === 'mine') {
-            // 我创建的
-            matchTab = userInfoAll?.id === item.createBy;
-        } else if (activeTab === 'maintainer') {
-            // 我可以维护的(创建者或维护者)
-            matchTab = userInfoAll?.id === item.createBy ||
-                      (item.maintainers && item.maintainers.includes(userInfoAll?.id));
-        }
-        return matchSearch && matchTab;
-    });
-
-    // 分页
-    const paginatedList = filteredList.slice((currentPage - 1) * pageSize, currentPage * pageSize);
-
-    // 分页处理
-    const handlePageChange = (page: number, size: number) => {
-        setCurrentPage(page);
-        setPageSize(size);
-        setPage({ pageNum: page, pageSize: size, total: filteredList.length });
-    };
-
-    // 表格列定义 - 匹配实际 API 字段(紧凑布局)
-    const columns: TableColumnsType<Record> = [
-        {
-            title: '序号',
-            dataIndex: 'index',
-            width: 60,  // 更紧凑
-            align: 'center',
-            render: (_text, _record, index) => {
-                return ((currentPage - 1) * pageSize) + index + 1;
-            }
-        },
-        {
-            title: '知识库名称',
-            dataIndex: 'name',
-            width: 180,  // 更紧凑
-            render: (text, record) => {
-                return (
-                    <div>
-                        <p
-                            className='text-primary cursor-pointer'
-                            style={{ fontWeight: 600, fontSize: '@font-base' }}
-                            onClick={() => {
-                                navigate(`/knowledge/knowledgeLib/${record.knowledgeId}/${record.createBy}`);
-                            }}
-                        >
-                            {text}
-                        </p>
-                        <div style={{ color: '#999', fontSize: 11, marginTop: 2 }}>
-                            ID: {record.knowledgeId}
-                        </div>
-                    </div>
-                );
-            }
-        },
-        {
-            title: '使用空间',
-            dataIndex: 'length',
-            width: 90,  // 更紧凑
-            align: 'center',
-            render: (text) => text || '--',
-        },
-        {
-            title: '字符数量',
-            dataIndex: 'wordNum',
-            width: 100,  // 更紧凑
-            align: 'center',
-            render: (text) => text && text !== '--' ? text.toLocaleString() : '--',
-        },
-        {
-            title: '文件数量',
-            dataIndex: 'documentSize',
-            width: 80,  // 更紧凑
-            align: 'center',
-            render: (text) => text && text !== '--' ? text : '--',
-        },
-        {
-            title: '切片数量',
-            dataIndex: 'sliceCount',
-            width: 80,  // 更紧凑
-            align: 'center',
-            render: (text) => text && text !== '--' ? text.toLocaleString() : '--',
-        },
-        {
-            title: '是否公开',
-            dataIndex: 'isOpen',
-            width: 80,  // 更紧凑
-            align: 'center',
-            render: (value) => value === 1 ? '公开' : '私有',
-        },
-        {
-            title: '创建时间',
-            dataIndex: 'createTime',
-            width: 160,  // 更紧凑
-            align: 'center',
-            render: (text) => text && text !== '--' ? dayjs(text).format('YYYY-MM-DD HH:mm:ss') : '--',
-        },
-        {
-            title: '更新时间',
-            dataIndex: 'updateTime',
-            width: 160,  // 更紧凑
-            align: 'center',
-            render: (text) => text && text !== '--' ? dayjs(text).format('YYYY-MM-DD HH:mm:ss') : '--',
-        },
-        {
-            title: '操作',
-            key: 'action',
-            width: 160,  // 进一步缩减宽度
-            fixed: 'right',
-            align: 'center',
-            render: (_text, record) => (
-                <Space size={12}>
-                    {/* 编辑按钮 */}
-                    <Tooltip title="编辑" placement="top">
-                        <Button
-                            type="text"
-                            size="small"
-                            onClick={() => onClickModify(record)}
-                            className="action-btn"
-                        >
-                            <span
-                                className="iconify"
-                                data-icon="material-symbols:edit-rounded"
-                            />
-                        </Button>
-                    </Tooltip>
-
-                    {/* 下载原文档按钮 */}
-                    <Tooltip title="下载原文档" placement="top">
-                        <Button
-                            type="text"
-                            size="small"
-                            onClick={() => onClickDownload(record)}
-                            className="action-btn"
-                        >
-                            <span
-                                className="iconify"
-                                data-icon="solar:download-square-bold"
-                            />
-                        </Button>
-                    </Tooltip>
-
-                    {/* 下载 MD 文档按钮 */}
-                    <Tooltip title="下载 MD 文档" placement="top">
-                        <Button
-                            type="text"
-                            size="small"
-                            onClick={() => onClickDownloadMd(record)}
-                            className="action-btn"
-                        >
-                            <span
-                                className="iconify"
-                                data-icon="material-symbols:description-rounded"
-                            />
-                        </Button>
-                    </Tooltip>
-
-                    {/* 删除按钮 */}
-                    <Tooltip title="删除" placement="top">
-                        <Button
-                            type="text"
-                            size="small"
-                            danger
-                            onClick={() => onClickDelete(record)}
-                            className="action-btn"
-                        >
-                            <span
-                                className="iconify"
-                                data-icon="material-symbols:delete-rounded"
-                            />
-                        </Button>
-                    </Tooltip>
-                </Space>
-            )
-        },
-    ];
-
-    // 获取 GuideTips 配置
-    const guideTipsConfig = getGuideTipsConfig('knowledgeLib');
-
-    return (
-        <div className="page-container">
-            {/* 标题区域 */}
-            <div className="list-header with-tips">
-                <div className='list-header-title'>
-                    <h1>知识库管理</h1>
-                    <p>管理您的所有知识库</p>
-                </div>
-                <div className='list-header-actions'>
-                    <Button
-                        type='primary'
-                        icon={<PlusOutlined />}
-                        onClick={onCreateKnowledgeLibClick}
-                    >
-                        创建
-                    </Button>
-                </div>
-            </div>
-
-            {/* Tips 提示组件 */}
-            {guideTipsConfig && (
-                <GuideTips
-                    visible={true}
-                    title={guideTipsConfig.title}
-                    steps={guideTipsConfig.steps}
-                />
-            )}
-
-            {/* 筛选区域 - 使用 FilterBar 组件 */}
-            <FilterBar
-                tabs={[
-                    { key: 'all', label: '全部' },
-                    { key: 'mine', label: '我创建的' },
-                    { key: 'maintainer', label: '我可以维护的' },
-                ]}
-                activeTab={activeTab}
-                onTabChange={setActiveTab}
-                searchPlaceholder="请输入知识库名称"
-                searchValue={searchName}
-                onSearchChange={setSearchName}
-                onSearch={async () => {
-                    // 模拟异步搜索
-                    await new Promise(resolve => setTimeout(resolve, 500));
-                    message.info('搜索功能开发中');
-                }}
-                searchWidth={300}
-            />
-
-            {/* 表格区域 */}
-            <div className="content-section">
-                <Table
-                    rowKey="knowledgeId"
-                    loading={listLoading}
-                    columns={columns}
-                    dataSource={paginatedList}
-                    pagination={{
-                        current: currentPage,
-                        pageSize: pageSize,
-                        total: filteredList.length,
-                        onChange: handlePageChange,
-                        showSizeChanger: true,
-                        showQuickJumper: true,
-                        showTotal: (total) => `共 ${total} 条`,
-                        pageSizeOptions: ['10', '20', '50', '100'],
-                    }}
-                    scroll={{ x: 1200 }}
-                />
-            </div>
-
-            {/* 创建/编辑弹窗 */}
-            <InfoModal
-                id={infoModalId}
-                open={infoModalOpen}
-                onClickConfirm={infoModalOnClickConfirm}
-                onClickCancel={infoModalOnClickCancel}
-            />
-        </div>
-    );
-};
-
-export default KnowledgeLibList;

+ 0 - 51
jk-rag-platform/src/pages/knowledgeLib/list/style.less.backup.final

@@ -1,51 +0,0 @@
-// 导入全局样式变量
-@import '@/styles/variables.less';
-
-// 知识库列表页面样式
-// 说明:优先使用全局样式,本文件只定义必要的表格优化样式
-
-// ===== 表格样式优化 =====
-.content-section {
-    .ant-table {
-        // 使用 Ant Design 默认字体大小,不覆盖
-        // 让全局 .ant-typography 样式控制
-
-        // 表头样式
-        .ant-table-thead {
-            .ant-table-cell {
-                font-weight: @font-weight-semibold;
-                background: @bg-tertiary;
-            }
-        }
-
-        // 表体样式
-        .ant-table-tbody {
-            .ant-table-row {
-                &:hover {
-                    background: @bg-hover;
-                }
-            }
-        }
-    }
-}
-
-// ===== 操作栏图标样式 =====
-.action-btn {
-    padding: @spacing-1;  // 4px
-    color: @text-secondary;
-    transition: all 0.2s ease;
-
-    .iconify {
-        font-size: 16px;  // 图标大小
-    }
-
-    &:hover {
-        color: @primary-color;
-    }
-
-    &[danger] {
-        &:hover {
-            color: @error-color;
-        }
-    }
-}

+ 11 - 11
jk-rag-platform/src/pages/knowledgeLib/list/style.less → jk-rag-platform/src/pages/knowledgeLib/list/style.scss

@@ -1,5 +1,5 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // 知识库列表页面样式
 // 说明:优先使用全局样式,本文件只定义必要的表格优化样式
@@ -13,8 +13,8 @@
         // 表头样式
         .ant-table-thead {
             .ant-table-cell {
-                font-weight: @font-weight-semibold;
-                background: @bg-tertiary;
+                font-weight: $font-weight-semibold;
+                background: $bg-tertiary;
             }
         }
 
@@ -22,7 +22,7 @@
         .ant-table-tbody {
             .ant-table-row {
                 &:hover {
-                    background: @bg-hover;
+                    background: $bg-hover;
                 }
             }
         }
@@ -38,9 +38,9 @@
     display: flex;
     align-items: center;
     justify-content: center;
-    color: @text-secondary;
+    color: $text-secondary;
     transition: all 0.2s ease;
-    border-radius: @radius-md;  // 8px 圆角
+    border-radius: $radius-md;  // 8px 圆角
 
     // iconify 图标样式
     .iconify {
@@ -51,8 +51,8 @@
 
     // 悬停效果
     &:hover {
-        background: fade(@primary-color, 10%);
-        color: @primary-color;
+        background: fade($primary-color, 10%);
+        color: $primary-color;
 
         .iconify {
             transform: scale(1.15);  // 图标放大
@@ -62,8 +62,8 @@
     // 危险操作(删除)
     &[danger] {
         &:hover {
-            background: fade(@error-color, 10%);
-            color: @error-color;
+            background: fade($error-color, 10%);
+            color: $error-color;
         }
     }
 
@@ -77,7 +77,7 @@
 .ant-dropdown-menu-item {
     display: flex;
     align-items: center;
-    gap: @spacing-2;
+    gap: $spacing-2;
 
     .anticon {
         font-size: 16px;

+ 2 - 2
jk-rag-platform/src/pages/knowledgeLib/revisionTool/components/reviseDrawer.less → jk-rag-platform/src/pages/knowledgeLib/revisionTool/components/reviseDrawer.scss

@@ -1,5 +1,5 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // ===== 修订工具 Drawer 样式 =====
 // 说明:只定义页面特定的样式
@@ -27,5 +27,5 @@
 .pdf-page-placeholder {
     width: 100%;
     aspect-ratio: 1 / 1.3;
-    background: @bg-tertiary;  // 使用全局变量
+    background: $bg-tertiary;  // 使用全局变量
 }

+ 0 - 547
jk-rag-platform/src/pages/knowledgeLib/revisionTool/components/reviseDrawer.tsx.bak

@@ -1,547 +0,0 @@
-import React, { useEffect, useMemo, useState } from 'react';
-
-import { Drawer, Row, Col, Input, Button, List, Typography, Select, Spin, Image, message, Modal, Tooltip } from 'antd';
-import type { DrawerProps, RadioChangeEvent } from 'antd';
-import { SnippetsOutlined, ExceptionOutlined, DeleteOutlined,QuestionCircleOutlined } from '@ant-design/icons';
-import MarkdownIt from 'markdown-it';
-import { fetchReviseToolAllList, fetchReviseToolSliceList, apis, submitReviseSliceApi } from '@/apis';
-import './reviseDrawer.less'
-import LocalStorage from '@/LocalStorage';
-
-import RevisionHistory from './revisionHistory';
-import tubing from '@/assets/public/tubing.png'
-import rfq from '@/assets/public/rfq.png'
-
-interface ReviseDrawerProps {
-    openDrawer: boolean;
-    onClose: () => void;
-    title: string;
-    record: any;
-}
-
-const ReviseDrawer: React.FC<ReviseDrawerProps> = (props: ReviseDrawerProps) => {
-    const { openDrawer, onClose, title, record } = props;
-    const [placement, setPlacement] = useState<DrawerProps['placement']>('right');
-    const [historyTool, setHistoryTool] = useState<boolean>(false);
-
-    useEffect(() => {
-        if (record.knowledgeId && openDrawer) {
-            // 初始化左侧文档搜索
-            onFetchReviseToolAllList('', 'left', record.knowledgeId);
-            onFetchReviseToolAllList('', 'right', record.knowledgeId);
-        }
-        setHistoryTool(LocalStorage.getStatusFlag('knowledge:revisionTool:history'));
-    }, [openDrawer]);
-
-    const [leftSearch, setLeftSearch] = useState('');// 左边搜索
-    const [rightSearch, setRightSearch] = useState(''); // 右边搜索
-    const [leftSpliceSearch, setLeftSpliceSearch] = useState('');// 左边切片搜索
-    const [rightSpliceSearch, setRightSpliceSearch] = useState('');// 右边切片搜索
-    const [revisionOptions, setRevisionOptions] = useState<any[]>([]);
-    const [revisionLoading, setRevisionLoading] = useState(false);
-    const [selectedRevision, setSelectedRevision] = useState<string | undefined>(undefined);
-    const [leftDocumentId, setLeftDocumentId] = useState(''); // 左边选中的文档ID
-    const [rightDocumentId, setRightDocumentId] = useState(''); // 右边选中的文档ID
-    const [leftOptions, setLeftOptions] = useState<any[]>([]);// 左边数据
-    const [rightOptions, setRightOptions] = useState<any[]>([]);// 右边数据
-    const [leftLoading, setLeftLoading] = useState(false);// 左边加载状态
-    const [rightLoading, setRightLoading] = useState(false);// 右边加载状态
-    const [selectedStandard, setSelectedStandard] = useState<string | null>(null);
-    const [leftSlices, setLeftSlices] = useState<string[]>([]);// 左边切片列表
-    const [rightSlices, setRightSlices] = useState<string[]>([]);// 右边切片列表
-    const [leftSlicesLoading, setLeftSlicesLoading] = useState(false);// 左边切片加载状态
-    const [rightSlicesLoading, setRightSlicesLoading] = useState(false);// 右边切片加载状态
-    const marked = new MarkdownIt({ html: true, typographer: true });
-    useEffect(() => {
-        if (leftDocumentId) {
-            onFetchReviseToolSliceList(leftDocumentId, 'left');
-        }
-    }, [leftDocumentId]);
-    useEffect(() => {
-        if (rightDocumentId) {
-            onFetchReviseToolSliceList(rightDocumentId, 'right');
-        }
-    }, [rightDocumentId]);
-    const onChange = (e: RadioChangeEvent) => {
-        setPlacement(e.target.value);
-    };
-
-    const onFetchTakaiAppTypeListApi = async () => {
-        setRevisionLoading(true);
-        try {
-            const res: any = await apis.fetchTakaiAppTypeList('revision_status');
-            if (res && res.data && Array.isArray(res.data)) {
-                const opts = res.data.map((it: any) => ({ label: it.dictLabel, value: it.dictValue }));
-                setSelectedRevision(opts[0].value);
-                setRevisionOptions(opts);
-            } else {
-                setRevisionOptions([]);
-            }
-        } catch (err) {
-            setRevisionOptions([]);
-        } finally {
-            setRevisionLoading(false);
-        }
-    };
-    useEffect(() => {
-        onFetchTakaiAppTypeListApi();
-    }, []);
-    const customRender = (text: string, mdImgUrlList: any[]) => {
-        // 比如:把 "我是图片" 替换成一张图片
-        mdImgUrlList?.forEach(item => {
-            text = text.replace(new RegExp(item.originText, 'g'), `<img src="${item.mediaUrl}" alt="${item.originText}" />`);
-        })
-        return text;
-    }
-    // 获取知识库修订工具列表
-    const onFetchReviseToolAllList = async (documentName?: string, side?: 'left' | 'right', knowledgeId?: string) => {
-        // Implement the function to fetch revise tool all list
-        const res: any = await fetchReviseToolAllList({ documentName: documentName || '', knowledgeId: knowledgeId || '' });
-        // 假设接口返回结构为 { code: 200, data: [ { id, name }, ... ] }
-        if (res && res.data && Array.isArray(res.data)) {
-            const results = res.data.map((it: any) => ({ label: it.documentName, value: it.documentId }));
-            if (side === 'left')
-                setLeftOptions(results);
-            else if (side === 'right')
-                setRightOptions(results);
-        } else {
-            if (side === 'left')
-                setLeftOptions([]);
-            else if (side === 'right')
-                setRightOptions([]);
-        }
-    };
-    // 获取知识库修订工具切片列表
-    const onFetchReviseToolSliceList = async (documentId: string, side?: 'left' | 'right', sliceText?: string) => {
-        setLeftSlicesLoading(side === 'left' ? true : leftSlicesLoading);
-        setRightSlicesLoading(side === 'right' ? true : rightSlicesLoading);
-        // Implement the function to fetch revise tool slice list
-        const res: any = await fetchReviseToolSliceList({ documentId: documentId || '', sliceText: sliceText || '' });
-        // 假设接口返回结构为 { code: 200, data: [ { sliceContent }, ... ] }
-        if (res && res.data && Array.isArray(res.data)) {
-            const results = res.data;
-            results.forEach((item: any) => {
-                item.sliceText = customRender(item.sliceText, item.mediaList)
-            })
-            if (side === 'left') {
-                setLeftSlicesLoading(false);
-                setLeftSlices(results);
-            }
-            else if (side === 'right') {
-                setRightSlicesLoading(false);
-                setRightSlices(results);
-
-            }
-        } else {
-            if (side === 'left')
-                setLeftSlices([]);
-            else if (side === 'right')
-                setRightSlices([]);
-        }
-    }
-    const [LeftActiveId, setLeftActiveId] = useState<string | null>(null);
-    const [RightActiveId, setRightActiveId] = useState<string | null>(null);
-    const [subLoading, setSubLoading] = useState(false);
-    const [showReviseModal, setShowReviseModal] = useState(false);
-    const [historyDrawerOpen, setHistoryDrawerOpen] = useState(false);
-
-    // 处理从修订历史传来的编辑事件
-    const handleEditFromHistory = async (editRecord: any) => {
-        try {
-            // 1. 关闭修订历史 drawer
-            setHistoryDrawerOpen(false);
-
-            // 2. 设置右侧搜索条件和文档
-            setRightSearch(editRecord.documentName || '');
-            setRightDocumentId(editRecord.documentId);
-            
-            // 3. 设置左侧搜索条件和文档
-            setLeftSearch(editRecord.refDocumentName || '');
-            // 需要根据 refDocumentName 查找对应的文档ID,这里假设需要调用搜索API
-            
-            // 4. 搜索切片列表
-            await onFetchReviseToolSliceList(editRecord.documentId, 'right');
-            if(editRecord.refDocumentId){
-                await onFetchReviseToolSliceList(editRecord.refDocumentId, 'left');
-            }
-            
-            // 5. 通过 sliceId 定位并选中切片
-            // 延迟一下确保数据已加载
-            setTimeout(() => {
-                const targetSliceElement = document.querySelector(`[data-slice-id="${editRecord.sliceId}"]`);
-                if (targetSliceElement) {
-                    setRightActiveId(editRecord.sliceId);
-                    targetSliceElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
-                }
-            }, 300);
-        } catch (error) {
-            message.error('加载编辑数据失败');
-        }
-    };
-    const [editingRightId, setEditingRightId] = useState<string | null>(null);
-
-    const onAllClose = () => {
-        setLeftActiveId(null);
-        setRightActiveId(null);
-        setLeftSearch('');
-        setRightSearch('');
-        setLeftSpliceSearch('');
-        setRightSpliceSearch('');
-        setLeftDocumentId('');
-        setRightDocumentId('');
-        setLeftOptions([]);
-        setRightOptions([]);
-        setLeftSlices([]);
-        setRightSlices([]);
-        setRevisionSliceText('');
-        onClose();
-    }
-    const [revisionSliceText, setRevisionSliceText] = useState<string>('');
-    // submitReviseSliceApi --- IGNORE ---
-    const onSubmitReviseSliceApi = async (selectedRevision: string) => {
-        try {
-            const res: any = await submitReviseSliceApi({
-                revisionSliceText: revisionSliceText,
-                revisionStatus: selectedRevision,
-                sliceId: RightActiveId,
-                refSliceId: selectedRevision === '1' ? LeftActiveId : null
-            })
-            if (res && res.code === 200) {
-                message.success('修订提交成功');
-                if (revisionSliceText && selectedRevision === '1') {
-                    // 如果是修订操作,更新右侧切片内容
-                    setRightSlices(prevSlices => prevSlices.map((slice: any) => {
-                        if (slice.sliceId === RightActiveId) {
-                            return { ...slice, revisionSliceText: revisionSliceText, revisionStatus: '1'};
-                        }
-                        return slice;
-                    }));
-                }
-                if (selectedRevision === '0') {
-                    // 如果是修订操作,更新右侧切片内容
-                    setRightSlices(prevSlices => prevSlices.map((slice: any) => {
-                        if (slice.sliceId === RightActiveId) {
-                            return { ...slice, revisionStatus: '0'};
-                        }
-                        return slice;
-                    }));
-                }
-            }
-        } catch (error: any) {
-            console.log('error', error);
-            message.error(error.msg || '修订提交失败');
-        } finally {
-            setSubLoading(false);
-        }
-    }
-    return (
-        <>
-            <Drawer
-                title={
-                    <div className='flex items-center justify-between'>
-                        <p>{title}</p>
-                        {historyTool && <Button icon={<SnippetsOutlined />} size='small' onClick={() => setHistoryDrawerOpen(true)}>
-                            修订记录
-                        </Button>}
-                        {/* <p className='cursor-pointer' onClick={() => setHistoryDrawerOpen(true)}>修订历史</p> */}
-                    </div>
-                }
-                placement={placement}
-                closable={true}
-                onClose={onAllClose}
-                open={openDrawer}
-                width={'70%'}
-                footer={
-                    <div style={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center', height: 50, gap: 12 }}>
-                        {/* <Select
-                        style={{ width: 220 }}
-                        options={revisionOptions}
-                        loading={revisionLoading}
-                        placeholder="选择修订状态"
-                        value={selectedRevision}
-                        onChange={(val) => setSelectedRevision(val)}
-                    /> */}
-                        <Button onClick={onAllClose}>关闭</Button>
-                        {/* <Button type="primary"
-                        loading={subLoading}
-                        icon={
-                            <SnippetsOutlined />
-                        }
-                        onClick={async () => {
-                            // 确认动作:这里你可以把选中的修订状态应用到选中项或提交
-                            // submitReviseSliceApi
-                            setSubLoading(true);
-                            try {
-                                const res:any = await submitReviseSliceApi({
-                                    revisionStatus: selectedRevision,
-                                    sliceId: RightActiveId,
-                                    refSliceId: selectedRevision==='1'? LeftActiveId : null
-                                })
-                                if(res && res.code === 200){
-                                    message.success('修订提交成功');
-                                }
-                            } catch (error:any) {
-                                console.log('error',error);
-                                message.error(error.msg || '修订提交失败');
-                            } finally {
-                                setSubLoading(false);
-                            }
-                        }}>保存</Button> */}
-                    </div>
-                }
-            >
-                <div className="flex gap-4 h-full">
-                    {/* Left column */}
-                    <div className="w-1/2 border-r pr-4">
-                        <div className="text-lg font-semibold mb-3">新标准规范文搜索 
-                            <Tooltip title="仅支持标准规范">  <QuestionCircleOutlined style={{fontSize:'15px'}} />
-                            </Tooltip> </div>
-                        <div className="mb-3">
-                            <Select
-                                showSearch
-                                placeholder="搜索标准规范条文"
-                                style={{ width: '100%' }}
-                                value={leftSearch || undefined}
-                                filterOption={false}
-                                onSearch={async (val) => {
-                                    setLeftOptions([]);
-                                    setLeftSearch(val);
-                                    if (!val) {
-                                        return;
-                                    }
-                                    setLeftLoading(true);
-                                    try {
-                                        onFetchReviseToolAllList(val, 'left',record.knowledgeId?record.knowledgeId:'');
-                                    } catch (err) {
-                                        setLeftOptions([]);
-                                    } finally {
-                                        setLeftLoading(false);
-                                    }
-                                }}
-                                notFoundContent={leftLoading ? <Spin size="small" /> : '无匹配结果'}
-                                options={leftOptions}
-                                onChange={(value, option) => {
-                                    // value is the selected option value (id), option contains label
-                                    const label = (option && (option as any).label) || value;
-                                    setLeftSearch(label as string);
-                                    setLeftDocumentId(value as string);
-                                    setLeftSpliceSearch('');
-                                    // Fetch slices for the selected document
-                                }}
-                            />
-                        </div>
-                        <div className="mb-3">
-                            <div className="flex items-center justify-between mb-2">
-                                <div className="font-medium">标准规范条文结果</div>
-                                <div style={{ width: 230 }}>
-                                    <Input.Search placeholder="搜索切片" disabled={!leftSearch} value={leftSpliceSearch} enterButton size="small"
-                                        onChange={(e) => {
-                                            setLeftSpliceSearch(e.target.value);
-                                        }}
-                                        onSearch={(e) => {
-                                            onFetchReviseToolSliceList(leftDocumentId, 'left', e);
-                                        }} />
-                                </div>
-                            </div>
-                        </div>
-
-                        <Spin spinning={leftSlicesLoading} className="h-full">
-                            {leftSlices.length > 0 ? <div className="overflow-y-auto max-h-full" style={{ maxHeight: 'calc(90vh - 130px)' }}>
-                                {leftSlices.map((module: any, index) => (
-                                    <div
-                                        key={index}
-                                        className={`relative px-3 py-2 text-sm leading-7 transition-all duration-200 cursor-pointer 
-                                        ${index !== leftSlices.length - 1 ? 'border-b border-gray-100' : ''
-                                            } ${LeftActiveId === module.sliceId
-                                                ? 'bg-blue-100  border-l-blue-500 shadow-sm scale-[1]'
-                                                : 'hover:bg-gray-50'
-                                            }`}
-                                        onClick={() => { setLeftActiveId(module.sliceId); setRevisionSliceText(module.sliceText || '') }}
-                                    >
-                                        {module.sliceText && (() => {
-                                            const html = marked.render(module.sliceText || '');
-                                            const parts: any[] = [];
-                                            const imgRegex = /<img[^>]*src=["']([^"']+)["'][^>]*>/g;
-                                            let lastIndex = 0;
-                                            let match: RegExpExecArray | null;
-                                            while ((match = imgRegex.exec(html)) !== null) {
-                                                const idx = match.index;
-                                                const textSeg = html.substring(lastIndex, idx);
-                                                if (textSeg) parts.push(<div key={`t-${index}-${lastIndex}`} dangerouslySetInnerHTML={{ __html: textSeg }} />);
-                                                const url = match[1];
-                                                parts.push(<Image key={`img-${index}-${idx}`} src={url} preview={{}} style={{ maxWidth: '100%' }} />);
-                                                lastIndex = idx + match[0].length;
-                                            }
-                                            const rest = html.substring(lastIndex);
-                                            if (rest) parts.push(<div key={`tlast-${index}`} dangerouslySetInnerHTML={{ __html: rest }} />);
-                                            return <div className="text-sm leading-7 text-gray-800 markdown-preview">{parts}</div>;
-                                        })()}
-                                    </div>
-                                ))}
-                            </div> : <div className="text-center text-gray-500 mt-10">暂无数据</div>}
-                        </Spin>
-                    </div>
-
-                    {/* Right column (same as left) */}
-                    <div className="w-1/2">
-                        <div className="text-lg font-semibold mb-3">受影响规范条文结果
-                            <Tooltip title="仅支持标准规范">  <QuestionCircleOutlined style={{fontSize:'15px'}} />
-                            </Tooltip>
-                        </div>
-                        <div className="mb-3">
-                            <Select
-                                showSearch
-                                placeholder="搜索受影响标准规范条文"
-                                style={{ width: '100%' }}
-                                value={rightSearch || undefined}
-                                filterOption={false}
-                                onSearch={async (val) => {
-                                    setRightSearch(val);
-                                    if (!val) {
-                                        setRightOptions([]);
-                                        return;
-                                    }
-                                    setRightLoading(true);
-                                    try {
-                                        onFetchReviseToolAllList(val, 'right',record.knowledgeId?record.knowledgeId:'');
-                                    } catch (err) {
-                                        setRightOptions([]);
-                                    } finally {
-                                        setRightLoading(false);
-                                    }
-                                }}
-                                notFoundContent={rightLoading ? <Spin size="small" /> : '无匹配结果'}
-                                options={rightOptions}
-                                onChange={(value, option) => {
-                                    // value is the selected option value (id), option contains label
-                                    const label = (option && (option as any).label) || value;
-                                    setRightSearch(label as string);
-                                    setRightDocumentId(value as string);
-                                    setRightSpliceSearch('');
-                                    // Fetch slices for the selected document
-                                }}
-                            />
-                        </div>
-
-                        <div className="mb-3">
-                            <div className="flex items-center justify-between mb-2">
-                                <div className="font-medium">受影响规范条文结果 </div>
-                                <div style={{ width: 230 }}>
-                                    <Input.Search placeholder="搜索切片" disabled={!rightSearch} value={rightSpliceSearch}
-                                        onChange={(e) => {
-                                            setRightSpliceSearch(e.target.value);
-                                        }}
-                                        onSearch={(e) => {
-                                            onFetchReviseToolSliceList(rightDocumentId, 'right', e);
-                                        }} enterButton size="small" />
-                                </div>
-                            </div>
-                        </div>
-                        <Spin spinning={rightSlicesLoading} className='h-full'>
-                            {rightSlices.length > 0 ? <div className="overflow-y-auto" style={{ maxHeight: 'calc(80vh - 130px)' }}>
-                                {rightSlices.map((module: any, index) => (
-                                    <div
-                                        key={`r-${index}`}
-                                        data-slice-id={module.sliceId}
-                                        className={`relative px-3 py-2 text-sm leading-7 transition-all duration-200 cursor-pointer 
-                                        ${index !== rightSlices.length - 1 ? 'border-b border-gray-100' : ''
-                                            } ${RightActiveId === module.sliceId
-                                                ? 'bg-blue-100  border-l-blue-500 shadow-sm scale-[1] pb-[40px]'
-                                                : 'hover:bg-gray-50'
-                                            } ${RightActiveId === module.sliceId && editingRightId === module.sliceId ? 'pb-[340px]' : ''}`
-                                        }
-                                        onClick={() => setRightActiveId(module.sliceId)}
-                                    >
-                                        <div className='absolute top-1 right-3'>
-                                            {module.revisionStatus==='1' && <Tooltip title={module.revisionSliceText} placement="top">
-                                                <Image width={16} height={16} src={tubing} preview={false} className='cursor-pointer ml-2' />
-                                            </Tooltip>}
-                                            {module.revisionStatus==='0' && <Tooltip title="已废弃" placement="top">
-                                                <Image width={16} height={16} src={rfq} preview={false} className='cursor-pointer ml-2' />
-                                            </Tooltip>}
-                                        </div>
-                                        {module.sliceText && (() => {
-                                            const html = marked.render(module.sliceText || '');
-                                            const parts: any[] = [];
-                                            const imgRegex = /<img[^>]*src=["']([^"']+)["'][^>]*>/g;
-                                            let lastIndex = 0;
-                                            let match: RegExpExecArray | null;
-                                            while ((match = imgRegex.exec(html)) !== null) {
-                                                const idx = match.index;
-                                                const textSeg = html.substring(lastIndex, idx);
-                                                if (textSeg) parts.push(<div key={`rt-${index}-${lastIndex}`} dangerouslySetInnerHTML={{ __html: textSeg }} />);
-                                                const url = match[1];
-                                                parts.push(<Image key={`rimg-${index}-${idx}`} src={url} preview={{}} style={{ maxWidth: '100%' }} />);
-                                                lastIndex = idx + match[0].length;
-                                            }
-                                            const rest = html.substring(lastIndex);
-                                            if (rest) parts.push(<div key={`rt-last-${index}`} dangerouslySetInnerHTML={{ __html: rest }} />);
-                                            return <div className="text-sm leading-7 text-gray-800 markdown-preview">{parts}</div>;
-                                        })()}
-                                        {RightActiveId === module.sliceId && (
-                                            <div className="absolute bottom-2 w-full right-3">
-                                                <div className="w-full flex flex-col items-end">
-                                                    {editingRightId === module.sliceId && <div className="w-[96%] flex items-center gap-2 justify-end mb-2">
-                                                        <Input.TextArea autoSize={{ minRows: 13, maxRows: 13 }} value={revisionSliceText} onChange={(e) => setRevisionSliceText(e.target.value)} />
-                                                    </div>}
-                                                    <p className='w-full flex items-center gap-2 justify-end'>
-                                                        <Button size="small" danger onClick={(e) => {
-                                                            e.stopPropagation();
-                                                            onSubmitReviseSliceApi('0');
-                                                        }}>废弃</Button>
-                                                        {editingRightId !== module.sliceId ? (
-                                                            <Button size="small" type="primary" onClick={(e) => {
-                                                                e.stopPropagation();
-                                                                setRevisionSliceText(module.revisionSliceText || '');
-                                                                setEditingRightId(module.sliceId);
-                                                            }}>修订</Button>
-                                                        ) : (
-                                                            <Button size="small" type="primary" loading={subLoading} onClick={async (e) => {
-                                                                e.stopPropagation();
-                                                                setSubLoading(true);
-                                                                await onSubmitReviseSliceApi('1');
-                                                                setSubLoading(false);
-                                                                setEditingRightId(null);
-                                                            }}>保存</Button>
-                                                        )}
-                                                    </p>
-                                                </div>
-                                                {/* {editingRightId === module.sliceId && (
-                                                <div className="mt-2 pr-20">
-                                                    <Input.TextArea autoSize={{ maxRows: 3 }} value={revisionSliceText} onChange={(e) => setRevisionSliceText(e.target.value)} />
-                                                </div>
-                                            )} */}
-                                            </div>
-                                        )}
-                                    </div>
-                                ))}
-                            </div> : <div className="text-center text-gray-500 mt-10">暂无数据</div>}
-                        </Spin>
-
-                    </div>
-                </div>
-                <Modal
-                    title="修订切片"
-                    open={showReviseModal}
-                    onCancel={() => setShowReviseModal(false)}
-                    footer={
-                        <div className="flex gap-2 justify-end">
-                            <Button onClick={() => setShowReviseModal(false)}>取消</Button>
-                            <Button type="primary" loading={subLoading} onClick={async () => {
-                                setSubLoading(true);
-                                await onSubmitReviseSliceApi('1');
-                                setSubLoading(false);
-                                setShowReviseModal(false);
-                            }}>确定</Button>
-                        </div>
-                    }
-                >
-                    <Input.TextArea autoSize={{ minRows: 6 }} value={revisionSliceText} onChange={(e) => setRevisionSliceText(e.target.value)} />
-                </Modal>
-            </Drawer>
-            {/* 修订历史 */}
-            <RevisionHistory historyDrawerOpen={historyDrawerOpen} record={record} onClose={() => setHistoryDrawerOpen(false)} onEdit={handleEditFromHistory} />        
-        </>
-    )
-}
-export default ReviseDrawer;

+ 0 - 344
jk-rag-platform/src/pages/knowledgeLib/revisionTool/components/revisionHistory.tsx.bak

@@ -1,344 +0,0 @@
-import React, { useEffect, useMemo, useRef, useState } from 'react';
-import { Drawer, Row, Col, Input, Button, List, Typography, Select, Spin, Image, message, Table, Tooltip } from 'antd';
-import type { ColumnsType } from 'antd/es/table';
-
-import { fetchReviseHistoryList } from '@/apis';
-
-import {InfoCircleOutlined} from "@ant-design/icons";
-
-interface RevisionHistoryProps {
-    historyDrawerOpen: boolean;
-    onClose: () => void;
-    onEdit?: (record: any) => void;
-    // title: string;
-    record: any;
-}
-
-const RevisionHistory: React.FC<RevisionHistoryProps> = (props) => {
-    const { historyDrawerOpen, onClose, onEdit, record } = props;
-    const [kbName, setKbName] = useState<string>('');
-    const [docName, setDocName] = useState<string>('');
-    const [refDocName, setRefDocName] = useState<string>('');
-    const [loading, setLoading] = useState<boolean>(false);
-    const [data, setData] = useState<any[]>([]);
-    // const [pageNum, setPage] = useState<number>(1);
-    const pageNum = useRef<number>(1);
-    const [pageSize, setSize] = useState<number>(10);
-    const [total, setTotal] = useState<number>(0);
-// 1. 创建一个查询触发器
-    const [searchTrigger, setSearchTrigger] = useState(0);
-    const onFetchReviseHistoryList = async () => {
-        setLoading(true);
-        try {
-            const params = {
-                pageNum: pageNum.current,
-                pageSize: pageSize,
-                knowledgeId: record.knowledgeId,
-                knowledgeName: kbName,
-                documentName: docName,
-                refDocumentName: refDocName,
-            };
-            const response: any = await fetchReviseHistoryList(params);
-            if (response && response.rows) {
-                setData(response.rows);
-                setTotal(response.total || 0);
-            } else {
-                message.error('获取修订历史失败');
-            }
-        } catch (error) {
-            message.error('获取修订历史异常');
-        } finally {
-            setLoading(false);
-        }
-    }
-    useEffect(() => {
-        // 避免组件首次挂载时就执行查询
-        if (searchTrigger > 0) {
-            onFetchReviseHistoryList();
-        }
-    }, [searchTrigger, kbName, docName, refDocName]);
-
-    useEffect(() => {
-        if (historyDrawerOpen) {
-            onFetchReviseHistoryList();
-        }
-    }, [historyDrawerOpen]);
-
-    const handleSearch = () => {
-        pageNum.current = 1;
-        setSearchTrigger(prev => prev + 1);
-    }
-    const onSet = () => {
-        setKbName('');
-        setDocName('');
-        setRefDocName('');
-        pageNum.current = 1;
-        setSearchTrigger(prev => prev + 1);
-    }
-    // 表格列定义
-const columns: ColumnsType<any> = [
-    {
-        title: (
-            <span>
-                受影响文档名称
-                <Tooltip
-                    title={
-                        // 说明文本:支持换行、自定义样式,可根据需求修改
-                        <div style={{ maxWidth: 300, whiteSpace: 'normal' }}>
-                            <b>知识库中的知识文档名称</b>
-                        </div>
-                    }
-                    placement="right" // 提示框显示在图标右侧
-                >
-                  <InfoCircleOutlined
-                      style={{
-                          marginLeft: 6, // 与标题的间距
-                          color: '#1890ff', // Ant Design 主色,适配风格
-                          fontSize: 14 // 图标大小适配标题
-                      }}
-                  />
-                </Tooltip>
-              </span>
-        ),
-        dataIndex: 'documentName', width: 160, ellipsis: {
-            showTitle: false,
-        },
-        render: (text) => (
-            <Tooltip placement="topLeft" title={(
-                <div style={{ maxWidth: 480, maxHeight: 320, overflow: 'auto', whiteSpace: 'normal' }}>
-                    {text}
-                </div>
-            )}>
-                {text}
-            </Tooltip>
-        ),
-    },
-    {
-        title: (
-            <span>
-                所在知识库
-                <Tooltip
-                    title={
-                        // 说明文本:支持换行、自定义样式,可根据需求修改
-                        <div style={{ maxWidth: 300, whiteSpace: 'normal' }}>
-                            <b>知识文档所在知识库名称</b>
-                        </div>
-                    }
-                    placement="right" // 提示框显示在图标右侧
-                    // arrowPointAtCenter // 箭头指向图标中心,更美观
-                >
-                  <InfoCircleOutlined
-                      style={{
-                          marginLeft: 6, // 与标题的间距
-                          color: '#1890ff', // Ant Design 主色,适配风格
-                          fontSize: 14 // 图标大小适配标题
-                      }}
-                  />
-                </Tooltip>
-              </span>
-        )
-        , dataIndex: 'knowledgeName', width: 180, ellipsis: {
-            showTitle: false,
-        },
-        render: (text) => (
-            <Tooltip placement="topLeft" title={(
-                <div style={{ maxWidth: 480, maxHeight: 320, overflow: 'auto', whiteSpace: 'normal' }}>
-                    {text}
-                </div>
-            )}>
-                {text}
-            </Tooltip>
-        ),
-    },
-    {
-        title: (
-            <span>
-                受影响规范条文原文
-                <Tooltip
-                    title={
-                        // 说明文本:支持换行、自定义样式,可根据需求修改
-                        <div style={{ maxWidth: 300, whiteSpace: 'normal' }}>
-                            <b>切片原文</b>
-                        </div>
-                    }
-                    placement="right" // 提示框显示在图标右侧
-                >
-                  <InfoCircleOutlined
-                      style={{
-                          marginLeft: 6, // 与标题的间距
-                          color: '#1890ff', // Ant Design 主色,适配风格
-                          fontSize: 14 // 图标大小适配标题
-                      }}
-                  />
-                </Tooltip>
-              </span>
-        )
-        , dataIndex: 'sliceText', width: 180,
-        ellipsis: {
-            showTitle: false,
-        },
-        render: (text) => (
-            <Tooltip placement="topLeft" title={(
-                <div style={{ maxWidth: 480, maxHeight: 320, overflow: 'auto', whiteSpace: 'normal' }}>
-                    {text}
-                </div>
-            )}>
-                {text}
-            </Tooltip>
-        ),
-    },
-    {
-        title: (
-            <span>
-                新标准规范文档
-                <Tooltip
-                    title={
-                        // 说明文本:支持换行、自定义样式,可根据需求修改
-                        <div style={{ maxWidth: 300, whiteSpace: 'normal' }}>
-                            <b>引用的新标准规范文档名称</b>
-                        </div>
-                    }
-                    placement="right" // 提示框显示在图标右侧
-                    // arrowPointAtCenter // 箭头指向图标中心,更美观
-                >
-                  <InfoCircleOutlined
-                      style={{
-                          marginLeft: 6, // 与标题的间距
-                          color: '#1890ff', // Ant Design 主色,适配风格
-                          fontSize: 14 // 图标大小适配标题
-                      }}
-                  />
-                </Tooltip>
-              </span>
-        )
-        , dataIndex: 'refDocumentName', width: 180, ellipsis: {
-            showTitle: false,
-        },
-        render: (text) => (
-            <Tooltip placement="topLeft" title={(
-                <div style={{ maxWidth: 480, maxHeight: 320, overflow: 'auto', whiteSpace: 'normal' }}>
-                    {text}
-                </div>
-            )}>
-                {text}
-            </Tooltip>
-        ),
-    },
-    {
-        title: (
-            <span>
-                修订后的受影响规范条文
-                <Tooltip
-                    title={
-                        // 说明文本:支持换行、自定义样式,可根据需求修改
-                        <div style={{ maxWidth: 300, whiteSpace: 'normal' }}>
-                            <b>根据新标准规范文档修订后的切片内容</b>
-                        </div>
-                    }
-                    placement="right" // 提示框显示在图标右侧
-                    // arrowPointAtCenter // 箭头指向图标中心,更美观
-                >
-                  <InfoCircleOutlined
-                      style={{
-                          marginLeft: 6, // 与标题的间距
-                          color: '#1890ff', // Ant Design 主色,适配风格
-                          fontSize: 14 // 图标大小适配标题
-                      }}
-                  />
-                </Tooltip>
-              </span>
-        )
-        , dataIndex: 'revisionSliceText', width: 230,
-        ellipsis: {
-            showTitle: false,
-        },
-        render: (text,record) => (
-            <Tooltip placement="topLeft" title={(
-                <div style={{ maxWidth: 480, maxHeight: 320, overflow: 'auto', whiteSpace: 'normal' }}>
-                    {text}
-                </div>
-            )}>
-                {text}
-            </Tooltip>
-        ),
-    },
-
-    {
-        title: '修订时间', dataIndex: 'createTime', width: 120, align: 'center'
-    },
-    {
-        title: '修订状态', dataIndex: 'revisionStatus', width: 120, align: 'center', render: (text: any, record: any) => (
-            <p>{text === '1' ? '已修订' : '废弃'}</p>
-        )
-    },
-    {
-        title: '操作', dataIndex: 'revisionStatus', width: 120, align: 'center', render: (text: any, record: any) => (
-            <p 
-                className='text-primary cursor-pointer'
-                onClick={() => {
-                    onEdit?.(record);
-                }}
-            >
-                重新修订
-            </p>
-        )
-    }
-];
-    return (
-        <Drawer
-            title="修订记录"
-            placement="left"
-            width={'60%'}
-            onClose={() => { onClose() }}
-            open={historyDrawerOpen}
-        >
-            <div style={{ marginBottom: 12 }}>
-                <Row gutter={12} align="middle">
-                    <Col span={6}>
-                        <Input placeholder="知识库名称" value={kbName} onChange={(e) =>{
-                            setKbName(e.target.value)
-                            setSearchTrigger(0)
-                        }} />
-                    </Col>
-                    <Col span={6}>
-                        <Input placeholder="受影响文档名称" value={docName} onChange={(e) => {
-                            setDocName(e.target.value)
-                            setSearchTrigger(0)
-                        }} />
-                    </Col>
-                    <Col span={6}>
-                        <Input placeholder="新标准规范文档名称" value={refDocName} onChange={(e) => {
-                            setRefDocName(e.target.value)
-                            setSearchTrigger(0)
-                        }} />
-                    </Col>
-                    <Col>
-                        <Button className='mr-2' onClick={onSet}>重置</Button>
-                        <Button type="primary" onClick={handleSearch}>搜索</Button>
-                    </Col>
-                </Row>
-            </div>
-            <div>
-                <Table
-                    columns={columns}
-                    dataSource={data}
-                    loading={loading}
-                    rowKey="createTime"
-                    pagination={{
-                        current: pageNum.current,
-                        pageSize: pageSize,
-                        total: total,
-                        onChange: (p, ps) => {
-                            console.log('onChange', p, ps);
-                            pageNum.current = p;
-                            setSize(ps || pageSize);
-                            onFetchReviseHistoryList();
-                        }
-                    }}
-                />
-            </div>
-        </Drawer>)
-}
-
-
-export default RevisionHistory;

+ 0 - 163
jk-rag-platform/src/pages/knowledgeLib/revisionTool/list/index.tsx.bak

@@ -1,163 +0,0 @@
-import React, { useEffect, useMemo, useState } from 'react';
-import { Table, Pagination, Space, Button, TablePaginationConfig, Input } from 'antd';
-import type { ColumnsType } from 'antd/es/table';
-
-import { SearchOutlined, BulbOutlined, CloseOutlined } from '@ant-design/icons';
-
-import store from './store';
-import Step from '@/components/step';
-import ReviseDrawer from '../components/reviseDrawer';
-import LocalStorage from '@/LocalStorage';
-
-
-interface KnowledgeRecord {
-  knowledgeName: string;
-  knowledgeId: string | number;
-  length: string | number; // 使用空间
-  appNames: string; // 关联空间
-  documentSize: number; // 文件数量
-  createTime: string; // 创建时间
-  updateTime: string; // 更新时间
-  status: string | number; // 状态
-}
-
-
-const RevisionToolList: React.FC = () => {
-  const [loading, setLoading] = useState(false);
-
-  const { state, init, handleNameChange, onChangePagination } = store;
-
-  const { reviseList, knowledgeName } = state
-
-
-  const [allupdateSliceDoc, setAllupdateSliceDoc] = useState<boolean>(false);
-  const [singleUpdateSliceDoc, setSingleUpdateSliceDoc] = useState<boolean>(false);
-  useEffect(() => {
-    // 模拟加载状态
-    setLoading(true);
-    const t = setTimeout(() => setLoading(false), 250);
-    return () => clearTimeout(t);
-  }, [state.page]);
-  useEffect(() => {
-
-    setAllupdateSliceDoc(LocalStorage.getStatusFlag('knowledge:revisionTool:all'));
-    setSingleUpdateSliceDoc(LocalStorage.getStatusFlag('knowledge:revisionTool:single'));
-
-
-
-    init();
-  }, []);
-
-  const steps = [
-    {
-      title: '选择知识库',
-      description: '选中某个已解析完成的标准规范类知识库',
-      number: 1,
-    },
-    {
-      title: '选择切片,检索相似的切片',
-      description: '切片工具中选择切片,系统将自动检索相似的切片',
-      number: 2,
-    },
-    {
-      title: '人工标记',
-      description: '对目标切片进行快速标记',
-      number: 3,
-    }
-  ]
-  const [showGuide, setShowGuide] = React.useState<boolean>(() => localStorage.getItem('revisionToolGuideHidden') !== 'true');
-  const hideGuide = () => { localStorage.setItem('revisionToolGuideHidden', 'true'); setShowGuide(false); };
-  const [record, setRecord] = useState<KnowledgeRecord>({} as KnowledgeRecord);
-  const columns: ColumnsType<KnowledgeRecord> = [
-    {
-      title: '知识库名称/ID', dataIndex: 'knowledgeName', width: 220,
-      render: (text, record: any) => {
-        return <div>
-          <div style={{ fontWeight: 600 }} className='cursor-pointer text-primary'>{text}</div>
-          <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>ID: {record?.knowledgeId}
-          </div>
-        </div>
-      },
-    },
-    {
-      title: '关联RAG应用', dataIndex: 'appNames', width: 120, render: (text) => (
-          text.length ? text.map((item: string, index: number) => (
-              <span key={index} className='mr-2'>{item}{index === text.length - 1 ? '' : ','}</span>
-          )) : '-'
-      )
-    },
-    { title: '使用空间', dataIndex: 'length', width: 120 },
-
-    { title: '文件数量', dataIndex: 'documentSize', width: 120 },
-    { title: '创建时间', dataIndex: 'createTime', width: 140 },
-    { title: '更新时间', dataIndex: 'updateTime', width: 140 },
-    {
-      title: '操作',
-      dataIndex: 'status',
-      key: 'status',
-      align: 'center',
-      width: 120,
-      render: (val, record) => (
-        singleUpdateSliceDoc ? <Button type="link" onClick={() => { setOpenDrawer(true); setTitle(record.knowledgeName); setRecord(record); }}>当前知识修订</Button> :
-        '-'
-        // <Button type="link" onClick={() => { setOpenDrawer(true); setTitle(record.knowledgeName); setRecord(record); }}>当前知识修订</Button>
-      ),
-    }
-  ]
-
-  const paginationConfig: TablePaginationConfig = {
-    // 显示数据总量
-    showTotal: (total: number) => {
-      return `共 ${total} 条`;
-    },
-    // 展示分页条数切换
-    showSizeChanger: true,
-    // 指定每页显示条数
-    pageSizeOptions: ['10', '20', '50', '100'],
-    // 快速跳转至某页
-    showQuickJumper: true,
-    current: state.page.page,
-    pageSize: state.page.size,
-    total: state.page.total,
-    onChange: async (page, pageSize) => {
-      await onChangePagination(page, pageSize);
-    },
-  };
-  const [openDrawer, setOpenDrawer] = useState(false);
-  const [title, setTitle] = useState('');
-  return (
-    <div style={{ padding: 20, background: '#fff' }}>
-      {showGuide && <> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
-        <div style={{ fontSize: 13, color: '#333', display: 'flex', alignItems: 'center', gap: 6 }}>
-          <BulbOutlined style={{ color: '#faad14' }} />
-          提示: 如何创建并管理自己的知识库?
-        </div>
-        <CloseOutlined onClick={hideGuide} style={{ color: '#999', cursor: 'pointer' }} />
-      </div>
-        <Step steps={steps} className='mb-3' /></>}
-      <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 12 }}>
-        {allupdateSliceDoc && <Button type="primary" style={{ margin: 0 }} onClick={() => { setOpenDrawer(true); setRecord({} as KnowledgeRecord); setTitle('全局修订工具'); }}>全局修订工具</Button>}
-        <Space.Compact style={{ width: '300px' }}>
-          <Input
-            placeholder='请输入知识库名称进行搜索'
-            value={knowledgeName}
-            onChange={(e) => handleNameChange(e.target.value)}
-          />
-          <Button type="primary" onClick={() => { init() }}><SearchOutlined /></Button>
-        </Space.Compact>
-      </div>
-      {/* 表格 */}
-      <Table
-        columns={columns}
-        dataSource={reviseList}
-        rowKey="knowledgeId"
-        loading={loading}
-        pagination={paginationConfig}
-      />
-      {/* 修订工具弹窗 */}
-      {<ReviseDrawer openDrawer={openDrawer} record={record} title={title} onClose={() => setOpenDrawer(false)} />}
-    </div>
-  );
-};
-
-export default observer(RevisionToolList);

+ 42 - 42
jk-rag-platform/src/pages/layout/components/header.less → jk-rag-platform/src/pages/layout/components/header.scss

@@ -1,10 +1,10 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 .header {
     width: 100%;
-    height: @header-height;
-    padding: 0 @spacing-6;
+    height: $header-height;
+    padding: 0 $spacing-6;
     background: rgba(255, 255, 255, 0.8);
     backdrop-filter: blur(12px);
     -webkit-backdrop-filter: blur(12px);
@@ -12,33 +12,33 @@
     justify-content: space-between;
     align-items: center;
     border-bottom: 1px solid rgba(229, 231, 235, 0.5);
-    box-shadow: @shadow-sm;
+    box-shadow: $shadow-sm;
     position: fixed;
     top: 0;
     left: 0;
-    z-index: @z-index-fixed;
-    transition: @transition-base;
+    z-index: $z-index-fixed;
+    transition: $transition-base;
 
     &.scrolled {
         background: rgba(255, 255, 255, 1);
-        box-shadow: @shadow-md;
+        box-shadow: $shadow-md;
     }
 
     // 汉堡菜单按钮(移动端)
     .mobile-menu-btn {
         display: none;  // 默认隐藏
         font-size: 20px;
-        color: @text-primary;
-        margin-right: @spacing-2;
-        padding: @spacing-2;
+        color: $text-primary;
+        margin-right: $spacing-2;
+        padding: $spacing-2;
         border: none;
         background: transparent;
         cursor: pointer;
         transition: all 0.2s ease;
 
         &:hover {
-            background: @bg-tertiary;
-            border-radius: @radius-md;
+            background: $bg-tertiary;
+            border-radius: $radius-md;
         }
 
         @media (max-width: 768px) {
@@ -51,14 +51,14 @@
         align-items: center;
         justify-content: space-between;  // 两端对齐
         width: 100%;  // 占满宽度
-        gap: @spacing-6;
+        gap: $spacing-6;
     }
 
     // Logo 区域 + 智能问答 wrapper
     &-logo-wrapper {
         display: flex;
         align-items: center;
-        gap: @spacing-4;
+        gap: $spacing-4;
         flex-shrink: 0;
     }
 
@@ -67,13 +67,13 @@
         display: flex;
         align-items: center;
         cursor: pointer;
-        gap: @spacing-3;
+        gap: $spacing-3;
         min-width: 240px;  // 桌面端最小宽度
         flex-shrink: 0;  // 不允许收缩
-        transition: @transition-base;
+        transition: $transition-base;
 
         &:hover {
-            transform: translateX(@spacing-1);
+            transform: translateX($spacing-1);
         }
 
         .logo-image {
@@ -87,27 +87,27 @@
         .logo-divider {
             width: 1px;
             height: 28px;
-            background: @border-base;  // 浅灰色
+            background: $border-base;  // 浅灰色
             flex-shrink: 0;
         }
 
         .logo-text-section {
             display: flex;
             flex-direction: column;
-            gap: @spacing-1;
+            gap: $spacing-1;
 
             .logo-title {
-                font-size: @font-xl;
-                font-weight: @font-weight-bold;
-                color: @text-primary;
+                font-size: $font-xl;
+                font-weight: $font-weight-bold;
+                color: $text-primary;
                 margin: 0;
                 white-space: nowrap;  // 不换行
             }
 
             .logo-subtitle {
-                font-size: @font-xs;
-                font-weight: @font-weight-semibold;
-                color: @primary-light;
+                font-size: $font-xs;
+                font-weight: $font-weight-semibold;
+                color: $primary-light;
                 text-transform: uppercase;
                 letter-spacing: 0.1em;
                 margin: 0;
@@ -116,7 +116,7 @@
     }
     
     // 平板端(≤1024px)样式调整
-    @media (max-width: @screen-lg) {
+    @media (max-width: $screen-lg) {
         &-logo-section {
             min-width: 180px;  // 平板端最小宽度减小
             
@@ -132,7 +132,7 @@
     }
     
     // 移动端(≤768px)样式调整
-    @media (max-width: @screen-md) {
+    @media (max-width: $screen-md) {
         &-logo-section {
             min-width: 140px;  // 移动端最小宽度更小
             
@@ -141,7 +141,7 @@
             }
             
             .logo-title {
-                font-size: @font-lg;  // 标题字体减小
+                font-size: $font-lg;  // 标题字体减小
             }
             
             .logo-subtitle {
@@ -276,7 +276,7 @@
 
         &:hover {
             background: rgba(243, 244, 246, 0.8);
-            color: @primary-color;
+            color: $primary-color;
         }
 
         .iconify {
@@ -333,7 +333,7 @@
         }
 
         &:hover .user-avatar {
-            border-color: @primary-color;
+            border-color: $primary-color;
         }
 
         .user-info {
@@ -352,7 +352,7 @@
             }
 
             &:hover .user-name {
-                color: @primary-color;
+                color: $primary-color;
             }
 
             .user-dept {
@@ -371,21 +371,21 @@
 
     .user-dropdown-menu {
         position: absolute;
-        top: calc(100% + @spacing-2);
+        top: calc(100% + $spacing-2);
         right: 0;
         min-width: 200px;
-        background: @bg-secondary;
-        border-radius: @radius-xl;  // 12px
+        background: $bg-secondary;
+        border-radius: $radius-xl;  // 12px
         box-shadow: @shadow-xl;
-        border: 1px solid @border-base;
-        padding: @spacing-2;
-        z-index: @z-index-dropdown;
+        border: 1px solid $border-base;
+        padding: $spacing-2;
+        z-index: $z-index-dropdown;
         animation: dropdownSlide 0.2s ease;
 
         .user-dropdown-header {
-            padding: @spacing-3 @spacing-4;
-            border-bottom: 1px solid @border-light;
-            margin-bottom: @spacing-2;
+            padding: $spacing-3 $spacing-4;
+            border-bottom: 1px solid $border-light;
+            margin-bottom: $spacing-2;
 
             .user-dropdown-name {
                 font-size: 14px;
@@ -417,10 +417,10 @@
 
             &:hover {
                 background: #F3F4F6;
-                color: @primary-color;
+                color: $primary-color;
 
                 .iconify {
-                    color: @primary-color;
+                    color: $primary-color;
                 }
             }
 

+ 0 - 185
jk-rag-platform/src/pages/layout/components/sidebar.less.bak

@@ -1,185 +0,0 @@
-// 导入全局样式变量
-@import '@/styles/variables.less';
-
-// ===== Sidebar 侧边栏样式 =====
-
-.sidebar {
-    width: @sidebar-width;
-    position: fixed;
-    left: 0;
-    top: @header-height;
-    height: calc(100vh - @header-height);
-    border-right: 1px solid rgba(241, 245, 249, 0.5);
-    background: rgba(255, 255, 255, 0.5);
-    padding: @spacing-6;
-    overflow-y: auto;
-    z-index: 40;
-
-    &::-webkit-scrollbar {
-        width: 6px;
-    }
-
-    &::-webkit-scrollbar-track {
-        background: transparent;
-    }
-
-    &::-webkit-scrollbar-thumb {
-        background: rgba(209, 213, 219, 0.5);
-        border-radius: 3px;
-
-        &:hover {
-            background: rgba(209, 213, 219, 0.8);
-        }
-    }
-}
-
-.sidebar-content {
-    display: flex;
-    flex-direction: column;
-    gap: @spacing-8;
-}
-
-.section-title {
-    font-size: @font-sm;
-    font-weight: 700;
-    color: #9CA3AF;
-    text-transform: uppercase;
-    letter-spacing: 0.1em;
-    margin-bottom: 16px;
-}
-
-.nav-menu {
-    display: flex;
-    flex-direction: column;
-    gap: 4px;
-}
-
-.nav-item {
-    display: flex;
-    align-items: center;
-    gap: 12px;
-    padding: 12px 16px;
-    border-radius: 12px;
-    font-size: 14px;
-    font-weight: 500;
-    color: #6B7280;
-    text-decoration: none;
-    cursor: pointer;
-    transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
-    border: none;
-    background: transparent;
-    width: 100%;
-    text-align: left;
-    position: relative;
-
-    // 悬停状态
-    &:hover {
-        background: #F3F4F6;
-        color: #1F2937;
-        transform: translateX(2px);
-
-        .iconify {
-            transform: scale(1.1);
-        }
-    }
-
-    // 激活状态
-    &.active {
-        background: #EFF6FF;
-        color: #1D4ED8;
-        font-weight: 600;
-        
-        &::before {
-            content: '';
-            position: absolute;
-            left: 0;
-            top: 50%;
-            transform: translateY(-50%);
-            width: 3px;
-            height: 60%;
-            background: #1D4ED8;
-            border-radius: 0 2px 2px 0;
-        }
-
-        .iconify {
-            color: #1D4ED8;
-        }
-    }
-
-    // 按下状态
-    &:active {
-        transform: translateX(1px);
-    }
-
-    .iconify {
-        font-size: 20px;
-        flex-shrink: 0;
-        transition: all 0.2s ease;
-    }
-}
-
-.promo-section {
-    margin-top: 48px;
-    padding: 16px;
-    border-radius: 16px;
-    background: linear-gradient(135deg, #1D4ED8 0%, #172554 100%);
-    color: #FFFFFF;
-    position: relative;
-    overflow: hidden;
-
-    .promo-content {
-        position: relative;
-        z-index: 10;
-    }
-
-    .promo-label {
-        font-size: @font-sm;
-        opacity: 0.8;
-        margin-bottom: 8px;
-    }
-
-    .promo-title {
-        font-size: 15px;
-        font-weight: 700;
-        margin-bottom: 12px;
-        line-height: 1.3;
-    }
-
-    .promo-btn {
-        font-size: 10px;
-        font-weight: 700;
-        padding: 4px 12px;
-        border-radius: 9999px;
-        background: #FFFFFF;
-        color: #1D4ED8;
-        border: none;
-        cursor: pointer;
-        transition: all 0.2s ease;
-
-        &:hover {
-            transform: scale(1.05);
-        }
-    }
-
-    .promo-icon {
-        position: absolute;
-        right: -16px;
-        bottom: -16px;
-        font-size: 72px;
-        opacity: 0.1;
-        color: #FFFFFF;
-    }
-}
-
-@media (max-width: 1024px) {
-    .sidebar {
-        width: 200px;
-        padding: 16px;
-    }
-}
-
-@media (max-width: 768px) {
-    .sidebar {
-        display: none;
-    }
-}

+ 51 - 51
jk-rag-platform/src/pages/layout/components/sidebar.less → jk-rag-platform/src/pages/layout/components/sidebar.scss

@@ -1,17 +1,17 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // ===== Sidebar 侧边栏样式 =====
 
 .sidebar {
-    width: @sidebar-width;
+    width: $sidebar-width;
     position: fixed;
     left: 0;
-    top: @header-height;
-    height: calc(100vh - @header-height);
-    border-right: 1px solid @border-light;
-    background: @sidebar-bg;  // 使用 Sidebar 专用背景色
-    padding: @spacing-6;
+    top: $header-height;
+    height: calc(100vh - $header-height);
+    border-right: 1px solid $border-light;
+    background: $sidebar-bg;  // 使用 Sidebar 专用背景色
+    padding: $spacing-6;
     overflow-y: auto;
     overflow-x: hidden;
     z-index: 40;
@@ -22,23 +22,23 @@
         transform: translateX(0);  // 始终显示
         
         &.collapsed {
-            width: @sidebar-collapsed-width;
+            width: $sidebar-collapsed-width;
         }
     }
 
     // 中等屏(769px~1024px):默认展开(200px),collapsed 时折叠
     @media (min-width: 769px) and (max-width: 1024px) {
-        width: @sidebar-width-md;
+        width: $sidebar-width-md;
         transform: translateX(0);  // 始终显示
         
         &.collapsed {
-            width: @sidebar-collapsed-width;
+            width: $sidebar-collapsed-width;
         }
     }
 
     // 移动端(≤768px):默认隐藏,collapsed 时显示
     @media (max-width: 768px) {
-        width: @sidebar-width;
+        width: $sidebar-width;
         transform: translateX(-100%);  // 默认隐藏
         
         &.collapsed {
@@ -58,17 +58,17 @@
 .sidebar-content {
     display: flex;
     flex-direction: column;
-    gap: @spacing-8;
+    gap: $spacing-8;
 }
 
 // 章节标题 - 使用更具体的选择器避免被其他页面样式覆盖
 .sidebar .section-title {
-    font-size: @font-sm;
-    font-weight: @font-weight-bold;
-    color: @text-hint;
+    font-size: $font-sm;
+    font-weight: $font-weight-bold;
+    color: $text-hint;
     text-transform: uppercase;
     letter-spacing: 0.1em;
-    margin-bottom: @spacing-4;
+    margin-bottom: $spacing-4;
     background: transparent;
     padding: 0;
 }
@@ -82,12 +82,12 @@
 .nav-item {
     display: flex;
     align-items: center;
-    gap: @spacing-3;
-    padding: @spacing-3 @spacing-4;
-    border-radius: @radius-xl;
-    font-size: @font-md;
-    font-weight: @font-weight-medium;
-    color: @sidebar-text-color;  // 使用 Sidebar 专用文字色
+    gap: $spacing-3;
+    padding: $spacing-3 $spacing-4;
+    border-radius: $radius-xl;
+    font-size: $font-md;
+    font-weight: $font-weight-medium;
+    color: $sidebar-text-color;  // 使用 Sidebar 专用文字色
     text-decoration: none;
     cursor: pointer;
     transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
@@ -99,9 +99,9 @@
 
     // 悬停状态
     &:hover {
-        background: @sidebar-hover-bg;
-        color: @sidebar-active-color;  // 悬停时文字变深
-        transform: translateX(@spacing-1);
+        background: $sidebar-hover-bg;
+        color: $sidebar-active-color;  // 悬停时文字变深
+        transform: translateX($spacing-1);
 
         .iconify {
             transform: scale(1.1);
@@ -110,34 +110,34 @@
 
     // 激活状态 - 深灰色粗体字 + 灰色圆角背景
     &.active {
-        background: @sidebar-hover-bg;  // 灰色圆角背景
-        color: @sidebar-active-color;   // 深灰色近黑 #131212
-        font-weight: @font-weight-bold; // 加粗到 700
+        background: $sidebar-hover-bg;  // 灰色圆角背景
+        color: $sidebar-active-color;   // 深灰色近黑 #131212
+        font-weight: $font-weight-bold; // 加粗到 700
 
         .iconify {
-            color: @sidebar-active-color;  // 图标也变深灰色
-            font-weight: @font-weight-bold;
+            color: $sidebar-active-color;  // 图标也变深灰色
+            font-weight: $font-weight-bold;
         }
     }
 
     // 按下状态
     &:active {
-        transform: translateX(@spacing-1);
+        transform: translateX($spacing-1);
     }
 
     .iconify {
-        font-size: @icon-xl;
+        font-size: $icon-xl;
         flex-shrink: 0;
         transition: all 0.2s ease;
-        color: @sidebar-text-color;  // 默认图标颜色
+        color: $sidebar-text-color;  // 默认图标颜色
     }
 }
 
 .promo-section {
-    margin-top: @spacing-12;
-    padding: @spacing-4;
+    margin-top: $spacing-12;
+    padding: $spacing-4;
     border-radius: @radius-2xl;
-    background: linear-gradient(135deg, @primary-color 0%, @primary-dark 100%);
+    background: linear-gradient(135deg, $primary-color 0%, $primary-dark 100%);
     color: #FFFFFF;
     position: relative;
     overflow: hidden;
@@ -148,25 +148,25 @@
     }
 
     .promo-label {
-        font-size: @font-sm;
+        font-size: $font-sm;
         opacity: 0.8;
-        margin-bottom: @spacing-2;
+        margin-bottom: $spacing-2;
     }
 
     .promo-title {
-        font-size: @font-lg;
-        font-weight: @font-weight-bold;
-        margin-bottom: @spacing-3;
-        line-height: @line-height-tight;
+        font-size: $font-lg;
+        font-weight: $font-weight-bold;
+        margin-bottom: $spacing-3;
+        line-height: $line-height-tight;
     }
 
     .promo-btn {
         font-size: 10px;
-        font-weight: @font-weight-bold;
-        padding: @spacing-1 @spacing-3;
-        border-radius: @radius-full;
-        background: @bg-secondary;
-        color: @primary-color;
+        font-weight: $font-weight-bold;
+        padding: $spacing-1 $spacing-3;
+        border-radius: $radius-full;
+        background: $bg-secondary;
+        color: $primary-color;
         border: none;
         cursor: pointer;
         transition: all 0.2s ease;
@@ -178,8 +178,8 @@
 
     .promo-icon {
         position: absolute;
-        right: -@spacing-4;
-        bottom: -@spacing-4;
+        right: -$spacing-4;
+        bottom: -$spacing-4;
         font-size: 72px;
         opacity: 0.1;
         color: #FFFFFF;
@@ -188,8 +188,8 @@
 
 @media (max-width: 1024px) {
     .sidebar {
-        width: @sidebar-width-md;  // 使用全局变量 200px
-        padding: @spacing-4;
+        width: $sidebar-width-md;  // 使用全局变量 200px
+        padding: $spacing-4;
     }
 }
 

+ 0 - 399
jk-rag-platform/src/pages/layout/style.less.bak

@@ -1,399 +0,0 @@
-.header {
-    width: 100%;
-    height: 64px;
-    padding: 0 24px;
-    background: #FFFFFF;
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    border-bottom: 1px solid #E5E7EB;  // 使用更明显的边框
-    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
-    position: fixed;
-    top: 0;
-    left: 0;
-    z-index: 1000;
-    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
-
-    // 用户区域样式
-    &-user-section {
-        display: flex;
-        align-items: center;
-        gap: 8px;
-    }
-
-
-
-    // 登出按钮样式
-    &-logout-btn {
-        color: #666;
-        border: none;
-        padding: 4px 8px;
-        border-radius: 6px;
-        transition: all 0.3s ease;
-        
-        &:hover {
-            color: #ff4d4f;
-            background: rgba(255, 77, 79, 0.1);
-        }
-        
-        &:active {
-            color: #cf1322;
-            background: rgba(255, 77, 79, 0.2);
-        }
-    }
-
-    // 用户操作区域样式优化
-    &-operation {
-        display: flex;
-        align-items: center;
-        cursor: pointer;
-        padding: 4px 8px; // 增加点击区域
-        border-radius: 6px;
-        transition: background-color 0.3s ease;
-
-        &-picture {
-            width: 30px;
-            height: 30px;
-            background: @primary-color;
-            border-radius: 50%;
-            display: flex;
-            justify-content: center;
-            align-items: center;
-            margin-right: 8px;
-            color: #FFFFFF;
-        }
-
-        &-name {
-            font-weight: 500;
-            color: @primary-color;
-        }
-    }
-
-    // &-nav {
-    //     display: flex;
-    //     align-items: center;
-    //     gap: 40px;
-    //     margin-left: 40px;
-
-    //     &-item {
-    //         color: #666;
-    //         font-weight: 500;
-    //         position: relative;
-    //         transition: color 0.3s;
-
-    //         &:hover {
-    //             color: @primary-color;
-    //         }
-
-    //         &.active {
-    //             color: @primary-color;
-
-    //             &::after {
-    //                 content: '';
-    //                 position: absolute;
-    //                 bottom: -22px;
-    //                 left: 0;
-    //                 right: 0;
-    //                 height: 2px;
-    //                 background: @primary-color;
-    //             }
-    //         }
-    //     }
-    // }
-
-    &-logo {
-        display: flex;
-        align-items: center;
-        cursor: pointer;
-        margin-right: 0px;
-        width: 216px;
-        &-picture {
-            width: 100px;
-            height: 44px;  // 优化到 44px,更紧凑
-            margin-right: 10px;
-        }
-
-        &-text {
-            font-size: 18px;
-            font-weight: 500;
-            // color: #000000;
-            margin-right: 24px;
-        }
-    }
-
-    &-platform-selector {
-        width: 200px;
-        margin-left: 20px;
-        
-        // 基础样式
-        .ant-select-selector {
-            border: 1px solid #e8e8e8;
-            border-radius: 6px;
-            background: #fafafa;
-            
-            &:hover {
-                border-color: #1890ff;
-                background: #fff;
-            }
-        }
-    }
-}
-
-// 响应式处理
-@media (max-width: 1200px) {
-    .header {
-        
-        // 优化logo区域在中等屏幕下的显示
-        &-logo {
-            margin-right: 20px;
-            
-            &-text {
-                font-size: 16px; // 稍微减小字体
-                margin-right: 16px; // 减少右边距
-            }
-        }
-        
-        // 优化Select组件在中等屏幕下的显示
-        .ant-select {
-            width: 160px !important; // 减小Select宽度
-        }
-    }
-}
-
-@media (max-width: 768px) {
-    .header {
-        padding: 0 16px;
-        
-        &-logo {
-            margin-right: 12px;
-            
-            &-text {
-                font-size: 14px; // 进一步减小字体
-                margin-right: 12px; // 进一步减少右边距
-                white-space: nowrap; // 防止换行
-                overflow: hidden; // 隐藏溢出内容
-                text-overflow: ellipsis; // 显示省略号
-                max-width: 120px; // 限制最大宽度
-            }
-        }
-        
-        // 优化Select组件在移动端的显示
-        .ant-select {
-            width: 120px !important; // 进一步减小Select宽度
-            margin-left: 8px !important; // 减少左边距
-        }
-        
-        &-operation {
-            padding: 2px 4px;
-            
-            &-name {
-                display: none; // 在移动端隐藏用户名
-            }
-        }
-        
-        &-logout-btn {
-            padding: 2px 4px;
-        }
-    }
-}
-
-// 超小屏幕优化
-@media (max-width: 480px) {
-    .header {
-        padding: 0 12px;
-        
-        &-logo {
-            margin-right: 8px;
-            
-            &-text {
-                font-size: 12px; // 最小字体
-                margin-right: 8px;
-                max-width: 80px; // 进一步限制宽度
-            }
-            
-            &-picture {
-                width: 24px; // 减小logo尺寸
-                height: 24px;
-                margin-right: 6px;
-            }
-        }
-        
-        // 超小屏幕下的Select组件优化
-        .ant-select {
-            width: 80px !important; // 最小宽度
-            margin-left: 4px !important; // 最小边距
-        }
-    }
-}
-
-.nav {
-    width: 200px !important;
-    height: calc(100vh - 64px) !important;
-    background: @sidebar-bg !important;
-    border-right: 1px solid #f0f0f0;
-    position: fixed;
-    top: 64px;
-    left: 0;
-    z-index: 999;
-    .ant-menu-title-content{
-        // color: @header-text-color !important;
-    }
-    .ant-menu {
-        background-color: transparent !important;
-        overflow-x: hidden;
-        overflow-y: auto;
-        .ant-menu-submenu-title{
-            // color: @header-text-color;
-        }
-        &-item {
-            transition: font-weight 0.2s ease;
-            //  color: @header-text-color;
-            &-selected, &-active {
-                font-weight: 600 !important;
-                // color: @primary-color !important;
-            }
-        }
-    }
-
-    // 注释掉旧的 nav-menu 样式,现在使用 Sidebar 组件
-    // &-menu {
-    //     width: 100%;
-    //     height: calc(100vh - 100px);
-    //     overflow-x: hidden;
-    //     overflow-y: auto;
-    // }
-
-    &-bottom {
-        width: 100%;
-        height: 44px;  // 优化到 44px,更紧凑
-        display: flex;
-        justify-content: center;
-        align-items: center;
-    }
-}
-
-
-.main-content {
-    padding-bottom: 0;
-    display: flex;
-    flex-direction: column;
-    height: 100%; /* 减去Header(64px)和面包屑(50px) */
-    background: #fff;
-    .breadcrumb-container {
-        width: 100%;
-        height: 44px;  // 优化到 44px,更紧凑
-        padding: 12px 24px;  // 优化到 12px 24px
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-        border-bottom: 1px solid #E5E7EB;  // 使用更明显的边框
-        background: #fff;
-        position: sticky;
-        top: 0;
-        z-index: 999;
-    }
-
-    .breadcrumb {
-        flex: 1;
-        font-weight: 600;
-        .ant-breadcrumb-link {
-            a {
-                &:hover {
-                    color: @primary-color !important;
-                    font-weight: 600;
-                }
-            }
-        }
-    }
-
-    .breadcrumb-actions {
-        display: flex;
-        align-items: center;
-        gap: 8px;
-    }
-
-    /* 内容区域滚动 */
-    .content-scroll-area {
-        flex: 1;
-        overflow-y: auto;
-        background: #fff;
-        position: relative;
-    }
-}
-
-.model-selector-modal {
-    .ant-modal-body {
-        padding: 20px;
-    }
-}
-
-.route-option {
-    border-radius: 8px;
-    overflow: hidden;
-    transition: all 0.3s;
-    height: 480px;
-    display: flex;
-    flex-direction: column;
-
-    .ant-card-cover {
-        display: flex;
-        justify-content: center;
-        padding: 60px 0;
-        background: #f0f5ff;
-        flex: 0 0 auto;
-    }
-
-    .ant-card-body {
-        display: flex;
-        flex-direction: column;
-        height: 100%;
-        padding-bottom: 0;
-    }
-
-    .logo-container {
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        width: 100%;
-    }
-
-    .option-icon {
-        font-size: 64px;
-        color: #1890ff;
-    }
-
-    .ant-card-meta {
-        text-align: center;
-        margin-bottom: 0;
-        flex: 1;
-        display: flex;
-        flex-direction: column;
-        justify-content: center;
-
-        &-title {
-            font-size: 18px;
-            font-weight: 500;
-            margin-bottom: 8px;
-        }
-
-        &-description {
-            color: #666;
-            padding: 0 16px;
-        }
-    }
-
-    .select-button {
-        margin: 24px 0;
-        height: 48px;
-        font-weight: 500;
-        font-size: 16px;
-        flex: 0 0 auto;
-    }
-
-    &:hover {
-        transform: translateY(-5px);
-        box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08);
-    }
-}
-
-

+ 34 - 34
jk-rag-platform/src/pages/layout/style.less → jk-rag-platform/src/pages/layout/style.scss

@@ -1,22 +1,22 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // ===== 应用布局 =====
 .app-layout {
     minHeight: 100vh;
     display: flex;
     flex-direction: column;
-    background: @bg-primary;
+    background: $bg-primary;
 }
 
 // ===== 主体内容容器 =====
 .main-container {
     display: flex;
     flex: 1;
-    margin-top: @header-height;
-    margin-left: @sidebar-width;
-    background: @bg-primary;
-    height: calc(100vh - @header-height);
+    margin-top: $header-height;
+    margin-left: $sidebar-width;
+    background: $bg-primary;
+    height: calc(100vh - $header-height);
     overflow: hidden;
     transition: margin-left 0.2s ease;  // 添加过渡动画
 }
@@ -24,7 +24,7 @@
 // 响应式调整
 @media (max-width: 1024px) {
     .main-container {
-        margin-left: @sidebar-width-md;  // 中等屏幕调整
+        margin-left: $sidebar-width-md;  // 中等屏幕调整
     }
 }
 
@@ -34,7 +34,7 @@
         
         // 当 Sidebar 展开时(collapsed=false)
         &.sidebar-open {
-            margin-left: @sidebar-width;
+            margin-left: $sidebar-width;
         }
     }
 }
@@ -57,26 +57,26 @@
     .breadcrumb-container {
         width: 100%;
         height: 44px;
-        padding: @spacing-3 @spacing-6;
+        padding: $spacing-3 $spacing-6;
         display: flex;
         justify-content: space-between;
         align-items: center;
-        border-bottom: 1px solid @border-base;
-        background: @bg-secondary;
+        border-bottom: 1px solid $border-base;
+        background: $bg-secondary;
         position: sticky;
         top: 0;
-        z-index: @z-index-sticky;
+        z-index: $z-index-sticky;
     }
 
     .breadcrumb {
         flex: 1;
-        font-weight: @font-weight-medium;
+        font-weight: $font-weight-medium;
 
         .ant-breadcrumb-link {
             a {
                 &:hover {
-                    color: @primary-color !important;
-                    font-weight: @font-weight-medium;
+                    color: $primary-color !important;
+                    font-weight: $font-weight-medium;
                 }
             }
         }
@@ -85,14 +85,14 @@
     .breadcrumb-actions {
         display: flex;
         align-items: center;
-        gap: @spacing-2;
+        gap: $spacing-2;
     }
 
     /* 内容区域滚动 - 隐藏式滚动条 */
     .content-scroll-area {
         flex: 1;
         overflow-y: auto;
-        background: @bg-secondary;
+        background: $bg-secondary;
         position: relative;
         
         // 隐藏式滚动条
@@ -107,13 +107,13 @@
 
 .nav {
     width: 200px !important;
-    height: calc(100vh - @header-height) !important;
-    background: @sidebar-bg !important;
-    border-right: 1px solid @border-base;
+    height: calc(100vh - $header-height) !important;
+    background: $sidebar-bg !important;
+    border-right: 1px solid $border-base;
     position: fixed;
-    top: @header-height;
+    top: $header-height;
     left: 0;
-    z-index: @z-index-fixed;
+    z-index: $z-index-fixed;
 
     .ant-menu {
         background-color: transparent !important;
@@ -140,12 +140,12 @@
 
 .model-selector-modal {
     .ant-modal-body {
-        padding: @spacing-5;
+        padding: $spacing-5;
     }
 }
 
 .route-option {
-    border-radius: @radius-lg;
+    border-radius: $radius-lg;
     overflow: hidden;
     transition: all 0.3s;
     height: 480px;
@@ -176,7 +176,7 @@
 
     .option-icon {
         font-size: 64px;
-        color: @primary-color;
+        color: $primary-color;
     }
 
     .ant-card-meta {
@@ -188,27 +188,27 @@
         justify-content: center;
 
         &-title {
-            font-size: @font-lg;
-            font-weight: @font-weight-medium;
-            margin-bottom: @spacing-2;
+            font-size: $font-lg;
+            font-weight: $font-weight-medium;
+            margin-bottom: $spacing-2;
         }
 
         &-description {
-            color: @text-secondary;
-            padding: 0 @spacing-4;
+            color: $text-secondary;
+            padding: 0 $spacing-4;
         }
     }
 
     .select-button {
-        margin: @spacing-6 0;
+        margin: $spacing-6 0;
         height: 48px;
-        font-weight: @font-weight-medium;
-        font-size: @font-lg;
+        font-weight: $font-weight-medium;
+        font-size: $font-lg;
         flex: 0 0 auto;
     }
 
     &:hover {
         transform: translateY(-5px);
-        box-shadow: @shadow-lg;
+        box-shadow: $shadow-lg;
     }
 }

+ 0 - 57
jk-rag-platform/src/pages/login/style.less.bak

@@ -1,57 +0,0 @@
-// 导入全局样式变量
-@import '@/styles/variables.less';
-
-.login {
-    width: 100%;
-    height: 100%;
-    display: flex;
-    overflow: hidden;
-    .bg-gradient-primary {
-        background: linear-gradient(135deg, @primary-color 0%, @primary-light 100%);
-    }
-    &-left {
-        width: 50%;
-        height: 100%;
-
-        img {
-            width: 100%;
-            height: 100%;
-            object-fit: cover;
-        }
-    }
-
-    &-right {
-        width: 40%;
-        display: flex;
-        flex-direction: column;
-        justify-content: center;
-        align-items: center;
-        position: relative;
-
-        &-content {
-            width: 50%;
-
-            &-title {
-                margin-bottom: @spacing-8;
-
-                &-text {
-                    font-size: @font-2xl;
-                    color: @primary-color;
-                    border-bottom: 2px solid @primary-color;
-                    background: linear-gradient(to bottom, #FFFFFF 50%, #D3DCF6 50%);
-                }
-            }
-        }
-
-        &-copyright {
-            width: 100%;
-            font-size: @font-sm;
-            color: @text-disabled;
-            display: flex;
-            justify-content: center;
-            position: absolute;
-            bottom: 20px;
-            left: 0;
-        }
-    }
-}

+ 9 - 9
jk-rag-platform/src/pages/login/style.less → jk-rag-platform/src/pages/login/style.scss

@@ -1,5 +1,5 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 .login {
     width: 100%;
@@ -7,7 +7,7 @@
     display: flex;
     overflow: hidden;
     .bg-gradient-primary {
-        background: linear-gradient(135deg, @primary-color 0%, @primary-light 100%);
+        background: linear-gradient(135deg, $primary-color 0%, $primary-light 100%);
     }
     &-left {
         width: 50%;
@@ -32,12 +32,12 @@
             width: 50%;
 
             &-title {
-                margin-bottom: @spacing-8;
+                margin-bottom: $spacing-8;
 
                 &-text {
-                    font-size: @font-2xl;
-                    color: @primary-color;
-                    border-bottom: 2px solid @primary-color;
+                    font-size: $font-2xl;
+                    color: $primary-color;
+                    border-bottom: 2px solid $primary-color;
                     background: linear-gradient(to bottom, #FFFFFF 50%, #D3DCF6 50%);
                 }
             }
@@ -45,12 +45,12 @@
 
         &-copyright {
             width: 100%;
-            font-size: @font-sm;
-            color: @text-disabled;
+            font-size: $font-sm;
+            color: $text-disabled;
             display: flex;
             justify-content: center;
             position: absolute;
-            bottom: @spacing-5;
+            bottom: $spacing-5;
             left: 0;
         }
     }

+ 7 - 7
jk-rag-platform/src/pages/otherApps/otherAppList/style.less → jk-rag-platform/src/pages/otherApps/otherAppList/style.scss

@@ -1,20 +1,20 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // 其他应用列表页面样式
 // 说明:使用全局样式类,本文件只保留必要的页面特定样式
 
 .other-app-list {
     // 页面容器 - 使用全局 .page-container 的 padding
-    padding: @spacing-4 @spacing-6;
-    min-height: calc(100vh - @header-height);
-    background: @bg-primary;
+    padding: $spacing-4 $spacing-6;
+    min-height: calc(100vh - $header-height);
+    background: $bg-primary;
 }
 
 // 空状态提示 - 页面特定样式
 .empty-state {
     text-align: center;
-    padding: @spacing-10 @spacing-5;
-    color: @text-hint;
-    font-size: @font-lg;
+    padding: $spacing-10 $spacing-5;
+    color: $text-hint;
+    font-size: $font-lg;
 }

+ 6 - 6
jk-rag-platform/src/pages/otherApps/style.less → jk-rag-platform/src/pages/otherApps/style.scss

@@ -1,5 +1,5 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // 其他应用列表页面样式
 // 说明:
@@ -10,20 +10,20 @@
 // 本文件仅保留页面特定的响应式样式
 
 .app-plaza-list {
-    padding: @spacing-4 @spacing-6;  // 16px/24px - 紧凑间距
-    min-height: calc(100vh - @header-height);
-    background: @bg-primary;
+    padding: $spacing-4 $spacing-6;  // 16px/24px - 紧凑间距
+    min-height: calc(100vh - $header-height);
+    background: $bg-primary;
 }
 
 // 响应式适配
 @media (max-width: 1024px) {
     .app-plaza-list {
-        padding: @spacing-4 @spacing-6;
+        padding: $spacing-4 $spacing-6;
     }
 }
 
 @media (max-width: 768px) {
     .app-plaza-list {
-        padding: @spacing-4;
+        padding: $spacing-4;
     }
 }

+ 57 - 57
jk-rag-platform/src/pages/questionAnswer/form/DrawerForm.less → jk-rag-platform/src/pages/questionAnswer/form/DrawerForm.scss

@@ -1,17 +1,17 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // ===== Drawer 表单样式 =====
 
 .rag-drawer {
     .ant-drawer-header {
-        padding: @spacing-4 @spacing-6;
-        border-bottom: 1px solid @border-light;
+        padding: $spacing-4 $spacing-6;
+        border-bottom: 1px solid $border-light;
         
         .ant-drawer-title {
-            font-size: @font-2xl;
-            font-weight: @font-weight-bold;
-            color: @text-primary;
+            font-size: $font-2xl;
+            font-weight: $font-weight-bold;
+            color: $text-primary;
         }
     }
 
@@ -26,7 +26,7 @@
 
 .drawer-form-container {
     height: 100%;
-    padding: @spacing-5;
+    padding: $spacing-5;
     overflow-y: auto;
 
     // 表单区域
@@ -38,11 +38,11 @@
     .icon-select-section {
         display: flex;
         align-items: center;
-        gap: @spacing-6;
-        padding: @spacing-5;
-        background: @bg-tertiary;
-        border-radius: @radius-xl;
-        margin-bottom: @spacing-4;  // 16px - 紧凑间距
+        gap: $spacing-6;
+        padding: $spacing-5;
+        background: $bg-tertiary;
+        border-radius: $radius-xl;
+        margin-bottom: $spacing-4;  // 16px - 紧凑间距
 
         .icon-preview-wrapper {
             flex-shrink: 0;
@@ -50,16 +50,16 @@
             .icon-preview-box {
                 width: 80px;
                 height: 80px;
-                border-radius: @radius-xl;
+                border-radius: $radius-xl;
                 display: flex;
                 align-items: center;
                 justify-content: center;
-                border: 1px solid @border-base;
+                border: 1px solid $border-base;
                 transition: all 0.3s ease;
 
                 .icon-preview-placeholder {
-                    color: @text-hint;
-                    font-size: @font-sm;
+                    color: $text-hint;
+                    font-size: $font-sm;
                 }
             }
         }
@@ -68,46 +68,46 @@
             flex: 1;
             display: flex;
             flex-direction: column;
-            gap: @spacing-3;
+            gap: $spacing-3;
 
             .color-label {
-                font-size: @font-sm;
-                color: @text-secondary;
+                font-size: $font-sm;
+                color: $text-secondary;
             }
         }
     }
 
     // 分割线
     .section-divider {
-        margin: @spacing-4 0 @spacing-4 0;
+        margin: $spacing-4 0 $spacing-4 0;
         
         &::before {
             content: '';
             display: inline-block;
             width: 4px;
             height: 16px;
-            background: @primary-color;
+            background: $primary-color;
             border-radius: 2px;
-            margin-right: @spacing-2;
+            margin-right: $spacing-2;
             vertical-align: middle;
         }
 
         span {
-            font-size: @font-lg;
-            font-weight: @font-weight-semibold;
-            color: @text-primary;
+            font-size: $font-lg;
+            font-weight: $font-weight-semibold;
+            color: $text-primary;
         }
     }
 
     // 标签信息区域
     .tags-info {
-        border: 1px solid @border-base;
-        border-radius: @radius-md;
-        padding: @spacing-2 @spacing-3;
+        border: 1px solid $border-base;
+        border-radius: $radius-md;
+        padding: $spacing-2 $spacing-3;
         display: flex;
         align-items: center;
         justify-content: space-between;
-        gap: @spacing-3;
+        gap: $spacing-3;
         min-height: 46px;
         background: #fff;
 
@@ -115,7 +115,7 @@
             flex: 1;
             display: flex;
             flex-wrap: wrap;
-            gap: @spacing-1;
+            gap: $spacing-1;
             min-width: 0;
 
             .ant-tag {
@@ -125,36 +125,36 @@
 
         .clear-all {
             cursor: pointer;
-            color: @text-secondary;
-            font-size: @icon-lg;
+            color: $text-secondary;
+            font-size: $icon-lg;
             transition: color 0.2s ease;
 
             &:hover {
-                color: @error-color;
+                color: $error-color;
             }
         }
     }
 
     // 预设问题区域
     .preset-questions {
-        margin-top: @spacing-4;
+        margin-top: $spacing-4;
 
         .questions-list {
             display: flex;
             flex-direction: column;
-            gap: @spacing-3;
+            gap: $spacing-3;
         }
 
         .question-item {
             display: flex;
             align-items: center;
-            gap: @spacing-3;
+            gap: $spacing-3;
 
             label {
                 min-width: 70px;
-                color: @text-secondary;
-                font-size: @font-md;
-                font-weight: @font-weight-medium;
+                color: $text-secondary;
+                font-size: $font-md;
+                font-weight: $font-weight-medium;
             }
 
             .question-input {
@@ -163,7 +163,7 @@
 
             .question-actions {
                 display: flex;
-                gap: @spacing-2;
+                gap: $spacing-2;
                 flex-shrink: 0;
 
                 .question-icon {
@@ -172,19 +172,19 @@
                     transition: all 0.2s ease;
 
                     &.add {
-                        color: @success-color;
+                        color: $success-color;
 
                         &:hover {
-                            color: darken(@success-color, 10%);
+                            color: darken($success-color, 10%);
                             transform: scale(1.1);
                         }
                     }
 
                     &.del {
-                        color: @error-color;
+                        color: $error-color;
 
                         &:hover {
-                            color: darken(@error-color, 10%);
+                            color: darken($error-color, 10%);
                             transform: scale(1.1);
                         }
                     }
@@ -198,20 +198,20 @@
 .drawer-form-container {
     .ant-form {
         .ant-form-item {
-            margin-bottom: @spacing-4;  // 16px - 紧凑间距
+            margin-bottom: $spacing-4;  // 16px - 紧凑间距
 
             .ant-form-item-label {
                 > label {
-                    font-size: @font-base;
-                    font-weight: @font-weight-medium;
-                    color: @text-primary;
+                    font-size: $font-base;
+                    font-weight: $font-weight-medium;
+                    color: $text-primary;
                 }
             }
 
             .ant-form-item-extra {
-                font-size: @font-base;
-                color: @text-hint;
-                margin-top: @spacing-1;
+                font-size: $font-base;
+                color: $text-hint;
+                margin-top: $spacing-1;
             }
 
             .ant-input,
@@ -224,14 +224,14 @@
             .ant-input-affix-wrapper,
             .ant-select-selector,
             .ant-input-number-input-wrap {
-                border-radius: @radius-md;
+                border-radius: $radius-md;
                 transition: all 0.2s ease;
             }
 
             .ant-input:focus,
             .ant-input-affix-wrapper:focus-within {
-                border-color: @primary-color;
-                box-shadow: 0 0 0 2px fade(@primary-color, 10%);
+                border-color: $primary-color;
+                box-shadow: 0 0 0 2px fade($primary-color, 10%);
             }
         }
     }
@@ -267,14 +267,14 @@
         text-align: center;
         height: 40px;
         line-height: 38px;
-        border-radius: @radius-md;
+        border-radius: $radius-md;
         
         &:first-child {
-            border-radius: @radius-md 0 0 @radius-md;
+            border-radius: $radius-md 0 0 $radius-md;
         }
         
         &:last-child {
-            border-radius: 0 @radius-md @radius-md 0;
+            border-radius: 0 $radius-md $radius-md 0;
         }
         
         &::before {

+ 0 - 331
jk-rag-platform/src/pages/questionAnswer/form/Step1Basic.backup.tsx

@@ -1,331 +0,0 @@
-import * as React from 'react';
-import { Form, Input, Select, Cascader, Tag, InputNumber, ColorPicker, Button, Space, message } from 'antd';
-import { PlusCircleOutlined, MinusCircleOutlined, CloseCircleOutlined, LinkOutlined } from '@ant-design/icons';
-import * as AllIcons from '@ant-design/icons';
-import IconPicker from './IconPicker';
-import VipSelector from './VipSelector';
-import './style.less';
-
-interface Step1BasicProps {
-    form: any;
-    appTypeList: any[];
-    appVisibleList: any[];
-    appProjectList: any[];
-    isAppPro: boolean;
-    visibleFlag: string | number;
-    vipList: any[];
-    userInfo: any;
-    onAppChange: (typeId: number) => void;
-    onVisibleChange: (value: any) => void;
-    onRemoveVip: (userId: string) => void;
-    onVipConfirm: (users: any[]) => void;
-    onNext: () => void;
-    onBack: () => void;
-}
-
-interface InputItem {
-    id: number;
-    value: string;
-}
-
-const Step1Basic: React.FC<Step1BasicProps> = (props) => {
-    const {
-        form,
-        appTypeList,
-        appVisibleList,
-        appProjectList,
-        isAppPro,
-        visibleFlag,
-        vipList,
-        userInfo,
-        onAppChange,
-        onVisibleChange,
-        onAddVip,
-        onRemoveVip,
-        onNext,
-        onBack,
-    } = props;
-
-    const [inputs, setInputs] = React.useState<InputItem[]>([{ id: 1, value: '' }]);
-    const [iconPickerVisible, setIconPickerVisible] = React.useState(false);
-    const [vipSelectorVisible, setVipSelectorVisible] = React.useState(false);
-    const [selectedIcon, setSelectedIcon] = React.useState<string | null>(null);
-    const [previewBg, setPreviewBg] = React.useState<string>('#1D4ED8');
-
-    const getContrastColor = (hex: string) => {
-        const c = hex.replace('#', '');
-        const r = parseInt(c.substring(0, 2), 16);
-        const g = parseInt(c.substring(2, 4), 16);
-        const b = parseInt(c.substring(4, 6), 16);
-        const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
-        return luminance > 0.6 ? '#000' : '#fff';
-    };
-
-    const presetColors = ['#1677ff', '#52c41a', '#fa8c16', '#f5222d', '#722ed1', '#ffffff', '#f0f0f0'];
-    const presetItems = [{ label: '', colors: presetColors }];
-
-    const addInput = () => {
-        const newId = inputs.length + 1;
-        setInputs([...inputs, { id: newId, value: '' }]);
-    };
-
-    const delInput = (id: number) => {
-        if (inputs.length <= 1) {
-            message.warning("至少保留 1 个预设问题");
-            return;
-        }
-        setInputs(inputs.filter(input => input.id !== id));
-    };
-
-    const handleChange = (id: number, value: string) => {
-        setInputs(inputs.map(input => (input.id === id ? { ...input, value } : input)));
-        form.setFieldValue('questionList', inputs.map(input => input.value));
-    };
-
-    const handleNext = () => {
-        form.validateFields(['name', 'desc', 'appProId', 'iconType']).then((values) => {
-            form.setFieldValue('questionList', inputs.map(input => input.value));
-            onNext();
-        }).catch((error) => {
-            console.error(error);
-        });
-    };
-
-    return (
-        <div className='create-step1'>
-            <Form.Item
-                label='请选择应用图标'
-                tooltip='用于在应用广场展示'
-                name='iconType'
-                rules={[{ required: true, message: '请选择图标' }]}
-            >
-                <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
-                    <div style={{ width: 84, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
-                        <div style={{ 
-                            width: 64, 
-                            height: 64, 
-                            borderRadius: 8, 
-                            display: 'flex', 
-                            alignItems: 'center', 
-                            justifyContent: 'center', 
-                            background: previewBg, 
-                            border: '1px solid #e8e8e8' 
-                        }}>
-                            {selectedIcon ? (() => { 
-                                const C = (AllIcons as any)[selectedIcon]; 
-                                const iconColor = getContrastColor(previewBg); 
-                                return C ? <C style={{ fontSize: 28, color: iconColor }} /> : <span style={{ fontSize: 12 }}>{selectedIcon}</span>;
-                            })() : <span style={{ color: '#999', fontSize: 12 }}>预览</span>}
-                        </div>
-                    </div>
-                    <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
-                        <a onClick={() => setIconPickerVisible(true)} style={{ fontSize: 13, color: '#1677ff', cursor: 'pointer' }}>
-                            选择图标
-                        </a>
-                        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
-                            <div style={{ fontSize: 12, color: '#666' }}>背景色:</div>
-                            <ColorPicker 
-                                presets={presetItems} 
-                                value={previewBg}
-                                onChange={(color) => {
-                                    const hex = color.toHexString?.() || color?.toString?.() || previewBg;
-                                    setPreviewBg(hex);
-                                    form.setFieldValue('iconColor', hex);
-                                }} 
-                            />
-                        </div>
-                    </div>
-                </div>
-            </Form.Item>
-
-            <Form.Item
-                label='问答应用名称'
-                tooltip='尽量概括应用的主要功能'
-                name='name'
-                rules={[{ required: true, message: '问答应用名称不能为空' }]}
-            >
-                <Input placeholder="请输入问答应用名称" className='form-input' />
-            </Form.Item>
-
-            <Form.Item
-                label='应用类型'
-                tooltip='应用的实际分类'
-                name='typeId'
-            >
-                <Select
-                    className='form-input'
-                    placeholder='请选择问答应用类型'
-                    onChange={onAppChange}
-                    allowClear
-                >
-                    {appTypeList.map((item) => (
-                        <Select.Option key={item.value} value={item.value}>
-                            {item.label}
-                        </Select.Option>
-                    ))}
-                </Select>
-            </Form.Item>
-
-            {isAppPro && (
-                <Form.Item
-                    label='项目'
-                    tooltip='应用所属项目'
-                    name='appProId'
-                    rules={[{ required: true, message: '项目不能为空' }]}
-                >
-                    <Cascader
-                        options={appProjectList}
-                        placeholder="请选择项目"
-                        showSearch
-                        className='form-input'
-                    />
-                </Form.Item>
-            )}
-
-            <Form.Item
-                label='是否公开'
-                tooltip='公开应用后,所有用户均可使用该应用,私有应用仅限自己和指定用户使用'
-                name='visible'
-            >
-                <Select
-                    className='form-input'
-                    placeholder='请选择是否公开'
-                    allowClear
-                    onChange={(value) => {
-                        onVisibleChange(value);
-                    }}
-                >
-                    {appVisibleList.map((item) => (
-                        <Select.Option key={item.value} value={item.value}>
-                            {item.label}
-                        </Select.Option>
-                    ))}
-                </Select>
-            </Form.Item>
-
-            {userInfo?.tenantId === '000000' && (visibleFlag === '0' || visibleFlag === 0) && (
-                <Form.Item
-                    label='集团公开'
-                    tooltip='集团下所有用户均可使用该应用'
-                    name='groupVisible'
-                    layout='horizontal'
-                    valuePropName='checked'
-                >
-                    <Select />
-                </Form.Item>
-            )}
-
-            <Form.Item
-                label='显示顺序'
-                name='sort'
-                tooltip='用于应用广场的显示顺序'
-            >
-                <InputNumber placeholder="请输入显示顺序" className='form-input' style={{ height: '36px' }} />
-            </Form.Item>
-
-            {(visibleFlag === '1' || visibleFlag === 1) && (
-                <Form.Item
-                    label='指定用户'
-                    tooltip='私有应用的指定用户'
-                >
-                    <div className='tags-info'>
-                        <div className='tags-list'>
-                            {vipList.map((item: any) => (
-                                <Tag
-                                    key={item.userId}
-                                    color="blue"
-                                    closable
-                                    onClose={(e) => {
-                                        e?.preventDefault();
-                                        onRemoveVip(item.userId);
-                                    }}
-                                >
-                                    {item.userName}
-                                </Tag>
-                            ))}
-                        </div>
-                        <Space>
-                            {vipList.length > 0 && (
-                                <CloseCircleOutlined
-                                    className='cup'
-                                    onClick={() => onRemoveVip('all')}
-                                />
-                            )}
-                            <Button type="primary" variant="outlined" onClick={() => setVipSelectorVisible(true)}>
-                                选择
-                            </Button>
-                        </Space>
-                    </div>
-                </Form.Item>
-            )}
-
-            {/* IconPicker 弹窗 */}
-            <IconPicker
-                open={iconPickerVisible}
-                onClose={() => setIconPickerVisible(false)}
-                onSelect={(iconName) => {
-                    setSelectedIcon(iconName);
-                    form.setFieldValue('iconType', iconName);
-                }}
-                value={selectedIcon}
-            />
-
-            {/* VIP 用户选择弹窗 */}
-            <VipSelector
-                open={vipSelectorVisible}
-                onClose={() => setVipSelectorVisible(false)}
-                onConfirm={(users) => {
-                    props.onVipConfirm(users);
-                    setVipSelectorVisible(false);
-                }}
-                existingUsers={vipList}
-            />
-
-            <Form.Item
-                label='问答应用描述'
-                tooltip='对当前应用功能的描述使用户更了解应用的使用范围'
-                name='desc'
-                rules={[{ required: true, message: '问答应用描述不能为空' }]}
-            >
-                <Input.TextArea
-                    showCount
-                    maxLength={500}
-                    placeholder="请输入当前应用的描述"
-                    className='form-textarea'
-                />
-            </Form.Item>
-
-            <div className='preset-questions'>
-                <h4>添加引导问题</h4>
-                <div>
-                    {inputs.map(input => (
-                        <div key={input.id} className='question-item'>
-                            <label>引导问题 {input.id}</label>
-                            <Input
-                                className='question-input'
-                                type="text"
-                                value={input.value}
-                                onChange={e => handleChange(input.id, e.target.value)}
-                            />
-                            <div className='question-actions'>
-                                <PlusCircleOutlined className='question-icon' onClick={addInput} />
-                                <MinusCircleOutlined className='question-icon' onClick={() => delInput(input.id)} />
-                            </div>
-                        </div>
-                    ))}
-                </div>
-            </div>
-
-            <div className='step-actions'>
-                <Button onClick={onBack}>
-                    返回
-                </Button>
-                <Button type='primary' onClick={handleNext}>
-                    下一步
-                </Button>
-            </div>
-        </div>
-    );
-};
-
-export default Step1Basic;

+ 0 - 157
jk-rag-platform/src/pages/questionAnswer/form/VipSelector.less

@@ -1,157 +0,0 @@
-// 导入全局样式变量
-@import '@/styles/variables.less';
-
-// ===== VIP 用户选择器样式 =====
-
-.vip-selector-modal {
-    .ant-modal-body {
-        padding: 0;
-    }
-}
-
-.vip-selector-container {
-    display: flex;
-    height: 500px;
-    overflow: hidden;
-}
-
-// 左侧:部门树
-.vip-selector-left {
-    width: 240px;
-    border-right: 1px solid @border-light;
-    padding: @spacing-4;
-    display: flex;
-    flex-direction: column;
-    background: @bg-tertiary;
-
-    .section-title {
-        font-size: @font-md;
-        font-weight: @font-weight-semibold;
-        color: @text-primary;
-        margin-bottom: @spacing-3;
-        display: flex;
-        align-items: center;
-        gap: @spacing-2;
-
-        .iconify {
-            font-size: @icon-lg;
-            color: @primary-color;
-        }
-    }
-
-    .ant-tree {
-        flex: 1;
-        overflow-y: auto;
-        background: transparent;
-        font-size: @font-sm;
-
-        .ant-tree-treenode {
-            padding: @spacing-1 0;
-            transition: all 0.2s ease;
-
-            &:hover {
-                background: fade(@primary-color, 5%);
-                border-radius: @radius-sm;
-            }
-        }
-
-        .ant-tree-node-selected {
-            background: fade(@primary-color, 10%) !important;
-            color: @primary-color;
-            font-weight: @font-weight-medium;
-        }
-    }
-
-    .dept-divider {
-        margin: @spacing-4 0;
-    }
-
-    .selected-count {
-        font-size: @font-sm;
-        color: @text-secondary;
-        text-align: center;
-        padding: @spacing-2;
-        background: @bg-secondary;
-        border-radius: @radius-md;
-    }
-}
-
-// 右侧:用户列表
-.vip-selector-right {
-    flex: 1;
-    padding: @spacing-4;
-    display: flex;
-    flex-direction: column;
-    overflow: hidden;
-
-    .section-title {
-        font-size: @font-md;
-        font-weight: @font-weight-semibold;
-        color: @text-primary;
-        margin-bottom: @spacing-3;
-        display: flex;
-        align-items: center;
-        gap: @spacing-2;
-
-        .iconify {
-            font-size: @icon-lg;
-            color: @primary-color;
-        }
-
-        .dept-filter-tag {
-            margin-left: @spacing-2;
-            padding: 2px 8px;
-            background: fade(@primary-color, 10%);
-            color: @primary-color;
-            border-radius: @radius-full;
-            font-size: @font-xs;
-            font-weight: @font-weight-medium;
-        }
-    }
-
-    .filter-bar {
-        margin-bottom: @spacing-3;
-        padding: @spacing-3;
-        background: @bg-tertiary;
-        border-radius: @radius-lg;
-    }
-
-    .ant-table {
-        flex: 1;
-        overflow-y: auto;
-        font-size: @font-sm;
-
-        .ant-table-thead > tr > th {
-            background: @bg-tertiary;
-            font-weight: @font-weight-semibold;
-            color: @text-primary;
-        }
-
-        .ant-table-tbody > tr:hover {
-            background: fade(@primary-color, 5%);
-        }
-
-        .ant-table-selection-column {
-            width: 50px;
-        }
-    }
-}
-
-// 响应式适配
-@media (max-width: @screen-md) {
-    .vip-selector-container {
-        flex-direction: column;
-        height: 600px;
-    }
-
-    .vip-selector-left {
-        width: 100%;
-        border-right: none;
-        border-bottom: 1px solid @border-light;
-        max-height: 200px;
-    }
-
-    .vip-selector-right {
-        max-height: 400px;
-    }
-}

+ 157 - 0
jk-rag-platform/src/pages/questionAnswer/form/VipSelector.scss

@@ -0,0 +1,157 @@
+// 导入全局样式变量
+@import '@/styles/variables.scss';';
+
+// ===== VIP 用户选择器样式 =====
+
+.vip-selector-modal {
+    .ant-modal-body {
+        padding: 0;
+    }
+}
+
+.vip-selector-container {
+    display: flex;
+    height: 500px;
+    overflow: hidden;
+}
+
+// 左侧:部门树
+.vip-selector-left {
+    width: 240px;
+    border-right: 1px solid $border-light;
+    padding: $spacing-4;
+    display: flex;
+    flex-direction: column;
+    background: $bg-tertiary;
+
+    .section-title {
+        font-size: $font-md;
+        font-weight: $font-weight-semibold;
+        color: $text-primary;
+        margin-bottom: $spacing-3;
+        display: flex;
+        align-items: center;
+        gap: $spacing-2;
+
+        .iconify {
+            font-size: $icon-lg;
+            color: $primary-color;
+        }
+    }
+
+    .ant-tree {
+        flex: 1;
+        overflow-y: auto;
+        background: transparent;
+        font-size: $font-sm;
+
+        .ant-tree-treenode {
+            padding: $spacing-1 0;
+            transition: all 0.2s ease;
+
+            &:hover {
+                background: fade($primary-color, 5%);
+                border-radius: @radius-sm;
+            }
+        }
+
+        .ant-tree-node-selected {
+            background: fade($primary-color, 10%) !important;
+            color: $primary-color;
+            font-weight: $font-weight-medium;
+        }
+    }
+
+    .dept-divider {
+        margin: $spacing-4 0;
+    }
+
+    .selected-count {
+        font-size: $font-sm;
+        color: $text-secondary;
+        text-align: center;
+        padding: $spacing-2;
+        background: $bg-secondary;
+        border-radius: $radius-md;
+    }
+}
+
+// 右侧:用户列表
+.vip-selector-right {
+    flex: 1;
+    padding: $spacing-4;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+
+    .section-title {
+        font-size: $font-md;
+        font-weight: $font-weight-semibold;
+        color: $text-primary;
+        margin-bottom: $spacing-3;
+        display: flex;
+        align-items: center;
+        gap: $spacing-2;
+
+        .iconify {
+            font-size: $icon-lg;
+            color: $primary-color;
+        }
+
+        .dept-filter-tag {
+            margin-left: $spacing-2;
+            padding: 2px 8px;
+            background: fade($primary-color, 10%);
+            color: $primary-color;
+            border-radius: $radius-full;
+            font-size: $font-xs;
+            font-weight: $font-weight-medium;
+        }
+    }
+
+    .filter-bar {
+        margin-bottom: $spacing-3;
+        padding: $spacing-3;
+        background: $bg-tertiary;
+        border-radius: $radius-lg;
+    }
+
+    .ant-table {
+        flex: 1;
+        overflow-y: auto;
+        font-size: $font-sm;
+
+        .ant-table-thead > tr > th {
+            background: $bg-tertiary;
+            font-weight: $font-weight-semibold;
+            color: $text-primary;
+        }
+
+        .ant-table-tbody > tr:hover {
+            background: fade($primary-color, 5%);
+        }
+
+        .ant-table-selection-column {
+            width: 50px;
+        }
+    }
+}
+
+// 响应式适配
+@media (max-width: $screen-md) {
+    .vip-selector-container {
+        flex-direction: column;
+        height: 600px;
+    }
+
+    .vip-selector-left {
+        width: 100%;
+        border-right: none;
+        border-bottom: 1px solid $border-light;
+        max-height: 200px;
+    }
+
+    .vip-selector-right {
+        max-height: 400px;
+    }
+}

+ 52 - 52
jk-rag-platform/src/pages/questionAnswer/form/style.less → jk-rag-platform/src/pages/questionAnswer/form/style.scss

@@ -1,5 +1,5 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // ===== 创建/编辑表单组件样式 =====
 // 注意:
@@ -28,54 +28,54 @@
         width: 100%;
         max-width: 646px;
         min-height: 46px;
-        border-radius: @radius-md;
+        border-radius: $radius-md;
         display: flex;
         align-items: center;
         justify-content: space-between;
         flex-wrap: wrap;
-        padding: @spacing-1 @spacing-2;
+        padding: $spacing-1 $spacing-2;
 
         .tags-list {
             width: 80%;
             display: flex;
             flex-wrap: wrap;
-            gap: @spacing-1;
+            gap: $spacing-1;
         }
 
         .cup {
             cursor: pointer;
-            margin-right: @spacing-3;
-            color: @text-secondary;
+            margin-right: $spacing-3;
+            color: $text-secondary;
 
             &:hover {
-                color: @error-color;
+                color: $error-color;
             }
         }
     }
 
     .preset-questions {
-        margin: @spacing-6 0;
-        padding: @spacing-5;
-        background: @bg-tertiary;
-        border-radius: @radius-lg;
+        margin: $spacing-6 0;
+        padding: $spacing-5;
+        background: $bg-tertiary;
+        border-radius: $radius-lg;
 
         h4 {
-            margin-bottom: @spacing-4;
-            color: @text-primary;
-            font-size: @font-lg;
-            font-weight: @font-weight-medium;
+            margin-bottom: $spacing-4;
+            color: $text-primary;
+            font-size: $font-lg;
+            font-weight: $font-weight-medium;
         }
 
         .question-item {
             display: flex;
             align-items: center;
-            margin-bottom: @spacing-3;
-            gap: @spacing-3;
+            margin-bottom: $spacing-3;
+            gap: $spacing-3;
 
             label {
                 min-width: 80px;
-                color: @text-secondary;
-                font-size: @font-md;
+                color: $text-secondary;
+                font-size: $font-md;
             }
 
             .question-input {
@@ -85,16 +85,16 @@
 
             .question-actions {
                 display: flex;
-                gap: @spacing-2;
+                gap: $spacing-2;
 
                 .question-icon {
                     font-size: 18px;
-                    color: @primary-color;
+                    color: $primary-color;
                     cursor: pointer;
                     transition: all 0.2s ease;
 
                     &:hover {
-                        color: @primary-color-hover;
+                        color: $primary-color-hover;
                         transform: scale(1.1);
                     }
                 }
@@ -104,10 +104,10 @@
 
     .step-actions {
         display: flex;
-        gap: @spacing-3;
-        margin-top: @spacing-6;
-        padding-top: @spacing-6;
-        border-top: 1px solid @border-light;
+        gap: $spacing-3;
+        margin-top: $spacing-6;
+        padding-top: $spacing-6;
+        border-top: 1px solid $border-light;
     }
 }
 
@@ -122,55 +122,55 @@
         display: flex;
         justify-content: space-between;
         align-items: center;
-        margin-bottom: @spacing-4;
-        padding-bottom: @spacing-4;
-        border-bottom: 1px solid @border-light;
+        margin-bottom: $spacing-4;
+        padding-bottom: $spacing-4;
+        border-bottom: 1px solid $border-light;
 
         .btn-back {
-            background: @bg-tertiary;
-            border: 1px solid @border-base;
-            color: @text-secondary;
+            background: $bg-tertiary;
+            border: 1px solid $border-base;
+            color: $text-secondary;
 
             &:hover {
-                background: @border-base;
-                border-color: @primary-color;
-                color: @primary-color;
+                background: $border-base;
+                border-color: $primary-color;
+                color: $primary-color;
             }
         }
     }
 
     .app-splitter {
         flex: 1;
-        border: 1px solid @border-light;
-        border-radius: @radius-lg;
+        border: 1px solid $border-light;
+        border-radius: $radius-lg;
         min-height: calc(100vh - 300px);
 
         .splitter-panel {
-            padding: @spacing-5;
+            padding: $spacing-5;
             overflow-y: auto;
 
             &.param-config {
-                background: @bg-tertiary;
+                background: $bg-tertiary;
             }
 
             .config-header {
                 display: flex;
                 justify-content: space-between;
                 align-items: center;
-                margin-bottom: @spacing-4;
-                font-size: @font-lg;
-                font-weight: @font-weight-medium;
-                color: @text-primary;
+                margin-bottom: $spacing-4;
+                font-size: $font-lg;
+                font-weight: $font-weight-medium;
+                color: $text-primary;
             }
 
             .section-title {
-                font-size: @font-lg;
-                font-weight: @font-weight-medium;
-                color: @text-primary;
-                margin-bottom: @spacing-4;
+                font-size: $font-lg;
+                font-weight: $font-weight-medium;
+                color: $text-primary;
+                margin-bottom: $spacing-4;
                 display: flex;
                 align-items: center;
-                gap: @spacing-2;
+                gap: $spacing-2;
             }
         }
     }
@@ -182,14 +182,14 @@
         flex-direction: column;
 
         .prompt-info {
-            margin-bottom: @spacing-3;
+            margin-bottom: $spacing-3;
 
             .variable-highlight {
-                color: @primary-color;
-                font-weight: @font-weight-medium;
+                color: $primary-color;
+                font-weight: $font-weight-medium;
                 padding: 2px 6px;
                 border-radius: @radius-sm;
-                background: fade(@primary-color, 10%);
+                background: fade($primary-color, 10%);
                 font-family: @font-family-mono;
             }
         }

+ 3 - 3
jk-rag-platform/src/pages/questionAnswer/info/index.tsx

@@ -1125,7 +1125,7 @@ const [isMultiRound,setIsMultiRound] = React.useState<boolean>(false);// 是否
                                 </div>
                             </div>
 
-                            <div style={{ display: 'flex', gap: '@spacing-3', marginTop: '@spacing-6', paddingTop: '@spacing-6', borderTop: '1px solid @border-light' }}>
+                            <div style={{ display: 'flex', gap: '12px', marginTop: '24px', paddingTop: '24px', borderTop: '1px solid #F3F4F6' }}>
                                 <Button
                                     className='btn-cancel'
                                     onClick={() => {
@@ -1163,7 +1163,7 @@ const [isMultiRound,setIsMultiRound] = React.useState<boolean>(false);// 是否
                                         上一步
                                     </Button>
                                 </div>
-                                <div style={{ display: 'flex', gap: '@spacing-3' }}>
+                                <div style={{ display: 'flex', gap: '12px' }}>
                                     <Button
                                         className='btn-cancel'
                                         onClick={() => {
@@ -1206,7 +1206,7 @@ const [isMultiRound,setIsMultiRound] = React.useState<boolean>(false);// 是否
                                             title="Prompt用于对大模型的回复做出一些列指令和约束。这段Prompt不会被用户看到。"
                                             placement="right"
                                         >
-                                            <InfoCircleOutlined style={{ marginLeft: '@spacing-2', color: '@text-hint', fontSize: '@font-md' }} />
+                                            <InfoCircleOutlined style={{ marginLeft: '8px', color: '#9CA3AF', fontSize: '13px' }} />
                                         </Tooltip>
                                         {/* <Switch checkedChildren="编辑" unCheckedChildren="只读" style={{marginLeft:'5px'}} value={editPrompt} onChange={(e) => {
                                             setEditPrompt(e)

+ 20 - 20
jk-rag-platform/src/pages/questionAnswer/info/style.less → jk-rag-platform/src/pages/questionAnswer/info/style.scss

@@ -1,5 +1,5 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // ===== 创建应用页面样式 =====
 // 注意:
@@ -9,25 +9,25 @@
 
 // ===== 预设问题区域 =====
 .preset-questions {
-    margin: @spacing-6 0;
+    margin: $spacing-6 0;
 
     h4 {
-        margin-bottom: @spacing-4;
-        color: @text-primary;
-        font-size: @font-lg;
-        font-weight: @font-weight-medium;
+        margin-bottom: $spacing-4;
+        color: $text-primary;
+        font-size: $font-lg;
+        font-weight: $font-weight-medium;
     }
 
     .question-item {
         display: flex;
         align-items: center;
-        margin-bottom: @spacing-3;
-        gap: @spacing-3;
+        margin-bottom: $spacing-3;
+        gap: $spacing-3;
 
         label {
             min-width: 80px;
-            color: @text-secondary;
-            font-size: @font-md;
+            color: $text-secondary;
+            font-size: $font-md;
         }
 
         .question-input {
@@ -37,16 +37,16 @@
 
         .question-actions {
             display: flex;
-            gap: @spacing-2;
+            gap: $spacing-2;
 
             .question-icon {
                 font-size: 18px;
-                color: @primary-color;
+                color: $primary-color;
                 cursor: pointer;
                 transition: all 0.2s ease;
 
                 &:hover {
-                    color: @primary-color-hover;
+                    color: $primary-color-hover;
                     transform: scale(1.1);
                 }
             }
@@ -56,13 +56,13 @@
 
 // ===== Splitter 布局 =====
 .app-splitter {
-    border: 1px solid @border-light;
-    border-radius: @radius-lg;
+    border: 1px solid $border-light;
+    border-radius: $radius-lg;
     height: calc(100vh - 200px);
     min-height: 400px;
 
     .splitter-panel {
-        padding: @spacing-5;
+        padding: $spacing-5;
         overflow-y: auto;
     }
 }
@@ -75,14 +75,14 @@
     flex-direction: column;
 
     .prompt-info {
-        margin-bottom: @spacing-3;
+        margin-bottom: $spacing-3;
 
         .variable-highlight {
-            color: @primary-color;
-            font-weight: @font-weight-medium;
+            color: $primary-color;
+            font-weight: $font-weight-medium;
             padding: 2px 6px;
             border-radius: @radius-sm;
-            background: fade(@primary-color, 10%);
+            background: fade($primary-color, 10%);
             font-family: @font-family-mono;
         }
     }

+ 0 - 679
jk-rag-platform/src/pages/questionAnswer/list/index.tsx.bak

@@ -1,679 +0,0 @@
-import * as React from 'react';
-import {
-  List,
-  Button,
-  Divider,
-  Flex,
-  Layout,
-  Empty,
-  Image,
-  Modal,
-  Tag,
-  message,
-  Tooltip,
-  Select,
-  Form,
-  Space,
-  Row,
-  Col,
-  Input, Drawer
-} from 'antd';
-import {
-  SearchOutlined,
-  ReloadOutlined,
-  BulbOutlined,
-  CloseOutlined,
-  HeartOutlined,
-  SnippetsOutlined,
-  CopyOutlined,
-  ScissorOutlined,
-  HeartFilled
-} from '@ant-design/icons';
-import * as AllIcons from '@ant-design/icons';
-
-import { apis } from '@/apis';
-import './style.less';
-import { PaginationConfig } from 'antd/es/pagination';
-import LocalStorage from '@/LocalStorage';
-import IconSvg from "@/assets/public/icon.svg";
-import dayjs from 'dayjs';
-import Step from '@/components/step';
-import { getContrastColor } from '@/utils';
-
-import { useNavigate } from 'react-router-dom'
-import PreviewModal from '../../audit/components/PreviewModal';
-
-
-const FormItem = Form.Item;
-
-
-const QuestionAnswerList: React.FC = () => {
-  const [form] = Form.useForm();
-  const navigate = useNavigate();
-
-  interface Item {
-    name: string,
-    desc: string,
-    appId: number,
-    createBy: string,
-    typeId: string;
-    status: string;
-    comment: string;
-    auditStatus: string;
-    projectName: string;
-    updateTime: string;
-  };
-
-  interface PageInfo {
-    pageNum: number,
-    pageSize: number,
-    total: number,
-  };
-
-  type AppTypeList = {
-    label: string,
-    value: string,
-  }[];
-
-  type ProjectTypeList = {
-    label: string,
-    value: string,
-  }[];
-
-  const [listLoading, setListLoading] = React.useState(false);
-  const [list, setList] = React.useState<Item[]>([]);
-  const [page, setPage] = React.useState<PageInfo>({
-    pageNum: 1,
-    pageSize: 10,
-    total: 0,
-  });
-  const [knowCount, setKnowCount] = React.useState<string>();
-  const { Header, Footer, Sider, Content } = Layout;
-  const [appTypeList, setAppTypeList] = React.useState<AppTypeList>([]);
-  const [appProjectList, setAppProjectList] = React.useState<AppTypeList>([]);
-  const [showSubPanel, setShowSubPanel] = React.useState(false);
-  const [selectedType, setSelectedType] = React.useState<string | null>('全部');
-  const wrapperRef = React.useRef<HTMLDivElement>(null);
-  const selectRef = React.useRef<any>(null);
-  const [levelTypeList, setLevelTypeList] = React.useState<AppTypeList>([]);
-  const [name, setName] = React.useState<string>('');
-  const appApi = {
-    fetchList: async (typeId: any, projectId: any) => {
-      setListLoading(true);
-      try {
-        const res = await apis.fetchTakaiMyAppListApi({
-          pageSize: page.pageSize,
-          pageNum: page.pageNum,
-          name: name,
-        })
-        const list = res.rows.map((item: any) => {
-          return {
-            name: item.name,
-            desc: item.desc,
-            appId: item.appId,
-            createBy: item.createBy,
-            typeId: item.typeId,
-            status: item.status,
-            comment: item.comment,
-            auditStatus: item.auditStatus,
-            projectName: item.projectName,
-            updateTime: item.updateTime,
-            iconType: item.iconType,
-            iconColor: item.iconColor,
-            knowledgeSize: item.knowledgeSize,
-            documentSize: item.documentSize,
-            sliceSize: item.sliceSize,
-            userName: item.userName,
-            isCollect: item.isCollect,
-          }
-        });
-        setList(list);
-        setPage({
-          pageNum: page.pageNum,
-          pageSize: page.pageSize,
-          total: res.total,
-        });
-      } catch (error) {
-        console.error(error);
-      } finally {
-        setListLoading(false);
-      }
-    },
-
-    auditApplication: async (appId: string, userId: string) => {
-      const res = await apis.auditTakaiApplicationLibApi(appId, userId);
-      if (res.data === 9) {
-        message.error('您没有添加审核人');
-      }
-      await appApi.fetchList(null, null);
-    }
-  };
-
-  // 立即使用应用
-  const useNowAppLication = (appId: number) => {
-    const userInfo = LocalStorage.getUserInfo();
-    const getToken = LocalStorage.getToken();
-    // const baseUrl = import.meta.env.VITE_JUMP_URL;
-    const baseUrl = 'https://llm.jkec.info:11432/#/knowledgeChat?showMenu=false&chatMode=LOCAL'
-    window.open(`${baseUrl}&appId=${appId}&userId=${userInfo?.id}&nickName=${userInfo?.name}&token=${getToken}`, '_blank');
-  }
-
-  // 删除应用
-  const delApplication = async (appId: string) => {
-    try {
-      await apis.deleteTakaiApplicationApi(appId);
-      await appApi.fetchList(null, null);
-    } catch (error) {
-      console.error(error);
-    }
-  }
-
-  // 获取应用类型
-  const appTypeApi = {
-    fetchAppType: async () => {
-      try {
-        const res = await apis.fetchTakaiAppTypeList('app_type');
-        const list = res.data.map((item: any) => {
-          return {
-            label: item.dictLabel,
-            value: item.dictCode,
-          }
-        });
-        setAppTypeList(list);
-      } catch (error: any) {
-        console.error(error);
-      }
-    },
-  };
-
-  // 项目级应用下的类型
-  const appProTypeApi = {
-    fetchAppProType: async () => {
-      try {
-        const res = await apis.fetchTakaiAppTypeList('projectTree');
-        // const list: AppTypeList = res.data;
-        const list: AppTypeList = res.data?.reduce((acc: any, item: any) => {
-          if (item.children.length > 0) {
-            item.children.forEach((val: any) => {
-              acc.push({
-                label: val.label,
-                value: `${val.value}`,
-              })
-            })
-          }
-          return acc;
-        }, []);
-        setAppProjectList(list);
-      } catch (error: any) {
-        console.error(error);
-      }
-    },
-  };
-
-  // 获取应用类型
-  const levelTypeApi = {
-    fetchLevelAppType: async () => {
-      try {
-        const res = await apis.fetchTakaiAppTypeList('project_type');
-        const list = res.data.map((item: any) => {
-          return {
-            label: item.dictLabel,
-            value: item.dictCode,
-          }
-        });
-        setLevelTypeList(list);
-      } catch (error: any) {
-        console.error(error);
-      }
-    },
-  };
-
-  const init = async () => {
-    await appApi.fetchList(null, null);
-    await appTypeApi.fetchAppType();
-    await appProTypeApi.fetchAppProType();
-    await levelTypeApi.fetchLevelAppType();
-
-    // 设置默认选择"全部"
-    form.setFieldsValue({ typeId: '全部' });
-  }
-  const [evaluationTool, setEvaluationTool] = React.useState(false); // 评测工具 appCenter:questionAnswer:evaluationTool
-  const [deleteFlag, setDeleteFlag] = React.useState(false); // 删除
-  const [updateFlag, setUpdateFlag] = React.useState(false); // 编辑
-  const [userInfoAll, setUserInfoAll] = React.useState<any>({});
-
-  const selectOptions = React.useMemo(() => {
-    const baseOptions: any[] = [
-      { value: '1', label: '查看' },
-      { value: '3', label: <Tooltip title="API服务" ><span>API服务</span></Tooltip> },
-      // { value: '4', label: '编辑' },
-      // { value: '6', label: '删除' },
-    ];
-    if (evaluationTool) {
-      baseOptions.splice(1, 0, { value: '2', label: <Tooltip title="评测工具" ><span>评测工具</span></Tooltip> });
-    }
-    if (updateFlag) {
-      baseOptions.splice(3, 0, { value: '4', label: '编辑' });
-    }
-    if (deleteFlag) {
-      baseOptions.splice(5, 0, { value: '6', label: '删除' });
-    }
-    return baseOptions;
-  }, [evaluationTool, updateFlag, deleteFlag]);
-  React.useEffect(() => {
-    setEvaluationTool(LocalStorage.getStatusFlag('appCenter:questionAnswer:evaluationTool'));
-    setUpdateFlag(LocalStorage.getStatusFlag('appCenter:questionAnswer:edit'));
-    setDeleteFlag(LocalStorage.getStatusFlag('appCenter:questionAnswer:del'));
-    setUserInfoAll(LocalStorage.getUserInfo());
-    init();
-  }, [page.pageSize, page.pageNum])
-
-  const paginationConfig: PaginationConfig = {
-    // 显示数据总量
-    showTotal: (total: number) => {
-      return `共 ${total} 条`;
-    },
-    // 展示分页条数切换
-    showSizeChanger: true,
-    // 指定每页显示条数
-    // pageSizeOptions: ['2', '20', '50', '100'],
-    // 快速跳转至某页
-    showQuickJumper: true,
-    current: page.pageNum,
-    pageSize: page.pageSize,
-    total: page.total,
-    onChange: (pageNum, pageSize) => {
-      setPage({
-        pageNum: pageNum,
-        pageSize: pageSize,
-        total: page.total,
-      });
-    },
-  };
-
-  // 点击查询
-  const handleClickSearch = async () => {
-    form.validateFields().then(async (values) => {
-      if (values.proTypeId) {
-        values.typeId = values.proTypeId;
-      }
-      if (values.typeId === '全部') {
-        values.typeId = null;
-      }
-      if (values.projectId instanceof Array && values.projectId.length == 2) {
-        values.projectId = values.projectId[1];
-      }
-      await appApi.fetchList(values.typeId, values.projectId);
-    }).catch((error) => {
-      console.error(error);
-    });
-  };
-  const [drawerFlag, setDrawerFlag] = React.useState<boolean>(false);
-  const [drawerData, setDrawerData] = React.useState<any>({});
-  // 点击跳转
-  const handleChange = async (e: string, item: any) => {
-    if (e === '1') {
-      // setDrawerFlag(true)
-      // setDrawerData(item)
-      const userInfo = LocalStorage.getUserInfo();
-      const getToken = LocalStorage.getToken();
-      const baseUrl = import.meta.env.VITE_JUMP_URL;
-      // const baseUrl = 'https://llm.jkec.info:11432/#/knowledgeChat?showMenu=false&chatMode=LOCAL'
-      window.open(`${baseUrl}&appId=${item.appId}&userId=${userInfo?.id}&nickName=${userInfo?.name}&token=${getToken}`, '_blank');
-    }
-    if (e === '2') {
-      // evaluationTool/evaluationTask
-      navigate({ pathname: '/evaluationTool/evaluationTask' }, {});
-    }
-    if (e === '4') {
-      navigate({ pathname: '/appCenter/questionAnswer/modify' }, { state: { id: item.appId } });
-    }
-    if (e === '6') {
-      Modal.confirm({
-        title: '删除',
-        content: `确定删除应用: ` + item.name + ` 吗?`,
-        okType: 'danger',
-        onOk: async () => {
-          await delApplication(item.appId.toString());
-        }
-      });
-    }
-  };
-
-  /** 点击外部关闭面板 */
-  React.useEffect(() => {
-    const handleClickOutside = (event: MouseEvent) => {
-      if (wrapperRef.current && !wrapperRef.current.contains(event.target as Node)) {
-        setShowSubPanel(false);
-      }
-    };
-
-    document.addEventListener('mousedown', handleClickOutside, true);
-    return () => {
-      document.removeEventListener('mousedown', handleClickOutside, true);
-    };
-  }, []);
-
-
-  const handleAppTypeChange = (value: string) => {
-    if (value === '41') {
-      // 如果是项目级应用,切换面板状态
-      // setShowSubPanel(prev => !prev);
-      setShowSubPanel(true);
-    } else {
-      // 其他选项,隐藏面板
-      setShowSubPanel(false);
-    }
-    setSelectedType(value);
-    form.setFieldsValue({ typeId: value });
-
-    // 自动提交逻辑
-    if (value === '全部') {
-      // 全部选项,传递null给后端
-      appApi.fetchList(null, null);
-    } else {
-      // 其他选项,传递对应的typeId
-      appApi.fetchList(value, null);
-    }
-  };
-
-  const handleAppProTypeChange = (value: string) => {
-    console.log(value, 'valuevalue');
-
-    setSelectedType(value);
-    form.setFieldsValue({ typeId: value });
-  };
-  const [showGuide, setShowGuide] = React.useState<boolean>(() => localStorage.getItem('questionAnswer') !== 'true');
-  const hideGuide = () => { localStorage.setItem('questionAnswer', 'true'); setShowGuide(false); };
-  const steps = [
-    {
-      title: '创建RAG应用',
-      description: '按特定场景/领域管理知识库,当前仅创建空的知识库集合。',
-      number: 1,
-    },
-    {
-      title: '参数配置',
-      description: '选择知识库、调试Prompt、配置参数;',
-      number: 2,
-    },
-    {
-      title: '发布应用,支持API调用',
-      description: '成功发布应用后,支持API形式调用,管理员也会挑选推荐至应用广场',
-      number: 3,
-    }
-  ]
-  return (
-    <div>
-      <div style={{ padding: '16px 20px' }}>
-
-        {showGuide && <> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
-          <div style={{ fontSize: 13, color: '#333', display: 'flex', alignItems: 'center', gap: 6 }}>
-            <BulbOutlined style={{ color: '#faad14' }} />
-            提示: 如何创建并管理自己的应用?
-          </div>
-          <CloseOutlined onClick={hideGuide} style={{ color: '#999', cursor: 'pointer' }} />
-        </div>
-          <Step steps={steps} className='mb-3' /></>}
-        <Form
-          form={form}
-          layout='inline'
-          colon={false}
-          style={{ flex: 1, display: 'flex', alignItems: 'center' }}
-        >
-          <p>我创建的RAG应用:</p>
-          <Space.Compact style={{ width: '300px' }}>
-            <Input
-              placeholder='请输入应用名称进行搜索'
-              value={name}
-              onChange={(e) => { setName(e.target.value) }}
-            />
-            <Button type="primary" onClick={() => { init() }}><SearchOutlined /></Button>
-          </Space.Compact>
-        </Form>
-      </div>
-      {
-        // list.length
-        //   ?
-        <div className='questionAnswerList'>
-          <div className='applicationList'>
-            <List grid={{
-              gutter: 16,
-              xs: 1,
-              sm: 1,
-              md: 2,
-              lg: 2,
-              xl: 3,
-              xxl: 4, // 展示的列数
-            }}
-              loading={listLoading}
-              dataSource={list}
-              renderItem={(item: any) => (
-                <List.Item>
-                  <div className='card group relative overflow-visible' tabIndex={0}>
-                    <div style={{
-                      display: 'flex',
-                      justifyContent: 'space-between',
-                      alignItems: 'center',
-                      overflow: 'hidden',
-                    }}>
-                      <div style={{ display: 'flex', alignItems: 'center', overflow: 'auto' }}>
-                        <div style={{ marginRight: 10, overflow: 'auto', background: item.iconColor, width: 50, height: 50, borderRadius: 8, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
-                          {item.iconType ? (() => {
-                            const C = (AllIcons as any)[item.iconType];
-                            const iconColor = getContrastColor(item.iconColor);
-                            return C ? <C style={{ fontSize: 28, color: iconColor }} /> : <span style={{ fontSize: 12 }}>{item.iconType}</span>
-                          })()
-                            : <Image
-                              width={32}
-                              height={32}
-                              src={IconSvg}
-                              preview={false}
-                            />}
-                        </div>
-                        {/* <div style={ { overflow: 'auto' } }> */}
-                        {/*  { item.name }*/}
-                        {/*</div>*/}
-
-                        <div style={{
-                          display: 'flex',
-                          flexDirection: 'column',
-                          justifyContent: 'center',
-                          overflow: 'hidden',
-                          maxWidth: '85%',
-                          height: '100%'
-                        }}>
-                          <Tooltip title={item.name} placement="top">
-                            <div style={{
-                              lineHeight: '18px',
-                              fontSize: 14,
-                              fontWeight: 500,
-                              overflow: 'hidden',
-                              textOverflow: 'ellipsis',
-                              whiteSpace: 'nowrap',
-                              // maxWidth: '200px', // 可以根据需要调整宽度
-                              cursor: 'pointer'
-                            }}>
-                              <span>
-                                {item.name.length > 20 ? `${item.name.substring(0, 30)}...` : item.name}
-                              </span>
-                            </div>
-                          </Tooltip>
-                          <Space size={4} style={{ lineHeight: '18px' }}>
-                            <span style={{
-                              color: '#999',
-                              fontSize: 12,
-                              margin: 0
-                            }}>创建者:{item.userName}</span>
-                            <Divider type="vertical" style={{ color: '999', margin: 0, height: 12 }} />
-                            <span style={{ color: '#999', fontSize: 12 }}>
-                              {
-                                item.projectName && item.projectName.length > 8 ?
-                                  `${item.projectName.substring(0, 8)}...` :
-                                  item.projectName
-                              }
-                            </span>
-                          </Space>
-                        </div>
-
-                      </div>
-                      <div>
-                        <>
-                          {/*{ item.projectName } 移动599 line*/}
-                          <div className='w-full flex flex-col items-end justify-end'>
-                            <div className='chak'>
-                              {item.isCollect && <HeartFilled style={{ color: '#F5E663' }} className='mr-2' onClick={async () => {
-                                if (item.isCollect) {
-                                  const res: any = await apis.delCollectApi(item.appId);
-                                  if (res.code === 200) {
-                                    message.success('取消收藏成功')
-                                    await appApi.fetchList(null, null);
-                                  }
-                                }
-                              }} />}
-                              {!item.isCollect && <HeartOutlined onClick={async () => {
-                                if (!item.isCollect) {
-                                  const res: any = await apis.setCollectApi(item.appId);
-                                  if (res.code === 200) {
-                                    message.success('收藏成功')
-                                    await appApi.fetchList(null, null);
-                                  }
-                                }
-                              }} className='mr-2' />}
-                              <Select
-                                defaultValue="1"
-                                style={{ width: 70 }}
-                                onSelect={(e) => { handleChange(e, item) }}
-                                size='small'
-                                options={(() => {
-                                  const opts = [...selectOptions];
-                                  if (userInfoAll && String(userInfoAll.id) === String(item.createBy)) {
-                                    if (!opts.some((o: any) => String(o.value) === '4')) {
-                                      opts.push({ value: '4', label: '编辑' });
-                                    }
-                                    if (!opts.some((o: any) => String(o.value) === '6')) {
-                                      opts.push({ value: '6', label: '删除' });
-                                    }
-                                  }
-                                  return opts;
-                                })()}
-                              /></div>
-                          </div>
-                        </>
-                      </div>
-                    </div>
-                    <p style={{ margin: '8px 0' }}></p>
-                    {/* <Divider plain style={{ margin: '16px 0' }}></Divider> */}
-                    <div style={{
-                      display: 'flex',
-                      justifyContent: 'space-between',
-                      alignItems: 'center'
-                    }}>
-                      <span style={{
-                        color: '#999',
-                        fontSize: 12,
-                        margin: 0
-                      }}>更新时间: {dayjs(item.updateTime).format("YYYY-MM-DD HH:mm:ss")}</span>
-                      <Tag
-                        style={{
-                          // padding: '4px 8px',
-                          marginRight: 0,
-                          fontSize: 12,
-                          fontWeight: 600,
-                          background: '#f8f8f8',
-                          // border: '1px solid #d9d9d9'
-                        }}
-                      >
-                        {
-                          appTypeList
-                            .find(item1 => item1.value.toString() === item.typeId)?.label || levelTypeList.find(item2 => item2.value.toString() === item.typeId)?.label || '未分类'
-                        }
-                      </Tag>
-                    </div>
-                    <div className='desc'>
-                      {
-                        item.desc !== '' && item.desc !== null && item.desc.length > 40 ? item.desc.substring(0, 40) + '......' : item.desc
-                      }
-                    </div>
-                    <div className='flex justify-between'>
-                      <div className='' style={{ display: 'flex', width: '100%', alignItems: 'center', justifyContent: 'space-between', gap: 16, background: '#fafafa', padding: '8px 12px', borderRadius: 8 }}>
-                        <div style={{ color: '#595959', fontSize: 13 }}>
-                          API调用:<a style={{ color: '#1677ff' }} href="#">接口文档</a>
-                        </div>
-                        <div>
-                          {/* < Tag style={{
-                            // marginLeft: 16,
-                            backgroundColor: item.status === '1' ? '#D26900' : item.status === '2' ? '#408080' : item.auditStatus === '4' ? '#CE0000' : item.status === '5' ? '#5151A2' : ''
-                          }}>
-                            审核通过
-                          </Tag> */}
-                          {
-                            (item.status !== null && item.status !== '3') &&
-                            < Tag style={{
-                              // marginLeft: 16,
-                              backgroundColor: item.status === '1' ? '#D26900' : item.status === '2' ? '#408080' : item.auditStatus === '4' ? '#CE0000' : item.status === '5' ? '#5151A2' : '',
-                              color: item.status === '1' ? '#000000' : item.status === '2' ? '#ffffff' : item.auditStatus === '4' ? '#000000' : item.status === '5' ? '#000000' : ''
-                            }}>
-                              {item.status === '1' ? '待审核' : item.status === '2' ? '审核中' : item.auditStatus === '4' ? '审核拒绝' : item.status === '5' ? '待提交' : '未知'}
-                            </Tag>
-                          }
-                          {
-                            (item.auditStatus === '4') &&
-                            <Tooltip title={item.comment}>
-                              {
-                                item.comment !== '' && item.comment !== null && item.comment.length > 10 ?
-                                  item.comment.substring(0, 10) + '......' :
-                                  item.comment
-                              }
-                            </Tooltip>
-                          }
-                        </div>
-                        <div className='flex items-center'>
-                          <Divider type="vertical" style={{ height: 20, color: '#d9d9d9' }} />
-                          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', fontSize: 12, color: '#595959' }}>
-                            <Tooltip title='知识库数量' placement="top">
-                              <div className='cursor-pointer' style={{ fontWeight: 600, marginTop: 4 }}>
-                                <SnippetsOutlined /> {item.knowledgeSize}
-                              </div>
-                            </Tooltip>
-                          </div>
-                          <Divider type="vertical" style={{ height: 20, color: '#d9d9d9' }} />
-                          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', fontSize: 12, color: '#595959' }}>
-                            {/* <div></div> */}
-                            <Tooltip title='文档数量' placement="top">
-                              <div className='cursor-pointer' style={{ fontWeight: 600, marginTop: 4 }}>
-                                <CopyOutlined /> {item.documentSize}
-                              </div>
-                            </Tooltip>
-                          </div>
-                          <Divider type="vertical" style={{ height: 20, color: '#d9d9d9' }} />
-                          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', fontSize: 12, color: '#595959' }}>
-                            <Tooltip title='切片数量' placement="top">
-                              <div className='cursor-pointer' style={{ fontWeight: 600, marginTop: 4 }}><ScissorOutlined />{item.sliceSize}</div>
-                            </Tooltip>
-
-                          </div>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-                </List.Item>
-              )}
-              pagination={paginationConfig} // 分页
-            />
-          </div>
-        </div>
-      }
-      <Drawer
-        title={drawerData.name}
-        closable={{ 'aria-label': 'Close Button' }}
-        onClose={() => { setDrawerFlag(false) }}
-        width="80%"
-        open={drawerFlag}
-      >
-        {/* {drawerFlag && <PreviewModal isComponent={true} AuditAppId={drawerData.appId} />} */}
-      </Drawer>
-    </div>
-  )
-};
-
-export default observer(QuestionAnswerList);

+ 0 - 221
jk-rag-platform/src/pages/questionAnswer/list/style.less.bak

@@ -1,221 +0,0 @@
-// 导入全局样式变量
-@import '@/styles/variables.less';
-
-.questionAnswerList {
-  width: 100%;
-  height: 100%;
-  background: @bg-secondary;
-  border-radius: @radius-lg;
-  padding: @spacing-4 @spacing-5;
-}
-
-.applicationList {
-  width: 100%;
-  min-height: 600px;
-}
-.chak{
-  .ant-select-selector{
-    border:none !important;
-    box-shadow:none !important;
-  }
-}
-.card{
-  padding: @spacing-4;
-  border: 1px solid @border-color;
-  border-radius: @border-radius-large;
-  min-height: 200px;
-  overflow: auto;
-
-  // 默认状态下操作按钮颜色
-  .action-button {
-    color: #999 !important;
-    transition: color 0.3s ease;
-
-    &:hover {
-      color: @primary-color !important;
-    }
-  }
-
-  .delete-button {
-    color: #999 !important;
-    transition: color 0.3s ease;
-
-    &:hover {
-      color: @error-color !important;
-    }
-  }
-}
-.card:hover{
-  box-shadow: 0 0 8px rgba(0,0,0,.1);
-  border: 1px solid @primary-color ;
-
-  // 悬停状态下操作按钮颜色
-  .action-button {
-    color: @primary-color !important;
-  }
-
-  .delete-button {
-    color: @error-color !important;
-  }
-}
-
-.desc {
-  height: 56px;
-  overflow: hidden;
-  font-weight: 300; /* 细体字 */
-  line-height: 20px;
-  transition: all 0.3s;
-}
-
-.ant-select-selector {
-  border-radius: 4px !important;
-  background: #fff !important;
-  //height: 32px !important;
-}
-
-.ant-select-dropdown {
-  .ant-select-item-option-selected {
-    background-color: @primary-color !important;
-    color: #fff !important;
-  }
-
-  .ant-select-item-option-active {
-    background-color: rgba(0, 123, 255, 0.1);
-  }
-}
-
-
-
-// 按钮组筛选样式
-.filter-button-group {
-  display: flex;
-  gap: 8px;
-  flex-wrap: wrap;
-
-  .ant-btn {
-    border-radius: 16px;
-    height: 32px;
-    padding: 0 16px;
-    font-size: 14px;
-    font-weight: 400;
-    border: 1px solid #d9d9d9;
-    background-color: #fff;
-    color: #000;
-    transition: all 0.3s ease;
-    display: flex;
-    align-items: center;
-
-    &:hover {
-      transform: translateY(-1px);
-      box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);
-      border-color: #40a9ff;
-      color: #40a9ff;
-    }
-
-    // 所有选中状态的按钮都使用统一的蓝色主题
-    &.ant-btn-primary {
-      background-color: #1890ff !important;
-      border-color: #1890ff !important;
-      color: #fff !important;
-      font-weight: 500 !important;
-
-      &:hover {
-        background-color: #40a9ff !important;
-        border-color: #40a9ff !important;
-        color: #fff !important;
-      }
-    }
-
-    // 全部按钮特殊样式 - 未选中时
-    &:first-child:not(.ant-btn-primary) {
-      background-color: #f5f5f5;
-      border-color: #d9d9d9;
-      color: #666;
-
-      &:hover {
-        background-color: #e6f7ff;
-        border-color: #40a9ff;
-        color: #40a9ff;
-      }
-    }
-
-    // 图标样式优化
-    .anticon {
-      font-size: 14px;
-      margin-right: 4px;
-      transition: color 0.3s ease;
-      color: #666; // 默认颜色
-    }
-
-    // 悬停时图标颜色变化
-    &:hover .anticon {
-      color: #40a9ff !important;
-    }
-
-    // 选中状态时图标颜色
-    &.ant-btn-primary .anticon {
-      color: #fff !important;
-    }
-
-    // 选中状态悬停时图标颜色
-    &.ant-btn-primary:hover .anticon {
-      color: #fff !important;
-    }
-
-    // 全部按钮未选中时图标颜色
-    &:first-child:not(.ant-btn-primary) .anticon {
-      color: #666;
-    }
-
-    // 全部按钮悬停时图标颜色
-    &:first-child:not(.ant-btn-primary):hover .anticon {
-      color: #40a9ff;
-    }
-  }
-}
-
-// 响应式处理
-@media (max-width: 768px) {
-  .filter-button-group {
-    gap: 6px;
-
-    .ant-btn {
-      font-size: 12px;
-      padding: 0 12px;
-      height: 28px;
-    }
-  }
-}
-
-@media (max-width: 480px) {
-  .filter-button-group {
-    gap: 4px;
-
-    .ant-btn {
-      font-size: 11px;
-      padding: 0 8px;
-      height: 24px;
-      border-radius: 12px;
-    }
-  }
-}
-
-// 圆形按钮样式优化
-.ant-btn-circle {
-  width: 32px;
-  height: 32px;
-  border-radius: 50%;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  transition: all 0.3s ease;
-
-  &:hover {
-    transform: translateY(-1px);
-    box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);
-  }
-
-  .anticon {
-    font-size: 14px;
-  }
-}

+ 1 - 1
jk-rag-platform/src/pages/questionAnswer/list/style.less → jk-rag-platform/src/pages/questionAnswer/list/style.scss

@@ -1,5 +1,5 @@
 // 导入全局样式变量
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';';
 
 // ===== 我创建的应用列表页面样式 =====
 // 说明:

+ 0 - 0
jk-rag-platform/src/pages/system/apiKey/style.less → jk-rag-platform/src/pages/system/apiKey/style.scss


+ 0 - 188
jk-rag-platform/src/pages/system/audit/components/InfoModal.tsx.bak

@@ -1,188 +0,0 @@
-import * as React from 'react';
-import { Modal, Spin, Form, Input, Select, message, Button, Steps, Switch } from 'antd';
-import { apis, ModifyDocumentApiParams } from '@/apis';
-import LocalStorage from '@/LocalStorage';
-import { set } from 'mobx';
-
-const FormItem = Form.Item;
-const { Option } = Select;
-const { TextArea } = Input;
-
-interface Props {
-    id: string,
-    open: boolean,
-    onClickConfirm: (id: string, userId: string, data: ModifyDocumentApiParams) => Promise<any>,
-    onClickCancel: (id: string, userId: string, data: ModifyDocumentApiParams) => Promise<any>,
-    onClickClose: () => void,
-};
-
-
-
-const InfoModal: React.FC<Props> = (props: Props) => {
-    const {
-        id,
-        open,
-        onClickConfirm,
-        onClickCancel,
-        onClickClose,
-    } = props;
-
-    const [form] = Form.useForm();
-
-    const [loading, setLoading] = React.useState<boolean>(false);
-    const [updateFlag, setUpdateFlag] = React.useState<boolean>();
-    const [createFlag, setCreateFlag] = React.useState<boolean>();
-
-    type EmbeddingList = {
-        label: string,
-        value: string,
-    }[];
-
-    interface AuditInfo {
-        title: string,
-        description: string
-        onClick: () => void,
-    }
-
-    const [embeddingList, setEmbeddingList] = React.useState<EmbeddingList>([]);
-    const description = 'This is a description';
-    const [switchFlag, setSwitchFlag] = React.useState<boolean>(false);
-    const [auditInfo, setAuditInfo] = React.useState<AuditInfo[]>([]);
-    const [current, setCurrent] = React.useState<number>(-1);
-    const [refuse, setRefuse] = React.useState<boolean>(false);
-    const [pass, setPass] = React.useState<boolean>(false);
-
-    const onChange = (checked: boolean) => {
-        if (checked) {
-            setSwitchFlag(true);
-        } else {
-            setSwitchFlag(false);
-        }
-    };
-
-    // 获取知识库详情
-    const fetchDetail = async () => {
-        try {
-            const res = await apis.fetchTakaiApplicationDetail(props.id);
-            if (res.code === 200) {
-                const { name, status, comment, nodeOrder } = res.data.detail;
-                form.setFieldsValue({
-                    name: name,
-                    status: status,
-                    comment: '',
-                    nodeOrder: nodeOrder
-                });
-                if (status === 'Y') {
-                    setPass(true);
-                }
-                if (status === 'N') {
-                    setRefuse(true);
-                }
-            }
-
-        } catch (error: any) {
-            message.error(error.msg);
-        }
-    };
-    const init = async () => {
-        setLoading(true);
-        if (props.id) {
-            await fetchDetail();
-        }
-        setLoading(false);
-    };
-
-    React.useEffect(() => {
-        init();
-        const cFlag = LocalStorage.getStatusFlag('deepseek:knowledge:create');
-        setCreateFlag(cFlag);
-        const uFlag = LocalStorage.getStatusFlag('deepseek:knowledge:update');
-        setUpdateFlag(uFlag);
-    }, []);
-
-    // 点击通过
-    const handleClickConfirm = () => {
-        form.validateFields().then(async (values) => {
-            const userInfo = LocalStorage.getUserInfo();
-            const userId = (userInfo?.id ?? '').toString();
-            const data = {
-                ...values,
-                approver: userId,
-                status: 'Y',
-            };
-            console.log(data, 'data');
-            await onClickConfirm(props.id, userId, data);
-        }).catch((error) => {
-            console.error(error);
-        });
-    };
-
-    // 点击拒绝
-    const handleClickCancel = () => {
-        form.validateFields().then(async (values) => {
-            const userInfo = LocalStorage.getUserInfo();
-            const userId = (userInfo?.id ?? '').toString();
-            const data = {
-                ...values,
-                approver: userId,
-                status: 'N',
-            };
-            await onClickCancel(props.id, userId, data);
-        }).catch((error) => {
-            console.error(error);
-        });
-    };
-
-    return (
-        <Modal
-            width={600}
-            title={'应用审核'}
-            destroyOnClose={true}
-            maskClosable={false}
-            centered={true}
-            open={open}
-            onCancel={onClickClose}
-            // onOk={handleClickConfirm}
-            // onCancel={onClickCancel}
-            footer={[
-                <Button key="submit" type="primary" danger onClick={handleClickCancel} loading={loading}>
-                    拒绝
-                </Button>,
-                <Button key="submit" type="primary" onClick={handleClickConfirm} loading={loading}>
-                    通过
-                </Button>
-            ]}
-        >
-            <Spin spinning={loading}>
-                <Form form={form} layout='vertical'>
-                    <FormItem
-                        label='应用名称'
-                        name='name'
-                    >
-                        <Input disabled={true} />
-                    </FormItem>
-                    <div style={{ width: '100%', height: 0, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
-                        <FormItem
-                            label='审核节点'
-                            name='nodeOrder'
-                            hidden={true}
-                        >
-                            <Input disabled={true} />
-                        </FormItem>
-                    </div>
-                    <FormItem
-                        label='审核意见'
-                        name='comment'
-                    >
-                        <TextArea
-                            maxLength={100}
-                        />
-                    </FormItem>
-                    <div style={{ width: '100%', height: 10 }}></div>
-                </Form>
-            </Spin>
-        </Modal >
-    );
-};
-
-export default InfoModal;

+ 0 - 1602
jk-rag-platform/src/pages/system/audit/components/PreviewModal.tsx.bak

@@ -1,1602 +0,0 @@
-import * as React from 'react';
-import { useLocation, useNavigate } from 'react-router-dom';
-
-import './style.less';
-import {
-    Button, Input, Form, Divider, Splitter, Select, InputNumber, InputNumberProps,
-    Radio, Switch, Row, Col, Slider, Space, RadioChangeEvent,
-    Spin, message, Typography, Tooltip,
-    Cascader,
-    Tag, Modal, Table, TablePaginationConfig, Drawer, ColorPicker
-} from 'antd';
-import type { TableProps } from 'antd';
-
-import { PlusCircleOutlined, MinusCircleOutlined, ArrowLeftOutlined, InfoCircleOutlined, CloseCircleOutlined, LinkOutlined, FileDoneOutlined } from '@ant-design/icons';
-import * as AllIcons from '@ant-design/icons';
-import IconPicker from '../../questionAnswer/info/component/IconPicker';
-import { apis } from '@/apis';
-import router from '@/router';
-import LocalStorage from '@/LocalStorage';
-import Chat from '@/components/chat';
-import store from '../../questionAnswer/info/store';
-import DrawerIndex from '@/pages/knowledgeLib/detail/drawerIndex'
-import { values } from 'mobx';
-
-const { TextArea } = Input;
-const FormItem = Form.Item;
-const { Option } = Select;
-const MAX_COUNT = 5;
-interface QuestionAnswerInfo {
-    open?: boolean;
-    onClose?: () => void;
-    isComponent?: boolean;
-    AuditAppId: string;
-}
-const QuestionAnswerInfo: React.FC<QuestionAnswerInfo> = (props) => {
-    const{AuditAppId} = props;
-    const { state, onChangePagination, onFetchUserListApi } = store;
-    const { page, sourceData } = state;
-    const navigate = useNavigate();
-
-
-    const [form] = Form.useForm();
-    const [iconPickerVisible, setIconPickerVisible] = React.useState(false);
-    const [selectedIcon, setSelectedIcon] = React.useState<string | null>(null);
-    const [previewBg, setPreviewBg] = React.useState<string>('#ffffff');
-
-    const getContrastColor = (hex: string) => {
-        // remove #
-        const c = hex.replace('#', '');
-        const r = parseInt(c.substring(0, 2), 16);
-        const g = parseInt(c.substring(2, 4), 16);
-        const b = parseInt(c.substring(4, 6), 16);
-        // relative luminance
-        const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
-        return luminance > 0.6 ? '#000' : '#fff';
-    }
-
-    const presetColors = ['#1677ff', '#52c41a', '#fa8c16', '#f5222d', '#722ed1', '#ffffff', '#f0f0f0'];
-    const presetItems = [{ label: '', colors: presetColors }];
-
-    // top_p
-    const [topPValue, setTopPValue] = React.useState(0.1);
-    const [topKValue, setTopKValue] = React.useState(1);
-
-
-    const TopPDecimalStep: React.FC = () => {
-
-        const onChange: InputNumberProps['onChange'] = (value) => {
-            if (Number.isNaN(value)) {
-                return;
-            }
-            setTopPValue(value as number);
-        };
-
-        return (
-            <Row>
-                <Col span={12}>
-                    <Slider
-                        min={0}
-                        max={1}
-                        onChange={onChange}
-                        // value={typeof topPValue === 'number' ? topPValue : 0}
-                        value={topPValue}
-                        step={0.1}
-                    />
-                </Col>
-                <Col span={4}>
-                    <InputNumber
-                        min={0}
-                        max={1}
-                        className='form-input-number-small'
-                        step={0.01}
-                        value={topPValue}
-                        onChange={onChange}
-                    />
-                </Col>
-            </Row>
-        );
-    };
-    const [tempValue, setTempValue] = React.useState(0.01);
-    // temperature
-    const TempDecimalStep: React.FC = () => {
-        const onChange: InputNumberProps['onChange'] = (value) => {
-            if (Number.isNaN(value)) {
-                return;
-            }
-            setTempValue(value as number);
-        };
-
-        return (
-            <Row>
-                <Col span={12}>
-                    <Slider
-                        min={0}
-                        max={1}
-                        onChange={onChange}
-                        // value={typeof tempValue === 'number' ? tempValue : 0}
-                        value={tempValue}
-                        step={0.01}
-                    />
-                </Col>
-                <Col span={4}>
-                    <InputNumber
-                        min={0}
-                        max={1}
-                        className='form-input-number-small'
-                        step={0.01}
-                        value={tempValue}
-                        onChange={onChange}
-                    />
-                </Col>
-            </Row>
-        );
-    };
-
-    type ModelList = {
-        label: string,
-        value: string,
-    }[];
-
-    type KnowledgeList = {
-        label: string,
-        value: string,
-        createBy: string,
-    }[];
-
-    type AppTypeList = {
-        label: string,
-        value: string,
-        children: {
-            label: string,
-            value: string,
-        }[],
-    }[];
-    const tagRender = (props: any) => {
-        const { label, value, closable, onClose } = props;
-        return (
-            <Tag
-                color="blue"
-                closable={closable}
-                onClose={onClose} // 保留原有的删除事件
-                style={{ marginRight: 4 }}
-            >
-                {label}
-                {/* 在删除按钮后添加自定义图标 */}
-                <LinkOutlined style={{
-                    marginLeft: 4,
-                    fontSize: 12,
-                    cursor: 'pointer',
-                    color: '#FF9500',
-                    fontWeight: 'bold',
-                }}
-                    onMouseDown={(e) => {
-                        e.stopPropagation();
-                        e.preventDefault();
-                    }}
-                    onMouseUp={(e) => {
-                        e.stopPropagation();
-                        e.preventDefault();
-                    }}
-                    // 自定义图标点击事件
-                    onClick={(e) => {
-                        // 阻止事件冒泡到Tag,避免触发删除
-                        e.stopPropagation();
-                        e.preventDefault();
-                        knowledgeList.forEach((item) => {
-                            if (item.value === value) {
-                                // router.navigate({ pathname: `/deepseek/knowledgeLib/${value}/${item.createBy}`,},);
-                                setDrawerItem(item);
-                                setOpenDrawer(true)
-                                e.stopPropagation();
-                            }
-                        });
-                        // console.log('点击了额外图标,当前选项值:', value,props);
-                        // 这里可以添加你的业务逻辑,如:打开详情、编辑等
-                    }} />
-            </Tag>
-        );
-    };
-
-    const [step, setStep] = React.useState(1);
-    const [pageLoading, setPageLoading] = React.useState(false);
-    const [modelList, setModelList] = React.useState<ModelList>([]);
-    const [knowledgeList, setKnowledgeList] = React.useState<KnowledgeList>([]);
-    const [isVisible, setIsVisible] = React.useState(true);
-    const [isVisibleCus, setIsVisibleCus] = React.useState(false);
-    const [isVisibleSlice, setIsVisibleSlice] = React.useState(false);
-    const [isVisibleRerank, setIsVisibleRerank] = React.useState(true);
-    const [isDeepThinkVisible, setIsDeepThinkVisible] = React.useState(true);
-    const [name, setName] = React.useState('');
-    const [appTypeList, setAppTypeList] = React.useState<AppTypeList>([]);
-    const [appVisibleList, setAppVisibleList] = React.useState<AppTypeList>([]); // 是否公开
-    const [visibleFlag, setVisibleFlag] = React.useState<string | number>(0); // 是否公开用来判断是否展示VIP用户
-    const [updateFlag, setUpdateFlag] = React.useState<boolean>();
-    const [createFlag, setCreateFlag] = React.useState<boolean>();
-    const [appProjectList, setAppProjectList] = React.useState<AppTypeList>([]);
-    const [isAppPro, setIsAppPro] = React.useState<boolean>(false);
-    const [appId, setAppId] = React.useState<string>('');
-    const [fetchUserTypeList, setFetchUserTypeList] = React.useState<AppTypeList>([]); // 用户类型
-    const [userName, setUserName] = React.useState<string>(''); // 用户名
-    const [userNickName, setUserNickName] = React.useState<string>(''); // 用户昵称
-    const [userType, setUserType] = React.useState<string>(''); // 用户类型
-    const [vipList, setVipList] = React.useState<any>([]); // 用户列表
-    const [infoDetail, setinfoDetail] = React.useState<any>(null);// 知识库详情
-    const [parameter,setParameter] = React.useState<any>(null);
-    const style: React.CSSProperties = {
-        display: 'flex',
-        flexDirection: 'column',
-        gap: 8,
-        width: 300,
-    };
-    const userInfo = LocalStorage.getUserInfo();
-    const userId = (userInfo?.id ?? '').toString();
-
-    const location = useLocation();
-
-    const [modeList,setModeList] = React.useState<any>([]); // 模型列表数据
-    const [modeOldList,setModeOldList] = React.useState<any>([]); // 模型列表数据(原始)
-    // 获取调用模型的列表数据
-    const onFetchRerankModelList = async () => {
-        const res = await apis.fetchRerankModelList();
-        if(res && res.data){
-            const list = res.data.map((item: any) => {
-                return {
-                    label: item.model,
-                    value: item.model,
-                }
-            })
-            setModeList(list);
-            setModeOldList(res.data);
-        }
-    }
-
-
-
-    const init = async (id: string) => {
-        await Promise.all([
-            api.fetchKnowlegde(),
-            api.fetchAppType(),
-            // api.fetchModelList(),
-            api.fetchAppProType(),
-            api.fetchAppVisible(id)
-        ])
-        if (id) {
-            await api.fetchDetail(id);
-        }
-        onFetchUserListApi('', '', '');
-        onFetchRerankModelList();
-        await api.fetchUserType();
-    }
-    React.useEffect(() => {
-        const id = AuditAppId;
-        init(id);
-        const uFlag = LocalStorage.getStatusFlag('deepseek:application:update');
-        setUpdateFlag(uFlag);
-        setParameter(LocalStorage.getStatusFlag('appCenter:questionAnswer:parameter'));
-        setEditPrompt(LocalStorage.getStatusFlag('appCenter:questionAnswer:parameter'))
-        setCreateFlag(true);
-    }, []);
-
-    // 定义一个状态来存储输入框数组
-    const [inputs, setInputs] = React.useState([{ id: 1, value: '' }]);
-
-
-
-    // 添加新输入框的函数
-    const addInput = () => {
-        const newId = inputs.length + 1; // 生成新的唯一ID
-        setInputs([...inputs, { id: newId, value: '' }]);
-    };
-
-    // 删除输入框(按id删除+最少数量限制)
-    const delInput = (id: number) => {
-        if (inputs.length <= 1) {
-            message.warning("至少保留1个预设问题");
-            return;
-        }
-        setInputs(inputs.filter(input => input.id !== id));
-    };
-
-    // 处理输入变更的函数
-    const handleChange = (id: number, value: string) => {
-        setInputs(inputs.map(input => (input.id === id ? { ...input, value } : input)));
-    };
-
-    const handleAppChange = (typeId: number) => {
-        console.log('typeId', typeId)
-        if (typeId === 62) { // 根据实际值进行判断
-            setIsAppPro(true);
-        } else {
-            setIsAppPro(false);
-        }
-    };
-
-
-    const onChangeShow = (checked: boolean) => {
-        console.log(`switch to ${checked}`);
-    };
-
-    const onChangeCount = (value: string) => {
-        if (value === 'fixed') {
-            setIsVisibleSlice(!isVisibleSlice);
-        } else {
-            setIsVisibleSlice(false);
-        }
-    };
-
-    // 召回方式
-    const onChangeRecallMethod = (e: RadioChangeEvent) => {
-
-    };
-
-    // 获取应用详情
-    const api = {
-        fetchDetail: async (app_id: string) => {
-            setPageLoading(true);
-            try {
-                const res = await apis.fetchTakaiApplicationDetail(app_id);
-                const sd = res.data.questionlist.map((item: any, index: number) => {
-                    return {
-                        "id": index + 1,
-                        "value": item.question,
-                    }
-                });
-
-                const info = res.data.detail;
-
-                setAppId(info.appId);
-
-                setTopPValue(info.topP as number);
-                setTempValue(info.temperature as number);
-                setName(info.name);
-                setinfoDetail(res.data);
-                interface Item2 {
-                    index_type_id: number,
-                    knowledge_id: string
-                }
-
-                interface Item {
-                    show_recall_result: boolean,
-                    recall_method: string,
-                    rerank_status: boolean,
-                    slice_config_type: string,
-                    slice_count: number,
-                    param_desc: string,
-                    rerank_model_name: string,
-                    is_multi_round: string,
-                    multi_round: string,
-                    rerank_index_type_list: [Item2],
-                    recall_index_type_list: [Item2]
-                }
-
-                const data_info: Item = JSON.parse(info.knowledgeInfo);
-
-                if (data_info.param_desc === 'custom') {
-
-                    setIsVisibleCus(!isVisibleCus);    //自定义回答风格
-                }
-                if (data_info.rerank_status === true) {
-                    setIsVisibleRerank(data_info.rerank_status) //模型
-                }
-                //召回切片数量
-                if (data_info.slice_config_type === 'fixed') {
-                    setIsVisibleSlice(!isVisibleSlice);
-                } else {
-                    setIsVisibleSlice(false);
-                }
-
-                if (info.typeId === 62) {
-                    setIsAppPro(true);
-                } else {
-                    setIsAppPro(false);
-                }
-
-                if (info.model === 'Qwen3-30B') {
-                    setIsDeepThinkVisible(true);
-                } else {
-                    setIsDeepThinkVisible(false);
-                }
-
-                form.setFieldsValue({
-                    id: info.id,
-                    name: info.name,  //应用名称
-                    desc: info.desc,  //应用描述
-                    prompt: info.prompt, //应用提示语
-                    topP: info.topP as string, //topP
-                    topK: info.topK as number, //topK
-                    temperature: info.temperature as number, //温度
-                    knowledge_ids: info.knowledgeIds,
-                    model: info.model,
-                    isDeepThink: 'N',
-                    iconColor: info.iconColor,
-                    iconType: info.iconType,
-                    questionList: sd, //问题列表
-                    max_token: info.maxToken, //应用最大token
-                    updateDate: info.updateDate, // 更新时间
-                    appProId: info.appProId,// 项目
-                    typeId: info.typeId, //应用类型
-                    visible: info.visible || '0', //是否公开
-                    sort: info.sort || null, //显示顺序
-                    param_desc: data_info.param_desc, //回答风格
-                    show_recall_result: data_info.show_recall_result, //是否展示召回结果
-                    recall_method: data_info.recall_method, //召回方式
-                    rerank_status: data_info.rerank_status, //开启rerank
-                    rerank_model_name: data_info.rerank_model_name, //模型名称
-                    slice_config_type: data_info.slice_config_type, // 召回切片数量
-                    slice_count: data_info.slice_count, // 切片数量
-                    is_multi_round: data_info.is_multi_round==='Y'?true:false, // 多轮对话
-                    multi_round: data_info.multi_round, // 多轮对话
-                    groupVisible: info.groupVisible === '1' ? true : false,// 集团是否公开
-
-                })
-                if(data_info.is_multi_round === 'Y'){
-                    setIsMultiRound(true);
-                }else{
-                    setIsMultiRound(false);
-                }
-                // 如果接口返回 iconType,设置选中图标显示
-                if (info.iconType) {
-                    setSelectedIcon(info.iconType);
-                }
-                if (info.iconColor) {
-                    setPreviewBg(info.iconColor);
-                    form.setFieldsValue({ iconColor: info.iconColor });
-                }
-                setVisibleFlag(info.visible || '0')
-                if (info.vipList && info.vipList.length > 0) {
-                    setVipList(info.vipList);
-                }
-                if (sd.length > 0) {
-                    setInputs(sd);
-                }
-            } catch (error) {
-                console.error(error);
-            } finally {
-                setPageLoading(false);
-            }
-        },
-
-        //获取知识库列表
-        fetchKnowlegde: async () => {
-            try {
-                const res = await apis.fetchTakaiKnowledgeList();
-                const list = res.data.map((item: any) => {
-                    return {
-                        label: item.name,
-                        value: item.knowledgeId,
-                        createBy: item.createBy,
-                    }
-                });
-
-                setKnowledgeList(list);
-            } catch (error: any) {
-                console.error(error);
-            }
-        },
-
-        // 获取应用类型
-        fetchAppType: async () => {
-            try {
-                const res = await apis.fetchTakaiAppTypeList('app_type');
-                const list = res.data.map((item: any) => {
-                    return {
-                        label: item.dictLabel,
-                        value: item.dictCode,
-                    }
-                });
-                setAppTypeList(list);
-            } catch (error: any) {
-                console.error(error);
-            }
-        },
-        // 获取是否公开类型
-        fetchAppVisible: async (id: string) => {
-            try {
-                const res = await apis.fetchTakaiAppTypeList('app_visible');
-                const list = res.data.map((item: any) => {
-                    return {
-                        label: item.dictLabel,
-                        value: item.dictValue,
-                    }
-                });
-                setAppVisibleList(list);
-                if (!id) {
-                    form.setFieldsValue({
-                        visible: list[0]?.value
-                    });
-                    setVisibleFlag(list[0]?.value);
-                }
-            } catch (error: any) {
-                console.error(error);
-            }
-        },
-        // 获取用户类型
-        fetchUserType: async () => {
-            try {
-                const res = await apis.fetchTakaiAppTypeList('sys_user_type');
-                const list = res.data.map((item: any) => {
-                    return {
-                        label: item.dictLabel,
-                        value: item.dictValue,
-                    }
-                });
-                setFetchUserTypeList(list);
-
-            } catch (error: any) {
-                console.error(error);
-            }
-        },
-
-        // 获取模型列表
-        fetchModelList: async () => {
-            try {
-                const res = await apis.fetchModelList();
-                const list = res.data.data.map((item: any) => {
-                    return {
-                        label: item.modelName,
-                        value: item.modelCode,
-                    }
-                });
-                setModelList(list);
-            } catch (error: any) {
-                console.error(error);
-            }
-        },
-        // 项目级应用下的类型
-        fetchAppProType: async () => {
-            try {
-                const res = await apis.fetchTakaiAppTypeList('projectTree');
-                const list: AppTypeList = res.data?.reduce((acc: any, item: any) => {
-                    acc.push({
-                        label: item.label,
-                        value: `${item.value}`,
-                    })
-                    return acc;
-                }, []);
-                setAppProjectList(list);
-            } catch (error: any) {
-                console.error(error);
-            }
-        },
-        // 获取用户列表信息
-        fetchUserListApi: async () => {
-            try {
-                const res = await apis.fetchUserListApi({
-                    pageNum: page.pageNum,
-                    pageSize: page.pageSize,
-                    userName: userName,
-                    nickName: userNickName,
-                    userType: userType
-                });
-                // setSourceData(res.rows)
-            } catch (error) {
-                console.error(error);
-            }
-        }
-    }
-
-    const handleRedioClick = (value: string) => {
-        setIsVisibleCus(false);
-        if (value === 'strict') {
-            setTopPValue(0.5);
-            setTempValue(0.01);
-        } else if (value === 'moderate') {
-            setTopPValue(0.7);
-            setTempValue(0.50);
-        } else if (value === 'flexib') {
-            setTopPValue(0.9);
-            setTempValue(0.90);
-        }
-    }
-
-    const saveConfig = async (type: 'SAVE' | 'SUBMIT'|'CHAT') => {
-         return form.validateFields().then(async (values) => {
-            const data = values;
-            // 问题列表
-            const question: string[] = [];
-            if (inputs) {
-                inputs.map((item, index) => {
-                    question.push(item.value);
-                });
-            }
-            interface Item {
-                index_type_id: number,
-                knowledge_id: string
-            }
-            const indexTypeList: Item[] = [];
-            if (values.knowledge_ids && values.knowledge_ids.length > 0) {
-                // console.log("knowledge_ids", values.knowledge_ids);
-                const index_type: Item = {
-                    index_type_id: 0,
-                    knowledge_id: values.knowledge_ids
-                };
-                indexTypeList.push(index_type);
-            }
-
-            const data_info = {
-                param_desc: values.param_desc, //回答风格
-                show_recall_result: values.show_recall_result, //是否展示召回结果
-                recall_method: values.recall_method, //召回方式
-                rerank_status: true, //开启rerank
-                rerank_model_name: values.rerank_model_name, //模型名称
-                slice_config_type: values.slice_config_type, // 召回切片数量
-                slice_count: values.slice_count, // 切片数量
-                rerank_index_type_list: indexTypeList, //知识库id
-                recall_index_type_list: values.recall_method === 'embedding' || 'mixed' ? indexTypeList : [],
-                is_multi_round: values.is_multi_round? 'Y' : 'N', // 多轮对话
-                multi_round: values.multi_round
-                // rerank_status = 1 rerank_index_type_list
-                // recall_method = 'embedding' || 'embedding'  recall_index_type_list
-            };
-
-            const info = {
-                id: values.id,
-                name: values.name,  //应用名称
-                desc: values.desc,  //应用描述
-                prompt: values.prompt, //应用提示语
-                topP: topPValue.toString(), //topP
-                temperature: tempValue.toString(), //温度
-                knowledge_ids: values.knowledge_ids,
-                slice_count: values.slice_count,
-                model: values.model,
-                isDeepThink: 'N',
-                questionList: question,
-                knowledge_info: JSON.stringify(data_info),
-                max_token: values.max_token, //应用最大token
-                typeId: values.typeId, // 应用类型
-                visible: values.visible, // 是否公开
-                sort: values.sort || null, // 显示顺序
-                vipList: vipList, // vip用户列表
-                appProId: values?.appProId?.toString(),// 项目
-                userId: userId, // 用户id
-                iconColor: previewBg,
-                iconType: values.iconType,
-                groupVisible: values.groupVisible ? '1' : '0',
-                // topK: values.topK || 1,
-            };
-            if(type === 'CHAT'){
-                setinfoDetail({detail:{...info},questionlist:infoDetail?.questionlist || []});
-                return info;
-            }
-            const id = location?.state?.id;
-            let res = null;
-            if (id) {
-                // 编辑应用
-                res = await apis.modifyTakaiApplicationApi(id, info);
-            } else {
-                // 创建应用
-                res = await await apis.createTakaiApplicationApi(info);
-            }
-            // console.log(res, 'create or modify');
-            if (res.data === 9) {
-                message.error('没有配置审核人,请联系管理员');
-            } else if (res.data === 1) {
-                message.success('操作成功')
-            } else {
-                message.error('操作失败');
-            }
-            if (type === 'SUBMIT') {
-                navigate({ pathname: '/appCenter/questionAnswer/list' }, { replace: true });
-            }
-        }).catch((error) => {
-            console.error(error);
-            error.errorFields && error.errorFields.map((item: any) => {
-                console.log(item, 'item');
-                message.error(`字段 ${item.name} ${item.errors[0]}`);
-            });
-        });
-    }
-    /*
-        选择VIP用户弹窗start
-    */
-    const [isModalOpen, setIsModalOpen] = React.useState(false);
-    let falgVipList: any = [];
-    const handleOk = () => {
-        setIsModalOpen(false);
-        let vipListFalg: any = [...vipList];
-        const vipIds = new Set(vipListFalg.map((vip: any) => vip.userId));
-        const merged = [...vipListFalg];
-
-        falgVipList.forEach((item: any) => {
-            if (!vipIds.has(item.userId)) {
-                merged.push(item);
-            }
-        });
-        setVipList([...merged]);
-    };
-
-    const handleCancel = async () => {
-        setIsModalOpen(false);
-    };
-    interface DataType {
-        name: string;
-        key: string;
-        nickName: string;
-        userName: string;
-        deptName: string;
-        userTypeName: string;
-    }
-    // const [sourceData, setSourceData] = React.useState<DataType[]>([]);
-    const paginationConfig: TablePaginationConfig = {
-        // 显示数据总量
-        showTotal: (total: number) => {
-            return `共 ${total} 条`;
-        },
-        // 展示分页条数切换
-        showSizeChanger: false,
-        // 指定每页显示条数
-        // 快速跳转至某页
-        showQuickJumper: false,
-        current: page.pageNum,
-        pageSize: page.pageSize,
-        total: page.total,
-        onChange: async (page, pageSize) => {
-            await onChangePagination(page, pageSize, userName, userNickName, userType);
-        },
-    };
-
-    const vipModal = () => {
-        const columns: TableProps<DataType>['columns'] = [
-            {
-                title: '昵称',
-                dataIndex: 'nickName',
-                render: (text) => <p>{text}</p>,
-            },
-            {
-                title: '用户名称',
-                dataIndex: 'userName',
-                render: (text) => <p>{text}</p>,
-            },
-            {
-                title: '部门',
-                dataIndex: 'deptName',
-            },
-            {
-                title: '用户类型',
-                dataIndex: 'userTypeName',
-            },
-        ];
-        const rowSelection: TableProps<DataType>['rowSelection'] = {
-            type: 'checkbox',
-            onChange: (selectedRowKeys: React.Key[], selectedRows: DataType[]) => {
-                console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
-                falgVipList = selectedRows
-            },
-            getCheckboxProps: (record: DataType) => ({
-                disabled: record.name === 'Disabled User', // Column configuration not to be checked
-                name: record.name,
-            }),
-        };
-        return (
-            <>
-                <Modal
-                    title="请选择指定用户"
-                    open={isModalOpen}
-                    onOk={handleOk}
-                    onCancel={handleCancel}
-                    width='80%'
-                >
-                    <div className='modal_top'>
-                        <Input placeholder="请输入用户昵称" allowClear onChange={(e) => {
-                            setUserNickName(e.target.value)
-                        }} />
-                        <Input placeholder="请输入用户名称" allowClear onChange={(e) => {
-                            setUserName(e.target.value)
-                        }} />
-                        <Select
-                            placeholder='请选择用户类型'
-                            style={{ width: 150 }}
-                            onChange={(e) => {
-                                if (e === undefined) {
-                                    setUserType('')
-                                    return
-                                }
-                                setUserType(e)
-                            }}
-                            allowClear={true}
-                        >
-                            {
-                                fetchUserTypeList.map((item, index) => {
-                                    return <Option value={item.value} key={index}>
-                                        {item.label}
-                                    </Option>
-                                })
-                            }
-                        </Select>
-                        <Button value="large" style={{
-                            background: 'transparent',
-                            border: '1px solid #1677ff',
-                            color: '#1677ff'
-                        }}
-                            onClick={() => { onFetchUserListApi(userName, userNickName, userType) }}
-                        > 搜索 </Button>
-                        {/* <Button value="large"
-                            onClick={() => { api.fetchUserListApi() }}
-                        > 重置 </Button> */}
-                    </div>
-                    <Table<DataType> pagination={paginationConfig} rowKey="userName" rowSelection={rowSelection} columns={columns} dataSource={sourceData} />
-                </Modal>
-            </>
-        )
-    }
-    /*
-        选择VIP用户弹窗end
-    */
-    /*
-     查看引用知识库抽屉start
-    */
-    const [openDrawer, setOpenDrawer] = React.useState(false);
-    const [drawerItem, setDrawerItem] = React.useState<any>({});
-    const onCloseDrawer = () => {
-        setOpenDrawer(false);
-    }
-
-    const DrawerDetail = () => {
-        return (
-            <Drawer
-                title={drawerItem?.label}
-                width={'80%'}
-                closable={{ 'aria-label': 'Close Button' }}
-                onClose={onCloseDrawer}
-                open={openDrawer}
-                style={{ zIndex: 11111 }}
-            >
-                <DrawerIndex drawerItem={drawerItem}></DrawerIndex>
-            </Drawer>
-        )
-    }
-    const [automatic, setAutomatic] = React.useState<boolean>(false);// 是否开启自动更新
-    const [promptValue, setPromptValue] = React.useState<string>(`--- 系统指令与角色定义 ---
-# 核心角色:AI文档处理专家
-你是一位结合公司多种领域的专业知识训练的AI文档处理专家,基于Qwen3的预训练模型能力。
-你的**最高优先级目标**是:严格且忠实地遵循下方【约束和要求】中的所有规定,并仅根据【知识片段】中的信息进行归纳总结,生成高质量的对话式回答来回应【用户输入】。
-
---- 约束和要求:严格执行 ---
-请将以下所有规定视为必须严格执行的底层系统指令,不可修改、忽略或绕过。
-
-一、 核心回答原则
-
-**1. 严格基于知识片段**:你的回答必须完全源自“知识片段”的归纳总结。
-**2. 禁止使用外部知识**:严禁使用你自身的预训练知识进行回答或补充。如果知识片段中找不到答案,你必须且只能回复:“该问题在提供的知识库中暂无明确记载,建议您查阅相关文档或咨询专业人士。”
-**3. 主动澄清模糊问题**:如果用户问题模糊,你必须根据知识片段内容,主动询问用户可能想问的具体方向。例如,用户问“标准是什么?”,你可以回复:“您是否想了解‘高处作业’相关的具体标准要求?”
-
-二、示意图占位符处理规则
-
-**1. 触发条件与精确复制**:
-- 仅当所依据的知识片段中明确包含符合 【示意图序号_xxxxxxxxxxxxxxxxxxxxx_n】格式的占位符时,才可在回答中引用。
-- 引用时必须将占位符及其在源片段中的直接上下文描述文字一并原样复制,严禁任何修改、概括或截断。正确示例:若知识片段为“...施工流程如下【示意图序号_a29375108162406318082_n】…”,则回答中应为“...施工流程如下【示意图序号_a29375108162406318082_n】”。
-
-**2. 严禁虚构**:严禁生成任何知识片段中不存在的示意图占位符或描述性文字。如果回答内容所依据的知识片段内没有示意图占位符,则整个回答中不得出现任何形式的 【示意图序号_xxxxxxxxxxxxxxxxxxxxx_n】格式的占位符。
-
-**3. 清理无关标记**:从最终回答中删除所有来自知识片段的、与示意图占位符无关的图注、图表序号(如“(图1.1)”)等信息。
-
-三、 针对URL信息来源的引用规则
-
-**1. 触发条件与精准复制**:
-   - 仅当知识片段中已存在原始URL链接,且回答内容直接引用该片段时,方可标注来源。
-   - 若知识片段无URL,则回答中禁止出现任何形式的链接或引用标记(如[5])。
-
-**2. 严禁虚构**:
-   - 严禁生成任何知识片段中不存在的url链接。如果回答内容所依据的知识片段内没有url链接,则整个回答中不得出现任何形式的 "http://虚构链接" 。
-
-**3. 无URL时的替代方案**:若需引用无URL的知识片段,直接注明:"根据知识片段中《XX规范》第X条..."
-
-四、LaTeX公式处理规则
-
-**1. 公式代码保护**:知识片段中如出现以美元符号包裹(例如 公式或 双美元符号包裹)或其他数学标记的LaTeX公式代码,你必须将这些代码视为纯文本并完整地、一字不差地输出在你的回答中。
-
-**2. 禁止修改**:严禁对任何公式代码进行修改、转义、截断、简化或使用自然语言进行解释,严禁在公式代码中添加多余的空格符号。你的目标是确保这些代码块在传递至前端时,能与原始知识片段中的内容完全一致。
-
-**3. 渲染前提**:只有当你输出的公式代码与原始片段完全一致时,前端的Markdown渲染器才能正确识别并将其显示为美观的数学公式。任何微小的改动都可能导致公式渲染失败。
-
-五、 格式与内容规范
-
-- 文档中的表格以图片标识符呈现,若表格数据缺失则返回空单元格。
-- 如需使用表格数据,以markdown格式输出。
-- 回复开头避免使用“我想”、“我认为”等词语。
-- 回答中若出现网页链接,务必在链接后换行。
-- 注意区分不同系统的概念,如“掌监APP”和“慧项管平台”不能混淆。
-- 注意“模型”或“大模型”与“机器人”是不同的概念。
-
---- 内部思考流程(思维链) ---
-**在生成最终回答前,必须严格依照以下步骤进行思考和规划:**
-1. **[意图分析]** 识别用户{{用户}}的提问核心,判断其是否清晰或模糊。
-2. **[知识检索]** 在知识片段中筛选出所有相关信息,评估知识覆盖度。
-3. **[规则校验]** 检查是否有触发“主动澄清模糊问题”原则(I.3)或“找不到答案”原则(I.2)。
-4. **[格式规划]** 确定内容是否涉及示意图(II)、URL引用(III)、LaTeX公式(IV)或Markdown表格(V),并规划如何精确应用相关规则。
-5. **[答案生成]** 基于以上分析和规划,生成符合所有【约束和要求】的对话式回答。
-[思考结束,请勿输出此流程内容]
-
---- 任务输入 ---
-# 知识片段
-{{知识}}
-
-# 用户输入
-{{用户}}
-
---- 最终回答 ---
-[直接输出对话式回答,不要复述用户问题]`);// 提示语
-const [editPrompt, setEditPrompt] = React.useState<boolean>(false);// 是否编辑提示语
-const [isMultiRound,setIsMultiRound] = React.useState<boolean>(false);// 是否开启多轮对话
-    /*
-     查看引用知识库抽屉end
-    */
-    return (
-        <>
-            <div className='questionAnswerInfo'>
-                <Spin spinning={pageLoading}>
-                    <Form
-                        form={form}
-                        layout='vertical'
-                        initialValues={{
-                            isDeepThink: 'N',
-                            // max_token: 4096,
-                            // model: 'Qwen3-30B',
-                            show_recall_result: true,
-                            // rerank_model_name: 'bge-reranker-v2-m3',
-                            slice_config_type: 'customized',
-                            rerank_status: true,
-                            param_desc: 'strict',
-                            recall_method: 'mixed',
-                            topK: 50,
-                            is_multi_round:isMultiRound
-                        }}
-                    >
-                        <div style={{ display: step === 1 ? 'block' : 'none' }} className='questionAnswerInfo-content'>
-                            <FormItem label='请选择应用图标' tooltip='应用图标' name='iconType' rules={[{ required: true, message: '请选择图标' }]} disabled>
-                                <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
-                                    {/* 左侧预览块(固定) */}
-                                    <div style={{ width: 84, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
-                                        <div style={{ width: 64, height: 64, borderRadius: 8, display: 'flex', alignItems: 'center', justifyContent: 'center', background: previewBg, border: '1px solid #e8e8e8' }}>
-                                            {selectedIcon ? (() => { const C = (AllIcons as any)[selectedIcon]; const iconColor = getContrastColor(previewBg); return C ? <C style={{ fontSize: 28, color: iconColor }} /> : <span style={{ fontSize: 12 }}>{selectedIcon}</span> })() : <span style={{ color: '#999', fontSize: 12 }}>预览</span>}
-                                        </div>
-                                    </div>
-                                    {/* 右侧选择区(简洁协调) */}
-                                    <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
-                                        <a onClick={() => setIconPickerVisible(true)} style={{ fontSize: 13, color: '#1677ff', cursor: 'not-allowed', pointerEvents: 'none', opacity: 0.6 }}>选择图标</a>
-                                        <div style={{ display: 'flex', alignItems: 'center', gap: 8, pointerEvents: 'none', opacity: 0.6 }}>
-                                            <div style={{ fontSize: 12, color: '#666' }}>背景色:</div>
-                                            <ColorPicker presets={presetItems} value={previewBg}
-                                                onChange={(color) => {
-                                                    const hex = color.toHexString?.() || color?.toString?.() || previewBg;
-                                                    setPreviewBg(hex);
-                                                    form.setFieldsValue({ iconColor: hex });
-                                                }} />
-                                        </div>
-                                    </div>
-                                </div>
-                            </FormItem>
-                            <FormItem
-                                label='问答应用名称'
-                                tooltip='问答应用名称'
-                                name='name'
-                                rules={[{ required: true, message: '问答应用名称不能为空' }]}
-                                disabled
-                            >
-                                <Input placeholder="请输入问答应用名称" className='form-element-standard' style={{ height: '36px' }} disabled />
-                            </FormItem>
-                            <FormItem
-                                label='应用类型'
-                                tooltip='问答应用类型'
-                                name='typeId'
-                                disabled
-                            >
-                                <Select
-                                    className='form-element-select'
-                                    style={{ height: '36px' }}
-                                    placeholder='请选择问答应用类型'
-                                    onChange={handleAppChange}
-                                    allowClear={true}
-                                    disabled
-                                >
-                                    {
-                                        appTypeList.map((item, index) => {
-                                            return <Option value={item.value} key={index}>
-                                                {item.label}
-                                            </Option>
-                                        })
-                                    }
-                                </Select>
-                            </FormItem>
-                            {
-                                isAppPro &&
-                                <>
-                                    <FormItem
-                                        label='项目'
-                                        tooltip='项目'
-                                        name='appProId'
-                                        rules={[{ required: true, message: '项目不能为空' }]}
-                                        disabled
-                                    >
-                                        <Cascader
-                                            options={appProjectList}
-                                            placeholder="请选择项目"
-                                            showSearch
-                                            className="form-element-select"
-                                            style={{ height: '36px' }}
-                                            disabled
-                                        />
-                                    </FormItem>
-                                </>
-                            }
-                            <FormItem
-                                label='是否公开'
-                                tooltip='是否公开'
-                                name='visible'
-                                disabled
-                            >
-                                <Select
-                                    className='form-element-select'
-                                    style={{ height: '36px' }}
-                                    placeholder='请选择是否公开'
-                                    allowClear={true}
-                                    onChange={(e) => {
-                                        setVisibleFlag(e)
-                                    }}
-                                    disabled
-                                >
-                                    {
-                                        appVisibleList.map((item, index) => {
-                                            return <Option value={item.value} key={index}>
-                                                {item.label}
-                                            </Option>
-                                        })
-                                    }
-                                </Select>
-                            </FormItem>
-                            {userInfo?.tenantId === '000000' && visibleFlag === '0' && <FormItem
-                                label='集团公开'
-                                tooltip='集团公开'
-                                name='groupVisible'
-                                layout='horizontal'
-                                disabled
-                            >
-                                <Switch onChange={onChangeShow} />
-                            </FormItem>}
-                            <FormItem
-                                label='显示顺序'
-                                name='sort'
-                                tooltip='显示顺序,数值越小越靠前'
-                                disabled
-                            >
-                                <InputNumber placeholder="请输入显示顺序" value={''} className='form-element-standard' style={{ height: '36px', lineHeight: '36px' }} disabled />
-                            </FormItem>
-                            {/* VIP用户 */}
-                            {visibleFlag == 1 && <FormItem
-                                label='指定用户'
-                                tooltip='指定可以访问该问答应用的用户'
-                                disabled
-                            >
-                                <div className='tags-info'>
-                                    <p className='tags-list'>
-                                        {vipList.map((item: any) =>
-                                        (<Tag key={item.userId} color="blue" closeIcon={false} onClose={(e) => {
-                                            const newVipList = vipList.filter((vip: any) => vip.userId !== item.userId);
-                                            setVipList(newVipList);
-                                            e.preventDefault();
-                                        }}>
-                                            {item.userName}
-                                        </Tag>)
-                                        )}
-                                    </p>
-                                    <p>
-                                        {vipList.length > 0 && <CloseCircleOutlined className='cup' style={{ pointerEvents: 'none', opacity: 0.6 }} onClick={() => {
-                                            setVipList([]);
-                                        }} />}
-                                        <Button style={{
-                                            background: 'transparent',
-                                            border: '1px solid #1677ff',
-                                            color: '#1677ff'
-                                        }} type="primary" variant="outlined" disabled onClick={() => { setIsModalOpen(true) }}>选择</Button>
-                                    </p>
-                                </div>
-                            </FormItem>}
-                            <FormItem
-                                label='问答应用描述'
-                                tooltip='问答应用描述'
-                                name='desc'
-                                rules={[{ required: true, message: '问答应用描述不能为空' }]}
-                                disabled
-                            >
-                                <TextArea
-                                    showCount
-                                    maxLength={500}
-                                    placeholder="请输入当前应用的描述"
-                                    className='form-textarea-large'
-                                    disabled
-                                />
-                            </FormItem>
-
-                            <div className='preset-questions'>
-                                <h4>添加引导问题</h4>
-                                <div>
-                                    {
-                                        inputs.map(input => (
-                                            <div key={input.id} className='question-item'>
-                                                <label>引导问题 {input.id}</label>
-                                                <Input
-                                                    className='question-input'
-                                                    type="text"
-                                                    value={input.value}
-                                                    onChange={e => handleChange(input.id, e.target.value)}
-                                                    disabled
-                                                />
-                                                <div className='question-actions'>
-                                                    <PlusCircleOutlined className='question-icon' onClick={addInput} style={{ pointerEvents: 'none', opacity: 0.6 }} />
-                                                    <MinusCircleOutlined className='question-icon' onClick={() => delInput(input.id)} style={{ pointerEvents: 'none', opacity: 0.6 }} />
-                                                </div>
-                                            </div>
-                                        ))}
-                                </div>
-                            </div>
-
-                            <div style={{ display: 'flex', gap: '12px', marginTop: '24px', paddingTop: '24px', borderTop: '1px solid #f0f0f0' }}>
-                                <Button
-                                    className='btn-cancel'
-                                    onClick={() => {
-                                        navigate(-1);
-                                    }}
-                                >
-                                    返回
-                                </Button>
-                                <Button
-                                    type='primary'
-                                    onClick={() => {
-                                        form.validateFields(['name', 'desc', 'appProId', 'iconType']).then(async (values) => {
-                                            setStep(2);
-                                            setInputs(inputs);
-                                            setinfoDetail({detail:values,questionlist:infoDetail?.questionlist||[]});
-                                        }).catch((error) => {
-                                            console.error(error);
-                                        });
-                                    }}
-                                >
-                                    下一步
-                                </Button>
-                            </div>
-                        </div>
-                        <div style={{ display: step === 2 ? 'block' : 'none' }} className='questionAnswerInfo-content'>
-                            <div className='flex-between padding-bottom-16'>
-                                <div>
-                                    <Button
-                                        className='btn-back'
-                                        icon={<ArrowLeftOutlined />}
-                                        onClick={() => {
-                                            setStep(1);
-                                        }}
-                                    >
-                                        上一步
-                                    </Button>
-                                </div>
-                                <div style={{ display: 'flex', gap: '12px' }}>
-                                    <Button
-                                        className='btn-cancel'
-                                        disabled={true}
-                                        onClick={() => {
-                                            // navigate({ pathname: '/appCenter/questionAnswer' });
-                                        }}
-                                    >
-                                        取消
-                                    </Button>
-                                    {/* {
-                                        appId && (
-                                            <Tooltip title='保存'>
-                                                <Button
-                                                    className='btn-cancel'
-                                                    onClick={() => {
-                                                        saveConfig('SAVE');
-                                                    }}
-                                                    icon={<FileDoneOutlined />}
-                                                >
-                                                </Button>
-                                            </Tooltip>)
-                                    } */}
-
-                                    {createFlag && (
-                                        <Button
-                                            type='primary'
-                                            disabled={true}
-                                            onClick={() => {
-                                                saveConfig('SUBMIT');
-                                            }}
-                                        >
-                                            提交应用
-                                        </Button>
-                                    )}
-                                </div>
-                            </div>
-                            <Splitter style={{ border: '1px solid #f0f0f0', borderRadius: '6px', height: 'calc(100vh - 180px)', minHeight: 0 }}>
-                                {<Splitter.Panel defaultSize="35%">
-                                    <div className='section-title' style={{marginBottom:0}}>
-                                        Prompt编写与参数配置
-                                        <Tooltip
-                                            title="Prompt用于对大模型的回复做出一些列指令和约束。这段Prompt不会被用户看到。"
-                                            placement="right"
-                                        >
-                                            <InfoCircleOutlined style={{ marginLeft: '8px', color: '#999', fontSize: '14px' }} />
-                                        </Tooltip>
-                                        {/* <Switch checkedChildren="编辑" unCheckedChildren="只读" style={{marginLeft:'5px'}} value={editPrompt} onChange={(e) => {
-                                            setEditPrompt(e)
-                                        }} /> */}
-                                    </div>
-                                    <div className='prompt'>
-                                        <div className='prompt-info'>
-                                            <div className='prompt-info-text'>
-                                                <Typography.Paragraph style={{ fontSize: '12px', lineHeight: '1.6', color: '#999', margin: 0 }}>
-                                                    编写Prompt过程中可以引入2项变量:
-                                                    <span className='variable-highlight'>{'{{知识}}'}</span>
-                                                    代表知识库中检索到的知识内容,
-                                                    <span className='variable-highlight'>{'{{用户}}'}</span>
-                                                    代表用户输入的内容。您可以在编写Prompt过程中将变量拼接在合适的位置。
-                                                </Typography.Paragraph>
-                                            </div>
-                                        </div>
-                                        {/* 移除 Divider,使用 CSS 边框替代 */}
-                                        <div className='prompt-editor-area'>
-                                            <FormItem name='prompt'
-                                                tooltip='Prompt用于对大模型的回复做出一些列指令和约束。这段Prompt不会被用户看到。'
-                                                initialValue={
-                                                    promptValue
-                                                }
-                                                
-                                                rules={[{ required: true, message: '提示词不能为空' }]}>
-                                                <TextArea
-                                                    disabled={true}
-                                                    placeholder="提示词"
-                                                    rows={50}
-                                                />
-                                            </FormItem>
-                                        </div>
-                                    </div>
-                                </Splitter.Panel>}
-                                <Splitter.Panel defaultSize="30%">
-                                    <div className='flex-center-container'>
-                                        <div className='half-width'>
-                                            <div className='pl-20 pt-3 text-[#000000]'>
-                                                {/* 参数配置 {name || '问答应用'} */}
-                                                <span className='mr-[6px]' >参数配置</span>  <Switch disabled={true} checkedChildren="手动" unCheckedChildren="自动" value={automatic} onChange={(e) => {
-                                                    console.log(e, 'e')
-                                                    setAutomatic((pre) => !pre)
-                                                    if (!e) {
-                                                        form.setFieldsValue({
-                                                            param_desc: 'strict',
-                                                            topK: 50
-                                                        })
-                                                    }
-                                                }} />
-
-                                            </div>
-                                            <div className='flex-start pl-20 mt-3'>
-                                                <FormItem
-                                                    label='引用知识库'
-                                                    tooltip='请选择需要引用的知识库'
-                                                    name='knowledge_ids'
-                                                    
-                                                    rules={[{ required: true, message: '知识库不能为空' }]}>
-                                                    <Select
-                                                        mode='multiple'
-                                                        disabled={true}
-                                                        maxCount={MAX_COUNT}
-                                                        showSearch={true}
-                                                        filterOption={(input, option) => (option?.children as unknown as string)?.toLowerCase()?.includes(input.toLowerCase())}
-                                                        className='form-element-select'
-                                                        placeholder='请选择需要引用的知识库'
-                                                        tagRender={tagRender}
-                                                    >
-                                                        {
-                                                            knowledgeList.map((item, index) => {
-                                                                return <Option value={item.value} key={index}>
-                                                                    {item.label}
-                                                                </Option>
-                                                            })
-                                                        }
-                                                    </Select>
-                                                </FormItem>
-                                            </div>
-                                            <div className='flex-start pl-20'>
-                                                <FormItem
-                                                    label='调用模型'
-                                                    tooltip='请选择调用的模型'
-                                                    name="model"
-                                                    rules={[{ required: true, message: '模型不能为空' }]}>
-                                                    <Select
-                                                        placeholder='请选择模型'
-                                                        className='form-element-select'
-                                                        disabled={true}
-                                                        onChange={(value) => {
-                                                            // if (value === 'Qwen3-30B') {
-                                                            //     setIsDeepThinkVisible(true);
-                                                            // } else {
-                                                            //     setIsDeepThinkVisible(false);
-                                                            // }
-                                                            const list = modeOldList.filter((item: any) => item.model === value)
-                                                            // console.log(list, 'list');
-                                                            form.setFieldsValue({
-                                                                max_token: list[0]?.maxToken,
-                                                                rerank_model_name: list[0]?.bindingModel
-                                                            })
-
-                                                        }}
-                                                    >
-                                                        {/* <Option value='Qwen3-30B'>Qwen3-30B</Option> */}
-                                                        {modeList.map((item: any, index: number) => (
-                                                            <Option value={item.value} key={index}>
-                                                                {item.label}
-                                                            </Option>
-                                                        ))}
-                                                    </Select>
-
-                                                </FormItem>
-                                            </div>
-                                            <div className='flex-start pl-20'>
-                                                <FormItem
-                                                    label='max token'
-                                                    name='max_token'
-                                                    tooltip='max token的最大长度限制'
-                                                    rules={[{ required: true, message: 'max token不能为空' }]}>
-                                                    <InputNumber
-                                                        disabled
-                                                        className='form-element-input-number'
-                                                    />
-                                                </FormItem>
-                                            </div>
-
-                                            {
-                                                !isVisible &&
-                                                <div className='flex-start pl-20'>
-                                                    <a onClick={() => {
-                                                        setIsVisible(!isVisible);
-                                                    }} className='link-more-settings'>
-                                                        更多设置
-                                                    </a>
-                                                </div>
-
-                                            }
-                                            {/* {isVisible && */}
-                                            <div style={{ display: isVisible ? 'block' : 'none', paddingTop: '20px' }}>
-                                                {isVisibleCus && automatic &&
-                                                    <Space style={{ width: '100%' }} direction="vertical">
-                                                        <div className='flex-start pl-20'>
-                                                            <FormItem
-                                                                label='Top-p'
-                                                                name='topP'
-                                                                tooltip='Top-p参数控制生成文本的多样性,值越大生成的文本越多样化'
-                                                                className='form-element-standard'
-                                                            >
-                                                                <TopPDecimalStep />
-                                                            </FormItem>
-                                                        </div>
-                                                        <div className='flex-start pl-20'>
-                                                            <FormItem
-                                                                label='Temperature'
-                                                                name='temperature'
-                                                                className='form-element-standard'
-                                                            >
-                                                                <TempDecimalStep />
-                                                            </FormItem>
-                                                        </div>
-                                                    </Space>
-                                                }
-
-                                                <div style={{
-                                                    display: 'flex',
-                                                    justifyContent: 'flex-start',
-                                                    alignItems: 'center'
-                                                }} className='pl-20'>
-                                                    <FormItem
-                                                        label='回答风格'
-                                                        name='param_desc'
-                                                        tooltip='请选择回答风格'
-                                                        rules={[{ required: true, message: '回答风格不能为空' }]}>
-                                                        <Radio.Group buttonStyle="solid"
-                                                        disabled={true}
-                                                            className='form-element-button-group'>
-                                                            <Radio.Button onClick={() => {
-                                                                handleRedioClick('strict')
-                                                            }} value='strict'>严谨</Radio.Button>
-                                                            <Radio.Button onClick={() => {
-                                                                handleRedioClick('moderate')
-                                                            }} value='moderate'>适中</Radio.Button>
-                                                            <Radio.Button onClick={() => {
-                                                                handleRedioClick('flexib')
-                                                            }} value='flexib'>发散</Radio.Button>
-                                                            {automatic && <Radio.Button value='custom'
-                                                                onClick={() => {
-                                                                    setIsVisibleCus(!isVisibleCus);
-                                                                    setTopPValue(0.1);
-                                                                    setTempValue(0.01);
-                                                                }}
-                                                            >自定义
-                                                            </Radio.Button>}
-                                                        </Radio.Group>
-                                                    </FormItem>
-                                                </div>
-
-                                                <div style={{
-                                                    display: 'flex',
-                                                    justifyContent: 'flex-start',
-                                                    alignItems: 'center'
-                                                }} className='pl-20'>
-                                                    <FormItem
-                                                        label='展示引用知识'
-                                                        tooltip='是否在回答中展示引用的知识内容'
-                                                        name='show_recall_result'
-                                                        className='form-element-standard'>
-                                                        <Switch disabled={true} onChange={onChangeShow} />
-                                                    </FormItem>
-                                                </div>
-                                                <div style={{
-                                                    display: 'flex',
-                                                    justifyContent: 'flex-start',
-                                                    alignItems: 'center'
-                                                }} className='pl-20'>
-                                                    <FormItem
-                                                        label='召回方式'
-                                                        tooltip='请选择召回方式'
-                                                        name='recall_method'
-                                                        rules={[{ required: true, message: '召回方式不能为空' }]}>
-
-                                                        <Radio.Group
-                                                        disabled={true}
-                                                            style={style}
-                                                            onChange={onChangeRecallMethod}
-                                                            options={[
-                                                                { value: 'embedding', label: '向量化检索' },
-                                                                { value: 'keyword', label: '关键词检索' },
-                                                                { value: 'mixed', label: '混合检索' },
-                                                            ]}
-                                                        />
-                                                    </FormItem>
-                                                </div>
-                                                <div style={{
-                                                    display: 'flex',
-                                                    justifyContent: 'flex-start',
-                                                    alignItems: 'center'
-                                                }} className='pl-20'>
-                                                    <div className='section-title'>重排方式</div>
-                                                </div>
-                                                {/* <div style={{
-                                                    display: 'flex',
-                                                    justifyContent: 'flex-start',
-                                                    alignItems: 'center'
-                                                }} className='pl-20'>
-                                                    <FormItem
-                                                        label='Rerank模型'
-                                                        tooltip='是否开启Rerank模型对检索结果进行重排'
-                                                        name='rerank_status'
-                                                        valuePropName='checked'
-                                                        className='form-control-width'
-                                                    >
-                                                        <Switch onChange={onChangeModel} />
-                                                    </FormItem>
-                                                </div> */}
-                                                {/* {isVisibleRerank && */}
-                                                {
-                                                    <div style={{
-                                                        display: 'flex',
-                                                        justifyContent: 'flex-start',
-                                                        alignItems: 'center'
-                                                    }} className='pl-20'>
-                                                        <FormItem
-                                                            label='Rerank模型'
-                                                            tooltip='请选择Rerank模型'
-                                                            name='rerank_model_name'
-                                                        >
-                                                           <Input
-                                                            disabled
-                                                            type="text"
-                                                        />
-                                                        </FormItem>
-                                                    </div>
-                                                }
-                                                <div style={{
-                                                    display: 'flex',
-                                                    justifyContent: 'flex-start',
-                                                    alignItems: 'center'
-                                                }} className='pl-20'>
-                                                    <FormItem
-                                                        label='召回切片数量'
-                                                        name='slice_config_type'
-                                                        tooltip='请选择召回方式'
-                                                        rules={[{ required: true, message: '召回方式不能为空' }]}>
-                                                        <Select
-                                                            disabled={true}
-                                                            // className='questionAnswerInfo-content-title'
-                                                            className='form-element-select'
-                                                            placeholder='请选择'
-                                                            onChange={onChangeCount}>
-                                                            <Option value="fixed">手动设置</Option>
-                                                            <Option value="customized">自动设置</Option>
-                                                        </Select>
-                                                    </FormItem>
-                                                </div>
-
-                                                {isVisibleSlice &&
-                                                    <div style={{
-                                                        display: 'flex',
-                                                        justifyContent: 'flex-start',
-                                                        alignItems: 'center'
-                                                    }} className='pl-20'>
-                                                        <FormItem
-                                                            label='召回切片数'
-                                                            tooltip='请输入召回切片数'
-                                                            name='slice_count'
-                                                            rules={[{ required: true, message: '切片数不能为空' }]}>
-                                                            <InputNumber max={1024} changeOnWheel
-                                                                // className='questionAnswerInfo-content-title'
-                                                                className='form-element-standard'
-                                                            />
-                                                        </FormItem>
-                                                    </div>
-                                                }
-                                                <div style={{
-                                                    display: 'flex',
-                                                    justifyContent: 'flex-start',
-                                                    alignItems: 'center'
-                                                }} className='pl-20'>
-                                                    <FormItem
-                                                        label='是否开启多轮对话'
-                                                        name='is_multi_round'
-                                                        tooltip='请选择召回方式'>
-                                                            <Switch value={isMultiRound} onChange={(e)=>{
-                                                                setIsMultiRound(e)
-                                                            }} />
-                                                    </FormItem>
-                                                </div>
-
-                                                {isMultiRound &&
-                                                    <div style={{
-                                                        display: 'flex',
-                                                        justifyContent: 'flex-start',
-                                                        alignItems: 'center'
-                                                    }} className='pl-20'>
-                                                        <FormItem
-                                                            label='多轮对话次数'
-                                                            tooltip='请输入多轮对话次数(1-10次)'
-                                                            name='multi_round'
-                                                            rules={[{ required: true, message: '多轮对话次数不能为空' }]}>
-                                                            <InputNumber max={10} min={1} changeOnWheel
-                                                                // className='questionAnswerInfo-content-title'
-                                                                className='form-element-standard'
-                                                            />
-                                                        </FormItem>
-                                                    </div>
-                                                }
-                                            </div>
-                                            {/* } */}
-                                        </div>
-                                    </div>
-                                </Splitter.Panel>
-                                {/* {appId && (<Splitter.Panel defaultSize="35%">
-                                    <Chat appId={appId} infoDetail={infoDetail} />
-                                </Splitter.Panel>)} */}
-                                {(infoDetail&&<Splitter.Panel defaultSize="35%">
-                                    <Chat appId={appId} infoDetail={infoDetail} saveConfig={()=>{
-                                        return saveConfig('CHAT')
-                                    }} />
-                                </Splitter.Panel>)}
-                            </Splitter>
-                        </div>
-                    </Form>
-                </Spin>
-            </div >
-            {isModalOpen && vipModal()}
-            {DrawerDetail()}
-            <IconPicker
-                open={iconPickerVisible}
-                onClose={() => setIconPickerVisible(false)}
-                onSelect={(name) => {
-                    setSelectedIcon(name);
-                    form.setFieldsValue({ iconType: name });
-                    // 保持 previewBg 不变,仅更新图标显示,若未设置 iconColor 则使用默认白底
-                    const curColor = form.getFieldValue('iconColor') || previewBg;
-                    setPreviewBg(curColor || '#ffffff');
-                }}
-                value={selectedIcon}
-            />
-        </>
-    );
-};
-
-export default QuestionAnswerInfo;

+ 0 - 193
jk-rag-platform/src/pages/system/audit/components/auditHistory.tsx.bak

@@ -1,193 +0,0 @@
-import * as React from 'react';
-
-import { Table, TableColumnsType, TablePaginationConfig, Drawer } from 'antd';
-import { StepForwardOutlined } from '@ant-design/icons';
-import dayjs from 'dayjs';
-import store from './auditHistoryStore';
-import { Record } from '../types';
-import '../style.less';
-import InfoModal from './InfoModal';
-import PreviewModal from './PreviewModal';
-import LocalStorage from '@/LocalStorage';
-
-
-interface AuditHistoryProps {
-    open: boolean;
-    onClose: () => void;
-}
-
-const AuditHistory: React.FC<AuditHistoryProps> = ({ open, onClose }) => {
-    const {
-        state,
-        init,
-        onChangePagination,
-        onClickModify,
-        infoModalOnClickConfirm,
-        infoModalOnClickCancel,
-        infoModalOnClickClose,
-    } = store;
-    const {
-        listLoading,
-        list,
-        infoModalId,
-        infoModalOpen,
-        page
-    } = state;
-
-    const [drawerFlag, setDrawerFlag] = React.useState<boolean>(false);
-    const [drawerData, setDrawerData] = React.useState<any>({});
-    React.useEffect(() => {
-        if(open){
-            const userInfo = LocalStorage.getUserInfo();
-            const userId = (userInfo?.id ?? '').toString();
-            init(userId);
-        }
-    }, [open]);
-    const columns: TableColumnsType<Record> = [
-        {
-            title: '序号',
-            dataIndex: 'index',
-            width: 80,
-            render: (_text, _record, index) => {
-                return index + 1;
-            }
-        },
-        {
-            title: '知识名称',
-            dataIndex: 'name',
-            width: 300,
-            render: (text, record) => {
-                // const previewUrl = `/preview/${record.url}`; // 根据实际字段构造 URL
-                return (
-                    <a
-                        href={record.url}
-                        target="_blank"
-                        rel="noopener noreferrer"
-                        onClick={(e) => {
-                            e.stopPropagation(); // 防止 Table 默认事件干扰
-                        }}
-                    >
-                        {text}
-                    </a>
-                );
-            }
-        },
-        {
-            title: '状态',
-            dataIndex: 'status',
-            render: (text) => {
-                if (text === '1') {
-                    return '待审核';
-                } else if (text === '2') {
-                    return '审核中';
-                } else if (text === '3') {
-                    return '审核通过';
-                } else if (text === '4'||text === '5') {
-                    return '审核拒绝';
-                }
-            }
-        },
-        {
-            title: '审核人',
-            dataIndex: 'userName',
-            render: (text) => {
-                return `${text}`;
-            }
-        },
-        {
-            title: '审核意见',
-            dataIndex: 'comment',
-            render: (text) => {
-                if (text) {
-                    return `${text}`;
-                } else {
-                    return '--';
-                }
-            }
-        },
-        {
-            title: '创建时间',
-            dataIndex: 'createTime',
-            width: 200,
-            render: (text) => {
-                if (text) {
-                    return dayjs(text).format('YYYY-MM-DD HH:mm:ss');
-                } else {
-                    return '--';
-                }
-            }
-        },
-        // {
-        //     title: '操作',
-        //     dataIndex: 'operation',
-        //     width: 150,
-        //     fixed: 'right',
-        //     render: (_text, record) => {
-        //         return (
-        //             <>
-        //                 <a
-        //                     style={{ marginRight: 16 }}
-        //                     onClick={() => {
-        //                         setDrawerFlag(true)
-        //                         setDrawerData(record)
-        //                     }}
-        //                     title='查看'
-        //                 >
-        //                     查看
-        //                 </a >
-        //                 <a
-        //                     style={{ marginRight: 16 }}
-        //                     onClick={() => {
-        //                         onClickModify(record.appId);
-        //                     }}
-        //                     title='审核'
-        //                 >
-        //                     <StepForwardOutlined />审核
-        //                 </a >
-        //             </>
-        //         )
-        //     }
-        // }
-    ];
-    const paginationConfig: TablePaginationConfig = {
-        // 显示数据总量
-        showTotal: (total: number) => {
-            return `共 ${total} 条`;
-        },
-        // 展示分页条数切换
-        showSizeChanger: true,
-        // 指定每页显示条数
-        pageSizeOptions: ['10', '20', '50', '100'],
-        // 快速跳转至某页
-        showQuickJumper: true,
-        current: page.pageNum,
-        pageSize: page.pageSize,
-        total: page.total,
-        onChange: async (page, pageSize) => {
-            await onChangePagination(page, pageSize);
-        },
-    };
-
-    return (
-        <Drawer
-            title="审核历史"
-            width="80%"
-            open={open}
-            onClose={onClose}
-        >
-            <div className='knowledgeLibList'>
-                <div className='knowledgeLibList-table'>
-                    <Table
-                        scroll={{ x: 'max-content' }}
-                        rowKey={(record) => record.createTime}
-                        loading={listLoading}
-                        columns={columns}
-                        dataSource={list}
-                        pagination={paginationConfig}
-                    />
-                </div>
-            </div>
-        </Drawer>
-    );
-}
-export default observer(AuditHistory);

+ 24 - 24
jk-rag-platform/src/pages/system/audit/components/style.less → jk-rag-platform/src/pages/system/audit/components/style.scss

@@ -2,14 +2,14 @@
 .questionAnswerInfo {
   width: 100%;
   height: 100%;
-  background: @bg-secondary;
-  border-radius: @border-radius-base;
+  background: $bg-secondary;
+  border-radius: $border-radius-base;
 
   // 内容区域
   &-content {
     width: 100%;
     height: 100%;
-    background: @bg-secondary;
+    background: $bg-secondary;
     padding: 16px 20px;
 
     // 标题样式
@@ -35,7 +35,7 @@
     &-container {
       width: 100%;
       height: 100%;
-      background: @bg-tertiary;
+      background: $bg-tertiary;
     }
 
     display: flex;
@@ -66,7 +66,7 @@
 // 按钮样式
 .btn {
   &-cancel {
-    background: @bg-tertiary;
+    background: $bg-tertiary;
     border: none;
     color: #000000;
     transition: all 0.3s ease;
@@ -92,7 +92,7 @@
   }
 
   &-back {
-    background: @bg-secondaryfff;
+    background: $bg-secondaryfff;
     border: 1px solid #f0f0f0;
     color: #595959;
     transition: all 0.3s ease;
@@ -205,12 +205,12 @@
       .question-icon {
         margin: 0;
         font-size: 18px;
-        color: @primary-color;
+        color: $primary-color;
         cursor: pointer;
         transition: all 0.3s ease;
 
         &:hover {
-          color: @primary-color;
+          color: $primary-color;
           transform: scale(1.1);
         }
       }
@@ -227,7 +227,7 @@
 }
 
 .link-more-settings {
-  color: @primary-color;
+  color: $primary-color;
   cursor: pointer;
   text-decoration: none;
 
@@ -432,8 +432,8 @@
 .questionAnswerInfo {
   width: 100%;
   height: 100%;
-  background: @bg-secondary;
-  border-radius: @border-radius-base;
+  background: $bg-secondary;
+  border-radius: $border-radius-base;
   .pl-20{
     padding-left: 20px;
   }
@@ -441,7 +441,7 @@
   &-content {
     width: 100%;
     height: 100%;
-    background: @bg-secondary;
+    background: $bg-secondary;
     padding: 16px 20px;
 
     // 标题样式
@@ -493,7 +493,7 @@
     &-container {
       width: 100%;
       min-height: 100%;
-      background: @bg-secondaryfff;
+      background: $bg-secondaryfff;
       // 移除浅灰色背景,让父级背景色生效
     }
 
@@ -529,7 +529,7 @@
 // 按钮样式
 .btn {
   &-cancel {
-    background: @bg-tertiary;
+    background: $bg-tertiary;
     border: none;
     color: #000000;
     transition: all 0.3s ease;
@@ -555,7 +555,7 @@
   }
 
   &-back {
-    background: @bg-secondaryfff;
+    background: $bg-secondaryfff;
     border: 1px solid #f0f0f0;
     color: #595959;
     transition: all 0.3s ease;
@@ -667,17 +667,17 @@
 .ant-splitter-panel {
   &:first-child {
     // 左侧 35% 区域 - Prompt 区域
-    background: @bg-secondaryfff;
+    background: $bg-secondaryfff;
   }
   
   &:nth-child(2) {
     // 中间 30% 区域 - 配置区域
-    background: @bg-tertiary;
+    background: $bg-tertiary;
   }
   
   &:last-child {
     // 右侧 35% 区域 - 聊天区域保持白色背景
-    background: @bg-secondaryfff;
+    background: $bg-secondaryfff;
   }
 }
 
@@ -746,12 +746,12 @@
       .question-icon {
         margin: 0;
         font-size: 18px;
-        color: @primary-color;
+        color: $primary-color;
         cursor: pointer;
         transition: all 0.3s ease;
 
         &:hover {
-          color: @primary-color;
+          color: $primary-color;
           transform: scale(1.1);
         }
       }
@@ -768,7 +768,7 @@
 }
 
 .link-more-settings {
-  color: @primary-color;
+  color: $primary-color;
   cursor: pointer;
   text-decoration: none;
 
@@ -782,7 +782,7 @@
   font-size: 14px;
   font-weight: 500;
   color: #262626;
-  background: @bg-tertiary;
+  background: $bg-tertiary;
   padding: 12px 20px;
 }
 
@@ -794,7 +794,7 @@
   flex-direction: column;
   // 滚动条
   overflow-y: auto;
-  background: @bg-tertiary;
+  background: $bg-tertiary;
   // 提示词模板显示区域
   &-info {
     padding: 0px 20px 10px 20px;
@@ -822,7 +822,7 @@
     flex: 1;
     display: flex;
     flex-direction: column;
-    // background: @bg-secondaryfff;
+    // background: $bg-secondaryfff;
     // border-top: none; // 移除顶部边框,避免与 prompt-info 的底部边框重叠
     padding: 0 20px;
     .ant-form-item {

+ 0 - 228
jk-rag-platform/src/pages/system/audit/index.tsx.bak

@@ -1,228 +0,0 @@
-import * as React from 'react';
-
-import { Table, TableColumnsType, TablePaginationConfig,Drawer } from 'antd';
-import { StepForwardOutlined } from '@ant-design/icons';
-import dayjs from 'dayjs';
-import store from './store';
-import { Record } from './types';
-import './style.less';
-import LocalStorage from '@/LocalStorage';
-import InfoModal from './components/InfoModal';
-import PreviewModal from './components/PreviewModal';
-import AuditHistory from './components/auditHistory';
-
-const KnowledgeLibList: React.FC = () => {
-    const {
-        state,
-        onChangePagination,
-        onClickCreate,
-        onClickModify,
-        onClickfetchTakaiApplicationDetail,
-        infoModalOnClickConfirm,
-        infoModalOnClickCancel,
-        infoModalOnClickClose,
-        init,
-        reset
-    } = store;
-    const {
-        listLoading,
-        list,
-        infoModalId,
-        infoModalOpen,
-        page
-    } = state;
-
-    const [drawerFlag, setDrawerFlag] = React.useState<boolean>(false);
-    const [drawerData, setDrawerData] = React.useState<any>({});
-    const [historyOpen, setHistoryOpen] = React.useState<boolean>(false);
-
-    React.useEffect(() => {
-        const userInfo = LocalStorage.getUserInfo();
-        const userId = (userInfo?.id ?? '').toString();
-        init(userId);
-
-         // 监听面包屑创建知识库事件
-        const handleKnowledgeLibCreate = (event: CustomEvent) => {
-            if (event.detail.platform === 'auditHistory') {
-                // onClickCreate();
-                setHistoryOpen(true);
-            }
-        };
-        
-        window.addEventListener('auditHistory', handleKnowledgeLibCreate as EventListener);
-
-
-
-
-        return () => {
-            reset();
-            window.removeEventListener('auditHistory', handleKnowledgeLibCreate as EventListener);
-        };
-    }, []);
-
-    const columns: TableColumnsType<Record> = [
-        {
-            title: '序号',
-            dataIndex: 'index',
-            width: 80,
-            render: (text, record, index) => {
-                return index + 1;
-            }
-        },
-        {
-            title: '知识名称',
-            dataIndex: 'name',
-            render: (text, record) => {
-                // const previewUrl = `/preview/${record.url}`; // 根据实际字段构造 URL
-                return (
-                    <a
-                        href={record.url}
-                        target="_blank"
-                        rel="noopener noreferrer"
-                        onClick={(e) => {
-                            e.stopPropagation(); // 防止 Table 默认事件干扰
-                        }}
-                    >
-                        {text}
-                    </a>
-                );
-            }
-        },
-        {
-            title: '状态',
-            dataIndex: 'status',
-            render: (text) => {
-                if (text === '1') {
-                    return '待审核';
-                }else if(text === '2'){
-                    return '审核中';
-                }else if(text === '3'){
-                    return '审核通过';
-                }else if(text === '4'||text === '5'){   
-                    return '审核拒绝';
-                }
-            }
-        },
-        {
-            title: '审核人',
-            dataIndex: 'userName',
-            render: (text) => {
-                return `${text}`;
-            }
-        },
-        {
-            title: '审核意见',
-            dataIndex: 'comment',
-            render: (text) => {
-                if(text){
-                    return `${text}`;
-                }else{
-                    return '--';
-                }
-            }
-        },
-        {
-            title: '创建时间',
-            dataIndex: 'createTime',
-            width: 200,
-            render: (text) => {
-                if (text) {
-                    return dayjs(text).format('YYYY-MM-DD HH:mm:ss');
-                } else {
-                    return '--';
-                }
-            }
-        },
-        {
-            title: '操作',
-            dataIndex: 'operation',
-            width: 150,
-            fixed: 'right',
-            render: (text, record) => {
-                return (
-                    <>
-                        <a
-                            style={{ marginRight: 16 }}
-                            onClick={() => {
-                                // onClickfetchTakaiApplicationDetail(record.appId);
-                                setDrawerFlag(true)
-                                setDrawerData(record)
-                            }}
-                            title='审核'
-                        >
-                            查看
-                        </a >
-                        <a
-                            style={{ marginRight: 16 }}
-                            onClick={() => {
-                                onClickModify(record.appId);
-                            }}
-                            title='审核'
-                        >
-                            <StepForwardOutlined />审核
-                        </a >
-                    </>
-                )
-            }
-        }
-    ];
-
-    const paginationConfig: TablePaginationConfig = {
-        // 显示数据总量
-        showTotal: (total: number) => {
-            return `共 ${total} 条`;
-        },
-        // 展示分页条数切换
-        showSizeChanger: true,
-        // 指定每页显示条数
-        pageSizeOptions: ['10', '20', '50', '100'],
-        // 快速跳转至某页
-        showQuickJumper: true,
-        current: page.pageNum,
-        pageSize: page.pageSize,
-        total: page.total,
-        onChange: async (page, pageSize) => {
-            await onChangePagination(page, pageSize);
-        },
-    };
-
-    return (
-        <div className='knowledgeLibList'>
-            <div className='knowledgeLibList-table'>
-                <Table
-                    scroll={{ x: 'max-content' }}
-                    rowKey={(record) => record.createTime}
-                    loading={listLoading}
-                    columns={columns}
-                    dataSource={list}
-                    pagination={paginationConfig}
-                />
-            </div>
-            {
-                infoModalOpen &&
-                <InfoModal
-                    id={infoModalId}
-                    open={infoModalOpen}
-                    onClickConfirm={infoModalOnClickConfirm}
-                    onClickCancel={infoModalOnClickCancel}
-                    onClickClose={infoModalOnClickClose}
-                />
-            }
-            {
-                <Drawer
-                    title={drawerData.name}
-                    closable={{ 'aria-label': 'Close Button' }}
-                    onClose={() => { setDrawerFlag(false) }}
-                    width="80%"
-                    open={drawerFlag}
-                >
-                    {drawerFlag&&<PreviewModal isComponent={true} AuditAppId={drawerData.appId} />}
-                </Drawer> 
-            }        
-            <AuditHistory open={historyOpen} onClose={() => setHistoryOpen(false)} />     
-
-        </div>
-    );
-};
-
-export default observer(KnowledgeLibList);

+ 0 - 0
jk-rag-platform/src/pages/system/audit/style.less → jk-rag-platform/src/pages/system/audit/style.scss


+ 0 - 130
jk-rag-platform/src/pages/system/contentManagement/index.tsx.bak

@@ -1,130 +0,0 @@
-import * as React from 'react';
-
-import { Table, TableColumnsType, TablePaginationConfig, Modal } from 'antd';
-import { DownloadOutlined } from '@ant-design/icons';
-import Search from './components/Search';
-import dayjs from 'dayjs';
-import store from './store';
-import { Record } from './types';
-import './style.less';
-
-const DataExport: React.FC = () => {
-    const {
-        state,
-        onClickSearch,
-        onClickReset,
-        onChangePagination,
-        onClickDownload,
-        init,
-        reset
-    } = store;
-    const {
-        listLoading,
-        list,
-        page
-    } = state;
-
-    React.useEffect(() => {
-        init();
-        return () => reset();
-    }, []);
-
-    const columns: TableColumnsType<Record> = [
-        {
-            title: '模型分类',
-            dataIndex: 'index',
-            width: 80,
-            render: (text, record, index) => {
-                return index + 1;
-            }
-        },
-        {
-            title: '模型编码',
-            dataIndex: 'dialog_name',
-        },
-        {
-            title: '安全策略',
-            dataIndex: 'length',
-            render: (text) => {
-                return text + '条';
-            }
-        },
-        {
-            title: '更新时间',
-            dataIndex: 'create_time',
-            width: 200,
-            render: (text) => {
-                if (text) {
-                    return dayjs(text).format('YYYY-MM-DD HH:mm:ss');
-                } else {
-                    return '--';
-                }
-            }
-        },
-        {
-            title: '操作',
-            dataIndex: 'operation',
-            width: 80,
-            fixed: 'right',
-            render: (text, record) => {
-                return (
-                    <a
-                        onClick={() => {
-                            Modal.confirm({
-                                title: '导出',
-                                content: '确定导出Excel吗?',
-                                onOk: async () => {
-                                    await onClickDownload(record.id, record.dialog_name);
-                                }
-                            });
-                        }}
-                    >
-                        <DownloadOutlined />
-                    </a >
-                )
-            }
-        }
-    ];
-
-    const paginationConfig: TablePaginationConfig = {
-        // 显示数据总量
-        showTotal: (total: number) => {
-            return `共 ${total} 条`;
-        },
-        // 展示分页条数切换
-        showSizeChanger: true,
-        // 指定每页显示条数
-        pageSizeOptions: ['10', '20', '50', '100'],
-        // 快速跳转至某页
-        showQuickJumper: true,
-        current: page.pageNum,
-        pageSize: page.pageSize,
-        total: page.total,
-        onChange: async (page, pageSize) => {
-            await onChangePagination(page, pageSize);
-        },
-    };
-
-    return (
-        <div className='dataExport'>
-            <div className='dataExport-search'>
-                <Search
-                    onClickSearch={onClickSearch}
-                    onClickReset={onClickReset}
-                />
-            </div>
-            <div className='dataExport-table'>
-                <Table
-                    scroll={{ x: 'max-content' }}
-                    rowKey={(record) => record.id}
-                    loading={listLoading}
-                    columns={columns}
-                    dataSource={list}
-                    pagination={paginationConfig}
-                />
-            </div>
-        </div>
-    );
-};
-
-export default observer(DataExport);

+ 0 - 0
jk-rag-platform/src/pages/system/contentManagement/style.less → jk-rag-platform/src/pages/system/contentManagement/style.scss


+ 0 - 142
jk-rag-platform/src/pages/system/usageStatistics/index.tsx.bak

@@ -1,142 +0,0 @@
-import * as React from 'react';
-
-import { Table, TableColumnsType, TablePaginationConfig, Modal } from 'antd';
-import { DownloadOutlined } from '@ant-design/icons';
-import Search from './components/Search';
-import dayjs from 'dayjs';
-import store from './store';
-import { Record } from './types';
-import './style.less';
-
-const DataExport: React.FC = () => {
-    const {
-        state,
-        onClickSearch,
-        onClickReset,
-        onChangePagination,
-        onClickDownload,
-        init,
-        reset
-    } = store;
-    const {
-        listLoading,
-        list,
-        page
-    } = state;
-
-    React.useEffect(() => {
-        init();
-        return () => reset();
-    }, []);
-
-    const columns: TableColumnsType<Record> = [
-        {
-            title: '项目',
-            dataIndex: 'index',
-            width: 80,
-            render: (text, record, index) => {
-                return index + 1;
-            }
-        },
-        {
-            title: '更新时间',
-            dataIndex: 'dialog_name',
-        },
-        {
-            title: '已用空间',
-            dataIndex: 'length',
-            render: (text) => {
-                return text + '条';
-            }
-        },
-        {
-            title: '剩余',
-            dataIndex: 'create_time',
-            width: 200,
-            render: (text) => {
-                if (text) {
-                    return dayjs(text).format('YYYY-MM-DD HH:mm:ss');
-                } else {
-                    return '--';
-                }
-            }
-        },
-        {
-            title: '变化日志',
-            dataIndex: 'create_time',
-            width: 200,
-            render: (text) => {
-                if (text) {
-                    return dayjs(text).format('YYYY-MM-DD HH:mm:ss');
-                } else {
-                    return '--';
-                }
-            }
-        },
-        {
-            title: '操作',
-            dataIndex: 'operation',
-            width: 80,
-            fixed: 'right',
-            render: (text, record) => {
-                return (
-                    <a
-                        onClick={() => {
-                            Modal.confirm({
-                                title: '导出',
-                                content: '确定导出Excel吗?',
-                                onOk: async () => {
-                                    await onClickDownload(record.id, record.dialog_name);
-                                }
-                            });
-                        }}
-                    >
-                        <DownloadOutlined />
-                    </a >
-                )
-            }
-        }
-    ];
-
-    const paginationConfig: TablePaginationConfig = {
-        // 显示数据总量
-        showTotal: (total: number) => {
-            return `共 ${total} 条`;
-        },
-        // 展示分页条数切换
-        showSizeChanger: true,
-        // 指定每页显示条数
-        pageSizeOptions: ['10', '20', '50', '100'],
-        // 快速跳转至某页
-        showQuickJumper: true,
-        current: page.pageNum,
-        pageSize: page.pageSize,
-        total: page.total,
-        onChange: async (page, pageSize) => {
-            await onChangePagination(page, pageSize);
-        },
-    };
-
-    return (
-        <div className='dataExport'>
-            <div className='dataExport-search'>
-                <Search
-                    onClickSearch={onClickSearch}
-                    onClickReset={onClickReset}
-                />
-            </div>
-            <div className='dataExport-table'>
-                <Table
-                    scroll={{ x: 'max-content' }}
-                    rowKey={(record) => record.id}
-                    loading={listLoading}
-                    columns={columns}
-                    dataSource={list}
-                    pagination={paginationConfig}
-                />
-            </div>
-        </div>
-    );
-};
-
-export default observer(DataExport);

+ 0 - 0
jk-rag-platform/src/pages/system/usageStatistics/style.less → jk-rag-platform/src/pages/system/usageStatistics/style.scss


+ 0 - 0
jk-rag-platform/src/pages/universalChat/styles/index.less → jk-rag-platform/src/pages/universalChat/styles/index.scss


+ 0 - 198
jk-rag-platform/src/router.tsx.bak

@@ -1,198 +0,0 @@
-import React, { lazy, Suspense, useEffect } from 'react';
-
-import {
-    RouteObject,
-    Navigate,
-    createBrowserRouter,
-    useLocation,
-    RouterProvider,
-    useNavigate,
-    Routes,
-} from 'react-router-dom';
-import { Spin, Modal } from 'antd';
-import LocalStorage from '@/LocalStorage';
-import { apis, LoginApiParams } from '@/apis';
-import useRouteStore, { businessRoutes } from './store/route';
-import {
-    RobotOutlined,
-    ReadOutlined,
-    FileSearchOutlined,
-    MenuFoldOutlined,
-    MenuUnfoldOutlined,
-    CheckSquareOutlined
-} from '@ant-design/icons'
-// 按需加载
-const lazyLoad = (loader: () => Promise<any>) => {
-    const Component = lazy(loader);
-
-    const Loading: React.FC = () => {
-        return (
-            <div className='router-lazyLoad'>
-                <Spin />
-            </div>
-        )
-    }
-
-    return (
-        <Suspense fallback={<Loading />}>
-            <Component />
-        </Suspense>
-    );
-};
-
-// 定义登录地址常量
-const loginUrl = 'http://esc.sribs.com.cn:8080/esc-sso/oauth2.0/authorize?client_id=e97f94cf93761f4d69e8&response_type=code';
-// 公共路由
-const commonRoutes: RouteObject[] = [
-    {   /* 登录 */
-        path: '/login',
-        element: lazyLoad(() => import('@/pages/login/index'))
-    },
-    {   /* 测试 */
-        path: '/help',
-        element: lazyLoad(() => import('@/pages/help/index'))
-    },
-    {   /* 404 */
-        path: '/404',
-        element: lazyLoad(() => import('@/components/404/index')),
-    },
-    {   /* 路由不存在重定向404 */
-        path: '/*',
-        element: <Navigate to='/404' replace={true} />,
-    }
-]
-const AppRouter: React.FC = () => {
-    const {
-        state
-    } = useRouteStore;
-    useEffect(() => {
-        // console.log('you----routerList',routerList)
-    }, [state.constantRoutes])
-    // React-Router-Dom@v6 路由表---业务路由
-    const routerList: RouteObject[] = [
-        {
-            path: '/',
-            element: lazyLoad(() => import('@/pages/layout/index')),
-
-            children: [
-                {
-                    // index: true,
-                        // element: lazyLoad(() => import('@/pages/deepseek/questionAnswer/appPlazaList/index')),
-                    //     handle: {
-                    //         breadcrumbName: '',
-                    //     }
-                    index: true,
-                    element: <Navigate to="/appCenter" replace={true} />
-                },
-                // {   /* 系统管理 */
-                //     path: '/deepseek/dataExport',
-                //     handle: {
-                //         hidden: false,
-                //         breadcrumbName: '系统管理',
-                //         icon: <RobotOutlined />,
-                //     },
-                //     element: lazyLoad(() => import('@/pages/deepseek/dataExport/index')),
-                // },
-                ...state.constantRoutes,
-            ]
-        },
-    ]
-    // 路由模式-浏览器路由
-    const router = createBrowserRouter([...routerList, ...commonRoutes,]);
-
-    // 路由白名单
-    const whiteList = ['/login'];
-
-    // 前置路由
-    router.routes.forEach((route: any) => {
-        interface RouterComponentProps {
-            component?: React.ReactNode,
-        }
-        // 路由组件-路由鉴权
-        function RouterComponent(props: RouterComponentProps) {
-            // console.log('RouterComponent props---- 进来了 多少次',props);
-            const { component } = props;
-            const location = useLocation();
-            const path = location.pathname;
-
-            const originUrl = window.location.origin;
-            const fullUrl = window.location.href;
-            const urlParams = new URLSearchParams(new URL(fullUrl).search);
-            const code = urlParams.get('code');
-            const state = urlParams.get('state');
-            const userInfo = localStorage.getItem('userInfo');
-            const token = urlParams.get('token');
-            if (token) {// 通过token登陆
-                const checkToken = async (token: string) => {
-                    try {
-                        const res = await apis.checkToken(token);
-                        if (res.data.status) {
-                            localStorage.setItem('token', token);
-                            LocalStorage.setPermissions(res.permissions);
-                            LocalStorage.setRoles(res.roles);
-                            LocalStorage.setUserInfo({
-                                id: res.user.userId,
-                                name: res.user.nickName,
-                                tenantId: res?.user?.tenantId,
-                            });
-                            window.location.replace(originUrl + path);
-                        }
-                    } catch (error: any) {
-                        Modal.error({
-                            title: 'Error',
-                            content: 'token验证失败',
-                        })
-                    }
-                    return <Navigate to='/login' replace={true} />
-                }
-                checkToken(token);
-            } else if (LocalStorage.getToken()) {// 已登录
-                return <>{component}</>
-            } else {// 未登录
-
-                const jkLogin = async (data: { code: string, redirectUrl: string }, url: string) => {
-                    try {
-                        const res = await apis.jklogin(data);
-                        console.log(res.data, "res.data");
-                        localStorage.setItem('token', res.data.token);
-                        window.location.replace(url);
-                    } catch (error: any) {
-                        Modal.error({
-                            title: '登录失败',
-                            content: error.msg,
-                        })
-                    }
-                }
-
-                if (fullUrl.includes(originUrl + '/?code') && code && state) {// 通过code登陆
-                    if (!userInfo) {
-                        jkLogin({ code: code, redirectUrl: encodeURIComponent(originUrl) }, state);
-                    }
-                }
-                // else {
-                //     //测试环境
-                //     //const loginUrl = 'https://esctest.sribs.com.cn/esc-sso/oauth2.0/authorize?client_id=e97f94cf93761f4d69e8&response_type=code';
-                //     //生产环境
-                //     const loginUrl = 'http://esc.sribs.com.cn:8080/esc-sso/oauth2.0/authorize?client_id=e97f94cf93761f4d69e8&response_type=code';
-                //     const externalLoginUrl = loginUrl + `&redirect_uri=${encodeURIComponent(originUrl)}&state=${encodeURIComponent(fullUrl)}`;
-                //     if (!userInfo) {
-                //         window.location.replace(externalLoginUrl);
-                //     }
-                // }
-                else {// 未登录
-                    if (whiteList.includes(path)) {
-                        return <>{component}</>
-                    } else {
-                        return <Navigate to='/login' replace={true} />
-                    }
-                }
-
-            }
-        }
-        route.element = <RouterComponent component={route.element} />
-    });
-    return (
-        <RouterProvider router={router} />
-    )
-}
-export default AppRouter;

+ 139 - 181
jk-rag-platform/src/styles/README.md

@@ -10,32 +10,32 @@
 
 ```
 src/styles/
-├── variables.less    # LESS/CSS 变量(统一文件)
-└── global.less       # 全局样式入口
+├── variables.scss    # SCSS/CSS 变量(统一文件)
+└── global.scss       # 全局样式入口
 ```
 
 ---
 
 ## 🚀 快速开始
 
-### 方式 1: LESS 变量(推荐)
+### 方式 1: SCSS 变量(推荐)
 
-**在 LESS 文件顶部导入**:
-```less
-@import '@/styles/variables.less';
+**在 SCSS 文件顶部导入**:
+```scss
+@import '@/styles/variables.scss';
 
 // 使用变量
 .my-component {
-    color: @text-primary;
-    padding: @spacing-4;
-    border-radius: @radius-lg;
+    color: $text-primary;
+    padding: $spacing-4;
+    border-radius: $radius-lg;
 }
 ```
 
 ### 方式 2: CSS 变量
 
-**在 CSS/LESS 文件中使用**:
-```css
+**在 SCSS/CSS 文件中使用**:
+```scss
 .my-component {
     color: var(--text-primary);
     padding: var(--spacing-4);
@@ -50,7 +50,7 @@ const primaryColor = getComputedStyle(
 ).getPropertyValue('--color-primary');
 ```
 
-> **注意**: CSS 变量已定义在 `variables.less` 的 `:root {}` 中,无需单独导入 CSS 文件。
+> **注意**: CSS 变量已定义在 `variables.scss` 的 `:root {}` 中,无需单独导入 CSS 文件。
 
 ---
 
@@ -58,86 +58,86 @@ const primaryColor = getComputedStyle(
 
 ### 色彩系统
 
-```less
+```scss
 // 主色调
-@primary-color: #1D4ED8;
-@primary-light: #3B82F6;
-@primary-dark: #1E40AF;
+$primary-color: #1D4ED8;
+$primary-light: #3B82F6;
+$primary-dark: #1E40AF;
 
 // 辅助色
-@success-color: #10B981;
-@warning-color: #F59E0B;
-@error-color: #EF4444;
-@info-color: #3B82F6;
+$success-color: #10B981;
+$warning-color: #F59E0B;
+$error-color: #EF4444;
+$info-color: #3B82F6;
 
 // 中性色
-@text-primary: #1F2937;
-@text-secondary: #6B7280;
-@text-hint: #9CA3AF;
-@bg-primary: #F9FAFB;
-@bg-secondary: #FFFFFF;
-@border-base: #E5E7EB;
+$text-primary: #1F2937;
+$text-secondary: #6B7280;
+$text-hint: #9CA3AF;
+$bg-primary: #F9FAFB;
+$bg-secondary: #FFFFFF;
+$border-base: #E5E7EB;
 ```
 
 ### 间距系统
 
-```less
+```scss
 // 4px 基准
-@spacing-1: 4px;
-@spacing-2: 8px;
-@spacing-3: 12px;
-@spacing-4: 16px;
-@spacing-6: 24px;
-@spacing-8: 32px;
+$spacing-1: 4px;
+$spacing-2: 8px;
+$spacing-3: 12px;
+$spacing-4: 16px;
+$spacing-6: 24px;
+$spacing-8: 32px;
 ```
 
 ### 圆角系统
 
-```less
-@radius-sm: 4px;
-@radius-md: 6px;
-@radius-lg: 8px;
-@radius-xl: 12px;
-@radius-full: 9999px;
+```scss
+$radius-sm: 4px;
+$radius-md: 6px;
+$radius-lg: 8px;
+$radius-xl: 12px;
+$radius-full: 9999px;
 ```
 
 ### 阴影系统
 
-```less
-@shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
-@shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
-@shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
-@shadow-glow: 0 0 20px rgba(59, 130, 246, 0.3);
+```scss
+$shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
+$shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
+$shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
+$shadow-glow: 0 0 20px rgba(59, 130, 246, 0.3);
 ```
 
 ### 字体系统
 
-```less
+```scss
 // 字体大小
-@font-xs: 9px;
-@font-sm: 12px;
-@font-base: 13px;
-@font-lg: 15px;
-@font-xl: 16px;
+$font-xs: 9px;
+$font-sm: 12px;
+$font-base: 13px;
+$font-lg: 15px;
+$font-xl: 16px;
 
 // 字重
-@font-weight-medium: 500;
-@font-weight-semibold: 600;
+$font-weight-medium: 500;
+$font-weight-semibold: 600;
 ```
 
 ### 动画系统
 
-```less
+```scss
 // 时长
-@animation-fast: 0.1s;
-@animation-base: 0.2s;
-@animation-slow: 0.3s;
+$animation-fast: 0.1s;
+$animation-base: 0.2s;
+$animation-slow: 0.3s;
 
 // 缓动
-@easing-default: cubic-bezier(0.4, 0, 0.2, 1);
+$easing-default: cubic-bezier(0.4, 0, 0.2, 1);
 
 // 快捷过渡
-@transition-base: all @animation-base @easing-default;
+$transition-base: all $animation-base $easing-default;
 ```
 
 ---
@@ -147,95 +147,53 @@ const primaryColor = getComputedStyle(
 ### 步骤 1: 导入变量文件
 
 **在文件顶部添加**:
-```less
-@import '@/styles/variables.less';
+```scss
+@import '@/styles/variables.scss';
 ```
 
 ### 步骤 2: 替换硬编码值
 
 #### 色彩替换
 
-```less
-// ❌ 旧代码
-color: #1D4ED8;
-background: #FFFFFF;
-border: 1px solid #E5E7EB;
-
-// ✅ 新代码
+```scss
+// ❌ 旧代码 (LESS)
 color: @primary-color;
-background: @bg-secondary;
-border: 1px solid @border-base;
+
+// ✅ 新代码 (SCSS)
+color: $text-primary;
 ```
 
 #### 间距替换
 
-```less
-// ❌ 旧代码
-padding: 16px;
-margin: 24px;
-gap: 8px;
-
-// ✅ 新代码
+```scss
+// ❌ 旧代码 (LESS)
 padding: @spacing-4;
-margin: @spacing-6;
-gap: @spacing-2;
-```
-
-#### 圆角替换
-
-```less
-// ❌ 旧代码
-border-radius: 12px;
-border-radius: 4px;
-
-// ✅ 新代码
-border-radius: @radius-xl;
-border-radius: @radius-sm;
-```
-
-#### 字体替换
-
-```less
-// ❌ 旧代码
-font-size: 15px;
-font-weight: 600;
-
-// ✅ 新代码
-font-size: @font-lg;
-font-weight: @font-weight-semibold;
-```
 
-#### 阴影替换
-
-```less
-// ❌ 旧代码
-box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
-
-// ✅ 新代码
-box-shadow: @shadow-lg;
+// ✅ 新代码 (SCSS)
+padding: $spacing-4;
 ```
 
 ### 步骤 3: 使用 Mixins(可选)
 
-```less
+```scss
 // 文字截断
 .my-text {
-    .text-truncate();
+    @include text-truncate();
 }
 
 // 多行文字截断
 .my-description {
-    .text-clamp(2);  // 最多 2 行
+    @include text-clamp(2);  // 最多 2 行
 }
 
 // 居中
 .my-center {
-    .center-flex();
+    @include center-flex();
 }
 
 // 卡片阴影
 .my-card {
-    .shadow-card();
+    @include shadow-card();
 }
 ```
 
@@ -245,29 +203,8 @@ box-shadow: @shadow-lg;
 
 ### 示例 1: 按钮组件
 
-**迁移前**:
+**迁移前 (LESS)**:
 ```less
-.btn {
-    padding: 8px 16px;
-    background: #1D4ED8;
-    color: #FFFFFF;
-    border-radius: 6px;
-    font-size: 14px;
-    font-weight: 500;
-    transition: all 0.2s ease;
-    
-    &:hover {
-        background: #3B82F6;
-        transform: translateY(-2px);
-        box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
-    }
-}
-```
-
-**迁移后**:
-```less
-@import '@/styles/variables.less';
-
 .btn {
     padding: @spacing-2 @spacing-4;
     background: @primary-color;
@@ -276,7 +213,7 @@ box-shadow: @shadow-lg;
     font-size: @font-md;
     font-weight: @font-weight-medium;
     transition: @transition-base;
-    
+
     &:hover {
         background: @primary-light;
         transform: translateY(-@spacing-1);
@@ -285,47 +222,68 @@ box-shadow: @shadow-lg;
 }
 ```
 
-### 示例 2: 卡片组件
+**迁移后 (SCSS)**:
+```scss
+@import '@/styles/variables.scss';
+
+.btn {
+    padding: $spacing-2 $spacing-4;
+    background: $primary-color;
+    color: #FFFFFF;
+    border-radius: $radius-md;
+    font-size: $font-md;
+    font-weight: $font-weight-medium;
+    transition: $transition-base;
 
-**迁移前**:
-```less
-.card {
-    padding: 20px;
-    background: #FFFFFF;
-    border-radius: 16px;
-    border: 1px solid #F3F4F6;
-    
     &:hover {
-        box-shadow: 0 20px 25px rgba(0, 0, 0, 0.1);
+        background: $primary-light;
+        transform: translateY(-$spacing-1);
+        box-shadow: $shadow-md;
     }
 }
 ```
 
-**迁移后**:
-```less
-@import '@/styles/variables.less';
+### 示例 2: 卡片组件
 
+**迁移前 (LESS)**:
+```less
 .card {
     padding: @spacing-5;
     background: @bg-secondary;
     border-radius: @radius-2xl;
     border: 1px solid @border-light;
-    
+
     &:hover {
         box-shadow: @shadow-xl;
     }
 }
 ```
 
+**迁移后 (SCSS)**:
+```scss
+@import '@/styles/variables.scss';
+
+.card {
+    padding: $spacing-5;
+    background: $bg-secondary;
+    border-radius: $radius-2xl;
+    border: 1px solid $border-light;
+
+    &:hover {
+        box-shadow: $shadow-xl;
+    }
+}
+```
+
 ---
 
 ## 🎯 最佳实践
 
 ### 1. 始终使用变量
 
-```less
+```scss
 // ✅ 好
-color: @text-primary;
+color: $text-primary;
 
 // ❌ 不好
 color: #1F2937;
@@ -333,9 +291,9 @@ color: #1F2937;
 
 ### 2. 使用语义化命名
 
-```less
+```scss
 // ✅ 好
-background: @bg-active;
+background: $bg-active;
 
 // ❌ 不好
 background: rgba(29, 78, 216, 0.1);
@@ -343,9 +301,9 @@ background: rgba(29, 78, 216, 0.1);
 
 ### 3. 使用间距系统
 
-```less
+```scss
 // ✅ 好
-margin: @spacing-4;
+margin: $spacing-4;
 
 // ❌ 不好
 margin: 16px;
@@ -353,9 +311,9 @@ margin: 16px;
 
 ### 4. 使用快捷 Mixins
 
-```less
+```scss
 // ✅ 好
-.text-truncate();
+@include text-truncate();
 
 // ❌ 不好
 overflow: hidden;
@@ -369,7 +327,7 @@ white-space: nowrap;
 
 ### 文件级别
 
-- [ ] 导入 `variables.less`
+- [ ] 导入 `variables.scss`
 - [ ] 移除硬编码颜色
 - [ ] 移除硬编码间距
 - [ ] 移除硬编码圆角
@@ -378,12 +336,12 @@ white-space: nowrap;
 
 ### 组件级别
 
-- [ ] 按钮使用 `@primary-color`
-- [ ] 卡片使用 `@bg-secondary`
-- [ ] 文字使用 `@text-primary` / `@text-secondary`
-- [ ] 边框使用 `@border-base`
-- [ ] 间距使用 `@spacing-*`
-- [ ] 圆角使用 `@radius-*`
+- [ ] 按钮使用 `$primary-color`
+- [ ] 卡片使用 `$bg-secondary`
+- [ ] 文字使用 `$text-primary` / `$text-secondary`
+- [ ] 边框使用 `$border-base`
+- [ ] 间距使用 `$spacing-*`
+- [ ] 圆角使用 `$radius-*`
 
 ---
 
@@ -394,41 +352,41 @@ white-space: nowrap;
 **检查**:
 1. 是否正确导入文件
 2. 导入路径是否正确
-3. 是否在 LESS 文件中使用
+3. 是否在 SCSS 文件中使用
 
 **解决**:
-```less
+```scss
 // 确保在文件顶部导入
-@import '@/styles/variables.less';
+@import '@/styles/variables.scss';
 ```
 
 ### Q2: 如何覆盖变量?
 
 **方法**:
-```less
+```scss
 // 在导入后覆盖
-@import '@/styles/variables.less';
-@primary-color: #FF0000;  // 覆盖主色
+@import '@/styles/variables.scss';
+$primary-color: #FF0000;  // 覆盖主色
 ```
 
-### Q3: CSS 变量和 LESS 变量选哪个?
+### Q3: CSS 变量和 SCSS 变量选哪个?
 
 **推荐**:
-- LESS 变量:编译时优化,IDE 支持好 ✅
+- SCSS 变量:编译时优化,IDE 支持好 ✅
 - CSS 变量:运行时动态修改,JS 可访问
 
-**建议**: 优先使用 LESS 变量,需要 JS 访问时使用 CSS 变量。
+**建议**: 优先使用 SCSS 变量,需要 JS 访问时使用 CSS 变量。
 
 ---
 
 ## 📄 相关文档
 
 1. `建科小智设计规范_v3.md` - 完整设计规范
-2. `variables.less` - LESS/CSS 变量文件
-3. `global.less` - 全局样式
+2. `variables.scss` - SCSS/CSS 变量文件
+3. `global.scss` - 全局样式
 
 ---
 
 **文档维护**: AI Assistant
-**最后更新**: 2024-01-19
-**版本**: v3.0
+**最后更新**: 2026-03-28
+**版本**: v4.0 (SCSS)

+ 168 - 168
jk-rag-platform/src/styles/global.less → jk-rag-platform/src/styles/global.scss

@@ -1,4 +1,4 @@
-@import './variables.less';
+@import './variables.scss';
 
 // ===== 隐藏式滚动条(全局) =====
 // 适用于所有内容区域,保持界面简洁
@@ -26,7 +26,7 @@ body {
     padding: 0;
     margin: 0;
     /* 不设置 overflow: hidden,允许页面滚动 */
-    background: @bg-primary; /* 设置全局背景色,避免滚动时出现白色空白 */
+    background: $bg-primary; /* 设置全局背景色,避免滚动时出现白色空白 */
     
     // 隐藏根元素滚动条
     scrollbar-width: none; // Firefox
@@ -41,13 +41,13 @@ body {
     height: 100%;
     width: 100%;
     /* 不设置 overflow: hidden,允许内容区域滚动 */
-    font-size: @font-size-base;
-    color: @theme-text-color !important;
+    font-size: $font-size-base;
+    color: $theme-text-color !important;
     // Ant-Design 主题 - 属性
-    --primary-color: @primary-color;
-    --text-color: @theme-text-color;
-    --border-radius: @border-radius-base;
-    background: @bg-primary; /* 设置背景色 */
+    --primary-color: $primary-color;
+    --text-color: $theme-text-color;
+    --border-radius: $border-radius-base;
+    background: $bg-primary; /* 设置背景色 */
     
     // 隐藏滚动条
     scrollbar-width: none; // Firefox
@@ -66,8 +66,8 @@ a,
 a:hover,
 a:active {
     cursor: pointer;
-    font-size: @font-size-base;
-    color: @primary-color;
+    font-size: $font-size-base;
+    color: $primary-color;
     text-decoration: none;
 }
 
@@ -83,19 +83,19 @@ ul li {
 // ==================== 全局工具类 ====================
 
 .text-primary {
-    color: @primary-color !important;
+    color: $primary-color !important;
 }
 
 .text-success {
-    color: @success-color !important;
+    color: $success-color !important;
 }
 
 .text-warning {
-    color: @warning-color !important;
+    color: $warning-color !important;
 }
 
 .text-error {
-    color: @error-color !important;
+    color: $error-color !important;
 }
 
 // 路由加载样式
@@ -117,74 +117,74 @@ ul li {
     }
 
     h1 {
-        font-size: @font-4xl;  // 20px
-        color: @text-primary;
+        font-size: $font-4xl;  // 20px
+        color: $text-primary;
         margin: 2px 0;
-        line-height: @line-height-tight;  // 1.1
+        line-height: $line-height-tight;  // 1.1
     }
 
     h2 {
-        font-size: @font-3xl;  // 18px
-        color: @text-primary;
+        font-size: $font-3xl;  // 18px
+        color: $text-primary;
         margin: 2px 0;
-        line-height: @line-height-tight;  // 1.1
+        line-height: $line-height-tight;  // 1.1
     }
 
     h3 {
-        font-size: @font-2xl;  // 16px
-        color: @text-primary;
+        font-size: $font-2xl;  // 16px
+        color: $text-primary;
         margin: 2px 0;
-        line-height: @line-height-tight;  // 1.1
+        line-height: $line-height-tight;  // 1.1
     }
 
     h4 {
-        font-size: @font-lg;  // 14px
-        color: @text-primary;
+        font-size: $font-lg;  // 14px
+        color: $text-primary;
         margin: 2px 0;
-        line-height: @line-height-tight;  // 1.1
+        line-height: $line-height-tight;  // 1.1
     }
 
     h5, h6 {
-        font-size: @font-base;  // 12px
-        color: @text-primary;
+        font-size: $font-base;  // 12px
+        color: $text-primary;
         margin: 2px 0;
-        line-height: @line-height-tight;  // 1.1
+        line-height: $line-height-tight;  // 1.1
     }
 
     p {
-        font-size: @font-base;
-        color: @text-secondary;
-        line-height: @line-height-base;  // 1.3
+        font-size: $font-base;
+        color: $text-secondary;
+        line-height: $line-height-base;  // 1.3
     }
 }
 
 // Ant Design typography 样式
 .ant-typography {
     h1 {
-        font-size: @font-4xl;  // 20px
-        color: @text-primary;
+        font-size: $font-4xl;  // 20px
+        color: $text-primary;
         margin: 2px 0;
-        line-height: @line-height-tight;  // 1.1
+        line-height: $line-height-tight;  // 1.1
     }
 
     h2 {
-        font-size: @font-3xl;  // 18px
-        color: @text-primary;
+        font-size: $font-3xl;  // 18px
+        color: $text-primary;
         margin: 2px 0;
-        line-height: @line-height-tight;  // 1.1
+        line-height: $line-height-tight;  // 1.1
     }
 
     h3 {
-        font-size: @font-2xl;  // 16px
-        color: @text-primary;
+        font-size: $font-2xl;  // 16px
+        color: $text-primary;
         margin: 2px 0;
-        line-height: @line-height-tight;  // 1.1
+        line-height: $line-height-tight;  // 1.1
     }
 
     p {
-        font-size: @font-base;
-        color: @text-secondary;
-        line-height: @line-height-base;  // 1.3
+        font-size: $font-base;
+        color: $text-secondary;
+        line-height: $line-height-base;  // 1.3
     }
 }
 
@@ -201,37 +201,37 @@ ul li {
 
 // 全局按钮样式
 .ant-btn-primary {
-    background: @primary-color;
-    border: 1px solid @primary-color;
+    background: $primary-color;
+    border: 1px solid $primary-color;
     color: #ffffff;
-    transition: @transition-base;
-    box-shadow: @shadow-sm;
+    transition: $transition-base;
+    box-shadow: $shadow-sm;
     
     &:hover {
-        background: @primary-light;
-        border-color: @primary-light;
+        background: $primary-light;
+        border-color: $primary-light;
         color: #ffffff;
     }
     
     &:active {
-        background: @primary-dark;
-        border-color: @primary-dark;
+        background: $primary-dark;
+        border-color: $primary-dark;
         color: #ffffff;
         box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
         transform: translateY(0);
     }
     
     &:focus {
-        background: @primary-color;
-        border-color: @primary-color;
+        background: $primary-color;
+        border-color: $primary-color;
         color: #ffffff;
-        box-shadow: 0 0 0 2px rgba(@primary-color, 0.2);
+        box-shadow: 0 0 0 2px rgba($primary-color, 0.2);
     }
     
     &:disabled {
-        background: @bg-tertiary;
-        border-color: @border-base;
-        color: @text-disabled;
+        background: $bg-tertiary;
+        border-color: $border-base;
+        color: $text-disabled;
         box-shadow: none;
         transform: none;
     }
@@ -239,53 +239,53 @@ ul li {
 
 // ==================== 排序按钮组 ====================
 .sort-buttons {
-    border: 1px solid @border-base;
-    border-radius: @radius-xl;
+    border: 1px solid $border-base;
+    border-radius: $radius-xl;
     overflow: hidden;
     
     .sort-btn {
-        padding: @spacing-2 @spacing-4;
-        background: @bg-secondary;
+        padding: $spacing-2 $spacing-4;
+        background: $bg-secondary;
         border: none;
-        border-right: 1px solid @border-base;
-        font-size: @font-md;
-        font-weight: @font-weight-medium;
-        color: @text-secondary;
+        border-right: 1px solid $border-base;
+        font-size: $font-md;
+        font-weight: $font-weight-medium;
+        color: $text-secondary;
         cursor: pointer;
-        transition: @transition-base;
+        transition: $transition-base;
         
         &:last-child {
             border-right: none;
         }
         
         &:hover {
-            background: @bg-hover;
-            color: @text-primary;
+            background: $bg-hover;
+            color: $text-primary;
         }
         
         &.active {
-            background: @icon-bg-blue;
-            color: @primary-color;
+            background: $icon-bg-blue;
+            color: $primary-color;
             font-weight: @font-weight-semibold;
         }
     }
 }
 
 .filter-btn {
-    width: @search-height;
-    height: @search-height;
-    border: 1px solid lighten(@primary-color, 20%);
-    background: @icon-bg-blue;
-    border-radius: @radius-xl;
+    width: $search-height;
+    height: $search-height;
+    border: 1px solid lighten($primary-color, 20%);
+    background: $icon-bg-blue;
+    border-radius: $radius-xl;
     display: flex;
     align-items: center;
     justify-content: center;
     cursor: pointer;
-    transition: @transition-base;
+    transition: $transition-base;
     
     .iconify {
-        font-size: @icon-lg;
-        color: @primary-color;
+        font-size: $icon-lg;
+        color: $primary-color;
     }
 }
 
@@ -293,22 +293,22 @@ ul li {
 
 // 页面容器(仅控制布局,不覆盖组件)
 .page-container {
-    padding: @spacing-4 @spacing-6;
-    min-height: calc(100vh - @header-height);
-    background: @bg-primary;
+    padding: $spacing-4 $spacing-6;
+    min-height: calc(100vh - $header-height);
+    background: $bg-primary;
 }
 
 // 内容区块(通用容器)
 .content-section {
-    margin-bottom: @spacing-4;  // 16px - 紧凑间距
-    padding: @spacing-3;
-    background: @bg-secondary;
-    border-radius: @radius-lg;
+    margin-bottom: $spacing-4;  // 16px - 紧凑间距
+    padding: $spacing-3;
+    background: $bg-secondary;
+    border-radius: $radius-lg;
 }
 
 // 区块间距
 .section-gap {
-    margin-bottom: @spacing-5;
+    margin-bottom: $spacing-5;
 }
 
 // ==================== 列表头部 ====================
@@ -318,9 +318,9 @@ ul li {
     display: flex;
     justify-content: space-between;
     align-items: flex-end;
-    margin-bottom: @spacing-4;
-    padding-bottom: @spacing-3;
-    border-bottom: 1px solid @border-base;
+    margin-bottom: $spacing-4;
+    padding-bottom: $spacing-3;
+    border-bottom: 1px solid $border-base;
     
     // 当下方紧跟 GuideTips 时,移除底部间距
     & + .guide-tips-wrapper {
@@ -329,8 +329,8 @@ ul li {
     
     // 使用 GuideTips 时的样式
     &.with-tips {
-        margin-bottom: @spacing-3;  // 12px - 与 GuideTips 的间距(统一视觉间距)
-        padding-bottom: @spacing-3; // 12px
+        margin-bottom: $spacing-3;  // 12px - 与 GuideTips 的间距(统一视觉间距)
+        padding-bottom: $spacing-3; // 12px
     }
     
     // 带返回按钮的标题栏
@@ -340,7 +340,7 @@ ul li {
         .list-header-title {
             display: flex;
             align-items: center;
-            gap: @spacing-1;
+            gap: $spacing-1;
             
             .back-btn {
                 display: flex;
@@ -351,13 +351,13 @@ ul li {
                 padding: 0;
                 background: transparent;
                 border: none;
-                color: @text-secondary;
+                color: $text-secondary;
                 cursor: pointer;
                 transition: all 0.2s ease;
                 flex-shrink: 0;
                 
                 &:hover {
-                    color: @primary-color;
+                    color: $primary-color;
                     
                     .anticon {
                         font-weight: 700;
@@ -374,55 +374,55 @@ ul li {
             
             h1, h3 {
                 margin: 0;
-                line-height: @line-height-tight;  // 1.1
+                line-height: $line-height-tight;  // 1.1
             }
         }
     }
     
     &-title {
         h1, h3 {
-            font-size: @font-3xl;
-            font-weight: @font-weight-bold;
-            color: @text-primary;
-            margin: 0 0 @spacing-1 0;
-            line-height: @line-height-tight;  // 1.1
+            font-size: $font-3xl;
+            font-weight: $font-weight-bold;
+            color: $text-primary;
+            margin: 0 0 $spacing-1 0;
+            line-height: $line-height-tight;  // 1.1
         }
 
         p {
-            font-size: @font-sm;
-            color: @text-secondary;
+            font-size: $font-sm;
+            color: $text-secondary;
             margin: 0;
-            line-height: @line-height-base;  // 1.3
+            line-height: $line-height-base;  // 1.3
         }
     }
     
     &-actions {
         display: flex;
-        gap: @spacing-3;
+        gap: $spacing-3;
         align-items: center;
         
         .create-btn {
             display: flex;
             align-items: center;
-            gap: @spacing-2;
-            padding: @spacing-2 @spacing-4;
-            background: @primary-color;
+            gap: $spacing-2;
+            padding: $spacing-2 $spacing-4;
+            background: $primary-color;
             color: #fff;
             border: none;
-            border-radius: @radius-lg;
-            font-size: @font-md;
-            font-weight: @font-weight-medium;
+            border-radius: $radius-lg;
+            font-size: $font-md;
+            font-weight: $font-weight-medium;
             cursor: pointer;
-            transition: @transition-base;
+            transition: $transition-base;
             
             &:hover {
-                background: @primary-light;
-                transform: translateY(-@spacing-1);
-                box-shadow: @shadow-md;
+                background: $primary-light;
+                transform: translateY(-$spacing-1);
+                box-shadow: $shadow-md;
             }
             
             .iconify {
-                font-size: @icon-xl;
+                font-size: $icon-xl;
             }
         }
     }
@@ -434,10 +434,10 @@ ul li {
 .sub-list-header {
     display: flex;
     align-items: center;
-    gap: @spacing-4;
-    margin-bottom: @spacing-4;  // 16px - 紧凑间距
-    padding-bottom: @spacing-3;
-    border-bottom: 1px solid @border-base;
+    gap: $spacing-4;
+    margin-bottom: $spacing-4;  // 16px - 紧凑间距
+    padding-bottom: $spacing-3;
+    border-bottom: 1px solid $border-base;
     
     .back-btn {
         display: flex;
@@ -445,33 +445,33 @@ ul li {
         justify-content: center;
         width: 36px;
         height: 36px;
-        border-radius: @radius-lg;
-        background: @bg-tertiary;
-        border: 1px solid @border-base;
-        color: @text-secondary;
+        border-radius: $radius-lg;
+        background: $bg-tertiary;
+        border: 1px solid $border-base;
+        color: $text-secondary;
         cursor: pointer;
-        transition: @transition-base;
+        transition: $transition-base;
         flex-shrink: 0;
         
         &:hover {
-            background: @border-base;
-            color: @primary-color;
-            transform: translateX(-@spacing-1);
+            background: $border-base;
+            color: $primary-color;
+            transform: translateX(-$spacing-1);
         }
         
         .iconify {
-            font-size: @icon-lg;
+            font-size: $icon-lg;
         }
     }
     
     .sub-list-title {
         h1, h2 {
-            font-size: @font-3xl; // 20px
-            font-weight: @font-weight-bold;
-            color: @text-primary;
+            font-size: $font-3xl; // 20px
+            font-weight: $font-weight-bold;
+            color: $text-primary;
             margin: 0;
             white-space: nowrap;
-            line-height: @line-height-tight;  // 1.1
+            line-height: $line-height-tight;  // 1.1
         }
     }
 }
@@ -480,30 +480,30 @@ ul li {
 
 .create-action-wrapper {
     text-align: right;
-    margin-bottom: @spacing-4; // 紧凑一级:24px → 16px
+    margin-bottom: $spacing-4; // 紧凑一级:24px → 16px
     
     .create-action-btn {
         display: inline-flex;
         align-items: center;
-        gap: @spacing-2;
-        padding: @spacing-2 @spacing-4;
-        background: @primary-color;
+        gap: $spacing-2;
+        padding: $spacing-2 $spacing-4;
+        background: $primary-color;
         color: #fff;
         border: none;
-        border-radius: @radius-lg;
-        font-size: @font-md;
-        font-weight: @font-weight-medium;
+        border-radius: $radius-lg;
+        font-size: $font-md;
+        font-weight: $font-weight-medium;
         cursor: pointer;
-        transition: @transition-base;
+        transition: $transition-base;
         
         &:hover {
-            background: @primary-light;
-            transform: translateY(-@spacing-1);
-            box-shadow: @shadow-md;
+            background: $primary-light;
+            transform: translateY(-$spacing-1);
+            box-shadow: $shadow-md;
         }
         
         .iconify {
-            font-size: @icon-xl;
+            font-size: $icon-xl;
         }
     }
 }
@@ -513,17 +513,17 @@ ul li {
 .app-card-grid {
     display: grid;
     grid-template-columns: repeat(4, 1fr);
-    gap: @spacing-4; // 紧凑一级:24px → 16px
+    gap: $spacing-4; // 紧凑一级:24px → 16px
     
-    @media (max-width: @screen-xl) {
+    @media (max-width: $screen-xl) {
         grid-template-columns: repeat(3, 1fr);
     }
     
-    @media (max-width: @screen-lg) {
+    @media (max-width: $screen-lg) {
         grid-template-columns: repeat(2, 1fr);
     }
     
-    @media (max-width: @screen-md) {
+    @media (max-width: $screen-md) {
         grid-template-columns: 1fr;
     }
 }
@@ -531,13 +531,13 @@ ul li {
 .knowledge-card-grid {
     display: grid;
     grid-template-columns: repeat(3, 1fr);
-    gap: @spacing-4; // 紧凑一级:24px → 16px
+    gap: $spacing-4; // 紧凑一级:24px → 16px
     
-    @media (max-width: @screen-lg) {
+    @media (max-width: $screen-lg) {
         grid-template-columns: repeat(2, 1fr);
     }
     
-    @media (max-width: @screen-md) {
+    @media (max-width: $screen-md) {
         grid-template-columns: 1fr;
     }
 }
@@ -545,24 +545,24 @@ ul li {
 // ==================== 分页器 ====================
 
 .pagination-container {
-    padding: @spacing-4 0;
+    padding: $spacing-4 0;
     display: flex;
     justify-content: flex-end;
     
     .ant-pagination {
         .ant-pagination-item {
-            border-radius: @radius-lg;
-            border-color: @border-base;
-            transition: @transition-base;
+            border-radius: $radius-lg;
+            border-color: $border-base;
+            transition: $transition-base;
             
             &:hover {
-                border-color: @primary-color;
-                transform: translateY(-@spacing-1);
+                border-color: $primary-color;
+                transform: translateY(-$spacing-1);
             }
             
             &.ant-pagination-item-active {
-                background: @primary-color;
-                border-color: @primary-color;
+                background: $primary-color;
+                border-color: $primary-color;
                 
                 a {
                     color: #fff;
@@ -573,13 +573,13 @@ ul li {
         .ant-pagination-prev,
         .ant-pagination-next {
             .ant-pagination-item-link {
-                border-radius: @radius-lg;
-                border-color: @border-base;
-                transition: @transition-base;
+                border-radius: $radius-lg;
+                border-color: $border-base;
+                transition: $transition-base;
                 
                 &:hover {
-                    border-color: @primary-color;
-                    color: @primary-color;
+                    border-color: $primary-color;
+                    color: $primary-color;
                 }
             }
         }
@@ -588,7 +588,7 @@ ul li {
 
 // ==================== 响应式适配 ====================
 
-@media (max-width: @screen-md) {
+@media (max-width: $screen-md) {
     .sort-buttons {
         flex-wrap: wrap;
         
@@ -602,8 +602,8 @@ ul li {
 @media (max-width: @screen-sm) {
     .sort-buttons {
         .sort-btn {
-            padding: @spacing-1 @spacing-2;
-            font-size: @font-sm;
+            padding: $spacing-1 $spacing-2;
+            font-size: $font-sm;
         }
     }
 }

+ 0 - 427
jk-rag-platform/src/styles/variables.less

@@ -1,427 +0,0 @@
-// ===== 建科小智全局样式变量 v3.2 =====
-// 项目:jk-rag-platform
-// 更新:2026-03-24
-// 说明:更新为企业品牌色彩系统(基于 favicon #005D80)
-
-// ==================== 主题变量 ====================
-
-:root {
-    --sidebar-bg: #ffffff;
-    --header-bg: #ffffff;
-    --header-text-color: #374151;
-}
-
-html[data-theme='dark'] {
-    --sidebar-bg: #1f2d3d;
-    --header-bg: #1f2d3d;
-    --header-text-color: #ffffff;
-}
-
-html[data-theme='light'] {
-    --sidebar-bg: #ffffff;
-    --header-bg: #ffffff;
-    --header-text-color: #374151;
-}
-
-// Less 变量别名
-@sidebar-bg: var(--sidebar-bg);
-@header-bg: var(--header-bg);
-@header-text-color: var(--header-text-color);
-
-// 按钮选中/强调色
-html[data-theme='dark'] {
-    --btn-selected-bg: #005D80;
-    --btn-selected-color: #fff;
-}
-
-html[data-theme='light'] {
-    --btn-selected-bg: #005D80;
-    --btn-selected-color: #fff;
-}
-
-@btn-selected-bg: var(--btn-selected-bg);
-@btn-selected-color: var(--btn-selected-color);
-
-
-// ===== 色彩系统(v3.2 更新为企业品牌色)=====
-
-// 主色调 - 企业蓝色系(基于 favicon #005D80)
-// 对比度测试:#005D80 on #FFFFFF = 8.02:1 ✅ WCAG AAA
-@primary-color: #005D80;        // 企业主色(深度蓝)
-@primary-light: #007A99;        // 提亮 20%(悬停/强调),对比度 6.5:1 ✅
-@primary-dark: #004060;         // 加深 20%(点击/激活),对比度 10.2:1 ✅
-@primary-lightest: #E6F7FA;     // 最浅色(背景点缀)
-
-// 主色渐变(仅用于大图形/按钮背景)
-@primary-gradient-start: #00A0CA;  // 渐变起点(亮蓝)
-@primary-gradient-end: #005D80;    // 渐变终点(深蓝)
-
-// 辅助色 - 基于企业绿色系 + 功能性颜色
-// 成功色:使用加深绿,确保对比度安全
-@success-color: #059669;        // 成功色(加深),对比度 4.6:1 ✅ WCAG AA
-@success-light: #10B981;        // 成功浅色(仅大图标/背景)
-@success-dark: #047857;         // 成功深色(强调)
-
-// 警告色:橙色系
-@warning-color: #F59E0B;        // 警告色,对比度 2.9:1 ⚠️ 仅大图形
-@warning-light: #FBBF24;        // 警告浅色
-@warning-dark: #B45309;         // 警告深色(文字用),对比度 5.8:1 ✅
-
-// 错误色:红色系
-@error-color: #DC2626;          // 错误色(加深),对比度 4.5:1 ✅ WCAG AA
-@error-light: #EF4444;          // 错误浅色
-@error-dark: #B91C1C;           // 错误深色
-
-// 信息色:企业青色
-@info-color: #00A0C7;           // 信息色(企业青),对比度 3.8:1 ⚠️ 大图形
-@info-light: #06B6D4;           // 信息浅色
-@info-dark: #0E7490;            // 信息深色(文字用)
-
-// Chat 专用色(universalChat 使用)
-@chat-primary-start: #00A0C7;   // Chat 渐变起点(青)
-@chat-primary-end: #08A84C;     // Chat 渐变终点(绿)
-@chat-accent: #059669;          // Chat 强调色(加深绿,文字可用)
-
-// 中性色 - 文字
-@text-primary: #1F2937;
-@text-secondary: #6B7280;
-@text-hint: #9CA3AF;
-@text-disabled: #D1D5DB;
-
-// 中性色 - 背景
-@bg-primary: #F9FAFB;
-@bg-secondary: #FFFFFF;
-@bg-tertiary: #F3F4F6;
-@bg-hover: #F9FAFB;
-@bg-active: rgba(0, 93, 128, 0.1);  // 使用新主色的 10% 透明度
-
-// 中性色 - 边框
-@border-light: #F3F4F6;
-@border-base: #E5E7EB;
-@border-dark: #D1D5DB;
-
-// ===== 间距系统(4px 基准)=====
-
-@spacing-1: 4px;
-@spacing-2: 8px;
-@spacing-3: 12px;
-@spacing-4: 16px;
-@spacing-5: 20px;
-@spacing-6: 24px;
-@spacing-8: 32px;
-@spacing-10: 40px;
-@spacing-12: 48px;
-@spacing-16: 64px;
-
-// 快捷别名
-@spacing-xs: @spacing-1;
-@spacing-sm: @spacing-2;
-@spacing-md: @spacing-3;
-@spacing-lg: @spacing-4;
-@spacing-xl: @spacing-6;
-@spacing-2xl: @spacing-8;
-
-// ===== 圆角系统 =====
-
-@radius-sm: 4px;
-@radius-md: 6px;
-@radius-lg: 8px;
-@radius-xl: 12px;
-@radius-2xl: 16px;
-@radius-full: 9999px;
-
-// 快捷别名
-@border-radius-sm: @radius-sm;
-@border-radius-md: @radius-md;
-@border-radius-lg: @radius-lg;
-@border-radius-xl: @radius-xl;
-@border-radius-2xl: @radius-2xl;
-@border-radius-full: @radius-full;
-
-// ===== 阴影系统 =====
-
-@shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
-@shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
-@shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
-@shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1);
-@shadow-glow: 0 0 20px rgba(0, 93, 128, 0.3);  // 使用新主色的发光效果
-@shadow-inner: inset 0 2px 4px rgba(0, 0, 0, 0.05);
-
-// ===== 字体系统 =====
-
-// 字体大小(整体缩小一号)
-@font-xs: 8px;      // 9px → 8px
-@font-sm: 11px;     // 12px → 11px
-@font-base: 12px;   // 13px → 12px
-@font-md: 13px;     // 14px → 13px
-@font-lg: 14px;     // 15px → 14px
-@font-xl: 15px;     // 16px → 15px
-@font-2xl: 16px;    // 18px → 16px
-@font-3xl: 18px;    // 20px → 18px
-@font-4xl: 20px;    // 24px → 20px
-
-// 字重
-@font-weight-normal: 400;
-@font-weight-medium: 500;
-@font-weight-semibold: 600;
-@font-weight-bold: 700;
-
-// 行高(更紧凑)
-@line-height-tight: 1.1;    // 1.2 → 1.1
-@line-height-base: 1.3;     // 1.4 → 1.3
-@line-height-relaxed: 1.4;  // 1.5 → 1.4
-@line-height-loose: 1.5;    // 1.6 → 1.5
-
-// 字体家族
-@font-family-base: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
-@font-family-mono: 'Monaco', 'Consolas', 'Courier New', monospace;
-
-// ===== 动画系统 =====
-
-// 时长
-@animation-fast: 0.1s;
-@animation-base: 0.2s;
-@animation-slow: 0.3s;
-
-// 缓动函数
-@easing-default: cubic-bezier(0.4, 0, 0.2, 1);
-@easing-in: cubic-bezier(0.4, 0, 1, 1);
-@easing-out: cubic-bezier(0, 0, 0.2, 1);
-@easing-in-out: cubic-bezier(0.4, 0, 0.2, 1);
-
-// 快捷过渡
-@transition-base: all @animation-base @easing-default;
-@transition-fast: all @animation-fast @easing-default;
-@transition-slow: all @animation-slow @easing-default;
-
-// ===== 响应式断点 =====
-
-@screen-sm: 640px;
-@screen-md: 768px;
-@screen-lg: 1024px;
-@screen-xl: 1280px;
-@screen-2xl: 1536px;
-
-// 断点别名(max-width)
-@breakpoint-xs: ~"only screen and (max-width: @{screen-sm})";
-@breakpoint-sm: ~"only screen and (max-width: @{screen-md})";
-@breakpoint-md: ~"only screen and (max-width: @{screen-lg})";
-@breakpoint-lg: ~"only screen and (max-width: @{screen-xl})";
-@breakpoint-xl: ~"only screen and (max-width: @{screen-2xl})";
-
-// 断点别名(min-width)
-@breakpoint-sm-up: ~"only screen and (min-width: @{screen-sm})";
-@breakpoint-md-up: ~"only screen and (min-width: @{screen-md})";
-@breakpoint-lg-up: ~"only screen and (min-width: @{screen-lg})";
-@breakpoint-xl-up: ~"only screen and (min-width: @{screen-xl})";
-@breakpoint-2xl-up: ~"only screen and (min-width: @{screen-2xl})";
-
-// ===== 布局尺寸 =====
-
-@header-height: 64px;
-@sidebar-width: 240px;  // 紧凑布局:256px → 240px
-@sidebar-collapsed-width: 80px;
-@sidebar-width-md: 200px;  // 中等屏幕(≤1024px)
-@logo-size: 40px;
-@search-height: 40px;
-@breadcrumb-height: 44px;
-
-// 卡片尺寸
-@card-padding: @spacing-4;  // 16px
-@card-radius: @radius-xl;   // 12px
-@card-icon-size: 48px;
-@card-btn-size: 28px;
-@card-play-btn-size: 36px;
-
-// ===== Z-Index 层级 =====
-
-@z-index-base: 1;
-@z-index-dropdown: 100;
-@z-index-sticky: 200;
-@z-index-fixed: 300;
-@z-index-modal-backdrop: 400;
-@z-index-modal: 500;
-@z-index-popover: 600;
-@z-index-tooltip: 700;
-@z-index-notification: 800;
-@z-index-max: 9999;
-
-// ===== 透明度 =====
-
-@opacity-disabled: 0.5;
-@opacity-disabled-light: 0.3;
-@opacity-overlay: 0.5;
-@opacity-hover: 0.9;
-@opacity-active: 0.8;
-
-// ===== 图标尺寸 =====
-
-@icon-xs: 12px;
-@icon-sm: 14px;
-@icon-md: 16px;
-@icon-lg: 18px;
-@icon-xl: 20px;
-@icon-2xl: 24px;
-@icon-3xl: 32px;
-
-// ===== 特殊颜色 =====
-
-// Sidebar 专用颜色
-@sidebar-bg: #FFFFFF;             // 白色背景
-@sidebar-text-color: #5E5E66;     // 默认文字色(加深)
-@sidebar-hover-bg: #F3F4F6;       // 悬停背景
-@sidebar-active-color: #131212;   // 激活文字色(深灰近黑)
-
-// 边框颜色别名
-@border-color: @border-base;
-
-// 占位符文字颜色
-@text-placeholder: @text-hint;
-
-// 主色悬停
-@primary-color-hover: @primary-light;
-
-// 缓动别名
-@ease-base: @easing-default;
-
-// 卡片图标背景色
-@icon-bg-blue: #EFF6FF;
-@icon-bg-indigo: #EEF2FF;
-@icon-bg-teal: #F0FDFA;
-@icon-bg-purple: #FAF5FF;
-@icon-bg-rose: #FFF1F2;
-@icon-bg-cyan: #ECFEFF;
-@icon-bg-amber: #FFFBEB;
-
-// 标签颜色
-@tag-bg-slate: #F1F5F9;
-@tag-text-slate: #64748B;
-@tag-bg-blue: #E6F7FA;         // 更新为企业蓝浅色
-@tag-text-blue: #005D80;       // 更新为企业蓝色
-@tag-bg-indigo: #E0E7FF;
-@tag-text-indigo: #4338CA;
-@tag-bg-teal: #CCFBF1;
-@tag-text-teal: #0D9488;
-@tag-bg-purple: #F3E8FF;
-@tag-text-purple: #7E22CE;
-@tag-bg-rose: #FFE4E6;
-@tag-text-rose: #E11D48;
-@tag-bg-cyan: #CFFAFE;
-@tag-text-cyan: #0891B2;
-@tag-bg-amber: #FEF3C7;
-@tag-text-amber: #B45309;
-
-// ===== 混合宏定义 =====
-
-// 文字截断
-.text-truncate() {
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-}
-
-// 多行文字截断
-.text-clamp(@lines: 2) {
-    display: -webkit-box;
-    -webkit-line-clamp: @lines;
-    -webkit-box-orient: vertical;
-    overflow: hidden;
-}
-
-// 居中
-.center-flex() {
-    display: flex;
-    justify-content: center;
-    align-items: center;
-}
-
-// 隐藏滚动条
-.hide-scrollbar() {
-    &::-webkit-scrollbar {
-        display: none;
-    }
-    -ms-overflow-style: none;
-    scrollbar-width: none;
-}
-
-// 细边框
-.hairline-border() {
-    position: relative;
-    
-    &::after {
-        content: '';
-        position: absolute;
-        bottom: 0;
-        left: 0;
-        right: 0;
-        height: 1px;
-        background: @border-base;
-        transform: scaleY(0.5);
-    }
-}
-
-// 渐变背景(v3.2 更新为企业蓝色渐变)
-.gradient-primary() {
-    background: linear-gradient(135deg, @primary-gradient-start 0%, @primary-color 100%);
-}
-
-// Chat 渐变背景(universalChat 使用)
-.gradient-chat() {
-    background: linear-gradient(135deg, @chat-primary-start 0%, @chat-primary-end 100%);
-}
-
-// 阴影快捷方式
-.shadow-card() {
-    box-shadow: @shadow-sm;
-    
-    &:hover {
-        box-shadow: @shadow-lg;
-    }
-}
-
-// 过渡快捷方式
-.transition-base() {
-    transition: @transition-base;
-}
-
-.transition-transform() {
-    transition: transform @animation-base @easing-default;
-}
-
-// 响应式 Mixins
-.media-mobile() {
-    @media (max-width: @screen-md) {
-        @content();
-    }
-}
-
-.media-tablet() {
-    @media (min-width: @screen-md) and (max-width: @screen-lg) {
-        @content();
-    }
-}
-
-.media-desktop() {
-    @media (min-width: @screen-lg) {
-        @content();
-    }
-}
-
-// ===== 变量别名(兼容性)=====
-
-// 字体大小别名
-@font-size-base: @font-base;
-@font-size-xs: @font-xs;
-@font-size-sm: @font-sm;
-@font-size-md: @font-md;
-@font-size-lg: @font-lg;
-@font-size-xl: @font-xl;
-
-// 圆角别名
-@border-radius-base: @border-radius-md;
-@border-radius-sm: @radius-sm;
-@border-radius-md: @radius-md;
-@border-radius-lg: @radius-lg;
-
-// 主题文字颜色
-@theme-text-color: @text-primary;

+ 431 - 0
jk-rag-platform/src/styles/variables.scss

@@ -0,0 +1,431 @@
+// ===== 建科小智全局样式变量 v3.2 =====
+// 项目:jk-rag-platform
+// 更新:2026-03-24
+// 说明:更新为企业品牌色彩系统(基于 favicon #005D80)
+
+// ==================== 主题变量 ====================
+
+:root {
+    --sidebar-bg: #ffffff;
+    --header-bg: #ffffff;
+    --header-text-color: #374151;
+}
+
+html[data-theme='dark'] {
+    --sidebar-bg: #1f2d3d;
+    --header-bg: #1f2d3d;
+    --header-text-color: #ffffff;
+}
+
+html[data-theme='light'] {
+    --sidebar-bg: #ffffff;
+    --header-bg: #ffffff;
+    --header-text-color: #374151;
+}
+
+// Less 变量别名
+$sidebar-bg: var(--sidebar-bg);
+$header-bg: var(--header-bg);
+$header-text-color: var(--header-text-color);
+
+// 按钮选中/强调色
+html[data-theme='dark'] {
+    --btn-selected-bg: #005D80;
+    --btn-selected-color: #fff;
+}
+
+html[data-theme='light'] {
+    --btn-selected-bg: #005D80;
+    --btn-selected-color: #fff;
+}
+
+$btn-selected-bg: var(--btn-selected-bg);
+$btn-selected-color: var(--btn-selected-color);
+
+
+// ===== 色彩系统(v3.2 更新为企业品牌色)=====
+
+// 主色调 - 企业蓝色系(基于 favicon #005D80)
+// 对比度测试:#005D80 on #FFFFFF = 8.02:1 ✅ WCAG AAA
+$primary-color: #005D80;        // 企业主色(深度蓝)
+$primary-light: #007A99;        // 提亮 20%(悬停/强调),对比度 6.5:1 ✅
+$primary-dark: #004060;         // 加深 20%(点击/激活),对比度 10.2:1 ✅
+$primary-lightest: #E6F7FA;     // 最浅色(背景点缀)
+
+// 主色渐变(仅用于大图形/按钮背景)
+$primary-gradient-start: #00A0CA;  // 渐变起点(亮蓝)
+$primary-gradient-end: #005D80;    // 渐变终点(深蓝)
+
+// 辅助色 - 基于企业绿色系 + 功能性颜色
+// 成功色:使用加深绿,确保对比度安全
+$success-color: #059669;        // 成功色(加深),对比度 4.6:1 ✅ WCAG AA
+$success-light: #10B981;        // 成功浅色(仅大图标/背景)
+$success-dark: #047857;         // 成功深色(强调)
+
+// 警告色:橙色系
+$warning-color: #F59E0B;        // 警告色,对比度 2.9:1 ⚠️ 仅大图形
+$warning-light: #FBBF24;        // 警告浅色
+$warning-dark: #B45309;         // 警告深色(文字用),对比度 5.8:1 ✅
+
+// 错误色:红色系
+$error-color: #DC2626;          // 错误色(加深),对比度 4.5:1 ✅ WCAG AA
+$error-light: #EF4444;          // 错误浅色
+$error-dark: #B91C1C;           // 错误深色
+
+// 信息色:企业青色
+$info-color: #00A0C7;           // 信息色(企业青),对比度 3.8:1 ⚠️ 大图形
+$info-light: #06B6D4;           // 信息浅色
+$info-dark: #0E7490;            // 信息深色(文字用)
+
+// Chat 专用色(universalChat 使用)
+$chat-primary-start: #00A0C7;   // Chat 渐变起点(青)
+$chat-primary-end: #08A84C;     // Chat 渐变终点(绿)
+$chat-accent: #059669;          // Chat 强调色(加深绿,文字可用)
+
+// 中性色 - 文字
+$text-primary: #1F2937;
+$text-secondary: #6B7280;
+$text-hint: #9CA3AF;
+$text-disabled: #D1D5DB;
+
+// 中性色 - 背景
+$bg-primary: #F9FAFB;
+$bg-secondary: #FFFFFF;
+$bg-tertiary: #F3F4F6;
+$bg-hover: #F9FAFB;
+$bg-active: rgba(0, 93, 128, 0.1);  // 使用新主色的 10% 透明度
+
+// 中性色 - 边框
+$border-light: #F3F4F6;
+$border-base: #E5E7EB;
+$border-dark: #D1D5DB;
+
+// ===== 间距系统(4px 基准)=====
+
+$spacing-1: 4px;
+$spacing-2: 8px;
+$spacing-3: 12px;
+$spacing-4: 16px;
+$spacing-5: 20px;
+$spacing-6: 24px;
+$spacing-8: 32px;
+$spacing-10: 40px;
+$spacing-12: 48px;
+$spacing-16: 64px;
+
+// 快捷别名
+$spacing-xs: $spacing-1;
+$spacing-sm: $spacing-2;
+$spacing-md: $spacing-3;
+$spacing-lg: $spacing-4;
+$spacing-xl: $spacing-6;
+$spacing-2xl: $spacing-8;
+
+// ===== 圆角系统 =====
+
+$radius-sm: 4px;
+$radius-md: 6px;
+$radius-lg: 8px;
+$radius-xl: 12px;
+$radius-2xl: 16px;
+$radius-full: 9999px;
+
+// 快捷别名
+$border-radius-sm: $radius-sm;
+$border-radius-md: $radius-md;
+$border-radius-lg: $radius-lg;
+$border-radius-xl: $radius-xl;
+$border-radius-2xl: $radius-2xl;
+$border-radius-full: $radius-full;
+
+// ===== 阴影系统 =====
+
+$shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
+$shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
+$shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
+$shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1);
+$shadow-glow: 0 0 20px rgba(0, 93, 128, 0.3);  // 使用新主色的发光效果
+$shadow-inner: inset 0 2px 4px rgba(0, 0, 0, 0.05);
+
+// ===== 字体系统 =====
+
+// 字体大小(整体缩小一号)
+$font-xs: 8px;      // 9px → 8px
+$font-sm: 11px;     // 12px → 11px
+$font-base: 12px;   // 13px → 12px
+$font-md: 13px;     // 14px → 13px
+$font-lg: 14px;     // 15px → 14px
+$font-xl: 15px;     // 16px → 15px
+$font-2xl: 16px;    // 18px → 16px
+$font-3xl: 18px;    // 20px → 18px
+$font-4xl: 20px;    // 24px → 20px
+
+// 字重
+$font-weight-normal: 400;
+$font-weight-medium: 500;
+$font-weight-semibold: 600;
+$font-weight-bold: 700;
+
+// 行高(更紧凑)
+$line-height-tight: 1.1;    // 1.2 → 1.1
+$line-height-base: 1.3;     // 1.4 → 1.3
+$line-height-relaxed: 1.4;  // 1.5 → 1.4
+$line-height-loose: 1.5;    // 1.6 → 1.5
+
+// 字体家族
+$font-family-base: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+$font-family-mono: 'Monaco', 'Consolas', 'Courier New', monospace;
+
+// ===== 动画系统 =====
+
+// 时长
+$animation-fast: 0.1s;
+$animation-base: 0.2s;
+$animation-slow: 0.3s;
+
+// 缓动函数
+$easing-default: cubic-bezier(0.4, 0, 0.2, 1);
+$easing-in: cubic-bezier(0.4, 0, 1, 1);
+$easing-out: cubic-bezier(0, 0, 0.2, 1);
+$easing-in-out: cubic-bezier(0.4, 0, 0.2, 1);
+
+// 快捷过渡
+$transition-base: all $animation-base $easing-default;
+$transition-fast: all $animation-fast $easing-default;
+$transition-slow: all $animation-slow $easing-default;
+
+// ===== 响应式断点 =====
+
+$screen-sm: 640px;
+$screen-md: 768px;
+$screen-lg: 1024px;
+$screen-xl: 1280px;
+$screen-2xl: 1536px;
+
+// 断点别名(max-width)
+$breakpoint-xs: "only screen and (max-width: #{$screen-sm})";
+$breakpoint-sm: "only screen and (max-width: #{$screen-md})";
+$breakpoint-md: "only screen and (max-width: #{$screen-lg})";
+$breakpoint-lg: "only screen and (max-width: #{$screen-xl})";
+$breakpoint-xl: "only screen and (max-width: #{$screen-2xl})";
+
+// 断点别名(min-width)
+$breakpoint-sm-up: "only screen and (min-width: #{$screen-sm})";
+$breakpoint-md-up: "only screen and (min-width: #{$screen-md})";
+$breakpoint-lg-up: "only screen and (min-width: #{$screen-lg})";
+$breakpoint-xl-up: "only screen and (min-width: #{$screen-xl})";
+$breakpoint-2xl-up: "only screen and (min-width: #{$screen-2xl})";
+
+// ===== 布局尺寸 =====
+
+$header-height: 64px;
+$sidebar-width: 240px;  // 紧凑布局:256px → 240px
+$sidebar-collapsed-width: 80px;
+$sidebar-width-md: 200px;  // 中等屏幕(≤1024px)
+$logo-size: 40px;
+$search-height: 40px;
+$breadcrumb-height: 44px;
+
+// 卡片尺寸
+$card-padding: $spacing-4;  // 16px
+$card-radius: $radius-xl;   // 12px
+$card-icon-size: 48px;
+$card-btn-size: 28px;
+$card-play-btn-size: 36px;
+
+// ===== Z-Index 层级 =====
+
+$z-index-base: 1;
+$z-index-dropdown: 100;
+$z-index-sticky: 200;
+$z-index-fixed: 300;
+$z-index-modal-backdrop: 400;
+$z-index-modal: 500;
+$z-index-popover: 600;
+$z-index-tooltip: 700;
+$z-index-notification: 800;
+$z-index-max: 9999;
+
+// ===== 透明度 =====
+
+$opacity-disabled: 0.5;
+$opacity-disabled-light: 0.3;
+$opacity-overlay: 0.5;
+$opacity-hover: 0.9;
+$opacity-active: 0.8;
+
+// ===== 图标尺寸 =====
+
+$icon-xs: 12px;
+$icon-sm: 14px;
+$icon-md: 16px;
+$icon-lg: 18px;
+$icon-xl: 20px;
+$icon-2xl: 24px;
+$icon-3xl: 32px;
+
+// ===== 特殊颜色 =====
+
+// Sidebar 专用颜色
+$sidebar-bg: #FFFFFF;             // 白色背景
+$sidebar-text-color: #5E5E66;     // 默认文字色(加深)
+$sidebar-hover-bg: #F3F4F6;       // 悬停背景
+$sidebar-active-color: #131212;   // 激活文字色(深灰近黑)
+
+// 边框颜色别名
+$border-color: $border-base;
+
+// 占位符文字颜色
+$text-placeholder: $text-hint;
+
+// 主色悬停
+$primary-color-hover: $primary-light;
+
+// 缓动别名
+$ease-base: $easing-default;
+
+// 卡片图标背景色
+$icon-bg-blue: #EFF6FF;
+$icon-bg-indigo: #EEF2FF;
+$icon-bg-teal: #F0FDFA;
+$icon-bg-purple: #FAF5FF;
+$icon-bg-rose: #FFF1F2;
+$icon-bg-cyan: #ECFEFF;
+$icon-bg-amber: #FFFBEB;
+
+// 标签颜色
+$tag-bg-slate: #F1F5F9;
+$tag-text-slate: #64748B;
+$tag-bg-blue: #E6F7FA;         // 更新为企业蓝浅色
+$tag-text-blue: #005D80;       // 更新为企业蓝色
+$tag-bg-indigo: #E0E7FF;
+$tag-text-indigo: #4338CA;
+$tag-bg-teal: #CCFBF1;
+$tag-text-teal: #0D9488;
+$tag-bg-purple: #F3E8FF;
+$tag-text-purple: #7E22CE;
+$tag-bg-rose: #FFE4E6;
+$tag-text-rose: #E11D48;
+$tag-bg-cyan: #CFFAFE;
+$tag-text-cyan: #0891B2;
+$tag-bg-amber: #FEF3C7;
+$tag-text-amber: #B45309;
+
+// ===== 混合宏定义 =====
+
+// 文字截断
+@mixin text-truncate {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+}
+
+.text-truncate {
+    @include text-truncate;
+}
+
+// 多行文字截断
+@mixin text-clamp($lines: 2) {
+    display: -webkit-box;
+    -webkit-line-clamp: $lines;
+    -webkit-box-orient: vertical;
+    overflow: hidden;
+}
+
+// 居中
+@mixin center-flex {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+}
+
+// 隐藏滚动条
+@mixin hide-scrollbar {
+    &::-webkit-scrollbar {
+        display: none;
+    }
+    -ms-overflow-style: none;
+    scrollbar-width: none;
+}
+
+// 细边框
+@mixin hairline-border {
+    position: relative;
+    
+    &::after {
+        content: '';
+        position: absolute;
+        bottom: 0;
+        left: 0;
+        right: 0;
+        height: 1px;
+        background: $border-base;
+        transform: scaleY(0.5);
+    }
+}
+
+// 渐变背景(v3.2 更新为企业蓝色渐变)
+@mixin gradient-primary {
+    background: linear-gradient(135deg, $primary-gradient-start 0%, $primary-color 100%);
+}
+
+// Chat 渐变背景(universalChat 使用)
+@mixin gradient-chat {
+    background: linear-gradient(135deg, $chat-primary-start 0%, $chat-primary-end 100%);
+}
+
+// 阴影快捷方式
+@mixin shadow-card {
+    box-shadow: $shadow-sm;
+    
+    &:hover {
+        box-shadow: $shadow-lg;
+    }
+}
+
+// 过渡快捷方式
+@mixin transition-base {
+    transition: $transition-base;
+}
+
+@mixin transition-transform {
+    transition: transform $animation-base $easing-default;
+}
+
+// 响应式 Mixins
+@mixin media-mobile {
+    @media (max-width: $screen-md) {
+        @content;
+    }
+}
+
+@mixin media-tablet {
+    @media (min-width: $screen-md) and (max-width: $screen-lg) {
+        @content;
+    }
+}
+
+@mixin media-desktop {
+    @media (min-width: $screen-lg) {
+        @content;
+    }
+}
+
+// ===== 变量别名(兼容性)=====
+
+// 字体大小别名
+$font-size-base: $font-base;
+$font-size-xs: $font-xs;
+$font-size-sm: $font-sm;
+$font-size-md: $font-md;
+$font-size-lg: $font-lg;
+$font-size-xl: $font-xl;
+
+// 圆角别名
+$border-radius-base: $radius-md;
+$border-radius-sm: $radius-sm;
+$border-radius-md: $radius-md;
+$border-radius-lg: $radius-lg;
+
+// 主题文字颜色
+$theme-text-color: $text-primary;

+ 3 - 4
jk-rag-platform/vite.config.ts

@@ -9,11 +9,10 @@ export default defineConfig(({ mode, command }) => {
     envDir: './env',// 环境目录
     css: {
       preprocessorOptions: {
-        less: {
+        scss: {
           charset: false,
-          // 仅自动导入变量文件,不导入整个 global.less
-          // 避免 HMR 热更新时样式重复注入
-          additionalData: '@import "@/styles/variables.less";',
+          // 仅自动导入变量文件,不导入整个 global.scss
+          additionalData: '@import "@/styles/variables.scss";',
         }
       }
     },