07.人力资源管理项目 -员工管理==>头部组件封装、全局注册、Vue插件(Vue.use)、数据渲染、格式化famatter、表格table排序

144 阅读11分钟

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.员工管理-头部组件-封装使用

目标

核心功能分析:

  1. PageTools 公共组件封装
  2. Excel 导入导出功能
  3. 员工列表表格渲染
  4. 分页功能

讲解

分析

在后续的业务开发中,我们经常会用到一个类似下图的工具栏,因此需要可以将其封装成公共组件,方便复用

问: 左侧的提示消息 和 右侧按钮 在每个页面中并不是一致的,怎么处理呢 ?

答案

  • 具名插槽

实现

  1. 创建组件实现基本结构,新建 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>
  2. 定义左右两侧内容的具名插槽

    • 我们定义了两个具名插槽,将来在使用组件的时候,只需要按照对应的插槽名称就可以在特定的位置插入内容
    • 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>
    
  3. 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>

小结

  1. 我们为什么要封装头部组件?

    答案

    • 因为后面有多个页面需要使用的这个组件, 所以我们要封装下

02.员工管理-头部组件-全局注册

目标

  1. 将工具栏组件进行全局注册
  2. 熟练将组件抽取成全局组件的流程

讲解

分析

在上一节,我们将业务组件在 components 中进行注册,定义成了局部组件,这种方式能够满足我们的业务,但这种写法会导致公用的组件需要在多个组件中分别进行注册,造成代码冗余。因此我们会将 全局通用组件进行全局注册

  1. 将组件在入口文件进行导入
  2. 使用 Vue.component 进行全局组件的绑定

实现

  1. 在 main.js 文件中导入组件并进行注册

    // 导入组件
    import PageTools from '@/components/PageTools'// 注册组件
    Vue.component('PageTools', PageTools)
    
  2. 将局部注册的导入、注册代码进行删除

小结

  1. 组件使用的基本步骤有哪些?

    答案

    • 创建, 引入, 注册, 使用组件

  2. 注册组件, 有哪2种方式?

    答案

    • 局部components选项注册
    • main.js入口处, 用Vue.component()全局注册组件方法

  3. 局部注册和全局注册的区别?

    答案

    • 局部注册, 只能在当前.vue组件中使用
    • 全局注册, 任意组件内使用, 但是一定要先走全局注册再走使用

03.员工管理-头部组件-Vue插件

上一节,虽然我们非常方便的将组件进行了全局注册

但是大家想象一个场景:如果我们需要注册的全局组件非常多,我们需要一个一个引入,然后分别调用 Vue.component 方法

main.js 文件会变的很大,不好维护,为了解决这个问题,我们学习一下 Vue插件 的形式

目标

掌握 Vue.use() 的用法,能用它来注册全局组件

讲解

分析

  1. 什么是 Vue.use() ?

    • 它是 Vue 提供一个静态方法,用来向 Vue 注册插件,点击 Vue-use 文档
  2. 使用格式 ?

    Vue.use(obj)
    
    • Vue.use 可以接收一个对象,Vue.use(obj)
    • 对象 obj 中需要提供一个 install 函数
    • 在 Vue.use(obj) 时,会自动调用该 install 函数,并传入 Vue构造器
    • 如果传入Vue.use()里是一个函数, 直接传入Vue构造器, 直接执行此函数

代码

  1. 在 main.js 文件中体验下 Vue.use()

    const MyInstall = {
      install(Vue) {
        // 在 install 方法中,使用 Vue.component 挂载组件
        Vue.component('PageTools', PageTools)
      }
    }
    ​
    Vue.use(MyInstall)
    
  2. 我们可以把这个配置对象, 封装到src/components/index.js中, 导出此对象到main.js中给Vue.use使用

    /**
     * 进行全局组件的挂载
     */import PageTools from './index.vue'export default {
      install(Vue) {
        Vue.component('PageTools', PageTools)
      }
    }
    ​
    
  1. main.js中导入注册

    // 导入组件
    import GlobalComponents from '@/components/index.js'
    Vue.use(GlobalComponents)
    

小结

  1. Vue.use方法有什么作用?

    答案

    • Vue.use用于给Vue添加中间件函数插件

  2. Vue.use()中可以传入什么?

    答案

    • 如果传入的是函数, 执行此函数传入Vue类作为参数
    • 如果传入的是对象, 必须有install方法, 会自动执行此方法, 传入Vue作为参数

  1. 为何要把对象封装的js文件内?

    答案

    • 以后要是再注册全局组件, 写到这个js中的插件对象中, 方便管理

04.员工管理-员工列表-静态结构

目标

实现员工管理页面静态结构搭建

讲解

分析

  1. 表格区域:使用 Table 表格 组件实现

    • 操作列使用 作用域插槽 实现
  2. 分页区域:使用 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>

小结

  1. el-table-column有什么用?

    答案

    • elementUI封装的组件, 设置表格有多少列

  2. el-table表格, 如何确定有多少行和列?

    答案

    • el-table中data属性对应数组里对象, 有多少个, 内部循环多少行表格
    • el-table-column决定有多少列

  3. Pagination组件特点是什么?

    答案

    • 只实现了标签创建和点击的事件触发和回调, JS中的数据逻辑包括请求都自己写

05.员工管理-员工列表-数据获取

目标

实现员工的数据加载

讲解

思路

  • 封装获取员工列表 api
  • 在创建组件时调用获取员工列表数据的方法
  • 将返回的数据赋值给 data 中的数据

实现

  1. 封装获取员工列表 api,在 src/api/employees.js 文件中封装员工的加载请求

    /**
     * @description: 获取员工列表
     * @param {*} params {page:当前页,size:每页条数}
     * @return {*}
     */
    export function getEmployeeListAPI(params) {
      return request({
        url: '/sys/user',
        methods: 'get',
        params
      })
    }
    
  2. api/index.js导出

    export * from './employees.js'
    
  3. 调用接口获取数据,在组件 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. 获取员工列表数据, 如何只获取第1页的10条数据呢?

    答案

    • 需要注意传参, 当前页码和当前页需要的数量, 以及Pagination组件对应上

  2. 如何确保请求的数据页数和条数和Pagination组件显示的一致?

    答案

    • Vue准备页码page变量和size,影响标签显示, 影响数据的获取

06.员工管理-员工列表-数据渲染

目标

  1. 实现员工列表页面的渲染
  2. 实现员工列表页分页的渲染

讲解

分析

  1. 按后端数据的格式内容,修改对应 el-table-colum 的 prop 属性
  2. 按 el-pagination 组件的要求,在页面中添加与分页相关的数据项同时监听页面改变事件

实现

  1. 按后端数据的格式内容,修改对应 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>
    
  2. 按 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>
    
  3. 在事件处理函数中, 实现请求和切换数据的逻辑

    methods: {
      // 每页显示的条数发生改变时触发
      handleSizeChange(newSize) {
        this.query.size = newSize
        this.getEmployeeList()
      },
    ​
      // 当前页面发生改变时触发
      handleCurrentChange(newPage) {
        this.query.page = newPage
        this.getEmployeeList()
      }
    }
    

小结

  1. 分页切换数据是如何做的?

    答案

    • 使用分页组件, 监听分页的点击事件, 切换query的参数
    • 然后重新请求数据, 拿到数据更新到表格里

07.员工管理-员工列表-格式化聘用形式

目标

将后端直接给到的数据做进行格式化处理

讲解

分析

  1. 后端给到我们前端的是一个数字类型的状态码(1, 2),每一个状态码对应有中文说明,其中

    • 1 代表 正式
    • 2 代表 非正式
  1. 问:那么如何将 数字类型的状态码 转换成对应的中文呢 ?

    答:通过枚举的方式转换成文字即可,

    枚举数据存放于我们提供的**资源/枚举**中,可以将枚举下的 constant 文件夹复制到src/文件夹下

    {
      // 聘用形式
      hireType: [
        {
          id: 1,
          value: '正式'
        },
        {
          id: 2,
          value: '非正式'
        }
      ]
      // ...其他
    }
    
  1. 问:表格某列如何格式化数据呢?

    答:查阅elementUI文档, 借助 el-table-columnformatter 属性进行设置

    <el-table-column
      prop="formOfEmployment"
      label="聘用形式"
      :formatter="formatter">
    </el-table-column>
    
    methods: {
      formatter(row) {
        // row对应行的数据对象
        return row.formOfEmployment;
      }
    }
    

    image-20230503231617341

    image-20230503231951164

    image-20230503232024882

实现

  1. api/constant/employees.js定义工具对象

    ==我们可以复制预习资料里对应的文件==

    // 员工
    export default {
      // 聘用形式
      hireType: [
        {
          id: 1,
          value: '正式'
        },
        {
          id: 2,
          value: '非正式'
        }
      ]
      // ...其他
    }
    
  2. 给聘用形式这一栏绑定 formatter 属性

    <el-table-column prop="formOfEmployment" label="聘用形式" :formatter="formatter" />
    
  3. 在 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 : '未知'
        }
    }
    

    image-20230504160749842

  4. 预览效果

小结

  1. Employees.hireType.find这个是什么意思?

    答案

    • 从Employees对象里取出某个属性的值, Employees.hireType是个数组, 然后用find查找符合目标的对象

08.员工管理-员工列表-格式化入职时间

目标

现在的时间, 有点不太准确, 我们给格式化成年-月-日格式

讲解

  1. 我们可以借助utils工具包里的parseTime方法格式化时间, 引入此方法并注册

    import { parseTime } from '@/utils'export default {
        // ...其他省略
        methods: {
            parseTime
            // 其他省略
        }
    }
    
  2. 在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>
    

小结

  1. methods中只写一个单词什么意思?

    答案

09.员工管理-员工列表-设置表格自排序

目标

点击入职时间后面的上下箭头,可以实现当前页数据按照入职时间升序或者降序排列

讲解

分析

查阅elemenet-ui文档 中的 table 已经提供了这个功能,直接使用即可

详见:表格-排序

基本步骤是:

  1. 给 el-table 添加 default-sort 属性,设置默认的排序列和排序顺序
  2. 给 el-table-column 添加 sortable 属性,即可实现以该列为基准的排序,接受一个Boolean,默认为false

实现

  1. 给 el-table 添加 default-sort 属性

    <el-table border :data="employeesList" :default-sort="{prop: 'workNumber'}"></el-table>
    
  2. 给 el-table-column 添加 sortable 属性

    <el-table-column label="工号" prop="workNumber" sortable />
    
  1. 发现排序有点问题, 分析发现工号的数据是字符串

    思考, 如何自定义列的排序过程, 查elementUI文档发现: table排序, 用el-table-column身上的sort-method属性, 自定义排序方法过程, 类似Array的sort方法

  1. 在el-table-column上添加自定义排序属性和指定方法名

    <el-table-column label="工号" prop="workNumber" sortable :sort-method="workNumberSortFn" />
    
  1. 在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)
    }
    

小结

  1. 为什么我们设置几个东西就可以让el-table组件实现排序了?

    答案

    • 把数据传入到el-table组件内, 然后根据你设置的字段sortable和设置哪个字段用于排序
    • 内部会根据你点击的方向, 来设置数组的顺序影响页面铺设