Quellcode durchsuchen

feat: 添加上传前文件重名检查功能,支持用户确认继续上传

xieyong vor 3 Tagen
Ursprung
Commit
8a570627bb

+ 13 - 0
src/components/addDoc/api/index.js

@@ -35,6 +35,19 @@ export async function fileSaveAPI(data) {
   return Promise.reject(new Error(res.data.message));
 }
 
+/**
+ * 批量检查文件名称是否已存在(未逻辑删除)
+ * @param {string[]} names 待检查的文件名称集合(不含扩展名)
+ * @returns {Promise<string[]>} 重复的名称列表(既有重名 + 本次请求内重复)
+ */
+export async function checkNameExistsAPI(names) {
+  const res = await request.post('/fm/file/checkNameExists', names);
+  if (res.data.code == 0) {
+    return res.data.data || [];
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
 /**
  * 详情
  * @id id

+ 41 - 5
src/components/addDoc/file-edit.vue

@@ -90,6 +90,7 @@
                 module="main"
                 :limit="100"
                 :multiple="true"
+                :beforeUploadFile="checkNameBeforeUpload"
                 @fileChange="fileChange"
               >
               </fileUpload>
@@ -123,7 +124,8 @@
     selectTreeList,
     listParentId,
     getDocTreeListAPI,
-    listCode
+    listCode,
+    checkNameExistsAPI
   } from './api/index';
   import FileUpload from './fileUpload.vue';
   import { setFolderList } from './util.js';
@@ -146,11 +148,11 @@
         id: '',
         lcyStatus: 1,
         fileType: 0,
-        codeType: '',
+        codeType: ''
       };
       return {
         activeName: '1',
-        
+
         templateVisible: false,
         folderList: [],
         allFolderList: [],
@@ -181,10 +183,16 @@
           businessCodeId: [
             { required: true, message: '请选择', trigger: 'blur' }
           ],
-          codeType: [{ required: this.activeName == 2 ? true : false, message: '请选择', trigger: 'blur' }],
+          codeType: [
+            {
+              required: this.activeName == 2 ? true : false,
+              message: '请选择',
+              trigger: 'blur'
+            }
+          ],
           directoryId: [{ required: true, message: '请选择', trigger: 'blur' }],
           storagePath: [{ required: true, message: '请选择', trigger: 'blur' }]
-        }
+        };
       }
     },
     async created() {
@@ -270,6 +278,34 @@
       fileChange(file) {
         this.form.name = file.name.replace(/\.[^/.]+$/, '');
       },
+      // 上传前查重:返回 false 取消上传;返回 Promise<boolean> 异步判断
+      async checkNameBeforeUpload(file) {
+        try {
+          const nameWithoutExt = file.name.replace(/\.[^/.]+$/, '');
+          const duplicates = await checkNameExistsAPI([nameWithoutExt]);
+          console.log(duplicates, 'duplicates1');
+          if (!duplicates || duplicates.length === 0) {
+            return true;
+          }
+          try {
+            await this.$confirm(
+              `以下文件名称已存在:${duplicates.join('、')},是否继续上传?`,
+              '重名提示',
+              {
+                type: 'warning',
+                confirmButtonText: '继续上传',
+                cancelButtonText: '取消上传'
+              }
+            );
+            return true;
+          } catch {
+            return false;
+          }
+        } catch (e) {
+          // 查重接口异常 → 放行
+          return true;
+        }
+      },
 
       /* 保存编辑 */
       save() {

+ 256 - 210
src/components/addDoc/fileUpload.vue

@@ -1,6 +1,7 @@
 <template>
   <div class="upload-file">
     <el-upload
+      ref="upload"
       class="upload-demo"
       action="#"
       v-loading="loading"
@@ -32,231 +33,276 @@
 </template>
 
 <script>
-import {
-  getFileList,
-  getFile,
-  removeFile
-} from '@/api/system/file/index.js';
-import {
-  uploadFileNew,
-} from './api';
-import { getImageUrl, getImagePath } from '@/utils/file';
-export default {
-  props: {
-    value: {
-      type: [Array, String],
-      default: () => []
-    },
-    multiple: {
-      type: Boolean,
-      default: false
-    },
-    // 所属模块
-    module: {
-      type: String,
-      required: true
-    },
-    // 文档库
-    showLib: {
-      type: Boolean,
-      default: false
-    },
-    // 限制数量
-    limit: {
-      type: Number,
-      default: -1
-    },
-    // 限制大小 mb
-    size: {
-      type: Number,
-      default: 1000
-    },
-    disabled: {
-      default: false,
-      type: Boolean
-    }
-  },
-  data() {
-    return {
-      documentVisible: false,
-      selectItem: null,
-      loading: false,
-      documentForm: {
-        name: ''
+  import { getFileList, getFile, removeFile } from '@/api/system/file/index.js';
+  import { uploadFileNew } from './api';
+  import { getImageUrl, getImagePath } from '@/utils/file';
+  export default {
+    props: {
+      value: {
+        type: [Array, String],
+        default: () => []
       },
-      columns: [
-        {
-          label: '序号',
-          type: 'index',
-          width: 55,
-          align: 'center'
-        },
-        {
-          label: '文档名称',
-          prop: 'name',
-          minWidth: '180',
-          showOverflowTooltip: true
-        },
-        {
-          label: '文档类型',
-          prop: 'type'
-        },
-        {
-          label: '系统'
-        },
-        {
-          label: '储存路径',
-          prop: 'storePath',
-          minWidth: '180',
-          showOverflowTooltip: true
-        },
-        {
-          label: '模块名',
-          prop: 'module'
-        },
-        {
-          label: '上传时间',
-          prop: 'createTime'
-        }
-      ]
-    };
-  },
-  computed: {
-    fileList: {
-      set(val) {
-        // console.log(val);
-        this.$emit(
-          'input',
-          val.map((item) => ({
-            ...item,
-            url: getImagePath(item.url)
-          }))
-        );
+      multiple: {
+        type: Boolean,
+        default: false
       },
-      get() {
-        // console.log(this.value, 2);
-        if (!Array.isArray(this.value)) return [];
-        const arr =
-          (this.value &&
-            this.value.map((item) => ({
-              ...item,
-              url: getImageUrl(item.url)
-            }))) ||
-          [];
-        return arr;
+      // 所属模块
+      module: {
+        type: String,
+        required: true
+      },
+      // 文档库
+      showLib: {
+        type: Boolean,
+        default: false
+      },
+      // 限制数量
+      limit: {
+        type: Number,
+        default: -1
+      },
+      // 限制大小 mb
+      size: {
+        type: Number,
+        default: 1000
+      },
+      disabled: {
+        default: false,
+        type: Boolean
+      },
+      // 上传前拦截钩子:返回 false 取消上传;返回 Promise<boolean> 异步判断(resolve(false) 取消)
+      beforeUploadFile: {
+        type: Function,
+        default: null
       }
-    }
-  },
-
-  methods: {
-    //点击查看图片
-    handleItem(file) {
-      getFile({ objectName: file.storePath }, file.name);
-    },
-    delFileList() {
-      this.$emit('input', []);
     },
-    handleOpenLib() {
-      this.documentVisible = true;
-      this.$nextTick(() => {
-        this.reload();
-      });
-    },
-    //图文档勾选
-    submitDocument() {
-      this.$emit('input', [
-        { url: this.selectItem.storePath, ...this.selectItem }
-      ]);
-      this.documentVisible = false;
-    },
-    datasource({ page, limit }) {
-      return getFileList({
-        ...this.documentForm,
-        pageNum: page,
-        size: limit
-      });
-    },
-    reload() {
-      this.$refs.table.reload();
+    data() {
+      return {
+        documentVisible: false,
+        selectItem: null,
+        loading: false,
+        documentForm: {
+          name: ''
+        },
+        columns: [
+          {
+            label: '序号',
+            type: 'index',
+            width: 55,
+            align: 'center'
+          },
+          {
+            label: '文档名称',
+            prop: 'name',
+            minWidth: '180',
+            showOverflowTooltip: true
+          },
+          {
+            label: '文档类型',
+            prop: 'type'
+          },
+          {
+            label: '系统'
+          },
+          {
+            label: '储存路径',
+            prop: 'storePath',
+            minWidth: '180',
+            showOverflowTooltip: true
+          },
+          {
+            label: '模块名',
+            prop: 'module'
+          },
+          {
+            label: '上传时间',
+            prop: 'createTime'
+          }
+        ]
+      };
     },
-    beforeRemove(file) {
-      if (file.id) {
-        return removeFile({
-          fileId: file.id
-        }).then(() => {
-          return true;
-        });
-        return true;
-      } else {
-        return true;
+    computed: {
+      fileList: {
+        set(val) {
+          // console.log(val);
+          this.$emit(
+            'input',
+            val.map((item) => ({
+              ...item,
+              url: getImagePath(item.url)
+            }))
+          );
+        },
+        get() {
+          // console.log(this.value, 2);
+          if (!Array.isArray(this.value)) return [];
+          const arr =
+            (this.value &&
+              this.value.map((item) => ({
+                ...item,
+                url: getImageUrl(item.url)
+              }))) ||
+            [];
+          return arr;
+        }
       }
     },
-    handleRemove(file, fileList) {
-      this.fileList = fileList;
-    },
-    beforeUpload(file) {
-      // if (file.size / 1024 / 1024 > this.size) {
-      //   this.$message.error(`大小不能超过 ${this.size}MB`);
-      //   return false;
-      // }
 
-      if (this.limit > 0 && this.fileList.length === this.limit) {
-        this.$message.error(`最多上传 ${this.limit}个文件`);
-        return false;
-      }
-      this.loading = true;
-      return uploadFileNew({
-        module: this.module,
-        multiPartFile: file
-      })
-        .then((res) => {
-          if (res.data) {
-            this.$emit('input', [
-              ...(this.value || []),
-              { ...file, url: res.data.storePath, ...res.data }
-            ]);
+    methods: {
+      //点击查看图片
+      handleItem(file) {
+        getFile({ objectName: file.storePath }, file.name);
+      },
+      delFileList() {
+        this.$emit('input', []);
+      },
+      handleOpenLib() {
+        this.documentVisible = true;
+        this.$nextTick(() => {
+          this.reload();
+        });
+      },
+      //图文档勾选
+      submitDocument() {
+        this.$emit('input', [
+          { url: this.selectItem.storePath, ...this.selectItem }
+        ]);
+        this.documentVisible = false;
+      },
+      datasource({ page, limit }) {
+        return getFileList({
+          ...this.documentForm,
+          pageNum: page,
+          size: limit
+        });
+      },
+      reload() {
+        this.$refs.table.reload();
+      },
+      beforeRemove(file) {
+        if (file.id) {
+          return removeFile({
+            fileId: file.id
+          }).then(() => {
+            return true;
+          });
+          return true;
+        } else {
+          return true;
+        }
+      },
+      handleRemove(file, fileList) {
+        this.fileList = fileList;
+      },
+      beforeUpload(file) {
+        // if (file.size / 1024 / 1024 > this.size) {
+        //   this.$message.error(`大小不能超过 ${this.size}MB`);
+        //   return false;
+        // }
+
+        if (this.limit > 0 && this.fileList.length === this.limit) {
+          this.$message.error(`最多上传 ${this.limit}个文件`);
+          this._removeCanceledFile(file);
+          return false;
+        }
+
+        // 父组件可在上传前异步拦截(如查重)
+        if (this.beforeUploadFile) {
+          const result = this.beforeUploadFile(file);
+          if (result === false) {
+            this._removeCanceledFile(file);
+            return false;
+          }
+          if (result && typeof result.then === 'function') {
+            return result.then((allowed) => {
+              if (allowed === false) {
+                this._removeCanceledFile(file);
+                return false;
+              }
+              return this._doUpload(file);
+            });
           }
-          this.$emit('fileChange', res.data);
-          return res.data;
+        }
+
+        return this._doUpload(file);
+      },
+      _doUpload(file) {
+        this.loading = true;
+        return uploadFileNew({
+          module: this.module,
+          multiPartFile: file
         })
-        .finally(() => {
-          this.loading = false;
-        });
-    },
-    handlRequest() {
-      return Promise.resolve();
+          .then((res) => {
+            if (res.data) {
+              this.$emit('input', [
+                ...(this.value || []),
+                { ...file, url: res.data.storePath, ...res.data }
+              ]);
+            }
+            this.$emit('fileChange', res.data);
+            return res.data;
+          })
+          .finally(() => {
+            this.loading = false;
+          });
+      },
+      // 取消上传时清理进度条;file-list 由 el-upload 自己从内部 uploadFiles 移除
+      _removeCanceledFile(file) {
+        const fileId = file.uid;
+        if (
+          fileId !== undefined &&
+          this.fileProgressMap[fileId] !== undefined
+        ) {
+          this.$delete(this.fileProgressMap, fileId);
+          this.$delete(this.fileNameMap, fileId);
+          if (Object.keys(this.fileProgressMap).length === 0) {
+            this.loading = false;
+          }
+        }
+        // el-upload 的 handleStart 在 beforeUpload 之前就把文件塞进 uploadFiles,
+        // 即便 beforeUpload 返回 false 也不会自动清掉;这里手动清掉,否则 display 还会显示
+        const upload = this.$refs.upload;
+        if (upload && Array.isArray(upload.uploadFiles)) {
+          const idx = upload.uploadFiles.findIndex((f) => f.uid === fileId);
+          if (idx !== -1) {
+            upload.uploadFiles.splice(idx, 1);
+          }
+        }
+      },
+      handlRequest() {
+        return Promise.resolve();
+      }
+      // onSuccess(response, file, fileList){
+      //   alert(1)
+      //    this.$emit('fileChange',file)
+      // },
     }
-    // onSuccess(response, file, fileList){
-    //   alert(1)
-    //    this.$emit('fileChange',file)
-    // },
-  }
-};
+  };
 </script>
 
 <style lang="scss" scoped>
-.upload-file {
-  display: flex;
-  justify-content: flex-start;
-  align-items: center;
+  .upload-file {
+    display: flex;
+    justify-content: flex-start;
+    align-items: center;
 
-  .lib {
-    margin-left: 12px;
-  }
+    .lib {
+      margin-left: 12px;
+    }
 
-  .imgs-box {
-    margin-left: 10px;
-    flex: 1;
-  }
-  .imgs-box .imgs-p {
-    height: 30px;
-    background: #f0f3f3;
-    line-height: 30px;
-    min-width: 480px;
-    margin-bottom: 5px;
-    padding: 0 10px;
-    display: flex;
-    justify-content: space-between;
+    .imgs-box {
+      margin-left: 10px;
+      flex: 1;
+    }
+    .imgs-box .imgs-p {
+      height: 30px;
+      background: #f0f3f3;
+      line-height: 30px;
+      min-width: 480px;
+      margin-bottom: 5px;
+      padding: 0 10px;
+      display: flex;
+      justify-content: space-between;
+    }
   }
-}
 </style>