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

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

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

image-20231229083122632

表格每行的最右侧的蓝色按钮就是点击下载,这里涉及到原生的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语法糖写法,代码比较长,关注一下与下载相关的代码:

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方法

// 下载文件
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(),因为操作了数据库,数据要更新,所以这是为了更新数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/276814.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android集成OpenSSL实现加解密-集成

导入so 将编译生成的 OpenSSL 动态库文件&#xff08;.so 文件&#xff09;复制到你的 Android 项目的 libs 目录中 导入头文件 将编译生成的include文件夹导入到项目中 build.gradle添加配置 defaultConfig {……testInstrumentationRunner "androidx.test.runner…

Qt/C++音视频开发62-电子放大/按下选择区域放大显示/任意选取区域放大

一、前言 电子放大这个功能思考了很久&#xff0c;也是一直拖到近期才静下心来完整这个小功能&#xff0c;这个功能的前提&#xff0c;主要得益于之前把滤镜打通了&#xff0c;玩出花样来了&#xff0c;只要传入对应的滤镜字符串&#xff0c;就可以实现各种各样的效果&#xf…

ssrf之curl协议,以及查看curl支持协议的办法

1.curl支持的协议 <?php phpinfo() ?> 将这个内容写在php中 打开小皮运行后可得 用ctrlf搜索curl得 可以查出curl所支持的协议 2.用ssrf获取信息 <?php function curl($url){ $chcurl_init();//创建cURL会话 curl_setopt($ch,CURLOPT_URL,$url);//设置cURL参…

字符串转成时间的SQL,一个多种数据库通用的函数

select date 2010-10-06 from dual; date 函数&#xff0c;此函数适用于&#xff1a; 1.MySQL数据库 2.Oracle数据库 3.达梦数据库 4.人大金仓数据库

重装系统以后无法git跟踪

总结&#xff1a;权限问题 故障定位 解决方案&#xff1a; 复制一份新的文件夹。&#xff08;新建的文件创建和写入权限都变了&#xff09; 修改文件为新的用户 执行提示的命令

OpenCV-Python(21):轮廓特征及周长、面积凸包检测和形状近似

2. 轮廓特征 轮廓特征是指由轮廓形状和结构衍生出来的一些特征参数。这些特征参数可以用于图像识别、目标检测和形状分析等应用中。常见的轮廓特征包括&#xff1a; 面积&#xff1a;轮廓所包围的区域的面积。周长&#xff1a;轮廓的周长&#xff0c;即轮廓线的长度。弧长&…

idea 插件开发之 HelloWorld

前言 本文使用的 idea 2023.3 版本进行插件入门开发&#xff0c;首先要说明的是 idea 2023 版本及以后的 idea&#xff0c;对插件开发进行了一定程度的变动&#xff1a; 1、创建项目时不再支持 maven 选项 2、必须是 jdk17 及以后版本&#xff08;点击查看官网版本对应关系&…

阿里云数据库PolarDB费用价格_MySQL版_PolarDB_分布式版

阿里云数据库PolarDB租用价格表&#xff0c;云数据库PolarDB MySQL版2核4GB&#xff08;通用&#xff09;、2个节点、60 GB存储空间55元5天&#xff0c;云数据库 PolarDB 分布式版标准版2核16G&#xff08;通用&#xff09;57.6元3天&#xff0c;阿里云百科aliyunbaike.com分享…

留言板(Mybatis连接数据库版)

目录 1.添加Mybatis和SQL的依赖 2.建立数据库和需要的表 3.对应表中的字段&#xff0c;补充Java对象 4.对代码进行逻辑分层 5.后端逻辑代码 之前的项目实例【基于Spring MVC的前后端交互案例及应用分层的实现】https://blog.csdn.net/weixin_67793092/article/details/134…

Q-Tester:适用于开发、生产和售后的诊断测试软件

Q-Tester.Expert是一款基于ODX&#xff08;ASAM MCD-2D / ISO 22901-1&#xff09;国际标准的工程诊断仪。通过此诊断仪可实现与ECU控制器之间的数据交互。这一基于ODX国际标准的解决方案&#xff0c;其优势在于&#xff1a;ODX数据库不仅可在开发部门交互&#xff0c;而且可在…

力扣刷题记录(20)LeetCode:198、213、337

198. 打家劫舍 我们从第一个开始分析&#xff1a; dp[i]:i表示索引&#xff0c;dp表示当前索引可以拿到的最高金额 索引为0时&#xff0c;可以拿到的最高金额为1&#xff1b; 索引为1时&#xff0c;可以拿到的最高金额就是在索引[0,1]之间取&#xff0c;为2 索引为2时&…

网络通信连接器

网络通信连接器 常用电子元器件类型 19S101-40ML5 射频连接器 SMD贴片 RF同轴连接器 \GS72V3SO-R SOP8 GS72V3 文章目录 网络通信连接器前言一、网络通信连接器二、19S101-40ML5 射频连接器 SMD贴片 RF同轴连接器三、GS72V3SO-R SOP8 GS72V3总结前言 网络通信连接器是用于在…

ChatGPT在地学、GIS、气象、农业、生态、环境等领域中的高级应用

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮&#xff0c;可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…

GPT分区格式

GPT分区格式 [rootlocalhost ~]# gdisk /dev/sdb -bash: gdisk: 未找到命令 [rootlocalhost ~]# yum -y install gdisk- gdisk命令用于查看磁盘使用情况和磁盘分区&#xff08;GPT分区格式&#xff09; - 命令格式&#xff1a;gdisk [选项...] [设备路径] - 常用选项&…

算法练习Day23 (Leetcode/Python-回溯算法)

46. Permutations Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order. Example 1: Input: nums [1,2,3] Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]思路&#xff1a;此题可用回溯…

作为铭文跨链赛道龙头,SoBit 有何突出之处?

跨链桥赛道将是铭文市场长期的发展的刚需 在比特币网络中&#xff0c;Ordinals 铭文铸造的铭文总量已经超过了 5100 万枚&#xff0c;并累计费用收入超 5028 BTC。同时&#xff0c;仅 BRC-20 叙事方向的市值&#xff0c;就已经超过了 30 亿美元&#xff0c;并且随着铭文资产种类…

Apache DolphinScheduler 3.1.9 版本发布:提升系统的稳定性和性能

&#x1f680;我们很高兴宣布&#xff0c;Apache DolphinScheduler 的最新版本 3.1.9 已正式发布&#xff01;此版本在 3.1.8 的基础上进行了关键的 bug 修复和文档更新&#xff0c;共计修复了 14 个 bug 和改进了 3 个文档。 主要更新亮点 本次更新重点解决了以下几个关键问题…

3D游戏角色建模纹理贴图处理

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 在本文中&#xff0c;我们将介绍 3D 纹理的基础知识&#xff0c;并讨…

15-网络安全框架及模型-BLP机密性模型

目录 BLP机密性模型 1 背景概述 2 模型原理 3 主要特性 4 优势和局限性 5 困难和挑战 6 应用场景 7 应用案例 BLP机密性模型 1 背景概述 BLP模型&#xff0c;全称为Bell-LaPadula模型&#xff0c;是在1973年由D.Bell和J.LaPadula在《Mathematical foundations and mod…

web渗透安全学习笔记:1、入门基础知识/ XXS漏洞

前言 自编写python渗透工具编写学习笔记专栏以来&#xff0c;笔者便发现了一个较为严重的问题&#xff1a;我们大多数文章都是学习如何用python编写扫描与利用漏洞的渗透工具&#xff0c;却没有真正解析漏洞的形成原因&#xff0c;长此以往我们的学习就只会浮于表面&#xff0c…