VUE3实现点击按钮下载文件功能

VUE3实现点击按钮下载文件功能

在写vue项目时,有个需求是点击表格中某一行的下载按钮,然后开始下载这一行对应的文件,效果如下:

表格每行的最右侧的蓝色按钮就是点击下载,这里涉及到原生的JavaScript写法,长期在写vue项目,原生的写法都很陌生了,记录一下

先上组件的原始代码:

html 复制代码
<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语法糖写法,代码比较长,关注一下与下载相关的代码:

html部分

html 复制代码
<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,并传入行数据作为参数

JavaScript部分

js部分是实现点击下载的核心,看downloadFile方法

javascript 复制代码
// 下载文件
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()									// 调用写好的方法刷新数据
}

相关的解释我都写在了上面的代码中,其中下面两个步骤不是必须的:

  1. await updateDownloadTimesAPI({ download_number, id }) ,点击下载后,要在数据库中标记,增加点击量
  2. getCurrentPageData(),因为操作了数据库,数据要更新,所以这是为了更新数据
相关推荐
阿雄不会写代码1 天前
Let‘s Encrypt HTTPS 证书配置指南
前端·chrome
每天吃饭的羊1 天前
hash结构
开发语言·前端·javascript
吃吃喝喝小朋友1 天前
JavaScript异步编程
前端·javascript
Trae1ounG1 天前
Vue生命周期
前端·javascript·vue.js
—Qeyser1 天前
Flutter Text 文本组件完全指南
开发语言·javascript·flutter
程序员小李白1 天前
js数据类型详细解析
前端·javascript·vue.js
weixin_462446231 天前
Python用Flask后端解析Excel图表,Vue3+ECharts前端动态还原(附全套代码)
前端·python·flask·echats
Kratzdisteln1 天前
【1902】0120-3 Dify变量引用只能引用一层
android·java·javascript
满栀5851 天前
jQuery 递归渲染多级树形菜单
前端·javascript·jquery