在写vue项目时,有个需求是点击表格中某一行的下载按钮,然后开始下载这一行对应的文件,效果如下:
表格每行的最右侧的蓝色按钮就是点击下载,这里涉及到原生的JavaScript写法,长期在写vue项目,原生的写法都很陌生了,记录一下
先上组件的原始代码:
<template>
<BreadCrumb ref="breadCrumb" :item="item">BreadCrumb>
<div class="pane-content">
<div class="pane-top">
<div class="module-common-header">
<div class="button-wrapped">
<el-upload v-model:file-list="fileList" class="upload-demo" multiple :on-exceed="handleExceed"
action="http://127.0.0.1:3088/api/files/uploadFile" :on-success="handleSuccess"
:show-file-list="false">
<el-button type="primary">上传文件el-button>
el-upload>
div>
div>
<div class="module-common-table">
<el-table :data="tableData" border style="width: 100%">
<el-table-column type="index" width="50">el-table-column>
<el-table-column show-overflow-tooltip v-for="(item, index) in tableLabel" :key="index"
:prop="item.prop" :label="item.label" />
<el-table-column fixed="right" label="操作">
<template #default="scope">
<el-button type="primary" size="small" @click="downloadFile(scope.row)">下载文件el-button>
<el-popconfirm title="确定删除该文件吗?" confirm-button-text="是" cancel-button-text="否"
@confirm="deleteFile(scope.row)">
<template #reference>
<el-button type="danger" size="small">删除文件el-button>
template>
el-popconfirm>
template>
el-table-column>
el-table>
div>
div>
<div class="table-footer">
<el-pagination :page-size="10" :pager-count="5" layout="prev, pager, next" :total="filesLength"
:current-page="paginationData.currentPage" @current-change="currentPageChange" />
div>
div>
template>
<script setup>
import { ref, onMounted } from 'vue'
import {
getFilesLengthAPI,
returnFileListDataAPI,
bindFileAndUserAPI,
deleteFileAPI,
updateDownloadTimesAPI,
} from '@/apis/files'
const item = ref({
first: '合同管理',
})
const tableData = ref([])
const tableLabel = [
{ prop: 'file_name', label: '合同名' },
{ prop: 'file_size', label: '合同文件大小' },
{ prop: 'upload_person', label: '上传人' },
{ prop: 'download_number', label: '下载次数' },
{ prop: 'upload_time', label: '上传时间' },
// { prop: 'message_content', label: '消息内容' },
]
const fileList = ref([])
// 上传成功之后的回调函数
const handleSuccess = async (response, uploadFile, uploadFiles) => {
if (response.status == 0) {
const name = JSON.parse(localStorage.user).userInfo.name
const url = response.url
const res = await bindFileAndUserAPI({ name, url })
if (res.status == 0) {
ElMessage.success('上传成功')
getCurrentPageData()
getFilesLength()
}
else ElMessage.error('上传失败')
} else {
ElMessage.error('上传失败,请检查是否重名')
}
}
// 超出文件个数限制的钩子
const handleExceed = (uploadFile, uploadFiles) => { }
// 下载文件
const downloadFile = async (row) => {
// console.log(row)
const { download_number, id } = row
await updateDownloadTimesAPI({ download_number, id })
const url = row.file_url
const link = document.createElement('a')
link.href = url
link.setAttribute('download', '')
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
getCurrentPageData()
}
// 删除文件
const deleteFile = async row => {
const res = await deleteFileAPI({ id: row.id })
if (res.status == 0) ElMessage.success('删除成功')
else ElMessage.error('删除失败')
getCurrentPageData()
getFilesLength()
}
// 分页
const paginationData = ref({
// 总页数
pageCount: 1,
// 当前页
currentPage: 1,
})
// 获取数据总数
const filesLength = ref(0)
const getFilesLength = async () => {
const res = await getFilesLengthAPI()
filesLength.value = res.filesCount
}
// 获取首页数据
const getFirstPageList = async () => {
const res = await returnFileListDataAPI({ page: 1 - 1 })
res.forEach(item => {
item.upload_time = item.upload_time?.slice(0, 19)
item.file_size = Math.floor(item.file_size) + 'kb'
})
tableData.value = res
}
// 页码切换
const currentPageChange = async (val) => {
paginationData.value.currentPage = val
const res = await returnFileListDataAPI({ page: val - 1 })
res.forEach(item => {
item.upload_time = item.upload_time?.slice(0, 19)
item.file_size = Math.floor(item.file_size) + 'kb'
})
tableData.value = res
}
// 增删数据后,需要刷新当前页数据
const getCurrentPageData = async () => {
const res = await returnFileListDataAPI({ page: paginationData.value.currentPage - 1 })
res.forEach(item => {
item.upload_time = item.upload_time?.slice(0, 19)
item.file_size = Math.floor(item.file_size) + 'kb'
})
tableData.value = res
}
onMounted(() => {
getFilesLength()
getFirstPageList()
})
script>
<style lang="scss" scoped>
.pane-content {
margin-top: 8px;
display: flex;
flex-direction: column;
justify-content: space-between;
height: calc(100vh - 118px);
background: #fff;
.pane-top {
padding: 8px;
background: #fff;
.module-common-header {
padding: 0 20px;
display: flex;
align-items: center;
justify-content: flex-end;
}
.module-common-table {
min-height: 10px;
padding: 10px 20px 20px;
margin-bottom: 8px;
background: #fff;
}
}
.table-footer {
display: flex;
justify-content: flex-end;
margin-bottom: 8px;
}
}
style>
我用的是vue3+setup语法糖写法,代码比较长,关注一下与下载相关的代码:
<el-table-column fixed="right" label="操作">
<template #default="scope">
<el-button type="primary" size="small" @click="downloadFile(scope.row)">下载文件el-button>
<el-popconfirm title="确定删除该文件吗?" confirm-button-text="是" cancel-button-text="否"
@confirm="deleteFile(scope.row)">
<template #reference>
<el-button type="danger" size="small">删除文件el-button>
template>
el-popconfirm>
template>
el-table-column>
其实就是表格最后一列,添加两个按钮,然后为这个按钮传入本行的所有数据,下载文件按钮添加点击函数downloadFile,并传入行数据作为参数
js部分是实现点击下载的核心,看downloadFile方法
// 下载文件
const downloadFile = async (row) => {
// console.log(row)
const { download_number, id } = row // 从行中获取下载文件接口的传入参数
await updateDownloadTimesAPI({ download_number, id }) // 调用下载文件的接口
const url = row.file_url // 从行数据中获取下载链接
const link = document.createElement('a') // 创建链接标签,即a标签,并将dom命名为link
link.href = url // 为dom为link的元素添加链接
link.setAttribute('download', '') // 为link设置下载属性
document.body.appendChild(link) // 把创建并配置好的link dom添加到页面文档中
link.click() // 模拟dom的点击事件
document.body.removeChild(link) // 从文档中移出link节点
getCurrentPageData() // 调用写好的方法刷新数据
}
相关的解释我都写在了上面的代码中,其中下面两个步骤不是必须的:
await updateDownloadTimesAPI({ download_number, id })
,点击下载后,要在数据库中标记,增加点击量getCurrentPageData()
,因为操作了数据库,数据要更新,所以这是为了更新数据