全栈入门,前后端入门--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=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()
    }
  })
}
</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 mapper
        PUBLIC "-//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.rows
		pageTotal.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 = true
	editOrAdd.value = "edit"
	let { id, username, name, gender, image, deptId, job } = row
	form.value.id = id
	form.value.username = username
	form.value.name = name
	form.value.gender = gender
	// ==1?'男':'女'
	form.value.image = image
	form.value.deptId = deptId
	form.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 = true
	request.get('/depts').then(res => {
		deptList.value = res.data
	})
}
const primaryAddEmp = () => {
	if (editOrAdd.value == "add") {
		delete form.value.id
		request.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}` : month
	return `${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 = 1
	pageSize4.value = 5
	getEmps()
}
</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:先就这样啦,欢迎评论区讨论,想要源码请私信,谢谢阅读!后续学到新知识还会分享

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

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

相关文章

[C++]使用纯opencv去部署yolov9的onnx模型

【介绍】 部署 YOLOv9 ONNX 模型在 OpenCV 的 C 环境中涉及一系列步骤。以下是一个简化的部署方案概述&#xff0c;以及相关的文案。 部署方案概述&#xff1a; 模型准备&#xff1a;首先&#xff0c;你需要确保你有 YOLOv9 的 ONNX 模型文件。这个文件包含了模型的结构和权…

opencv生成一张图片

opencv也可以创造出一张照片&#xff0c;下面就是创造一张照片&#xff0c;并存放到项目文件夹下的示例 #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <iostream> #include <vector> using namespace cv…

sql注入之sqli-labs-less-2 数值型报错注入

输入?id1 进行试探,第二关数值型&#xff0c;没有字符串的单引号&#xff0c;所以输入单引号报错&#xff0c; 经试探?id1 order by 5 -- 如果是错误的数值&#xff0c;显示如下&#xff1a; 正确 的为&#xff1a; ?id1 order by 3 -- 进行注入查看回显点&#xff1a; …

个体工商户营业执照怎么在网上年检?

第一步PC端登录国家企业信用信息公示系统&#xff01; 第二步点企业信息填报&#xff01; 第三步选择省份&#xff01; 第四步登陆方式&#xff01; 第五步点左上角年度报告填写&#xff01; 第六步下滑浏览点确认&#xff01; 第七步按照实际情况填写&#xff01; 第八步…

Linux笔记--文件权限

一、相关概念 Linux最优秀的地方之一就在于多人多任务环境。为了让各个使用者有较为保密的文件数据&#xff0c;文件的权限管理尤为重要。 ●文件的可存取身份: owner:文件拥有者 group:文件所属用户组 others:其他人 ●文件权限: r: read&#xff0c;读 文件:是否能查看文件内…

代码随想录刷题笔记 DAY 41 | 整数拆分 No.343 | 不同的二叉搜索树 No.96

文章目录 Day 4101. 整数拆分&#xff08;No. 343&#xff09;<1> 题目<2> 笔记<3> 代码 02. 不同的二叉搜索树&#xff08;No. 96&#xff09;<1> 题目<2> 笔记<3> 代码 Day 41 01. 整数拆分&#xff08;No. 343&#xff09; 题目链接 …

2024.3.1 小项目

1、机械臂 #include <myhead.h> #define SER_IP "192.168.125.32" //服务器端IP #define SER_PORT 8888 //服务器端端口号#define CLI_IP "192.168.68.148" //客户端IP #define CLI_PORT 9999 /…

Mybatis plus扩展功能-Db静态工具

目录 1 前言 2 使用方法 2.1 Db静态工具拥有的部分方法 2.2 举例 1 前言 在我们的服务层中&#xff0c;有时为了实现一个方法需要引入其它的Mapper层方法&#xff0c;但是&#xff0c;这样可能出现循环依赖。虽然Spring已经给我们解决了简单的循环依赖问题&#xff0c;但是…

【书生·浦语大模型实战营】第4节 课后作业

XTuner 大模型单卡低成本微调实战 0. 课程链接1. 课后作业1.2 进阶作业 0. 课程链接 课程链接&#xff1a;https://github.com/InternLM/tutorial/blob/main/xtuner/README.md 1. 课后作业 构建数据集&#xff0c;使用 XTuner 微调 InternLM-Chat-7B 模型, 让模型学习到它是你…

数字化转型导师坚鹏:证券公司数字化思维升级之道

证券公司数字化思维升级之道 ——数字化思维之六脉神剑 课程背景&#xff1a; 很多证券公司存在以下问题&#xff1a; 不知道数字化转型如何改变思维模式&#xff1f; 不清楚需要建立什么样的数字化思维&#xff1f; 不知道如何开展数字化思维提升工作&#xff1f; 课…

Redis小白入门教程

Redis入门教程 1. Redis入门1.1 Redis简介1.2 Redis服务启动与停止1.2.1 Redis下载1.2.2 服务启动命令1.2.3 客户端连接命令1.2.4 修改Redis配置文件 2. Redis数据类型2.1 五种常用数据类型介绍2.1.1 字符串操作命令2.1.2 哈希操作命令2.1.3 列表操作命令2.1.4 集合操作命令2.1…

JWT的原理与隐患

什么是JWT JWT通常由三部分组成&#xff1a;头信息&#xff08;header&#xff09;, 消息体&#xff08;payload&#xff09;和签名&#xff08;signature&#xff09;。 头信息指定了该JWT使用的签名算法 header {"alg":"HS256","typ":"…

9.8分割等和子集(LC416-M)

算法&#xff1a; 可以转换为背包问题&#xff1a; 一个商品如果可以重复多次放入是完全背包&#xff0c;而只能放入一次是01背包&#xff0c;写法还是不一样的。 要明确本题中我们要使用的是01背包&#xff0c;因为元素我们只能用一次。 只有确定了如下四点&#xff0c;才能…

C++_程序流程结构_循环结构_while

while循环结构 作用 满足循环条件&#xff0c;执行循环语句 语法 while (循环条件&#xff09;{循环语句}解释 只要循环条件的结果为真&#xff0c;就执行循环语句 流程图 示例 注意 在执行循环语句时候&#xff0c;程序必须提供跳出循环的出口&#xff0c;否则出现死循…

B083-SpringCloud-eureka ribbon feign hystrix

目录 eureka基础项目准备注册中心的搭建生产者注册到eureka消费者注册到eureka并通过eureka调用生产者eureka集群 服务提供者集群集群以后消费者调用服务的问题ribbon消费者使用ribbon负载均衡赋值负载均衡策略负载均衡优化 feignHystrixHystrix概述Ribbon搭配Hystrix降级处理F…

springboot+vue学生信息管理系统学籍 成绩 选课 奖惩,奖学金缴费idea maven mysql

技术栈 ide工具&#xff1a;IDEA 或者eclipse 编程语言: java 数据库: mysql5.7 框架&#xff1a;ssmspringboot都有 前端&#xff1a;vue.jsElementUI 详细技术&#xff1a;springbootSSMvueMYSQLMAVEN 数据库工具&#xff1a;Navicat/SQLyog都可以学生信息管理系统主要实现角…

★【递归】【链表】Leetcode 21. 合并两个有序链表

★【递归】【链表】Leetcode 21. 合并两个有序链表 解法1 &#xff1a;递归链表 简直是好题啊好题多做做 ---------------&#x1f388;&#x1f388;题目链接&#x1f388;&#x1f388;------------------- 解法1 &#xff1a;递归链表 简直是好题啊好题多做做 >>>…

解决Excel客户端中的Copilot灰色不可用

很多小伙伴已经用上了office套件中的copilot功能 Copilot for Microsoft 365账号介绍与相关问题的解答 Copilot for Microsoft 365账号登录指南 Copilot for Microsoft 365功能使用指南 问题发现 大部分人使用的都是Word和PowerPoint功能&#xff0c;但是也有部分小伙伴使…

(学习日记)2024.03.03:UCOSIII第五节:常用汇编指令+OS初始化+启动任务+任务切换

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

力扣hot5---双指针

题目&#xff1a; 解决方案&#xff1a;双指针 指针 i 指向最左侧&#xff0c;指针 j 指向最右侧。此时在宽度上达到了最大值&#xff0c;那么哪个柱子更矮&#xff0c;哪个柱子向内部移动&#xff0c;知道 i 与 j 相遇。为什么呢&#xff1f; 如果哪个哪个柱子更矮&#xff0c…