【免费Web系列】大家好 ,今天是Web课程的第二一天点赞收藏关注,持续更新作品 !

这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r
员工管理
1. 条件分页查询
1.1 概述
在页面原型中,我们可以看到在查询员工信息列表时,既需要根据条件动态查询,还需要对查询的结果进行分页处理。

那要完成这个页面布局,我们就需要用到ElementPlus中提供的组件,包括 Form表单、Button按钮、Table表格、Pagination分页组件。

1.2 接口文档

1.3 页面布局
1.3.1 搜索栏
搜索栏制作,主要用到ElementPlus中的组件包括:Button 组件 Form 组件 。 具体的布局代码如下:
<script setup lang="ts">
import type { SearchEmpModel } from '@/api/model/model';
import {ref, onMounted} from 'vue'
//搜索栏对象声明
const searchEmp = ref<SearchEmpModel>({name: '',gender: '',begin: '',end: '',date: []
})
</script>
<template><h1>员工管理</h1> <br><!-- 搜索栏 --><el-form :inline="true" :model="searchEmp" class="demo-form-inline"><el-form-item label="姓名"><el-input v-model="searchEmp.name" placeholder="请输入员工姓名" clearable /></el-form-item><el-form-item label="性别"><el-select v-model="searchEmp.gender" placeholder="请选择" clearable><el-option label="男" value="1" /><el-option label="女" value="2" /></el-select></el-form-item>
<el-form-item label="入职时间"><el-date-picker v-model="searchEmp.date" type="daterange" range-separator="到" start-placeholder="开始时间" end-placeholder="结束时间"/></el-form-item>
<el-form-item><el-button type="primary" @click="">查询</el-button><el-button type="default" @click="">重置</el-button></el-form-item></el-form>
<!-- 按钮 --><el-button type="primary" @click="">+ 新增员工</el-button><el-button type="danger" @click="">- 批量删除</el-button>
<!-- 表格 -->
<!-- 分页栏 -->
</template>
<style scoped>
</style>
浏览器打开页面,具体效果如下:

我们可以在表单中,输入搜索条件,看看表单绑定的数据值。

我们可以看到,日期时间组件中选择的开始时间和结束时间,数据绑定到了 date 属性中,是一个数组,里面两个值,一个开始时间、一个结束时间。 而在执行查询时,从接口文档中,我们可以看出,需要的是开始时间 begin 和 结束时间 end。
而当我们选择的入职时间范围发生变化,应该实时计算出 开始时间 begin 和 结束时间 end,那这里可以通过Vue框架中的 watch侦听 来解决。
1.3.2 watch侦听对象
作用:侦听一个或多个响应式数据源,并在数据源变化时,调用所给的 回调函数。
语法:
1). 导入 watch 函数
2). 执行 watch 函数,传入要侦听的响应式数据源(ref对象)和回调函数
A. 侦听一个响应式对象
//演示watch侦听
const myname = ref<string>('')
watch(myname, (newVal, oldVal)=>{console.log(`name的值, newVal: ${newVal}, oldVal: ${oldVal}`);
})
B. 侦听对象的单个属性
//侦听searchEmp对象中的name的变化
watch(() => searchEmp.value.name, (newVal , oldVal) => {console.log(`name的值, newVal: ${newVal}, oldVal: ${oldVal}`);
})
C. 侦听对象的全部属性(深度侦听)
watch(searchEmp, (newVal, oldVal) => {console.log(`name的值, newVal: ${newVal.name}, oldVal: ${oldVal.name}`);
}, {deep: true})
watch函数的第三个参数是可选的,常见两个选项:
deep(boolean):是否深度侦听,默认浅层侦听。
immediate(boolean):是否在侦听创建时,立即触发回调函数
案例中,入职日期的侦听如下代码如下:
//侦听searchEmp的date属性
watch(() => searchEmp.value.date, (newVal, oldVal) => {if(newVal.length>0) {searchEmp.value.begin = newVal[0]searchEmp.value.end = newVal[1]}else {searchEmp.value.begin = ''searchEmp.value.end = ''}
})

1.3.3 数据表格
1). 在 src/views/emp/index.vue 中的 <template> </template> 部分增加如下内容:
<!-- 表格 --><!-- 列表展示 --><el-table :data="tableData" border style="width: 100%" fit @selection-change="handleSelectionChange"><el-table-column type="selection" width="55" /><el-table-column prop="name" label="姓名" align="center" width="130px" /><el-table-column label="性别" align="center" width="100px"><template #default="scope">{{ scope.row.gender == 1 ? '男' : '女' }}</template></el-table-column><el-table-column prop="image" label="头像" align="center"><template #default="scope"><img :src="scope.row.image" height="40"></template></el-table-column><el-table-column prop="deptName" label="所属部门" align="center" /><el-table-column prop="job" label="职位" align="center" width="100px"><template #default="scope"><span v-if="scope.row.job == 1">班主任</span><span v-else-if="scope.row.job == 2">讲师</span><span v-else-if="scope.row.job == 3">学工主管</span><span v-else-if="scope.row.job == 4">教研主管</span><span v-else-if="scope.row.job == 5">咨询师</span><span v-else>其他</span></template></el-table-column><el-table-column prop="entryDate" label="入职时间" align="center" width="130px" /><el-table-column prop="updateTime" label="最后修改时间" align="center" /><el-table-column label="操作" align="center"><template #default="scope"><el-button type="primary" size="small" @click="">编辑</el-button><el-button type="danger" size="small" @click="">删除</el-button></template></el-table-column></el-table><br>
<!-- 分页组件Pagination --><el-paginationv-model:current-page="pagination.currentPage"v-model:page-size="pagination.pageSize":page-sizes="[5, 10, 20, 50, 100]"layout="total, sizes, prev, pager, next, jumper":total="pagination.total"@size-change="handleSizeChange"@current-change="handleCurrentChange"/>
2). 在 src/views/emp/index.vue 中的 <script> </script> 部分增加如下内容:
//列表展示数据
const tableData = ref<EmpModelArray>([])
//复选框
let selectIds = ref<number[]>([])
const handleSelectionChange = (selection: any[]) => {selectIds.value = selection.map(item => item.id)
}
//分页组件
const pagination = ref<PaginationParam>({currentPage: 1, pageSize: 5, total: 0})
//每页展示记录数发生变化时触发
const handleSizeChange = (pageSize: number) => {pagination.value.pageSize = pageSizequeryPage()
}
//当前页码发生变化时触发
const handleCurrentChange = (page: number) => {pagination.value.currentPage = pagequeryPage()
}
//分页条件查询
const queryPage = async () => {}
1.4 页面交互
1). 为 "查询" 按钮绑定事件,点击查询按钮调用queryPage函数.

//分页条件查询
const queryPage = async () => {const result = await queryPageApi(searchEmp.value.begin,searchEmp.value.end,searchEmp.value.gender,searchEmp.value.name,pagination.value.currentPage,pagination.value.pageSize)
if(result.code) {tableData.value = result.data.rowspagination.value.total = result.data.total}
}
//钩子函数
onMounted(() => {queryPage()
})
2). 为 "重置" 按钮绑定事件 , 点击重置按钮, 清空搜索表单, 并重新查询.

//重置
const reset = () => {searchEmp.value = {name:'', begin:'', end:'', date: [], gender: ''}queryPage()
}
到此,员工列表的动态条件分页查询,就已经完成了。 目前 src/views/emp/index.vue 中的完整代码如下
<script setup lang="ts">
import type { EmpModelArray, PaginationParam, SearchEmpModel } from '@/api/model/model'
import {ref, onMounted, watch} from 'vue'
import { queryPageApi} from '@/api/emp'
//搜索栏对象声明
const searchEmp = ref<SearchEmpModel>({ name: '', gender: '', begin: '', end: '', date: []})
//列表展示数据
const tableData = ref<EmpModelArray>([])
//复选框
let selectIds = ref<number[]>([])
const handleSelectionChange = (selection: any[]) => {selectIds.value = selection.map(item => item.id)
}
//分页组件
const pagination = ref<PaginationParam>({currentPage: 1, pageSize: 5, total: 0})
//每页展示记录数发生变化时触发
const handleSizeChange = (pageSize: number) => {pagination.value.pageSize = pageSizequeryPage()
}
//当前页码发生变化时触发
const handleCurrentChange = (page: number) => {pagination.value.currentPage = pagequeryPage()
}
//分页条件查询
const queryPage = async () => {const result = await queryPageApi(searchEmp.value.begin,searchEmp.value.end,searchEmp.value.gender,searchEmp.value.name,pagination.value.currentPage,pagination.value.pageSize)
if(result.code) {tableData.value = result.data.rowspagination.value.total = result.data.total}
}
//钩子函数
onMounted(() => {queryPage()
})
//重置
const reset = () => {searchEmp.value = {name:'', begin:'', end:'', date: [], gender: ''}queryPage()
}
//侦听searchEmp的date属性
watch(() => searchEmp.value.date, (newVal, oldVal) => {if(newVal.length>0) {searchEmp.value.begin = newVal[0]searchEmp.value.end = newVal[1]}else {searchEmp.value.begin = ''searchEmp.value.end = ''}
})
</script>
<template><h1>员工管理</h1> <br><!-- 搜索栏 --><el-form :inline="true" :model="searchEmp" class="demo-form-inline"><el-form-item label="姓名"><el-input v-model="searchEmp.name" placeholder="请输入员工姓名" clearable /></el-form-item><el-form-item label="性别"><el-select v-model="searchEmp.gender" placeholder="请选择" clearable><el-option label="男" value="1" /><el-option label="女" value="2" /></el-select></el-form-item>
<el-form-item label="入职时间"><el-date-picker v-model="searchEmp.date" type="daterange" value-format="YYYY-MM-DD" range-separator="到" start-placeholder="开始时间" end-placeholder="结束时间"/></el-form-item>
<el-form-item><el-button type="primary" @click="queryPage()">查询</el-button><el-button type="default" @click="reset()">重置</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="tableData" border style="width: 100%" fit @selection-change="handleSelectionChange"><el-table-column type="selection" width="55" /><el-table-column prop="name" label="姓名" align="center" width="130px" /><el-table-column label="性别" align="center" width="100px"><template #default="scope">{{ scope.row.gender == 1 ? '男' : '女' }}</template></el-table-column><el-table-column prop="image" label="头像" align="center"><template #default="scope"><img :src="scope.row.image" height="40"></template></el-table-column><el-table-column prop="deptName" label="所属部门" align="center" /><el-table-column prop="job" label="职位" align="center" width="100px"><template #default="scope"><span v-if="scope.row.job == 1">班主任</span><span v-else-if="scope.row.job == 2">讲师</span><span v-else-if="scope.row.job == 3">学工主管</span><span v-else-if="scope.row.job == 4">教研主管</span><span v-else-if="scope.row.job == 5">咨询师</span><span v-else>其他</span></template></el-table-column><el-table-column prop="entryDate" label="入职时间" align="center" width="130px" /><el-table-column prop="updateTime" label="最后修改时间" align="center" /><el-table-column label="操作" align="center"><template #default="scope"><el-button type="primary" size="small" @click="">编辑</el-button><el-button type="danger" size="small" @click="">删除</el-button></template></el-table-column></el-table><br>
<!-- 分页组件Pagination --><el-paginationv-model:current-page="pagination.currentPage"v-model:page-size="pagination.pageSize":page-sizes="[5, 10, 20, 50, 100]"layout="total, sizes, prev, pager, next, jumper":total="pagination.total"@size-change="handleSizeChange"@current-change="handleCurrentChange"/></template>
<style scoped>
</style>
最终,打开浏览器效果如下:

2. 新增员工
2.1 需求分析
通过页面原型,我们可以看到,新增员工的表单提交的数据,包括员工的基本数据,员工的工作经历数据。

2.2 页面布局
2.2.1 介绍
我们看到这个表单,每一行放了两个表单项。 而头像这一行,是一个表单项,这里呢,我们可以使用 ElementPlus 提供的 layout 布局来实现。通过基础的 24 分栏,迅速简便地创建布局。


2.2.2 基本信息
先来完成员工基本信息表单的制作。 具体的代码如下:
1). <template> </template> 中的布局代码如下
<!-- 新增员工/修改员工-Dialog --><!-- 新增/修改员工对话框 --><el-dialog v-model="dialogFormVisible" :title="formTitle"><el-form :model="emp" ><!-- 第一行 --><el-row><el-col :span="12"><el-form-item label="用户名" :label-width="labelWidth" prop="username"><el-input v-model="emp.username" /></el-form-item></el-col><el-col :span="12"><el-form-item label="姓名" :label-width="labelWidth" prop="name"><el-input v-model="emp.name" /></el-form-item></el-col></el-row><!-- 第二行 --><el-row><el-col :span="12"><el-form-item label="性别" :label-width="labelWidth" prop="gender"><el-select v-model="emp.gender" placeholder="请选择" style="width: 100%;"><el-option v-for="gender in genders" :label="gender.name" :value="gender.value" /></el-select></el-form-item></el-col><el-col :span="12"><el-form-item label="手机号" :label-width="labelWidth" prop="phone"><el-input v-model="emp.phone" /></el-form-item></el-col></el-row>
<!-- 第三行 --><el-row><el-col :span="12"><el-form-item label="薪资" :label-width="labelWidth" prop="salary"><el-input v-model="emp.salary" /></el-form-item></el-col><el-col :span="12"><el-form-item label="入职日期" :label-width="labelWidth"><el-date-picker v-model="emp.entryDate" type="date" placeholder="请选择入职日期" value-format="YYYY-MM-DD" style="width: 100%;"/></el-form-item></el-col></el-row>
<!-- 第四行 --><el-row><el-col :span="12"><el-form-item label="所属部门" :label-width="labelWidth"><el-select v-model="emp.deptId" placeholder="请选择" style="width: 100%;"><el-option v-for="dept in depts" :label="dept.name" :value="dept.id" /></el-select></el-form-item></el-col><el-col :span="12"><el-form-item label="职位" :label-width="labelWidth"><el-select v-model="emp.job" placeholder="请选择" style="width: 100%;"><el-option v-for="job in jobs" :label="job.name" :value="job.value" /></el-select></el-form-item></el-col></el-row>
<!-- 第五行 --><el-row :gutter="10"><el-col :span="24"><el-form-item label="头像" label-width="80px"><el-upload class="avatar-uploader" action="/api/upload" :show-file-list="false":on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload"><img v-if="emp.image" :src="emp.image" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></el-form-item></el-col></el-row></el-form>
<template #footer><span class="dialog-footer"><el-button @click="dialogFormVisible = false; ">取消</el-button><el-button type="primary" @click="">保存</el-button></span></template>
</el-dialog>
2). <script> </script> 中的代码如下
//钩子函数 - 添加调用queryAllDept() 代码
onMounted(() => {queryPage()queryAllDept()
})
//查询所有部门
const depts = ref<DeptModelArray>([])
const queryAllDept = async () => {const result = await queryAllApi()if(result.code) {depts.value = result.data}
}
//----------- 新增 / 修改 ---------------------------
//职位列表数据
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 }])
let dialogFormVisible = ref<boolean>(false) //控制新增/修改的对话框的显示与隐藏
let labelWidth = ref<number>(80) //form表单label的宽度
let formTitle = ref<string>('') //表单的标题
let emp = ref<EmpModel>({ //员工对象-表单数据绑定username: '',password: '',name: '',gender: '',phone: '',job: '',salary: '',image: '',entryDate: '',deptId: '',exprList: []
})
//文件上传
// let imageUrl = ref<string>()
const handleAvatarSuccess: UploadProps['onSuccess'] = (response, uploadFile) => {emp.value.image = response.data;
}
const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {if (rawFile.type !== 'image/jpeg' && rawFile.type !== 'image/png') {ElMessage.error('图片格式不支持!')return false} else if (rawFile.size / 1024 / 1024 > 10) {ElMessage.error('图片大小不能超过 10 MB!')return false}return true
}
//新增员工-打开对话框
const add = () => {dialogFormVisible.value = trueformTitle.value = '新增员工'
}
//清空表单
const clearEmp = () => {emp.value = {username: '',password: '',name: '',gender: '',phone: '',job: '',salary: '',image: '',entryDate: '',deptId: '',exprList: new Array<EmpExprModel>()}
}
3). <style> </style> 的css样式代码如下:
.avatar-uploader .avatar {width: 78px;height: 78px;display: block;}.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 #ccc;border-radius: 5px;}
打开浏览器,看到新增员工的表单呈现出来了:

2.2.3 工作经历
2.2.3.1 思路

新增员工的基本信息表单已经制作完成了,那接下来,要制作的是员工的工作经历。 员工的过往工作经历可能是多条,点击 "添加员工工作经历" 按钮,如何增加一个条目 ? 点击每一条后面的删除按钮,需要删除当前条件?
-
Vue是基于数据驱动视图展示的。
-
"添加" 时,我们可以往数组中添加数据。
-
"删除" 时,可以删除数组中的元素。
-
一旦数据发生变化,视图中的展示就会发生变化。
2.2.3.2 实现
1). 在 <template> </template> 中定义的表单中,增加如下代码:
<!-- 第六行 --><!-- 第六行 --><el-row :gutter="10"><el-col :span="24"><el-form-item label="工作经历" :label-width="labelWidth"><el-button type="success" size="small" @click="addWorkItem">+ 添加工作经历</el-button></el-form-item></el-col></el-row>
<!-- 第七...行 --><el-row :gutter="5" v-for="expr in emp.exprList"><el-col :span="10"><el-form-item label="时间" size="small" :label-width="labelWidth"><el-date-picker v-model="expr.exprDate" type="daterange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" value-format="YYYY-MM-DD"/></el-form-item></el-col><el-col :span="6"><el-form-item label="公司" size="small"><el-input v-model="expr.company" placeholder="公司名称"/></el-form-item></el-col>
<el-col :span="6"><el-form-item label="职位" size="small"><el-input v-model="expr.job" placeholder="职位名称"/></el-form-item></el-col>
<el-col :span="2"><el-form-item size="small"><el-button type="danger" @click="delWorkItem(expr)">- 删除</el-button></el-form-item></el-col></el-row>
2). 在 <script> </script> 中增加如下代码:
//动态添加工作经历 .
const addWorkItem = () => {emp.value.exprList.push({exprDate: [],begin: '',end: '',company: '',job: ''})
}
//动态删除工作经历 .
const delWorkItem = (expr: EmpExprModel) => {if(emp.value.exprList) {const index = emp.value.exprList.indexOf(expr)if(index != -1){emp.value.exprList.splice(index,1)}}
}
//监听-emp员工对象中的工作经历数据
watch(emp, (newVal, oldVal) => {if(emp.value.exprList) {emp.value.exprList.forEach(expr => {expr.begin = expr.exprDate[0]expr.end = expr.exprDate[1]})}
}, {deep: true})
打开浏览器,点击 新增员工,点击 “添加员工工作经历” 测试:



2.3 页面交互
基本的页面布局,我们完成之后,接下来,就需要完成页面的交互操作。 当点击 “保存” 按钮,需要执行如下操作:
-
点击保存之后,发送异步请求到服务端,提交数据。
-
保存完毕之后,如果成功,关闭对话框,重新加载列表数据。
-
保存完毕之后,如果失败,提示错误信息。
具体操作如下:
1). 为 "保存按钮" 绑定事件
//-------------保存员工信息
const save = async () => {//表单校验let result = await addApi(emp.value)if(result.code) {ElMessage.success('操作成功')dialogFormVisible.value = falsequeryPage()}else {ElMessage.error(result.msg)}
}
2). 在 <script></script> 中定义函数
2.4 表单校验
结合页面原型及接口文档,梳理校验规则:
| 字段 | 是否必填 | 其他限制 |
|---|---|---|
| 用户名 | 是 | 长度2-20 |
| 姓名 | 是 | 2-10 |
| 性别 | 是 | |
| 手机号 | 是 | 符合手机号规则,正则 |
| 薪资 | 否 | 全为数字,第一位不为0,正则 |
1). 参照 Element Plus 中的Form表单组件,定义校验规则;
//表单校验规则
const empFormRef = ref<FormInstance>()
const rules = ref<FormRules<EmpModel>>({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[3-9]\d{9}$/g, message: '请输入合法的手机号', trigger: 'blur' }],salary: [{ pattern: /^[1-9]\d*$/g, message: '请输入合法的数字', trigger: 'blur' }]
})
2). 将校验规则与Form表单组件进行属性绑定;

3). 在保存员工时,进行表单校验,校验通过再提交数据;

完善 save 函数,完善后的代码如下:
const save = async (form: FormInstance|undefined) => {if(!form) return;//表单校验form.validate(async (valid) => {if(valid) {let result = await addApi(emp.value)if(result.code) {ElMessage.success('操作成功')dialogFormVisible.value = falsequeryPage()}else {ElMessage.error(result.msg)} }})
}
4). 点击取消、新增、修改时,重置表单校验规则;
//重置表单
const resetForm = (empForm: FormInstance | undefined) => {if (!empForm) returnempForm.resetFields()
}
新增员工时,重置表单校验规则:

点击取消时,重置表单校验规则:

打开浏览器,访问测试:



到此呢,关于员工列表的动态条件分页查询。 新增员工的功能,我们都已经实现了,目前 src/views/emp/index.vue 文件的代码如下:
<script setup lang="ts">
import type { DeptModelArray, EmpExprModel, EmpModel, EmpModelArray, PaginationParam, SearchEmpModel } from '@/api/model/model'
import { ref, onMounted, watch } from 'vue'
import { addApi, queryPageApi } from '@/api/emp'
import { queryAllApi } from '@/api/dept'
import { ElMessage, type FormInstance, type FormRules, type UploadProps } from 'element-plus';
//搜索栏对象声明
const searchEmp = ref<SearchEmpModel>({ name: '', gender: '', begin: '', end: '', date: []})
//列表展示数据
const tableData = ref<EmpModelArray>([])
//复选框
let selectIds = ref<number[]>([])
const handleSelectionChange = (selection: any[]) => {selectIds.value = selection.map(item => item.id)
}
//分页组件
const pagination = ref<PaginationParam>({currentPage: 1, pageSize: 5, total: 0})
//每页展示记录数发生变化时触发
const handleSizeChange = (pageSize: number) => {pagination.value.pageSize = pageSizequeryPage()
}
//当前页码发生变化时触发
const handleCurrentChange = (page: number) => {pagination.value.currentPage = pagequeryPage()
}
//分页条件查询
const queryPage = async () => {const result = await queryPageApi(searchEmp.value.begin,searchEmp.value.end,searchEmp.value.gender,searchEmp.value.name,pagination.value.currentPage,pagination.value.pageSize)
if(result.code) {tableData.value = result.data.rowspagination.value.total = result.data.total}
}
//钩子函数
onMounted(() => {queryPage()queryAllDept()
})
//查询所有部门
const depts = ref<DeptModelArray>([])
const queryAllDept = async () => {const result = await queryAllApi()if(result.code) {depts.value = result.data}
}
//重置
const reset = () => {searchEmp.value = {name:'', begin:'', end:'', date: [], gender: ''}queryPage()
}
//侦听searchEmp的date属性
watch(() => searchEmp.value.date, (newVal, oldVal) => {if(newVal.length>0) {searchEmp.value.begin = newVal[0]searchEmp.value.end = newVal[1]}else {searchEmp.value.begin = ''searchEmp.value.end = ''}
})
//----------- 新增 / 修改 ---------------------------
//职位列表数据
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 }])
let dialogFormVisible = ref<boolean>(false) //控制新增/修改的对话框的显示与隐藏
let labelWidth = ref<number>(80) //form表单label的宽度
let formTitle = ref<string>('') //表单的标题
let emp = ref<EmpModel>({ //员工对象-表单数据绑定username: '',password: '',name: '',gender: '',phone: '',job: '',salary: '',image: '',entryDate: '',deptId: '',exprList: []
})
//文件上传
// let imageUrl = ref<string>()
const handleAvatarSuccess: UploadProps['onSuccess'] = (response, uploadFile) => {emp.value.image = response.data;
}
const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {if (rawFile.type !== 'image/jpeg' && rawFile.type !== 'image/png') {ElMessage.error('图片格式不支持!')return false} else if (rawFile.size / 1024 / 1024 > 10) {ElMessage.error('图片大小不能超过 10 MB!')return false}return true
}
//新增员工-打开对话框
const add = () => {dialogFormVisible.value = trueformTitle.value = '新增员工'
}
//动态添加工作经历 .
const addWorkItem = () => {emp.value.exprList.push({exprDate: [],begin: '',end: '',company: '',job: ''})
}
//动态删除工作经历 .
const delWorkItem = (expr: EmpExprModel) => {if(emp.value.exprList) {const index = emp.value.exprList.indexOf(expr)if(index != -1){emp.value.exprList.splice(index,1)}}
}
//-------------保存员工信息
const save = async (form: FormInstance|undefined) => {if(!form) return;//表单校验form.validate(async (valid) => {if(valid) {let result = await addApi(emp.value)if(result.code) {ElMessage.success('操作成功')dialogFormVisible.value = falsequeryPage()}else {ElMessage.error(result.msg)} }})
}
//表单校验规则
const empFormRef = ref<FormInstance>()
const rules = ref<FormRules<EmpModel>>({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[3-9]\d{9}$/g, message: '请输入合法的手机号', trigger: 'blur' }],salary: [{ pattern: /^[1-9]\d*$/g, message: '请输入合法的数字', trigger: 'blur' }]
})
//重置表单
const resetForm = (empForm: FormInstance | undefined) => {if (!empForm) returnempForm.resetFields()
}
//清空表单
const clearEmp = () => {emp.value = {username: '',password: '',name: '',gender: '',phone: '',job: '',salary: '',image: '',entryDate: '',deptId: '',exprList: new Array<EmpExprModel>()}
}
</script>
<template><h1>员工管理</h1> <br><!-- 搜索栏 --><el-form :inline="true" :model="searchEmp" class="demo-form-inline"><el-form-item label="姓名"><el-input v-model="searchEmp.name" placeholder="请输入员工姓名" clearable /></el-form-item><el-form-item label="性别"><el-select v-model="searchEmp.gender" placeholder="请选择" clearable><el-option label="男" value="1" /><el-option label="女" value="2" /></el-select></el-form-item>
<el-form-item label="入职时间"><el-date-picker v-model="searchEmp.date" type="daterange" value-format="YYYY-MM-DD" range-separator="到" start-placeholder="开始时间" end-placeholder="结束时间"/></el-form-item>
<el-form-item><el-button type="primary" @click="queryPage()">查询</el-button><el-button type="default" @click="reset()">重置</el-button></el-form-item></el-form>
<!-- 按钮 --><el-button type="primary" @click="add(); resetForm(empFormRef); clearEmp()">+ 新增员工</el-button><el-button type="danger" @click="">- 批量删除</el-button><br><br>
<!-- 表格 --><!-- 列表展示 --><el-table :data="tableData" border style="width: 100%" fit @selection-change="handleSelectionChange"><el-table-column type="selection" width="55" /><el-table-column prop="name" label="姓名" align="center" width="130px" /><el-table-column label="性别" align="center" width="100px"><template #default="scope">{{ scope.row.gender == 1 ? '男' : '女' }}</template></el-table-column><el-table-column prop="image" label="头像" align="center"><template #default="scope"><img :src="scope.row.image" height="40"></template></el-table-column><el-table-column prop="deptName" label="所属部门" align="center" /><el-table-column prop="job" label="职位" align="center" width="100px"><template #default="scope"><span v-if="scope.row.job == 1">班主任</span><span v-else-if="scope.row.job == 2">讲师</span><span v-else-if="scope.row.job == 3">学工主管</span><span v-else-if="scope.row.job == 4">教研主管</span><span v-else-if="scope.row.job == 5">咨询师</span><span v-else>其他</span></template></el-table-column><el-table-column prop="entryDate" label="入职时间" align="center" width="130px" /><el-table-column prop="updateTime" label="最后修改时间" align="center" /><el-table-column label="操作" align="center"><template #default="scope"><el-button type="primary" size="small" @click="">编辑</el-button><el-button type="danger" size="small" @click="">删除</el-button></template></el-table-column></el-table><br>
<!-- 分页组件Pagination --><el-paginationv-model:current-page="pagination.currentPage"v-model:page-size="pagination.pageSize":page-sizes="[5, 10, 20, 50, 100]"layout="total, sizes, prev, pager, next, jumper":total="pagination.total"@size-change="handleSizeChange"@current-change="handleCurrentChange"/><!-- 新增员工/修改员工-Dialog --><!-- 新增/修改员工对话框 --><el-dialog v-model="dialogFormVisible" :title="formTitle"><el-form :model="emp" ref="empFormRef" :rules="rules"><!-- 第一行 --><el-row><el-col :span="12"><el-form-item label="用户名" :label-width="labelWidth" prop="username"><el-input v-model="emp.username" /></el-form-item></el-col><el-col :span="12"><el-form-item label="姓名" :label-width="labelWidth" prop="name"><el-input v-model="emp.name" /></el-form-item></el-col></el-row><!-- 第二行 --><el-row><el-col :span="12"><el-form-item label="性别" :label-width="labelWidth" prop="gender"><el-select v-model="emp.gender" placeholder="请选择" style="width: 100%;"><el-option v-for="gender in genders" :label="gender.name" :value="gender.value" /></el-select></el-form-item></el-col><el-col :span="12"><el-form-item label="手机号" :label-width="labelWidth" prop="phone"><el-input v-model="emp.phone" /></el-form-item></el-col></el-row>
<!-- 第三行 --><el-row><el-col :span="12"><el-form-item label="薪资" :label-width="labelWidth" prop="salary"><el-input v-model="emp.salary" /></el-form-item></el-col><el-col :span="12"><el-form-item label="入职日期" :label-width="labelWidth"><el-date-picker v-model="emp.entryDate" type="date" placeholder="请选择入职日期" value-format="YYYY-MM-DD" style="width: 100%;"/></el-form-item></el-col></el-row>
<!-- 第四行 --><el-row><el-col :span="12"><el-form-item label="所属部门" :label-width="labelWidth"><el-select v-model="emp.deptId" placeholder="请选择" style="width: 100%;"><el-option v-for="dept in depts" :label="dept.name" :value="dept.id" /></el-select></el-form-item></el-col><el-col :span="12"><el-form-item label="职位" :label-width="labelWidth"><el-select v-model="emp.job" placeholder="请选择" style="width: 100%;"><el-option v-for="job in jobs" :label="job.name" :value="job.value" /></el-select></el-form-item></el-col></el-row>
<!-- 第五行 --><el-row :gutter="10"><el-col :span="24"><el-form-item label="头像" label-width="80px"><el-upload class="avatar-uploader" action="/api/upload" :show-file-list="false":on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload"><img v-if="emp.image" :src="emp.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="工作经历" :label-width="labelWidth"><el-button type="success" size="small" @click="addWorkItem">+ 添加工作经历</el-button></el-form-item></el-col></el-row>
<!-- 第七...行 --><el-row :gutter="5" v-for="expr in emp.exprList"><el-col :span="10"><el-form-item label="时间" size="small" :label-width="labelWidth"><el-date-picker v-model="expr.exprDate" type="daterange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" value-format="YYYY-MM-DD"/></el-form-item></el-col><el-col :span="6"><el-form-item label="公司" size="small"><el-input v-model="expr.company" placeholder="公司名称"/></el-form-item></el-col>
<el-col :span="6"><el-form-item label="职位" size="small"><el-input v-model="expr.job" placeholder="职位名称"/></el-form-item></el-col>
<el-col :span="2"><el-form-item size="small"><el-button type="danger" @click="delWorkItem(expr)">- 删除</el-button></el-form-item></el-col></el-row>
</el-form>
<template #footer><span class="dialog-footer"><el-button @click="dialogFormVisible = false; resetForm(empFormRef)">取消</el-button><el-button type="primary" @click="save(empFormRef)">保存</el-button></span></template>
</el-dialog>
</template>
<style scoped>.avatar-uploader .avatar {width: 78px;height: 78px;display: block;}.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 #ccc;border-radius: 5px;}
</style>
相关文章:
【免费Web系列】大家好 ,今天是Web课程的第二一天点赞收藏关注,持续更新作品 !
这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r 员工管理 1. 条件分页查询 1.1 概述 在页面原型中,我们可以看到在查询员工信息列表时,既需要根据条件动态查询,还需要对查询的结果进行分页处理。 那要完成这个页面…...
【单片机毕业设计选题24007】-基于STM32和阿里云的家庭健康数据监测系统
系统功能: 本课题设计是基于STM32单片机作为控制主体,通过HX711称重模块,HC-SR04超声波测距模块,红外测温,心率传感器等模块通过I2C或SPI接口与STM32进行通信,并读取传感器输出的身高,体重,心率…...
基于微信公众号开发h5的前端流程
1.首先公众号进行配置,必须要https域名 还有个txt文件,有弹框提示需要下载放在服务器上 前端处理code的代码封装 // 微信公众号授权 export function wxAuthorize(calback) {// 非静默授权,第一次有弹框 这里的回调页面就是放在服务器上微信…...
python操作数据库,django操作数据库
安装驱动 pip install mysqlclient工程同名app下的settings.py DATABASES {default: {ENGINE: django.db.backends.mysql,NAME: test,USER: root,PASSWORD: hirain123,HOST: localhost,PORT: 3306,OPTION; {init_command: SET sql_model"STRICT_TRANS_TABLES",}} …...
React框架资源
React框架资源可以从多个方面获取,包括官方文档、教程、书籍、社区等。以下是一些React框架资源的清晰分点和归纳: 官方文档 新官方文档:React在2023年3月发布了全新的官方文档,位于https://react.dev/。新文档包含教程、指南…...
【数据结构】初识数据结构之复杂度与链表
【数据结构】初识数据结构之复杂度与链表 🔥个人主页:大白的编程日记 🔥专栏:C语言学习之路 文章目录 【数据结构】初识数据结构之复杂度与链表前言一.数据结构和算法1.1数据结构1.2算法1.3数据结构和算法的重要性 二.时间与空间…...
word怎么单页横向设置(页码不连续版)
打开word,将光标放在第一页的最后位置。 然后点击布局下的分隔符,选择下一页。 将光标放在第二页的开头,点击布局下的纸张方向,选择横向即可。 效果展示。 PS:如果那一页夹在两页中间,那么在…...
搭建 Tomcat 集群【Nginx 负载均衡】
当我们想要提高后端服务器的并发性能,可以通过分配更多的资源给 Tomcat 服务器,但是这只能提高一部分的性能。因为每台 Tomcat 的服务器是有最大连接数为 200.所以即可拥有无穷无尽的内存,也会因为单台 Tomcat 的原因而无法发挥这些资源的最大…...
深入理解指针(二)
目录 1. 数组名的理解 2. 使用指针访问数组 3. ⼀维数组传参的本质 4. 冒泡排序 5. 二级指针 6. 指针数组 7. 指针数组模拟二维数组 1. 数组名的理解 有下面一段代码: #include <stdio.h> int main() {int arr[10] { 1,2,3,4,5,6,7,8,9,10 };int* p &arr[…...
【Qt 学习笔记】Qt窗口 | 标准对话框 | 文件对话框QFileDialog
博客主页:Duck Bro 博客主页系列专栏:Qt 专栏关注博主,后期持续更新系列文章如果有错误感谢请大家批评指出,及时修改感谢大家点赞👍收藏⭐评论✍ Qt窗口 | 标准对话框 | 文件对话框QFileDialog 文章编号:Q…...
换卡槽=停机?新手机号使用指南!
刚办理的手机号莫名其妙的就被停用了?这到底是怎么回事?这篇文章快来学习一下吧。 先说一下,你的手机为什么被停机? 现在运营商对于手机卡的使用有着非常严格的要求,尤其是刚办理的新号码,更是“严上加…...
主题切换之根元素CSS自定义类
要实现CSS样式的主题切换,可以通过在HTML中添加一个按钮来触发JavaScript事件,进而通过JavaScript动态修改HTML元素的class或直接切换CSS文件,以达到改变页面整体风格的目的。以下是实现这一功能的步骤、原理及代码示例。 原理: …...
如何在 ASP.NET Core Web Api 项目中应用 NLog 写日志?
前言 昨天分享了在 .NET Core Console 项目中应用 NLog 写日志的详细例子,有几位小伙伴私信说 ASP.NET Core Web Api 项目中无法使用,其实在 ASP.NET Core Web Api 项目中应用 NLog 写日志,跟 .NET Core Console 项目是有些不一样的…...
selenium execute_script常用方法汇总
driver.execute_script() 是 Selenium WebDriver 中非常强大且灵活的功能,可以用来执行任意的 JavaScript 代码在浏览器上下文中。以下是一些常用的 execute_script() 方法的例子和用法: 修改元素的属性和值 python# 修改输入框的值 driver.execute_sc…...
如何选择最佳的APP封装平台-小猪APP分发为您解忧
在开发移动应用程序的过程中,选择一个可靠的APP封装平台显得尤为重要。无论你是初创企业还是大型企业,找到一个合适的平台可以大大简化你的开发流程。如何选择最佳的APP封装平台呢?今天我们就来聊聊这个话题,并重点介绍一下小猪AP…...
Linux基础 (十八):Libevent 库的安装与使用
目录 一、Libevent 概述 1.0 Libevent的安装 1.0.1 使用源码方式 1.0.2 终端命令行安装 1.1 主要特性 1.2 主要组件 1.3 Libevent 使用模型 1.4 原理 1.5 使用的基本步骤 1.5.1 初始化事件基础设施 1.5.2. 创建和绑定服务器套接字 1.5.3. 设置监听事件 1.5.4. 定义…...
冒泡排序的详细介绍 , 以及c , python , Java的实现方法
冒泡排序(Bubble Sort)是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成…...
使用llama.cpp实现LLM大模型的格式转换、量化、推理、部署
使用llama.cpp实现LLM大模型的格式转换、量化、推理、部署 概述 llama.cpp的主要目标是能够在各种硬件上实现LLM推理,只需最少的设置,并提供最先进的性能。提供1.5位、2位、3位、4位、5位、6位和8位整数量化,以加快推理速度并减少内存使用。…...
给你一个扫码支付的二维码,如何写测试用例?
前言 面试的时候,经常会临场出题:给你一个xxx, 如何测试, 或者说如何写测试用例?xxx可以是圆珠笔,水杯,电梯等生活中常见的场景。 那么给你一个支付的二维码,如何写测试用例呢? 二维码扫码支…...
计算机专业在未来的发展与抉择
目录 前言 计算机专业的发展历史 计算机专业的前景 计算机专业的挑战 如何判断自己是否适合计算机专业 计算机行业的未来发展态势 作为过来人和从业者 前言 随着2024年高考落幕,数百万高三学生又将面临人生中的重要抉择:选择大学专业。在这个关键…...
龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
