vben
2020-11-15 661db0c767772bb7a30da9d3eeaf2b47858ccf0b
src/components/Upload/src/UploadModal.vue
@@ -1,31 +1,44 @@
<template>
  <BasicModal
    width="800px"
    title="上传"
    okText="保存"
    v-bind="$attrs"
    @register="register"
    @ok="handleOk"
    :closeFunc="handleCloseFunc"
    :maskClosable="false"
    width="800px"
    title="上传组件"
    :keyboard="false"
    wrapClassName="upload-modal"
    :okButtonProps="{ disabled: isUploadingRef }"
    :okButtonProps="getOkButtonProps"
    :cancelButtonProps="{ disabled: isUploadingRef }"
  >
    <template #centerdFooter>
      <a-button @click="handleStartUpload" color="success" :loading="isUploadingRef">
        {{ isUploadingRef ? '上传中' : '开始上传' }}
      <a-button
        @click="handleStartUpload"
        color="success"
        :disabled="!getIsSelectFile"
        :loading="isUploadingRef"
      >
        {{ getUploadBtnText }}
      </a-button>
    </template>
    <Upload :accept="getStringAccept" :multiple="multiple" :before-upload="beforeUpload">
      <a-button type="primary"> 选择文件 </a-button>
      <span class="px-2">{{ getHelpText }}</span>
    </Upload>
    <BasicTable @register="registerTable" :dataSource="fileListRef" />
    <BasicTable @register="registerTable" :dataSource="fileListRef">
      <template #toolbar>
        <Upload :accept="getStringAccept" :multiple="multiple" :before-upload="beforeUpload">
          <a-button type="primary"> 选择文件 </a-button>
        </Upload>
      </template>
      <template #tableTitle>
        <Alert :message="getHelpText" type="info" banner></Alert>
      </template>
    </BasicTable>
  </BasicModal>
</template>
<script lang="ts">
  import { defineComponent, reactive, ref, toRef, unref } from 'vue';
  import { Upload } from 'ant-design-vue';
  import { defineComponent, reactive, ref, toRefs, unref, computed } from 'vue';
  import { Upload, Alert } from 'ant-design-vue';
  import { BasicModal, useModalInner } from '/@/components/Modal';
  import { BasicTable, useTable } from '/@/components/Table';
  // hooks
@@ -39,23 +52,56 @@
  import { checkFileType, checkImgType, getBase64WithFile } from './utils';
  import { buildUUID } from '/@/utils/uuid';
  import { createImgPreview } from '/@/components/Preview/index';
  import { uploadApi } from '/@/api/demo/upload';
  import { uploadApi } from '/@/api/sys/upload';
  import { isFunction } from '/@/utils/is';
  import { warn } from '/@/utils/log';
  export default defineComponent({
    components: { BasicModal, Upload, BasicTable },
    components: { BasicModal, Upload, BasicTable, Alert },
    props: basicProps,
    setup(props, { emit }) {
      const [register, { closeModal }] = useModalInner();
      const { getAccept, getStringAccept, getHelpText } = useUploadType({
        acceptRef: toRef(props, 'accept'),
        helpTextRef: toRef(props, 'helpText'),
        maxNumberRef: toRef(props, 'maxNumber'),
        maxSizeRef: toRef(props, 'maxSize'),
      //   是否正在上传
      const isUploadingRef = ref(false);
      const fileListRef = ref<FileItem[]>([]);
      const state = reactive<{ fileList: FileItem[] }>({
        fileList: [],
      });
      const fileListRef = ref<FileItem[]>([]);
      const state = reactive<{ fileList: FileItem[] }>({ fileList: [] });
      const [register, { closeModal }] = useModalInner();
      const { accept, helpText, maxNumber, maxSize } = toRefs(props);
      const { getAccept, getStringAccept, getHelpText } = useUploadType({
        acceptRef: accept,
        helpTextRef: helpText,
        maxNumberRef: maxNumber,
        maxSizeRef: maxSize,
      });
      const { createMessage } = useMessage();
      const getIsSelectFile = computed(() => {
        return (
          fileListRef.value.length > 0 &&
          !fileListRef.value.every((item) => item.status === UploadResultStatus.SUCCESS)
        );
      });
      const getOkButtonProps = computed(() => {
        const someSuccess = fileListRef.value.some(
          (item) => item.status === UploadResultStatus.SUCCESS
        );
        return {
          disabled: isUploadingRef.value || fileListRef.value.length === 0 || !someSuccess,
        };
      });
      const getUploadBtnText = computed(() => {
        const someError = fileListRef.value.some(
          (item) => item.status === UploadResultStatus.ERROR
        );
        return isUploadingRef.value ? '上传中' : someError ? '重新上传失败文件' : '开始上传';
      });
      // 上传前校验
      function beforeUpload(file: File) {
        const { size, name } = file;
@@ -73,6 +119,14 @@
          createMessage.error!(`只能上传${accept.join(',')}格式文件`);
          return false;
        }
        const commonItem = {
          uuid: buildUUID(),
          file,
          size,
          name,
          percent: 0,
          type: name.split('.').pop(),
        };
        // 生成图片缩略图
        if (checkImgType(file)) {
          // beforeUpload,如果异步会调用自带上传方法
@@ -81,29 +135,13 @@
            fileListRef.value = [
              ...unref(fileListRef),
              {
                uuid: buildUUID(),
                file,
                thumbUrl,
                size,
                name,
                percent: 0,
                type: name.split('.').pop(),
                ...commonItem,
              },
            ];
          });
        } else {
          fileListRef.value = [
            ...unref(fileListRef),
            {
              uuid: buildUUID(),
              file,
              size,
              name,
              percent: 0,
              type: name.split('.').pop(),
            },
          ];
          fileListRef.value = [...unref(fileListRef), commonItem];
        }
        return false;
      }
@@ -112,6 +150,7 @@
        const index = fileListRef.value.findIndex((item) => item.uuid === record.uuid);
        index !== -1 && fileListRef.value.splice(index, 1);
      }
      // 预览
      function handlePreview(record: FileItem) {
        const { thumbUrl = '' } = record;
@@ -119,19 +158,18 @@
          imageList: [thumbUrl],
        });
      }
      const [registerTable] = useTable({
        columns: createTableColumns(),
        actionColumn: createActionColumn(handleRemove, handlePreview),
        pagination: false,
      });
      //   是否正在上传
      const isUploadingRef = ref(false);
      async function uploadApiByItem(item: FileItem) {
        const { api } = props;
        if (!api || !isFunction(api)) {
          return warn('upload api must exist and be a function');
        }
        try {
          item.status = UploadResultStatus.UPLOADING;
          const { data } = await uploadApi(
            {
              ...(props.uploadParams || {}),
              file: item.file,
            },
            function onUploadProgress(progressEvent: ProgressEvent) {
@@ -154,32 +192,42 @@
          };
        }
      }
      // 点击开始上传
      async function handleStartUpload() {
        const { maxNumber } = props;
        if (fileListRef.value.length > maxNumber) {
          return createMessage.warning(`最多只能上传${maxNumber}个文件`);
        }
        try {
          isUploadingRef.value = true;
          // 只上传不是成功状态的
          const uploadFileList =
            fileListRef.value.filter((item) => item.status !== UploadResultStatus.SUCCESS) || [];
          const data = await Promise.all(
            unref(fileListRef).map((item) => {
            uploadFileList.map((item) => {
              return uploadApiByItem(item);
            })
          );
          isUploadingRef.value = false;
          // 生产环境:抛出错误
          const errorList = data.filter((item) => !item.success);
          if (errorList.length > 0) {
            throw errorList;
          }
          const errorList = data.filter((item: any) => !item.success);
          if (errorList.length > 0) throw errorList;
        } catch (e) {
          isUploadingRef.value = false;
          throw e;
        }
      }
      //   点击保存
      function handleOk() {
        // TODO: 没起作用:okButtonProps={{ disabled: state.isUploading }}
        const { maxNumber } = props;
        if (fileListRef.value.length > maxNumber) {
          return createMessage.warning(`最多只能上传${maxNumber}个文件`);
        }
        if (isUploadingRef.value) {
          createMessage.warning('请等待文件上传后,保存');
          return;
          return createMessage.warning('请等待文件上传后,保存');
        }
        const fileList: string[] = [];
@@ -189,18 +237,15 @@
            fileList.push(responseData.url);
          }
        }
        // 存在一个上传成功的即可保存
        if (fileList.length <= 0) {
          createMessage.warning('没有上传成功的文件,无法保存');
          return;
          return createMessage.warning('没有上传成功的文件,无法保存');
        }
        console.log(fileList);
        emit('change', fileList);
        fileListRef.value = [];
        closeModal();
        emit('change', fileList);
      }
      // 点击关闭:则所有操作不保存,包括上传的
      function handleCloseFunc() {
        if (!isUploadingRef.value) {
@@ -211,11 +256,22 @@
          return false;
        }
      }
      const [registerTable] = useTable({
        columns: createTableColumns(),
        actionColumn: createActionColumn(handleRemove, handlePreview),
        pagination: false,
        inset: true,
        scroll: {
          y: 3000,
        },
      });
      return {
        register,
        closeModal,
        getHelpText,
        getStringAccept,
        getOkButtonProps,
        beforeUpload,
        registerTable,
        fileListRef,
@@ -224,14 +280,13 @@
        handleStartUpload,
        handleOk,
        handleCloseFunc,
        getIsSelectFile,
        getUploadBtnText,
      };
    },
  });
</script>
<style lang="less">
  //   /deep/ .ant-upload-list {
  //     display: none;
  //   }
  .upload-modal {
    .ant-upload-list {
      display: none;