【免费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年高考落幕,数百万高三学生又将面临人生中的重要抉择:选择大学专业。在这个关键…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...

自然语言处理——文本分类
文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益(IG) 分类器设计贝叶斯理论:线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别, 有单标签多类别文本分类和多…...

快速排序算法改进:随机快排-荷兰国旗划分详解
随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...

内窥镜检查中基于提示的息肉分割|文献速递-深度学习医疗AI最新文献
Title 题目 Prompt-based polyp segmentation during endoscopy 内窥镜检查中基于提示的息肉分割 01 文献速递介绍 以下是对这段英文内容的中文翻译: ### 胃肠道癌症的发病率呈上升趋势,且有年轻化倾向(Bray等人,2018&#x…...