vue3+ts使用ElUpload上传(包含loading与重新上传)

<template>
  <div class="com-dialog-style">
    <el-dialog
      class="import-completed-dialog"
      :title="isSuccess ? '导入成功' : '导入用户'"
      v-model="dialogVisible"
      width="60%"
    >
      <!-- 未导入 -->
      <div class="import-completed-cont" v-if="!isSuccess">
        <div class="upload-box">
          <div class="upload-title">选择文件导入</div>
          <div class="upload-content">
            <div>
              <el-upload
                ref="uploadRef"
                class="upload"
                action="/api/user/import"
                drag
                :limit="1"
                :show-file-list="false"
                :before-upload="beforeAvatarUpload"
                :on-change="excelChange"
                :on-success="excelSuccess"
                successExcel
                :auto-upload="false"
              >
                <el-icon class="tips-icon"
                  ><el-icon><UploadFilled /></el-icon
                ></el-icon>
                <div class="el-upload__text">点击或将文件拖拽到这里上传</div>
              </el-upload>
            </div>
            <div class="right-text">
              <div>
                <a :href="excelUrl" class="download-text">下载导入模板</a>
              </div>
              <div class="el-upload__tip" slot="tip">
                仅支持xls或xlsx格式、行数在1002行以内、大小在10M以内的文档。上传后再次上传可替换。
              </div>
              <div class="file-name">{{ fileName }}</div>
            </div>
          </div>
        </div>
        <div class="import-instructions">
          <div class="import-instructions-title">导入说明</div>
          <div>
            <div class="instructions-item-mgt">
              1、一次最多导入<span class="error">1000</span
              >条,请确保文件数据不超过1000条。
            </div>
            <div class="instructions-item-mgt">
              2、<span class="error">工资编码</span>是用户的<span class="error"
                >唯一标识符</span
              >,若系统中已存在相同工资编码的用户,则导入是更新用户信息,而不是新增。
            </div>
            <div class="instructions-item-mgt">
              3、导入功能仅能维护用户基本信息,要新增或修改用户头像请到<span
                class="error"
                >盒子端</span
              >操作。
            </div>
            <div class="instructions-item-mgt">
              4、盒子端批量导入头像时,请用“<span class="error"
                >姓名_工资编码</span
              >”作为文件名,例如:张三_985326
            </div>
            <div class="instructions-item-mgt">
              5、每天凌晨后台会自动将盒子端的头像同步到用户列表(根据姓名和工资编码匹配),点击列表右侧的“<span
                class="error"
                >更新头像</span
              >”可立即同步。
            </div>
            <div class="instructions-item-mgt">
              6、<span class="error">请勿随意修改模板的表头字段</span
              >,否则会导致导入失败。
            </div>
            <div class="instructions-item-mgt">
              7、带 *
              字段为必填;手机号如有填写限制11位,性别请勿填男女以外的其他内容。
            </div>
          </div>
        </div>
        <span slot="footer" class="dialog-footer">
          <el-button type="primary" @click="uploadData">开始导入</el-button>
        </span>
      </div>
      <!-- 导入完成 -->
      <div class="import-completed-cont-ok" v-else>
        <div class="import-completed-item">
          本次共导入 <span>{{ excelData.total_num }}</span> 条数据。
        </div>
        <div class="import-completed-item">
          导入成功
          <span class="success">{{
            excelData.insert_num + excelData.update_num
          }}</span>
          条,其中新增用户
          <span>{{ excelData.insert_num }}</span> 个,修改用户信息
          <span class="waring">{{ excelData.update_num }}</span>
          个(工资编码已存在的用户,导入是更新用户信息,而不是新增)。
        </div>

        <div class="import-completed-item">
          导入失败
          <span class="error">{{ excelData.fail_num }}</span> 条,你可<el-button
            class="btn-text"
            type="text"
            @click="downloadFailureList"
            >下载导入失败名单</el-button
          >,查看失败原因,然后直接在该表格修正,然后直接上传该文件重新导入(重新导入时,“失败原因”那一列可以不删除)。
        </div>
        <div class="import-completed-item">
          关闭该窗口后,点击列表右上角“下载导入失败名单”可下载最后一次导入失败的名单。
        </div>
        <span slot="footer" class="dialog-footer">
          <el-button type="primary" @click="closeSuccess">我知道了</el-button>
        </span>
      </div>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive } from "vue";
import {
  ElDialog,
  ElButton,
  ElNotification,
  ElUpload,
  ElLoading,
} from "element-plus";
import { UploadFilled } from "@element-plus/icons";
import { user_fail } from "@/api";
const dialogVisible = ref(false);
const fileName = ref(""); //导入文件名称
const isSuccess = ref(false); //是否导入成功
const loading = ref(false);
const excelUrl =
  process.env.NODE_ENV === "development"
    ? "https://dc-bj-test.familyhealth.tech" + "/export/导入用户模板.xlsx"
    : window.location.origin + "/export/导入用户模板.xlsx"; //excel下载地址
const handleOpen = () => {
  dialogVisible.value = true;
};
const excelData = ref();
const loadingInstance = ref<ElLoading>();
const beforeAvatarUpload = (files) => {
  //获取文件名后缀
  const fileType = files.name.endsWith(".xlsx") || files.name.endsWith(".xls");
  loadingInstance.value = ElLoading.service({
    lock: true,
    text: "正在导入,请稍后",
    background: "rgba(0, 0, 0, 0.7)",
  });
};
const uploadRef = ref(null);
// 文件改变
const excelChange = (file) => {
  console.log(file, "file");
  fileName.value = file.name;
};
// 文件上传成功
const excelSuccess = ({ data, status, message }) => {
  if (status === 200) {
    isSuccess.value = true;
    excelData.value = data;
  } else if (status === 20005) {
    const msg = message.replace(/\,/g, "<br />");
    ElNotification({
      dangerouslyUseHTMLString: true,
      message: `你上传的文件里面,以下工资编码出现过两次以上,工资编码是用户唯一标识符,不允许重复,请先修正。<br />${msg}`,
      type: "error",
      duration: 0, //消息存在时间,0表示一直存在
    });
  } else {
    ElNotification({
      message: message || "导入内容有误",
      type: "error",
    });
  }
  loadingInstance.value?.close(); //上传成功后关闭loading
  uploadRef.value.clearFiles(); //删除文件
};
const uploadData = async () => {
  uploadRef.value.submit();
};
// 下载导入失败名单
const downloadFailureList = async () => {
  const { status, data } = await user_fail();
  if (status === 200) {
    window.open(data.url);
    ElNotification({
      message: "导出成功",
      type: "success",
    });
  }
};
const closeSuccess = () => {
  dialogVisible.value = false;
  isSuccess.value = false;
};
defineExpose({
  handleOpen,
});
</script>

<style lang="scss">
.import-completed-dialog {
  .success {
    color: #67c23a !important;
  }
  .error {
    color: red !important;
  }
  .waring {
    color: #e6a23c !important;
  }
  .upload-box {
    background: #f7f9fc;
    padding: 12px;
  }
  .upload-content {
    display: flex;
    align-items: flex-end;
    justify-content: flex-start;
    margin: 12px 0 0 0;
  }
  .upload-title {
    font-size: var(--el-font-size-large);
  }
  .tips-icon {
    font-size: 28px;
    color: #409eff;
  }
  .download-text {
    color: #409eff;
  }
  .right-text {
    margin-left: 0.1rem;
  }
  .import-instructions {
    font-size: var(--el-dialog-content-font-size);
    line-height: var(--el-font-size-large);
    padding: 0 0 0 14px;
    margin: 20px 0;
    .import-instructions-title {
      font-size: var(--el-font-size-large);
      margin-bottom: 10px;
    }
    .instructions-item-mgt {
      margin-bottom: 8px;
    }
  }
  .import-completed-item {
    text-align: justify;
    font-size: var(--el-dialog-content-font-size);
    span {
      color: #409eff;
      font-weight: bold;
    }

    .btn-text {
      span {
        color: red;
      }
    }
  }
  .import-completed-item {
    margin-bottom: 0.16rem;
  }
  .dialog-footer {
    justify-content: center;
  }
  .file-name {
    margin-top: 0.05rem;
    color: #409eff;
    font-weight: bold;
  }
}
</style>

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue 3中使用TypeScript封装axios并包含loading功能的步骤如下: 1. 首先,安装axios和@types/axios依赖: ``` npm install axios @types/axios ``` 2. 创建一个名为api.ts文件,用于封装axios请求和loading功能: ```typescript import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'; import { reactive } from 'vue'; // 创建一个loading对象,用于控制loading状态 const loading = reactive({ isLoading: false, }); // 创建一个axios实例 const instance = axios.create({ baseURL: 'https://api.example.com', // 设置请求的基础URL timeout: 5000, // 设置请求超时时间 }); // 请求拦截器,在发送请求之前执行 instance.interceptors.request.use( (config: AxiosRequestConfig) => { loading.isLoading = true; // 开启loading状态 return config; }, (error) => { loading.isLoading = false; // 关闭loading状态 return Promise.reject(error); } ); // 响应拦截器,在接收到响应之后执行 instance.interceptors.response.use( (response: AxiosResponse) => { loading.isLoading = false; // 关闭loading状态 return response.data; }, (error) => { loading.isLoading = false; // 关闭loading状态 return Promise.reject(error); } ); // 封装get请求方法 export const get = async <T>(url: string, config?: AxiosRequestConfig): Promise<T> => { try { const response = await instance.get<T>(url, config); return response; } catch (error) { throw new Error(error); } }; // 封装post请求方法 export const post = async <T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> => { try { const response = await instance.post<T>(url, data, config); return response; } catch (error) { throw new Error(error); } }; export default { loading, }; ``` 3. 在Vue组件中使用封装的axios和loading功能: ```vue <template> <div> <button @click="fetchData">Fetch Data</button> <div v-if="api.loading.isLoading">Loading...</div> <div v-else>{{ data }}</div> </div> </template> <script> import { ref } from 'vue'; import api from './api'; export default { setup() { const data = ref(''); const fetchData = async () => { try { const response = await api.get<string>('/data'); data.value = response; } catch (error) { console.error(error); } }; return { fetchData, data, ...api, }; }, }; </script> ``` 在上述代码中,我们通过`api.ts`文件封装了axios的get和post请求方法,并使用`reactive`创建了一个loading对象来控制loading状态。在Vue组件中,我们可以直接调用`api.get`或`api.post`方法来发送请求,并通过`api.loading.isLoading`来控制loading的显示与隐藏。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值