小工具知识网

当前位置: 首页 > 行业知识 > 全栈入门,前后端入门--springboot3+vue3制作一个后台管理项目

行业知识

全栈入门,前后端入门--springboot3+vue3制作一个后台管理项目

2026-11-10 23:15:09 来源:互联网转载

一:前言

        1:因为本人也是全栈初学者,现在主职是公司前端,鉴于当前行业形式,单单只掌握一门语言已经不再吃香,甚至有点危险,35岁危机极大可能提前。作为00后要始终保持危机意识,不至于在25,26岁时被裁去卖煎饼果子,所以现在决定开始自学后端,争取早日成为一名全栈开发,能独自自主完成单个项目,早日回到老家工作,开始养老人生。

        2:请注意:本教程只注重功能实现(增删改查,文件上传等),前端的样式可能

                会略显简陋,有强迫症的慎入!!

        3:前置知识:前端(前端三件套,vue3)后端(java基础,mysql基础)

        4:项目开展前提:

                工具:Intellij IDEA 2023.3.4,vscode,mySql,vue3,springboot3,element-plus等

                开发插件:

                        截图工具:pixpin

                        接口测试工具:postman

                        vscode:Volar,prettier,auto close tag

                        Intellij IDEA:chinese language汉化,codeGlance Pro代码地图,MybatisX,ptg

二:创建项目

        1:前端项目

                  1:使用  npm create vue@latest  命令创建一个vue3项目,勾选pinia和router

                   2:npm install 下载依赖

                  3:按需引入element-plus,点击此链接跳转官网

                  4:把main.js中的框架自带样式注释或删除

                   5:测试一下elementplus是否被正确配置,改造app.vue,并npm run dev运行项目

                                

                可以看到,elementplus已被正确配置并成功引入

       2:后端项目

                1:打开idea,新建项目

     

                新建项目,勾选项目所需依赖,想知道勾选的依赖有什么作用的请自行网上搜索,加深印象。到这里项目就创建成功了。

                2:思考后台样式与表设计

        三:开始编写代码

                 1:前端页面

                                使用elementplus布局容器

     app.vue

import { RouterLink, RouterView } from 'vue-router'          侧边栏              头部        内容            .container{  height: 100vh;  .aside{    background-color: aqua;  }  .header{    background-color: chocolate;  }  .main{    background-color: beige;  }}

在侧边栏使用menu菜单

app.vue代码

import { RouterLink, RouterView } from 'vue-router'import { ref } from 'vue'import {  Document,  Menu as IconMenu,  Location,  Setting,} from '@element-plus/icons-vue'let isCollapse = ref(true)const handleOpen = (key, keyPath) => {//菜单展开  console.log(key, keyPath)}const handleClose = (key, keyPath) => {//菜单折叠  console.log(key, keyPath)}                                                      首页                                                                                          公司信息管理                                      部门管理              员工管理                                                                                  公司图表数据                                                                      暂未开发...                                                            展开            折叠                    头部                                      .container {  height: 100vh;  .aside {    background-color: #fff;  }  .header {    background-color: chocolate;  }  .main {    background-color: beige;  }}

编写homeView.vue文件

   

首页

前端技术选型
  • vue3
  • elementplus
  • router
  • pinia
  • axios
  • ...
后端技术选型
  • springboot3
  • maven
  • lombok
  • pageHelper
  • ...

运行项目,效果图

        2:后端接口开发

                   1:项目连接数据库

                                1-1:

 

                1-2:

                1-3:1-2用到的sql语句

-- 部门管理create table dept(     id int unsigned primary key auto_increment comment '主键ID',     name varchar(10) not null unique comment '部门名称',     create_time datetime not null comment '创建时间',     update_time datetime not null comment '修改时间') comment '部门表';-- 部门表测试数据insert into dept (id, name, create_time, update_time)values(    1,'学工部',now(),now()),    (2,'教研部',now(),now()),    (3,'咨询部',now(),now()),    (4,'就业部',now(),now()),    (5,'人事部',now(),now());-- 员工管理(带约束)create table emp (             id int unsigned primary key auto_increment comment 'ID',             username varchar(20) not null unique comment '用户名',             password varchar(32) default '123456' comment '密码',             name varchar(10) not null comment '姓名',             gender tinyint unsigned not null comment '性别, 说明: 1 男, 2 女',             image varchar(300) comment '图像',             job tinyint unsigned comment '职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师',             entrydate date comment '入职时间',             dept_id int unsigned comment '部门ID',             create_time datetime not null comment '创建时间',             update_time datetime not null comment '修改时间') comment '员工表';-- 员工表测试数据INSERT INTO emp(id, username, password, name, gender, image, job, entrydate,dept_id, create_time, update_time) VALUES  (1,'zhangsan','123456','张三',1,'1.jpg',4,'2000-01-01',2,now(),now()),  (2,'liguang','123456','李广',1,'2.jpg',2,'2015-01-01',2,now(),now()),  (3,'xinqiji','123456','辛弃疾',1,'3.jpg',2,'2008-05-01',2,now(),now()),  (4,'libai','123456','李白',1,'4.jpg',2,'2007-01-01',2,now(),now()),  (5,'sushi','123456','苏轼',1,'5.jpg',2,'2012-12-05',2,now(),now()),  (6,'dufu','123456','杜甫',2,'6.jpg',3,'2013-09-05',1,now(),now()),  (7,'dumu','123456','杜牧',2,'7.jpg',1,'2005-08-01',1,now(),now()), (8,'zhouyu','123456','周瑜',2,'8.jpg',1,'2014-11-09',1,now(),now()),(9,'zhugeliang','123456','诸葛亮',2,'9.jpg',1,'2011-03-11',1,now(),now()),(10,'xiahoudun','123456','夏侯惇',2,'10.jpg',1,'2013-09-05',1,now(),now()),(11,'liqingzhao','123456','李清照',1,'11.jpg',5,'2007-02-01',3,now(),now()),(12,'taoyuanming','123456','陶渊明',1,'12.jpg',5,'2008-08-18',3,now(),now()),(13,'wangwei','123456','王伟',1,'13.jpg',5,'2012-11-01',3,now(),now()),(14,'weiyingwu','123456','韦应物',1,'14.jpg',2,'2002-08-01',2,now(),now()),(15,'zhanghong','123456','张红',1,'15.jpg',2,'2011-05-01',2,now(),now()),(16,'heqiao','123456','何桥',1,'16.jpg',2,'2007-01-01',2,now(),now()),(17,'liyuan','123456','李渊',1,'17.jpg',NULL,'2015-03-21',NULL,now(),now());

                1-4:

                1-5:创建实体类

可以看到两个实体类的get和set方法占据了超多代码量,这时候我们下载的lombok依赖就起作用了,对上面两个实体类稍加改造

代码量得到极大的删减,看着就很舒坦!

根据java项目三层架构原则,创建控制层,业务层(逻辑层,service层),持久层(dao)文件夹

 3:编写查询部门接口

        3-1:按照图示编写代码

可以看到有很多注解,大大简化了我们的开发效率,因为有很多注解在这里就不一一解释了,用过几次就知道大概是什么意思了,我是个实用主义,不过多的去探究其中底层原理,会拿来用解决项目中的问题就可,想研究底层原理的可自行百度,谢谢

        3-2:运行项目

出现这个页面就代表运行成功,接下来使用postman工具来测试接口

        3-3:打开postman,测试接口

我们发现测试成功,有数据返回,但是createTime和updateTime字段是null,再看数据表可以看到明明是有值的,这是因为数据表的字段是create_time和update_time字段,一个是驼峰命名,一个是用下划线命名,字段名不一致,而且我们希望测试成功能在控制台打印相关信息,方便查看调试。这两个问题一块解决,如图所示:

修改完成后重启项目,再来测试一下

ok,成功!接下来就要写前端页面了,把返回的数据渲染到页面上

3-4:前端使用axios进行网络请求,并配置跨域

         打开前端项目,使用 npm i axios 下载axios依赖

        在vite.config.js中配置跨域,因为前后端项目端口不一样,所以即使是同一个电脑,也需要进行跨域配置

 server: {    proxy: {      '/api': {        target: 'http://localhost:8080',        changeOrigin: true,        rewrite: (path) => path.replace(/^\/api/, '')      }    }  },

        然后在src文件夹下创建utils文件夹,在utils下创建request.js文件夹,二次封装axios

        在deptInfo.vue文件中测试请求

接下来在浏览器打开控制台,查看网络请求

成功拿到数据

3-5:把数据渲染到表格中

DeptInfo.vue文件

import request from '@/utils/request.js'import { ref,onMounted } from 'vue'onMounted(()=>{  getAllDept()})const getAllDept = () => {  request.get("/api/depts").then(res=>{    tableData.value=res.data   })}let tableData = ref([])                                     修改        删除              

页面展示

渲染成功

4:编写增加部门接口

        4-1:后端接口

重启项目,打开postman测试接口

不确定的可以打开数据表查看

新增部门成功

4-2:前端页面

DeptInfo.vue文件

import request from '@/utils/request.js'import { ref,onMounted } from 'vue'import {  Plus} from '@element-plus/icons-vue'onMounted(()=>{  getAllDept()})let tableData = ref([])const getAllDept = () => {  request.get("/api/depts").then(res=>{    tableData.value=res.data   })}let addDeptButton = ref(false)let form=ref({  name:''})const addDeptPrimary = () => {//点击确定,添加部门  request.post("/api/depts",form.value).then(res=>{    if(res.code==0){      ElMessage({        message: res.msg,        type: 'success',      })      getAllDept()      addDeptButton.value=false    }  })}const dialogcancel = () => {//点击取消,重置表单  form.value={    name:''  }}                 新增                                  修改        删除                                                            取消                  确定                      

页面效果

5:编写删除部门接口

        5-1:后端接口

重启项目,打开postman测试

测试成功

5-2:前端页面

页面效果

6:编写修改部门接口

        6-1:后端接口

别忘了重启项目,打开postman测试

6-2:前端页面

DeptInfo.vue

import request from '@/utils/request.js'import { ref,onMounted } from 'vue'import {  Plus} from '@element-plus/icons-vue'import { getRowIdentity } from 'element-plus/es/components/table/src/util';onMounted(()=>{  getAllDept()})let tableData = ref([])const getAllDept = () => {  request.get("/api/depts").then(res=>{    tableData.value=res.data   })}let addDeptButton = ref(false)let form=ref({  name:''})let addOrEdit=ref('')const addDeptButton1 = () => {  addDeptButton.value=true  addOrEdit.value='add'}let deptId=ref(null)const handleEdit = (index,row) => {  form.value.name=row.name  addDeptButton.value=true  addOrEdit.value='edit'  deptId.value=row.id}const addDeptPrimary = () => {//点击确定,添加部门修改部门  addOrEdit.value=='add'&&request.post("/api/depts",form.value).then(res=>{    if(res.code==0){      ElMessage({        message: res.msg,        type: 'success',      })      getAllDept()      addDeptButton.value=false    }  })  addOrEdit.value=='edit'&&request.put("/api/depts",{name:form.value.name,id:deptId.value}).then(res=>{    if(res.code==0){      ElMessage({        message: res.msg,        type: 'success',      })      getAllDept()      addDeptButton.value=false    }  })}const dialogcancel = () => {//点击取消,重置表单  form.value={    name:''  }}const handleDelete = (index,row) => {  request.delete("/api/depts/"+row.id).then(res=>{    if(res.code==0){      ElMessage({        message: res.msg,        type: 'success',      })      getAllDept()    }  })}                 新增                                  修改        删除                                                            取消                  确定                      

效果展示

7:分页及搜索功能实现

        7-1:编写员工增删改查功能和上面是大差不差的,现在给员工查询列表加上分页功能

             com.github.pagehelper            pagehelper-spring-boot-starter            2.1.0        

在pom.xml文件中添加以上依赖

开始写接口之前还需要写一个实体类,PageBean.java

7-2:后端接口

        springboot操作数据库有两种方式,一种是刚才一直在用的注解方式,另一种是xml方式,两种各有优缺点,进行复杂查询时一般使用xml方式,它更灵活,因为查询员工列表,查询参数会有分页参数,还有搜索框参数,参数较多,决定使用xml方式。

在resources文件夹中创建com/hezhanying/mapper目录,目录中创建EmpMapper.xml文件,如图所示

接下来就可以写查询语句了,需要有pageNo(第几页)pageSize(每页多少条数据)name(员工姓名)gender(性别)begin end(入职日期在begin和end之间的)

                select * from emp                                    name like concat('%',#{name},'%')                                        and gender = #{gender}                                        and entrydate between #{begin} and #{end}                            order by update_time desc    

开始写后端逻辑

重启项目,打开postman测试

测试成功

7-3:前端页面

EmployeeInformation.vue

import { ref, onMounted } from 'vue'import request from '@/utils/request.js'import {Plus,Minus} from '@element-plus/icons-vue'let tableData = ref([])onMounted(() => {getEmps()})const getEmps = (page = 1, pageSize = 5,name='',gender='',entryDateStart='',entryDateEnd='') => {request.get(`/api/emps?pageNo=${page}&pageSize=${pageSize}&name=${name}&gender=${gender}&begin=${entryDateStart}&end=${entryDateEnd}`).then(res => {tableData.value = res.data.rowspageTotal.value = res.data.total})}const multipleTableRef = ref()const multipleSelection = ref([])const handleSelectionChange = (val) => {multipleSelection.value = val}let editOrAdd = ref('')const handleEdit = (index, row) => {request.get('/depts').then(res => {deptList.value = res.data})dialogFormVisible.value = trueeditOrAdd.value = "edit"let { id, username, name, gender, image, deptId, job } = rowform.value.id = idform.value.username = usernameform.value.name = nameform.value.gender = gender// ==1?'男':'女'form.value.image = imageform.value.deptId = deptIdform.value.job = job// ==1?'班主任':job==2?'讲师':job==3?'学工主管':job==4?'教研主管':job==5?'咨询师':'null'console.log(index, row)}const handleDelete = (index, row) => {console.log(index, row)request.delete('/emps/' + row.id).then(res => {if (res.code === 0) {ElMessage({message: res.msg,type: 'success',})getEmps(currentPage4.value,pageSize4.value,searchForm.value.name,searchForm.value.gender,searchForm.value.entryDateStart,searchForm.value.entryDateEnd)}})}const dialogFormVisible = ref(false)const form = ref({id: '',username: '',name: '',gender: '',image: '',deptId: '',job: ''})let deptList = ref([])let jobList = ref([{ name: '班主任', id: 1 },{ name: '讲师', id: 2 },{ name: '学工主管', id: 3 },{ name: '教研主管', id: 4 },{ name: '咨询师', id: 5 },])let genderList = ref([{ name: '男', id: 1 },{ name: '女', id: 2 },])const addEmp = () => {editOrAdd.value = "add"dialogFormVisible.value = truerequest.get('/depts').then(res => {deptList.value = res.data})}const primaryAddEmp = () => {if (editOrAdd.value == "add") {delete form.value.idrequest.post("/emps", form.value).then(res => {ElMessage({message: res.msg,type: 'success',})getEmps()dialogFormVisible.value = false})} else if (editOrAdd.value == "edit") {console.log('form.value :>> ', form.value);request.put("/emps", form.value).then(res => {ElMessage({message: res.msg,type: 'success',})getEmps()dialogFormVisible.value = false})}}const closeDialog = (params) => {form.value = {id: '',username: '',name: '',gender: '',image: '',deptId: '',job: ''}}let currentPage4 = ref(1)//第几页let pageSize4 = ref(5)//每页几条数据let pageTotal = ref()const handleSizeChange = (val) => {//切换每页显示条数getEmps(currentPage4.value, val,searchForm.value.name,searchForm.value.gender,searchForm.value.entryDateStart,searchForm.value.entryDateEnd)pageSize4.value = val}const handleCurrentChange = (val) => {//切换页码getEmps(val, pageSize4.value,searchForm.value.name,searchForm.value.gender,searchForm.value.entryDateStart,searchForm.value.entryDateEnd)currentPage4.value = val}let searchForm = ref({name: '', gender: '',entryDate: [],entryDateStart: '',entryDateEnd: '',})const shortcuts = [{text: '最近一周',value: () => {const end = new Date()const start = new Date()start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)return [start, end]},},{text: '最近一月',value: () => {const end = new Date()const start = new Date()start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)return [start, end]},},{text: '最近三月',value: () => {const end = new Date()const start = new Date()start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)return [start, end]},},]function padZeroToMonth(dateStr) {const [year, month, day] = dateStr.split('-').map(Number)const paddedMonth = month  {searchForm.value.entryDateStart = searchForm.value.entryDate.length!=0 ? padZeroToMonth(searchForm.value.entryDate[0].toLocaleDateString().replace(/\//g, '-')):''searchForm.value.entryDateEnd =  searchForm.value.entryDate.length!=0 ? padZeroToMonth(searchForm.value.entryDate[1].toLocaleDateString().replace(/\//g, '-')):''// request.get(`/emps?name=${searchForm.value.name}&gender=${searchForm.value.gender}`).then(res => {getEmps(currentPage4.value, pageSize4.value,searchForm.value.name,searchForm.value.gender,searchForm.value.entryDateStart,searchForm.value.entryDateEnd)// })}const onReset = (params) => {searchForm.value = {name: '',gender: '',entryDate: [],entryDateStart: '',entryDateEnd: '',}currentPage4.value = 1pageSize4.value = 5getEmps()}搜索重置批量删除新增{ scope.row.date }} -->{{ scope.row.gender == 1 ? '男' : '女' }}{{ scope.row.job == 1 ? '班主任' : scope.row.job == 2 ? '讲师' : scope.row.job == 3 ? '学工主管' :scope.row.job == 4 ? '教研主管' : scope.row.job == 5 ? '咨询师' : '暂无职务' }} { scope.row.updateTime.replace(/\T/g, ' ') }} -->修改删除    取消确定.demo-form-inline .el-input {--el-input-width: 220px;}.demo-form-inline .el-select {--el-select-width: 220px;}

页面效果

非常完美!增加,修改和删除功能请参照修改删除部门接口

8:新增员工,上传头像功能实现

        8-1:因为现在企业的文件都是上传到阿里云或者腾讯云上的,放到自己磁盘上的弊端很大,在这里就不演示给大家了,在实际工作中根本用不到,都是用的阿里云,腾讯云之类的,有想知道的小伙伴可以自行百度,又因为开通云服务器要钱,而且费时,等后续补充吧。暂时用string字符串类型代替吧

        8-2:-----------------03-05更新分界线,开通阿里云服务器上传图片-------------------------------------

                下面开始介绍使用云服务器存储图片

                  1:打开阿里云官网,阿里云-计算,为了无法计算的价值 (aliyun.com)

                   2:注册账户,有了账户就直接登录

                   3:登录成功后,点击控制台

               4:选择对象存储oss

                5: 引入sdk

        

 

 然后再回到文档

 再创建一个utils文件夹,文件夹内创建一个AliOSSUtils类,把复制的代码放进去

 这段代码这是一个示例,并不是我们想要的,还需要改造一下,改造后的代码如下

package com.hezhanying.utils;import com.aliyun.oss.OSS;import com.aliyun.oss.OSSClientBuilder;import org.springframework.stereotype.Component;import org.springframework.web.multipart.MultipartFile;import java.io.IOException;import java.io.InputStream;import java.util.UUID;@Componentpublic class AliOSSUtils {    private String endpoint = "";    private String accessKeyId = "";    private String accessKeySecret = "";    private String bucketName = "";    /**     * 实现上传图片到OSS     */    public String upload(MultipartFile multipartFile) throws IOException {        // 获取上传的文件的输入流        InputStream inputStream = multipartFile.getInputStream();        // 避免文件覆盖        String originalFilename = multipartFile.getOriginalFilename();        String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));        //上传文件到 OSS        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);        ossClient.putObject(bucketName, fileName, inputStream);        //文件访问路径        String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;        // 关闭ossClient        ossClient.shutdown();        return url;// 把上传到oss的路径返回    }}

 回到阿里云,搜索oss存储,点进来,这个oss对象存储没弄过的可能会提醒你需要先开通,跟着指示开通就行

 请在复制进来的网址前面加上https://

点击进入bucket, 能看到我们刚才创建的东西

 

 

 

 

 

 再controller软件包中创建UploadController类

 写上传图片的接口,代码如下

package com.hezhanying.controller;import com.hezhanying.pojo.Result;import com.hezhanying.utils.AliOSSUtils;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.multipart.MultipartFile;import java.io.IOException;@Slf4j@RestControllerpublic class UploadController {    @Autowired    private AliOSSUtils aliOSSUtils;    @PostMapping("/api/upload")    public Result upload(MultipartFile image) throws IOException {        String url= aliOSSUtils.upload(image);        return Result.success(url);    }}

重启项目,打开postman测试啦

 

 上传成功!

8-3:使用elementplus的el-upload组件,上传用户头像

修改这个vue文件

代码如下

import { ref, onMounted } from 'vue'import request from '@/utils/request.js'import {Plus,Minus, Delete, Download, ZoomIn} from '@element-plus/icons-vue'let tableData = ref([])onMounted(() => {getEmps()})const getEmps = (page = 1, pageSize = 5, name = '', gender = '', entryDateStart = '', entryDateEnd = '') => {request.get(`/api/emps?pageNo=${page}&pageSize=${pageSize}&name=${name}&gender=${gender}&begin=${entryDateStart}&end=${entryDateEnd}`).then(res => {tableData.value = res.data.rowspageTotal.value = res.data.total})}const multipleTableRef = ref()const multipleSelection = ref([])const handleSelectionChange = (val) => {multipleSelection.value = val}let editOrAdd = ref('')const handleEdit = (index, row) => {request.get('/api/depts').then(res => {deptList.value = res.data})dialogFormVisible.value = trueeditOrAdd.value = "edit"let { id, username, name, gender, image, deptId, job } = rowform.value.id = idform.value.username = usernameform.value.name = nameform.value.gender = gender// ==1?'男':'女'form.value.image = imageform.value.deptId = deptIdform.value.job = job// ==1?'班主任':job==2?'讲师':job==3?'学工主管':job==4?'教研主管':job==5?'咨询师':'null'console.log(index, row)}const handleDelete = (index, row) => {console.log(index, row)request.delete('/api/emps/' + row.id).then(res => {if (res.code === 0) {ElMessage({message: res.msg,type: 'success',})getEmps(currentPage4.value, pageSize4.value, searchForm.value.name, searchForm.value.gender, searchForm.value.entryDateStart, searchForm.value.entryDateEnd)}})}const dialogFormVisible = ref(false)const form = ref({id: '',username: '',name: '',gender: '',image: '',deptId: '',job: ''})let deptList = ref([])let jobList = ref([{ name: '班主任', id: 1 },{ name: '讲师', id: 2 },{ name: '学工主管', id: 3 },{ name: '教研主管', id: 4 },{ name: '咨询师', id: 5 },])let genderList = ref([{ name: '男', id: 1 },{ name: '女', id: 2 },])const addEmp = () => {editOrAdd.value = "add"dialogFormVisible.value = truerequest.get('/api/depts').then(res => {deptList.value = res.data})}const primaryAddEmp = () => {if (editOrAdd.value == "add") {delete form.value.idrequest.post("/api/emps", form.value).then(res => {ElMessage({message: res.msg,type: 'success',})getEmps()dialogFormVisible.value = false})} else if (editOrAdd.value == "edit") {request.put("/api/emps", form.value).then(res => {ElMessage({message: res.msg,type: 'success',})getEmps()dialogFormVisible.value = false})}}const closeDialog = (params) => {form.value = {id: '',username: '',name: '',gender: '',image: '',deptId: '',job: ''}}let currentPage4 = ref(1)//第几页let pageSize4 = ref(5)//每页几条数据let pageTotal = ref()const handleSizeChange = (val) => {//切换每页显示条数getEmps(currentPage4.value, val, searchForm.value.name, searchForm.value.gender, searchForm.value.entryDateStart, searchForm.value.entryDateEnd)pageSize4.value = val}const handleCurrentChange = (val) => {//切换页码getEmps(val, pageSize4.value, searchForm.value.name, searchForm.value.gender, searchForm.value.entryDateStart, searchForm.value.entryDateEnd)currentPage4.value = val}let searchForm = ref({name: '',gender: '',entryDate: [],entryDateStart: '',entryDateEnd: '',})const shortcuts = [{text: '最近一周',value: () => {const end = new Date()const start = new Date()start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)return [start, end]},},{text: '最近一月',value: () => {const end = new Date()const start = new Date()start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)return [start, end]},},{text: '最近三月',value: () => {const end = new Date()const start = new Date()start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)return [start, end]},},]function padZeroToMonth(dateStr) {const [year, month, day] = dateStr.split('-').map(Number)const paddedMonth = month  {searchForm.value.entryDateStart = searchForm.value.entryDate.length != 0 ? padZeroToMonth(searchForm.value.entryDate[0].toLocaleDateString().replace(/\//g, '-')) : ''searchForm.value.entryDateEnd = searchForm.value.entryDate.length != 0 ? padZeroToMonth(searchForm.value.entryDate[1].toLocaleDateString().replace(/\//g, '-')) : ''getEmps(currentPage4.value, pageSize4.value, searchForm.value.name, searchForm.value.gender, searchForm.value.entryDateStart, searchForm.value.entryDateEnd)}const onReset = (params) => {searchForm.value = {name: '',gender: '',entryDate: [],entryDateStart: '',entryDateEnd: '',}currentPage4.value = 1pageSize4.value = 5getEmps()}const dialogImageUrl = ref('')const dialogVisible = ref(false)const disabled = ref(false)const handleRemove = (file) => {console.log("resmove",file.url)}const handlePictureCardPreview = (file) => {dialogImageUrl.value = file.urldialogVisible.value = true}const handleDownload = (file) => {console.log(file.url)}const uploadSuccess = (file,upload) => {console.log('filessss :>> ', file,upload);}const uploadChange = async(file, fileLists) => {let formData = new FormData();formData.append('image', file.raw);try {const response = await request.post('/api/upload', formData, {headers: {'Content-Type': 'multipart/form-data'}});form.value.image=response.data} catch (error) {console.error('error :>> ', error);}}const bemo = (file,fileList) => {form.value.image=''console.log('object :>> ', file,fileList);}搜索重置批量删除新增{{ scope.row.gender == 1 ? '男' : '女' }}{{ scope.row.job == 1 ? '班主任' : scope.row.job == 2 ? '讲师' : scope.row.job == 3 ? '学工主管' :scope.row.job == 4 ? '教研主管' : scope.row.job == 5 ? '咨询师' : '暂无职务' }}{ scope.row.updateTime.replace(/\T/g, ' ') }} -->修改删除取消确定.avatar-uploader .avatar {width: 178px;height: 178px;display: block;}.demo-form-inline .el-input {--el-input-width: 220px;}.demo-form-inline .el-select {--el-select-width: 220px;}.avatar-uploader .el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);}.avatar-uploader .el-upload:hover {border-color: var(--el-color-primary);}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;text-align: center;}

 启动项目,新增用户

 sql只保存图片的地址

 修改的功能,只需要重新上传图片把新图片的地址替换一下就可以了

测试完成后尽快删除bucket,如果不删除产生费用

 

 

 

 删除的过程中同样需要安全验证

友善提醒:使用云服务器时一定不能暴露id,key,地址之类的,小心被刷流量产生高额费用!!

9:--------03-07更新登录鉴权,接口过滤------------------------

        9-1:登录鉴权共有三种方案,一是cookie,二是session,三是jwt(json web token),主流方案是使用jwt,这里就介绍jwt

        再pom.xml文件中引入

            io.jsonwebtoken            jjwt            0.9.1        

            在utils软件包中创建JwtUtils类,代码如下

package com.hezhanying.utils;import io.jsonwebtoken.Claims;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import java.util.Date;import java.util.Map;public class JwtUtils {    private static String signKey = "hezhanying";//签名密钥    private static Long expire = 43200000L; //有效时间    /**     * 生成JWT令牌     登录成功,生成JWT令牌并返回     * @param claims JWT第二部分负载 payload 中存储的内容     * @return     */    public static String generateJwt(Map claims){        String jwt = Jwts.builder()                .addClaims(claims)//自定义信息(有效载荷)                .signWith(SignatureAlgorithm.HS256, signKey)//签名算 法(头部)                .setExpiration(new Date(System.currentTimeMillis() + expire))//过期时间                .compact();        return jwt;    }    /**     * 解析JWT令牌     * @param jwt JWT令牌     * @return JWT第二部分负载 payload 中存储的内容     */    public static Claims parseJWT(String jwt){        Claims claims = Jwts.parser()                .setSigningKey(signKey)//指定签名密钥                .parseClaimsJws(jwt)//指定令牌Token                .getBody();        return claims;    }}

 

 

 重启打开postman测试

 成功!

9-2:接口过滤器filter和Interceptor拦截器,我这里使用filter过滤器

再pom.xml文件中引入

             com.alibaba            fastjson            1.2.76        

 新建filter软件包,再软件包下新建LoginCheckFilter类,代码如下

package com.hezhanying.filter;import com.alibaba.fastjson.JSONObject;import com.hezhanying.pojo.Result;import com.hezhanying.utils.JwtUtils;import jakarta.servlet.*;import jakarta.servlet.annotation.WebFilter;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import lombok.extern.slf4j.Slf4j;import org.springframework.util.StringUtils;import java.io.IOException;@Slf4j@WebFilter(urlPatterns = "/*") //拦截所有请求public class LoginCheckFilter implements Filter {    @Override    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException,ServletException {//前置:强制转换为http协议的请求对象、响应对象 (转换原因:要使用子类中特有方法)        HttpServletRequest request = (HttpServletRequest) servletRequest;        HttpServletResponse response = (HttpServletResponse) servletResponse;//1.获取请求url        String url = request.getRequestURL().toString();        log.info("请求路径:{}", url); //请求路径:http://localhost:8080/api/login//2.判断请求url中是否包含login,如果包含,说明是登录操作,放行        if(url.contains("/api/login")){            chain.doFilter(request, response);//放行请求            return;//结束当前方法的执行        }//3.获取请求头中的令牌(Authorization)        String Authorization = request.getHeader("Authorization");        log.info("从请求头中获取的令牌:{}",Authorization);//4.判断令牌是否存在,如果不存在,返回错误结果(未登录)        if(!StringUtils.hasLength(Authorization)){            log.info("Token不存在");            Result responseResult = Result.error("NOT_LOGIN");//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)            String json = JSONObject.toJSONString(responseResult);            response.setContentType("application/json;charset=utf- 8");//响应            response.getWriter().write(json);            return;        }//5.解析token,如果解析失败,返回错误结果(未登录)        try {            JwtUtils.parseJWT(Authorization);        }catch (Exception e){            log.info("令牌解析失败!");            Result responseResult = Result.error("NOT_LOGIN");//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)            String json = JSONObject.toJSONString(responseResult);            response.setContentType("application/json;charset=utf- 8");//响应            response.getWriter().write(json);            return;        }//6.放行        chain.doFilter(request, response);    }}

 再加上这个注解

重启项目就可以测试了, 

 

 这里前端页面就不再展示了,接口已通,实现页面只是时间问题,登录页面及路由跳转没有技术难题

10:先就这样啦,欢迎评论区讨论,想要源码请私信,谢谢阅读!后续学到新知识还会分享

上一篇:时间序列分析技巧(一):根据ACF、PACF进行AR、MA、ARMA模型选择

下一篇:公司怎么自建服务器?