17-前端Web实战(Tlias案例-员工管理)
在昨天的课程中,我们完成了部门管理的功能开发。 那今天呢,我们要来开发的是员工管理的功能。 我们来看一下员工管理的页面原型:

包括如下几个部分:
- 条件分页查询
- 新增员工
- 修改员工
- 删除员工
除了员工管理的功能以外,还涉及到登录、退出等功能。
条件分页查询
介绍
在页面原型中,我们可以看到在查询员工信息列表时,既需要根据条件动态查询,还需要对查询的结果进行分页处理。

那接下来,我们在制作这个页面时,将先完成基本的页面布局,再完成数据的动态加载。 而页面布局,又分为4个部分,分别为:
- 搜索栏
- 按钮
- 数据展示表格
- 分页条
页面布局
搜索表单

💡 你是一名前端开发工程师,现在基于Vue3+ElementPlus开发了一个前端项目。现在要制作一个员工管理的页面 /emp/index.vue,先生成顶部的搜索栏。 具体的要求如下:
- 搜索栏,包括如下三个表单项,分别为: a. 姓名,文本输入框,输入框提示为:请输入员工姓名 b. 性别,下拉框,选项有男 、女。 如果选择男,提交的值为1;如果选择女,提交的值为2。默认提示信息:请选择 c. 入职日期,日期范围时间选择组件,包括开始时间,和 结束时间,默认第一个时间选择框提示信息:开始日期,第二个时间选择框提示信息:结束日期;展示的值与提交值都为 yyyy-MM-dd 格式
- 搜索栏之后,有两个按钮,一个是查询,一个是清空。
- 搜索栏中的三个表单项与后面的两个按钮,都显示在一行里。
根据AI生成的代码,我们在其基础上做一个改造,具体代码如下:
1). 在 views/emp/index.vue 中的 <script setup></script> 中增加如下js代码:
import { ref} from 'vue'
const searchEmp = ref({
name: '',
gender: '',
date: [],
begin: '',
end: ''
})
const handleSearch = () => {
// 处理查询逻辑
console.log('Search:', searchEmp.value)
}
const handleReset = () => {
// 清空表单
searchEmp.value = {
name: '',
gender: '',
date: []
}
handleSearch()
}2). 在 views/emp/index.vue 中的 <template></template> 中增加如下js代码:
<h1>员工管理</h1> <br>
{{ searchEmp }}
<el-form :inline="true" :model="searchEmp">
<el-form-item label="姓名">
<el-input v-model="searchEmp.name" placeholder="请输入员工姓名"></el-input>
</el-form-item>
<el-form-item label="性别">
<el-select v-model="searchEmp.gender" placeholder="请选择">
<el-option label="男" value="1"></el-option>
<el-option label="女" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="入职日期">
<el-date-picker
v-model="searchEmp.date"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">清空</el-button>
</el-form-item>
</el-form>注意:在element-plus中,提供的日期选择组件,最终提交的值的格式,如果为 2024-02-01 这种格式,需要设置value-format属性为 YYYY-MM-DD。
我们在测试的时候,为了能够看到搜索表单采集到的数据,可以直接在通过插值表达式 在表单上方,将搜索表单项的值直接输出出来。
打开浏览器,我们可以看到如下页面效果:

通过测试,我们可以看到,输入的入职日期的搜索条件,用的是element-plus的日期范围组件,开始时间和结束时间,两个值,是封装到了一个数组中。
而通过接口接口文档,我们可以可以看到最终在查询员工列表数据时,搜索条件中,入职日期的开始时间 和 结束时间,是两个值,一个是begin开始时间,一个是end结束时间。

那么此时,我们就需要将searchEmp对象中的date这个数组的值,解析出来,复制给begin和end。而且,一旦date的值发生变化,我们就需要重新计算begin和end这两个时间,把数组中的第一个元素,赋值给begin;第二个元素,赋值给end。

而要实现这个效果,我们就需要用到Vue3中提供的 watch 侦听。
watch侦听
作用:侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。
用法:
- 导入watch函数
- 执行watch函数,传入要侦听的响应式数据源(ref对象)和回调函数;

- 侦听对象的单个属性

- 侦听对象的全部属性

第三个可选参数,常见两个选项:
- deep(boolean):是否深度侦听,默认浅层侦听。
- immediate(boolean):是否在侦听创建时立即触发回调函数。
介绍完了watch侦听的基本语法之后,我们再来完成搜索栏中员工入职日期的开始时间和结束时间的处理。 我们要做到,在输入的时间发生变化时,自动为searchEmp中的begin,end两个属性赋值。
具体代码如下,在 views/emp/index.vue 文件中的 <script></script> 中增加watch监听:
import { ref, watch } from 'vue'
const searchEmp = ref({
name: '',
gender: '',
date: [],
begin: '',
end: ''
})
//侦听searchEmp中的date属性
watch(
() => searchEmp.value.date,
(newValue, oldValue) => {
if(newValue.length == 2){
searchEmp.value.begin = newValue[0]
searchEmp.value.end = newValue[1]
}else {
searchEmp.value.begin = ''
searchEmp.value.end = ''
}
}
)
const handleSearch = () => {
// 处理查询逻辑
console.log('Search:', searchEmp.value)
}
const handleReset = () => {
// 清空表单
searchEmp.value = {
name: '',
gender: '',
date: [],
begin: '',
end: ''
}
handleSearch()
}表格展示
接下来,我们再来制作这个表格。通过页面原型,我们可以看到表格的展示内容及展示形式:

💡 AI提示词(prompt): 再制作一个表格,要求表格有边框线,具体包含以下几列,每一列的要求如下:
- 第一列,是一个复选框。
- 第二列,姓名。
- 第三列,头像,展示的是一张图片,注意控制图片的大小展示问题。
- 第四列,部门名称。
- 第五列,职位。1 展示为 班主任;2 展示为 讲师;3 展示为 学工主管; 4 展示为 教研主管; 5 展示为 咨询师; 其他展示为其他;
- 第六列,入职日期。
- 第七列,最后操作时间。
- 第八列,操作。 里面包含两个按钮,一个是编辑,一个是删除。 上述几列用来展示如下json格式的数据: [ { "id": 1, "username": "jinyong", "password": "123456", "name": "金庸", "gender": 1, "image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-02-00-27-53B.jpg", "job": 2, "salary": 8000, "entryDate": "2015-01-01", "deptId": 2, "deptName": "教研部", "createTime": "2022-09-01T23:06:30", "updateTime": "2022-09-02T00:29:04" } ] 在表格的下方,再制作一个分页组件,在分页组件中要展示每页展示记录数、上一页 、页码、下一页、跳转到页码 这几项信息。
最终AI生成的代码,调整后如下所示(绿色背景部分为新添加部分):
<script setup>
import { ref, watch } from 'vue'
const searchEmp = ref({
name: '',
gender: '',
date: [],
begin: '',
end: ''
})
//侦听searchEmp中的date属性
watch(
() => searchEmp.value.date,
(newValue, oldValue) => {
if(newValue.length == 2){
searchEmp.value.begin = newValue[0]
searchEmp.value.end = newValue[1]
}else {
searchEmp.value.begin = ''
searchEmp.value.end = ''
}
}
)
const handleSearch = () => {
// 处理查询逻辑
console.log('Search:', searchEmp.value)
}
const handleReset = () => {
// 清空表单
searchEmp.value = {
name: '',
gender: '',
date: [],
begin: '',
end: ''
}
handleSearch()
}
// 示例数据
const empList = ref([
{
"id": 1,
"username": "jinyong",
"password": "123456",
"name": "金庸",
"gender": 1,
"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-02-00-27-53B.jpg",
"job": 2,
"salary": 8000,
"entryDate": "2015-01-01",
"deptId": 2,
"deptName": "教研部",
"createTime": "2022-09-01T23:06:30",
"updateTime": "2022-09-02T00:29:04"
}
])
// 分页配置
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
// 职位转换函数
const getJobTitle = (job) => {
switch (job) {
case 1:
return '班主任'
case 2:
return '讲师'
case 3:
return '学工主管'
case 4:
return '教研主管'
case 5:
return '咨询师'
default:
return '其他'
}
}
// 分页处理
const handleSizeChange = (val) => {
pageSize.value = val
handleSearch()
}
const handleCurrentChange = (val) => {
currentPage.value = val
handleSearch()
}
// 操作处理
const handleEdit = (id) => {
console.log('Edit:', id)
}
const handleDelete = (id) => {
console.log('Delete:', id)
}
</script>
<template>
<h1>员工管理</h1> <br>
{{ searchEmp }}
<el-form :inline="true" :model="searchEmp">
<el-form-item label="姓名">
<el-input v-model="searchEmp.name" placeholder="请输入员工姓名"></el-input>
</el-form-item>
<el-form-item label="性别">
<el-select v-model="searchEmp.gender" placeholder="请选择">
<el-option label="男" value="1"></el-option>
<el-option label="女" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="入职日期">
<el-date-picker
v-model="searchEmp.date"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">清空</el-button>
</el-form-item>
</el-form>
<el-button type="primary" @click=""> + 新增员工</el-button>
<el-button type="danger" @click=""> - 批量删除</el-button>
<br><br>
<!-- 表格 -->
<el-table :data="empList" border style="width: 100%">
<el-table-column type="selection" width="55" align="center"></el-table-column>
<el-table-column prop="name" label="姓名" width="120" align="center"></el-table-column>
<el-table-column label="性别" width="170" align="center">
<template #default="scope" >
{{ scope.row.gender == 1 ? '男' : '女' }}
</template>
</el-table-column>
<el-table-column label="头像" width="170" align="center">
<template #default="scope" >
<img :src="scope.row.image" alt="Avatar" class="avatar" />
</template>
</el-table-column>
<el-table-column prop="deptName" label="部门名称" width="170" align="center"></el-table-column>
<el-table-column label="职位" width="120" align="center">
<template #default="scope">
{{ getJobTitle(scope.row.job) }}
</template>
</el-table-column>
<el-table-column prop="entryDate" label="入职日期" width="180" align="center"></el-table-column>
<el-table-column prop="updateTime" label="最后操作时间" width="210" align="center"></el-table-column>
<el-table-column label="操作" fixed="right" align="center">
<template #default="scope">
<el-button size="small" type="primary" @click="handleEdit(scope.row.id)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<br>
<!-- 分页 -->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
>
</el-pagination>
</template>
<style scoped>
.avatar {
height: 40px;
}
</style>代码编写完毕后,我们打开浏览器会看到如下效果:

我们可以看到,图片、职位字段都正常展示出来了。
页面交互
基本的页面布局完成之后,接下来,我们再来完成页面的交互效果,具体效果有如下三项:
- 页面加载完毕后,查询员工信息列表。
- 点击查询按钮,查询员工信息列表。
- 当页码、每页展示记录数发生变化时,查询员工信息列表。
那么要想实现上述的效果,我们就需要在Vue的钩子函数 onMounted 中,调用查询方法 handleSearch,然后在 handleSearch 方法中发起异步请求,请求服务端。
当页码、每页展示的记录数发生变化时,会触发 handleSizeChange、handleCurrentChange 方法,然后在这两个方法中再调用 handleSearch 方法,发起异步请求,请求服务端即可。
具体代码实现如下:
1). 在 src/api 目录下再定义一个 emp.js
我们可以一次性将增删改查相关的api操作的函数都定义好。
import request from '@/utils/request'
//查询员工列表
export const queryPageApi = (name,gender,begin,end,page,pageSize) => request.get(`/emps?name=${name}&gender=${gender}&begin=${begin}&end=${end}&page=${page}&pageSize=${pageSize}`)
//添加员工
export const addEmpApi = (data) => request.post('/emps', data)
//根据ID查询员工信息
export const queryInfoApi = (id) => request.get(`/emps/${id}`)
//修改员工信息
export const updateEmpApi = (data) => request.put('/emps', data)
//删除员工
export const deleteEmpApi = (ids) => request.delete(`/emps?ids=${ids}`)2). 在 src/views/emp/index.vue 文件中完成页面交互实现
<script setup>
import { ref, watch, onMounted } from 'vue'
import { queryPageApi } from '@/api/emp'
const searchEmp = ref({
name: '',
gender: '',
date: [],
begin: '',
end: ''
})
//侦听searchEmp中的date属性
watch(
() => searchEmp.value.date,
(newValue, oldValue) => {
if(newValue.length == 2){
searchEmp.value.begin = newValue[0]
searchEmp.value.end = newValue[1]
}else {
searchEmp.value.begin = ''
searchEmp.value.end = ''
}
}
)
onMounted(() => {
handleSearch()
})
//查询员工
const handleSearch = async () => {
console.log('Search:', searchEmp.value)
const result = await queryPageApi(searchEmp.value.name, searchEmp.value.gender, searchEmp.value.begin, searchEmp.value.end, currentPage.value, pageSize.value);
if(result.code){
empList.value = result.data.rows
total.value = result.data.total
}
}
const handleReset = () => {
// 清空表单
searchEmp.value = {
name: '',
gender: '',
date: [],
begin: '',
end: ''
}
handleSearch()
}
// 示例数据
const empList = ref([])
// 分页配置
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
// 职位转换函数
const getJobTitle = (job) => {
switch (job) {
case 1:
return '班主任'
case 2:
return '讲师'
case 3:
return '学工主管'
case 4:
return '教研主管'
case 5:
return '咨询师'
default:
return '其他'
}
}
// 分页处理
const handleSizeChange = (val) => {
pageSize.value = val
handleSearch()
}
const handleCurrentChange = (val) => {
currentPage.value = val
handleSearch()
}
// 操作处理
const handleEdit = (row) => {
console.log('Edit:', row)
}
const handleDelete = (row) => {
console.log('Delete:', row)
}
</script>
<template>
<h1>员工管理</h1> <br>
{{ searchEmp }}
<el-form :inline="true" :model="searchEmp">
<el-form-item label="姓名">
<el-input v-model="searchEmp.name" placeholder="请输入员工姓名"></el-input>
</el-form-item>
<el-form-item label="性别">
<el-select v-model="searchEmp.gender" placeholder="请选择">
<el-option label="男" value="1"></el-option>
<el-option label="女" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="入职日期">
<el-date-picker
v-model="searchEmp.date"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">清空</el-button>
</el-form-item>
</el-form>
<el-button type="primary" @click=""> + 新增员工</el-button>
<el-button type="danger" @click=""> - 批量删除</el-button>
<br><br>
<!-- 表格 -->
<el-table :data="empList" border style="width: 100%">
<el-table-column type="selection" width="55" align="center"></el-table-column>
<el-table-column prop="name" label="姓名" width="120" align="center"></el-table-column>
<el-table-column label="性别" width="80" align="center">
<template #default="scope" >
{{ scope.row.gender == 1 ? '男' : '女' }}
</template>
</el-table-column>
<el-table-column label="头像" width="170" align="center">
<template #default="scope" >
<img :src="scope.row.image" alt="Avatar" class="avatar" />
</template>
</el-table-column>
<el-table-column prop="deptName" label="部门名称" width="170" align="center"></el-table-column>
<el-table-column label="职位" width="120" align="center">
<template #default="scope">
{{ getJobTitle(scope.row.job) }}
</template>
</el-table-column>
<el-table-column prop="entryDate" label="入职日期" width="180" align="center"></el-table-column>
<el-table-column prop="updateTime" label="最后操作时间" width="210" align="center"></el-table-column>
<el-table-column label="操作" fixed="right" align="center">
<template #default="scope">
<el-button size="small" type="primary" @click="handleEdit(scope.row.id)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<br>
<!-- 分页 -->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
>
</el-pagination>
</template>
<style scoped>
.avatar {
height: 40px;
}
</style>代码编写完成之后,我们可以打开浏览器测试一下:

新增员工
页面布局
需求

从页面原型中,我们可以看到,新增员工信息的表单包含两个部分,分别为:
- 员工的基本信息
- 员工的工作经历信息
基本布局实现
接下来,我们也可以基于AI来帮我们完成页面的整体布局。
💡 AI提示词(prompt): 接下来,要完成的是新增员工的功能,当点击页面上的 “新增员工” 按钮后,要弹出一个Dialog 对框框,在对话框中要承载一个用户信息的form表单。具体的要求如下:
该对话框中的form表单在新增 和 修改员工时,使用的是同一个表单。 所以在新增员工时,表单标题显示为 “新增员工”, 修改员工时,表单的标题显示为 “修改员工” 。
表单分为两个部分展示,一个部分是员工的基本信息,另外一部分为员工的工作经历信息 。
员工的基本信息,包括以下几项,要求表单中的 每一行展示两个表单项:
- 第一行:展示两个表单项,为用户名 和 姓名。 两个表单项均为输入框,用户名的输入框提示信息 “请输入员工用户名,2-20个字”; 姓名输入框提示信息 “请输入员工姓名,2-10个字”
- 第二行:展示两个表单项,性别 和 手机号表单项。 性别 为下拉框,展示 男 、女,男提交的值为1,女提交的值为2; 手机号为输入框,提示信息为 “请输入员工手机号”
- 第三行:展示两个表单项,职位 和 薪资两个表单项。 职位 为下拉框,展示 班主任(提交值为1)、讲师(提交值为2)、学工主管(提交值为3)、教研主管(提交值为4)、咨询师(提交值为5); 薪资为 输入框,提示信息 “请输入员工薪资”
- 第四行:展示两个表单项,所属部门 和 入职日期表单项。 所属部门为下拉框,先写死两个测试数据。 入职日期为日期选择组件,可以选择日期,展示和提交的值格式都类似于这样 2024-09-01
- 第五行:展示一个表单项,头像,这一列有点特殊,为图片上传组件,上传完图片后,要展示出预览图。
在两个部分之间,添加一个按钮,按钮名为 “添加工作经历”。
员工的工作经历信息,包括以下几项,在一行中要展示这三个表单项:
- 时间:包括开始时间和结束时间,是一个日期范围组件。
- 公司: 是一个输入框, 提示信息 "请输入公司名称"。
- 职位: 是一个输入框, 提示信息 "请输入职位"。 时间 , 公司, 职位 这三个表单项要在一行中显示。
在表单的下方,有两个按钮,一个是保存,一个是取消。
先不考虑表单校验。
上述表单,最后对应的数据模型为: { "image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-03-07-37-38222.jpg", "username": "linpingzhi", "name": "林平之", "gender": 1, "job": 1, "entryDate": "2022-09-18", "deptId": 1, "phone": "18809091234", "salary": 8000, "exprList": [ { "company": "百度科技股份有限公司", "job": "java开发", "begin": "2012-07-01", "end": "2019-03-03" }, { "company": "阿里巴巴科技股份有限公司", "job": "架构师", "begin": "2019-03-15", "end": "2023-03-01" } ] }
结合AI生成的表单布局代码,最终调整后的代码结构如下:
在 src/views/emp/index.vue 中增加如下代码(绿色背景部分):
<script setup>
import { ref, watch, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { queryPageApi } from '@/api/emp'
const searchEmp = ref({
name: '',
gender: '',
date: [],
begin: '',
end: ''
})
//侦听searchEmp中的date属性
watch(
() => searchEmp.value.date,
(newValue, oldValue) => {
if(newValue.length == 2){
searchEmp.value.begin = newValue[0]
searchEmp.value.end = newValue[1]
}else {
searchEmp.value.begin = ''
searchEmp.value.end = ''
}
}
)
onMounted(() => {
handleSearch()
})
//查询员工
const handleSearch = async () => {
console.log('Search:', searchEmp.value)
const result = await queryPageApi(searchEmp.value.name, searchEmp.value.gender, searchEmp.value.begin, searchEmp.value.end, currentPage.value, pageSize.value);
if(result.code){
empList.value = result.data.rows
total.value = result.data.total
}
}
const handleReset = () => {
// 清空表单
searchEmp.value = {
name: '',
gender: '',
date: [],
begin: '',
end: ''
}
handleSearch()
}
// 示例数据
const empList = ref([])
// 分页配置
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
// 职位转换函数
const getJobTitle = (job) => {
switch (job) {
case 1:
return '班主任'
case 2:
return '讲师'
case 3:
return '学工主管'
case 4:
return '教研主管'
case 5:
return '咨询师'
default:
return '其他'
}
}
// 分页处理
const handleSizeChange = (val) => {
pageSize.value = val
handleSearch()
}
const handleCurrentChange = (val) => {
currentPage.value = val
handleSearch()
}
// 操作处理
const handleEdit = (row) => {
console.log('Edit:', row)
}
const handleDelete = (row) => {
console.log('Delete:', row)
}
//新增员工
const addEmp = () => {
dialogVisible.value = true
dialogTitle.value = '新增员工'
}
//新增/修改表单
const employeeFormRef = ref(null)
const employee = ref({
username: '',
name: '',
gender: '',
phone: '',
job: '',
salary: '',
deptId: '',
entryDate: '',
image: '',
exprList: []
})
// 控制弹窗
const dialogVisible = ref(false)
const dialogTitle = ref('新增员工')
//文件上传
// 图片上传成功后触发
const handleAvatarSuccess = (response,uploadFile) => {
employee.value.image = response.data
}
// 文件上传之前触发
const beforeAvatarUpload = (rawFile) => {
if (rawFile.type !== 'image/jpeg' && rawFile.type !== 'image/png') {
ElMessage.error('只支持上传图片')
return false
} else if (rawFile.size / 1024 / 1024 > 10) {
ElMessage.error('只能上传10M以内图片')
return false
}
return true
}
</script>
<template>
<h1>员工管理</h1> <br>
<el-form :inline="true" :model="searchEmp">
<el-form-item label="姓名">
<el-input v-model="searchEmp.name" placeholder="请输入员工姓名"></el-input>
</el-form-item>
<el-form-item label="性别">
<el-select v-model="searchEmp.gender" placeholder="请选择">
<el-option label="男" value="1"></el-option>
<el-option label="女" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="入职日期">
<el-date-picker
v-model="searchEmp.date"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">清空</el-button>
</el-form-item>
</el-form>
<el-button type="primary" @click="addEmp"> + 新增员工</el-button>
<el-button type="danger" @click=""> - 批量删除</el-button>
<br><br>
<!-- 表格 -->
<el-table :data="empList" border style="width: 100%">
<el-table-column type="selection" width="55" align="center"></el-table-column>
<el-table-column prop="name" label="姓名" width="120" align="center"></el-table-column>
<el-table-column label="性别" width="80" align="center">
<template #default="scope" >
{{ scope.row.gender == 1 ? '男' : '女' }}
</template>
</el-table-column>
<el-table-column label="头像" width="170" align="center">
<template #default="scope" >
<img :src="scope.row.image" alt="Avatar" class="avatar" />
</template>
</el-table-column>
<el-table-column prop="deptName" label="部门名称" width="170" align="center"></el-table-column>
<el-table-column label="职位" width="120" align="center">
<template #default="scope">
{{ getJobTitle(scope.row.job) }}
</template>
</el-table-column>
<el-table-column prop="entryDate" label="入职日期" width="180" align="center"></el-table-column>
<el-table-column prop="updateTime" label="最后操作时间" width="210" align="center"></el-table-column>
<el-table-column label="操作" fixed="right" align="center">
<template #default="scope">
<el-button size="small" type="primary" @click="handleEdit(scope.row.id)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<br>
<!-- 分页 -->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
>
</el-pagination>
<!-- 新增/修改员工的对话框 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle">
<el-form ref="employeeFormRef" :model="employee" label-width="80px">
<!-- 基本信息 -->
<!-- 第一行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="用户名">
<el-input v-model="employee.username" placeholder="请输入员工用户名,2-20个字"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="姓名">
<el-input v-model="employee.name" placeholder="请输入员工姓名,2-10个字"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="性别">
<el-select v-model="employee.gender" placeholder="请选择性别" style="width: 100%;">
<el-option label="男" value="1"></el-option>
<el-option label="女" value="2"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号">
<el-input v-model="employee.phone" placeholder="请输入员工手机号"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="职位">
<el-select v-model="employee.job" placeholder="请选择职位" style="width: 100%;">
<el-option label="班主任" value="1"></el-option>
<el-option label="讲师" value="2"></el-option>
<el-option label="学工主管" value="3"></el-option>
<el-option label="教研主管" value="4"></el-option>
<el-option label="咨询师" value="5"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="薪资">
<el-input v-model="employee.salary" placeholder="请输入员工薪资"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第四行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="所属部门">
<el-select v-model="employee.deptId" placeholder="请选择部门" style="width: 100%;">
<el-option label="研发部" value="1"></el-option>
<el-option label="市场部" value="2"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入职日期">
<el-date-picker v-model="employee.entryDate" type="date" style="width: 100%;" placeholder="选择日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD"></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<!-- 第五行 -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="头像">
<el-upload
class="avatar-uploader"
action="/api/upload"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="employee.image" :src="employee.image" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form-item>
</el-col>
</el-row>
<!-- 工作经历 -->
<!-- 第六行 -->
<el-row :gutter="10">
<el-col :span="24">
<el-form-item label="工作经历">
<el-button type="success" size="small" @click="">+ 添加工作经历</el-button>
</el-form-item>
</el-col>
</el-row>
<!-- 第七行 ... 工作经历 -->
<el-row :gutter="3">
<el-col :span="10">
<el-form-item size="small" label="时间" label-width="80px">
<el-date-picker type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" ></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item size="small" label="公司" label-width="60px">
<el-input placeholder="请输入公司名称"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item size="small" label="职位" label-width="60px">
<el-input placeholder="请输入职位"></el-input>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item size="small" label-width="0px">
<el-button type="danger" >- 删除</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 底部按钮 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="">保存</el-button>
</span>
</template>
</el-dialog>
</template>
<style scoped>
.avatar {
height: 40px;
}
.avatar-uploader .avatar {
width: 78px;
height: 78px;
display: block;
}
.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: 78px;
height: 78px;
text-align: center;
/* 添加灰色的虚线边框 */
border: 1px dashed var(--el-border-color);
}
</style>代码编写完毕后,打开浏览器看到最终效果如下:

这样最基本的布局就有了,接下来,我们需要对其进行进一步的优化。
页面布局优化
表单中的数据动态展示 。比如:所属部门、职位、性别,这些源数据我们可以定义在js代码中,在页面中基于v-for指令,遍历展示出来,便于项目的统一维护处理。
优化后的页面内容如下:
<script setup>
import { ref, watch, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { queryPageApi } from '@/api/emp'
import { queryAllApi as queryAllDeptApi } from '@/api/dept'
//职位列表数据
const jobs = ref([{ name: '班主任', value: 1 },{ name: '讲师', value: 2 },{ name: '学工主管', value: 3 },{ name: '教研主管', value: 4 },{ name: '咨询师', value: 5 },{ name: '其他', value: 6 }])
//性别列表数据
const genders = ref([{ name: '男', value: 1 }, { name: '女', value: 2 }])
//部门列表数据
const deptList = ref([])
const searchEmp = ref({
name: '',
gender: '',
date: [],
begin: '',
end: ''
})
//侦听searchEmp中的date属性
watch(
() => searchEmp.value.date,
(newValue, oldValue) => {
if(newValue.length == 2){
searchEmp.value.begin = newValue[0]
searchEmp.value.end = newValue[1]
}else {
searchEmp.value.begin = ''
searchEmp.value.end = ''
}
}
)
onMounted(async () => {
handleSearch()
//加载所有部门数据
const result = await queryAllDeptApi();
if(result.code){
deptList.value = result.data
}
})
//查询员工
const handleSearch = async () => {
console.log('Search:', searchEmp.value)
const result = await queryPageApi(searchEmp.value.name, searchEmp.value.gender, searchEmp.value.begin, searchEmp.value.end, currentPage.value, pageSize.value);
if(result.code){
empList.value = result.data.rows
total.value = result.data.total
}
}
const handleReset = () => {
// 清空表单
searchEmp.value = {
name: '',
gender: '',
date: [],
begin: '',
end: ''
}
handleSearch()
}
// 示例数据
const empList = ref([])
// 分页配置
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
// 职位转换函数
const getJobTitle = (job) => {
switch (job) {
case 1:
return '班主任'
case 2:
return '讲师'
case 3:
return '学工主管'
case 4:
return '教研主管'
case 5:
return '咨询师'
default:
return '其他'
}
}
// 分页处理
const handleSizeChange = (val) => {
pageSize.value = val
handleSearch()
}
const handleCurrentChange = (val) => {
currentPage.value = val
handleSearch()
}
// 操作处理
const handleEdit = (row) => {
console.log('Edit:', row)
}
const handleDelete = (row) => {
console.log('Delete:', row)
}
//新增员工
const addEmp = () => {
dialogVisible.value = true
dialogTitle.value = '新增员工'
}
//新增/修改表单
const employeeFormRef = ref(null)
const employee = ref({
username: '',
name: '',
gender: '',
phone: '',
job: '',
salary: '',
deptId: '',
entryDate: '',
image: '',
exprList: []
})
// 控制弹窗
const dialogVisible = ref(false)
const dialogTitle = ref('新增员工')
//文件上传
// 图片上传成功后触发
const handleAvatarSuccess = (response,uploadFile) => {
employee.value.image = response.data
}
// 文件上传之前触发
const beforeAvatarUpload = (rawFile) => {
if (rawFile.type !== 'image/jpeg' && rawFile.type !== 'image/png') {
ElMessage.error('只支持上传图片')
return false
} else if (rawFile.size / 1024 / 1024 > 10) {
ElMessage.error('只能上传10M以内图片')
return false
}
return true
}
</script>
<template>
<h1>员工管理</h1> <br>
<el-form :inline="true" :model="searchEmp">
<el-form-item label="姓名">
<el-input v-model="searchEmp.name" placeholder="请输入员工姓名"></el-input>
</el-form-item>
<el-form-item label="性别">
<el-select v-model="searchEmp.gender" placeholder="请选择">
<el-option label="男" value="1"></el-option>
<el-option label="女" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="入职日期">
<el-date-picker
v-model="searchEmp.date"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">清空</el-button>
</el-form-item>
</el-form>
<el-button type="primary" @click="addEmp"> + 新增员工</el-button>
<el-button type="danger" @click=""> - 批量删除</el-button>
<br><br>
<!-- 表格 -->
<el-table :data="empList" border style="width: 100%">
<el-table-column type="selection" width="55" align="center"></el-table-column>
<el-table-column prop="name" label="姓名" width="120" align="center"></el-table-column>
<el-table-column label="性别" width="80" align="center">
<template #default="scope" >
{{ scope.row.gender == 1 ? '男' : '女' }}
</template>
</el-table-column>
<el-table-column label="头像" width="170" align="center">
<template #default="scope" >
<img :src="scope.row.image" alt="Avatar" class="avatar" />
</template>
</el-table-column>
<el-table-column prop="deptName" label="部门名称" width="170" align="center"></el-table-column>
<el-table-column label="职位" width="120" align="center">
<template #default="scope">
{{ getJobTitle(scope.row.job) }}
</template>
</el-table-column>
<el-table-column prop="entryDate" label="入职日期" width="180" align="center"></el-table-column>
<el-table-column prop="updateTime" label="最后操作时间" width="210" align="center"></el-table-column>
<el-table-column label="操作" fixed="right" align="center">
<template #default="scope">
<el-button size="small" type="primary" @click="handleEdit(scope.row.id)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<br>
<!-- 分页 -->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
>
</el-pagination>
<!-- 新增/修改员工的对话框 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle">
<el-form ref="employeeFormRef" :model="employee" label-width="80px">
<!-- 基本信息 -->
<!-- 第一行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="用户名">
<el-input v-model="employee.username" placeholder="请输入员工用户名,2-20个字"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="姓名">
<el-input v-model="employee.name" placeholder="请输入员工姓名,2-10个字"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="性别">
<el-select v-model="employee.gender" placeholder="请选择性别" style="width: 100%;">
<el-option v-for="gender in genders" :key="gender.name" :label="gender.name" :value="gender.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号">
<el-input v-model="employee.phone" placeholder="请输入员工手机号"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="职位">
<el-select v-model="employee.job" placeholder="请选择职位" style="width: 100%;">
<el-option v-for="job in jobs" :key="job.name" :label="job.name" :value="job.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="薪资">
<el-input v-model="employee.salary" placeholder="请输入员工薪资"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第四行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="所属部门">
<el-select v-model="employee.deptId" placeholder="请选择部门" style="width: 100%;">
<el-option v-for="dept in deptList" :key="dept.id" :label="dept.name" :value="dept.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入职日期">
<el-date-picker v-model="employee.entryDate" type="date" style="width: 100%;" placeholder="选择日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD"></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<!-- 第五行 -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="头像">
<el-upload
class="avatar-uploader"
action="/api/upload"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="employee.image" :src="employee.image" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form-item>
</el-col>
</el-row>
<!-- 工作经历 -->
<!-- 第六行 -->
<el-row :gutter="10">
<el-col :span="24">
<el-form-item label="工作经历">
<el-button type="success" size="small" @click="">+ 添加工作经历</el-button>
</el-form-item>
</el-col>
</el-row>
<!-- 第七行 ... 工作经历 -->
<el-row :gutter="3">
<el-col :span="10">
<el-form-item size="small" label="时间" label-width="80px">
<el-date-picker type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" ></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item size="small" label="公司" label-width="60px">
<el-input placeholder="请输入公司名称"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item size="small" label="职位" label-width="60px">
<el-input placeholder="请输入职位"></el-input>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item size="small" label-width="0px">
<el-button type="danger" >- 删除</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 底部按钮 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="">保存</el-button>
</span>
</template>
</el-dialog>
</template>
<style scoped>
.avatar {
height: 40px;
}
.avatar-uploader .avatar {
width: 78px;
height: 78px;
display: block;
}
.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: 78px;
height: 78px;
text-align: center;
/* 添加灰色的虚线边框 */
border: 1px dashed var(--el-border-color);
}
</style>到此呢,页面的基本布局我们就已经完成了。 那接下来呢,我们就可以打开浏览器,看看最终的效果:

添加/删除工作经历
需求
新增员工的基本信息表单已经制作完成了,那接下来,要制作的是员工的工作经历。
点击 "添加工作经历" 按钮后,新添加一条工作经历;点击每一条后面的 "删除" 按钮,需要删除当前工作经历。

员工的过往工作经历可能是多条,点击 "添加员工工作经历" 按钮,如何增加一个条目 ? 点击每一条后面的删除按钮,需要删除当前条件?
- Vue是基于数据驱动视图展示的。
- "添加" 时,我们可以往数组中添加数据。
- "删除" 时,可以删除数组中的元素。
- 一旦数据发生变化,视图中的展示就会发生变化。
实现
1). 在 src/views/emp/index.vue 中的 <script> </script> 中增加如下代码:
//工作经历
//动态添加工作经历 .
const addExprItem = () => {
employee.value.exprList.push({exprDate: [], begin: '', end: '', company: '', job: ''})
}
//动态删除工作经历 .
const delExprItem = (index) => {
employee.value.exprList.splice(index, 1)
}
//监听-employee员工对象中的工作经历数据
watch(employee, (newVal, oldVal) => {
if(employee.value.exprList) {
employee.value.exprList.forEach(expr => {
expr.begin = expr.exprDate[0]
expr.end = expr.exprDate[1]
})
}
}, {deep: true})2). 在 src/views/emp/index.vue 中的 <template> </template> 中的按钮绑定事件,并遍历展示工作经历信息:
<!-- 新增/修改员工的对话框 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle">
<el-form ref="employeeFormRef" :model="employee" label-width="80px">
<!-- 基本信息 -->
<!-- 第一行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="用户名">
<el-input v-model="employee.username" placeholder="请输入员工用户名,2-20个字"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="姓名">
<el-input v-model="employee.name" placeholder="请输入员工姓名,2-10个字"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="性别">
<el-select v-model="employee.gender" placeholder="请选择性别" style="width: 100%;">
<el-option v-for="gender in genders" :key="gender.name" :label="gender.name" :value="gender.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号">
<el-input v-model="employee.phone" placeholder="请输入员工手机号"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="职位">
<el-select v-model="employee.job" placeholder="请选择职位" style="width: 100%;">
<el-option v-for="job in jobs" :key="job.name" :label="job.name" :value="job.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="薪资">
<el-input v-model="employee.salary" placeholder="请输入员工薪资"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第四行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="所属部门">
<el-select v-model="employee.deptId" placeholder="请选择部门" style="width: 100%;">
<el-option v-for="dept in deptList" :key="dept.id" :label="dept.name" :value="dept.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入职日期">
<el-date-picker v-model="employee.entryDate" type="date" style="width: 100%;" placeholder="选择日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD"></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<!-- 第五行 -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="头像">
<el-upload
class="avatar-uploader"
action="/api/upload"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="employee.image" :src="employee.image" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form-item>
</el-col>
</el-row>
<!-- 工作经历 -->
<!-- 第六行 -->
<el-row :gutter="10">
<el-col :span="24">
<el-form-item label="工作经历">
<el-button type="success" size="small" @click="addExprItem">+ 添加工作经历</el-button>
</el-form-item>
</el-col>
</el-row>
<!-- 第七行 ... 工作经历 -->
<el-row :gutter="3" v-for="(expr, index) in employee.exprList">
<el-col :span="10">
<el-form-item size="small" label="时间" label-width="80px">
<el-date-picker type="daterange" v-model="expr.exprDate" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" ></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item size="small" label="公司" label-width="60px">
<el-input placeholder="请输入公司名称" v-model="expr.company"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item size="small" label="职位" label-width="60px">
<el-input placeholder="请输入职位" v-model="expr.job"></el-input>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item size="small" label-width="0px">
<el-button type="danger" @click="delExprItem(index)">- 删除</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 底部按钮 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="">保存</el-button>
</span>
</template>
</el-dialog>到目前为止,src/views/emp/index.vue 页面的内容如下所示:
<script setup>
import { ref, watch, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { queryPageApi } from '@/api/emp'
import { queryAllApi as queryAllDeptApi } from '@/api/dept'
//职位列表数据
const jobs = ref([{ name: '班主任', value: 1 },{ name: '讲师', value: 2 },{ name: '学工主管', value: 3 },{ name: '教研主管', value: 4 },{ name: '咨询师', value: 5 },{ name: '其他', value: 6 }])
//性别列表数据
const genders = ref([{ name: '男', value: 1 }, { name: '女', value: 2 }])
//部门列表数据
const deptList = ref([])
const searchEmp = ref({
name: '',
gender: '',
date: [],
begin: '',
end: ''
})
//侦听searchEmp中的date属性
watch(
() => searchEmp.value.date,
(newValue, oldValue) => {
if(newValue.length == 2){
searchEmp.value.begin = newValue[0]
searchEmp.value.end = newValue[1]
}else {
searchEmp.value.begin = ''
searchEmp.value.end = ''
}
}
)
onMounted(async () => {
handleSearch()
//加载所有部门数据
const result = await queryAllDeptApi();
if(result.code){
deptList.value = result.data
}
})
//查询员工
const handleSearch = async () => {
console.log('Search:', searchEmp.value)
const result = await queryPageApi(searchEmp.value.name, searchEmp.value.gender, searchEmp.value.begin, searchEmp.value.end, currentPage.value, pageSize.value);
if(result.code){
empList.value = result.data.rows
total.value = result.data.total
}
}
const handleReset = () => {
// 清空表单
searchEmp.value = {
name: '',
gender: '',
date: [],
begin: '',
end: ''
}
handleSearch()
}
// 示例数据
const empList = ref([])
// 分页配置
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
// 职位转换函数
const getJobTitle = (job) => {
switch (job) {
case 1:
return '班主任'
case 2:
return '讲师'
case 3:
return '学工主管'
case 4:
return '教研主管'
case 5:
return '咨询师'
default:
return '其他'
}
}
// 分页处理
const handleSizeChange = (val) => {
pageSize.value = val
handleSearch()
}
const handleCurrentChange = (val) => {
currentPage.value = val
handleSearch()
}
// 操作处理
const handleEdit = (row) => {
console.log('Edit:', row)
}
const handleDelete = (row) => {
console.log('Delete:', row)
}
//新增员工
const addEmp = () => {
dialogVisible.value = true
dialogTitle.value = '新增员工'
}
//新增/修改表单
const employeeFormRef = ref(null)
const employee = ref({
username: '',
name: '',
gender: '',
phone: '',
job: '',
salary: '',
deptId: '',
entryDate: '',
image: '',
exprList: []
})
// 控制弹窗
const dialogVisible = ref(false)
const dialogTitle = ref('新增员工')
//文件上传
// 图片上传成功后触发
const handleAvatarSuccess = (response,uploadFile) => {
employee.value.image = response.data
}
// 文件上传之前触发
const beforeAvatarUpload = (rawFile) => {
if (rawFile.type !== 'image/jpeg' && rawFile.type !== 'image/png') {
ElMessage.error('只支持上传图片')
return false
} else if (rawFile.size / 1024 / 1024 > 10) {
ElMessage.error('只能上传10M以内图片')
return false
}
return true
}
//工作经历
//动态添加工作经历 .
const addExprItem = () => {
employee.value.exprList.push({exprDate: [], begin: '', end: '', company: '', job: ''})
}
//动态删除工作经历 .
const delExprItem = (index) => {
employee.value.exprList.splice(index, 1)
}
//监听-employee员工对象中的工作经历数据
watch(employee, (newVal, oldVal) => {
if(employee.value.exprList) {
employee.value.exprList.forEach(expr => {
expr.begin = expr.exprDate[0]
expr.end = expr.exprDate[1]
})
}
}, {deep: true})
</script>
<template>
<h1>员工管理</h1> <br>
<el-form :inline="true" :model="searchEmp">
<el-form-item label="姓名">
<el-input v-model="searchEmp.name" placeholder="请输入员工姓名"></el-input>
</el-form-item>
<el-form-item label="性别">
<el-select v-model="searchEmp.gender" placeholder="请选择">
<el-option label="男" value="1"></el-option>
<el-option label="女" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="入职日期">
<el-date-picker
v-model="searchEmp.date"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">清空</el-button>
</el-form-item>
</el-form>
<el-button type="primary" @click="addEmp"> + 新增员工</el-button>
<el-button type="danger" @click=""> - 批量删除</el-button>
<br><br>
<!-- 表格 -->
<el-table :data="empList" border style="width: 100%">
<el-table-column type="selection" width="55" align="center"></el-table-column>
<el-table-column prop="name" label="姓名" width="120" align="center"></el-table-column>
<el-table-column label="性别" width="80" align="center">
<template #default="scope" >
{{ scope.row.gender == 1 ? '男' : '女' }}
</template>
</el-table-column>
<el-table-column label="头像" width="170" align="center">
<template #default="scope" >
<img :src="scope.row.image" alt="Avatar" class="avatar" />
</template>
</el-table-column>
<el-table-column prop="deptName" label="部门名称" width="170" align="center"></el-table-column>
<el-table-column label="职位" width="120" align="center">
<template #default="scope">
{{ getJobTitle(scope.row.job) }}
</template>
</el-table-column>
<el-table-column prop="entryDate" label="入职日期" width="180" align="center"></el-table-column>
<el-table-column prop="updateTime" label="最后操作时间" width="210" align="center"></el-table-column>
<el-table-column label="操作" fixed="right" align="center">
<template #default="scope">
<el-button size="small" type="primary" @click="handleEdit(scope.row.id)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<br>
<!-- 分页 -->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
>
</el-pagination>
<!-- 新增/修改员工的对话框 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle">
<el-form ref="employeeFormRef" :model="employee" label-width="80px">
<!-- 基本信息 -->
<!-- 第一行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="用户名">
<el-input v-model="employee.username" placeholder="请输入员工用户名,2-20个字"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="姓名">
<el-input v-model="employee.name" placeholder="请输入员工姓名,2-10个字"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="性别">
<el-select v-model="employee.gender" placeholder="请选择性别" style="width: 100%;">
<el-option v-for="gender in genders" :key="gender.name" :label="gender.name" :value="gender.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号">
<el-input v-model="employee.phone" placeholder="请输入员工手机号"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="职位">
<el-select v-model="employee.job" placeholder="请选择职位" style="width: 100%;">
<el-option v-for="job in jobs" :key="job.name" :label="job.name" :value="job.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="薪资">
<el-input v-model="employee.salary" placeholder="请输入员工薪资"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第四行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="所属部门">
<el-select v-model="employee.deptId" placeholder="请选择部门" style="width: 100%;">
<el-option v-for="dept in deptList" :key="dept.id" :label="dept.name" :value="dept.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入职日期">
<el-date-picker v-model="employee.entryDate" type="date" style="width: 100%;" placeholder="选择日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD"></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<!-- 第五行 -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="头像">
<el-upload
class="avatar-uploader"
action="/api/upload"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="employee.image" :src="employee.image" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form-item>
</el-col>
</el-row>
<!-- 工作经历 -->
<!-- 第六行 -->
<el-row :gutter="10">
<el-col :span="24">
<el-form-item label="工作经历">
<el-button type="success" size="small" @click="addExprItem">+ 添加工作经历</el-button>
</el-form-item>
</el-col>
</el-row>
<!-- 第七行 ... 工作经历 -->
<el-row :gutter="3" v-for="(expr, index) in employee.exprList">
<el-col :span="10">
<el-form-item size="small" label="时间" label-width="80px">
<el-date-picker type="daterange" v-model="expr.exprDate" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" ></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item size="small" label="公司" label-width="60px">
<el-input placeholder="请输入公司名称" v-model="expr.company"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item size="small" label="职位" label-width="60px">
<el-input placeholder="请输入职位" v-model="expr.job"></el-input>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item size="small" label-width="0px">
<el-button type="danger" @click="delExprItem(index)">- 删除</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 底部按钮 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="">保存</el-button>
</span>
</template>
</el-dialog>
</template>
<style scoped>
.avatar {
height: 40px;
}
.avatar-uploader .avatar {
width: 78px;
height: 78px;
display: block;
}
.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: 78px;
height: 78px;
text-align: center;
/* 添加灰色的虚线边框 */
border: 1px dashed var(--el-border-color);
}
</style>接下来,我们就可以打开浏览器看到具体的页面效果了。

页面交互
保存员工信息
基本的页面布局,我们完成之后,接下来,就需要完成页面的交互操作。 当点击 “保存” 按钮,需要执行如下操作:
- 点击保存之后,发送异步请求到服务端,提交数据。
- 保存完毕之后,如果成功,关闭对话框,重新加载列表数据。
- 保存完毕之后,如果失败,提示错误信息。
具体操作如下:
1). 为 "保存" 按钮绑定事件
<!-- ........ 前面的代码省略了......... -->
<!-- 底部按钮 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="save">保存</el-button>
</span>
</template>
</el-dialog>2). 在 <script></script> 中定义函数 save
//保存员工信息
const save = async () => {
const result = await addEmpApi(employee.value);
if(result.code){
ElMessage.success('新增员工成功')
dialogVisible.value = false
handleSearch()
}else {
ElMessage.error(result.msg)
}
}然后我们就可以打开浏览器,测试添加员工了。

表单校验
从页面原型中的需求描述,我们就可以提取出这个表单的校验规则。 我们可以打开页面原型来看一下:

通过页面原型,我们可以提取到的校验规则如下:
表单校验规则:
- 用户名:必填,长度2-20
- 姓名:必填,长度2-10
- 性别:必填
- 手机号:必填,符合手机号规则
接下来,我们可以借助于AI,帮我们生成对应的校验规则:
💡 AI提示词(prompt): 请帮我生成ElementPlus中表单的校验规则,具体规则如下:
- 用户名: 必填, 长度2-20
- 姓名: 必填, 长度2-10
- 性别: 必填
- 手机号: 必填, 11位, 符合手机号规则 请帮我生成表单的校验规则, 并为表单项绑定对应的校验规则。
参考AI生成的代码,并在其基础上进行改造完成新增员工表单的校验。最终代码形式如下(新增代码为绿色背景):
<script setup>
import { ref, watch, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { queryPageApi, addEmpApi } from '@/api/emp'
import { queryAllApi as queryAllDeptApi } from '@/api/dept'
//职位列表数据
const jobs = ref([{ name: '班主任', value: 1 },{ name: '讲师', value: 2 },{ name: '学工主管', value: 3 },{ name: '教研主管', value: 4 },{ name: '咨询师', value: 5 },{ name: '其他', value: 6 }])
//性别列表数据
const genders = ref([{ name: '男', value: 1 }, { name: '女', value: 2 }])
//部门列表数据
const deptList = ref([])
const searchEmp = ref({
name: '',
gender: '',
date: [],
begin: '',
end: ''
})
//侦听searchEmp中的date属性
watch(
() => searchEmp.value.date,
(newValue, oldValue) => {
if(newValue.length == 2){
searchEmp.value.begin = newValue[0]
searchEmp.value.end = newValue[1]
}else {
searchEmp.value.begin = ''
searchEmp.value.end = ''
}
}
)
onMounted(async () => {
handleSearch()
//加载所有部门数据
const result = await queryAllDeptApi();
if(result.code){
deptList.value = result.data
}
})
//查询员工
const handleSearch = async () => {
console.log('Search:', searchEmp.value)
const result = await queryPageApi(searchEmp.value.name, searchEmp.value.gender, searchEmp.value.begin, searchEmp.value.end, currentPage.value, pageSize.value);
if(result.code){
empList.value = result.data.rows
total.value = result.data.total
}
}
const handleReset = () => {
// 清空表单
searchEmp.value = {
name: '',
gender: '',
date: [],
begin: '',
end: ''
}
handleSearch()
}
// 示例数据
const empList = ref([])
// 分页配置
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
// 职位转换函数
const getJobTitle = (job) => {
switch (job) {
case 1:
return '班主任'
case 2:
return '讲师'
case 3:
return '学工主管'
case 4:
return '教研主管'
case 5:
return '咨询师'
default:
return '其他'
}
}
// 分页处理
const handleSizeChange = (val) => {
pageSize.value = val
handleSearch()
}
const handleCurrentChange = (val) => {
currentPage.value = val
handleSearch()
}
// 操作处理
const handleEdit = (row) => {
console.log('Edit:', row)
}
const handleDelete = (row) => {
console.log('Delete:', row)
}
//新增/修改表单
const employeeFormRef = ref(null)
const employee = ref({
username: '',
name: '',
gender: '',
phone: '',
job: '',
salary: '',
deptId: '',
entryDate: '',
image: '',
exprList: []
})
//新增员工
const addEmp = () => {
dialogVisible.value = true
dialogTitle.value = '新增员工'
//清空表单内容及校验提示信息
employee.value = {
username: '',
name: '',
gender: '',
phone: '',
job: '',
salary: '',
deptId: '',
entryDate: '',
image: '',
exprList: []
}
if(employeeFormRef.value){
employeeFormRef.value.resetFields()
}
}
//表单校验规则
// 验证规则
const rules = ref({
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 2, max: 20, message: '用户名长度应在2到20个字符之间', trigger: 'blur' }
],
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' },
{ min: 2, max: 10, message: '姓名长度应在2到10个字符之间', trigger: 'blur' }
],
gender: [
{ required: true, message: '请选择性别', trigger: 'change' }
],
phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1\d{10}$/g, message: '请输入有效的手机号', trigger: 'blur' }
]
});
// 控制弹窗
const dialogVisible = ref(false)
const dialogTitle = ref('新增员工')
//文件上传
// 图片上传成功后触发
const handleAvatarSuccess = (response,uploadFile) => {
employee.value.image = response.data
}
// 文件上传之前触发
const beforeAvatarUpload = (rawFile) => {
if (rawFile.type !== 'image/jpeg' && rawFile.type !== 'image/png') {
ElMessage.error('只支持上传图片')
return false
} else if (rawFile.size / 1024 / 1024 > 10) {
ElMessage.error('只能上传10M以内图片')
return false
}
return true
}
//工作经历
//动态添加工作经历 .
const addExprItem = () => {
employee.value.exprList.push({exprDate: [], begin: '', end: '', company: '', job: ''})
}
//动态删除工作经历 .
const delExprItem = (index) => {
employee.value.exprList.splice(index, 1)
}
//监听-employee员工对象中的工作经历数据
watch(employee, (newVal, oldVal) => {
if(employee.value.exprList) {
employee.value.exprList.forEach(expr => {
expr.begin = expr.exprDate[0]
expr.end = expr.exprDate[1]
})
}
}, {deep: true})
//保存员工信息
const save = async () => {
employeeFormRef.value.validate(async valid => {
if(valid){ // 校验通过
const result = await addEmpApi(employee.value);
if(result.code){
ElMessage.success('新增员工成功')
dialogVisible.value = false
handleSearch()
}else {
ElMessage.error(result.msg)
}
}
})
}
</script>
<template>
<h1>员工管理</h1> <br>
<el-form :inline="true" :model="searchEmp">
<el-form-item label="姓名">
<el-input v-model="searchEmp.name" placeholder="请输入员工姓名"></el-input>
</el-form-item>
<el-form-item label="性别">
<el-select v-model="searchEmp.gender" placeholder="请选择">
<el-option label="男" value="1"></el-option>
<el-option label="女" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="入职日期">
<el-date-picker
v-model="searchEmp.date"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">清空</el-button>
</el-form-item>
</el-form>
<el-button type="primary" @click="addEmp"> + 新增员工</el-button>
<el-button type="danger" @click=""> - 批量删除</el-button>
<br><br>
<!-- 表格 -->
<el-table :data="empList" border style="width: 100%">
<el-table-column type="selection" width="55" align="center"></el-table-column>
<el-table-column prop="name" label="姓名" width="120" align="center"></el-table-column>
<el-table-column label="性别" width="80" align="center">
<template #default="scope" >
{{ scope.row.gender == 1 ? '男' : '女' }}
</template>
</el-table-column>
<el-table-column label="头像" width="170" align="center">
<template #default="scope" >
<img :src="scope.row.image" alt="Avatar" class="avatar" />
</template>
</el-table-column>
<el-table-column prop="deptName" label="部门名称" width="170" align="center"></el-table-column>
<el-table-column label="职位" width="120" align="center">
<template #default="scope">
{{ getJobTitle(scope.row.job) }}
</template>
</el-table-column>
<el-table-column prop="entryDate" label="入职日期" width="180" align="center"></el-table-column>
<el-table-column prop="updateTime" label="最后操作时间" width="210" align="center"></el-table-column>
<el-table-column label="操作" fixed="right" align="center">
<template #default="scope">
<el-button size="small" type="primary" @click="handleEdit(scope.row.id)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<br>
<!-- 分页 -->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
>
</el-pagination>
<!-- 新增/修改员工的对话框 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle">
<el-form ref="employeeFormRef" :model="employee" :rules="rules" label-width="80px">
<!-- 基本信息 -->
<!-- 第一行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="用户名" prop="username">
<el-input v-model="employee.username" placeholder="请输入员工用户名,2-20个字"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="姓名" prop="name">
<el-input v-model="employee.name" placeholder="请输入员工姓名,2-10个字"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="性别" prop="gender">
<el-select v-model="employee.gender" placeholder="请选择性别" style="width: 100%;">
<el-option v-for="gender in genders" :key="gender.name" :label="gender.name" :value="gender.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号" prop="phone">
<el-input v-model="employee.phone" placeholder="请输入员工手机号"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="职位">
<el-select v-model="employee.job" placeholder="请选择职位" style="width: 100%;">
<el-option v-for="job in jobs" :key="job.name" :label="job.name" :value="job.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="薪资">
<el-input v-model="employee.salary" placeholder="请输入员工薪资"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第四行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="所属部门">
<el-select v-model="employee.deptId" placeholder="请选择部门" style="width: 100%;">
<el-option v-for="dept in deptList" :key="dept.id" :label="dept.name" :value="dept.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入职日期">
<el-date-picker v-model="employee.entryDate" type="date" style="width: 100%;" placeholder="选择日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD"></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<!-- 第五行 -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="头像">
<el-upload
class="avatar-uploader"
action="/api/upload"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="employee.image" :src="employee.image" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form-item>
</el-col>
</el-row>
<!-- 工作经历 -->
<!-- 第六行 -->
<el-row :gutter="10">
<el-col :span="24">
<el-form-item label="工作经历">
<el-button type="success" size="small" @click="addExprItem">+ 添加工作经历</el-button>
</el-form-item>
</el-col>
</el-row>
<!-- 第七行 ... 工作经历 -->
<el-row :gutter="3" v-for="(expr, index) in employee.exprList">
<el-col :span="10">
<el-form-item size="small" label="时间" label-width="80px">
<el-date-picker type="daterange" v-model="expr.exprDate" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" ></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item size="small" label="公司" label-width="60px">
<el-input placeholder="请输入公司名称" v-model="expr.company"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item size="small" label="职位" label-width="60px">
<el-input placeholder="请输入职位" v-model="expr.job"></el-input>
</el-form-item>
</el-col>
<el-col :span="2">
<el-form-item size="small" label-width="0px">
<el-button type="danger" @click="delExprItem(index)">- 删除</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 底部按钮 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="save">保存</el-button>
</span>
</template>
</el-dialog>
</template>
<style scoped>
.avatar {
height: 40px;
}
.avatar-uploader .avatar {
width: 78px;
height: 78px;
display: block;
}
.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: 78px;
height: 78px;
text-align: center;
/* 添加灰色的虚线边框 */
border: 1px dashed var(--el-border-color);
}
</style>代码编写完毕后,我们可以打开浏览器测试一下。

我们可以看到,我们为表单添加的校验规则,确实生效了。 并且,如果表单校验不通过,是不能保存员工信息的。
