From 0f74d0721b7407709b6bdd45dc6b58970820536b Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 09 六月 2026 10:03:46 +0800
Subject: [PATCH] docs(knowledge-base): 更新知识库RAG向量检索功能前端联调文档

---
 src/api/collaborativeApproval/knowledgeBase.js          |   20 +
 doc/20260608_知识库RAG向量检索功能前端联调文档.md                      |  598 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/views/collaborativeApproval/knowledgeBase/index.vue |   72 ++---
 3 files changed, 652 insertions(+), 38 deletions(-)

diff --git "a/doc/20260608_\347\237\245\350\257\206\345\272\223RAG\345\220\221\351\207\217\346\243\200\347\264\242\345\212\237\350\203\275\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md" "b/doc/20260608_\347\237\245\350\257\206\345\272\223RAG\345\220\221\351\207\217\346\243\200\347\264\242\345\212\237\350\203\275\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..cdac388
--- /dev/null
+++ "b/doc/20260608_\347\237\245\350\257\206\345\272\223RAG\345\220\221\351\207\217\346\243\200\347\264\242\345\212\237\350\203\275\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
@@ -0,0 +1,598 @@
+# 鐭ヨ瘑搴撴枃浠朵笂浼犱笌RAG鍚戦噺妫�绱㈠姛鑳藉墠绔仈璋冩枃妗�
+
+## 涓�銆佸姛鑳芥杩�
+
+鐭ヨ瘑搴撴ā鍧楁柊澧炴枃浠朵笂浼犲姛鑳斤紝涓婁紶鐨勬枃浠朵細瀹炴椂杩涘叆鍚戦噺搴撹繘琛屽垏鐗囧鐞嗭紝閰嶅悎AI妯″潡瀹炵幇RAG锛堟绱㈠寮虹敓鎴愶級闂瓟鍔熻兘銆�
+
+### 鍔熻兘妯″潡
+1. **鐭ヨ瘑搴撴枃浠剁鐞�**锛氫娇鐢ㄧ郴缁熷凡鏈夌殑闄勪欢绠$悊鏈哄埗涓婁紶鏂囦欢
+2. **鍚戦噺妫�绱㈠鐞�**锛氭枃浠跺唴瀹硅嚜鍔ㄥ垏鐗囧苟瀛樺叆鍚戦噺搴擄紙Pinecone锛�
+3. **鐭ヨ瘑搴撻棶绛�**锛氬熀浜庝笂浼犳枃浠跺唴瀹硅繘琛屾櫤鑳介棶绛�
+
+## 浜屻�佹暟鎹簱鍙樻洿
+
+### 2.1 鏂板鐭ヨ瘑搴撴枃浠跺悜閲忚褰曡〃
+
+```sql
+-- 鐭ヨ瘑搴撴枃浠跺悜閲忚褰曡〃锛堢敤浜庤窡韪悜閲忓寲鐘舵�侊級
+CREATE TABLE IF NOT EXISTS knowledge_base_vector (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '涓婚敭ID',
+    knowledge_base_id BIGINT NOT NULL COMMENT '鍏宠仈鐭ヨ瘑搴揑D',
+    storage_blob_id BIGINT NOT NULL COMMENT '鍏宠仈鏂囦欢blob ID',
+    file_name VARCHAR(255) NOT NULL COMMENT '鏂囦欢鍚嶇О',
+    file_type VARCHAR(50) NOT NULL COMMENT '鏂囦欢绫诲瀷(docx/pdf/xlsx/txt绛�)',
+    vector_status TINYINT DEFAULT 0 COMMENT '鍚戦噺鍖栫姸鎬�: 0-寰呭鐞�, 1-澶勭悊涓�, 2-宸插畬鎴�, 3-澶辫触',
+    vector_error VARCHAR(500) COMMENT '鍚戦噺鍖栧け璐ュ師鍥�',
+    chunk_count INT DEFAULT 0 COMMENT '鍒囩墖鏁伴噺',
+    namespace VARCHAR(100) COMMENT '鍚戦噺鍛藉悕绌洪棿',
+    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    create_user INT COMMENT '鍒涘缓浜�',
+    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+    update_user INT COMMENT '鏇存柊浜�',
+    tenant_id BIGINT COMMENT '绉熸埛ID',
+    dept_id BIGINT COMMENT '閮ㄩ棬ID',
+    INDEX idx_knowledge_base_id (knowledge_base_id),
+    INDEX idx_storage_blob_id (storage_blob_id),
+    INDEX idx_vector_status (vector_status)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鐭ヨ瘑搴撴枃浠跺悜閲忚褰曡〃';
+```
+
+### 2.2 淇敼鐭ヨ瘑搴撹〃
+
+```sql
+-- 鐭ヨ瘑搴撹〃澧炲姞瀛楁
+ALTER TABLE knowledge_base
+ADD COLUMN file_count INT DEFAULT 0 COMMENT '鏂囦欢鏁伴噺',
+ADD COLUMN total_chunk_count INT DEFAULT 0 COMMENT '鎬诲垏鐗囨暟閲�',
+ADD COLUMN description VARCHAR(500) COMMENT '鐭ヨ瘑搴撴弿杩�';
+```
+
+## 涓夈�佹帴鍙h璁�
+
+### 3.1 鏂囦欢涓婁紶锛堜娇鐢ㄧ郴缁熷凡鏈夋帴鍙o級
+
+**鎺ュ彛鍦板潃**锛歚POST /common/upload`
+
+**璇锋眰鏂瑰紡**锛歮ultipart/form-data
+
+**璇锋眰鍙傛暟**锛�
+| 鍙傛暟鍚� | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|--------|------|------|------|
+| files | MultipartFile[] | 鏄� | 涓婁紶鐨勬枃浠跺垪琛� |
+
+**鍝嶅簲缁撴灉**锛�
+```json
+{
+  "code": 200,
+  "msg": "鎿嶄綔鎴愬姛",
+  "data": [
+    {
+      "id": 123,
+      "name": "鎿嶄綔鎵嬪唽.docx",
+      "url": "/profile/upload/20260608/xxx.docx",
+      "previewURL": "/common/preview/xxx?token=yyy",
+      "downloadURL": "/common/download/xxx?token=yyy",
+      "storageAttachmentId": null
+    }
+  ]
+}
+```
+
+### 3.2 淇濆瓨鐭ヨ瘑搴撴枃浠跺叧鑱旓紙瑙﹀彂鍚戦噺鍖栵級
+
+**閲嶈**锛氫笂浼犲畬鎴愬悗锛�**蹇呴』**璋冪敤姝ゆ帴鍙f潵鍏宠仈鏂囦欢骞惰Е鍙戝悜閲忓寲澶勭悊銆�
+
+**鎺ュ彛鍦板潃**锛歚POST /knowledgeBase/file/save`
+
+**璇锋眰鍙傛暟**锛�
+```json
+{
+  "knowledgeBaseId": 10,
+  "storageBlobIds": [123, 124]
+}
+```
+
+| 鍙傛暟鍚� | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|--------|------|------|------|
+| knowledgeBaseId | Long | 鏄� | 鐭ヨ瘑搴揑D |
+| storageBlobIds | List<Long> | 鏄� | 涓婁紶杩斿洖鐨勬枃浠禸lob ID鍒楄〃 |
+
+**鍝嶅簲缁撴灉**锛�
+```json
+{
+  "code": 200,
+  "msg": "鎿嶄綔鎴愬姛"
+}
+```
+
+**璋冪敤鏃舵満**锛氬湪 `/common/upload` 涓婁紶鎴愬姛鍚庯紝绔嬪嵆璋冪敤姝ゆ帴鍙c��
+
+**娉ㄦ剰**锛氭鎺ュ彛浼氾細
+1. 淇濆瓨闄勪欢鍏宠仈鍒� `storage_attachment` 琛�
+2. 鍒涘缓鍚戦噺璁板綍鍒� `knowledge_base_vector` 琛�
+3. **寮傛瑙﹀彂鍚戦噺鍖栧鐞�**锛堟枃浠跺垏鐗� 鈫� 鍚戦噺宓屽叆 鈫� 瀛樺叆Pinecone锛�
+
+---
+
+### 3.3 鏌ヨ鍚戦噺鍖栫姸鎬侊紙鎺ㄨ崘浣跨敤锛�
+
+**鎺ュ彛鍦板潃**锛歚GET /knowledgeBase/vector/status/{knowledgeBaseId}`
+
+**鍝嶅簲缁撴灉**锛�
+```json
+{
+  "code": 200,
+  "data": [
+    {
+      "id": 1,
+      "storageBlobId": 123,
+      "fileName": "鎿嶄綔鎵嬪唽.docx",
+      "fileType": "docx",
+      "vectorStatus": 2,
+      "chunkCount": 15,
+      "namespace": "kb-10",
+      "vectorError": null
+    }
+  ]
+}
+```
+
+### 3.4 鍒犻櫎鐭ヨ瘑搴撴枃浠�
+
+**鎺ュ彛鍦板潃**锛歚DELETE /knowledgeBase/file/delete`
+
+**璇锋眰鍙傛暟**锛�
+```json
+{
+  "ids": [1, 2, 3]
+}
+```
+
+**娉ㄦ剰**锛氭鎺ュ彛浼氬悓鏃跺垹闄ゅ悜閲忓簱涓殑鐩稿叧鏁版嵁銆�
+
+**鍝嶅簲缁撴灉**锛�
+```json
+{
+  "code": 200,
+  "msg": "鎿嶄綔鎴愬姛"
+}
+```
+
+### 3.5 閲嶆柊鍚戦噺鍖栨枃浠�
+
+**鎺ュ彛鍦板潃**锛歚POST /knowledgeBase/vector/reprocess/{vectorId}`
+
+**鍝嶅簲缁撴灉**锛�
+```json
+{
+  "code": 200,
+  "msg": "宸查噸鏂版彁浜ゅ悜閲忓寲浠诲姟"
+}
+```
+
+### 3.6 鐭ヨ瘑搴撻棶绛旀帴鍙�
+
+**鎺ュ彛鍦板潃**锛歚POST /ai/knowledge/chat`锛堟祦寮忚繑鍥烇級
+
+**璇锋眰鍙傛暟**锛�
+```json
+{
+  "knowledgeBaseId": 10,
+  "memoryId": "session-xxx",
+  "question": "濡備綍鎿嶄綔瀹℃壒娴佺▼锛�"
+}
+```
+
+| 鍙傛暟鍚� | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|--------|------|------|------|
+| knowledgeBaseId | Long | 鏄� | 鐭ヨ瘑搴揑D |
+| memoryId | String | 鏄� | 浼氳瘽ID锛岀敤浜庝繚鎸佷笂涓嬫枃 |
+| question | String | 鏄� | 鐢ㄦ埛鎻愰棶鍐呭 |
+
+**鍝嶅簲缁撴灉**锛堟祦寮忚繑鍥� `text/stream;charset=utf-8`锛夛細
+```
+鏍规嵁鐭ヨ瘑搴撳唴瀹癸紝瀹℃壒娴佺▼鐨勬搷浣滄楠ゅ涓嬶細
+1. 鐧诲綍绯荤粺鍚庤繘鍏ュ鎵圭鐞嗘ā鍧�...
+```
+
+### 3.7 鐭ヨ瘑搴撻棶绛斾細璇濆巻鍙�
+
+**鎺ュ彛鍦板潃**锛歚GET /ai/knowledge/history/{memoryId}`
+
+**鍝嶅簲缁撴灉**锛�
+```json
+{
+  "code": 200,
+  "data": [
+    {
+      "role": "user",
+      "content": "濡備綍鎿嶄綔瀹℃壒娴佺▼锛�",
+      "createTime": "2026-06-08 10:00:00"
+    },
+    {
+      "role": "assistant",
+      "content": "鏍规嵁鐭ヨ瘑搴撳唴瀹�...",
+      "createTime": "2026-06-08 10:01:00"
+    }
+  ]
+}
+```
+
+## 鍥涖�佹枃浠剁被鍨嬫敮鎸�
+
+| 鏂囦欢绫诲瀷 | 鎵╁睍鍚� | 璇存槑 |
+|----------|--------|------|
+| Word鏂囨。 | .docx | 鏀寔鏂囨湰鎻愬彇 |
+| Excel琛ㄦ牸 | .xlsx, .xls | 鏀寔琛ㄦ牸鍐呭鎻愬彇 |
+| PDF鏂囨。 | .pdf | 鏀寔PDF鏂囨湰鎻愬彇 |
+| 鏂囨湰鏂囦欢 | .txt, .md, .json, .csv | 鐩存帴璇诲彇鍐呭 |
+| 浠g爜鏂囦欢 | .java, .js, .vue, .html, .sql绛� | 鐩存帴璇诲彇鍐呭 |
+
+**鏂囦欢澶у皬闄愬埗**锛氬崟鏂囦欢鏈�澶�10MB锛堢郴缁熼粯璁ら檺鍒讹級
+
+## 浜斻�佸悜閲忓寲鐘舵�佽鏄�
+
+| 鐘舵�佸�� | 鐘舵�佸悕绉� | 璇存槑 |
+|--------|----------|------|
+| 0 | 寰呭鐞� | 鏂囦欢宸蹭笂浼狅紝绛夊緟鍚戦噺鍖栧鐞� |
+| 1 | 澶勭悊涓� | 姝e湪杩涜鍚戦噺鍒囩墖澶勭悊 |
+| 2 | 宸插畬鎴� | 鍚戦噺鍖栧畬鎴愶紝鍙繘琛屾绱㈤棶绛� |
+| 3 | 澶辫触 | 鍚戦噺鍖栧け璐ワ紝闇�閲嶆柊澶勭悊 |
+
+## 鍏�佸墠绔粍浠惰璁�
+
+### 6.1 鏂囦欢涓婁紶缁勪欢锛堜娇鐢ㄧ郴缁熷凡鏈変笂浼狅級
+
+```vue
+<template>
+  <div class="knowledge-file-upload">
+    <!-- 鐭ヨ瘑搴撻�夋嫨 -->
+    <el-select v-model="selectedKnowledgeBase" placeholder="閫夋嫨鐭ヨ瘑搴�" style="width: 200px">
+      <el-option
+        v-for="kb in knowledgeBaseList"
+        :key="kb.id"
+        :label="kb.title"
+        :value="kb.id"
+      />
+    </el-select>
+
+    <!-- 浣跨敤绯荤粺宸叉湁涓婁紶缁勪欢 -->
+    <el-upload
+      :action="uploadUrl"
+      :headers="uploadHeaders"
+      :on-success="handleUploadSuccess"
+      :before-upload="beforeUpload"
+      :accept="acceptTypes"
+      :limit="10"
+      :file-list="fileList"
+      multiple
+    >
+      <el-button type="primary">鐐瑰嚮涓婁紶</el-button>
+      <template #tip>
+        <div class="el-upload__tip">
+          鏀寔 docx銆亁lsx銆乸df銆乼xt 绛夋牸寮忥紝鍗曟枃浠朵笉瓒呰繃10MB
+        </div>
+      </template>
+    </el-upload>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+import { getToken } from '@/utils/auth'
+import request from '@/utils/request'
+
+const uploadUrl = '/common/upload'
+const uploadHeaders = { Authorization: 'Bearer ' + getToken() }
+const acceptTypes = '.docx,.xlsx,.xls,.pdf,.txt,.md,.json,.csv'
+const uploadedBlobs = ref([])
+
+const beforeUpload = (file) => {
+  const maxSize = 10 * 1024 * 1024
+  if (file.size > maxSize) {
+    ElMessage.error('鏂囦欢澶у皬涓嶈兘瓒呰繃10MB')
+    return false
+  }
+  return true
+}
+
+// 涓婁紶鎴愬姛鍚庝繚瀛樻枃浠跺叧鑱斿苟瑙﹀彂鍚戦噺鍖�
+const handleUploadSuccess = async (response, file) => {
+  if (response.code === 200) {
+    uploadedBlobs.value.push(...response.data)
+    // 璋冪敤鐭ヨ瘑搴撴枃浠朵繚瀛樻帴鍙o紝鍏宠仈鏂囦欢骞惰Е鍙戝悜閲忓寲
+    await saveKnowledgeBaseFiles()
+    ElMessage.success('鏂囦欢涓婁紶鎴愬姛锛屾鍦ㄥ鐞嗗悜閲忓寲...')
+    refreshVectorStatus()
+  } else {
+    ElMessage.error(response.msg)
+  }
+}
+
+// 淇濆瓨鏂囦欢鍏宠仈鍒扮煡璇嗗簱骞惰Е鍙戝悜閲忓寲
+const saveKnowledgeBaseFiles = async () => {
+  await request.post('/knowledgeBase/file/save', {
+    knowledgeBaseId: selectedKnowledgeBase.value,
+    storageBlobIds: uploadedBlobs.value.map(b => b.id)
+  })
+  uploadedBlobs.value = []
+}
+</script>
+```
+
+### 6.2 鏂囦欢鍒楄〃缁勪欢
+
+```vue
+<template>
+  <el-table :data="fileList" v-loading="loading">
+    <el-table-column prop="name" label="鏂囦欢鍚�" width="200" />
+    <el-table-column prop="fileType" label="绫诲瀷" width="80">
+      <template #default="{ row }">
+        {{ getFileType(row.name) }}
+      </template>
+    </el-table-column>
+    <el-table-column prop="vectorStatus" label="鍚戦噺鍖栫姸鎬�" width="120">
+      <template #default="{ row }">
+        <el-tag :type="getVectorStatusType(row.vectorStatus)">
+          {{ getVectorStatusText(row.vectorStatus) }}
+        </el-tag>
+      </template>
+    </el-table-column>
+    <el-table-column prop="chunkCount" label="鍒囩墖鏁�" width="80" />
+    <el-table-column prop="createTime" label="涓婁紶鏃堕棿" width="160" />
+    <el-table-column label="鎿嶄綔" width="180">
+      <template #default="{ row }">
+        <el-button type="primary" size="small" link @click="previewFile(row)">棰勮</el-button>
+        <el-button type="primary" size="small" link @click="downloadFile(row)">涓嬭浇</el-button>
+        <el-button
+          v-if="row.vectorStatus === 3"
+          type="warning"
+          size="small"
+          link
+          @click="revectorFile(row)"
+        >閲嶆柊澶勭悊</el-button>
+        <el-button type="danger" size="small" link @click="deleteFile(row)">鍒犻櫎</el-button>
+      </template>
+    </el-table-column>
+  </el-table>
+</template>
+
+<script setup>
+import request from '@/utils/request'
+
+const getFileType = (name) => {
+  return name?.split('.').pop() || ''
+}
+
+const getVectorStatusText = (status) => {
+  const statusMap = { 0: '寰呭鐞�', 1: '澶勭悊涓�', 2: '宸插畬鎴�', 3: '澶辫触' }
+  return statusMap[status] || '鏈煡'
+}
+
+const getVectorStatusType = (status) => {
+  const typeMap = { 0: 'info', 1: 'warning', 2: 'success', 3: 'danger' }
+  return typeMap[status] || 'info'
+}
+
+const previewFile = (row) => {
+  window.open(row.previewURL, '_blank')
+}
+
+const downloadFile = (row) => {
+  window.open(row.downloadURL, '_blank')
+}
+
+const deleteFile = async (row) => {
+  await request.delete('/knowledgeBase/file/delete', { data: [row.id] })
+  ElMessage.success('鍒犻櫎鎴愬姛')
+  refreshFileList()
+}
+
+const revectorFile = async (row) => {
+  await request.post(`/knowledgeBase/vector/reprocess/${row.vectorId}`)
+  ElMessage.success('宸查噸鏂版彁浜ゅ悜閲忓寲浠诲姟')
+  refreshVectorStatus()
+}
+</script>
+```
+
+### 6.3 鐭ヨ瘑搴撻棶绛旂粍浠�
+
+```vue
+<template>
+  <div class="knowledge-chat">
+    <!-- 鐭ヨ瘑搴撻�夋嫨 -->
+    <div class="kb-selector">
+      <el-select v-model="selectedKnowledgeBase" placeholder="閫夋嫨鐭ヨ瘑搴�">
+        <el-option v-for="kb in knowledgeBaseList" :key="kb.id" :label="kb.title" :value="kb.id" />
+      </el-select>
+    </div>
+
+    <!-- 鑱婂ぉ鍖哄煙 -->
+    <div class="chat-container">
+      <div class="message-list" ref="messageList">
+        <div v-for="msg in messages" :key="msg.id" :class="['message', msg.role]">
+          <div class="message-content">{{ msg.content }}</div>
+        </div>
+        <div v-if="streamingContent" class="message assistant">
+          <div class="message-content">{{ streamingContent }}</div>
+        </div>
+      </div>
+
+      <!-- 杈撳叆鍖哄煙 -->
+      <div class="input-area">
+        <el-input
+          v-model="question"
+          placeholder="杈撳叆闂锛屽熀浜庣煡璇嗗簱鍐呭鍥炵瓟..."
+          @keyup.enter="sendQuestion"
+        />
+        <el-button type="primary" @click="sendQuestion" :loading="sending">鍙戦��</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, nextTick } from 'vue'
+import { getToken } from '@/utils/auth'
+
+const selectedKnowledgeBase = ref(null)
+const question = ref('')
+const messages = ref([])
+const sending = ref(false)
+const streamingContent = ref('')
+const memoryId = ref('kb-chat-' + Date.now())
+
+const sendQuestion = async () => {
+  if (!question.value.trim() || !selectedKnowledgeBase.value) return
+
+  sending.value = true
+  streamingContent.value = ''
+  messages.value.push({ id: Date.now(), role: 'user', content: question.value })
+
+  try {
+    const response = await fetch('/ai/knowledge/chat', {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/json',
+        Authorization: 'Bearer ' + getToken()
+      },
+      body: JSON.stringify({
+        knowledgeBaseId: selectedKnowledgeBase.value,
+        memoryId: memoryId.value,
+        question: question.value
+      })
+    })
+
+    // 娴佸紡璇诲彇鍝嶅簲
+    const reader = response.body.getReader()
+    const decoder = new TextDecoder()
+
+    while (true) {
+      const { done, value } = await reader.read()
+      if (done) break
+      streamingContent.value += decoder.decode(value, { stream: true })
+      // 婊氬姩鍒板簳閮�
+      nextTick(() => {
+        const list = document.querySelector('.message-list')
+        if (list) list.scrollTop = list.scrollHeight
+      })
+    }
+
+    messages.value.push({ id: Date.now(), role: 'assistant', content: streamingContent.value })
+    streamingContent.value = ''
+  } catch (error) {
+    ElMessage.error('闂瓟璇锋眰澶辫触')
+  } finally {
+    sending.value = false
+    question.value = ''
+  }
+}
+</script>
+
+<style scoped>
+.knowledge-chat {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+.kb-selector {
+  padding: 10px;
+  border-bottom: 1px solid #eee;
+}
+.chat-container {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+}
+.message-list {
+  flex: 1;
+  overflow-y: auto;
+  padding: 10px;
+}
+.message {
+  margin-bottom: 10px;
+  padding: 10px;
+  border-radius: 8px;
+}
+.message.user {
+  background: #e6f7ff;
+  text-align: right;
+}
+.message.assistant {
+  background: #f5f5f5;
+}
+.input-area {
+  padding: 10px;
+  display: flex;
+  gap: 10px;
+}
+</style>
+```
+
+## 涓冦�佷笟鍔℃祦绋�
+
+### 7.1 鏂囦欢涓婁紶娴佺▼锛堥噸瑕侊級
+
+```
+鍓嶇璋冪敤 /common/upload 涓婁紶鏂囦欢
+    鈫�
+鑾峰彇 StorageBlobVO 鍒楄〃锛堝寘鍚玝lobId銆侀瑙圲RL銆佷笅杞経RL锛�
+    鈫�
+璋冪敤 /knowledgeBase/file/save 鍏宠仈鏂囦欢鍒扮煡璇嗗簱
+    鈫�
+鍚庣鍒涘缓鍚戦噺璁板綍 鈫� 寮傛瑙﹀彂鍚戦噺鍖栧鐞�
+    鈫�
+寮傛浠诲姟锛氭彁鍙栨枃浠舵枃鏈� 鈫� 鍒囩墖 鈫� 璋冪敤Embedding妯″瀷鐢熸垚鍚戦噺 鈫� 瀛樺叆Pinecone
+    鈫�
+鏇存柊 knowledge_base_vector 琛ㄧ姸鎬佷负宸插畬鎴�
+```
+
+**鍏抽敭鐐�**锛�
+- 蹇呴』璋冪敤 `/knowledgeBase/file/save` 鎺ュ彛鎵嶈兘瑙﹀彂鍚戦噺鍖�
+- 鍚戦噺鍖栨槸寮傛澶勭悊鐨勶紝涓嶄細闃诲璇锋眰
+- 鍓嶇鍙�氳繃杞 `/knowledgeBase/vector/status/{knowledgeBaseId}` 鏌ョ湅澶勭悊杩涘害
+
+### 7.2 鐭ヨ瘑搴撻棶绛旀祦绋�
+
+```
+鐢ㄦ埛鎻愰棶 鈫� 璋冪敤 Embedding 妯″瀷瀵归棶棰樺悜閲忓寲
+    鈫�
+鍦� Pinecone 涓绱紙鍛藉悕绌洪棿: kb-{knowledgeBaseId})
+    鈫�
+鑾峰彇鐩稿叧鍒囩墖鏂囨湰 鈫� 浣滀负涓婁笅鏂� + 鐢ㄦ埛闂鍙戠粰 AI 妯″瀷
+    鈫�
+AI 娴佸紡鐢熸垚鍥炵瓟 鈫� 杩斿洖鍓嶇
+```
+
+## 鍏�佹妧鏈疄鐜拌鐐�
+
+### 8.1 鏂囨湰鍒囩墖绛栫暐
+
+- **鍒囩墖澶у皬**锛氶粯璁ゆ瘡鐗� 500 瀛楃
+- **閲嶅彔澶у皬**锛氶粯璁� 100 瀛楃閲嶅彔锛屼繚璇佽涔夎繛璐�
+- **鍒囩墖鍏冩暟鎹�**锛氬寘鍚枃浠禝D銆佺煡璇嗗簱ID銆佸垏鐗囩储寮�
+
+### 8.2 鍚戦噺鍛藉悕绌洪棿
+
+姣忎釜鐭ヨ瘑搴撲娇鐢ㄧ嫭绔嬪懡鍚嶇┖闂达細`kb-{knowledgeBaseId}`
+
+## 涔濄�佹敞鎰忎簨椤�
+
+1. **鏂囦欢涓婁紶蹇呴』璋冪敤 `/knowledgeBase/file/save` 瑙﹀彂鍚戦噺鍖�**
+2. 鍒犻櫎鏂囦欢鏃跺悓姝ュ垹闄ゅ悜閲忓簱涓殑鐩稿叧鍒囩墖
+3. 澶ф枃浠跺悜閲忓寲鍙兘鑰楁椂杈冮暱锛屽墠绔渶杞鐘舵�佹垨鏄剧ず杩涘害
+4. 鐭ヨ瘑搴撻棶绛斾緷璧栧悜閲忔绱㈣川閲忥紝寤鸿浼樺寲鍒囩墖绛栫暐
+5. 涓嶅悓鐭ヨ瘑搴撲娇鐢ㄤ笉鍚屽懡鍚嶇┖闂达紝閬垮厤鏁版嵁娣锋穯
+
+## 鍗併�侀敊璇爜璇存槑
+
+| 閿欒鐮� | 璇存槑 |
+|--------|------|
+| 40001 | 鏂囦欢绫诲瀷涓嶆敮鎸� |
+| 40002 | 鏂囦欢澶у皬瓒呭嚭闄愬埗 |
+| 40003 | 鐭ヨ瘑搴撲笉瀛樺湪 |
+| 50001 | 鏂囦欢涓婁紶澶辫触 |
+| 50002 | 鏂囦欢鍐呭鎻愬彇澶辫触 |
+| 50003 | 鍚戦噺鍖栧鐞嗗け璐� |
+| 50004 | 鍚戦噺妫�绱㈠け璐� |
\ No newline at end of file
diff --git a/src/api/collaborativeApproval/knowledgeBase.js b/src/api/collaborativeApproval/knowledgeBase.js
index c032bc2..af266bf 100644
--- a/src/api/collaborativeApproval/knowledgeBase.js
+++ b/src/api/collaborativeApproval/knowledgeBase.js
@@ -54,7 +54,7 @@
   });
 }
 
-// 鏌ヨ鐭ヨ瘑搴撴枃浠跺悜閲忓寲鐘舵��
+// 鏌ヨ鐭ヨ瘑搴撴枃浠跺悜閲忓寲鐘舵�侊紙鍖呭惈鏂囦欢鍒楄〃锛�
 export function getVectorStatus(knowledgeBaseId) {
   return request({
     url: `/knowledgeBase/vector/status/${knowledgeBaseId}`,
@@ -62,6 +62,24 @@
   });
 }
 
+// 淇濆瓨鐭ヨ瘑搴撴枃浠跺叧鑱旓紙瑙﹀彂鍚戦噺鍖栵級
+export function saveKnowledgeBaseFiles(data) {
+  return request({
+    url: "/knowledgeBase/file/save",
+    method: "post",
+    data,
+  });
+}
+
+// 鍒犻櫎鐭ヨ瘑搴撴枃浠�
+export function deleteKnowledgeBaseFile(ids) {
+  return request({
+    url: "/knowledgeBase/file/delete",
+    method: "delete",
+    data: ids,
+  });
+}
+
 // 閲嶆柊鍚戦噺鍖栨枃浠�
 export function reprocessVector(vectorId) {
   return request({
diff --git a/src/views/collaborativeApproval/knowledgeBase/index.vue b/src/views/collaborativeApproval/knowledgeBase/index.vue
index 669b260..ab78893 100644
--- a/src/views/collaborativeApproval/knowledgeBase/index.vue
+++ b/src/views/collaborativeApproval/knowledgeBase/index.vue
@@ -256,6 +256,7 @@
               :action="uploadUrl"
               :headers="uploadHeaders"
               :on-success="handleUploadSuccess"
+              :on-change="handleFileChange"
               :before-upload="beforeUpload"
               :accept="acceptTypes"
               :file-list="uploadFileList"
@@ -398,9 +399,10 @@
   updateKnowledgeBase,
   getVectorStatus,
   reprocessVector,
-  knowledgeChat
+  knowledgeChat,
+  saveKnowledgeBaseFiles,
+  deleteKnowledgeBaseFile
 } from "@/api/collaborativeApproval/knowledgeBase.js";
-import { attachmentList, createAttachment, deleteAttachment } from "@/api/basicData/storageAttachment.js";
 import useUserStore from '@/store/modules/user';
 import { userListNoPageByTenantId } from '@/api/system/user.js';
 import { getToken } from '@/utils/auth';
@@ -874,33 +876,17 @@
   refreshFileList();
 };
 
-// 鍒锋柊鏂囦欢鍒楄〃
+// 鍒锋柊鏂囦欢鍒楄〃锛堢洿鎺ヤ娇鐢ㄥ悜閲忕姸鎬佹帴鍙o級
 const refreshFileList = async () => {
   if (!selectedKnowledgeBaseId.value) return;
 
   fileLoading.value = true;
   try {
-    // 鑾峰彇闄勪欢鍒楄〃
-    const attachmentRes = await attachmentList({
-      recordType: 'knowledge_base',
-      recordId: selectedKnowledgeBaseId.value,
-      application: 'rag_file'
-    });
-
-    // 鑾峰彇鍚戦噺鍖栫姸鎬�
-    const vectorRes = await getVectorStatus(selectedKnowledgeBaseId.value);
-
-    // 鍚堝苟鏁版嵁
-    const vectorMap = {};
-    (vectorRes.data || []).forEach(v => {
-      vectorMap[v.storageBlobId] = v;
-    });
-
-    fileList.value = (attachmentRes.data || []).map(file => ({
-      ...file,
-      vectorStatus: vectorMap[file.storageBlobId]?.vectorStatus ?? 0,
-      chunkCount: vectorMap[file.storageBlobId]?.chunkCount ?? 0,
-      vectorId: vectorMap[file.storageBlobId]?.id
+    const res = await getVectorStatus(selectedKnowledgeBaseId.value);
+    fileList.value = (res.data || []).map(item => ({
+      ...item,
+      name: item.fileName,
+      vectorId: item.id
     }));
   } catch (error) {
     console.error('鑾峰彇鏂囦欢鍒楄〃澶辫触:', error);
@@ -924,23 +910,35 @@
 const handleUploadSuccess = async (response, file) => {
   if (response.code === 200) {
     uploadedBlobs.value.push(...response.data);
-    await saveAttachment();
-    ElMessage.success('鏂囦欢涓婁紶鎴愬姛锛屾鍦ㄥ鐞嗗悜閲忓寲...');
-    uploadedBlobs.value = [];
-    refreshFileList();
   } else {
     ElMessage.error(response.msg || '涓婁紶澶辫触');
   }
 };
 
-// 淇濆瓨闄勪欢鍏宠仈鍒扮煡璇嗗簱
-const saveAttachment = async () => {
-  await createAttachment({
-    recordType: 'knowledge_base',
-    recordId: selectedKnowledgeBaseId.value,
-    application: 'rag_file',
-    storageBlobDTOs: uploadedBlobs.value.map(b => b.id)
-  });
+// 涓婁紶鏂囦欢鍙樺寲澶勭悊锛堢敤浜庢娴嬫墍鏈夋枃浠朵笂浼犲畬鎴愶級
+const handleFileChange = (file, fileList) => {
+  // 褰撴枃浠剁姸鎬佸彉鍖栦笖娌℃湁姝e湪涓婁紶鐨勬枃浠舵椂锛岀粺涓�鎻愪氦
+  if (file.status === 'success' && fileList.every(f => f.status === 'success' || f.status === 'fail')) {
+    if (uploadedBlobs.value.length > 0) {
+      saveKnowledgeBaseFilesAndRefresh();
+    }
+  }
+};
+
+// 淇濆瓨鐭ヨ瘑搴撴枃浠跺叧鑱斿苟瑙﹀彂鍚戦噺鍖�
+const saveKnowledgeBaseFilesAndRefresh = async () => {
+  try {
+    await saveKnowledgeBaseFiles({
+      knowledgeBaseId: selectedKnowledgeBaseId.value,
+      storageBlobIds: uploadedBlobs.value.map(b => b.id)
+    });
+    ElMessage.success('鏂囦欢涓婁紶鎴愬姛锛屾鍦ㄥ鐞嗗悜閲忓寲...');
+  } catch (error) {
+    ElMessage.error('淇濆瓨鏂囦欢鍏宠仈澶辫触');
+  } finally {
+    uploadedBlobs.value = [];
+    refreshFileList();
+  }
 };
 
 // 鑾峰彇鏂囦欢绫诲瀷
@@ -987,7 +985,7 @@
       type: 'warning'
     });
 
-    await deleteAttachment([row.id]);
+    await deleteKnowledgeBaseFile([row.id]);
     ElMessage.success('鍒犻櫎鎴愬姛');
     refreshFileList();
   } catch (error) {

--
Gitblit v1.9.3