vue3-导入导出文件和打印文件
📅 2026/7/15 7:08:48
👁️ 阅读次数
📝 编程学习
调用api下载excel文件
export const exportShopTarget = (参数) => request.get(`api链接?查询参数`, { Body请求参数 }, { responseType: 'blob' } )一定要加上responseType: 'blob'这个参数,否则会显示文件损坏
下载单个文件:
const exportData = async () => { try { const response = await exportShopTarget(参数) const url = window.URL.createObjectURL(new Blob([response.data])) const link = document.createElement('a') link.href = url link.setAttribute( 'download', `目标数据.xlsx` ) // 设置下载文件的名称 document.body.appendChild(link) link.click() // 清理:释放内存中的URL对象 document.body.removeChild(link) window.URL.revokeObjectURL(url) } catch (error) { ElMessage({ message: `导出失败`, type: 'error' }) } }批量下载压缩文件:
把selectedFileList数组使用encodeURIComponent编码传递
const encodedElements = selectedFileList.value.map((item) => '"' + item + '"') const queryString = encodedElements.join(',') const codeListEncoded = encodeURIComponent('[' + queryString + ']') const response = await downloadFile(codeListEncoded) if (response.status === 200) { const url = window.URL.createObjectURL(new Blob([response.data])) const link = document.createElement('a') link.href = url link.setAttribute('download', '批量下载.zip') document.body.appendChild(link) link.click() document.body.removeChild(link) loading.value = false } else { ElMessage({ message: `文件下载失败,${response.data.msg}`, type: 'error' }) }读取excel文件里的数据转化为json数组
首先安装xlsx
pnpm install xlsximport * as XLSX from 'xlsx' const file = fileList.value[0] const reader = new FileReader() reader.onload = async (e) => { const data = new Uint8Array(e.target.result) const workbook = XLSX.read(data, { type: 'array' }) const sheetName = workbook.SheetNames[0] // 获取第一个工作表的名称 const worksheet = workbook.Sheets[sheetName] const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 }) // header: 1 表示第一行作为头部信息 console.log(jsonData.splice(1))//获取除去表头的json数据 } reader.readAsArrayBuffer(file)下载静态excel文件模板
把文件放置在public文件里面的static文件里,文件名要用英文
<el-button type="primary" color="#A88D5B"> <a href="/static/importTemplate.xlsx" download="店铺目标导入模板" style="color: white; text-decoration: none" >下载导入模板 </a> </el-button>调用api上传文件
import request from '@/utils/request' //文件上传接口 export const fileUpload = (formData) => request.post('接口', formData, { headers: { 'Content-Type': 'multipart/form-data' // 设置 Content-Type 为 multipart/form-data } })import { fileUpload } from '@/api/file.js' import { ref } from 'vue' const selectedFileList = ref({ fileList: [], codeList: [] }) const uploadFile = (e, code) => { let file = e.target.files[0] // 获取选中的文件 if (file) { selectedFileList.value.fileList.push(file)//文件列表 selectedFileList.value.codeList.push(code)//文件对应的code列表 } e.target.value = '' // 允许重复上传同一个文件 } const submit = () => { if (selectedFileList.value.fileList.length === 0) { ElMessage({ message: '请至少勾选一个要上传的文件', type: 'warning' }) return } ElMessageBox.confirm( `确认上传已勾选的${selectedFileList.value.fileList.length}个文件?`, '温馨提示', { type: 'info', confirmButtonText: '确认', cancelButtonText: '取消', confirmButtonClass: 'ExitConfirmButton', cancelButtonClass: 'ExitCancelButton' } ) .then(async () => { let formData = new FormData() selectedFileList.value.fileList.forEach((fileObj, index) => { formData.append(`files[${index}]`, fileObj) }) selectedFileList.value.codeList.forEach((code, index) => { formData.append(`fileCodes[${index}]`, code) }) const response = await fileUpload(formData) if (response.data.code === 0) { ElMessage({ message: '文件上传成功', type: 'success' }) filterFileList() selectedFileList.value = { fileList: [], codeList: [] } } else { ElMessage({ message: `文件上传失败,${response.data.msg}`, type: 'error' }) } }) .catch(() => { //取消后执行 }) }<input type="file" @change="(e) => uploadFile(e,文件code)" />使用formData上传文件的注意点:
1.api接口里要加headers: {
'Content-Type': 'multipart/form-data'
}
2.formData就类似于一个对象,使用api传递时外面就不要包{}了,就变成json类型了
3.formData.append(参数名, 值):参数名要和后端对应上
4.查看是否携带内容,标头有内容长度
打印文件
const printTotal = async () => { const content = printFrame.value.contentWindow.document content.open() content.write(document.getElementById('salesTotal').innerHTML) // 获取并写入内容 content.close() printFrame.value.contentWindow.print() }<ticketTotal style="zoom: 1.5" id="salesTotal" />可以打印某个组件里的内容,而不打印全部页面,而且也不影响其他的运行
zoom: 1.5的作用是放大/缩小组件的显示
或者写成:
const getPrintHtml = (elementId) => { const el = document.getElementById(elementId) if (!el) return '' const clone = el.cloneNode(true) clone.removeAttribute('id') clone.style.zoom = '1' clone.style.marginTop = '0' return clone.outerHTML } const printDocument = (elementId) => { const html = getPrintHtml(elementId) if (!html) return const doc = printFrame.value.contentWindow.document doc.open() doc.write(`<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> @page { size: 80mm auto; margin: 0; } html, body { margin: 0; padding: 0; width: auto; height: auto; } @media print { html, body { height: auto; overflow: visible; } } </style> </head> <body>${html}</body> </html>`) doc.close() printFrame.value.contentWindow.print() } const printTotal = () => printDocument('salesTotal')style设置的解析:
@page { size: 80mm auto; margin: 0; }— 按热敏小票宽度(约 80mm,可根据打印机实际宽度来调整),高度随内容自动伸缩;设置margin: 0会去掉勾选系统自带的页眉和页脚功能margin: 0; padding: 0
去掉浏览器默认边距,避免四周多出空白。width/height: auto
让页面宽高跟小票内容走,而不是固定成一整页屏幕高度。@media print里的height: auto+overflow: visible
只在真正打印时生效:高度仍随内容伸缩,内容不被裁切,方便配合上面的@page { size: 80mm auto }做「纸宽固定、纸高自适应」。
编程学习
技术分享
实战经验