lhd 10 hours ago
parent
commit
0093fab05d
1 changed files with 544 additions and 0 deletions
  1. 544 0
      src/components/UploadFile.vue

+ 544 - 0
src/components/UploadFile.vue

@@ -0,0 +1,544 @@
+<template>
+  <div class="upload-file">
+    <div class="file-area">
+      <span class="label-text">{{ labelText }}</span>
+      <div class="upload-box">
+        <el-upload
+          ref="upload"
+          class="file-uploader"
+          action="no"
+          :accept="acceptFileType"
+          :multiple="limit === null || limit > 1"
+          :show-file-list="false"
+          :auto-upload="false"
+          :file-list="fileList"
+          :on-change="onFileChange"
+          :on-exceed="handleExceed"
+          :limit="limit"
+        >
+          <el-button>{{ type === 'h5_games' ? '选择Zip压缩包或单个html文件' : '选取' + labelText + '文件' }}</el-button>
+        </el-upload>
+        <el-button size="small" type="primary" @click="uploadFiles"> 上传 </el-button>
+        <el-button size="small" type="primary" @click="useResource"> 使用资源 </el-button>
+      </div>
+    </div>
+    <el-divider v-if="showDivider" />
+    <div class="upload-tip">{{ uploadTip }}</div>
+    <ul v-if="fileList.length > 0" slot="file-list" class="file-list">
+      <li v-for="(file, i) in fileList" :key="i">
+        <div class="file-name">
+          <span>
+            <SvgIcon v-if="iconClass" :icon-class="iconClass" size="12" />
+            <!-- 编辑序号和名称 -->
+            <template v-if="content.file_info[file.file_id] && content.file_info[file.file_id].isEdit">
+              <el-input v-model="content.file_info[file.file_id].xuhao" placeholder="序号" style="width: 80px" />
+              <el-input v-model="content.file_info[file.file_id].file_name" placeholder="名称" />
+            </template>
+            <!-- 可以编辑序号名称状态下显示序号 -->
+            <span v-else>{{
+              canEditName && file.file_id
+                ? content.file_info[file.file_id].xuhao + content.file_info[file.file_id].file_name
+                : (file.file_name ?? file.name)
+            }}</span>
+            <!-- <span>({{ file.size }})</span> -->
+          </span>
+          <el-progress
+            v-if="file.progress > 0 && file.progress < 100"
+            type="circle"
+            :percentage="file.progress"
+            :width="20"
+            color="#2A5AF6"
+            stroke-linecap="butt"
+            :show-text="false"
+          />
+          <span v-else-if="file.file_id"> 完成</span>
+        </div>
+        <SvgIcon icon-class="delete-black" size="12" @click="removeFile(file, i)" />
+        <SvgIcon
+          v-show="type === 'picture' && file.file_id"
+          icon-class="mark"
+          size="12"
+          @click="viewDialog(file.file_id)"
+        />
+        <!-- 编辑名称和序号 -->
+        <template v-if="canEditName && file.file_id">
+          <SvgIcon
+            v-if="content.file_info[file.file_id].isEdit"
+            icon-class="icon-save"
+            size="12"
+            @click="changeIsEdit(content.file_info[file.file_id])"
+          />
+          <SvgIcon v-else icon-class="icon-edit" size="12" @click="changeIsEdit(content.file_info[file.file_id])" />
+        </template>
+      </li>
+    </ul>
+
+    <SelectResource
+      :visible.sync="visibleResource"
+      :project-id="projectId"
+      :accept="accept"
+      :courseware-id="coursewareId"
+      @selectResource="selectResource"
+    />
+  </div>
+</template>
+
+<script>
+import { fileUpload } from '@/api/app';
+import { conversionSize } from '@/utils/common';
+import { isEnable } from '@/views/book/courseware/data/common';
+import { getConfig, getToken } from '@/utils/auth';
+
+import SelectResource from '@/views/book/courseware/create/components/base/common/SelectResource.vue';
+import { SubmitFileToResourceStore } from '@/api/book';
+export default {
+  name: 'UploadFile',
+  components: {
+    SelectResource,
+  },
+  props: {
+    // 项目id
+    projectId: {
+      type: String,
+      default: '',
+    },
+    // 课件id
+    coursewareId: {
+      type: String,
+      default: '',
+    },
+    // 组件id
+    componentId: {
+      type: String,
+      default: '',
+    },
+    // 组件标签
+    labelText: {
+      type: String,
+      default: '',
+    },
+    // 上传支持的文件格式
+    acceptFileType: {
+      type: String,
+      default: '*',
+    },
+    // 提示语
+    uploadTip: {
+      type: String,
+      default: '',
+    },
+    // 图标
+    iconClass: {
+      type: String,
+      default: '',
+    },
+    fileList: {
+      type: Array,
+      default: () => [],
+    },
+    fileIdList: {
+      type: Array,
+      default: () => [],
+    },
+    fileInfoList: {
+      type: Array,
+      default: () => [],
+    },
+    type: {
+      type: String,
+      default: '',
+    },
+    singleSize: {
+      type: Number,
+      default: 100,
+    },
+    totalSize: {
+      type: Number,
+      default: 1024,
+    },
+    limit: {
+      type: Number,
+      default: null,
+    },
+    canEditName: {
+      type: Boolean,
+      default: false,
+    },
+    fileInfo: {
+      type: Object,
+      default: () => ({}),
+    },
+    index: {
+      // 如果是二维数组里循环上传 一维索引
+      type: Number,
+      default: null,
+    },
+    indexs: {
+      // 如果是二维数组里循环上传 二维索引
+      type: Number,
+      default: null,
+    },
+    // 是否显示分割线
+    showDivider: {
+      type: Boolean,
+      default: true,
+    },
+  },
+  data() {
+    return {
+      curFile: null,
+      conversionSize,
+      visible: false,
+      content: {
+        file_list: this.fileList,
+        file_id_list: this.fileIdList,
+        file_info_list: this.fileInfoList,
+        file_info: this.fileInfo,
+      },
+      visibleResource: false,
+      isEnable,
+    };
+  },
+  computed: {
+    accept() {
+      let accept = '*';
+      if (this.acceptFileType.includes('.mp3')) {
+        accept = 'audio';
+      } else if (this.acceptFileType.includes('.mp4')) {
+        accept = 'video';
+      } else if (this.acceptFileType.includes('.jpg')) {
+        accept = 'image';
+      } else if (this.acceptFileType.includes('.zip')) {
+        accept = 'h5_game';
+      } else if (this.acceptFileType.includes('.txt')) {
+        accept = 'text';
+      }
+      return accept;
+    },
+  },
+  watch: {
+    content: {
+      handler(val) {
+        this.$emit('updateFileList', val, this.index, this.indexs);
+      },
+      deep: true,
+    },
+  },
+  methods: {
+    // 显示自定义样式文件列表
+    onFileChange(file, fileList) {
+      this.afterSelectFile(file);
+      fileList.forEach((file) => {
+        if (!file.progress || file.progress <= 0) file.progress = 0;
+      });
+      const lists = this.$refs.upload.uploadFiles;
+      if (lists.length === 0) return;
+      const files = lists.filter((item) => {
+        let find = this.content.file_list.findIndex((p) => p.uid === item.uid);
+        return find === -1;
+      });
+      if (this.limit !== null && this.content.file_list.length + files.length > this.limit) {
+        this.$message.warning(
+          `当前限制选择 ${this.limit} 个文件,本次选择了 ${files.length} 个文件,共选择了 ${
+            files.length + this.content.file_list.length
+          } 个文件`,
+        );
+        return;
+      }
+      this.content.file_list = [...this.content.file_list, ...files];
+    },
+
+    handleExceed(files, fileList) {
+      this.$message.warning(
+        `当前限制选择 ${this.limit} 个文件,本次选择了 ${files.length} 个文件,共选择了 ${
+          files.length + fileList.length
+        } 个文件`,
+      );
+    },
+
+    // 删除文件
+    removeFile(file, i) {
+      this.$confirm('是否删除当前文件?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          this.$refs.upload.handleRemove(file);
+          this.content.file_list.splice(i, 1);
+          this.content.file_id_list.splice(i, 1);
+        })
+        .catch(() => {});
+    },
+
+    // 文件校验
+    afterSelectFile(file) {
+      const fileName = file.name;
+      let singleSizeTip = `文件[${fileName}]大小超过${conversionSize(this.singleSize * 1024 * 1024)},被移除!`;
+      if (file.size > this.singleSize * 1024 * 1024) {
+        this.$message.error(singleSizeTip);
+        this.$refs.upload.handleRemove(file);
+        return false;
+      }
+
+      const suffix = fileName.slice(fileName.lastIndexOf('.') + 1, fileName.length).toLowerCase();
+      let fileType = [];
+      let typeTip = '';
+
+      if (this.type === 'audio') {
+        fileType = ['mp3', 'acc', 'wma', 'wav'];
+        typeTip = '音频文件只能是 mp3、acc、wma、wav格式!';
+      } else if (
+        this.type === 'picture' ||
+        this.type === 'image_text' ||
+        this.type === 'drawing' ||
+        this.type === 'character_structure' ||
+        this.type === 'newWord_template' ||
+        this.type === 'character'
+      ) {
+        fileType = ['jpg', 'png', 'jpeg'];
+        typeTip = '图片文件只能是 jpg、png、jpeg 格式!';
+      } else if (this.type === 'video' || this.type === 'video_interaction') {
+        fileType = ['mp4'];
+        typeTip = '视频文件只能是 mp4 格式!';
+      } else if (this.type === 'upload_preview' || this.type === 'video_interaction_file') {
+        fileType = [
+          'png',
+          'jpg',
+          'jpeg',
+          'txt',
+          'pdf',
+          'doc',
+          'docx',
+          'xls',
+          'xlsx',
+          'ppt',
+          'pptx',
+          'mp3',
+          'wma',
+          'mp4',
+          'mov',
+          'zip',
+          'rar',
+        ];
+        typeTip = '文件不支持';
+      } else if (this.type === 'h5_games') {
+        fileType = ['zip', 'html'];
+        typeTip = 'H5游戏文件只能是 zip、html 格式!';
+      } else if (this.type === '3DModel') {
+        fileType = ['fbx', 'zip', 'gltf', 'glb'];
+        typeTip = '3D模型文件只能是 fbx、gltf、glb、zip 格式!';
+      }
+      const isNeedType = fileType.includes(suffix);
+      if (!isNeedType) {
+        typeTip += `,[${fileName}]被移除!`;
+        this.$message.error(typeTip);
+        this.$refs.upload.handleRemove(file);
+        return false;
+      }
+    },
+
+    // 上传文件
+    uploadFiles() {
+      const files = (this.content.file_list || []).filter((file) => file.uid && file.status !== 'success');
+      if (files.length <= 0) {
+        this.$message.error('没有需要上传的文件!');
+        return false;
+      }
+      const totalSize = files.reduce((sum, cur) => sum + Number(cur.size || 0), 0);
+      if (totalSize > this.totalSize * 1024 * 1024) {
+        this.$message.error(`文件总大小不能超过${conversionSize(this.totalSize * 1024 * 1024)}!`);
+        return false;
+      }
+
+      files
+        .filter((p) => {
+          let pro = p.progress || -1;
+          return pro <= 0;
+        })
+        .forEach((file) => {
+          // 添加验证
+          if (!file.raw || !(file.raw instanceof Blob)) {
+            this.$message.error(`文件 ${file.name} 无效,请删除后重新选择!`);
+            return;
+          }
+          let form = new FormData();
+          form.append(file.name, file.raw, file.name);
+          fileUpload('Mid', form, {
+            handleUploadProgress: (progressEvent) => {
+              // 进度到99等服务器返回文件信息后才算实际完成
+              let per = Number((progressEvent.progress * 99).toFixed(2) || 0);
+              let en = this.content.file_list.find((p) => p.uid === file.uid);
+              if (en) {
+                en.progress = per;
+                this.$forceUpdate();
+              }
+            },
+          }).then(({ file_info_list }) => {
+            let file_index = this.content.file_list.findIndex((p) => p.uid === file.uid);
+            if (file_index > -1) {
+              if (this.type === 'picture') {
+                this.content.file_info_list[file_index] = {
+                  file_id: file_info_list[0].file_id,
+                  file_name: file_info_list[0].file_name,
+                  title: '',
+                  intro: '',
+                };
+              }
+              this.content.file_list[file_index] = {
+                file_id: file_info_list[0].file_id,
+                file_name: file_info_list[0].file_name,
+                file_url: file_info_list[0].file_url,
+              };
+              if (this.canEditName) {
+                let obj = {
+                  xuhao: '',
+                  isEdit: false,
+                  file_name: file_info_list[0].file_name,
+                };
+                this.$set(this.content.file_info, file_info_list[0].file_id, obj);
+              }
+              this.content.file_id_list.push(file_info_list[0].file_id);
+              this.$refs.upload.uploadFiles = [];
+              this.$forceUpdate();
+            }
+          });
+        });
+    },
+
+    // 显示弹窗
+    viewDialog(file_id) {
+      if (file_id) this.visible = true;
+      this.curFile = this.content.file_info_list.find((file) => file.file_id === file_id);
+    },
+
+    // 给文件加介绍
+    fillDescribeToFile(file) {
+      let en = this.content.file_info_list.find((p) => p.file_id === file.file_id);
+      if (en) {
+        Object.assign(en, file);
+      }
+    },
+    // 使用资源
+    useResource() {
+      this.visibleResource = true;
+    },
+    selectResource({ file_id, file_name, file_url, intro }) {
+      this.content.file_list.push({ file_id, file_name, file_url });
+      this.content.file_id_list.push(file_id);
+      this.content.file_info_list.push({ file_id, file_name, title: '', intro });
+      if (this.canEditName) {
+        let obj = {
+          xuhao: '',
+          isEdit: false,
+          file_name,
+        };
+        this.$set(this.content.file_info, file_id, obj);
+      }
+      this.visibleResource = false;
+    },
+    // 编辑文件名及序号
+    changeIsEdit(file) {
+      file.isEdit = !file.isEdit;
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.module-content {
+  .file-area {
+    display: flex;
+    column-gap: 16px;
+    align-items: center;
+
+    .label-text {
+      font-size: 14px;
+      color: $font-light-color;
+    }
+
+    div {
+      flex: 1;
+    }
+  }
+
+  .el-divider {
+    margin: 16px 0;
+  }
+
+  .upload-tip {
+    margin-bottom: 16px;
+    font-size: 12px;
+    color: #86909c;
+  }
+
+  .upload-box {
+    display: flex;
+    justify-content: space-between;
+
+    .el-button + .el-button {
+      margin-left: 2px;
+    }
+
+    .file-uploader {
+      flex: 1;
+
+      :deep .el-upload {
+        &--text {
+          width: 100%;
+          background-color: $fill-color;
+          border-radius: 2px 0 0 2px;
+
+          .el-button {
+            width: 100%;
+            color: #86909c;
+            text-align: left;
+          }
+        }
+      }
+    }
+
+    .el-button {
+      border-radius: 0 2px 2px 0;
+    }
+  }
+
+  .old_file_list {
+    margin-top: 16px;
+  }
+
+  .file-list {
+    display: flex;
+    flex-direction: column;
+    row-gap: 16px;
+
+    li {
+      display: flex;
+      column-gap: 12px;
+      align-items: center;
+
+      .file-name {
+        display: flex;
+        column-gap: 14px;
+        align-items: center;
+        justify-content: space-between;
+        max-width: 500px; // 360px有点窄
+        padding: 8px 12px;
+        font-size: 14px;
+        color: #1d2129;
+        background-color: #f7f8fa;
+
+        span {
+          display: flex;
+          column-gap: 14px;
+          align-items: center;
+        }
+      }
+
+      .svg-icon {
+        cursor: pointer;
+      }
+    }
+  }
+}
+</style>