07.人力资源管理项目 -员工管理==>头部组件封装、全局注册、Vue插件(Vue.use)、数据渲染、格式化formatter、表格table排序
这一次从PageTools组件的封装的着手,既帮忙复习了组件注册的不同方式又拓展了对vue插件的使用方法以方便维护main.js文件;
在分页组件Pagenation中,只实现了标签创建和点击的事件触发和回调, JS中的数据逻辑包括请求都需要自己写;
table表格可以通过formatter进行格式化,值得注意的是,formatter方法有一些自带的参数,但经过我的实际使用,发现其中的cellValue有时候是Number类型的,有时候是String类型的,这个一开始折磨了我半天,不过解决方法也很简单,在cellValue前面加一个加号‘+’即可;
对于格式化入职时间,实际体验下来utils下的parseTime方法并不好用,会出现大量的NaN,使用第三方库dayjs的体验则相对要好很多;
对于设置表格自排序,涉及两个以上的排序我还是没太搞懂,至少在时间上默认的排序并不像element-ui中那么好用,哪怕是序号的排序也人需要通过:sort-method="" 来自己重写排序方法,不过求差排序的方式还是不错的;
01.员工管理-头部组件-封装使用
目标
核心功能分析:
- PageTools 公共组件封装
- Excel 导入导出功能
- 员工列表表格渲染
- 分页功能
讲解
分析
在后续的业务开发中,我们经常会用到一个类似下图的工具栏,因此需要可以将其封装成公共组件,方便复用
问: 左侧的提示消息 和 右侧按钮 在每个页面中并不是一致的,怎么处理呢 ?
答案
- 具名插槽
实现
-
创建组件实现基本结构,新建
src\components\PageTools\index.vue文件,,实现页面结构的创建<template> <el-card> <div class="page-tools"> <!-- 左侧 --> <div class="left"> <div class="tips"> <i class="el-icon-info" /> <span>文字区域</span> </div> </div> <div class="right"> <!-- 右侧 --> 按钮区域 </div> </div> </el-card> </template> <script> export default {} </script> <style lang="scss" scoped> .page-tools { display: flex; justify-content: space-between; align-items: center; .tips { line-height: 34px; padding: 0px 15px; border-radius: 5px; border: 1px solid rgba(145, 213, 255, 1); background: rgba(230, 247, 255, 1); i { margin-right: 10px; color: #409eff; } } } </style> -
定义左右两侧内容的具名插槽
- 我们定义了两个具名插槽,将来在使用组件的时候,只需要按照对应的插槽名称就可以在特定的位置插入内容
- slot-left 左侧的文本区域
- slot-right 右侧按钮区域
<template> <el-card> <div class="page-tools"> <!-- 左侧 --> <div class="left"> <div class="tips"> <i class="el-icon-info" /> <slot name="slot-left"> <span>文字区域</span> </slot> </div> </div> <div class="right"> <!-- 右侧 --> <slot name="slot-right"> <span>按钮区域</span> </slot> </div> </div> </el-card> </template> -
在
src\views\employees\index.vue组件中使用封装的 PageTools 组件- 导入注册组件
- 使用组件的时候注入自定义内容
<template> <div class="employees-container"> <div class="app-container"> <!-- 通用工具栏组件使用 --> <page-tools> <!-- 自定义左侧内容 --> <template #slot-left> <span>共 19 条记录</span> </template> <!-- 自定义右侧内容 --> <template #slot-right> <el-button type="danger" size="small">导入excel</el-button> <el-button type="success" size="small">导出excel</el-button> <el-button type="primary" size="small">新增员工</el-button> </template> </page-tools> </div> </div> </template> <script> // 导入组件 import PageTools from '@/components/PageTools' export default { components: { PageTools // 导入自定义组件 } } </script> <style lang="scss" scoped></style>
小结
-
我们为什么要封装头部组件?
答案
- 因为后面有多个页面需要使用的这个组件, 所以我们要封装下
02.员工管理-头部组件-全局注册
目标
- 将工具栏组件进行全局注册
- 熟练将组件抽取成全局组件的流程
讲解
分析
在上一节,我们将业务组件在 components 中进行注册,定义成了局部组件,这种方式能够满足我们的业务,但这种写法会导致公用的组件需要在多个组件中分别进行注册,造成代码冗余。因此我们会将 全局通用组件进行全局注册
- 将组件在入口文件进行导入
- 使用 Vue.component 进行全局组件的绑定
实现
-
在 main.js 文件中导入组件并进行注册
// 导入组件 import PageTools from '@/components/PageTools' // 注册组件 Vue.component('PageTools', PageTools) -
将局部注册的导入、注册代码进行删除
小结
-
组件使用的基本步骤有哪些?
答案
- 创建, 引入, 注册, 使用组件
-
注册组件, 有哪2种方式?
答案
- 局部components选项注册
- main.js入口处, 用Vue.component()全局注册组件方法
-
局部注册和全局注册的区别?
答案
- 局部注册, 只能在当前.vue组件中使用
- 全局注册, 任意组件内使用, 但是一定要先走全局注册再走使用
03.员工管理-头部组件-Vue插件
上一节,虽然我们非常方便的将组件进行了全局注册
但是大家想象一个场景:如果我们需要注册的全局组件非常多,我们需要一个一个引入,然后分别调用 Vue.component 方法
main.js 文件会变的很大,不好维护,为了解决这个问题,我们学习一下 Vue插件 的形式
目标
掌握 Vue.use() 的用法,能用它来注册全局组件
讲解
分析
-
什么是 Vue.use() ?
- 它是 Vue 提供一个静态方法,用来向 Vue 注册插件,点击 Vue-use 文档
-
使用格式 ?
Vue.use(obj)- Vue.use 可以接收一个对象,Vue.use(obj)
- 对象 obj 中需要提供一个 install 函数
- 在 Vue.use(obj) 时,会自动调用该 install 函数,并传入 Vue构造器
- 如果传入Vue.use()里是一个函数, 直接传入Vue构造器, 直接执行此函数
代码
-
在 main.js 文件中体验下 Vue.use()
const MyInstall = { install(Vue) { // 在 install 方法中,使用 Vue.component 挂载组件 Vue.component('PageTools', PageTools) } } Vue.use(MyInstall) -
我们可以把这个配置对象, 封装到
src/components/index.js中, 导出此对象到main.js中给Vue.use使用/** * 进行全局组件的挂载 */ import PageTools from './index.vue' export default { install(Vue) { Vue.component('PageTools', PageTools) } }
-
main.js中导入注册
// 导入组件 import GlobalComponents from '@/components/index.js' Vue.use(GlobalComponents)
小结
-
Vue.use方法有什么作用?
答案
- Vue.use用于给Vue添加中间件函数插件
-
Vue.use()中可以传入什么?
答案
- 如果传入的是函数, 执行此函数传入Vue类作为参数
- 如果传入的是对象, 必须有install方法, 会自动执行此方法, 传入Vue作为参数
-
为何要把对象封装的js文件内?
答案
- 以后要是再注册全局组件, 写到这个js中的插件对象中, 方便管理
04.员工管理-员工列表-静态结构
目标
实现员工管理页面静态结构搭建
讲解
分析
-
表格区域:使用 Table 表格 组件实现
- 操作列使用 作用域插槽 实现
-
分页区域:使用 Pagination 分页 组件实现
实现
<template>
<div class="employees-container">
<div class="app-container">
<!-- 通用工具栏组件使用 -->
<page-tools>
<!-- 自定义左侧内容 -->
<template #slot-left>
<span>共19条记录</span>
</template>
<!-- 自定义右侧内容 -->
<template #slot-right>
<el-button type="danger" size="small">导入excel</el-button>
<el-button type="success" size="small">导出excel</el-button>
<el-button type="primary" size="small">新增员工</el-button>
</template>
</page-tools>
<el-card style="margin-top: 10px;">
<el-table border>
<el-table-column label="序号" />
<el-table-column label="姓名" />
<el-table-column label="头像" />
<el-table-column label="手机号" />
<el-table-column label="工号" />
<el-table-column label="聘用形式" />
<el-table-column label="部门" />
<el-table-column label="入职时间" />
<el-table-column label="操作" width="280">
<template>
<el-button type="text" size="small">查看</el-button>
<el-button type="text" size="small">分配角色</el-button>
<el-button type="text" size="small">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<el-row type="flex" justify="center" align="middle" style="height: 60px">
<!-- 分页区域 -->
<el-pagination
:current-page="query.page"
:page-sizes="[10, 15, 20, 25]"
:page-size="query.size"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-row>
</el-card>
</div>
</div>
</template>
<script>
export default {
name: 'Employees',
data() {
return {
query: {
page: 1, // 页码
size: 10 // 每页条数
},
employeesList: [], // 员工列表
total: 0 // 数据总条数
}
},
methods: {
// 每页显示的条数发生改变时触发
handleSizeChange(newSize) {},
// 当前页面发生改变时触发
handleCurrentChange(newPage) {}
}
}
</script>
<style lang="scss" scoped></style>
小结
-
el-table-column有什么用?
答案
- elementUI封装的组件, 设置表格有多少列
-
el-table表格, 如何确定有多少行和列?
答案
- el-table中data属性对应数组里对象, 有多少个, 内部循环多少行表格
- el-table-column决定有多少列
-
Pagination组件特点是什么?
答案
- 只实现了标签创建和点击的事件触发和回调, JS中的数据逻辑包括请求都自己写
05.员工管理-员工列表-数据获取
目标
实现员工的数据加载
讲解
思路
- 封装获取员工列表 api
- 在创建组件时调用获取员工列表数据的方法
- 将返回的数据赋值给 data 中的数据
实现
-
封装获取员工列表 api,在
src/api/employees.js文件中封装员工的加载请求/** * @description: 获取员工列表 * @param {*} params {page:当前页,size:每页条数} * @return {*} */ export function getEmployeeListAPI(params) { return request({ url: '/sys/user', methods: 'get', params }) } -
在
api/index.js导出export * from './employees.js' -
调用接口获取数据,在组件
src/employees/employees.vue中引入上边定义的方法并调用created() { // 调用获取员工列表的方法 this.getEmployeeList() }, methods: { // 获取员工列表 async getEmployeeList() { // 调用获取员工列表的 API const res = await getEmployeeListAPI(this.query) console.log(res) // 根据返回的状态码给用户提示 if (!res.success) return this.$message.error(res.message) // 将返回的结果赋值 data 中的数据 this.employeesList = res.data.rows this.total = res.data.total } }
小结
-
获取员工列表数据, 如何只获取第1页的10条数据呢?
答案
- 需要注意传参, 当前页码和当前页需要的数量, 以及Pagination组件对应上
-
如何确保请求的数据页数和条数和Pagination组件显示的一致?
答案
- Vue准备页码page变量和size,影响标签显示, 影响数据的获取
06.员工管理-员工列表-数据渲染
目标
- 实现员工列表页面的渲染
- 实现员工列表页分页的渲染
讲解
分析
- 按后端数据的格式内容,修改对应 el-table-colum 的 prop 属性
- 按 el-pagination 组件的要求,在页面中添加与分页相关的数据项同时监听页面改变事件
实现
-
按后端数据的格式内容,修改对应 el-table-colum 的 prop 属性, ==直接复制==
<el-table border :data="employeesList"> <el-table-column label="序号" type="index" /> <el-table-column label="姓名" prop="username" /> <el-table-column label="头像" prop="staffPhoto" /> <el-table-column label="手机号" prop="mobile" /> <el-table-column label="工号" prop="workNumber" /> <el-table-column label="聘用形式" prop="formOfEmployment" /> <el-table-column label="部门" prop="departmentName" /> <el-table-column label="入职时间" prop="timeOfEntry" /> <el-table-column label="操作" width="280"> <template> <el-button type="text" size="small">查看</el-button> <el-button type="text" size="small">分配角色</el-button> <el-button type="text" size="small">删除</el-button> </template> </el-table-column> </el-table> -
按 el-pagination 组件的要求,在页面中添加与分页相关的数据项同时监听页面改变事件==已绑好==
<!-- 分页组件 --> <el-row type="flex" justify="center" align="middle" style="height: 60px"> <!-- 分页区域 --> <el-pagination :current-page="query.page" :page-sizes="[10, 15, 20, 25]" :page-size="query.size" layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange" /> </el-row> -
在事件处理函数中, 实现请求和切换数据的逻辑
methods: { // 每页显示的条数发生改变时触发 handleSizeChange(newSize) { this.query.size = newSize this.getEmployeeList() }, // 当前页面发生改变时触发 handleCurrentChange(newPage) { this.query.page = newPage this.getEmployeeList() } }
小结
-
分页切换数据是如何做的?
答案
- 使用分页组件, 监听分页的点击事件, 切换query的参数
- 然后重新请求数据, 拿到数据更新到表格里
07.员工管理-员工列表-格式化聘用形式
目标
将后端直接给到的数据做进行格式化处理
讲解
分析
-
后端给到我们前端的是一个数字类型的状态码(1, 2),每一个状态码对应有中文说明,其中
- 1 代表 正式
- 2 代表 非正式
-
问:那么如何将 数字类型的状态码 转换成对应的中文呢 ?
答:通过枚举的方式转换成文字即可,
枚举数据存放于我们提供的**
资源/枚举**中,可以将枚举下的 constant 文件夹复制到src/文件夹下{ // 聘用形式 hireType: [ { id: 1, value: '正式' }, { id: 2, value: '非正式' } ] // ...其他 }
-
问:表格某列如何格式化数据呢?
答:查阅elementUI文档, 借助
el-table-column的formatter属性进行设置<el-table-column prop="formOfEmployment" label="聘用形式" :formatter="formatter"> </el-table-column>methods: { formatter(row) { // row对应行的数据对象 return row.formOfEmployment; } }
实现
-
在
api/constant/employees.js定义工具对象==我们可以复制预习资料里对应的文件==
// 员工 export default { // 聘用形式 hireType: [ { id: 1, value: '正式' }, { id: 2, value: '非正式' } ] // ...其他 } -
给聘用形式这一栏绑定 formatter 属性
<el-table-column prop="formOfEmployment" label="聘用形式" :formatter="formatter" /> -
在 methods 中声明 formatter 方法
import Employees from '@/api/constant/employees' methods: { // 格式化表格的某一项 formatter(row, column, cellValue, index) { // 用数组的 find 方法找到 id = 1 的元素,再取出它的 value const obj = Employees.hireType.find((item) => item.id === +cellValue) return obj ? obj.value : '未知' } } -
预览效果
小结
-
Employees.hireType.find这个是什么意思?
答案
- 从Employees对象里取出某个属性的值, Employees.hireType是个数组, 然后用find查找符合目标的对象
08.员工管理-员工列表-格式化入职时间
目标
现在的时间, 有点不太准确, 我们给格式化成年-月-日格式
讲解
-
我们可以借助utils工具包里的parseTime方法格式化时间, 引入此方法并注册
import { parseTime } from '@/utils' export default { // ...其他省略 methods: { parseTime // 其他省略 } } -
在el-table-column中, 设置自定义显示列的内容, 设置显示格式
<el-table-column label="入职时间" prop="timeOfEntry"> <template v-slot="{ row }"> <span>{{ parseTime(row.timeOfEntry, '{yyyy}-{mm}-{dd}') }}</span> </template> </el-table-column>
小结
-
methods中只写一个单词什么意思?
答案
09.员工管理-员工列表-设置表格自排序
目标
点击入职时间后面的上下箭头,可以实现当前页数据按照入职时间升序或者降序排列
讲解
分析
查阅elemenet-ui文档 中的 table 已经提供了这个功能,直接使用即可
详见:表格-排序
基本步骤是:
- 给 el-table 添加 default-sort 属性,设置默认的排序列和排序顺序
- 给 el-table-column 添加 sortable 属性,即可实现以该列为基准的排序,接受一个
Boolean,默认为false
实现
-
给 el-table 添加 default-sort 属性
<el-table border :data="employeesList" :default-sort="{prop: 'workNumber'}"></el-table> -
给 el-table-column 添加 sortable 属性
<el-table-column label="工号" prop="workNumber" sortable />
-
发现排序有点问题, 分析发现工号的数据是字符串
思考, 如何自定义列的排序过程, 查elementUI文档发现: table排序, 用el-table-column身上的sort-method属性, 自定义排序方法过程, 类似Array的sort方法
-
在el-table-column上添加自定义排序属性和指定方法名
<el-table-column label="工号" prop="workNumber" sortable :sort-method="workNumberSortFn" />
-
在methods选项重定义方法, 以及排序的过程
关于数组的sort排序方法可以看这个链接数组的sort排序方法
// 员工列-自定义排序 workNumberSortFn(a, b) { // 打印a和b发现是表格数组里的对象 // sort方法使用: // 如果return小于0, 那么 a 会被排列到 b 之前 // 如果return等于0, 那么 a 和 b 位置不变 // 如果return大于0, 那么 a 会被排列到 b 之后 return parseInt(a.workNumber) - parseInt(b.workNumber) }
小结
-
为什么我们设置几个东西就可以让el-table组件实现排序了?
答案
- 把数据传入到el-table组件内, 然后根据你设置的字段sortable和设置哪个字段用于排序
- 内部会根据你点击的方向, 来设置数组的顺序影响页面铺设