index.vue 11 KB
<script lang="ts" setup name="UploadFiles">
import { ref, computed } from "vue";
import { ElMessage, genFileId } from "element-plus";
import { Plus, Download, Upload, } from "@element-plus/icons-vue";
import type { UploadRawFile } from 'element-plus'

const emits = defineEmits(["onUpload", "beforeUPload", "uploadFile", "uploadBtnClick", "cascaderChange", "selectChange"]);
const props = defineProps({
  uploadInfo: {
    type: Object,
    default: {}
  }
})

const fileUploadRef = ref()

const fileList = computed({
  get: () => {
    return props.uploadInfo.fileList ?? []
  },
  set: (val) => {
    return val
  }
})
const extraParams = computed(() => {
  return props.uploadInfo.extraParams ?? {}
})
const steps = computed(() => {
  return props.uploadInfo.steps ?? []
})
const triggerBtn = computed(() => {
  return props.uploadInfo.triggerBtn ?? {}
})
const toolBar = computed(() => {
  return props.uploadInfo.toolBar ?? []
})

const onUpload = async (file, list) => {
  const isExcel = file.raw.type === 'application/vnd.ms-excel' || file.raw.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
  if (!isExcel) {
    const fileUpload = fileUploadRef.value[0] || fileUploadRef.value
    fileUpload?.handleRemove(file)
    ElMessage.error('上传文件只能是 .xls, .xlsx 格式');
    return false;
  }
  emits("onUpload", file, list)
}

// 数据导入
const beforeUPload = (file: any) => {
  emits('beforeUPload', file)
};
// 文件数超出提示
const exceedFile = (files, cover) => {
  if (cover) {
    const fileUpload = fileUploadRef.value[0] || fileUploadRef.value
    fileUpload?.clearFiles()
    const file = files[0] as UploadRawFile
    file.uid = genFileId()
    fileUpload?.handleStart(file)
  } else {
    ElMessage.error('超过最大上传文件个数!');
  }
};
// 上传错误提示
const handleError = () => {
  ElMessage.error('导入数据失败,请您重新上传!');
};
//上传成功提示
const handleSuccess = (response, uploadFile, uploadFiles) => {
  console.log('导入数据成功!')
};

const btnClick = (file, type) => {
  if (type == 'remove') {
    const fileUpload = fileUploadRef.value[0] || fileUploadRef.value
    fileUpload?.handleRemove(file)
  } else if (type == 'upload') {
    file.show = false
    emits('uploadFile', file)
  }
}

const toolBtnClick = (btn) => {
  emits('uploadBtnClick', btn)
}

const cascaderChange = (val) => {
  emits('cascaderChange', val)
}

const selectChange = (val) => {
  emits('selectChange', val)
}

const treeSelectLoad = (node, resolve, item) => {

}

defineExpose({
  fileList,
  extraParams
});

</script>
<template>
  <div class="upload_panel_wrap">
    <template v-if="props.uploadInfo.type == 'panel'">
      <div class="upload_step_panel">
        <div class="step_item" v-for="item in steps">
          <p class="item_title">{{ item.title }}</p>
          <div class="form_item" v-if="item.type == 'select'">
            <span class="item_label" :class="{ required_mark: item.selectInfo.required }">{{ item.selectInfo.label
            }}:</span>
            <el-select v-model="extraParams[item.selectInfo.field]" :placeholder="item.selectInfo.placeholder"
              :disabled="item.selectInfo.disabled" @change="selectChange">
              <el-option v-for="opts in item.selectInfo.options" :label="opts.label" :value="opts.value" />
            </el-select>
          </div>
          <div class="form_item" v-if="item.type == 'tree-select'">
            <span class="item_label" :class="{ required_mark: item.selectInfo.required }">{{ item.selectInfo.label
            }}:</span>
            <el-tree-select
              v-model="extraParams[item.selectInfo.field]"
              :data="item.selectInfo.options"
              :placeholder="item.selectInfo.placeholder"
              :clearable="item.selectInfo.clearable"
              :filterable="item.selectInfo.filterable"
              :disabled="item.selectInfo.disabled"
              :lazy="item.selectInfo.lazy ?? true"
              :check-strictly = "item.selectInfo.checkStrictly ?? false"
              node-key="guid"
              :load="(node, resolve) => treeSelectLoad(node, resolve, item)"
              :props="item.selectInfo.props"
              :default-expanded-keys="item.selectInfo.expandKeys"
            >
            </el-tree-select>
          </div>
          <div class="form_item" v-else-if="item.type == 'cascader'">
            <span class="item_label" :class="{ required_mark: item.cascaderInfo.required }">{{ item.cascaderInfo.label
            }}:</span>
            <el-cascader v-model="extraParams[item.cascaderInfo.field]" :props="item.cascaderInfo.props"
              :options="item.cascaderInfo.options" :show-all-levels="item.cascaderInfo.showAllLevels ?? true"
              :clearable="item.cascaderInfo.clearable" :collapse-tags="item.cascaderInfo.collapse ?? false"
              :collapse-tags-tooltip="item.cascaderInfo.tagsTooltip ?? false"
              :max-collapse-tags="item.cascaderInfo.maxTags ?? 1" :filterable="item.cascaderInfo.filterable ?? false"
              @change="cascaderChange" />
          </div>
          <div class="item_btn" v-else-if="item.type == 'btn_down'">
            <el-button plain @click="toolBtnClick({ value: 'exoprt_model' })" v-preReClick>
              <el-icon>
                <Download />
              </el-icon>
              <span>下载模板</span>
            </el-button>
          </div>
          <el-upload v-else ref="fileUploadRef" class="upload_panel" :class="[props.uploadInfo.col]"
            v-model:file-list="fileList" :action="item.uploadInfo.action" :auto-upload="item.uploadInfo.auto ?? ''"
            :drag="item.uploadInfo.drag ?? false" :accept="item.uploadInfo.accept" :limit="item.uploadInfo.limit ?? 1"
            :on-change="onUpload" :on-exceed="val => exceedFile(val, item.uploadInfo.cover ?? false)"
            :on-error="handleError" :on-success="handleSuccess" :before-upload="beforeUPload"
            :show-file-list="item.uploadInfo.showList ?? true">
            <template #trigger>
              <el-button type="primary">
                <el-icon>
                  <Upload />
                </el-icon>
                <span>导入文件</span>
              </el-button>
            </template>
            <template #tip v-if="item.uploadInfo.tips">
              <div class="el-upload__tip">{{ item.uploadInfo.tips }}</div>
            </template>
            <template #file="{ file }">
              <div class="file_panel">
                <div class="file_item">
                  <div class="file_name">
                    <el-icon>
                      <svg-icon name="icon-excel" />
                    </el-icon>
                    <span>{{ file.name }}</span>
                  </div>
                  <div class="file_btn">
                    <span class="text_btn" @click="btnClick(file, 'remove')" v-preReClick>删除</span>
                  </div>
                </div>
              </div>
            </template>
          </el-upload>
        </div>
      </div>
    </template>
    <template v-else>
      <el-upload ref="fileUploadRef" class="upload_panel" :class="[props.uploadInfo.col]" v-model:file-list="fileList"
        :action="props.uploadInfo.action" :auto-upload="props.uploadInfo.auto ?? ''" :drag="props.uploadInfo.drag ?? false"
        :accept="props.uploadInfo.accept" :limit="props.uploadInfo.limit ?? 1" :on-change="onUpload"
        :on-exceed="val => exceedFile(val, props.uploadInfo.cover ?? false)" :on-error="handleError"
        :on-success="handleSuccess" :before-upload="beforeUPload" :show-file-list="props.uploadInfo.showList ?? true">
        <template #trigger v-if="props.uploadInfo.dragable">
          <el-icon :class="[props.uploadInfo.col]">
            <Plus />
          </el-icon>
          <div class="el-upload__text">点击或拖拽上传</div>
        </template>
        <template #trigger v-else>
          <el-button :type="triggerBtn.type" :plain="triggerBtn.plain">
            <el-icon v-if="triggerBtn.icon && triggerBtn.icon == 'Upload'">
              <Upload />
            </el-icon>
            <span>{{ triggerBtn.label }}</span>
          </el-button>
        </template>
        <template #file="{ file }">
          <div class="file_panel">
            <div class="file_item">
              <div class="file_name">
                <el-icon>
                  <svg-icon name="icon-excel" />
                </el-icon>
                <span>{{ file.name }}</span>
              </div>
              <div class="file_btn">
                <span class="text_btn" v-if="file.show ?? true" @click="btnClick(file, 'upload')" v-preReClick>上传</span>
                <span class="text_btn" @click="btnClick(file, 'remove')" v-preReClick>删除</span>
              </div>
            </div>
          </div>
        </template>
        <div class="tool_bar" v-if="toolBar.length">
          <el-button v-for="btn in toolBar" :type="btn.type" :plain="btn.plain" @click="toolBtnClick(btn)" v-preReClick>
            <el-icon v-if="btn.icon && btn.icon == 'Download'">
              <Download />
            </el-icon>
            <span>{{ btn.label }}</span>
          </el-button>
        </div>
      </el-upload>
    </template>
  </div>
</template>

<style lang="scss" scoped>
.upload_panel_wrap {
  width: 100%;

  .upload_panel {
    display: flex;
    align-items: center;
    flex-wrap: wrap;

    :deep(.el-upload-list) {
      width: 100%;
      margin: 0;

      .el-upload-list__item {
        margin-top: 8px;
        margin-bottom: 0;
      }

      .el-upload {
        width: 100%;
      }
    }

    .tool_bar {
      margin-left: 8px;
    }

    .file_panel {
      .file_item {
        display: flex;
        justify-content: space-between;
        align-items: center;

        .file_name {
          color: var(--el-color-regular);
          display: flex;
          align-items: center;

          .el-icon {
            color: #3A7049;
            margin-right: 8px;
            width: 20px;
            height: 20px;
            vertical-align: middle;

            svg {
              width: 100%;
              height: 100%;
            }
          }
        }

        .file_btn {
          .text_btn {
            margin-left: 8px;
          }
        }
      }
    }

    &.row-reverse {
      flex-direction: row-reverse;

      .tool_bar {
        margin-left: 0;
        margin-right: 8px;
      }
    }
  }

  .upload_step_panel {
    .step_item {
      margin-bottom: 8px;

      .item_title {
        color: var(--el-color-regular);
        margin: 0;
        line-height: 36px;
      }

      .form_item {
        margin: 8px 0;

        .item_label {
          padding: 0 8px;

          &.required_mark {
            &::after {
              margin-left: 0;
              left: 0;
            }
          }
        }
      }

      .item_btn {
        margin: 8px 0;
      }
    }

    .upload_panel {
      display: block;
      padding-top: 36px;
      position: relative;

      :deep(.el-upload__tip) {
        position: absolute;
        top: 0;
        margin: 0;
        color: var(--el-text-color-regular);
        font-size: 14px;
      }
    }
  }
}
</style>