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

本文主要是介绍全栈入门,前后端入门--springboot3+vue3制作一个后台管理项目,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一:前言

        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

<script setup>
import { RouterLink, RouterView } from 'vue-router'</script><template>
<div class="common-layout"><el-container class="container"><el-aside class="aside" width="200px">侧边栏</el-aside><el-container><el-header class="header">头部</el-header><el-main class="main">内容</el-main></el-container></el-container></div>
</template><style scoped lang="less">
.container{height: 100vh;.aside{background-color: aqua;}.header{background-color: chocolate;}.main{background-color: beige;}
}
</style>

在侧边栏使用menu菜单

app.vue代码

<script setup>
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)
}
</script><template><div class="common-layout"><el-container class="container"><el-aside class="aside" :style="{ 'width': !isCollapse ? '200px' : '60px' }"><el-menu default-active="/" class="el-menu-vertical-demo" :collapse="isCollapse" @open="handleOpen"@close="handleClose" router><el-menu-item index="/"><el-icon><icon-menu /></el-icon><template #title>首页</template></el-menu-item><el-sub-menu index="gongsi"><template #title><el-icon><location /></el-icon><span>公司信息管理</span></template><el-menu-item-group><el-menu-item index="/gongsi/DeptInfo">部门管理</el-menu-item><el-menu-item index="/gongsi/EmployeeInformation">员工管理</el-menu-item></el-menu-item-group></el-sub-menu><el-menu-item index="3"><el-icon><document /></el-icon><template #title>公司图表数据</template></el-menu-item><el-menu-item index="4"><el-icon><setting /></el-icon><template #title>暂未开发...</template></el-menu-item></el-menu></el-aside><el-container><el-header class="header"><el-radio-group v-model="isCollapse" style="margin-bottom: 20px"><el-radio-button :value="false">展开</el-radio-button><el-radio-button :value="true">折叠</el-radio-button></el-radio-group>头部</el-header><el-main class="main"><RouterView /></el-main></el-container></el-container></div>
</template><style scoped lang="less">
.container {height: 100vh;.aside {background-color: #fff;}.header {background-color: chocolate;}.main {background-color: beige;}
}
</style>

编写homeView.vue文件

<script setup>
</script><template><div><h1>首页</h1><div style="display: flex;"><div class="left"><div>前端技术选型</div><ul><li>vue3</li><li>elementplus</li><li>router</li><li>pinia</li><li>axios</li><li>...</li></ul></div><div class="right"><div>后端技术选型</div><ul><li>springboot3</li><li>maven</li><li>lombok</li><li>pageHelper</li><li>...</li></ul></div></div></div>
</template>

运行项目,效果图

        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文件

<script setup>
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([])</script><template><div><el-table :data="tableData" style="width: 100%"><el-table-column prop="id" label="序号" width="180" /><el-table-column prop="name" label="部门名称" width="180" /><el-table-column prop="updateTime" label="最后操作时间" /><el-table-column label="操作"><template #default="scope"><el-button size="small" @click="handleEdit(scope.$index, scope.row)">修改</el-button><el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button></template></el-table-column></el-table></div>
</template>

页面展示

渲染成功

4:编写增加部门接口

        4-1:后端接口

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

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

新增部门成功

4-2:前端页面

DeptInfo.vue文件

<script setup>
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:''}
}
</script><template><div><el-button type="primary" size="large" @click="addDeptButton = true"><el-icon><Plus /></el-icon>新增</el-button><el-table :data="tableData" style="width: 100%"><el-table-column prop="id" label="序号" width="180" /><el-table-column prop="name" label="部门名称" width="180" /><el-table-column prop="updateTime" label="最后操作时间" /><el-table-column label="操作"><template #default="scope"><el-button size="small" @click="handleEdit(scope.$index, scope.row)">修改</el-button><el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button></template></el-table-column></el-table><el-dialog v-model="addDeptButton" title="新增部门" width="500" draggable @close="dialogcancel"><el-form :model="form"><el-form-item label="部门名称" label-width="140px"><el-input v-model="form.name" autocomplete="off" /></el-form-item></el-form><template #footer><div class="dialog-footer"><el-button @click="addDeptButton = false">取消</el-button><el-button type="primary" @click="addDeptPrimary">确定</el-button></div></template></el-dialog></div>
</template>

页面效果

5:编写删除部门接口

        5-1:后端接口

重启项目,打开postman测试

测试成功

5-2:前端页面

页面效果

6:编写修改部门接口

        6-1:后端接口

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

6-2:前端页面

DeptInfo.vue

<script setup>
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=trueaddOrEdit.value='add'
}
let deptId=ref(null)
const handleEdit = (index,row) => {form.value.name=row.nameaddDeptButton.value=trueaddOrEdit.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()}})
}
</script><template><div><el-button type="primary" size="large" @click="addDeptButton1"><el-icon><Plus /></el-icon>新增</el-button><el-table :data="tableData" style="width: 100%"><el-table-column prop="id" label="序号" width="180" /><el-table-column prop="name" label="部门名称" width="180" /><el-table-column prop="updateTime" label="最后操作时间" /><el-table-column label="操作"><template #default="scope"><el-button size="small" @click="handleEdit(scope.$index, scope.row)">修改</el-button><el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button></template></el-table-column></el-table><el-dialog v-model="addDeptButton" :title="addOrEdit=='add'?'新增部门':'修改部门'" width="500" draggable @close="dialogcancel"><el-form :model="form"><el-form-item label="部门名称" label-width="140px"><el-input v-model="form.name" autocomplete="off" /></el-form-item></el-form><template #footer><div class="dialog-footer"><el-button @click="addDeptButton = false">取消</el-button><el-button type="primary" @click="addDeptPrimary">确定</el-button></div></template></el-dialog></div>
</template>

效果展示

7:分页及搜索功能实现

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

 <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>2.1.0</version></dependency>

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

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

7-2:后端接口

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

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

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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hezhanying.mapper.EmpMapper"><!-- 条件分页查询 --><select id="getAllEmp" resultType="com.hezhanying.pojo.Emp">select * from emp<where><if test="name != null and name != ''">name like concat('%',#{name},'%')</if><if test="gender != null">and gender = #{gender}</if><if test="begin != null and end != null">and entrydate between #{begin} and #{end}</if></where>order by update_time desc</select>
</mapper>

开始写后端逻辑

重启项目,打开postman测试

测试成功

7-3:前端页面

EmployeeInformation.vue

<script setup>
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 < 10 ? `0${month}` : monthreturn `${year}-${paddedMonth}-${day}`
}
const onSearch = () => {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()
}
</script>
<template><el-form :inline="true" :model="searchForm" class="demo-form-inline"><el-form-item label="姓名"><el-input v-model="searchForm.name" placeholder="请输入姓名" clearable /></el-form-item><el-form-item label="性别"><el-select v-model="searchForm.gender" placeholder="请选择性别" clearable><el-option v-for="(item, index) in genderList" :label="item.name" :value="item.id" /></el-select></el-form-item><el-form-item label="入职日期"><el-date-picker v-model="searchForm.entryDate" type="daterange" unlink-panels range-separator="至"start-placeholder="开始日期" end-placeholder="结束日期" :shortcuts="shortcuts" size="large" /></el-form-item><el-form-item><el-button type="primary" @click="onSearch">搜索</el-button></el-form-item><el-form-item><el-button type="warning" @click="onReset">重置</el-button></el-form-item></el-form><el-button type="danger" size="large"><el-icon><Minus /></el-icon>批量删除</el-button><el-button type="primary" size="large" @click="addEmp"><el-icon><Plus /></el-icon>新增</el-button><el-table ref="multipleTableRef" :data="tableData" style="width: 100%" max-height="500px"@selection-change="handleSelectionChange"><el-table-column type="selection" /><el-table-column property="id" label="序号" sortable /><el-table-column property="name" label="姓名" /><el-table-column property="image" label="头像"><template #default="scope"><!-- {{ scope.row.date }} --><img :src="scope.row.image" /></template></el-table-column><el-table-column label="性别"><template #default="scope">{{ scope.row.gender == 1 ? '男' : '女' }}</template></el-table-column><el-table-column label="职位"><template #default="scope">{{ scope.row.job == 1 ? '班主任' : scope.row.job == 2 ? '讲师' : scope.row.job == 3 ? '学工主管' :scope.row.job == 4 ? '教研主管' : scope.row.job == 5 ? '咨询师' : '暂无职务' }}</template></el-table-column><el-table-column property="entrydate" label="入职日期" sortable/><el-table-column property="updateTime" label="最后操作时间" sortable/> <!-- <template #default="scope">{{ scope.row.updateTime.replace(/\T/g, ' ') }}</template></el-table-column> --><el-table-column label="操作"><template #default="scope"><el-button size="small" @click="handleEdit(scope.$index, scope.row)">修改</el-button><el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button></template></el-table-column></el-table><el-pagination v-model:current-page="currentPage4" v-model:page-size="pageSize4" :page-sizes="[5, 10, 20, 30]"background layout="total, sizes, prev, pager, next, jumper" :total="pageTotal" @size-change="handleSizeChange"@current-change="handleCurrentChange" /><el-dialog v-model="dialogFormVisible" :title="editOrAdd == 'edit' ? '修改学员信息' : editOrAdd == 'add' ? '新增学员' : ''"width="500" @close="closeDialog"><el-form :model="form"><el-form-item label="用户名" label-width="140px"><el-input v-model="form.username" autocomplete="off" /></el-form-item><el-form-item label="姓名" label-width="140px"><el-input v-model="form.name" autocomplete="off" /></el-form-item><el-form-item label="头像" label-width="140px"><el-input v-model="form.image" autocomplete="off" /></el-form-item><el-form-item label="性别" label-width="140px"><el-select v-model="form.gender" placeholder="请选择性别"><el-option v-for="(item, index) in genderList" :label="item.name" :value="item.id" /></el-select></el-form-item><el-form-item label="职位" label-width="140px"><el-select v-model="form.job" placeholder="请选择职位"><el-option v-for="(item, index) in jobList" :label="item.name" :value="item.id" /></el-select></el-form-item><el-form-item label="部门" label-width="140px"><el-select v-model="form.deptId" placeholder="请选择部门"><el-option v-for="(item, index) in deptList" :label="item.name" :value="item.id" /></el-select></el-form-item></el-form><template #footer><div class="dialog-footer"><el-button @click="dialogFormVisible = false">取消</el-button><el-button type="primary" @click="primaryAddEmp">确定</el-button></div></template></el-dialog>
</template>
<style scoped></style>
<style>
.demo-form-inline .el-input {--el-input-width: 220px;
}.demo-form-inline .el-select {--el-select-width: 220px;
}
</style>

页面效果

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

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

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

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

这篇关于全栈入门,前后端入门--springboot3+vue3制作一个后台管理项目的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/766973

相关文章

用Microsoft.Extensions.Hosting 管理WPF项目.

首先引入必要的包: <ItemGroup><PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" /><PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /><PackageReference Include="Serilog

C++必修:模版的入门到实践

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C++学习 贝蒂的主页:Betty’s blog 1. 泛型编程 首先让我们来思考一个问题,如何实现一个交换函数? void swap(int& x, int& y){int tmp = x;x = y;y = tmp;} 相信大家很快就能写出上面这段代码,但是如果要求这个交换函数支持字符型

eclipse运行springboot项目,找不到主类

解决办法尝试了很多种,下载sts压缩包行不通。最后解决办法如图: help--->Eclipse Marketplace--->Popular--->找到Spring Tools 3---->Installed。

零基础STM32单片机编程入门(一)初识STM32单片机

文章目录 一.概要二.单片机型号命名规则三.STM32F103系统架构四.STM32F103C8T6单片机启动流程五.STM32F103C8T6单片机主要外设资源六.编程过程中芯片数据手册的作用1.单片机外设资源情况2.STM32单片机内部框图3.STM32单片机管脚图4.STM32单片机每个管脚可配功能5.单片机功耗数据6.FALSH编程时间,擦写次数7.I/O高低电平电压表格8.外设接口

vue, 左右布局宽,可拖动改变

1:建立一个draggableMixin.js  混入的方式使用 2:代码如下draggableMixin.js  export default {data() {return {leftWidth: 330,isDragging: false,startX: 0,startWidth: 0,};},methods: {startDragging(e) {this.isDragging = tr

关于如何更好管理好数据库的一点思考

本文尝试从数据库设计理论、ER图简介、性能优化、避免过度设计及权限管理方面进行思考阐述。 一、数据库范式 以下通过详细的示例说明数据库范式的概念,将逐步规范化一个例子,逐级说明每个范式的要求和变换过程。 示例:学生课程登记系统 初始表格如下: 学生ID学生姓名课程ID课程名称教师教师办公室1张三101数学王老师101室2李四102英语李老师102室3王五101数学王老师101室4赵六103物理陈

ps基础入门

1.基础      1.1新建文件      1.2创建指定形状      1.4移动工具          1.41移动画布中的任意元素          1.42移动画布          1.43修改画布大小          1.44修改图像大小      1.5框选工具      1.6矩形工具      1.7图层          1.71图层颜色修改          1

C++入门01

1、.h和.cpp 源文件 (.cpp)源文件是C++程序的实际实现代码文件,其中包含了具体的函数和类的定义、实现以及其他相关的代码。主要特点如下:实现代码: 源文件中包含了函数、类的具体实现代码,用于实现程序的功能。编译单元: 源文件通常是一个编译单元,即单独编译的基本单位。每个源文件都会经过编译器的处理,生成对应的目标文件。包含头文件: 源文件可以通过#include指令引入头文件,以使

vue项目集成CanvasEditor实现Word在线编辑器

CanvasEditor实现Word在线编辑器 官网文档:https://hufe.club/canvas-editor-docs/guide/schema.html 源码地址:https://github.com/Hufe921/canvas-editor 前提声明: 由于CanvasEditor目前不支持vue、react 等框架开箱即用版,所以需要我们去Git下载源码,拿到其中两个主

React+TS前台项目实战(十七)-- 全局常用组件Dropdown封装

文章目录 前言Dropdown组件1. 功能分析2. 代码+详细注释3. 使用方式4. 效果展示 总结 前言 今天这篇主要讲全局Dropdown组件封装,可根据UI设计师要求自定义修改。 Dropdown组件 1. 功能分析 (1)通过position属性,可以控制下拉选项的位置 (2)通过传入width属性, 可以自定义下拉选项的宽度 (3)通过传入classN