Vue3 导入导出
常见的模式以及库
javascript
// FileSaver
https://github.com/eligrey/FileSaver.js/
👉导出用户
这里后端接口对应我们Rust导出方式一
🍎安装依赖file-saver
模式
javascript
yarn add file-saver
🍎用户导出Excel
文档部分
javascript
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
这里我们拦截流,主要是我们响应拦截器,当后端返回来的是流数据的时候,前端这个时候就不能按照普通数据一样处理
javascript
// 响应拦截器
// 在 axios 配置文件中
service.interceptors.response.use(
(response) => {
// 如果是 blob 响应,直接返回,不要解包
if (response.config.responseType === 'blob') {
return response; // 保留完整的 response 对象
}
// 其他响应正常解包
return response.data;
},
(error) => {
return Promise.reject(error);
}
);
javascript
// 通用导出下载方法
// 创建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('下载文件出现错误,请联系管理员!');
});
};
🍎注册使用
javascript
import { download} from '@/utils/methods'
export default {
install(app) {
// 全局方法
app.config.globalProperties.$download = download
}
}
🍎测试一下这个地址
javascript
http://localhost:8888/api/system/users/export
看看我们拿到的信息,直接访问这个地址。
或者去掉token认证信息,然后再进行访问
这个时候我们之前写的接口
javascript
can not parse "export" to a i32
尝试一下,导出方法已经ok了,这就是采取最普通的file-saver
的方式进行的导出
我们导出的数据大致如下
javascript
用户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库使用同样代码实现
javascript
// 全局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();
}
👉下载用户模板
🍎导出逻辑
javascript
<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`);
}
🍎通用方法
javascript
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('下载文件出现错误,请联系管理员!');
});
};
👉导入用户
🍎导出功能
这里我们直接先把接口写上去,后面自己拼接一下就行
javascript
<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>
方法逻辑
javascript
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;
};
🍎完善成功信息提示
javascript
// 导入成功
const handleSuccess = (res,uploadFile) => {
if(res.code==200){
ElMessage.success(res.msg)
}else{
ElMessage.error(res.msg)
}
// console.log(res,uploadFile);
}
🍎完善加载效果
javascript
// 上传前文件检测
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。可以继续优化了