Procházet zdrojové kódy

Merge branch 'dev' of http://192.168.0.3:3000/CRBC-MaaS-Platform-Project/LQAdminFront into dev

lingmin_package@163.com před 1 měsícem
rodič
revize
8d8545f3da

+ 1 - 1
.env.dev

@@ -1,6 +1,6 @@
 # 开发环境配置
 VITE_APP_TITLE=管理平台 - 开发环境
-VITE_API_BASE_URL=http://192.168.92.61
+VITE_API_BASE_URL=http://localhost:8000
 VITE_APP_ENV=dev
 VITE_APP_DEBUG=true
 

+ 442 - 34
package-lock.json

@@ -27,6 +27,7 @@
         "eslint": "^8.53.0",
         "eslint-plugin-vue": "^9.18.1",
         "prettier": "^3.0.3",
+        "rimraf": "^5.0.5",
         "typescript": "~5.2.0",
         "vite": "^4.5.0",
         "vue-tsc": "^1.8.22"
@@ -644,6 +645,53 @@
       "dev": true,
       "license": "BSD-3-Clause"
     },
+    "node_modules/@isaacs/cliui": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+      "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^5.1.2",
+        "string-width-cjs": "npm:string-width@^4.2.0",
+        "strip-ansi": "^7.0.1",
+        "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+        "wrap-ansi": "^8.1.0",
+        "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+      "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
     "node_modules/@jridgewell/sourcemap-codec": {
       "version": "1.5.5",
       "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
@@ -688,6 +736,17 @@
         "node": ">= 8"
       }
     },
+    "node_modules/@pkgjs/parseargs": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+      "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=14"
+      }
+    },
     "node_modules/@pkgr/core": {
       "version": "0.2.9",
       "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz",
@@ -1579,6 +1638,13 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/eastasianwidth": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+      "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/element-plus": {
       "version": "2.13.0",
       "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.13.0.tgz",
@@ -1604,6 +1670,13 @@
         "vue": "^3.3.0"
       }
     },
+    "node_modules/emoji-regex": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/entities": {
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.0.tgz",
@@ -2086,6 +2159,69 @@
         "node": "^10.12.0 || >=12.0.0"
       }
     },
+    "node_modules/flat-cache/node_modules/brace-expansion": {
+      "version": "1.1.12",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+      "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/flat-cache/node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "deprecated": "Glob versions prior to v9 are no longer supported",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/flat-cache/node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/flat-cache/node_modules/rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "deprecated": "Rimraf versions prior to v4 are no longer supported",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
     "node_modules/flatted": {
       "version": "3.3.3",
       "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
@@ -2113,6 +2249,23 @@
         }
       }
     },
+    "node_modules/foreground-child": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+      "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "cross-spawn": "^7.0.6",
+        "signal-exit": "^4.0.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
     "node_modules/form-data": {
       "version": "4.0.5",
       "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
@@ -2198,22 +2351,21 @@
       }
     },
     "node_modules/glob": {
-      "version": "7.2.3",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-      "deprecated": "Glob versions prior to v9 are no longer supported",
+      "version": "10.5.0",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+      "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
       "dev": true,
       "license": "ISC",
       "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.1.1",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
+        "foreground-child": "^3.1.0",
+        "jackspeak": "^3.1.2",
+        "minimatch": "^9.0.4",
+        "minipass": "^7.1.2",
+        "package-json-from-dist": "^1.0.0",
+        "path-scurry": "^1.11.1"
       },
-      "engines": {
-        "node": "*"
+      "bin": {
+        "glob": "dist/esm/bin.mjs"
       },
       "funding": {
         "url": "https://github.com/sponsors/isaacs"
@@ -2232,28 +2384,20 @@
         "node": ">=10.13.0"
       }
     },
-    "node_modules/glob/node_modules/brace-expansion": {
-      "version": "1.1.12",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
-      "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
     "node_modules/glob/node_modules/minimatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "version": "9.0.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
       "dev": true,
       "license": "ISC",
       "dependencies": {
-        "brace-expansion": "^1.1.7"
+        "brace-expansion": "^2.0.1"
       },
       "engines": {
-        "node": "*"
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
       }
     },
     "node_modules/globals": {
@@ -2437,6 +2581,16 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/is-glob": {
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -2477,6 +2631,22 @@
       "dev": true,
       "license": "ISC"
     },
+    "node_modules/jackspeak": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+      "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "@isaacs/cliui": "^8.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      },
+      "optionalDependencies": {
+        "@pkgjs/parseargs": "^0.11.0"
+      }
+    },
     "node_modules/js-cookie": {
       "version": "3.0.5",
       "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
@@ -2590,6 +2760,13 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "dev": true,
+      "license": "ISC"
+    },
     "node_modules/magic-string": {
       "version": "0.30.21",
       "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
@@ -2675,6 +2852,16 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/minipass": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+      "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      }
+    },
     "node_modules/ms": {
       "version": "2.1.3",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -2793,6 +2980,13 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/package-json-from-dist": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+      "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+      "dev": true,
+      "license": "BlueOak-1.0.0"
+    },
     "node_modules/parent-module": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -2843,6 +3037,23 @@
         "node": ">=8"
       }
     },
+    "node_modules/path-scurry": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+      "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "lru-cache": "^10.2.0",
+        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
     "node_modules/path-type": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
@@ -3034,17 +3245,16 @@
       }
     },
     "node_modules/rimraf": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
-      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
-      "deprecated": "Rimraf versions prior to v4 are no longer supported",
+      "version": "5.0.10",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
+      "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==",
       "dev": true,
       "license": "ISC",
       "dependencies": {
-        "glob": "^7.1.3"
+        "glob": "^10.3.7"
       },
       "bin": {
-        "rimraf": "bin.js"
+        "rimraf": "dist/esm/bin.mjs"
       },
       "funding": {
         "url": "https://github.com/sponsors/isaacs"
@@ -3127,6 +3337,19 @@
         "node": ">=8"
       }
     },
+    "node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
     "node_modules/slash": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@@ -3146,6 +3369,76 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/string-width": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+      "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eastasianwidth": "^0.2.0",
+        "emoji-regex": "^9.2.2",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/string-width-cjs": {
+      "name": "string-width",
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/string-width-cjs/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/string-width/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/string-width/node_modules/strip-ansi": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+      "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
     "node_modules/strip-ansi": {
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
@@ -3159,6 +3452,20 @@
         "node": ">=8"
       }
     },
+    "node_modules/strip-ansi-cjs": {
+      "name": "strip-ansi",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/strip-json-comments": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -3496,6 +3803,107 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/wrap-ansi": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+      "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.1.0",
+        "string-width": "^5.0.1",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wrap-ansi-cjs": {
+      "name": "wrap-ansi",
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/ansi-styles": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+      "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/wrap-ansi/node_modules/strip-ansi": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+      "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
     "node_modules/wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",

+ 72 - 0
src/api/knowledge-base.ts

@@ -0,0 +1,72 @@
+
+import request from './request'
+
+export interface KnowledgeBase {
+  id: string
+  name: string
+  collection_name: string
+  description?: string
+  status: string
+  document_count: number
+  created_at: string
+  updated_at: string
+}
+
+export interface KnowledgeBaseParams {
+  page?: number
+  page_size?: number
+  keyword?: string
+  status?: string
+}
+
+export interface CreateKnowledgeBaseData {
+  name: string
+  collection_name: string
+  description?: string
+  dimension?: number
+}
+
+export interface UpdateKnowledgeBaseData {
+  name?: string
+  description?: string
+  status?: string
+}
+
+export const getKnowledgeBases = (params: KnowledgeBaseParams) => {
+  return request({
+    url: '/api/v1/sample/knowledge-base',
+    method: 'get',
+    params
+  })
+}
+
+export const createKnowledgeBase = (data: CreateKnowledgeBaseData) => {
+  return request({
+    url: '/api/v1/sample/knowledge-base',
+    method: 'post',
+    data
+  })
+}
+
+export const updateKnowledgeBase = (id: string, data: UpdateKnowledgeBaseData) => {
+  return request({
+    url: '/api/v1/sample/knowledge-base/' + id,
+    method: 'put',
+    data
+  })
+}
+
+export const updateKnowledgeBaseStatus = (id: string, status: string) => {
+  return request({
+    url: '/api/v1/sample/knowledge-base/' + id + '/status',
+    method: 'patch',
+    params: { status }
+  })
+}
+
+export const deleteKnowledgeBase = (id: string) => {
+  return request({
+    url: '/api/v1/sample/knowledge-base/' + id,
+    method: 'delete'
+  })
+}

+ 68 - 0
src/api/snippet.ts

@@ -0,0 +1,68 @@
+
+import request from './request'
+
+export interface Snippet {
+  id: string
+  collection_name: string
+  doc_name: string
+  code: string
+  content: string
+  char_count: number
+  meta_info: string
+  status: string
+  created_at: string
+  updated_at: string
+}
+
+export interface SnippetParams {
+  page?: number
+  page_size?: number
+  kb?: string
+  keyword?: string
+  status?: string
+}
+
+export interface CreateSnippetData {
+  collection_name: string
+  doc_name: string
+  content: string
+  meta_info?: string
+}
+
+export interface UpdateSnippetData {
+  collection_name: string
+  doc_name?: string
+  content: string
+}
+
+export const getSnippets = (params: SnippetParams) => {
+  return request({
+    url: '/api/v1/document/snippet',
+    method: 'get',
+    params
+  })
+}
+
+export const createSnippet = (data: CreateSnippetData) => {
+  return request({
+    url: '/api/v1/document/snippet',
+    method: 'post',
+    data
+  })
+}
+
+export const updateSnippet = (id: string, data: UpdateSnippetData) => {
+  return request({
+    url: '/api/v1/document/snippet/' + id,
+    method: 'put',
+    data
+  })
+}
+
+export const deleteSnippet = (id: string, kb: string) => {
+  return request({
+    url: '/api/v1/document/snippet/' + id,
+    method: 'delete',
+    params: { kb }
+  })
+}

+ 6 - 0
src/router/index.ts

@@ -104,6 +104,12 @@ const routes: RouteRecordRaw[] = [
         component: () => import('@/views/documents/KnowledgeBase.vue'),
         meta: { requiresAdmin: true }
       },
+      {
+        path: 'admin/documents/snippet',
+        name: 'KnowledgeSnippet',
+        component: () => import('@/views/documents/KnowledgeSnippet.vue'),
+        meta: { requiresAdmin: true }
+      },
       {
         path: 'admin/basic-info/basis',
         name: 'BasicInfoBasis',

+ 399 - 67
src/views/documents/KnowledgeBase.vue

@@ -1,68 +1,392 @@
+
 <template>
   <div class="kb-container">
     <div class="header-section">
       <div class="title-info">
-        <h2>知识库管理中心</h2>
-        <p class="subtitle">管理和查看已转换的知识库内容</p>
+        <h2>知识库管理</h2>
+        <p class="subtitle">管理所有知识库和相关内容</p>
       </div>
       <div class="action-buttons">
-        <el-button type="primary" @click="handleRefresh">
-          <el-icon><Refresh /></el-icon> 刷新数据
+        <el-select v-model="queryParams.status" placeholder="所有状态" clearable style="width: 120px; margin-right: 12px" @change="handleSearch">
+          <el-option label="正常" value="normal" />
+          <el-option label="测试" value="test" />
+          <el-option label="已禁用" value="disabled" />
+        </el-select>
+        <el-input
+          v-model="queryParams.keyword"
+          placeholder="搜索知识库名称..."
+          style="width: 240px; margin-right: 12px"
+          clearable
+          @keyup.enter="handleSearch"
+          @clear="handleSearch"
+        >
+          <template #prefix>
+            <el-icon><Search /></el-icon>
+          </template>
+        </el-input>
+        <el-button type="primary" @click="handleAdd">
+          <el-icon><Plus /></el-icon> 新建知识库
         </el-button>
       </div>
     </div>
 
-    <el-card class="status-card">
-      <template #header>
-        <div class="card-header">
-          <span>知识库概览</span>
+    <el-card class="table-card" shadow="never">
+      <el-table :data="tableData" v-loading="loading" style="width: 100%" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" />
+        
+        <el-table-column label="知识库名称" min-width="200">
+          <template #default="{ row }">
+            <div class="kb-name-cell">
+              <el-icon class="kb-icon" :size="24" :style="{ color: getIconColor(row.name) }"><Collection /></el-icon>
+              <div class="kb-info">
+                <div class="name">{{ row.name }}</div>
+                <div class="desc">{{ row.description || '暂无描述' }}</div>
+              </div>
+            </div>
+          </template>
+        </el-table-column>
+
+        <el-table-column prop="collection_name" label="知识库表名" min-width="150" />
+
+        <el-table-column prop="status" label="状态" width="100">
+          <template #default="{ row }">
+            <el-tag :type="getStatusType(row.status)" effect="light" round>{{ getStatusText(row.status) }}</el-tag>
+          </template>
+        </el-table-column>
+
+        <el-table-column prop="created_at" label="创建时间" width="160">
+            <template #default="{ row }">
+                {{ formatTime(row.created_at) }}
+            </template>
+        </el-table-column>
+
+        <el-table-column prop="updated_at" label="修改时间" width="160">
+            <template #default="{ row }">
+                {{ formatTime(row.updated_at) }}
+            </template>
+        </el-table-column>
+
+        <el-table-column prop="document_count" label="文档数量" width="100" />
+
+        <el-table-column label="操作" width="180" fixed="right">
+          <template #default="{ row }">
+            <el-tooltip content="查看" placement="top">
+                <el-button link type="primary" @click="handleView(row)">
+                    <el-icon><View /></el-icon>
+                </el-button>
+            </el-tooltip>
+            <el-tooltip content="编辑" placement="top">
+                <el-button link type="primary" @click="handleEdit(row)">
+                    <el-icon><Edit /></el-icon>
+                </el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+                <el-button link type="danger" @click="handleDelete(row)">
+                    <el-icon><Delete /></el-icon>
+                </el-button>
+            </el-tooltip>
+            <el-dropdown @command="(cmd) => handleCommand(cmd, row)" trigger="click">
+                <el-button link type="info">
+                    <el-icon><MoreFilled /></el-icon>
+                </el-button>
+                <template #dropdown>
+                    <el-dropdown-menu>
+                        <el-dropdown-item command="toggleStatus">
+                            {{ row.status === 'disabled' ? '启用' : '禁用' }}
+                        </el-dropdown-item>
+                    </el-dropdown-menu>
+                </template>
+            </el-dropdown>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div class="pagination-container">
+        <div class="pagination-info">
+            显示 {{ (pagination.page - 1) * pagination.pageSize + 1 }} 到 
+            {{ Math.min(pagination.page * pagination.pageSize, pagination.total) }} 条,
+            共 {{ pagination.total }} 条记录
         </div>
-      </template>
-      <el-row :gutter="20">
-        <el-col :span="8">
-          <div class="stat-box">
-            <div class="label">总条目</div>
-            <div class="value">{{ stats.total }}</div>
-          </div>
-        </el-col>
-        <el-col :span="8">
-          <div class="stat-box">
-            <div class="label">已解析</div>
-            <div class="value success">{{ stats.parsed }}</div>
-          </div>
-        </el-col>
-        <el-col :span="8">
-          <div class="stat-box">
-            <div class="label">待解析</div>
-            <div class="value warning">{{ stats.pending }}</div>
-          </div>
-        </el-col>
-      </el-row>
+        <el-pagination
+          v-model:current-page="pagination.page"
+          v-model:page-size="pagination.pageSize"
+          :total="pagination.total"
+          :page-sizes="[10, 20, 50, 100]"
+          layout="prev, pager, next, sizes"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
     </el-card>
 
-    <div class="content-section">
-      <el-empty description="知识库功能开发中..." />
-    </div>
+    <!-- Create/Edit Dialog -->
+    <el-dialog
+      v-model="dialogVisible"
+      :title="dialogType === 'create' ? '新建知识库' : '编辑知识库'"
+      width="500px"
+      @close="resetForm"
+    >
+      <el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
+        <el-form-item label="名称" prop="name">
+          <el-input v-model="formData.name" placeholder="请输入知识库名称" />
+        </el-form-item>
+        <el-form-item label="集合名称" prop="collection_name" v-if="dialogType === 'create'">
+          <el-input v-model="formData.collection_name" placeholder="请输入Milvus集合名称(英文)" />
+        </el-form-item>
+        <el-form-item label="描述" prop="description">
+          <el-input v-model="formData.description" type="textarea" rows="3" placeholder="请输入描述信息" />
+        </el-form-item>
+        <el-form-item label="维度" prop="dimension" v-if="dialogType === 'create'">
+          <el-input-number v-model="formData.dimension" :min="1" :step="1" />
+        </el-form-item>
+        <el-form-item label="状态" prop="status">
+            <el-radio-group v-model="formData.status">
+                <el-radio label="normal">正常</el-radio>
+                <el-radio label="test">测试</el-radio>
+                <el-radio label="disabled">禁用</el-radio>
+            </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="dialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
+        </span>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup lang="ts">
 import { ref, reactive, onMounted } from 'vue'
-import { Refresh } from '@element-plus/icons-vue'
-import { ElMessage } from 'element-plus'
+import { Search, Plus, Collection, View, Edit, Delete, MoreFilled } from '@element-plus/icons-vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import type { FormInstance, FormRules } from 'element-plus'
+import { 
+    getKnowledgeBases, 
+    createKnowledgeBase, 
+    updateKnowledgeBase, 
+    updateKnowledgeBaseStatus, 
+    deleteKnowledgeBase,
+    type KnowledgeBase 
+} from '@/api/knowledge-base'
+import dayjs from 'dayjs'
+
+// Query Parameters
+const queryParams = reactive({
+  page: 1,
+  pageSize: 10,
+  keyword: '',
+  status: ''
+})
+
+// Table Data
+const loading = ref(false)
+const tableData = ref<KnowledgeBase[]>([])
+const pagination = reactive({
+  page: 1,
+  pageSize: 10,
+  total: 0
+})
 
-const stats = reactive({
-  total: 0,
-  parsed: 0,
-  pending: 0
+// Dialog
+const dialogVisible = ref(false)
+const dialogType = ref<'create' | 'edit'>('create')
+const submitLoading = ref(false)
+const formRef = ref<FormInstance>()
+const formData = reactive({
+  id: '',
+  name: '',
+  collection_name: '',
+  description: '',
+  dimension: 768,
+  status: 'normal'
 })
 
-const handleRefresh = () => {
-  ElMessage.success('刷新成功')
+const rules: FormRules = {
+  name: [{ required: true, message: '请输入知识库名称', trigger: 'blur' }],
+  collection_name: [
+    { required: true, message: '请输入集合名称', trigger: 'blur' },
+    { pattern: /^[a-zA-Z0-9_]+$/, message: '只能包含字母、数字和下划线', trigger: 'blur' }
+  ],
+  dimension: [{ required: true, message: '请输入维度', trigger: 'blur' }]
+}
+
+// Methods
+const loadData = async () => {
+  loading.value = true
+  try {
+    const res = await getKnowledgeBases({
+        page: pagination.page,
+        page_size: pagination.pageSize,
+        keyword: queryParams.keyword,
+        status: queryParams.status
+    })
+    tableData.value = res.data
+    pagination.total = res.meta?.total || 0
+  } catch (error) {
+    console.error(error)
+  } finally {
+    loading.value = false
+  }
+}
+
+const handleSearch = () => {
+  pagination.page = 1
+  loadData()
+}
+
+const handleSizeChange = (val: number) => {
+  pagination.pageSize = val
+  loadData()
+}
+
+const handleCurrentChange = (val: number) => {
+  pagination.page = val
+  loadData()
+}
+
+const handleSelectionChange = (val: KnowledgeBase[]) => {
+  // console.log(val)
+}
+
+const handleAdd = () => {
+  dialogType.value = 'create'
+  formData.id = ''
+  formData.name = ''
+  formData.collection_name = ''
+  formData.description = ''
+  formData.dimension = 768
+  formData.status = 'normal'
+  dialogVisible.value = true
+}
+
+const handleEdit = (row: KnowledgeBase) => {
+  dialogType.value = 'edit'
+  formData.id = row.id
+  formData.name = row.name
+  formData.collection_name = row.collection_name
+  formData.description = row.description || ''
+  formData.status = row.status
+  // dimension cannot be changed
+  dialogVisible.value = true
+}
+
+const handleView = (row: KnowledgeBase) => {
+    ElMessage.info(`查看知识库: ${row.name}`)
+}
+
+const handleDelete = (row: KnowledgeBase) => {
+  ElMessageBox.confirm(
+    `确定要删除知识库 "${row.name}" 吗?此操作不可恢复,且会删除对应的Milvus集合。`,
+    '警告',
+    {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning',
+    }
+  )
+    .then(async () => {
+      try {
+        await deleteKnowledgeBase(row.id)
+        ElMessage.success('删除成功')
+        loadData()
+      } catch (error) {
+        // error handled by request interceptor
+      }
+    })
+    .catch(() => {})
+}
+
+const handleCommand = async (command: string, row: KnowledgeBase) => {
+    if (command === 'toggleStatus') {
+        const newStatus = row.status === 'disabled' ? 'normal' : 'disabled'
+        try {
+            await updateKnowledgeBaseStatus(row.id, newStatus)
+            ElMessage.success('状态更新成功')
+            loadData()
+        } catch (error) {
+            console.error(error)
+        }
+    }
+}
+
+const handleSubmit = async () => {
+  if (!formRef.value) return
+  await formRef.value.validate(async (valid) => {
+    if (valid) {
+      submitLoading.value = true
+      try {
+        if (dialogType.value === 'create') {
+          await createKnowledgeBase({
+            name: formData.name,
+            collection_name: formData.collection_name,
+            description: formData.description,
+            dimension: formData.dimension
+          })
+          ElMessage.success('创建成功')
+        } else {
+          await updateKnowledgeBase(formData.id, {
+            name: formData.name,
+            description: formData.description,
+            status: formData.status
+          })
+          ElMessage.success('更新成功')
+        }
+        dialogVisible.value = false
+        loadData()
+      } catch (error) {
+        console.error(error)
+      } finally {
+        submitLoading.value = false
+      }
+    }
+  })
+}
+
+const resetForm = () => {
+  if (formRef.value) {
+    formRef.value.resetFields()
+  }
+}
+
+// Helpers
+const getStatusType = (status: string) => {
+  const map: Record<string, string> = {
+    normal: 'success',
+    test: 'info',
+    disabled: 'danger'
+  }
+  return map[status] || 'info'
+}
+
+const getStatusText = (status: string) => {
+  const map: Record<string, string> = {
+    normal: '正常',
+    test: '测试',
+    disabled: '已禁用'
+  }
+  return map[status] || status
+}
+
+const formatTime = (time: string) => {
+    if (!time) return '-'
+    return dayjs(time).format('YYYY-MM-DD HH:mm')
+}
+
+// 为图标生成随机颜色或基于名称的固定颜色
+const getIconColor = (name: string) => {
+    const colors = ['#409eff', '#67c23a', '#e6a23c', '#f56c6c', '#909399', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc']
+    let hash = 0
+    for (let i = 0; i < name.length; i++) {
+        hash = name.charCodeAt(i) + ((hash << 5) - hash)
+    }
+    const index = Math.abs(hash) % colors.length
+    return colors[index]
 }
 
 onMounted(() => {
-  // 初始化逻辑
+  loadData()
 })
 </script>
 
@@ -80,7 +404,8 @@ onMounted(() => {
 
 .title-info h2 {
   margin: 0;
-  font-size: 24px;
+  font-size: 20px;
+  font-weight: 600;
   color: #303133;
 }
 
@@ -90,44 +415,51 @@ onMounted(() => {
   font-size: 14px;
 }
 
-.status-card {
-  margin-bottom: 20px;
+.table-card {
+  border-radius: 8px;
 }
 
-.stat-box {
-  text-align: center;
-  padding: 20px;
-  background-color: #f8f9fa;
-  border-radius: 8px;
+.kb-name-cell {
+  display: flex;
+  align-items: flex-start;
+  padding: 8px 0;
 }
 
-.stat-box .label {
-  font-size: 14px;
-  color: #606266;
-  margin-bottom: 8px;
+.kb-icon {
+  margin-right: 12px;
+  margin-top: 4px;
 }
 
-.stat-box .value {
-  font-size: 28px;
-  font-weight: bold;
-  color: #303133;
+.kb-info {
+  flex: 1;
 }
 
-.stat-box .value.success {
-  color: #67c23a;
+.kb-info .name {
+  font-weight: 600;
+  font-size: 14px;
+  color: #303133;
+  margin-bottom: 4px;
 }
 
-.stat-box .value.warning {
-  color: #e6a23c;
+.kb-info .desc {
+  font-size: 12px;
+  color: #909399;
+  line-height: 1.4;
+  display: -webkit-box;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 2;
+  overflow: hidden;
 }
 
-.content-section {
-  background: #fff;
-  border-radius: 4px;
-  min-height: 400px;
+.pagination-container {
   display: flex;
-  justify-content: center;
+  justify-content: space-between;
   align-items: center;
-  border: 1px border #ebeef5;
+  margin-top: 20px;
+}
+
+.pagination-info {
+    color: #909399;
+    font-size: 13px;
 }
 </style>

+ 467 - 0
src/views/documents/KnowledgeSnippet.vue

@@ -0,0 +1,467 @@
+<template>
+  <div class="snippet-container">
+    <div class="header-section">
+      <div class="title-info">
+        <h2>知识片段管理</h2>
+        <p class="subtitle">管理所有知识片段和相关内容</p>
+      </div>
+      <div class="action-buttons">
+        <el-select v-model="queryParams.status" placeholder="所有状态" clearable style="width: 120px; margin-right: 12px" @change="handleSearch">
+          <el-option label="启用" value="normal" />
+          <el-option label="禁用" value="disabled" />
+        </el-select>
+        <el-select v-model="queryParams.kb" placeholder="所有知识库" clearable style="width: 150px; margin-right: 12px" @change="handleSearch">
+          <el-option 
+            v-for="item in kbOptions" 
+            :key="item.value" 
+            :label="item.label" 
+            :value="item.value" 
+          />
+        </el-select>
+        <el-input
+          v-model="queryParams.keyword"
+          placeholder="搜索知识片段..."
+          style="width: 240px; margin-right: 12px"
+          clearable
+          @keyup.enter="handleSearch"
+          @clear="handleSearch"
+        >
+          <template #prefix>
+            <el-icon><Search /></el-icon>
+          </template>
+        </el-input>
+        <el-button type="primary" @click="handleAdd">
+          <el-icon><Plus /></el-icon> 新建片段
+        </el-button>
+      </div>
+    </div>
+
+    <!-- 搜索栏下方工具条 -->
+    <div class="toolbar-section">
+        <el-input
+          v-model="queryParams.contentSearch"
+          placeholder="搜索知识片段"
+          style="width: 300px; margin-right: auto"
+          clearable
+        />
+        <el-button type="primary" @click="handleAdd">
+            <el-icon><Plus /></el-icon> 新建片段
+        </el-button>
+        <el-button>
+            <el-icon><Download /></el-icon> 导出片段
+        </el-button>
+        <el-button>
+            <el-icon><Filter /></el-icon> 筛选
+        </el-button>
+    </div>
+
+    <el-card class="table-card" shadow="never">
+      <el-table :data="tableData" v-loading="loading" style="width: 100%" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" />
+        
+        <el-table-column label="文档名称" min-width="150">
+          <template #default="{ row }">
+            <div class="doc-name-cell">
+                <el-icon class="doc-icon"><Document /></el-icon>
+                <span>{{ row.doc_name }}</span>
+            </div>
+          </template>
+        </el-table-column>
+
+        <el-table-column prop="code" label="片段编号" width="160" />
+
+        <el-table-column label="片段内容" min-width="300">
+            <template #default="{ row }">
+                <div class="content-cell">{{ row.content }}</div>
+            </template>
+        </el-table-column>
+
+        <el-table-column prop="char_count" label="字符数量" width="100" />
+
+        <el-table-column label="元数据信息" min-width="200">
+            <template #default="{ row }">
+                <span class="meta-info">{{ row.meta_info }}</span>
+            </template>
+        </el-table-column>
+
+        <el-table-column prop="status" label="状态" width="80">
+          <template #default="{ row }">
+            <el-tag :type="row.status === 'normal' ? 'success' : 'info'" effect="plain" class="status-tag">
+                {{ row.status === 'normal' ? '启用' : '禁用' }}
+            </el-tag>
+          </template>
+        </el-table-column>
+
+        <el-table-column prop="created_at" label="创建时间" width="160" />
+        <el-table-column prop="updated_at" label="修改时间" width="160" />
+
+        <el-table-column label="操作" width="120" fixed="right">
+          <template #default="{ row }">
+            <el-tooltip content="查看" placement="top">
+                <el-button link type="primary" @click="handleView(row)">
+                    <el-icon><View /></el-icon>
+                </el-button>
+            </el-tooltip>
+            <el-tooltip content="编辑" placement="top">
+                <el-button link type="primary" @click="handleEdit(row)">
+                    <el-icon><Edit /></el-icon>
+                </el-button>
+            </el-tooltip>
+            <el-tooltip content="删除" placement="top">
+                <el-button link type="danger" @click="handleDelete(row)">
+                    <el-icon><Delete /></el-icon>
+                </el-button>
+            </el-tooltip>
+            <el-dropdown trigger="click">
+                <el-button link type="info">
+                    <el-icon><MoreFilled /></el-icon>
+                </el-button>
+                <template #dropdown>
+                    <el-dropdown-menu>
+                        <el-dropdown-item>更多操作</el-dropdown-item>
+                    </el-dropdown-menu>
+                </template>
+            </el-dropdown>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div class="pagination-container">
+        <div class="pagination-info">
+            显示 {{ (pagination.page - 1) * pagination.pageSize + 1 }} 到 
+            {{ Math.min(pagination.page * pagination.pageSize, pagination.total) }} 条,
+            共 {{ pagination.total }} 条记录
+        </div>
+        <el-pagination
+          v-model:current-page="pagination.page"
+          v-model:page-size="pagination.pageSize"
+          :total="pagination.total"
+          :page-sizes="[10, 20, 50, 100]"
+          layout="prev, pager, next, sizes"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </el-card>
+    <el-dialog
+      v-model="dialogVisible"
+      :title="dialogType === 'add' ? '新建片段' : '编辑片段'"
+      width="600px"
+      @close="resetForm"
+    >
+      <el-form :model="formData" label-width="100px">
+        <el-form-item label="所属知识库" required>
+          <el-select v-model="formData.collection_name" placeholder="请选择知识库" :disabled="dialogType === 'edit'">
+            <el-option
+              v-for="item in kbOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="文档名称">
+          <el-input v-model="formData.doc_name" placeholder="请输入文档来源名称" />
+        </el-form-item>
+        <el-form-item label="片段内容" required>
+          <el-input
+            v-model="formData.content"
+            type="textarea"
+            :rows="6"
+            placeholder="请输入片段内容"
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="dialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="handleSubmit" :loading="submitLoading">
+            确定
+          </el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, onMounted } from 'vue'
+import { Search, Plus, Document, View, Edit, Delete, MoreFilled, Download, Filter } from '@element-plus/icons-vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { 
+    getSnippets, 
+    createSnippet, 
+    updateSnippet, 
+    deleteSnippet,
+    type Snippet 
+} from '@/api/snippet'
+import { getKnowledgeBases } from '@/api/knowledge-base'
+
+// Table Data
+const tableData = ref<Snippet[]>([])
+
+const queryParams = reactive({
+  page: 1,
+  pageSize: 10,
+  keyword: '',
+  status: '',
+  kb: '',
+  contentSearch: ''
+})
+
+const loading = ref(false)
+const pagination = reactive({
+  page: 1,
+  pageSize: 10,
+  total: 0
+})
+
+// Dialog
+const dialogVisible = ref(false)
+const dialogType = ref<'add' | 'edit'>('add')
+const submitLoading = ref(false)
+const formData = reactive({
+    id: '',
+    collection_name: '',
+    doc_name: '',
+    content: ''
+})
+
+// Methods
+const loadData = async () => {
+    loading.value = true
+    try {
+        const res = await getSnippets({
+            page: pagination.page,
+            page_size: pagination.pageSize,
+            keyword: queryParams.keyword,
+            kb: queryParams.kb, // 知识库集合名称
+        })
+        if (res.code === 0) {
+            tableData.value = res.data
+            pagination.total = res.meta?.total || 0
+        } else {
+            // 如果返回需要选择知识库的提示,清空列表
+            tableData.value = []
+            pagination.total = 0
+            if (res.message !== "请选择知识库") {
+                ElMessage.warning(res.message)
+            }
+        }
+    } catch (error) {
+        console.error(error)
+        // error handled by interceptor
+    } finally {
+        loading.value = false
+    }
+}
+
+// 加载知识库列表供筛选
+const kbOptions = ref<{label: string, value: string}[]>([])
+const loadKbOptions = async () => {
+    try {
+        const res = await getKnowledgeBases({ page_size: 100 })
+        if (res.code === 0) {
+            kbOptions.value = res.data.map((item: any) => ({
+                label: item.name,
+                value: item.collection_name
+            }))
+            // 修改为默认不选中(查询所有),直接加载数据
+            if (!queryParams.kb) {
+                loadData()
+            }
+        }
+    } catch (error) {
+        console.error("加载知识库选项失败", error)
+    }
+}
+
+onMounted(() => {
+    loadKbOptions()
+})
+
+// Methods
+const handleSearch = () => {
+    pagination.page = 1
+    loadData()
+}
+
+const handleAdd = () => {
+    dialogType.value = 'add'
+    resetForm()
+    // 如果筛选栏选中了知识库,默认填入
+    if (queryParams.kb) {
+        formData.collection_name = queryParams.kb
+    }
+    dialogVisible.value = true
+}
+
+const handleEdit = (row: Snippet) => {
+    dialogType.value = 'edit'
+    formData.id = row.id
+    formData.collection_name = row.collection_name
+    formData.doc_name = row.doc_name
+    formData.content = row.content
+    dialogVisible.value = true
+}
+
+const handleSubmit = async () => {
+    if (!formData.collection_name || !formData.content) {
+        ElMessage.warning('请填写完整信息')
+        return
+    }
+    
+    submitLoading.value = true
+    try {
+        if (dialogType.value === 'add') {
+            await createSnippet({
+                collection_name: formData.collection_name,
+                doc_name: formData.doc_name || '手动添加',
+                content: formData.content,
+                meta_info: ''
+            })
+            ElMessage.success('创建成功')
+        } else {
+            await updateSnippet(formData.id, {
+                collection_name: formData.collection_name,
+                doc_name: formData.doc_name,
+                content: formData.content
+            })
+            ElMessage.success('更新成功')
+        }
+        dialogVisible.value = false
+        loadData()
+    } catch (error) {
+        console.error(error)
+    } finally {
+        submitLoading.value = false
+    }
+}
+
+const resetForm = () => {
+    formData.id = ''
+    formData.collection_name = ''
+    formData.doc_name = ''
+    formData.content = ''
+}
+
+const handleDelete = (row: Snippet) => {
+    ElMessageBox.confirm(
+        '确定要删除该知识片段吗?',
+        '警告',
+        {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning',
+        }
+    ).then(async () => {
+        try {
+            await deleteSnippet(row.id, row.collection_name)
+            ElMessage.success('删除成功')
+            loadData()
+        } catch (error) {
+            // handled
+        }
+    })
+}
+
+const handleView = (row: Snippet) => {
+    ElMessage.info(`查看片段: ${row.code}`)
+}
+
+const handleSelectionChange = (val: any) => {
+    console.log(val)
+}
+
+const handleSizeChange = (val: number) => {
+    pagination.pageSize = val
+    loadData()
+}
+
+const handleCurrentChange = (val: number) => {
+    pagination.page = val
+    loadData()
+}
+</script>
+
+<style scoped>
+.snippet-container {
+  padding: 20px;
+}
+
+.header-section {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+}
+
+.title-info h2 {
+  margin: 0;
+  font-size: 20px;
+  font-weight: 600;
+  color: #303133;
+}
+
+.subtitle {
+  margin: 8px 0 0;
+  color: #909399;
+  font-size: 14px;
+}
+
+.toolbar-section {
+    display: flex;
+    align-items: center;
+    margin-bottom: 16px;
+    gap: 12px;
+}
+
+.table-card {
+  border-radius: 8px;
+}
+
+.doc-name-cell {
+    display: flex;
+    align-items: center;
+    color: #606266;
+}
+
+.doc-icon {
+    margin-right: 8px;
+    font-size: 16px;
+}
+
+.content-cell {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-line-clamp: 2;
+    -webkit-box-orient: vertical;
+    color: #303133;
+    line-height: 1.5;
+}
+
+.meta-info {
+    color: #909399;
+    font-size: 13px;
+}
+
+.status-tag {
+    border-radius: 4px;
+    padding: 0 12px;
+    height: 24px;
+    line-height: 22px;
+}
+
+.pagination-container {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-top: 20px;
+}
+
+.pagination-info {
+    color: #909399;
+    font-size: 13px;
+}
+</style>