From ff5b4a4202405b26393c5f71999e43f3e2746499 Mon Sep 17 00:00:00 2001
From: 张诺 <zhang_12370@163.com>
Date: 星期三, 08 四月 2026 15:13:12 +0800
Subject: [PATCH] feat(知识库): 添加批量操作和文件删除功能

---
 src/pages/cooperativeOffice/collaborativeApproval/knowledgeBase/detail.vue |   78 +++++++++++---
 src/pages/cooperativeOffice/collaborativeApproval/knowledgeBase/index.vue  |  206 ++++++++++++++++++++++++++++++++++++++++-
 src/api/managementMeetings/knowledgeBase.js                                |   11 ++
 3 files changed, 269 insertions(+), 26 deletions(-)

diff --git a/src/api/managementMeetings/knowledgeBase.js b/src/api/managementMeetings/knowledgeBase.js
index ad0ce77..2493fbe 100644
--- a/src/api/managementMeetings/knowledgeBase.js
+++ b/src/api/managementMeetings/knowledgeBase.js
@@ -33,4 +33,13 @@
     method: "delete",
     data: query,
   });
-}
\ No newline at end of file
+}
+
+// 鍒犻櫎鐭ヨ瘑搴撴枃浠�
+export function delKnowledgeBaseFile(fileIds) {
+  return request({
+    url: "/knowledgeBaseFile/del",
+    method: "delete",
+    data: fileIds,
+  });
+}
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/knowledgeBase/detail.vue b/src/pages/cooperativeOffice/collaborativeApproval/knowledgeBase/detail.vue
index 88af8c9..a8f708a 100644
--- a/src/pages/cooperativeOffice/collaborativeApproval/knowledgeBase/detail.vue
+++ b/src/pages/cooperativeOffice/collaborativeApproval/knowledgeBase/detail.vue
@@ -108,10 +108,10 @@
             <view class="upload-tip" v-if="!readonly">
               鏀寔鏂囨。锛坉oc, docx, xls, xlsx, pdf, txt锛夊拰鍥剧墖锛坖pg, jpeg, png, gif锛夋牸寮�
             </view>
-            <view class="file-list" v-if="form.commonFileList && form.commonFileList.length > 0">
-              <view v-for="(file, index) in form.commonFileList" :key="index" class="file-item">
+            <view class="file-list" v-if="form.files && form.files.length > 0">
+              <view v-for="(file, index) in form.files" :key="index" class="file-item">
                 <up-icon name="file-text" size="20" color="#667eea"></up-icon>
-                <text class="file-name" @click="previewFile(file)">{{ file.name || file.fileName }}</text>
+                <text class="file-name">{{ file.name || file.fileName }}</text>
                 <up-icon v-if="!readonly" name="close-circle-fill" size="18" color="#ff4d4f" @click="handleRemoveFile(index)"></up-icon>
               </view>
             </view>
@@ -172,9 +172,12 @@
   import {
     addKnowledgeBase,
     updateKnowledgeBase,
+    delKnowledgeBaseFile,
   } from "@/api/managementMeetings/knowledgeBase";
   import { userListNoPageByTenantId } from "@/api/system/user";
   import upload from "@/utils/upload";
+  import { getToken } from "@/utils/auth";
+  import config from "@/config";
 
   defineOptions({ name: "knowledge-base-detail" });
 
@@ -206,7 +209,7 @@
     creator: userStore.nickName || "",
     usageCount: 0,
     tempFileIds: [],
-    commonFileList: []
+    files: []
   });
 
   // 閫夋嫨鍣ㄧ姸鎬�
@@ -300,11 +303,11 @@
           if (!form.value.tempFileIds) form.value.tempFileIds = [];
           form.value.tempFileIds.push(res.data.tempId);
           
-          if (!form.value.commonFileList) form.value.commonFileList = [];
-          form.value.commonFileList.push({
-            id: res.data.tempId,
+          if (!form.value.files) form.value.files = [];
+          form.value.files.push({
+            tempId: res.data.tempId,
             name: item.name || '鏈懡鍚嶆枃浠�',
-            url: res.data.url
+            url: res.data.url || res.data.tempPath
           });
           
           showToast('涓婁紶鎴愬姛');
@@ -321,20 +324,48 @@
 
   const deleteFile = (event) => {
     const { index } = event;
-    form.value.commonFileList.splice(index, 1);
-    if (form.value.tempFileIds) {
-      form.value.tempFileIds.splice(index, 1);
-    }
+    handleRemoveFile(index);
   };
 
-  const handleRemoveFile = (index) => {
-    form.value.commonFileList.splice(index, 1);
-    if (form.value.tempFileIds) {
-      form.value.tempFileIds.splice(index, 1);
+  const handleRemoveFile = async (index) => {
+    const list = Array.isArray(form.value.files)
+      ? form.value.files
+      : [];
+    const file = list[index];
+    if (!file) return;
+
+    const fileId = file.id;
+    const tempId = file.tempId || file.tempFileId;
+    const isEditMode = detailType.value === 2;
+    const canDeleteRemote = isEditMode && !!fileId;
+
+    try {
+      if (canDeleteRemote) {
+        uni.showLoading({ title: "鍒犻櫎涓�...", mask: true });
+        const res = await delKnowledgeBaseFile([fileId]);
+        uni.hideLoading();
+        if (res.code !== 200) {
+          showToast(res.msg || "鍒犻櫎澶辫触");
+          return;
+        }
+      }
+
+      form.value.files.splice(index, 1);
+      if (form.value.tempFileIds) {
+        const idx = form.value.tempFileIds.findIndex(
+          v => String(v) === String(tempId || fileId)
+        );
+        if (idx > -1) form.value.tempFileIds.splice(idx, 1);
+      }
+    } catch (e) {
+      uni.hideLoading();
+      console.error("鍒犻櫎闄勪欢澶辫触:", e);
+      showToast("鍒犻櫎澶辫触");
     }
   };
 
   const previewFile = (file) => {
+    console.log("previewFile", file);
     if (file.url) {
       // 濡傛灉鏄浘鐗囷紝棰勮鍥剧墖
       const isImage = /\.(jpg|jpeg|png|gif)$/i.test(file.name || file.fileName || file.url);
@@ -345,7 +376,10 @@
       } else {
         // 鍏朵粬鏂囦欢灏濊瘯鎵撳紑
         uni.downloadFile({
-          url: file.url,
+          url: file.url.startsWith("http") ? file.url : (config.baseUrl + file.url),
+          header: {
+            Authorization: "Bearer " + getToken(),
+          },
           success: (res) => {
             if (res.statusCode === 200) {
               uni.openDocument({
@@ -401,8 +435,14 @@
           if (!knowledgeId.value || String(data.id) === String(knowledgeId.value)) {
             form.value = JSON.parse(JSON.stringify(data));
             // 鍏煎澶勭悊鏂囦欢鍒楄〃
-            if (form.value.commonFileList) {
-              form.value.tempFileIds = form.value.commonFileList.map(f => f.id || f.tempId);
+            if (form.value.files) {
+              const ids = (form.value.files || [])
+                .map(f => f?.tempId || f?.tempFileId || f?.id)
+                .filter(v => v !== undefined && v !== null && v !== "");
+              form.value.tempFileIds = ids;
+            }
+            if (!Array.isArray(form.value.files)) {
+              form.value.files = [];
             }
           }
         }
diff --git a/src/pages/cooperativeOffice/collaborativeApproval/knowledgeBase/index.vue b/src/pages/cooperativeOffice/collaborativeApproval/knowledgeBase/index.vue
index 5370e3c..a68e1cb 100644
--- a/src/pages/cooperativeOffice/collaborativeApproval/knowledgeBase/index.vue
+++ b/src/pages/cooperativeOffice/collaborativeApproval/knowledgeBase/index.vue
@@ -2,7 +2,32 @@
   <view class="sales-accoun">
     <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
     <PageHeader title="鐭ヨ瘑搴�"
-                @back="goBack" />
+                @back="goBack">
+      <template #right>
+        <view class="header-actions">
+          <uni-icons v-if="!batchMode"
+                     type="download"
+                     size="20"
+                     color="#333"
+                     @click="handleExport" />
+          <uni-icons v-if="!batchMode"
+                     type="trash"
+                     size="20"
+                     color="#333"
+                     @click="enterBatchMode" />
+          <uni-icons v-if="batchMode"
+                     type="trash"
+                     size="20"
+                     color="#333"
+                     @click="handleBatchDelete" />
+          <uni-icons v-if="batchMode"
+                     type="closeempty"
+                     size="20"
+                     color="#333"
+                     @click="exitBatchMode" />
+        </view>
+      </template>
+    </PageHeader>
     <!-- 鎼滅储鍜岀瓫閫夊尯鍩� -->
     <view class="search-section">
       <view class="search-bar">
@@ -20,6 +45,18 @@
                   color="#999"></u-icon>
         </view>
       </view>
+      <view class="filter-row">
+        <view class="filter-input">
+          <up-input v-model="typeName"
+                    readonly
+                    placeholder="璇烽�夋嫨鐭ヨ瘑绫诲瀷"
+                    @click="showTypeSheet = true" />
+        </view>
+        <view class="filter-reset"
+              @click="resetFilters">
+          閲嶇疆
+        </view>
+      </view>
     </view>
     <!-- 鎷滆璁板綍鍒楄〃 -->
     <view class="ledger-list"
@@ -35,6 +72,13 @@
                          color="#ffffff"></up-icon>
               </view>
               <text class="item-id">鐭ヨ瘑鏍囬锛歿{ item.title || '-' }}</text>
+            </view>
+            <view v-if="batchMode"
+                  class="select-icon"
+                  @click.stop="toggleSelection(item)">
+              <uni-icons :type="isSelected(item) ? 'checkbox-filled' : 'checkbox'"
+                         size="20"
+                         :color="isSelected(item) ? '#667eea' : '#999'" />
             </view>
           </view>
           <up-divider></up-divider>
@@ -66,25 +110,26 @@
             </view>
           </view>
           <!-- 鎸夐挳鍖哄煙 -->
-          <view class="action-buttons">
+          <view class="action-buttons"
+                v-if="!batchMode">
             <u-button type="info"
                       size="small"
                       class="action-btn"
                       plain
-                      @click="viewDetail(item, 3)">
+                      @click.stop="viewDetail(item, 3)">
               鏌ョ湅璇︽儏
             </u-button>
             <u-button type="primary"
                       size="small"
                       class="action-btn"
-                      @click="viewDetail(item, 2)">
+                      @click.stop="viewDetail(item, 2)">
               缂栬緫
             </u-button>
             <u-button type="error"
                       size="small"
                       class="action-btn"
                       plain
-                      @click="confirmDelete(item)">
+                      @click.stop="confirmDelete(item)">
               鍒犻櫎
             </u-button>
           </view>
@@ -102,6 +147,12 @@
                size="24"
                color="#ffffff"></up-icon>
     </view>
+
+    <up-action-sheet :show="showTypeSheet"
+                     :actions="typeOptions"
+                     title="璇烽�夋嫨鐭ヨ瘑绫诲瀷"
+                     @select="onTypeSelect"
+                     @close="showTypeSheet = false" />
   </view>
 </template>
 
@@ -115,6 +166,8 @@
     delKnowledgeBase,
   } from "@/api/managementMeetings/knowledgeBase";
   import useUserStore from "@/store/modules/user";
+  import config from "@/config";
+  import { getToken } from "@/utils/auth";
 
   defineOptions({ name: "knowledge-base-index" });
   
@@ -129,9 +182,13 @@
 
   // 鎼滅储鍏抽敭璇�
   const name = ref("");
+  const type = ref("");
+  const showTypeSheet = ref(false);
 
   // 鎷滆璁板綍鏁版嵁
   const visitList = ref([]);
+  const batchMode = ref(false);
+  const selectedIds = ref([]);
 
   // 杩斿洖涓婁竴椤�
   const goBack = () => {
@@ -139,6 +196,30 @@
   };
   
   const { knowledge_type } = useDict("knowledge_type");
+  const typeOptions = computed(() => {
+    const opts = (knowledge_type?.value || []).map(item => ({
+      name: item.label,
+      value: item.value
+    }));
+    return [{ name: "鍏ㄩ儴", value: "" }, ...opts];
+  });
+
+  const typeName = computed(() => {
+    const item = typeOptions.value.find(i => String(i.value) === String(type.value));
+    return item ? item.name : (type.value || "");
+  });
+
+  const onTypeSelect = (action) => {
+    type.value = action.value;
+    showTypeSheet.value = false;
+    handleQuery();
+  };
+
+  const resetFilters = () => {
+    name.value = "";
+    type.value = "";
+    handleQuery();
+  };
   
   // 鏍煎紡鍖栧洖娆炬柟寮�
   const formatReceiptType = params => {
@@ -190,6 +271,7 @@
       current: -1,
       size: -1,
       title: name.value,
+      type: type.value || undefined,
     };
     listKnowledgeBase(params)
       .then(res => {
@@ -234,6 +316,49 @@
     });
   };
 
+  const enterBatchMode = () => {
+    batchMode.value = true;
+    selectedIds.value = [];
+  };
+
+  const exitBatchMode = () => {
+    batchMode.value = false;
+    selectedIds.value = [];
+  };
+
+  const toggleSelection = item => {
+    const id = item?.id;
+    if (!id) return;
+    const idx = selectedIds.value.findIndex(v => String(v) === String(id));
+    if (idx > -1) {
+      selectedIds.value.splice(idx, 1);
+    } else {
+      selectedIds.value.push(id);
+    }
+  };
+
+  const isSelected = item => {
+    const id = item?.id;
+    if (!id) return false;
+    return selectedIds.value.some(v => String(v) === String(id));
+  };
+
+  const handleBatchDelete = () => {
+    if (selectedIds.value.length === 0) {
+      showToast("璇烽�夋嫨瑕佸垹闄ょ殑鐭ヨ瘑");
+      return;
+    }
+    uni.showModal({
+      title: "鍒犻櫎纭",
+      content: `纭畾瑕佸垹闄ら�変腑鐨� ${selectedIds.value.length} 鏉$煡璇嗗悧锛焋,
+      success: res => {
+        if (res.confirm) {
+          deleteKnowledge(selectedIds.value);
+        }
+      },
+    });
+  };
+
   // 鍒犻櫎纭
   const confirmDelete = item => {
     uni.showModal({
@@ -250,11 +375,15 @@
   // 鎵ц鍒犻櫎
   const deleteKnowledge = id => {
     showLoadingToast("鍒犻櫎涓�...");
-    delKnowledgeBase([id])
+    const ids = Array.isArray(id) ? id : [id];
+    delKnowledgeBase(ids)
       .then(res => {
         closeToast();
         if (res.code === 200) {
           showToast("鍒犻櫎鎴愬姛");
+          if (batchMode.value) {
+            exitBatchMode();
+          }
           getList(); // 閲嶆柊鑾峰彇鍒楄〃
         } else {
           showToast("鍒犻櫎澶辫触");
@@ -264,6 +393,41 @@
         closeToast();
         showToast("鍒犻櫎澶辫触");
       });
+  };
+
+  const handleExport = async () => {
+    try {
+      uni.showLoading({ title: "瀵煎嚭涓�...", mask: true });
+      const params = {
+        title: name.value || undefined,
+        type: type.value || undefined,
+      };
+      const query = Object.entries(params)
+        .filter(([, v]) => v !== undefined && v !== null && v !== "")
+        .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
+        .join("&");
+      const url = config.baseUrl + "/knowledgeBase/export" + (query ? `?${query}` : "");
+      const res = await uni.downloadFile({
+        url,
+        header: {
+          Authorization: "Bearer " + getToken(),
+        },
+      });
+      uni.hideLoading();
+
+      if (res.statusCode !== 200) {
+        showToast("瀵煎嚭澶辫触");
+        return;
+      }
+      uni.openDocument({
+        filePath: res.tempFilePath,
+        showMenu: true,
+        fail: () => showToast("鎵撳紑鏂囦欢澶辫触"),
+      });
+    } catch (e) {
+      uni.hideLoading();
+      showToast("瀵煎嚭澶辫触");
+    }
   };
 
   onMounted(() => {
@@ -318,6 +482,36 @@
     z-index: 100;
   }
 
+  .header-actions {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+    padding-right: 8px;
+  }
+
+  .filter-row {
+    margin-top: 10px;
+    display: flex;
+    align-items: center;
+    gap: 10px;
+  }
+
+  .filter-input {
+    flex: 1;
+  }
+
+  .filter-reset {
+    padding: 6px 12px;
+    background: #f5f7fa;
+    border-radius: 6px;
+    color: #666;
+    font-size: 12px;
+  }
+
+  .select-icon {
+    padding-left: 10px;
+  }
+
   .action-buttons {
     display: flex;
     justify-content: flex-end;

--
Gitblit v1.9.3