人资项目day06-组织架构树形结构布局

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

认识组织架构

组织架构产品prd

人资项目day06-组织架构树形结构布局_第1张图片

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

人资项目day06-组织架构树形结构布局_第2张图片

 

实现组织架构的头部内容

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

       
    
       
       
         
            江苏传智播客教育科技股份有限公司
         

         
           
             
              负责人
             
               
               
                 
                    操作
                 

                 
                 
                    添加子部门
                 

               

             

           

         

       

         
     

树形组件认识

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

人资项目day06-组织架构树形结构布局_第3张图片

 树形组件属性

参数 说明 类型 可选值 默认值
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'
            }]
          }]
        }]

人资项目day06-组织架构树形结构布局_第4张图片

 

实现树形的静态组织架构

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

 
 
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进行代码的简化

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

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

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

在钩子函数中调用接口

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)
    }
  }
}

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

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

人资项目day06-组织架构树形结构布局_第5张图片

 封装一个工具方法,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, '')

删除部门功能实现

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

在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

新增部门功能-建立组件

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

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

其中的交互设计如下

人资项目day06-组织架构树形结构布局_第6张图片

 设计要求

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

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

  // 需要传入一个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 // 显示窗体
    }
  },
   
   

子组件触发新增事件· 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
    }

人资项目day06-组织架构树形结构布局_第7张图片

 

完成新增部门的规则校验

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

部门名称(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对于访问的频率过高,我们需要控制访问频率

部门名称不能和同级别的重复,这里注意,我们需要找到所有同级别的数据,进行校验,所以还需要另一个参数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: '' }
 }

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