• SpringBoot和Vue实现文件上传与下载——基于SpringBoot和Vue的后台管理系统项目系列博客(十五)


    系列文章目录

    1. 系统功能演示——基于SpringBoot和Vue的后台管理系统项目系列博客(一)
    2. Vue2安装并集成ElementUI——基于SpringBoot和Vue的后台管理系统项目系列博客(二)
    3. Vue2前端主体框架搭建——基于SpringBoot和Vue的后台管理系统项目系列博客(三)
    4. SpringBoot后端初始框架搭建——基于SpringBoot和Vue的后台管理系统项目系列博客(四)
    5. SpringBoot集成Mybatis——基于SpringBoot和Vue的后台管理系统项目系列博客(五)
    6. SpringBoot实现增删改查——基于SpringBoot和Vue的后台管理系统项目系列博客(六)
    7. SpringBoot实现分页查询——基于SpringBoot和Vue的后台管理系统项目系列博客(七)
    8. SpringBoot实现集成Mybatis-Plus和SwaggerUI——基于SpringBoot和Vue的后台管理系统项目系列博客(八)
    9. Vue实现增删改查——基于SpringBoot和Vue的后台管理系统项目系列博客(九)
    10. SpringBoot实现代码生成器——基于SpringBoot和Vue的后台管理系统项目系列博客(十)
    11. Vue使用路由——基于SpringBoot和Vue的后台管理系统项目系列博客(十一)
    12. SpringBoot和Vue实现导入导出——基于SpringBoot和Vue的后台管理系统项目系列博客(十二)
    13. SpringBoot和Vue实现用户登录注册与异常处理——基于SpringBoot和Vue的后台管理系统项目系列博客(十三)
    14. SpringBoot和Vue实现用户个人信息展示与保存与集成JWT——基于SpringBoot和Vue的后台管理系统项目系列博客(十四)
    15. SpringBoot和Vue实现文件上传与下载——基于SpringBoot和Vue的后台管理系统项目系列博客(十五)
    16. SpringBoot和Vue整合ECharts——基于SpringBoot和Vue的后台管理系统项目系列博客(十六)
    17. SpringBoot和Vue实现权限菜单功能——基于SpringBoot和Vue的后台管理系统项目系列博客(十七)
    18. SpringBoot实现1对1、1对多、多对多关联查询——基于SpringBoot和Vue的后台管理系统项目系列博客(十八)
    19. 用户前台页面设计与实现——基于SpringBoot和Vue的后台管理系统项目系列博客(十九)
    20. SpringBoot集成Redis实现缓存——基于SpringBoot和Vue的后台管理系统项目系列博客(二十)
    21. SpringBoot和Vue集成高德地图——基于SpringBoot和Vue的后台管理系统项目系列博客(二十一)
    22. SpringBoot和Vue集成视频播放组件——基于SpringBoot和Vue的后台管理系统项目系列博客(二十二)
    23. SpringBoot和Vue集成Markdown和多级评论——基于SpringBoot和Vue的后台管理系统项目系列博客(二十三)

    项目资源下载

    1. GitHub下载地址
    2. Gitee下载地址
    3. 项目MySql数据库文件


    前言

      今天的主要内容包括:实现文件上传与下载功能准备工作、文件上传接口的实现、文件下载接口的实现、使用md5校验唯一文件、文件上传和下载的后端功能实现、文件上传和下载的前端功能实现、用户上传头像功能实现等。今天的内容比较多,也有一定难度,但是只要一步一步跟着我做,就不会有太大问题。下面就开始今天的学习!


    一、实现文件上传与下载功能准备工作

    1. 为了保存文件,按照如下配置新建表。其中id为主键且自动递增。is_delete默认为0表示未删除,如果为1表示已删除。enable默认为1表示可用,如果为0表示不可用
      在这里插入图片描述
    2. 表名为sys_file
      在这里插入图片描述
    3. 在application.yml中配置一下文件保存路径,代码运行时会自动创建文件夹。读者需要注意改成自己对应的目录
      在这里插入图片描述

    二、文件上传接口的实现

    1. 设置拦截器将file请求都放行,方便后续测试
      在这里插入图片描述
    2. 然后在entity中新建Files.java实体类
      在这里插入图片描述
    3. 在Files.java实体类中输入如下内容
      在这里插入图片描述
    4. 然后在mapper中新建FileMapper.java接口
      在这里插入图片描述
    5. 在FileMapper.java接口中输入如下内容
      在这里插入图片描述
    6. 在此目录下新建FileController.java
      在这里插入图片描述
    7. 首先在FileController.java中加入如下注解,注明文件上传接口的路由(请求地址)
      在这里插入图片描述
    8. 然后在FileController.java中引用如下内容包括刚才的接口和文件存储地址
      在这里插入图片描述
    9. 然后在FileController.java加入如下函数接口,功能是实现文件上传
    /**
     * 文件上传接口
     *
     * @param file 前端传递过来的文件
     * @return 返回文件url
     * @throws IOException 异常处理
     */
    @PostMapping("/upload")
    public String upload(@RequestParam MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();
        String type = FileUtil.extName(originalFilename);
        long size = file.getSize();
        // 存储到磁盘
        File uploadParentFile = new File(fileUploadPath);
        // 判断配置的文件目录是否存在,若不存在则创建一个新的文件目录
        if (!uploadParentFile.exists()) {
            uploadParentFile.mkdirs();
        }
        // 定义一个文件唯一的标识码
        String uuid = IdUtil.fastSimpleUUID();
        String fileUUID = uuid + StrUtil.DOT + type;
        File uploadFile = new File(fileUploadPath + fileUUID);
        // 把获取到的文件存储到磁盘目录
        file.transferTo(uploadFile);
        String url = "http://localhost:9090/file/" + fileUUID;
        // 存储到数据库
        Files saveFile = new Files();
        saveFile.setName(originalFilename);
        saveFile.setType(type);
        saveFile.setSize(size / 1024);
        saveFile.setUrl(url);
        fileMapper.insert(saveFile);
        return url;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    1. 然后使用Postman进行测试
      在这里插入图片描述
    2. 发现文件已经成功上传到后端服务器
      在这里插入图片描述
    3. 并且数据库中已经存在此文件数据
      在这里插入图片描述
    4. 为了存储到数据库中的文件size为kb表示,在FileController.java的文件上传函数接口中的size除以1024再保存
      在这里插入图片描述
    5. 再测试一下发现已经是用kb表示了
      在这里插入图片描述

    三、文件下载接口的实现

    1. 在 FileController.java中加入如下函数接口,目的是实现文件下载功能
    /**
     * 文件下载接口
     *
     * @param fileUUID 文件唯一标识符
     * @param response 请求响应
     * @throws IOException 异常处理
     */
    @GetMapping("/{fileUUID}")
    public void download(@PathVariable String fileUUID, HttpServletResponse response) throws IOException {
        // 根据文件的唯一标识码获取文件
        File uploadFile = new File(fileUploadPath + fileUUID);
        // 设置输出流的格式
        ServletOutputStream os = response.getOutputStream();
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileUUID, "UTF-8"));
        response.setContentType("application/octet-stream");
        // 读取文件的字节流
        os.write(FileUtil.readBytes(uploadFile));
        os.flush();
        os.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    1. 然后我们将刚刚文件上传返回的文件url复制到浏览器请求
      在这里插入图片描述
    2. 然后发现可以实现文件的下载功能
      在这里插入图片描述

    四、使用md5校验唯一文件

    1. 在数据库中的sys_file表中加入如下字段,按照我的配置就行
      在这里插入图片描述
    2. 在Files.java中加入md5属性
      在这里插入图片描述
    3. 然后在FileController.java中加入如下代码获取数据库中的md5
      在这里插入图片描述
    4. 然后将FileController.java中的文件上传函数修改为如下内容
    /**
     * 文件上传接口
     *
     * @param file 前端传递过来的文件
     * @return 返回文件url
     * @throws IOException 异常处理
     */
    @PostMapping("/upload")
    public String upload(@RequestParam MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();
        String type = FileUtil.extName(originalFilename);
        long size = file.getSize();
        // 定义一个文件唯一的标识码
        String uuid = IdUtil.fastSimpleUUID();
        String fileUUID = uuid + StrUtil.DOT + type;
        File uploadFile = new File(fileUploadPath + fileUUID);
        // 判断配置的文件目录是否存在,若不存在则创建一个新的文件目录
        File parentFile = uploadFile.getParentFile();
        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }
        String url;
        // 获取文件的md5,通过对比md5避免重复上传相同内容的文件
        String md5 = SecureUtil.md5(file.getInputStream());
        // 从数据库查询是否存在相同的记录
        Files dbFiles = getFileByMd5(md5);
        if (dbFiles != null) { // 文件已存在
            url = dbFiles.getUrl();
        } else {
            // 上传文件到磁盘
            file.transferTo(uploadFile);
            // 数据库若不存在重复文件,则不删除刚才上传的文件
            url = "http://localhost:9090/file/" + fileUUID;
        }
        // 存储数据库
        Files saveFile = new Files();
        saveFile.setName(originalFilename);
        saveFile.setType(type);
        saveFile.setSize(size / 1024);
        saveFile.setUrl(url);
        saveFile.setMd5(md5);
        fileMapper.insert(saveFile);
        return url;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    1. 然后我们使用Postman测试一下,分两次上传,上传同一个文件,但是不同名字
      在这里插入图片描述
    2. 然后我们发现对于相同文件只上传了一个,并且数据库中有两个数据,md5值一样,但是文件名字不一样,这样就实现了我们的功能
      在这里插入图片描述

    五、文件上传和下载的后端功能实现

    1. 在FileController.java中加入分页查询接口
      在这里插入图片描述
    2. 在FileController.java中加入更新文件信息接口
      在这里插入图片描述
    3. 在FileController.java中加入按照文件id删除接口
      在这里插入图片描述
    4. 在FileController.java中加入按照id批量删除文件删除接口
      在这里插入图片描述

    六、文件上传和下载的前端功能实现

    1. 在views中新建File.vue
      在这里插入图片描述
    2. 在File.vue中加入如下代码
    <template>
        <div>
            <div style="margin: 10px 0">
                <el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search" v-model="name">el-input>
                <el-button class="ml-5" type="primary" @click="load">搜索el-button>
                <el-button type="warning" @click="reset">重置el-button>
            div>
            <div style="margin: 10px 0">
                <el-upload action="http://localhost:9090/file/upload" :show-file-list="false" :on-success="handleFileUploadSuccess" style="display: inline-block">
                    <el-button type="primary" class="ml-5">上传文件 <i class="el-icon-top">i>el-button>
                el-upload>
                <el-popconfirm
                        class="ml-5"
                        confirm-button-text='确定'
                        cancel-button-text='我再想想'
                        icon="el-icon-info"
                        icon-color="red"
                        title="您确定批量删除这些数据吗?"
                        @confirm="delBatch"
                >
                    <el-button type="danger" slot="reference">批量删除 <i class="el-icon-remove-outline">i>el-button>
                el-popconfirm>
    
            div>
            <el-table :data="tableData" border stripe :header-cell-class-name="'headerBg'"  @selection-change="handleSelectionChange">
                <el-table-column type="selection" width="55">el-table-column>
                <el-table-column prop="id" label="ID" width="80">el-table-column>
                <el-table-column prop="name" label="文件名称">el-table-column>
                <el-table-column prop="type" label="文件类型">el-table-column>
                <el-table-column prop="size" label="文件大小(kb)">el-table-column>
                <el-table-column label="下载">
                    <template slot-scope="scope">
                        <el-button type="primary" @click="download(scope.row.url)">下载el-button>
                    template>
                el-table-column>
                <el-table-column label="启用">
                    <template slot-scope="scope">
                        <el-switch v-model="scope.row.enable" active-color="#13ce66" inactive-color="#ccc" @change="changeEnable(scope.row)">el-switch>
                    template>
                el-table-column>
                <el-table-column label="操作"  width="200" align="center">
                    <template slot-scope="scope">
                        <el-popconfirm
                                class="ml-5"
                                confirm-button-text='确定'
                                cancel-button-text='我再想想'
                                icon="el-icon-info"
                                icon-color="red"
                                title="您确定删除吗?"
                                @confirm="del(scope.row.id)">
                            <el-button type="danger" slot="reference">删除 <i class="el-icon-remove-outline">i>el-button>
                        el-popconfirm>
                    template>
                el-table-column>
            el-table>
    
            <div style="padding: 10px 0">
                <el-pagination
                        @size-change="handleSizeChange"
                        @current-change="handleCurrentChange"
                        :current-page="pageNum"
                        :page-sizes="[2, 5, 10, 20]"
                        :page-size="pageSize"
                        layout="total, sizes, prev, pager, next, jumper"
                        :total="total">
                el-pagination>
            div>
    
        div>
    template>
    
    <script>
        export default {
            name: "File",
            data() {
                return {
                    tableData: [],
                    name: '',
                    multipleSelection: [],
                    pageNum: 1,
                    pageSize: 10,
                    total: 0
                }
            },
            created() {
                this.load()
            },
            methods: {
                load() {
                    this.request.get("/file/page", {
                        params: {
                            pageNum: this.pageNum,
                            pageSize: this.pageSize,
                            name: this.name,
                        }
                    }).then(res => {
                        this.tableData = res.data.records
                        this.total = res.data.total
                    })
                },
                changeEnable(row) {
                    this.request.post("/file/update", row).then(res => {
                        if (res.code === '200') {
                            this.$message.success("操作成功")
                        }
                    })
                },
                del(id) {
                    this.request.delete("/file/" + id).then(res => {
                        if (res.code === '200') {
                            this.$message.success("删除成功")
                            this.load()
                        } else {
                            this.$message.error("删除失败")
                        }
                    })
                },
                handleSelectionChange(val) {
                    console.log(val)
                    this.multipleSelection = val
                },
                delBatch() {
                    let ids = this.multipleSelection.map(v => v.id)  // [{}, {}, {}] => [1,2,3]
                    this.request.post("/file/del/batch", ids).then(res => {
                        if (res.code === '200') {
                            this.$message.success("批量删除成功")
                            this.load()
                        } else {
                            this.$message.error("批量删除失败")
                        }
                    })
                },
                reset() {
                    this.name = ""
                    this.load()
                },
                handleSizeChange(pageSize) {
                    console.log(pageSize)
                    this.pageSize = pageSize
                    this.load()
                },
                handleCurrentChange(pageNum) {
                    console.log(pageNum)
                    this.pageNum = pageNum
                    this.load()
                },
                handleFileUploadSuccess(res) {
                    console.log(res)
                    this.load()
                },
                download(url) {
                    window.open(url)
                }
            }
        }
    script>
    
    <style scoped>
    
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    1. 在index.js中加入文件管理界面的路由信息
      在这里插入图片描述
    2. 在Aside.vue中加入文件管理界面的侧边栏信息
      在这里插入图片描述
    3. 然后测试一下相关功能:搜索、重置、上传文件、批量删除、下载、删除、启用、翻页。发现所有功能都可以正常使用,也就是说我们的文件管理模块已经完成了
      在这里插入图片描述

    七、用户上传头像功能实现

    1. 首先将Person.vue中的内容全部修改为如下内容
    <template>
        <el-card style="width: 500px;">
            <el-form label-width="80px" size="small">
                <el-upload
                        class="avatar-uploader"
                        :action="'http://localhost:9090/file/upload'"
                        :show-file-list="false"
                        :on-success="handleAvatarSuccess"
                >
                    <img v-if="form.avatar" :src="form.avatar" class="avatar">
                    <i v-else class="el-icon-plus avatar-uploader-icon">i>
                el-upload>
                <el-form-item label="用户名">
                    <el-input v-model="form.username" disabled autocomplete="off">el-input>
                el-form-item>
                <el-form-item label="昵称">
                    <el-input v-model="form.nickname" autocomplete="off">el-input>
                el-form-item>
                <el-form-item label="邮箱">
                    <el-input v-model="form.email" autocomplete="off">el-input>
                el-form-item>
                <el-form-item label="电话">
                    <el-input v-model="form.phone" autocomplete="off">el-input>
                el-form-item>
                <el-form-item label="地址">
                    <el-input v-model="form.address" autocomplete="off">el-input>
                el-form-item>
                <el-form-item label="头像链接">
                    <el-input type="textarea" v-model="form.avatar" autocomplete="off">el-input>
                el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="save">保 存el-button>
                el-form-item>
            el-form>
        el-card>
    template>
    
    <script>
        export default {
            name: "Person",
            data() {
                return {
                    form: {},
                    user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
                }
            },
            created() {
                this.getUser().then(res => {
                    console.log(res)
                    this.form = res
                })
            },
            methods: {
                async getUser() {
                    return (await this.request.get("/user/username/" + this.user.username)).data
                },
                save() {
                    this.request.post("/user", this.form).then(res => {
                        if (res) {
                            this.$message.success("保存成功")
                            // 触发父级更新User的方法
                            this.$emit("refreshUser")
                            // 更新浏览器存储的用户信息
                            this.getUser().then(res => {
                                res.token = JSON.parse(localStorage.getItem("user")).token
                                localStorage.setItem("user", JSON.stringify(res))
                            })
                        } else {
                            this.$message.error("保存失败")
                        }
                    })
                },
                handleAvatarSuccess(res) {
                    this.form.avatar = res
                }
            }
        }
    script>
    
    <style scoped>
        .avatar-uploader {
            text-align: center;
            padding-bottom: 10px;
        }
    
        .avatar-uploader .el-upload {
            border: 1px dashed #d9d9d9;
            border-radius: 6px;
            cursor: pointer;
            position: relative;
            overflow: hidden;
        }
    
        .avatar-uploader .el-upload:hover {
            border-color: #409EFF;
        }
    
        .avatar-uploader-icon {
            font-size: 28px;
            color: #8c939d;
            width: 138px;
            height: 138px;
            line-height: 138px;
            text-align: center;
        }
    
        .avatar {
            width: 138px;
            height: 138px;
            display: block;
        }
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    1. 然后将Manage.vue中的内容全部修改为如下内容
    <template>
        <el-container style="min-height: 100vh">
    
            <el-aside :width="sideWidth + 'px'" style="box-shadow: 2px 0 6px rgb(0 21 41 / 35%);">
                <Aside :isCollapse="isCollapse" :logoTextShow="logoTextShow"/>
            el-aside>
    
            <el-container>
                <el-header style="border-bottom: 1px solid #ccc;">
                    <Header :collapseBtnClass="collapseBtnClass" @asideCollapse="collapse" :user="user"/>
                el-header>
    
                <el-main>
                    
                    <router-view @refreshUser="getUser"/>
                el-main>
    
            el-container>
        el-container>
    template>
    
    <script>
    
        import Aside from "@/components/Aside";
        import Header from "@/components/Header";
    
        export default {
            name: 'Home',
            data() {
                return {
                    collapseBtnClass: 'el-icon-s-fold',
                    isCollapse: false,
                    sideWidth: 200,
                    logoTextShow: true,
                    user: {}
                }
            },
            components: {
                Aside,
                Header
            },
            create() {
                // 从后台获取最新的User数据
                this.getUser()
            },
            methods: {
                collapse() {  // 点击收缩按钮触发
                    this.isCollapse = !this.isCollapse
                    if (this.isCollapse) {  // 收缩
                        this.sideWidth = 64
                        this.collapseBtnClass = 'el-icon-s-unfold'
                        this.logoTextShow = false
                    } else {   // 展开
                        this.sideWidth = 200
                        this.collapseBtnClass = 'el-icon-s-fold'
                        this.logoTextShow = true
                    }
                },
                getUser() {
                    let username = localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")).username : ""
                    if (username) {
                        // 从后台获取User数据
                        this.request.get("/user/username/" + username).then(res => {
                            // 重新赋值后台的最新User数据
                            this.user = res.data
                        })
                    }
                }
            }
        }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    1. 最后将Header.vue的内容全部修改为如下内容
    <template>
        <div style="line-height: 60px; display: flex">
            <div style="flex: 1;">
                <span :class="collapseBtnClass" style="cursor: pointer; font-size: 18px" @click="collapse">span>
                <el-breadcrumb separator="/" style="display: inline-block; margin-left: 10px">
                    <el-breadcrumb-item :to="'/'">首页el-breadcrumb-item>
                    <el-breadcrumb-item>{{ currentPathName }}el-breadcrumb-item>
                el-breadcrumb>
            div>
            <el-dropdown style="width: 150px; cursor: pointer; text-align: right">
                <div style="display: inline-block">
                    <img :src="user.avatar" referrerPolicy="no-referrer"
                         style="width: 30px; border-radius: 50%; position: relative; top: 10px; right: 5px">
                    <span>{{ user.nickname }}span><i class="el-icon-arrow-down" style="margin-left: 5px">i>
                div>
                <el-dropdown-menu slot="dropdown" style="width: 100px; text-align: center">
                    <el-dropdown-item style="font-size: 14px; padding: 5px 0">
                        <router-link to="/password">修改密码router-link>
                    el-dropdown-item>
                    <el-dropdown-item style="font-size: 14px; padding: 5px 0">
                        <router-link to="/person">
                            个人信息
                        router-link>
                    el-dropdown-item>
                    <el-dropdown-item style="font-size: 14px; padding: 5px 0">
                        <span style="text-decoration: none" @click="logout">退出span>
                    el-dropdown-item>
                el-dropdown-menu>
            el-dropdown>
        div>
    template>
    
    <script>
        export default {
            name: "Header",
            props: {
                collapseBtnClass: String,
                user: Object
            },
            computed: {
                currentPathName() {
                    return this.$store.state.currentPathName;  //需要监听的数据
                }
            },
            data() {
                return {
    
                }
            },
            methods: {
                collapse() {
                    this.$emit("asideCollapse")
                },
                logout() {
                    this.$router.push("/login")
                    localStorage.removeItem("user")
                    this.$message.success("退出成功")
                }
            }
        }
    script>
    
    <style scoped>
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    1. 然后来到前端测试,发现可以成功实现用户的头像上传功能
      在这里插入图片描述

    总结

      以上就是今天学习的全部内容,下篇博文给会给大家带来关于SpringBoot和Vue整合ECharts的内容。明天见!

  • 相关阅读:
    第五章:数组、排序和查找
    技术干货|极速、极智、极简的昇思MindSpore Lite:助力华为Watch更加智能
    Wireshark流量分析例题
    lnmp 论坛框架搭建
    测试与爬虫—抓包神器之Charles
    @AutoConfigureAfter注解
    推荐《一拳超人》
    SLAM论文详解(5) — Bundle_Adjustment_LM(BALM)论文详解
    kube-controller-manager和kube-scheduler不能正常启动
    ubuntu18.04下zookeeper安装与简单使用
  • 原文地址:https://blog.csdn.net/IronmanJay/article/details/127725301