人力资源中台项目(day05)

组织架构树形结构布局

目标:使用element-UI组件布局组织架构的基本布局

认识组织架构

组织架构产品prd

一个企业的组织架构是该企业的灵魂,组织架构多常采用树形金字塔式结构,本章节,我们布局出页面的基本结构

实现组织架构的头部内容

首先实现头部的结构,采用element的行列布局

       
    
        
        
          
            江苏传智播客教育科技股份有限公司
          
          
            
              
              负责人
              
                
                
                  
                    操作
                  
                  
                  
                    添加子部门
                  
                
              
            
          
        
         
      

样式

树形组件认识

接下来,实现树形的结构,采用element的tree组件, 如图效果

树形组件属性

参数 说明 类型 可选值 默认值
default-expand-all 是否默认展开所有节点 boolean
data 展示数据 array
node-key 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的 String
props 配置选项,具体看下表 object

props属性

参数 说明 类型 可选值 默认值
label 指定节点标签为节点对象的某个属性值 string, function(data, node)
children 指定子树为节点对象的某个属性值 string
disabled 指定节点选择框是否禁用为节点对象的某个属性值 boolean, function(data, node)
isLeaf 指定节点是否为叶子节点,仅在指定了 lazy 属性的情况下生效 boolean, function(data, node)

data是组成树形数据的关键,如下的数据便能构建树形数据

 [{
          label: '一级 1',
          children: [{
            label: '二级 1-1',
            children: [{
              label: '三级 1-1-1'
            }]
          }]
        }, {
          label: '一级 2',
          children: [{
            label: '二级 2-1',
            children: [{
              label: '三级 2-1-1'
            }]
          }, {
            label: '二级 2-2',
            children: [{
              label: '三级 2-2-1'
            }]
          }]
        }, {
          label: '一级 3',
          children: [{
            label: '二级 3-1',
            children: [{
              label: '三级 3-1-1'
            }]
          }, {
            label: '二级 3-2',
            children: [{
              label: '三级 3-2-1'
            }]
          }]
        }]

实现树形的静态组织架构

由此,我们首先实现静态数据的组织架构

 
 
export default {
  data() {
    return {
      defaultProps: {
        label: 'name'
      },
      departs: [
        { name: '总裁办', children: [{ name: '董事会' }] },
        { name: '行政部' },
        { name: '人事部' }
      ]
    }
  }
}

接下来,对每个层级节点增加显示内容,此时需要用到tree的插槽

最终形成静态结构效果

提交代码

本节任务:完成树形结构的显示

将树形的操作内容单独抽提成组件

目标: 将树形的操作内容单独抽提成组件

封装单独的树操作栏组件

通过第一个章节,我们发现,树形的顶级内容实际和子节点的内容是一致的,此时可以将该部分抽提成一个组件,节省代码

组件 src/views/departments/components/tree-tools.vue


​
​

在组织架构中应用操作栏组件

接下来,在src/views/departments/index.vue进行代码的简化

上面代码中,company变量需要在data中定义

company: { name: '江苏传智播客教育科技股份有限公司', manager: '负责人' },

同时,由于在两个位置都使用了该组件,但是放置在最上层的组件是不需要显示 删除部门编辑部门

所以,增加一个新的属性 isRoot(是否根节点)进行控制

 props: {
    treeNode: {
      required: true, // 设置当前数据为必填
      type: Object // 类型是Object
    },
    isRoot: {
      type: Boolean,
      default: false
    }
  }
 
     

组件中, 根据isRoot判断显示

     
          编辑部门
         删除部门

通过封装,代码看上去更加紧凑,简洁,这就是封装的魅力

提交代码

本节任务:将树形内容单独抽提组件

获取组织架构数据,并进行树形处理

目标获取真实的组织架构数据,并将其转化成树形数据显示在页面上

封装API接口,获取组织架构数据

现在基本的静态结构已经形成,接下来需要获取真实的数据

首先,封装获取组织架构的请求 src/api/departments.js

/** *
 *
 * 获取组织架构数据
 * **/
export function getDepartments() {
  return request({
    url: '/company/department'
  })
}
​

在钩子函数中调用接口

import TreeTools from './components/tree-tools'
import { getDepartments } from '@/api/departments'
export default {
  components: {
    TreeTools
  },
  data() {
    return {
      company: { }, // 就是头部的数据结构
      departs: [],
      defaultProps: {
        label: 'name' // 表示 从这个属性显示内容
      }
    }
  },
  created() {
    this.getDepartments() // 调用自身的方法
  },
  methods: {
    async getDepartments() {
      const result = await getDepartments()
      this.company = { name: result.companyName, manager: '负责人' }
      this.departs = result.depts // 需要将其转化成树形结构
      console.log(result)
    }
  }
}

将数组数据转化成树形结构

然后,我们需要将列表型的数据,转化成树形数据,这里需要用到递归算法

封装一个工具方法,src/utils/index.js

/** *
 *
 *  将列表型的数据转化成树形数据 => 递归算法 => 自身调用自身 => 一定条件不能一样, 否则就会死循环
 *  遍历树形 有一个重点 要先找一个头儿
 * ***/
export function tranListToTreeData(list, rootValue) {
  var arr = []
  list.forEach(item => {
    if (item.pid === rootValue) {
      // 找到之后 就要去找 item 下面有没有子节点
      const children = tranListToTreeData(list, item.id)
      if (children.length) {
        // 如果children的长度大于0 说明找到了子节点
        item.children = children
      }
      arr.push(item) // 将内容加入到数组中
    }
  })
  return arr
}

调用转化方法,转化树形结构

  this.company = { name: result.companyName, manager: '负责人' } // 这里定义一个空串  因为 它是根 所有的子节点的数据pid 都是 ""
   this.departs = tranListToTreeData(result.depts, '')

这样一来,树形数据就有了,下一章节,就可以针对部门进行操作

提交代码

本节任务 获取组织架构数据,并进行树形处理

删除部门功能实现

目标实现操作功能的删除功能

封装删除接口,注册下拉菜单事件

首先,封装删除功能模块 src/api/departments.js

/** *
 *  根据id根据部门  接口是根据restful的规则设计的   删除 delete  新增 post  修改put 获取 get
 * **/
export function delDepartments(id) {
  return request({
    url: `/company/department/${id}`,
    method: 'delete'
  })
}

然后,在tree-tools组件中,监听下拉菜单的点击事件 src/views/departments/index.vue

          
            
              操作
            
            
            
              添加子部门
              
              编辑部门
              删除部门
            
          

dropdown下拉菜单的监听事件command

    // 操作节点调用的方法
    operateDepts(type) {
      if (type === 'add') {
        // 添加子部门的操作
      } else if (type === 'edit') {
        //  编辑部门的操作
      } else {
        //  删除操作
      }
    }

调用删除接口,通知父组件更新数据

删除之前,提示用户是否删除,然后调用删除接口

    // 操作节点调用的方法
    operateDepts(type) {
      if (type === 'add') {
        // 添加子部门的操作
      } else if (type === 'edit') {
        //  编辑部门的操作
      } else {
        //  删除操作
        this.$confirm('确定要删除该部门吗').then(() => {
          // 如果点击了确定就会进入then
          return delDepartments(this.treeNode.id) // 返回promise对象
        }).then(() => {
          //  如果删除成功了  就会进入这里
        })
      }
    }

上面代码中,我们已经成功删除了员工数据,但是怎么通知父组件进行更新呢

在前面的课程中,我们已经学习过可以通过自定义事件this.$emit的方式来进行

         //  如果删除成功了  就会进入这里
          this.$emit('delDepts') // 触发自定义事件
          this.$message.success('删除部门成功')

父组件监听事件 src/views/department/index.vue

提交代码

本节任务:删除部门功能实现

新增部门功能-建立组件

目标:实现新增部门功能的组件建立

封装新增接口,新建组件中的弹层结构

首先, 封装新增部门的api模块 src/api/departments.js

/**
 *  新增部门接口
 *
 * ****/
export function addDepartments(data) {
  return request({
    url: '/company/department',
    method: 'post',
    data
  })
}
​

然后,我们需要构建一个新增部门的窗体组件 src/views/department/components/add-dept.vue

其中的交互设计如下

设计要求

点击新增子部门显示弹层组件

然后,我们需要用属性控制组件的显示或者隐藏

  // 需要传入一个props变量来控制 显示或者隐藏
  props: {
    showDialog: {
      type: Boolean,
      default: false
    }
  }

departments/index.vue 中引入该组件

import AddDept from './components/add-dept' // 引入新增部门组件
export default {
  components: { AddDept }
}

定义控制窗体显示的变量showDialog

 data() {
    return {
      showDialog: false // 显示窗体
    }
  },
    
    

当点击新增部门时,弹出组件

注意,点击新增时tree-tools组件,所以这里,我们依然需要子组件调用父组件

子组件触发新增事件· src/views/departments/tree-tools.vue

  if (type === 'add') {
        // 添加子部门的操作
        // 告诉父组件 显示弹层
        this.$emit('addDepts', this.treeNode) // 为何传出treeNode 因为是添加子部门 需要当前部门的数据
      }

父组件监听事件

2处添加 

方法中弹出层,记录在哪个节点下添加子部门

 addDepts(node) {
      this.showDialog = true // 显示弹层
      // 因为node是当前的点击的部门, 此时这个部门应该记录下来,
      this.node = node
    }

提交代码

本节任务:新增部门功能-建立组件

完成新增部门的规则校验

目标 完成新增部门功能的规则校验和数据提交部分

完成新增表单的基本校验条件

部门名称(name):必填 1-50个字符 / 同级部门中禁止出现重复部门

部门编码(code):必填 1-50个字符 / 部门编码在整个模块中都不允许重复

部门负责人(manager):必填

部门介绍 ( introduce):必填 1-300个字符

定义数据结构

  formData: {
        name: '', // 部门名称
        code: '', // 部门编码
        manager: '', // 部门管理者
        introduce: '' // 部门介绍
  },

完成表单校验需要的前置条件

  • el-form配置model和rules属性

  • el-form-item配置prop属性

  • 表单进行v-model双向绑定

配置新增表单的基本校验规则

根据这些要求,校验规则

  data() {
    return {
      // 定义表单数据
      formData: {
        name: '', // 部门名称
        code: '', // 部门编码
        manager: '', // 部门管理者
        introduce: '' // 部门介绍
      },
      // 定义校验规则
      rules: {
        name: [
          { required: true, message: '部门名称不能为空', trigger: 'blur' },
          { min: 1, max: 50, message: '部门名称要求1-50个字符', trigger: 'blur' }
        ],
        code: [
          { required: true, message: '部门编码不能为空', trigger: 'blur' },
          { min: 1, max: 50, message: '部门编码要求1-50个字符', trigger: 'blur' }
        ],
        manager: [{ required: true, message: '部门负责人不能为空', trigger: 'blur' }],
        introduce: [
          { required: true, message: '部门介绍不能为空', trigger: 'blur' },
          { trigger: 'blur', min: 1, max: 300, message: '部门介绍要求1-50个字符' }
        ]
      }
    }
  }

部门名称和部门编码的自定义校验

注意:部门名称和部门编码的规则 有两条我们需要通过自定义校验函数validator来实现

首先,在校验名称和编码时,要获取最新的组织架构,这也是我们这里trigger采用blur的原因,因为change对于访问的频率过高,我们需要控制访问频率

      // 首先获取最新的组织架构数据
      const { depts } = await getDepartments()

部门名称不能和同级别的重复,这里注意,我们需要找到所有同级别的数据,进行校验,所以还需要另一个参数pid

props: {
    //   用来控制窗体是否显示或者隐藏
    showDialog: {
      type: Boolean,
      default: false
    },
    // 当前操作的节点
    treeNode: {
      type: Object,
      default: null
    }
  },
  

根据当前部门id,找到所有子部门相关的数据,判断是否重复

    // 现在定义一个函数 这个函数的目的是 去找 同级部门下 是否有重复的部门名称
    const checkNameRepeat = async(rule, value, callback) => {
      // 先要获取最新的组织架构数据
      const { depts } = await getDepartments()
      // depts是所有的部门数据
      // 如何去找技术部所有的子节点
      const isRepeat = depts.filter(item => item.pid === this.treeNode.id).some(item => item.name === value)
      isRepeat ? callback(new Error(`同级部门下已经有${value}的部门了`)) : callback()
    }

检查部门编码的过程同理

     // 检查编码重复
    const checkCodeRepeat = async(rule, value, callback) => {
      // 先要获取最新的组织架构数据
      const { depts } = await getDepartments()
      const isRepeat = depts.some(item => item.code === value && value) // 这里加一个 value不为空 因为我们的部门有可能没有code
      isRepeat ? callback(new Error(`组织架构中已经有部门使用${value}编码`)) : callback()
    }

在规则中定义

     // 定义校验规则
      rules: {
        name: [
          { required: true, message: '部门名称不能为空', trigger: 'blur' },
          { min: 1, max: 50, message: '部门名称要求1-50个字符', trigger: 'blur' },
          // 自定义函数的形式校验
          {trigger: 'blur',validator: checkNameRepeat }
        ],
        code: [
          { required: true, message: '部门编码不能为空', trigger: 'blur' },
          { min: 1, max: 50, message: '部门编码要求1-50个字符', trigger: 'blur' }, 
          { trigger: 'blur',validator: checkCodeRepeat}
        ],
        manager: [{ required: true, message: '部门负责人不能为空', trigger: 'blur' }],
        introduce: [
          { required: true, message: '部门介绍不能为空', trigger: 'blur' },
          { trigger: 'blur', min: 1, max: 300, message: '部门介绍要求1-50个字符' }
        ]
      }

处理首部内容的pid数据

需要注意:在最根级的tree-tools组件中,由于treenode属性中没有id,id便是undefined,但是通过undefined进行等值判断是寻找不到对应的根节点的, 所以在传值时,我们将id属性设置为 “”

src/views/departments/index.vue

 async getDepartments() {
      const result = await getDepartments()
      this.departs = tranListToTreeData(result.depts, '')
      this.company = { name: result.companyName, manager: '负责人', id: '' }
 },

提交代码

本节任务:完成新增部门的规则校验

新增部门功能-部门负责人数据

目标:获取新增表单中的部门负责人下拉数据

在上节的表单中,部门负责人是下拉数据,我们应该从员工接口中获取该数据

首先,封装获取简单员工列表的模块 src/api/employees.js

import request from '@/utils/request'
​
/**
 *  获取员工的简单列表
 * **/
export function getEmployeeSimple() {
  return request({
    url: '/sys/user/simple'
  })
}
​

然后,在add-dept.vue中的select聚焦事件focus中调用该接口,因为我们要获取实时的最新数据


    
     

获取员工列表

import  { getEmployeeSimple }   from '@/api/employees'
methods: {
    // 获取员工简单列表数据
    async  getEmployeeSimple() {
        this.peoples = await getEmployeeSimple()
    }
}
data:{
    peoples: [] // 接收获取的员工简单列表的数据
}
​

提交代码

本节任务:新增部门功能-部门负责人数据

新增功能-提交-取消-关闭

目标: 完成新增模块的提交-取消-关闭等功能

校验通过,调用新增接口

当点击新增页面的确定按钮时,我们需要完成对表单的整体校验,如果校验成功,进行提交

首先,在点击确定时,校验表单

给el-form定义一个ref属性

    
    // 点击确定时触发
    btnOK() {
      this.$refs.deptForm.validate(isOK => {
        if (isOK) {
          // 表示可以提交了
        }
      })
    }

然后,在校验通过时,调用新增接口

因为是添加子部门,所以我们需要将新增的部门pid设置成当前部门的id,新增的部门就成了自己的子部门

    // 点击确定时触发
    btnOK() {
      this.$refs.deptForm.validate(async isOK => {
        if (isOK) {
          // 表示可以提交了
          await addDepartments({ ...this.formData, pid: this.treeNode.id }) // 调用新增接口 添加父部门的id
        }
      })
    }

同样,在新增成功之后,调用告诉父组件,重新拉取数据

 this.$emit('addDepts')

父组件

 

本节注意:同学们可能会疑惑,我们tree-tools.vueadd-dept.vue两个组件都触发了addDepts事件,不冲突吗?

这里,我们触发的自定义事件都是组件自身的,他们之间没有任何关系,只是名字相同而已,大家不要混淆

利用sync修饰符关闭新增弹层

这里我们学习一个新的技巧,sync修饰符

按照常规,想要让父组件更新showDialog的话,需要这样做

// 子组件
this.$emit('changedialog', false) //触发事件
// 父组件

// JS 里面
 method(value) {
    this.showDialog = value
}

但是,vuejs为我们提供了sync修饰符,它提供了一种简写模式 也就是

// 子组件 update:固定写法 (update:props名称, 值)
this.$emit('update:showDialog', false) //触发事件
// 父组件 sync修饰符

只要用sync修饰,就可以省略父组件的监听和方法,直接将值赋值给showDialog

你可能感兴趣的:(vue,elementui,vue.js,javascript,前端)