From f2d004a07d198d6d483f93228005506ae5c70ed2 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 09 六月 2026 16:43:04 +0800
Subject: [PATCH] feat(collaborativeApproval): 添加知识库RAG向量检索问答功能

---
 doc/知识库模块前端实现文档.md                                      | 1212 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/api/collaborativeApproval/knowledgeBase.js          |   19 
 vite.config.js                                          |    2 
 src/views/collaborativeApproval/knowledgeBase/index.vue |  133 +++++
 4 files changed, 1,352 insertions(+), 14 deletions(-)

diff --git "a/doc/\347\237\245\350\257\206\345\272\223\346\250\241\345\235\227\345\211\215\347\253\257\345\256\236\347\216\260\346\226\207\346\241\243.md" "b/doc/\347\237\245\350\257\206\345\272\223\346\250\241\345\235\227\345\211\215\347\253\257\345\256\236\347\216\260\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..c2ea6e2
--- /dev/null
+++ "b/doc/\347\237\245\350\257\206\345\272\223\346\250\241\345\235\227\345\211\215\347\253\257\345\256\236\347\216\260\346\226\207\346\241\243.md"
@@ -0,0 +1,1212 @@
+# 鐭ヨ瘑搴撴ā鍧楀畬鏁村墠绔疄鐜版枃妗�
+
+## 涓�銆佹ā鍧楁杩�
+
+鐭ヨ瘑搴撴ā鍧楁槸涓�涓泦鎴愪簡RAG(妫�绱㈠寮虹敓鎴�)鎶�鏈殑鏅鸿兘鐭ヨ瘑绠$悊绯荤粺,鏀寔:
+- **鐭ヨ瘑搴揅RUD绠$悊** - 鍒涘缓銆佺紪杈戙�佸垹闄ゃ�佹煡璇㈢煡璇嗗簱
+- **鏂囦欢涓婁紶涓庡悜閲忓寲** - 鏀寔澶氱鏂囦欢鏍煎紡,鑷姩杩涜鍚戦噺鍒囩墖澶勭悊
+- **鏅鸿兘闂瓟** - 鍩轰簬涓婁紶鏂囦欢鍐呭杩涜AI闂瓟
+- **鏂囦欢绠$悊** - 鏌ョ湅鏂囦欢鍚戦噺鍖栫姸鎬�,鏀寔閲嶆柊澶勭悊鍜屽垹闄�
+
+### 鎶�鏈灦鏋�
+- **鍓嶇妗嗘灦**: Vue 3 + Composition API
+- **UI缁勪欢搴�**: Element Plus
+- **鍚戦噺鏁版嵁搴�**: Pinecone
+- **AI妯″瀷**: 闃块噷浜戦�氫箟鍗冮棶
+- **鏂囦欢澶勭悊**: 鏀寔docx銆亁lsx銆乸df銆乼xt銆乵d绛夋牸寮�
+
+---
+
+## 浜屻�佹枃浠剁粨鏋�
+
+```
+src/
+鈹溾攢鈹� api/
+鈹�   鈹斺攢鈹� collaborativeApproval/
+鈹�       鈹斺攢鈹� knowledgeBase.js          # API鎺ュ彛灏佽
+鈹溾攢鈹� views/
+鈹�   鈹斺攢鈹� collaborativeApproval/
+鈹�       鈹斺攢鈹� knowledgeBase/
+鈹�           鈹斺攢鈹� index.vue              # 涓婚〉闈㈢粍浠�
+鈹斺攢鈹� components/
+    鈹溾攢鈹� PIMTable/
+    鈹�   鈹斺攢鈹� PIMTable.vue              # 琛ㄦ牸缁勪欢
+    鈹斺攢鈹� Dialog/
+        鈹斺攢鈹� FormDialog.vue             # 寮圭獥缁勪欢
+```
+
+---
+
+## 涓夈�丄PI鎺ュ彛瀹氫箟
+
+### 3.1 鏂囦欢浣嶇疆
+`src/api/collaborativeApproval/knowledgeBase.js`
+
+### 3.2 瀹屾暣鎺ュ彛鍒楄〃
+
+```javascript
+import request from "@/utils/request";
+import { getToken } from '@/utils/auth';
+
+// 1. 鏌ヨ鐭ヨ瘑搴撳垪琛�(鍒嗛〉)
+export function listKnowledgeBase(query) {
+  return request({
+    url: "/knowledgeBase/getList",
+    method: "get",
+    params: query,
+  });
+}
+
+// 2. 鏂板鐭ヨ瘑搴�
+export function addKnowledgeBase(data) {
+  return request({
+    url: "/knowledgeBase/add",
+    method: "post",
+    data: data,
+  });
+}
+
+// 3. 淇敼鐭ヨ瘑搴�
+export function updateKnowledgeBase(data) {
+  return request({
+    url: "/knowledgeBase/update",
+    method: "post",
+    data: data,
+  });
+}
+
+// 4. 鍒犻櫎鐭ヨ瘑搴�
+export function delKnowledgeBase(query) {
+  return request({
+    url: "/knowledgeBase/delete",
+    method: "delete",
+    data: query,
+  });
+}
+
+// 5. 鏌ヨ鐭ヨ瘑搴撴枃浠跺悜閲忓寲鐘舵��(鍖呭惈鏂囦欢鍒楄〃)
+export function getVectorStatus(knowledgeBaseId) {
+  return request({
+    url: `/knowledgeBase/vector/status/${knowledgeBaseId}`,
+    method: "get",
+  });
+}
+
+// 6. 淇濆瓨鐭ヨ瘑搴撴枃浠跺叧鑱�(瑙﹀彂鍚戦噺鍖�)
+export function saveKnowledgeBaseFiles(data) {
+  return request({
+    url: "/knowledgeBase/file/save",
+    method: "post",
+    data,
+  });
+}
+
+// 7. 鍒犻櫎鐭ヨ瘑搴撴枃浠�
+export function deleteKnowledgeBaseFile(ids) {
+  return request({
+    url: "/knowledgeBase/file/delete",
+    method: "delete",
+    data: ids,
+  });
+}
+
+// 8. 閲嶆柊鍚戦噺鍖栨枃浠�
+export function reprocessVector(vectorId) {
+  return request({
+    url: `/knowledgeBase/vector/reprocess/${vectorId}`,
+    method: "post",
+  });
+}
+
+// 9. 鐭ヨ瘑搴撻棶绛�(娴佸紡)
+export function knowledgeChat(data, onMessage) {
+  const token = getToken();
+  return fetch(import.meta.env.VITE_APP_BASE_API + '/ai/knowledge/chat', {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'Authorization': 'Bearer ' + token
+    },
+    body: JSON.stringify(data)
+  });
+}
+
+// 10. 鏌ヨ鐭ヨ瘑搴撻棶绛斿巻鍙�
+export function getKnowledgeHistory(memoryId) {
+  return request({
+    url: `/ai/knowledge/history/${memoryId}`,
+    method: "get",
+  });
+}
+```
+
+### 3.3 鎺ュ彛鍙傛暟璇存槑
+
+#### 鐭ヨ瘑搴撳垪琛ㄦ煡璇�
+```javascript
+// 璇锋眰鍙傛暟
+{
+  current: 1,        // 褰撳墠椤电爜
+  size: 20,          // 姣忛〉鏉℃暟
+  title: "",         // 鐭ヨ瘑鏍囬(鍙��)
+  type: ""           // 鐭ヨ瘑绫诲瀷(鍙��)
+}
+
+// 鍝嶅簲鏁版嵁
+{
+  code: 200,
+  data: {
+    total: 100,
+    records: [
+      {
+        id: 1,
+        title: "鎿嶄綔鎵嬪唽",
+        type: "guide",
+        scenario: "绯荤粺鎿嶄綔鎸囧",
+        efficiency: "high",
+        problem: "鐢ㄦ埛涓嶄細鎿嶄綔绯荤粺",
+        solution: "鎸夌収鎿嶄綔鎵嬪唽鎵ц...",
+        keyPoints: "姝ラ1,姝ラ2,姝ラ3",
+        creator: "寮犱笁",
+        usageCount: 10,
+        fileCount: 3,           // 鏂囦欢鏁伴噺
+        totalChunkCount: 45,    // 鎬诲垏鐗囨暟閲�
+        createTime: "2026-06-08 10:00:00"
+      }
+    ]
+  }
+}
+```
+
+#### 淇濆瓨鏂囦欢鍏宠仈
+```javascript
+// 璇锋眰鍙傛暟
+{
+  knowledgeBaseId: 10,         // 鐭ヨ瘑搴揑D
+  storageBlobIds: [123, 124]   // 涓婁紶鏂囦欢杩斿洖鐨刡lob ID鍒楄〃
+}
+
+// 鍝嶅簲鏁版嵁
+{
+  code: 200,
+  msg: "鎿嶄綔鎴愬姛"
+}
+```
+
+#### 鍚戦噺鍖栫姸鎬佹煡璇�
+```javascript
+// 鍝嶅簲鏁版嵁
+{
+  code: 200,
+  data: [
+    {
+      id: 1,
+      storageBlobId: 123,
+      fileName: "鎿嶄綔鎵嬪唽.docx",
+      fileType: "docx",
+      vectorStatus: 2,         // 0-寰呭鐞�,1-澶勭悊涓�,2-宸插畬鎴�,3-澶辫触
+      chunkCount: 15,          // 鍒囩墖鏁伴噺
+      namespace: "kb-10",
+      vectorError: null,
+      createTime: "2026-06-08 10:00:00"
+    }
+  ]
+}
+```
+
+#### 鐭ヨ瘑搴撻棶绛�
+```javascript
+// 璇锋眰鍙傛暟
+{
+  knowledgeBaseId: 10,
+  memoryId: "session-xxx",     // 浼氳瘽ID,鐢ㄤ簬淇濇寔涓婁笅鏂�
+  question: "濡備綍鎿嶄綔瀹℃壒娴佺▼?"
+}
+
+// 鍝嶅簲(娴佸紡杩斿洖 text/stream;charset=utf-8)
+// 鏍规嵁鐭ヨ瘑搴撳唴瀹�,瀹℃壒娴佺▼鐨勬搷浣滄楠ゅ涓�:
+// 1. 鐧诲綍绯荤粺鍚庤繘鍏ュ鎵圭鐞嗘ā鍧�...
+```
+
+---
+
+## 鍥涖�佹牳蹇冪粍浠跺疄鐜�
+
+### 4.1 涓婚〉闈㈢粨鏋�
+
+椤甸潰閲囩敤Tab椤电甯冨眬,鍖呭惈涓や釜涓昏鍔熻兘妯″潡:
+- **鐭ヨ瘑搴撶鐞�** - 鐭ヨ瘑搴揅RUD鎿嶄綔
+- **鐭ヨ瘑搴撻棶绛�** - 鍩轰簬RAG鐨勬櫤鑳介棶绛�
+
+### 4.2 鏁版嵁妯″瀷瀹氫箟
+
+```javascript
+// 鍝嶅簲寮忔暟鎹�
+const data = reactive({
+  // 鎼滅储琛ㄥ崟
+  searchForm: {
+    title: "",
+    type: "",
+  },
+
+  // 鍒嗛〉閰嶇疆
+  page: {
+    current: 1,
+    size: 20,
+    total: 0,
+  },
+
+  // 琛ㄦ牸鏁版嵁
+  tableData: [],
+  tableLoading: false,
+  selectedIds: [],
+
+  // 鐭ヨ瘑搴撹〃鍗�
+  form: {
+    title: "",
+    type: "",
+    scenario: "",
+    efficiency: "",
+    problem: "",
+    solution: "",
+    keyPoints: "",
+    creator: "",
+    usageCount: 0
+  },
+
+  // 寮圭獥鎺у埗
+  dialogVisible: false,
+  dialogTitle: "",
+  dialogType: "add",  // add or edit
+  viewDialogVisible: false,
+  currentKnowledge: {},
+
+  // 鏂囦欢绠$悊
+  filesDialogVisible: false,
+  currentKnowledgeBase: null,
+  fileList: [],
+  uploadedBlobIds: [],
+  savingFiles: false,
+
+  // 鐭ヨ瘑搴撻棶绛�
+  chatDialogVisible: false,
+  messages: [],
+  inputQuestion: "",
+  chatLoading: false,
+  memoryId: ""
+});
+```
+
+### 4.3 琛ㄦ牸鍒楅厤缃�
+
+```javascript
+const tableColumn = ref([
+  {
+    label: "鐭ヨ瘑鏍囬",
+    prop: "title",
+    showOverflowTooltip: true,
+  },
+  {
+    label: "鐭ヨ瘑绫诲瀷",
+    prop: "type",
+    dataType: "tag",
+    formatData: (params) => getKnowledgeTypeLabel(params),
+    formatType: (params) => getKnowledgeTypeTagType(params)
+  },
+  {
+    label: "閫傜敤鍦烘櫙",
+    prop: "scenario",
+    width: 150,
+    showOverflowTooltip: true,
+  },
+  {
+    label: "瑙e喅鏁堢巼",
+    prop: "efficiency",
+    dataType: "tag",
+    formatData: (params) => {
+      const efficiencyMap = {
+        high: "鏄捐憲鎻愬崌",
+        medium: "涓�鑸彁鍗�",
+        low: "杞诲井鎻愬崌"
+      };
+      return efficiencyMap[params] || params;
+    },
+    formatType: (params) => {
+      const typeMap = {
+        high: "success",
+        medium: "warning",
+        low: "info"
+      };
+      return typeMap[params] || "info";
+    }
+  },
+  {
+    label: "鏂囦欢鏁伴噺",
+    prop: "fileCount",
+    width: 100,
+    align: "center"
+  },
+  {
+    label: "鍒囩墖鏁伴噺",
+    prop: "totalChunkCount",
+    width: 100,
+    align: "center"
+  },
+  {
+    label: "浣跨敤娆℃暟",
+    prop: "usageCount",
+    width: 100,
+    align: "center"
+  },
+  {
+    label: "鍒涘缓浜�",
+    prop: "creator",
+    width: 120,
+  },
+  {
+    label: "鍒涘缓鏃堕棿",
+    prop: "createTime",
+    width: 180,
+  },
+  {
+    dataType: "action",
+    label: "鎿嶄綔",
+    align: "center",
+    fixed: "right",
+    width: 280,
+    operation: [
+      {
+        name: "缂栬緫",
+        type: "text",
+        clickFun: (row) => openForm("edit", row)
+      },
+      {
+        name: "鏂囦欢",
+        type: "text",
+        clickFun: (row) => openFilesDialog(row)
+      },
+      {
+        name: "闂瓟",
+        type: "text",
+        clickFun: (row) => openChatDialog(row)
+      },
+      {
+        name: "璇︽儏",
+        type: "text",
+        clickFun: (row) => viewKnowledge(row)
+      }
+    ]
+  }
+]);
+```
+
+---
+
+## 浜斻�佹牳蹇冧笟鍔¢�昏緫
+
+### 5.1 鏂囦欢涓婁紶涓庡悜閲忓寲娴佺▼
+
+#### 娴佺▼鍥�
+```
+鐢ㄦ埛鐐瑰嚮"涓婁紶鏂囦欢"
+    鈫�
+閫夋嫨鏂囦欢(鏀寔澶氶��)
+    鈫�
+鍓嶇鏍¢獙鏂囦欢绫诲瀷鍜屽ぇ灏�
+    鈫�
+璋冪敤 /common/upload 涓婁紶鏂囦欢
+    鈫�
+鑾峰彇 storageBlobId 鍒楄〃
+    鈫�
+鐢ㄦ埛鐐瑰嚮"淇濆瓨鏂囦欢鍏宠仈"
+    鈫�
+璋冪敤 /knowledgeBase/file/save
+    鈫�
+鍚庣鍒涘缓鍚戦噺璁板綍 + 寮傛瑙﹀彂鍚戦噺鍖�
+    鈫�
+鍓嶇寤惰繜1绉掑埛鏂版枃浠跺垪琛�
+    鈫�
+鏄剧ず鍚戦噺鍖栫姸鎬�(寰呭鐞嗏啋澶勭悊涓啋宸插畬鎴�)
+```
+
+#### 浠g爜瀹炵幇
+
+```vue
+<template>
+  <div class="file-manager">
+    <!-- 鏂囦欢涓婁紶 -->
+    <div class="upload-section">
+      <el-upload
+        :action="uploadUrl"
+        :headers="uploadHeaders"
+        :on-success="handleUploadSuccess"
+        :on-error="handleUploadError"
+        :before-upload="beforeUpload"
+        multiple
+        :show-file-list="false"
+        accept=".txt,.md,.docx,.xlsx,.xls,.pdf"
+      >
+        <el-button type="primary">涓婁紶鏂囦欢</el-button>
+      </el-upload>
+      <el-button
+        type="success"
+        @click="saveFiles"
+        :disabled="uploadedBlobIds.length === 0"
+        :loading="savingFiles"
+      >
+        淇濆瓨鏂囦欢鍏宠仈
+      </el-button>
+    </div>
+
+    <!-- 鏂囦欢鍒楄〃 -->
+    <el-table :data="fileList" style="margin-top: 20px" border>
+      <el-table-column prop="fileName" label="鏂囦欢鍚�" />
+      <el-table-column prop="fileType" label="鏂囦欢绫诲瀷" width="100" />
+      <el-table-column label="鍚戦噺鍖栫姸鎬�" width="120">
+        <template #default="{ row }">
+          <el-tag :type="getStatusType(row.vectorStatus)">
+            {{ getStatusText(row.vectorStatus) }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="chunkCount" label="鍒囩墖鏁�" width="100" />
+      <el-table-column label="鎿嶄綔" width="150">
+        <template #default="{ row }">
+          <el-button
+            v-if="row.vectorStatus === 3"
+            type="text"
+            @click="reprocessFile(row)"
+          >
+            閲嶆柊澶勭悊
+          </el-button>
+          <el-button type="text" @click="deleteFile(row)" style="color: #f56c6c">
+            鍒犻櫎
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script setup>
+import { getToken } from "@/utils/auth";
+
+// 鏂囦欢涓婁紶閰嶇疆
+const uploadUrl = import.meta.env.VITE_APP_BASE_API + "/common/upload";
+const uploadHeaders = { Authorization: "Bearer " + getToken() };
+const uploadedBlobIds = ref([]);
+const fileList = ref([]);
+
+// 涓婁紶鍓嶆牎楠�
+const beforeUpload = (file) => {
+  const allowedTypes = ['.txt', '.md', '.docx', '.xlsx', '.xls', '.pdf'];
+  const fileName = file.name.toLowerCase();
+  const isAllowed = allowedTypes.some(type => fileName.endsWith(type));
+
+  if (!isAllowed) {
+    ElMessage.error('鍙敮鎸� txt銆乵d銆乨ocx銆亁lsx銆亁ls銆乸df 鏍煎紡鐨勬枃浠�');
+    return false;
+  }
+
+  const isLt50M = file.size / 1024 / 1024 < 50;
+  if (!isLt50M) {
+    ElMessage.error('鏂囦欢澶у皬涓嶈兘瓒呰繃 50MB');
+    return false;
+  }
+
+  return true;
+};
+
+// 涓婁紶鎴愬姛
+const handleUploadSuccess = (response, file) => {
+  if (response.code === 200) {
+    uploadedBlobIds.value.push(response.data.id);
+    ElMessage.success(`鏂囦欢 ${file.name} 涓婁紶鎴愬姛`);
+  } else {
+    ElMessage.error(response.msg || "涓婁紶澶辫触");
+  }
+};
+
+// 淇濆瓨鏂囦欢鍏宠仈
+const saveFiles = async () => {
+  if (uploadedBlobIds.value.length === 0) {
+    ElMessage.warning("璇峰厛涓婁紶鏂囦欢");
+    return;
+  }
+
+  savingFiles.value = true;
+  try {
+    await saveKnowledgeBaseFiles({
+      knowledgeBaseId: currentKnowledgeBase.value.id,
+      storageBlobIds: uploadedBlobIds.value
+    });
+
+    ElMessage.success("鏂囦欢鍏宠仈淇濆瓨鎴愬姛,姝e湪鍚庡彴澶勭悊鍚戦噺鍖�");
+    uploadedBlobIds.value = [];
+
+    // 寤惰繜鍒锋柊鏂囦欢鍒楄〃
+    setTimeout(() => {
+      loadFileList();
+    }, 1000);
+  } catch (error) {
+    console.error("淇濆瓨鏂囦欢鍏宠仈澶辫触:", error);
+    ElMessage.error("淇濆瓨鏂囦欢鍏宠仈澶辫触");
+  } finally {
+    savingFiles.value = false;
+  }
+};
+
+// 鍔犺浇鏂囦欢鍒楄〃
+const loadFileList = async () => {
+  if (!currentKnowledgeBase.value?.id) return;
+
+  try {
+    const res = await getVectorStatus(currentKnowledgeBase.value.id);
+    fileList.value = res.data || [];
+  } catch (error) {
+    console.error("鍔犺浇鏂囦欢鍒楄〃澶辫触:", error);
+    ElMessage.error("鍔犺浇鏂囦欢鍒楄〃澶辫触");
+  }
+};
+
+// 鐘舵�佹槧灏�
+const getStatusText = (status) => {
+  const map = {
+    0: '寰呭鐞�',
+    1: '澶勭悊涓�',
+    2: '宸插畬鎴�',
+    3: '澶辫触'
+  };
+  return map[status] || '鏈煡';
+};
+
+const getStatusType = (status) => {
+  const map = {
+    0: 'info',
+    1: 'warning',
+    2: 'success',
+    3: 'danger'
+  };
+  return map[status] || 'info';
+};
+
+// 閲嶆柊澶勭悊鍚戦噺鍖栫殑鏂囦欢
+const reprocessFile = async (row) => {
+  try {
+    await reprocessVector(row.id);
+    ElMessage.success("宸查噸鏂版彁浜ゅ悜閲忓寲浠诲姟");
+    setTimeout(() => {
+      loadFileList();
+    }, 1000);
+  } catch (error) {
+    console.error("閲嶆柊澶勭悊澶辫触:", error);
+    ElMessage.error("閲嶆柊澶勭悊澶辫触");
+  }
+};
+
+// 鍒犻櫎鏂囦欢
+const deleteFile = async (row) => {
+  try {
+    await ElMessageBox.confirm(
+      "纭畾瑕佸垹闄よ鏂囦欢鍚�?鍒犻櫎鍚庡皢鏃犳硶鎭㈠鍚戦噺鏁版嵁",
+      "鍒犻櫎纭",
+      {
+        confirmButtonText: "纭畾",
+        cancelButtonText: "鍙栨秷",
+        type: "warning"
+      }
+    );
+
+    await deleteKnowledgeBaseFiles([row.id]);
+    ElMessage.success("鍒犻櫎鎴愬姛");
+    loadFileList();
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error("鍒犻櫎鏂囦欢澶辫触:", error);
+      ElMessage.error("鍒犻櫎鏂囦欢澶辫触");
+    }
+  }
+};
+</script>
+```
+
+### 5.2 鐭ヨ瘑搴撻棶绛旀祦绋�
+
+#### 娴佺▼鍥�
+```
+鐢ㄦ埛閫夋嫨鐭ヨ瘑搴�
+    鈫�
+杈撳叆闂骞舵彁浜�
+    鈫�
+鍓嶇鐢熸垚memoryId(鐢ㄤ簬浼氳瘽涓婁笅鏂�)
+    鈫�
+璋冪敤 /ai/knowledge/chat (娴佸紡鎺ュ彛)
+    鈫�
+鍚庣澶勭悊:
+  - 瀵归棶棰樿繘琛屽悜閲忓寲
+  - 鍦≒inecone涓绱㈢浉鍏冲垏鐗�
+  - 鏋勫缓涓婁笅鏂嘝rompt
+  - 璋冪敤LLM鐢熸垚鍥炵瓟
+    鈫�
+娴佸紡杩斿洖AI鍥炵瓟
+    鈫�
+鍓嶇瀹炴椂鏄剧ず鍥炵瓟鍐呭
+    鈫�
+鑷姩婊氬姩鍒板簳閮�
+```
+
+#### 浠g爜瀹炵幇
+
+```vue
+<template>
+  <div class="knowledge-chat">
+    <div class="chat-header">
+      <el-tag type="success">褰撳墠鐭ヨ瘑搴�: {{ currentKnowledgeBase?.title }}</el-tag>
+    </div>
+
+    <!-- 瀵硅瘽鍖哄煙 -->
+    <div class="chat-messages" ref="chatMessagesRef">
+      <div
+        v-for="(msg, index) in messages"
+        :key="index"
+        :class="['message', msg.role]"
+      >
+        <div class="message-role">{{ msg.role === 'user' ? '鎴�' : 'AI鍔╂墜' }}</div>
+        <div class="message-content">{{ msg.content }}</div>
+      </div>
+      <div v-if="chatLoading" class="message assistant">
+        <div class="message-role">AI鍔╂墜</div>
+        <div class="message-content typing">姝e湪鎬濊�冧腑...</div>
+      </div>
+    </div>
+
+    <!-- 杈撳叆妗� -->
+    <div class="chat-input">
+      <el-input
+        v-model="inputQuestion"
+        placeholder="璇疯緭鍏ラ棶棰�,鎸夊洖杞﹀彂閫�"
+        @keyup.enter="sendMessage"
+        :disabled="chatLoading"
+      >
+        <template #append>
+          <el-button @click="sendMessage" :loading="chatLoading">鍙戦��</el-button>
+        </template>
+      </el-input>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { nextTick } from 'vue';
+import { getToken } from "@/utils/auth";
+
+const messages = ref([]);
+const inputQuestion = ref("");
+const chatLoading = ref(false);
+const memoryId = ref("");
+const chatMessagesRef = ref();
+
+// 鎵撳紑闂瓟寮圭獥
+const openChatDialog = (row) => {
+  currentKnowledgeBase.value = row;
+  chatDialogVisible.value = true;
+  memoryId.value = crypto.randomUUID(); // 鐢熸垚鍞竴浼氳瘽ID
+  messages.value = [];
+  inputQuestion.value = "";
+};
+
+// 鍙戦�佹秷鎭�
+const sendMessage = async () => {
+  if (!inputQuestion.value.trim()) {
+    ElMessage.warning("璇疯緭鍏ラ棶棰�");
+    return;
+  }
+
+  const question = inputQuestion.value.trim();
+
+  // 娣诲姞鐢ㄦ埛娑堟伅
+  messages.value.push({
+    role: 'user',
+    content: question
+  });
+
+  inputQuestion.value = "";
+  chatLoading.value = true;
+
+  // 婊氬姩鍒板簳閮�
+  await nextTick();
+  scrollToBottom();
+
+  try {
+    // 娴佸紡璇锋眰
+    const response = await fetch('/api/ai/knowledge/chat', {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/json',
+        'Authorization': 'Bearer ' + getToken()
+      },
+      body: JSON.stringify({
+        knowledgeBaseId: currentKnowledgeBase.value.id,
+        memoryId: memoryId.value,
+        question: question
+      })
+    });
+
+    if (!response.ok) {
+      throw new Error('璇锋眰澶辫触');
+    }
+
+    // 澶勭悊SSE娴佸紡鍝嶅簲
+    const reader = response.body.getReader();
+    const decoder = new TextDecoder();
+    let aiContent = '';
+
+    messages.value.push({ role: 'assistant', content: '' });
+
+    while (true) {
+      const { done, value } = await reader.read();
+      if (done) break;
+
+      const text = decoder.decode(value);
+      aiContent += text;
+      messages.value[messages.value.length - 1].content = aiContent;
+
+      // 婊氬姩鍒板簳閮�
+      await nextTick();
+      scrollToBottom();
+    }
+  } catch (error) {
+    console.error("闂瓟璇锋眰澶辫触:", error);
+    ElMessage.error("闂瓟璇锋眰澶辫触,璇风◢鍚庨噸璇�");
+    messages.value.push({
+      role: 'assistant',
+      content: '鎶辨瓑,鍙戠敓浜嗛敊璇�,璇风◢鍚庨噸璇�'
+    });
+  } finally {
+    chatLoading.value = false;
+  }
+};
+
+// 婊氬姩鍒板簳閮�
+const scrollToBottom = () => {
+  if (chatMessagesRef.value) {
+    chatMessagesRef.value.scrollTop = chatMessagesRef.value.scrollHeight;
+  }
+};
+</script>
+
+<style scoped>
+.knowledge-chat {
+  display: flex;
+  flex-direction: column;
+  height: 500px;
+}
+
+.chat-messages {
+  flex: 1;
+  overflow-y: auto;
+  padding: 16px;
+  background: #f5f7fa;
+  border-radius: 8px;
+  margin-bottom: 16px;
+}
+
+.message {
+  margin-bottom: 16px;
+  max-width: 80%;
+}
+
+.message.user {
+  margin-left: auto;
+  text-align: right;
+}
+
+.message.assistant {
+  margin-right: auto;
+}
+
+.message-role {
+  font-size: 12px;
+  color: #909399;
+  margin-bottom: 4px;
+}
+
+.message-content {
+  display: inline-block;
+  padding: 10px 14px;
+  border-radius: 8px;
+  line-height: 1.6;
+  word-wrap: break-word;
+  white-space: pre-wrap;
+}
+
+.message.user .message-content {
+  background: #409eff;
+  color: white;
+}
+
+.message.assistant .message-content {
+  background: white;
+  color: #303133;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.typing {
+  animation: typing 1.5s infinite;
+}
+
+@keyframes typing {
+  0%, 50%, 100% { opacity: 1; }
+  25%, 75% { opacity: 0.5; }
+}
+</style>
+```
+
+---
+
+## 鍏�佸叧閿疄鐜扮粏鑺�
+
+### 6.1 鏂囦欢涓婁紶閰嶇疆
+
+```javascript
+// 涓婁紶鍦板潃
+const uploadUrl = import.meta.env.VITE_APP_BASE_API + "/common/upload";
+
+// 璇锋眰澶�(蹇呴』鎼哄甫Token)
+const uploadHeaders = {
+  Authorization: "Bearer " + getToken()
+};
+
+// 鏀寔鐨勬枃浠剁被鍨�
+const acceptTypes = '.txt,.md,.docx,.xlsx,.xls,.pdf';
+
+// 鏂囦欢澶у皬闄愬埗
+const maxSize = 50 * 1024 * 1024; // 50MB
+```
+
+### 6.2 鍚戦噺鍖栫姸鎬佽疆璇�
+
+```javascript
+// 寮�濮嬭疆璇㈠悜閲忓寲鐘舵��
+const startVectorStatusPolling = () => {
+  const timer = setInterval(async () => {
+    const res = await getVectorStatus(currentKnowledgeBase.value.id);
+    const hasProcessing = res.data.some(item => item.vectorStatus === 1);
+
+    if (!hasProcessing) {
+      clearInterval(timer);
+    }
+
+    fileList.value = res.data;
+  }, 3000); // 姣�3绉掕疆璇竴娆�
+};
+```
+
+### 6.3 娴佸紡鍝嶅簲澶勭悊
+
+```javascript
+// 浣跨敤 Fetch API 澶勭悊娴佸紡鍝嶅簲
+const response = await fetch('/api/ai/knowledge/chat', {
+  method: 'POST',
+  headers: {
+    'Content-Type': 'application/json',
+    'Authorization': 'Bearer ' + getToken()
+  },
+  body: JSON.stringify({
+    knowledgeBaseId: 10,
+    memoryId: "session-xxx",
+    question: "闂鍐呭"
+  })
+});
+
+// 鑾峰彇鍙娴�
+const reader = response.body.getReader();
+const decoder = new TextDecoder();
+
+// 閫愬潡璇诲彇鏁版嵁
+while (true) {
+  const { done, value } = await reader.read();
+  if (done) break;
+
+  const text = decoder.decode(value);
+  // 澶勭悊鏂囨湰鍧�
+  processText(text);
+}
+```
+
+### 6.4 浼氳瘽绠$悊
+
+```javascript
+// 鐢熸垚鍞竴浼氳瘽ID
+const memoryId = crypto.randomUUID();
+
+// 鎴栦娇鐢ㄦ椂闂存埑
+const memoryId = 'kb-chat-' + Date.now();
+
+// 浼氳瘽ID鐢ㄤ簬:
+// 1. 淇濇寔瀵硅瘽涓婁笅鏂�
+// 2. 鏀寔澶氳疆瀵硅瘽
+// 3. 鏌ヨ鍘嗗彶璁板綍
+```
+
+---
+
+## 涓冦�佺姸鎬佺鐞�
+
+### 7.1 鍚戦噺鍖栫姸鎬佸畾涔�
+
+| 鐘舵�佸�� | 鐘舵�佸悕绉� | 璇存槑 | 鏍囩棰滆壊 |
+|--------|----------|------|----------|
+| 0 | 寰呭鐞� | 鏂囦欢宸蹭笂浼�,绛夊緟鍚戦噺鍖栧鐞� | info(鐏拌壊) |
+| 1 | 澶勭悊涓� | 姝e湪杩涜鍚戦噺鍒囩墖澶勭悊 | warning(姗欒壊) |
+| 2 | 宸插畬鎴� | 鍚戦噺鍖栧畬鎴�,鍙繘琛屾绱㈤棶绛� | success(缁胯壊) |
+| 3 | 澶辫触 | 鍚戦噺鍖栧け璐�,闇�閲嶆柊澶勭悊 | danger(绾㈣壊) |
+
+### 7.2 鐭ヨ瘑绫诲瀷閰嶇疆
+
+```javascript
+// 浣跨敤瀛楀吀閰嶇疆鐭ヨ瘑绫诲瀷
+const { knowledge_type } = proxy.useDict("knowledge_type");
+
+// 绀轰緥鏁版嵁
+const knowledgeTypeOptions = [
+  { value: 'contract', label: '鍚堝悓鐭ヨ瘑', elTagType: 'success' },
+  { value: 'approval', label: '瀹℃壒娴佺▼', elTagType: 'warning' },
+  { value: 'solution', label: '瑙e喅鏂规', elTagType: 'primary' },
+  { value: 'experience', label: '缁忛獙鍒嗕韩', elTagType: 'info' },
+  { value: 'guide', label: '鎿嶄綔鎸囧崡', elTagType: 'danger' }
+];
+```
+
+### 7.3 瑙e喅鏁堢巼鏄犲皠
+
+```javascript
+const efficiencyMap = {
+  high: { label: '鏄捐憲鎻愬崌', color: 'success', score: 40, time: '2-3澶�' },
+  medium: { label: '涓�鑸彁鍗�', color: 'warning', score: 25, time: '1-2澶�' },
+  low: { label: '杞诲井鎻愬崌', color: 'info', score: 15, time: '0.5-1澶�' }
+};
+```
+
+---
+
+## 鍏�佹牱寮忚璁�
+
+### 8.1 鏂囦欢绠$悊鏍峰紡
+
+```css
+.file-manager {
+  padding: 20px 0;
+}
+
+.upload-section {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+```
+
+### 8.2 闂瓟鐣岄潰鏍峰紡
+
+```css
+.knowledge-chat {
+  display: flex;
+  flex-direction: column;
+  height: 500px;
+}
+
+.chat-messages {
+  flex: 1;
+  overflow-y: auto;
+  padding: 16px;
+  background: #f5f7fa;
+  border-radius: 8px;
+  margin-bottom: 16px;
+}
+
+.message {
+  margin-bottom: 16px;
+  max-width: 80%;
+}
+
+.message.user {
+  margin-left: auto;
+  text-align: right;
+}
+
+.message.assistant {
+  margin-right: auto;
+}
+
+.message-content {
+  display: inline-block;
+  padding: 10px 14px;
+  border-radius: 8px;
+  line-height: 1.6;
+  word-wrap: break-word;
+  white-space: pre-wrap;
+}
+
+.message.user .message-content {
+  background: #409eff;
+  color: white;
+}
+
+.message.assistant .message-content {
+  background: white;
+  color: #303133;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+```
+
+---
+
+## 涔濄�佹敞鎰忎簨椤�
+
+### 9.1 鏂囦欢涓婁紶
+
+1. **蹇呴』璋冪敤淇濆瓨鎺ュ彛**: 涓婁紶鎴愬姛鍚庡繀椤昏皟鐢� `/knowledgeBase/file/save` 鎵嶈兘瑙﹀彂鍚戦噺鍖�
+2. **鏂囦欢绫诲瀷闄愬埗**: 鍙敮鎸� txt銆乵d銆乨ocx銆亁lsx銆亁ls銆乸df 鏍煎紡
+3. **鏂囦欢澶у皬闄愬埗**: 鍗曟枃浠舵渶澶� 50MB
+4. **寮傛澶勭悊**: 鍚戦噺鍖栨槸寮傛澶勭悊,涓嶄細闃诲鐢ㄦ埛鎿嶄綔
+
+### 9.2 鍚戦噺鍖栫姸鎬�
+
+1. **鐘舵�佽疆璇�**: 寤鸿姣�3-5绉掕疆璇竴娆$姸鎬�
+2. **鍋滄杞**: 褰撴墍鏈夋枃浠剁姸鎬侀兘涓嶆槸"澶勭悊涓�"鏃跺仠姝㈣疆璇�
+3. **澶辫触澶勭悊**: 鐘舵�佷负"澶辫触"鏃跺彲鐐瑰嚮"閲嶆柊澶勭悊"鎸夐挳
+
+### 9.3 鐭ヨ瘑搴撻棶绛�
+
+1. **浼氳瘽ID**: 姣忔鎵撳紑闂瓟寮圭獥闇�瑕佺敓鎴愭柊鐨� memoryId
+2. **娴佸紡澶勭悊**: 浣跨敤 Fetch API 澶勭悊娴佸紡鍝嶅簲,涓嶆敮鎸� axios
+3. **閿欒澶勭悊**: 闇�瑕佸鐞嗙綉缁滈敊璇拰AI鍝嶅簲閿欒
+4. **鑷姩婊氬姩**: 姣忔鏀跺埌鏂版秷鎭嚜鍔ㄦ粴鍔ㄥ埌搴曢儴
+
+### 9.4 鏁版嵁涓�鑷存��
+
+1. **鍒犻櫎鐭ヨ瘑搴�**: 闇�瑕佸悓鏃跺垹闄ゅ叧鑱旂殑鏂囦欢鍜屽悜閲忔暟鎹�
+2. **鍒犻櫎鏂囦欢**: 鍒犻櫎鏂囦欢鏃跺悓姝ュ垹闄ゅ悜閲忓簱涓殑鐩稿叧鍒囩墖
+3. **鍒锋柊鍒楄〃**: 鏂囦欢鎿嶄綔鍚庨渶瑕佸埛鏂扮煡璇嗗簱鍒楄〃,鏇存柊鏂囦欢鏁伴噺鍜屽垏鐗囨暟閲�
+
+---
+
+## 鍗併�佹祴璇曟鏌ユ竻鍗�
+
+### 10.1 鐭ヨ瘑搴撶鐞�
+
+- [ ] 鏂板鐭ヨ瘑搴撴垚鍔�
+- [ ] 缂栬緫鐭ヨ瘑搴撴垚鍔�
+- [ ] 鍒犻櫎鐭ヨ瘑搴撴垚鍔�(鍗曚釜/鎵归噺)
+- [ ] 鎼滅储鍔熻兘姝e父(鎸夋爣棰樸�佺被鍨�)
+- [ ] 鍒嗛〉鍔熻兘姝e父
+- [ ] 瀵煎嚭鍔熻兘姝e父
+
+### 10.2 鏂囦欢涓婁紶
+
+- [ ] 鍗曟枃浠朵笂浼犳垚鍔�
+- [ ] 澶氭枃浠朵笂浼犳垚鍔�
+- [ ] 鏂囦欢绫诲瀷鏍¢獙姝e父
+- [ ] 鏂囦欢澶у皬鏍¢獙姝e父
+- [ ] 淇濆瓨鏂囦欢鍏宠仈鎴愬姛
+- [ ] 鍚戦噺鍖栫姸鎬佹纭樉绀�
+
+### 10.3 鏂囦欢绠$悊
+
+- [ ] 鏌ョ湅鏂囦欢鍒楄〃姝e父
+- [ ] 鍚戦噺鍖栫姸鎬佽疆璇㈡甯�
+- [ ] 閲嶆柊澶勭悊澶辫触鏂囦欢鎴愬姛
+- [ ] 鍒犻櫎鏂囦欢鎴愬姛
+- [ ] 鏂囦欢棰勮/涓嬭浇姝e父
+
+### 10.4 鐭ヨ瘑搴撻棶绛�
+
+- [ ] 闂瓟寮圭獥鎵撳紑姝e父
+- [ ] 鍙戦�侀棶棰樻垚鍔�
+- [ ] 娴佸紡鍝嶅簲鏄剧ず姝e父
+- [ ] 澶氳疆瀵硅瘽姝e父
+- [ ] 鑷姩婊氬姩鍒板簳閮�
+- [ ] 閿欒澶勭悊姝e父
+
+---
+
+## 鍗佷竴銆佸父瑙侀棶棰�
+
+### Q1: 鏂囦欢涓婁紶鍚庢病鏈夎Е鍙戝悜閲忓寲?
+
+**A**: 妫�鏌ユ槸鍚﹁皟鐢ㄤ簡 `/knowledgeBase/file/save` 鎺ュ彛銆備笂浼犳垚鍔熷悗蹇呴』璋冪敤姝ゆ帴鍙f墠鑳借Е鍙戝悜閲忓寲銆�
+
+### Q2: 鍚戦噺鍖栫姸鎬佷竴鐩存槸"澶勭悊涓�"?
+
+**A**: 鍙兘鍘熷洜:
+1. 鍚庡彴鏈嶅姟鏈惎鍔�
+2. Embedding妯″瀷璋冪敤澶辫触
+3. Pinecone杩炴帴澶辫触
+
+寤鸿鏌ョ湅鍚庡彴鏃ュ織鎺掓煡闂銆�
+
+### Q3: 闂瓟杩斿洖绌哄唴瀹�?
+
+**A**: 鍙兘鍘熷洜:
+1. 鐭ヨ瘑搴撲腑娌℃湁鏂囦欢
+2. 鏂囦欢鏈畬鎴愬悜閲忓寲
+3. 妫�绱㈢浉浼煎害浣庝簬闃堝��
+
+寤鸿妫�鏌ユ枃浠舵暟閲忓拰鍚戦噺鍖栫姸鎬併��
+
+### Q4: 娴佸紡鍝嶅簲鏄剧ず涔辩爜?
+
+**A**: 纭繚璇锋眰澶村寘鍚纭殑缂栫爜璁剧疆:
+```javascript
+headers: {
+  'Content-Type': 'application/json'
+}
+```
+
+### Q5: 濡備綍璋冭瘯娴佸紡鎺ュ彛?
+
+**A**: 浣跨敤娴忚鍣ㄥ紑鍙戣�呭伐鍏�:
+1. 鎵撳紑 Network 鏍囩
+2. 鎵惧埌 `/ai/knowledge/chat` 璇锋眰
+3. 鏌ョ湅 Response 鏍囩,鍙互鐪嬪埌娴佸紡杩斿洖鐨勫唴瀹�
+
+---
+
+## 鍗佷簩銆佷紭鍖栧缓璁�
+
+### 12.1 鎬ц兘浼樺寲
+
+1. **铏氭嫙婊氬姩**: 娑堟伅鍒楄〃瓒呰繃100鏉℃椂浣跨敤铏氭嫙婊氬姩
+2. **闃叉姈鑺傛祦**: 鎼滅储杈撳叆浣跨敤闃叉姈,鐘舵�佽疆璇娇鐢ㄨ妭娴�
+3. **鎳掑姞杞�**: 鏂囦欢鍒楄〃浣跨敤鎳掑姞杞�
+
+### 12.2 鐢ㄦ埛浣撻獙浼樺寲
+
+1. **杩涘害鎻愮ず**: 鍚戦噺鍖栨椂鏄剧ず杩涘害鏉�
+2. **蹇嵎閿�**: 鏀寔蹇嵎閿搷浣�(濡� Ctrl+Enter 鍙戦��)
+3. **鍘嗗彶璁板綍**: 鏀寔鏌ョ湅鍘嗗彶闂瓟璁板綍
+4. **瀵煎嚭瀵硅瘽**: 鏀寔瀵煎嚭瀵硅瘽鍐呭
+
+### 12.3 鍔熻兘鎵╁睍
+
+1. **鏂囦欢棰勮**: 鏀寔鍦ㄧ嚎棰勮鏂囦欢鍐呭
+2. **鎵归噺鎿嶄綔**: 鏀寔鎵归噺鍒犻櫎銆佹壒閲忛噸鏂板鐞�
+3. **鍚戦噺鍖栭厤缃�**: 鍏佽鐢ㄦ埛閰嶇疆鍒囩墖澶у皬銆侀噸鍙犲ぇ灏�
+4. **鐩镐技搴﹂槇鍊�**: 鍏佽鐢ㄦ埛璋冩暣妫�绱㈢浉浼煎害闃堝��
+
+---
+
+## 鍗佷笁銆佹洿鏂版棩蹇�
+
+### v1.0.0 (2026-06-08)
+- 鉁� 瀹屾垚鐭ヨ瘑搴揅RUD鍔熻兘
+- 鉁� 瀹屾垚鏂囦欢涓婁紶涓庡悜閲忓寲鍔熻兘
+- 鉁� 瀹屾垚鐭ヨ瘑搴撻棶绛斿姛鑳�
+- 鉁� 瀹屾垚鏂囦欢绠$悊鍔熻兘
+- 鉁� 瀹屾垚鍚戦噺鍖栫姸鎬佹樉绀�
+
+### v1.1.0 (璁″垝涓�)
+- 馃敳 娣诲姞鍘嗗彶璁板綍鏌ヨ
+- 馃敳 娣诲姞鎵归噺鎿嶄綔鍔熻兘
+- 馃敳 娣诲姞鏂囦欢棰勮鍔熻兘
+- 馃敳 浼樺寲鍚戦噺鍖栬繘搴︽樉绀�
\ No newline at end of file
diff --git a/src/api/collaborativeApproval/knowledgeBase.js b/src/api/collaborativeApproval/knowledgeBase.js
index 8ad24dd..d7dce3e 100644
--- a/src/api/collaborativeApproval/knowledgeBase.js
+++ b/src/api/collaborativeApproval/knowledgeBase.js
@@ -1,4 +1,5 @@
 import request from "@/utils/request";
+import { getToken } from '@/utils/auth';
 
 // 鏌ヨ鐭ヨ瘑搴撳垪琛�
 export function listKnowledgeBase(query) {
@@ -87,20 +88,24 @@
   });
 }
 
-// 鑾峰彇鐭ヨ瘑搴撳垪琛�(闂瓟鐢�)
-export function getKnowledgeBaseListForChat() {
+// 鏌ヨ鐭ヨ瘑搴撻棶绛斿巻鍙�
+export function getKnowledgeHistory(memoryId) {
   return request({
-    url: "/ai/knowledge/list",
+    url: `/ai/knowledge/history/${memoryId}`,
     method: "get",
   });
 }
 
 // 鐭ヨ瘑搴撻棶绛�(娴佸紡)
 export async function knowledgeChat(data) {
-  const response = await fetch("/api/ai/knowledge/chat", {
-    method: "POST",
-    headers: { "Content-Type": "application/json" },
-    body: JSON.stringify(data),
+  const token = getToken();
+  const response = await fetch(import.meta.env.VITE_APP_BASE_API + '/ai/knowledge/chat', {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'Authorization': 'Bearer ' + token
+    },
+    body: JSON.stringify(data)
   });
   return response.body;
 }
diff --git a/src/views/collaborativeApproval/knowledgeBase/index.vue b/src/views/collaborativeApproval/knowledgeBase/index.vue
index 8d625cf..907e51f 100644
--- a/src/views/collaborativeApproval/knowledgeBase/index.vue
+++ b/src/views/collaborativeApproval/knowledgeBase/index.vue
@@ -259,6 +259,22 @@
           >
             淇濆瓨鏂囦欢鍏宠仈
           </el-button>
+          <el-button
+            v-if="uploadedBlobIds.length > 0"
+            type="text"
+            @click="clearUploadedFiles"
+            style="margin-left: 10px"
+          >
+            娓呯┖寰呬繚瀛樺垪琛�
+          </el-button>
+        </div>
+
+        <!-- 寰呬繚瀛樼殑鏂囦欢鍒楄〃 -->
+        <div v-if="uploadedBlobIds.length > 0" class="uploaded-list">
+          <div class="uploaded-tip">
+            <el-icon style="color: #409eff"><InfoFilled /></el-icon>
+            <span>宸蹭笂浼� {{ uploadedBlobIds.length }} 涓枃浠�,璇风偣鍑�"淇濆瓨鏂囦欢鍏宠仈"鎸夐挳瑙﹀彂鍚戦噺鍖栧鐞�</span>
+          </div>
         </div>
 
         <!-- 鏂囦欢鍒楄〃涓庡悜閲忓寲鐘舵�� -->
@@ -273,6 +289,12 @@
             </template>
           </el-table-column>
           <el-table-column prop="chunkCount" label="鍒囩墖鏁�" width="100" align="center" />
+          <el-table-column label="閿欒淇℃伅" width="200" show-overflow-tooltip>
+            <template #default="{ row }">
+              <span v-if="row.vectorError" style="color: #f56c6c">{{ row.vectorError }}</span>
+              <span v-else style="color: #909399">-</span>
+            </template>
+          </el-table-column>
           <el-table-column prop="createTime" label="涓婁紶鏃堕棿" width="180" />
           <el-table-column label="鎿嶄綔" width="150" align="center">
             <template #default="{ row }">
@@ -326,7 +348,7 @@
         <div class="chat-input">
           <el-input
             v-model="inputQuestion"
-            placeholder="璇疯緭鍏ラ棶棰橈紝鎸夊洖杞﹀彂閫�"
+            placeholder="璇疯緭鍏ラ棶棰�,鎸夊洖杞﹀彂閫�(Ctrl+Enter蹇嵎鍙戦��)"
             @keyup.enter="sendMessage"
             :disabled="chatLoading"
           >
@@ -334,6 +356,9 @@
               <el-button @click="sendMessage" :loading="chatLoading">鍙戦��</el-button>
             </template>
           </el-input>
+          <div class="chat-actions">
+            <el-button type="text" size="small" @click="clearMessages">娓呯┖瀵硅瘽</el-button>
+          </div>
         </div>
       </div>
     </FormDialog>
@@ -341,7 +366,7 @@
 </template>
 
 <script setup>
-import { Search } from "@element-plus/icons-vue";
+import { Search, InfoFilled } from "@element-plus/icons-vue";
 import { onMounted, ref, reactive, toRefs, getCurrentInstance, computed, watch, nextTick } from "vue";
 import { ElMessage, ElMessageBox } from "element-plus";
 import PIMTable from "@/components/PIMTable/PIMTable.vue";
@@ -412,6 +437,7 @@
   fileList: [],
   uploadedBlobIds: [],
   savingFiles: false,
+  vectorStatusTimer: null, // 鍚戦噺鍖栫姸鎬佽疆璇㈠畾鏃跺櫒
   chatDialogVisible: false,
   messages: [],
   inputQuestion: "",
@@ -436,6 +462,7 @@
   fileList,
   uploadedBlobIds,
   savingFiles,
+  vectorStatusTimer,
   chatDialogVisible,
   messages,
   inputQuestion,
@@ -872,9 +899,45 @@
   try {
     const res = await getVectorStatus(currentKnowledgeBase.value.id);
     fileList.value = res.data || [];
+
+    // 妫�鏌ユ槸鍚︽湁澶勭悊涓殑鏂囦欢,濡傛灉鏈夊垯鍚姩杞
+    const hasProcessing = res.data.some(item => item.vectorStatus === 1);
+    if (hasProcessing && !vectorStatusTimer.value) {
+      startVectorStatusPolling();
+    } else if (!hasProcessing && vectorStatusTimer.value) {
+      stopVectorStatusPolling();
+    }
   } catch (error) {
     console.error("鍔犺浇鏂囦欢鍒楄〃澶辫触:", error);
     ElMessage.error("鍔犺浇鏂囦欢鍒楄〃澶辫触");
+  }
+};
+
+// 寮�濮嬭疆璇㈠悜閲忓寲鐘舵��
+const startVectorStatusPolling = () => {
+  vectorStatusTimer.value = setInterval(async () => {
+    try {
+      const res = await getVectorStatus(currentKnowledgeBase.value.id);
+      fileList.value = res.data || [];
+
+      // 妫�鏌ユ槸鍚﹁繕鏈夊鐞嗕腑鐨勬枃浠�
+      const hasProcessing = res.data.some(item => item.vectorStatus === 1);
+      if (!hasProcessing) {
+        stopVectorStatusPolling();
+        ElMessage.success("鎵�鏈夋枃浠跺悜閲忓寲澶勭悊瀹屾垚");
+      }
+    } catch (error) {
+      console.error("杞鍚戦噺鍖栫姸鎬佸け璐�:", error);
+      stopVectorStatusPolling();
+    }
+  }, 3000); // 姣�3绉掕疆璇竴娆�
+};
+
+// 鍋滄杞鍚戦噺鍖栫姸鎬�
+const stopVectorStatusPolling = () => {
+  if (vectorStatusTimer.value) {
+    clearInterval(vectorStatusTimer.value);
+    vectorStatusTimer.value = null;
   }
 };
 
@@ -957,6 +1020,12 @@
   }
 };
 
+// 娓呯┖寰呬繚瀛樼殑鏂囦欢鍒楄〃
+const clearUploadedFiles = () => {
+  uploadedBlobIds.value = [];
+  ElMessage.success("宸叉竻绌哄緟淇濆瓨鏂囦欢鍒楄〃");
+};
+
 // 鍒犻櫎鏂囦欢
 const deleteFile = async (row) => {
   try {
@@ -1009,7 +1078,8 @@
   currentKnowledgeBase.value = null;
   fileList.value = [];
   uploadedBlobIds.value = [];
-  getList(); // 鍒锋柊涓诲垪琛紝鏇存柊鏂囦欢鏁伴噺
+  stopVectorStatusPolling(); // 鍋滄杞
+  getList(); // 鍒锋柊涓诲垪琛�,鏇存柊鏂囦欢鏁伴噺
 };
 
 // ============ 鐭ヨ瘑搴撻棶绛旂浉鍏� ============
@@ -1027,6 +1097,11 @@
 const sendMessage = async () => {
   if (!inputQuestion.value.trim()) {
     ElMessage.warning("璇疯緭鍏ラ棶棰�");
+    return;
+  }
+
+  if (!currentKnowledgeBase.value?.id) {
+    ElMessage.error("鐭ヨ瘑搴撲俊鎭紓甯�");
     return;
   }
 
@@ -1061,7 +1136,8 @@
     });
 
     if (!response.ok) {
-      throw new Error('璇锋眰澶辫触');
+      const errorText = await response.text();
+      throw new Error(errorText || '璇锋眰澶辫触');
     }
 
     // 澶勭悊SSE娴佸紡鍝嶅簲
@@ -1083,16 +1159,40 @@
       await nextTick();
       scrollToBottom();
     }
+
+    // 濡傛灉AI杩斿洖绌哄唴瀹�,鏄剧ず鎻愮ず
+    if (!aiContent.trim()) {
+      messages.value[messages.value.length - 1].content = '鎶辨瓑,鐭ヨ瘑搴撲腑鏈壘鍒扮浉鍏冲唴瀹�,璇峰皾璇曞叾浠栭棶棰樸��';
+    }
   } catch (error) {
     console.error("闂瓟璇锋眰澶辫触:", error);
-    ElMessage.error("闂瓟璇锋眰澶辫触锛岃绋嶅悗閲嶈瘯");
+    ElMessage.error("闂瓟璇锋眰澶辫触,璇风◢鍚庨噸璇�");
     messages.value.push({
       role: 'assistant',
-      content: '鎶辨瓑锛屽彂鐢熶簡閿欒锛岃绋嶅悗閲嶈瘯'
+      content: '鎶辨瓑,鍙戠敓浜嗛敊璇�,璇风◢鍚庨噸璇�'
     });
   } finally {
     chatLoading.value = false;
   }
+};
+
+// 娓呯┖瀵硅瘽
+const clearMessages = () => {
+  ElMessageBox.confirm(
+    "纭畾瑕佹竻绌烘墍鏈夊璇濊褰曞悧?",
+    "娓呯┖纭",
+    {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning"
+    }
+  ).then(() => {
+    messages.value = [];
+    memoryId.value = crypto.randomUUID(); // 閲嶆柊鐢熸垚浼氳瘽ID
+    ElMessage.success("瀵硅瘽宸叉竻绌�");
+  }).catch(() => {
+    // 鐢ㄦ埛鍙栨秷
+  });
 };
 
 // 婊氬姩鍒板簳閮�
@@ -1195,6 +1295,22 @@
   align-items: center;
 }
 
+.uploaded-list {
+  margin-top: 16px;
+  padding: 12px;
+  background: #f0f9ff;
+  border-radius: 6px;
+  border: 1px solid #b3d8ff;
+}
+
+.uploaded-tip {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  color: #409eff;
+  font-size: 14px;
+}
+
 /* 鐭ヨ瘑搴撻棶绛旀牱寮� */
 .knowledge-chat {
   display: flex;
@@ -1272,4 +1388,9 @@
   margin-top: auto;
 }
 
+.chat-actions {
+  margin-top: 8px;
+  text-align: right;
+}
+
 </style>
diff --git a/vite.config.js b/vite.config.js
index fcc019d..474f7ac 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -8,7 +8,7 @@
   const { VITE_APP_ENV } = env;
   const baseUrl =
       env.VITE_APP_ENV === "development"
-          ? "http://1.15.17.182:9048"
+          ? "http://localhost:7005"
           : env.VITE_BASE_API;
   const javaUrl =
       env.VITE_APP_ENV === "development"

--
Gitblit v1.9.3