SaaS系统用户权限设计1

简介: SaaS系统用户权限设计

SaaS系统用户权限设计

学习目标:

理解RBAC模型的基本概念及设计思路

了解SAAS-HRM中权限控制的需求及表结构分析完成组织机构的基本CRUD操作

完成用户管理的基本CRUD操作完成角色管理的基本CRUD操作

组织机构管理

需求分析

需求分析

实现企业组织结构管理,实现部门的基本CRUD操作


数据库表设计

CREATE TABLE `co_department` (
 `id` varchar(40) NOT NULL,
`company_id` varchar(255) NOT NULL COMMENT '企业ID',
`parent_id` varchar(255) DEFAULT NULL COMMENT '父级部门ID',
`name` varchar(255) NOT NULL COMMENT '部门名称',
`code` varchar(255) NOT NULL COMMENT '部门编码',
`category` varchar(255) DEFAULT NULL COMMENT '部门类别',
`manager_id` varchar(255) DEFAULT NULL COMMENT '负责人ID',
`city` varchar(255) DEFAULT NULL COMMENT '城市',
`introduce` text COMMENT '介绍',
`create_time` datetime NOT NULL COMMENT '创建时间',
`manager` varchar(40) DEFAULT NULL COMMENT '部门负责人',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

微服务实现

抽取公共代码

(1) 在公共controller

ihrm_commoncom.模块下的 ihrm.common.controller 包下添加公共controller

package com.ihrm.common.controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 公共controller
*     获取request,response
*     获取企业id,获取企业名称
*/
public class BaseController {
    protected HttpServletRequest request;
    protected HttpServletResponse response;
    @ModelAttribute
    public void setReqAndResp(HttpServletRequest request, HttpServletResponse response) 
{
        this.request = request;
        this.response = response;
   }
    //企业id,(暂时使用1,以后会动态获取)
    public String parseCompanyId() {
        return "1";
   }
    public String parseCompanyName() {
        return "江苏传智播客教育股份有限公司";
   }
}

骚戴理解:@ModelAttribute是Spring MVC中的一个注释,它的作用是将HTTP请求参数绑定到指定的ModelAttribute对象并添加到ModelAndView中。 通俗地讲,它可以在请求处理程序方法之前预处理模型属性,以便在请求处理程序方法中使用。在Spring MVC中,当从浏览器提交表单时,所有表单字段的名称和值都被收集到一个名为“请求参数”的数据结构中。 @ModelAttribute注释可以将此请求参数映射到Java对象的属性中。

(2) 公共service

ihrm_commoncom.模块下的 ihrm.common.service 包下添加公共BaseService

public class BaseService<T> {
    protected Specification<T> getSpecification(String companyId) {
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
                return cb.equal(root.get("companyId").as(String.class),companyId);
           }
       };
   }
}

骚戴理解:在SpringData里面提供的findAll方法如果是没有任何参数就是获取所有的数据,如果需要查询所有符合条件的数据,那么将就需要传入一个参数Specification,Specification参数其实就是条件 ,这里的getSpecification方法是用来创建这个条件的,这里是抽出来了,这个条件的意思是查询companyId这个公司id下的所有部门(组织架构)


criteriabuilder.equal(root.get("companyid").as(string.class), companyid)中的两个companyid指的是不同的变量。


第一个companyid:表示root对象所代表的实体类中的属性名,该属性用于和后面传递进来的companyid进行比较。

第二个companyid:表示方法参数中传递进来的某个值,用于与实体的companyid属性进行比较。

简而言之,这个代码片段的作用是使用criteriabuilder构造一个查询条件,即根据传入的companyid值筛选出实体类中属性名为companyid的值等于该值的记录。


public predicate topredicate(root<t> root, criteriaquery<?> criteriaquery, criteriabuilder cb) 是一个接口方法,用于创建 criteria api 中的查询谓语(predicate),它接收三个参数:


root<t> root:代表查询的根节点,可以从中获取实体类的属性。

criteriaquery<?> criteriaquery:代表将被执行的查询对象。

criteriabuilder cb:代表 criteria api 的工厂类,用于创建各种查询条件。

该方法会返回一个 predicate 类型的查询条件,表示要在给定的查询中使用的过滤器或限制条件。predicate 是 criteria api 中与 boolean 表达式相关的基础接口,用于构造 where 子句中的条件表达式。


这个方法是通用的,可以在不同的场景下使用。例如,在 spring data jpa 中,我们可以使用它来创建基于查询方法名的动态查询。具体而言,我们可以定义一个接口方法并使用 @query 注解,以便将该方法与特定的 jpql 查询一起使用。然后,我们可以在该方法中编写自定义查询逻辑,并使用 topredicate() 方法创建基于标准的查询谓词。


(3)公共DeptListResult

package com.ihrm.common.response;
import com.ihrm.domain.company.Company;
import com.ihrm.domain.company.Department;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
@NoArgsConstructor
public class DeptListResult {
    private String companyId;
    private String companyName;
    private String companyManage;
    private List<Department> depts;
    public DeptListResult(Company company,List<Department> list) {
        this.companyId = company.getId();
        this.companyName = company.getName();
        this.companyManage = company.getLegalRepresentative();
        this.depts = list;
    }
}

骚戴理解:DeptListResult这个类的作用就是封装前端需要的企业下所有组织架构(部门)信息,因为前端展示的时候不但要一个企业下所有的部门列表信息,还要企业的相关信息,所以这里就封装成了DeptListResult类

    /**
     * 组织架构列表
     */
    @RequestMapping(value = "/departments", method = RequestMethod.GET)
    public Result findAll() throws Exception {
        Company company = companyService.findById(parseCompanyId());
        List<Department> list = departmentService.findAll(parseCompanyId());
        return  new Result(ResultCode.SUCCESS,new DeptListResult(company,list));
   }

实现基本CRUD操作

(1)实体类

在com.ihrm.domain.company包下创建Department实体类

package com.ihrm.domain.company;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* (Department)实体类
*/
@Entity
@Table(name = "co_department")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department implements Serializable {
    private static final long serialVersionUID = -9084332495284489553L;
    //ID
    @Id
    private String id;
    /**
     * 父级ID
     */
    private String parentId;
    /**
     * 企业ID
     */
    private String companyId;
    /**
     * 部门名称
     */
    private String name;
    /**
     * 部门编码,同级部门不可重复
     */
    private String code;
    /**
     * 负责人ID
     */
    private String managerId;
    /**
 * 负责人名称
 */
    private String manager;
    /**
     * 介绍
     */
    private String introduce;
    /**
     * 创建时间
     */
    private Date createTime;
}

(2)持久化层

在 com.ihrm.company.dao 包下创建DepartmentDao

package com.ihrm.company.dao;
import com.ihrm.domain.company.Department;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/**
* 部门操作持久层
*/
public interface DepartmentDao extends JpaRepository<Department, String>, 
JpaSpecificationExecutor<Department> {
}

(3)业务层

在 com.ihrm.company.service 包下创建DepartmentService

package com.ihrm.company.service;
import com.ihrm.common.entity.ResultCode;
import com.ihrm.common.exception.CommonException;
import com.ihrm.common.service.BaseService;
import com.ihrm.common.utils.IdWorker;
import com.ihrm.company.dao.DepartmentDao;
import com.ihrm.domain.company.Department;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.Date;
import java.util.List;
/**
* 部门操作业务逻辑层
*/
@Service
public class DepartmentService extends BaseService {
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private DepartmentDao departmentDao;
    /**
     * 添加部门
     */
    public void save(Department department) {
        //填充其他参数
        department.setId(idWorker.nextId() + "");
        department.setCreateTime(new Date());
        departmentDao.save(department);
   }
    /**
     * 更新部门信息
     */
    public void update(Department department) {
        Department sourceDepartment = departmentDao.findById(department.getId()).get();
        sourceDepartment.setName(department.getName());
        sourceDepartment.setPid(department.getPid());
        sourceDepartment.setManagerId(department.getManagerId());
        sourceDepartment.setIntroduce(department.getIntroduce());
        sourceDepartment.setManager(department.getManager());
        departmentDao.save(sourceDepartment);
   }
    /**
     * 根据ID获取部门信息
     *
     * @param id 部门ID
     * @return 部门信息
     */
    public Department findById(String id) {
        return departmentDao.findById(id).get();
   }
    /**
     * 删除部门
     *
     * @param id 部门ID
     */
    public void delete(String id) {
        departmentDao.deleteById(id);
   }
    /**
     * 获取部门列表
     */
    public List<Department> findAll(String companyId) {
        return departmentDao.findAll(getSpecification(companyId));
   }
}

骚戴理解:以前用mybatis的时候写的修改都是直接把新的对象穿给dao层,在dao层动态更新(动态SQL便签),这里由于用的是SpringData的API,看下面的接口可以发现这个框架并没有提供update方法,所以这里的修改都是通过save新增方法来实现的


还要注意findById方法是有一个.get()后缀的,例如 departmentDao.findById(id).get(),容易忘/漏

getSpecification方法是创建条件谓语,这个方法是来自于父类BaseService的方法

(4)控制层

在 ihrm.company.controller 创建控制器类DepartmentController

package com.ihrm.company.controller;
import com.ihrm.common.controller.BaseController;
import com.ihrm.common.entity.Result;
import com.ihrm.common.entity.ResultCode;
import com.ihrm.company.service.CompanyService;
import com.ihrm.company.service.DepartmentService;
import com.ihrm.domain.company.Company;
import com.ihrm.domain.company.Department;
import com.ihrm.domain.company.response.DeptListResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;
/**
* 控制器层
*/
@CrossOrigin
@RestController
@RequestMapping("/company")
public class DepartmentController extends BaseController{
    @Autowired
    private DepartmentService departmentService;
    @Autowired
    private CompanyService companyService;
    /**
     * 添加部门
     */
    @RequestMapping(value = "/departments", method = RequestMethod.POST)
    public Result add(@RequestBody Department department) throws Exception {
        department.setCompanyId(parseCompanyId());
        departmentService.save(department);
        return Result.SUCCESS();
   }
    /**
     * 修改部门信息
     */
    @RequestMapping(value = "/departments/{id}", method = RequestMethod.PUT)
    public Result update(@PathVariable(name = "id") String id, @RequestBody Department
department) throws Exception {
        department.setCompanyId(parseCompanyId());
        department.setId(id);
        departmentService.update(department);
        return Result.SUCCESS();
   }
    /**
     * 删除部门
     */
    @RequestMapping(value = "/departments/{id}", method = RequestMethod.DELETE)
    public Result delete(@PathVariable(name = "id") String id) throws Exception {
        departmentService.delete(id);
        return Result.SUCCESS();
   }
    /**
     * 根据id查询
     */
    @RequestMapping(value = "/departments/{id}", method = RequestMethod.GET)
    public Result findById(@PathVariable(name = "id") String id) throws Exception {
        Department department = departmentService.findById(id);
        return new Result(ResultCode.SUCCESS,department);
   }
    /**
     * 组织架构列表
     */
    @RequestMapping(value = "/departments", method = RequestMethod.GET)
    public Result findAll() throws Exception {
        Company company = companyService.findById(parseCompanyId());
        List<Department> list = departmentService.findAll(parseCompanyId());
        return  new Result(ResultCode.SUCCESS,new DeptListResult(company,list));
   }
}

骚戴理解:控制器调用的parseCompanyId方法和parseCompanyName方法都是来自父类BaseController中

前端实现

创建模块

  • 使用命令行创建module-departments模块并引入到工程中
  • 在src/main.js中注册模块
import departments from '@/module-departments/' // 组织机构管理
Vue.use(departments, store)
  • 在/module-departments/router/index.js配置路由
import Layout from '@/module-dashboard/pages/layout'
const _import = require('@/router/import_' + process.env.NODE_ENV)
export default [
 {
    root: true,
    path: '/departments',
    component: Layout,
    redirect: 'noredirect',
    name: 'departments',
    meta: {
      title: '组织架构管理',
      icon: 'architecture'
   },
    children: [
     {
        path: 'index',
        component: _import('departments/pages/index'),
        name: 'organizations-index',
        meta: {title: '组织架构', icon: 'architecture', noCache: true}
     }
   ]
 }
]


配置请求API

在/src/api/base/创建departments.js作为组织机构管理的API公共接口方法

import {createAPI} from '@/utils/request'
//查询部门列表
export const list = data => createAPI('/company/department', 'get', data)
//保存部门
//data  {id:“”,name:“”}
export const save = data => createAPI('/company/department', 'post', data)
//根据id查询部门 {id:“”}
export const find = data => createAPI(`/company/department/${data.id}`, 'get', data)
//根据id删除部门 {id:""}
export const deleteById = data => createAPI(`/company/department/${data.id}`, 'delete', data)
//根据id更新部门 {id:"",name:"",code:""}
export const update = data => createAPI(`/company/department/${data.id}`, 'put', data)
//保存或更新的方法
export const saveOrupdate = data => {return data.id?update(data):save(data)}

骚戴理解: {return data.id?update(data):add(data)}的意思是判断data里面有没有id,如果有的话就调update方法,没有就调用add方法

构造树形列表

(1)构造页面样式

<template>
  <div class="dashboard-container">
    <div class="app-container">
      <el-card shadow="never">
            <div class='organization-index'>
              <div class='organization-index-top'>
                <div class='main-top-title'>
                  <el-tabs v-model="activeName">
                    <el-tab-pane label="组织结构" name="first"></el-tab-pane>
                    <div class="el-tabs-report">
                      <a class="el-button el-button--primary el-button--mini" title="导出" >导入</a>
                      <a class="el-button el-button--primary el-button--mini" title="导出" >导出</a>
                    </div>
                  </el-tabs>
                </div>
              </div>
              <div style="overflow: scroll;white-space:nowrap"  class="treBox">
                <div class="treeCon clearfix">
                    <span>
                      <i class="fa fa-university" aria-hidden="true"></i>
                      <span ><strong>{{departData.companyName}}</strong></span>
                    </span>
                    <div class="fr">
                      <span class="treeRinfo">
                        <div class="treeRinfo">
                          <span>{{departData.companyManage}}</span>
                          <span>在职  <em class="colGreen" title="在职人数">---</em>&nbsp;&nbsp;(<em class="colGreen" title="正式员工">---</em>&nbsp;/&nbsp;<em class="colRed" title="非正式员工">---</em>)</span>
                        </div>
                        <div class="treeRinfo">
                          <el-dropdown class="item">
                            <span class="el-dropdown-link">
                              操作<i class="el-icon-arrow-down el-icon--right"></i>
                            </span>
                            <el-dropdown-menu slot="dropdown">
                                <el-dropdown-item>
                                  <el-button type="text" @click="handlAdd('')">添加子部门</el-button>
                                </el-dropdown-item>
                              <el-dropdown-item>
                                <el-button type="text" @click="handleList()">查看待分配员工</el-button>
                              </el-dropdown-item>
                            </el-dropdown-menu>
                          </el-dropdown>
                        </div>
                      </span>  
                    </div>
                  </div>
                  <!-- 
                    构造树形列表
                      叶子 <i class="fa fa-male"></i>
                      非叶子 
                        展开 <i class="fa fa-minus-square-o">
                        闭合 <i class="fa fa-plus-square-o">
                    <div class="generalClass" slot-scope="{node,data}" style="width:99%">
                  -->
                  <el-tree :props="{label:'name'}" :data="depts" node-key="id" default-expand-all>
                    <!--
                      node : 是否展开,是否叶子节点
                      data:部门对象
                            id,name
                     -->
                    <div class="generalClass" slot-scope="{node,data}" style="width:99%">
                        <span>
                           <i v-if="node.isLeaf" class="fa fa-male"></i>
                           <i v-else :class="node.expanded?'fa fa-minus-square-o':'fa fa-plus-square-o'"></i>
                          <span>{{ node.label }}</span>
                        </span>
                         <div class="fr">
                          <span class="treeRinfo">
                            <div class="treeRinfo">
                              <span>{{departData.companyManage}}</span>
                              <span>在职  <em class="colGreen" title="在职人数">---</em>&nbsp;&nbsp;(<em class="colGreen" title="正式员工">---</em>&nbsp;/&nbsp;<em class="colRed" title="非正式员工">---</em>)</span>
                            </div>
                            <div class="treeRinfo">
                              <el-dropdown class="item">
                                <span class="el-dropdown-link">
                                  操作<i class="el-icon-arrow-down el-icon--right"></i>
                                </span>
                                <el-dropdown-menu slot="dropdown">
                                    <el-dropdown-item>
                                      <el-button type="text" @click="handlAdd(data.id)">添加子部门</el-button>
                                    </el-dropdown-item>
                                    <el-dropdown-item>
                                      <el-button type="text" @click="handUpdate(data.id)">查看部门</el-button>
                                    </el-dropdown-item>
                                    <el-dropdown-item>
                                      <el-button type="text" @click="handleList()">查看待分配员工</el-button>
                                    </el-dropdown-item>
                                    <el-dropdown-item>
                                    <el-button type="text" @click="handleDelete(data.id)">删除部门</el-button>
                                  </el-dropdown-item>
                                </el-dropdown-menu>
                              </el-dropdown>
                            </div>
                          </span>  
                        </div>
                      </div>
                  </el-tree>
              </div>
            </div>    
      </el-card>
    </div>
    <!--:visible.sync 是否显示 -->
    <!--引入组件-->
    <component v-bind:is="deptAdd" ref="addDept"></component>
</div>
</template>
<!-- 引入组件 -->
<script>
//引入api
import {list,saveOrupdate,find,deleteById} from "@/api/base/dept"
import commonApi from '@/utils/common'
import deptAdd from './../components/add'
export default {
  components:{deptAdd},
  data() {
    return {
      deptAdd:'deptAdd',
      activeName: 'first', 
      departData:{},
      depts:[]
    }
  },
  methods: {
      //添加部门
    handlAdd(parentId) {
      //父页面调用子组件中的内容
      this.$refs.addDept.parentId = parentId;
      this.$refs.addDept.dialogFormVisible = true
    },
    //查看部门
    handUpdate(id) {
      //根据id查询部门
      find({id:id}).then(res => {
         //数据绑定到dept对象中
         this.$refs.addDept.dept = res.data.data;
         this.$refs.addDept.dialogFormVisible = true
      })
    },
    handleDelete(id) {
       this.$confirm('是否删除此条记录?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
               deleteById({id:id}).then(res=> {
                this.$message({
                  message: res.data.message,
                  type: res.data.success?'success':'error'
                });
                if(res.data.success) {
                  location.reload();
                }
              })
        })
    },
    //构造查询方法
    getList() {
      list().then(res => {
       this.departData = res.data.data
       //将普通的数据转化为父子接口
       this.depts = commonApi.transformTozTreeFormat(res.data.data.depts);
       console.log(this.depts)
      })
    }
  },
  created: function() {
    this.getList();
  },
}
</script>
<style rel="stylesheet/scss" lang="scss">
.el-dropdown {
  color: #000000
}
.el-tree-node__content>.el-tree-node__expand-icon {
  padding:0px;
}
.el-tree-node__expand-icon {
  color:#ffffff
}
.generalClassNode {
  padding-left: 20px;
}
.el-tree-node__content{
  font-size: 16px;
  line-height: 36px;
  height:36px;
}
.custom-tree-node{
  padding-left: 20px;
}
.objectTree {
  overflow: auto;
  z-index: 100;
  width: 300px;
  border: 1px solid #dcdfe6;
  margin-top: 5px;
  left: 70px;
}
.el-tabs__content {
  overflow: initial;
}
.boxpad {
  margin-left: -40px;
}
</style>
<style  rel="stylesheet/scss" lang="scss" scoped>
.el-tree-node__expand-icon{
}
.el-icon-caret-right{}
.el-tree-node__content{
  font-size: 14px;
  line-height: 36px;
}
.generalClass {
  font-size: 14px;
  line-height: 36px;
  color:#000000
}
.all {
  position: relative;
  min-height: 100%;
  padding-bottom: 200px;
}
.organization-main:after,
.organization-index-top:after {
  display: block;
  clear: both;
  content: '';
  visibility: hidden;
  height: 0;
}
.organization-main {
  font-size: 14px;
  font-size: 14px;
}
.organization-index {
  padding-bottom: 20px;
  margin-left: 20px;
}
.main-top-title {
  padding-left: 20px;
  padding-top: 20px;
  text-align: left;
}
::-webkit-scrollbar-thumb {
  background-color: #018ee8;
  height: 50px;
  outline-offset: -2px;
  outline: 8px solid #fff;
  -webkit-border-radius: 4px;
}
::-webkit-scrollbar-track-piece {
  background-color: #fff;
  -webkit-border-radius: 0;
}
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
::-webkit-scrollbar-thumb:hover {
  background-color: #fb4446;
  height: 50px;
  -webkit-border-radius: 4px;
}
.modal-total {
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  background: #000;
  z-index: 90;
  opacity: 0.2;
}
.modal {
  width: 400px;
  height: 300px;
  background-color: #ffffff;
  z-index: 999;
  position: absolute;
  left: 45%;
  top: 20%;
  text-align: center;
}
.treBox {
  padding: 30px 120px 0;
}
.organization-index-top {
  position: relative;
  .el-tabs-report {
    position: absolute;
    top: -50px;
    right: 15px;
  }
}
.treeCon {
  border-bottom: 1px solid #cfcfcf;
  padding: 10px 0;
  margin-bottom: 10px;
  .el-dropdown {
    color: #333;
  }
}
.treeRinfo {
  display: inline-block;
}
.treeRinfo span {
  padding-left: 30px;
}
</style>

(2)树形机构列表

  <!-- 
    构造树形列表
      叶子 <i class="fa fa-male"></i>
      非叶子 
        展开 <i class="fa fa-minus-square-o">
        闭合 <i class="fa fa-plus-square-o">
    <div class="generalClass" slot-scope="{node,data}" style="width:99%">
  -->
  <el-tree :props="{label:'name'}" :data="depts" node-key="id" default-expand-all>
    <!--
      node : 是否展开,是否叶子节点
      data:部门对象
            id,name
     -->
    <div class="generalClass" slot-scope="{node,data}" style="width:99%">
        <span>
           <i v-if="node.isLeaf" class="fa fa-male"></i>
           <i v-else :class="node.expanded?'fa fa-minus-square-o':'fa fa-plus-square-o'"></i>
          <span>{{ node.label }}</span>
        </span>
         <div class="fr">
          <span class="treeRinfo">
            <div class="treeRinfo">
              <span>{{departData.companyManage}}</span>
              <span>在职  <em class="colGreen" title="在职人数">---</em>&nbsp;&nbsp;(<em class="colGreen" title="正式员工">---</em>&nbsp;/&nbsp;<em class="colRed" title="非正式员工">---</em>)</span>
            </div>
            <div class="treeRinfo">
              <el-dropdown class="item">
                <span class="el-dropdown-link">
                  操作<i class="el-icon-arrow-down el-icon--right"></i>
                </span>
                <el-dropdown-menu slot="dropdown">
                    <el-dropdown-item>
                      <el-button type="text" @click="handlAdd(data.id)">添加子部门</el-button>
                    </el-dropdown-item>
                    <el-dropdown-item>
                      <el-button type="text" @click="handUpdate(data.id)">查看部门</el-button>
                    </el-dropdown-item>
                    <el-dropdown-item>
                      <el-button type="text" @click="handleList()">查看待分配员工</el-button>
                    </el-dropdown-item>
                    <el-dropdown-item>
                    <el-button type="text" @click="handleDelete(data.id)">删除部门</el-button>
                  </el-dropdown-item>
                </el-dropdown-menu>
              </el-dropdown>
            </div>
          </span>  
        </div>
      </div>
  </el-tree>

骚戴理解:


解释<el-tree :props="{label:'name'}" :data="depts" node-key="id" default-expand-all>


这是vue.js中使用的一个el-tree组件,用于显示层次结构的树形数据。以下是对代码中每个属性的解释:


:props="{label:'name'}":这个属性设置了要显示在树节点上的文本内容的属性名为“name”,即树节点将显示"data"数组中每个元素的"name"属性。 后面可以通过{{ node.label }}去获取name,也就是部门的名称

:data="depts":这个属性设置了树形数据的来源为一个名为“depts”的数据数组,即树的每个节点由数组中的一个元素表示。

node-key="id":这个属性设置了树节点的唯一标识符为"data"数组中每个元素的"id"属性,这将有助于在树中添加、删除或更新节点时进行准确定位。

default-expand-all:这个属性设置了默认情况下,所有树节点都将展开显示。

解释 <i v-if="node.isLeaf" class="fa fa-male"></i> 和<i v-else :class="node.expanded?'fa fa-minus-square-o':'fa fa-plus-square-o'"></i>


如果 node.isleaf 为真(判断是不是叶子节点),则渲染一个男性符号的图标 <i class="fa fa-male"></i>。

否则,就渲染一个可以展开和折叠的矩形图标。该矩形图标的样式取决于 node.expanded 属性的值:

如果 node.expanded 为 true(节点展开的话),则渲染带有减号的图标 <i class="fa fa-minus-square-o"></i>。

如果 node.expanded 为 false(节点关闭的话),则渲染带有加号的图标 `<i class="fa fa-plus-square-o">

解释以下代码

 <span>在职  <em class="colGreen" title="在职人数">---</em>&nbsp;&nbsp;(<em class="colGreen" title="正式员工">---</em>&nbsp;/&nbsp;<em class="colRed" title="非正式员工">---</em>)</span>

<em> 标签用于在文本中强调某些词语,使其在视觉上与其他文本内容区别开来。&nbsp;表示空格,实现效果如下所示

目录
相关文章
|
3天前
|
存储 运维 Java
java云his系统源码一站式诊所SaaS系统Java版云HIS系统 八大特点
HIS系统采用面向技术架构的分析与设计方法,应用多层次应用体系架构设计,运用基于构件技术的系统搭建模式与基于组件模式的系统内核结构。通过建立统一接口标准,实现数据交换和集成共享,通过统一身份认证和授权控制,实现业务集成、界面集成。
28 1
|
5天前
|
数据采集 前端开发 Java
Java医院绩效考核系统源码maven+Visual Studio Code一体化人力资源saas平台系统源码
医院绩效解决方案包括医院绩效管理(BSC)、综合奖金核算(RBRVS),涵盖从绩效方案的咨询与定制、数据采集、绩效考核及反馈、绩效奖金核算到科到组、分配到员工个人全流程绩效管理;将医院、科室、医护人员利益绑定;全面激活人才活力;兼顾质量和效益、长期与短期利益;助力医院降本增效,持续改善、优化收入、成本结构。
19 0
|
5天前
|
Java 云计算
Java智能区域医院云HIS系统SaaS源码
云HIS提供标准化、信息化、可共享的医疗信息管理系统,实现医患事务管理和临床诊疗管理等标准医疗管理信息系统的功能。优化就医、管理流程,提升患者满意度、基层首诊率,通过信息共享、辅助诊疗等手段,提高基层医生的服务能力构建和谐的基层医患关系。
39 2
|
5天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
107 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
5天前
|
消息中间件 缓存 Java
java基于云部署的SaaS医院云HIS系统源码 心理CT、B超 lis、电子病历
云HIS系统是一款满足基层医院各类业务需要的健康云产品。该产品能帮助基层医院完成日常各类业务,提供病患预约挂号支持、病患问诊、电子病历、开药发药、会员管理、统计查询、医生工作站和护士工作站等一系列常规功能,还能与公卫、PACS等各类外部系统融合,实现多层机构之间的融合管理。
46 12
|
5天前
|
运维 供应链 安全
SaaS模式云HIS数字化医院信息系统源码
云HIS具有可扩展、易共享、易协同、低成本、体验号、更便捷、易维护的优势,重新定义了数字化医院信息系统,实现数字化医院信息系统的转型升级。云 HIS 系统具有功能完善,涵盖临床各业务部门,采集、抽提、汇总、存贮、展现所有的临床诊疗资料(包括:数据、文本、图形、图像、声音等),是医疗机构实现临床信息化的理想信息平台。
27 1
|
5天前
|
消息中间件 运维 监控
基于SaaS云部署、云计算的区域医院云HIS系统源码(运维管理+运营管理+综合监管)
医院云his系统采用主流成熟技术开发,B/S架构,软件结构简洁、代码规范易阅读,SaaS应用,全浏览器访问,前后端分离,多服务协同,服务可拆分,功能易扩展。多医院统一登录患者主索引建立、主数据管理,统一对外接口管理。
40 1
|
5天前
|
监控 Java BI
java基于云计算的SaaS医院his信息系统源码 HIS云平台源码
基于云计算技术的B/S架构的HIS系统源码,SaaS模式Java版云HIS系统,融合B/S版电子病历系统,支持电子病历四级,HIS与电子病历系统均拥有自主知识产权。
43 5
|
5天前
|
运维 安全 定位技术
云HIS系统采用B/S架构云端SaaS服务的方式提供,使用用户通过浏览器即能访问
云HIS系统采用B/S架构云端SaaS服务的方式提供,使用用户通过浏览器即能访问
29 2
|
5天前
|
缓存 小程序
Java+saas模式 智慧校园系统源码MySQL5.7+ elmentui前后端分离架构 让校园管理更高效的数字化平台系统源码
智慧校园是在数字通增强版基础上,研发的一套面向教育行业的数字化校园软件,其显著特点是集学校网站、协同办公、即时通讯、网络空间、移动办公于一体。在满足教职工日常办公需要的同时,拥有诸多教育行业功能,并提供便捷易用的“家校通”平台以满足老师、学生、家长的日常交流。数字通智慧校园教育版中的协同办公、即时通讯、移动办公等功能模块随通用版一同改进,将网络办公最新技术应用到教育行业。
47 1