Vue3 导入导出

举报
林太白 发表于 2025/08/13 13:34:48 2025/08/13
【摘要】 Vue3 导入导出

outline: deep

Vue3 导入导出

常见的模式以及库

// FileSaver

https://github.com/eligrey/FileSaver.js/

👉导出用户

这里后端接口对应我们Rust导出方式一

🍎安装依赖file-saver模式

yarn add file-saver

🍎用户导出Excel文档部分

import { ref, reactive, toRefs, onMounted, watch,getCurrentInstance} from 'vue'
const { proxy } = getCurrentInstance();

/** 导出按钮操作 */
function handleExport() {
  proxy.$download("/system/users/export", {
      ...queryParams.value,
  }, `role_${new Date().getTime()}.xlsx`);
}

🍎通用导出方法

这里我们可以直接写到axios的拦截器部分,因为我们导出的时候依靠的需要拦截流

src\utils\request.ts

这里我们拦截流,主要是我们响应拦截器,当后端返回来的是流数据的时候,前端这个时候就不能按照普通数据一样处理

// 响应拦截器
// 在 axios 配置文件中
service.interceptors.response.use(
  (response) => {
    // 如果是 blob 响应,直接返回,不要解包
    if (response.config.responseType === 'blob') {
      return response; // 保留完整的 response 对象
    }
    // 其他响应正常解包
    return response.data;
  },
  (error) => {
    return Promise.reject(error);
  }
);
// 通用导出下载方法
// 创建axios实例
import axios from 'axios'
import { saveAs } from 'file-saver'
const service = axios.create({
  // axios中请求配置有baseURL选项,表示请求URL公共部分
  baseURL: "http://localhost:8888/api",//import.meta.env.VITE_APP_BASE_API,
  // 超时
  timeout: 10000
})

// 将JavaScript对象转换为 URL 查询参数字符串
export function transParams(params) {
  let result = ''
  for (const propName of Object.keys(params)) {
    const value = params[propName]
    var part = encodeURIComponent(propName) + "="
    if (value !== null && value !== "" && typeof (value) !== "undefined") {
      if (typeof value === 'object') {
        for (const key of Object.keys(value)) {
          if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
            let params = propName + '[' + key + ']'
            var subPart = encodeURIComponent(params) + "="
            result += subPart + encodeURIComponent(value[key]) + "&"
          }
        }
      } else {
        result += part + encodeURIComponent(value) + "&"
      }
    }
  }
  return result
}
// 下载方法
export const download = (url, params, filename, config = {}) => {
  return service.post(url, params, {
    transformRequest: [(params) => { return transParams(params) }],
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    responseType: 'blob',
    ...config
  }).then(async (response) => {
    // 确保 response 是完整的响应对象,不是被解包的数据
    const data = response.data || response;
    
    // 检查是否为错误响应(JSON 格式)
    const contentType = response.headers?.['content-type'] || '';
    if (contentType.includes('application/json')) {
      const text = await data.text();
      const json = JSON.parse(text);
      ElMessage.error(json.msg || '下载失败');
      return;
    }
    
    // 创建 blob 并下载
    const blob = new Blob([data], { type: 'text/csv;charset=utf-8;' });
    saveAs(blob, filename || 'users.csv');
  }).catch((r) => {
    console.error(r);
    ElMessage.error('下载文件出现错误,请联系管理员!');
  });
};

🍎注册使用

import { download} from '@/utils/methods'

export default {
  install(app) {
    // 全局方法
    app.config.globalProperties.$download = download
  }
}

🍎测试一下这个地址

http://localhost:8888/api/system/users/export

看看我们拿到的信息,直接访问这个地址。
或者去掉token认证信息,然后再进行访问

这个时候我们之前写的接口

can not parse "export" to a i32

尝试一下,导出方法已经ok了,这就是采取最普通的file-saver的方式进行的导出

我们导出的数据大致如下

用户ID,用户名,姓名,年龄,性别,电话,地址,状态,头像,身高,体重,疾病
44,666666,san,666666,1,18735797977,666666,0,/uploads/images/1746683201317-nexusvue-feedback.png,,,18735797977
65,123456,,,1,18735797977,111,0,/uploads/images/1748584154718-2.png,,,18735797977
67,admin,,20,0,admin,,0,/uploads/images/9ff7b427-fe1b-46c6-a7c9-20823aac07c0.png,,,

🍎添加加载效果

这里添加一个导出时候的加载效果

glabalui.ts这里我们搭建一个文件。用于不同UI库使用同样代码实现

// 全局UI展示
// 目的=> 切换Element Plus和 Antd库的UI组件
// 打开遮罩层
import { ElMessage, ElMessageBox, ElNotification, ElLoading } from 'element-plus'
let loadingInstance; // 全局loading

/**
 * 显示加载遮罩层的函数
 * @param {string} content - 加载时显示的文本内容
 */
export const loading=(content)=>{
  // 创建并显示一个全屏的加载遮罩层
  // ElLoading.service 是 Element UI 提供的全局服务方法
  loadingInstance = ElLoading.service({
    // 是否锁定屏幕滚动
    lock: true,
    // 加载时显示的文本内容
    text: content,
    // 遮罩层背景颜色,使用 rgba 格式设置半透明黑色
    background: "rgba(0, 0, 0, 0.7)",
  });
};

// 关闭遮罩层
export const closeLoading=()=> {
  loadingInstance.close();
}

👉下载用户模板

🍎导出逻辑

<el-col :span="1.5">
    <el-button type="info" icon="Download" @click="handleDowload"
        v-hasPermi="['system:role:dowload']">下载模板</el-button>
</el-col>


// 导出用户模板
const handleDowload = async () => {
    proxy.$download("/system/users/exporttemplate", {}, `role_${new Date().getTime()}.xlsx`);
}

🍎通用方法

import { saveAs } from 'file-saver';
// 将JavaScript对象转换为 URL 查询参数字符串
export function transParams(params) {
  let result = ''
  for (const propName of Object.keys(params)) {
    const value = params[propName]
    var part = encodeURIComponent(propName) + "="
    if (value !== null && value !== "" && typeof (value) !== "undefined") {
      if (typeof value === 'object') {
        for (const key of Object.keys(value)) {
          if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
            let params = propName + '[' + key + ']'
            var subPart = encodeURIComponent(params) + "="
            result += subPart + encodeURIComponent(value[key]) + "&"
          }
        }
      } else {
        result += part + encodeURIComponent(value) + "&"
      }
    }
  }
  return result
}
// 下载方法
export const download = (url, params, filename, config = {}) => {
  loading('正在下载,请稍候...');
  return service.post(url, params, {
    transformRequest: [(params) => { return transParams(params) }],
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    responseType: 'blob',
    ...config
  }).then(async (response) => {
    // 确保 response 是完整的响应对象,不是被解包的数据
    const data = response.data || response;
    
    // 检查是否为错误响应(JSON 格式)
    const contentType = response.headers?.['content-type'] || '';
    if (contentType.includes('application/json')) {
      const text = await data.text();
      const json = JSON.parse(text);
      ElMessage.error(json.msg || '下载失败');
      return;
    }
    
    // 创建 blob 并下载
    const blob = new Blob([data], { type: 'text/csv;charset=utf-8;' });
    saveAs(blob, filename || 'users.csv');
    closeLoading();
  }).catch((r) => {
    console.error(r);
    closeLoading();
    ElMessage.error('下载文件出现错误,请联系管理员!');
  });
};

👉导入用户

🍎导出功能

这里我们直接先把接口写上去,后面自己拼接一下就行

 <el-col :span="1.5">
      <el-upload class="upload-demo"
      :on-success="handleSuccess"
      action='http://localhost:8888/api/system/users/import' 
      :before-upload="beforeFileUpload" accept=".xls,xlsx"
          :show-file-list="false">
          <el-button type="primary" icon="Upload">导入数据</el-button>
      </el-upload>
  </el-col>

方法逻辑

const beforeFileUpload = (rawFile) => {
  const fileExtension = rawFile.name.split('.').pop().toLowerCase(); // 获取文件扩展名
  if (fileExtension !== 'xls' && fileExtension !== 'xlsx') {
    ElMessage.error('文件类型为.xls or .xlsx格式');
    return false;
  } else if (rawFile.size / 1024 / 1024 > 50) { // 设置最大文件大小为 5MB
    ElMessage.error('文件超过 50MB!');
    return false;
  }

  return true;
};

🍎完善成功信息提示


// 导入成功
const handleSuccess = (res,uploadFile) => {
    if(res.code==200){
        ElMessage.success(res.msg)
    }else{
        ElMessage.error(res.msg)
    }
    // console.log(res,uploadFile);
}

🍎完善加载效果

// 上传前文件检测
const beforeFileUpload = (rawFile) => {
  loading.value = true;
  const fileExtension = rawFile.name.split('.').pop().toLowerCase(); // 获取文件扩展名
  if (fileExtension !== 'xls' && fileExtension !== 'xlsx') {
    ElMessage.error('文件类型为.xls or .xlsx格式');
    loading.value = false;
    return false;
  } else if (rawFile.size / 1024 / 1024 > 50) { // 设置最大文件大小为 5MB
    ElMessage.error('文件超过 50MB!');
    loading.value = false;
    return false;
  }
  return true;
};

测试一下,ok。可以继续优化了

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。