vue3中,下载模板并进行上传导入文件 & 父子组件props传函数两种写法-传动态接口
效果
接口返回模板
传参
返回
代码
1、页面文件
index.vue
<template>
<div>
<el-button type="primary" @click="() => (isShowImport = true)">
导入
</el-button>
<!-- 导入 -->
<importPopup
v-model:showDialog="isShowImport"
:download-fun="downLoadPlanEvent"
:import-fun="planImport"
@import-success="onLoad"
/>
<el-pagination
background
:current-page="page.currentPage"
layout="total, sizes, prev, pager, next, jumper"
:page-size="page.pageSize"
:page-sizes="[10, 20, 30, 40, 50, 100]"
:total="page.total"
@current-change="hanleCurrentChange"
@size-change="handleSizeChange"
/>
</div>
</template>
<script setup>
import {
downloadPlanList,
planImport,
queryByPagePlanList,
} from "@/api/acceptManage/index";
import { downLoadxls } from "@src/utils/util";
import importPopup from "@src/components/importPopup";
const page = reactive({
pageSize: 10,
currentPage: 1,
total: 0,
});
const tableData = ref([]);
const isShowImport = ref(false);
const downLoadPlanEvent = () => {
downloadPlanList({}).then((res) => {
downLoadxls(res, "结算清单下载");
});
};
const hanleCurrentChange = (val) => {
page.currentPage = val;
onLoad();
};
const handleSizeChange = (val) => {
page.pageSize = val;
onLoad();
};
const onLoad = async () => {
const formData = {
pageSize: page.pageSize,
pageNumber: page.currentPage,
...formInline.value,
startDate: prjDate.value ? prjDate.value[0] : "",
endDate: prjDate.value ? prjDate.value[1] : "",
};
await queryByPagePlanList(formData).then((res) => {
const { code, data, message } = res;
if (code === "00000") {
data.data.forEach((item) => {
item.processState === ""
? (item.processState = "无")
: item.processState;
});
tableData.value = data.data;
page.total = Number(data.total);
} else {
ElMessage.error(message);
}
});
};
</script>
2、封装组件文件
两种写法——fun1.value()和fun2.value(param).then(res)
src\components\importPopup.vue
<!--
showDialog //弹窗是否显示
downloadFun //下载模板方法
importFun //导入方法
import-success //导入成功点击确定回调方法
-->
<template>
<div class="item-content">
<el-dialog
v-model="showDialog"
:close-on-click-modal="true"
:destroy-on-close="true"
:show-close="true"
title="导入"
width="35%"
@close="handleCancel"
>
<el-upload
ref="uploadRef"
action="#"
class="upload-demo"
drag
:file-list="fileList"
:http-request="uploadFiles"
:limit="1"
:multiple="false"
:show-file-list="true"
>
<!-- :on-change="handleChange"
:on-remove="handleRemove" -->
<i class="el-icon-upload"></i>
<div class="el-upload__text">
<em>选择文件</em>
或将文件拖拽至此区域
</div>
<div class="el-upload__tip">支持格式:xls、xlsx</div>
</el-upload>
<el-button type="text" @click="handleDownLoadTemplate">
下载导入模板
</el-button>
<div class="upload-footer">
<el-button type="primary" @click="handleSubmit">确 定</el-button>
<el-button plain type="primary" @click="handleCancel">取 消</el-button>
</div>
</el-dialog>
<!-- 导入失败 -->
<el-dialog
v-model="isShowFail"
class="fail-dialog"
:close-on-click-modal="true"
:destroy-on-close="true"
:show-close="true"
title="导入失败"
width="35%"
@close="handleClose"
>
<h4 class="fail-title">
导入失败,请求改后重新上传,错误数据{{
errorListData.length
}}条,失败原因如下:
</h4>
<el-table :data="errorListData">
<el-table-column label="行数" prop="index" />
<el-table-column label="错误原因" prop="msg" />
</el-table>
</el-dialog>
</div>
</template>
<script setup>
const props = defineProps({
showDialog: {
type: Boolean,
default: false,
},
downloadFun: {
default: null,
type: Function,
},
importFun: {
default: null,
type: Function,
},
})
const { downloadFun, importFun } = toRefs(props)
const emit = defineEmits(['update:showDialog', 'import-success'])
const showDialog = computed({
get: () => props.showDialog,
set: (val) => emit('update:showDialog', val),
})
const fileList = ref([])
const errorListData = ref([])
const isShowFail = ref(false)
const successListData = ref()
//下载导入模板
const handleDownLoadTemplate = () => {
downloadFun.value() // 传函数写法一
}
//提交
const uploadFiles = (options) => {
const formData = new FormData()
formData.append('file', options.file)
//传函数写法二
importFun
.value(formData)
.then((res) => {
const { data, code } = res
if (code === '00000') {
successListData.value = data
}
})
.catch((res) => {
const { data, code } = res
if (code !== '00000') {
const { errorList } = data
const errs = errorList.map((item) => {
return {
index: `第${item.row}行`,
msg: item.message,
}
})
errorListData.value = errs
fileList.value = []
isShowFail.value = true
}
})
}
const handleSubmit = () => {
if (errorListData.value.length > 0) {
isShowFail.value = true
} else {
isShowFail.value = false
ElMessage.success('导入成功')
emit('update:showDialog', false)
emit('import-success', successListData.value)
}
}
// 导入弹窗取消
const handleCancel = () => {
emit('update:showDialog', false)
}
// 导入失败弹窗关闭
const handleClose = () => {
isShowFail.value = false
errorListData.value = []
}
</script>
<style lang="scss" scoped>
.fail-dialog {
.fail-title {
margin: 0px 0 10px;
}
:deep() {
.ym-dialog__body {
padding: 10px 20px 20px;
}
}
}
.upload-footer {
display: flex;
justify-content: center;
}
</style>
3、接口文件
src\app\science\api\acceptManage\index.js
import request from '@src/utils/request'
import { sciencePostUrl } from '@/config'
//年度验收计划列表
export const queryByPagePlanList = (data) => {
return request({
url: `${sciencePostUrl}/checkPlan/getPlanPage`,
method: 'post',
data,
})
}
//年度验收计划-结算清单下载
export const downloadPlanList = (data) => {
return request({
url: `${sciencePostUrl}/checkPlan/exportQuery`,
responseType: 'blob',
headers: {
'Content-Type': 'application/json',
},
method: 'post',
data,
})
}
//年度验收计划导入
export const planImport = (data) => {
return request({
url: `${sciencePostUrl}/checkPlan/importPlan`,
method: 'post',
data,
})
}
src\app\science\config\index.js
/**
* @description 4个子配置,vue/cli配置|通用配置|主题配置|网络配置导出
* config中的部分配置由vue.config.js读取,本质是node,故不可使用window等浏览器对象
*/
const cli = require('./cli.config')
const prefixApi = require('./prefixApi.config')
module.exports = {
...cli,
...prefixApi,
}
src\app\science\config\prefixApi.config.js
module.exports = {
planPostUrl: '/srbm-prj-plan-front/member',
sciencePostUrl: '/srbm-prj-techprj-front/member',
}
4、方法文件-本地返回模板
src\utils\util.js
//文件流导出数据处理
export const downLoadxls = (res, fileName) => {
let name = fileName
if (res.headers['content-disposition']) {
const contentDisposition = res.headers['content-disposition'].split('=')
name = (contentDisposition && decodeURI(contentDisposition[1])) || ''
}
const file = new File([res.data], name, res.data)
const href = URL.createObjectURL(file)
const aTag = document.createElement('a')
aTag.download = file.name
aTag.target = '_blank'
aTag.href = href
aTag.click()
URL.revokeObjectURL(href)
}
//本地文件下载 下载的模板文件放在public下的file文件夹中
export const downFile = (fileName) => {
const anchor = document.createElement('a')
anchor.href = `${process.env.BASE_URL}static/file/${fileName}`
anchor.setAttribute('download', fileName)
anchor.innerHTML = 'downloading...'
anchor.style.display = 'none'
document.body.appendChild(anchor)
setTimeout(() => {
anchor.click()
document.body.removeChild(anchor)
setTimeout(() => {
self.URL.revokeObjectURL(anchor.href)
}, 250)
}, 66)
}
//本地文件下载-自定义文件名
export const downFileNew = (fileName, name) => {
const anchor = document.createElement('a')
anchor.href = `${process.env.BASE_URL}static/file/${fileName}`
anchor.download = name
// anchor.setAttribute('download', name)
anchor.innerHTML = 'downloading...'
anchor.style.display = 'none'
console.log('anchor', anchor)
document.body.appendChild(anchor)
setTimeout(() => {
anchor.click()
document.body.removeChild(anchor)
setTimeout(() => {
console.log(123, anchor)
self.URL.revokeObjectURL(anchor.href)
}, 250)
}, 66)
}