admin管理员组

文章数量:1122851

SaaS

第3章-SaaS系统用户权限设计

1 组织机构管理

1.1 需求分析

1.1.1 需求分析

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

1.1.2 数据库表设计

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.2 微服务实现

1.2.1 抽取公共代码

(1) 在公共controller

添加公共controller

import org.springframework.web.bind.annotation.ModelAttribute;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class BaseController {protected HttpServletRequest request;protected HttpServletResponse response;protected String companyId;protected String companyName;@ModelAttributepublic void setResAnReq(HttpServletRequest request,HttpServletResponse response) {this.request = request;this.response = response;/*** 目前使用 companyId = 1*         companyName = "传智播客"*/thispanyId = "1";thispanyName = "传智播客";}}
(2) 公共service

添加公共BaseService

import org.springframework.data.jpa.domain.Specification;import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;public class BaseService<T> {protected Specification<T> getSpec(String companyId) {Specification<T> spect = new Specification() {@Overridepublic Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder cb) {//根据企业id查询return cb.equal(root.get("companyId").as(String.class),companyId);}};return spect;}
}

1.2.2 实现基本CRUD操作

(1)实体类

创建Department实体类

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@Idprivate String id;/*** 父级ID*/private String pid;/*** 企业ID*/private String companyId;/*** 部门名称*/private String name;/*** 部门编码,同级部门不可重复*/private String code;/*** 负责人ID*/private String managerId;/***  负责人名称*/private String manager;/*** 介绍*/private String introduce;/*** 创建时间*/private Date createTime;
}
(2)持久化层

创建DepartmentDao

import com.ihrm.domainpany.Department;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;/*** 部门dao接口*/
public interface DepartmentDao extends JpaRepository<Department,String> ,JpaSpecificationExecutor<Department> {
}
(3)业务层

创建DepartmentService

import com.ihrmmon.service.BaseService;
import com.ihrmmon.utils.IdWorker;
import com.ihrmpany.dao.DepartmentDao;
import com.ihrm.domainpany.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.List;@Service
public class DepartmentService extends BaseService {@Autowiredprivate DepartmentDao departmentDao;@Autowiredprivate IdWorker idWorker;/*** 1.保存部门*/public void save(Department department) {//设置主键的值String id = idWorker.nextId()+"";department.setId(id);//调用dao保存部门departmentDao.save(department);}/*** 2.更新部门*/public void update(Department department) {//1.根据id查询部门Department dept = departmentDao.findById(department.getId()).get();//2.设置部门属性dept.setCode(department.getCode());dept.setIntroduce(department.getIntroduce());dept.setName(department.getName());//3.更新部门departmentDao.save(dept);}/*** 3.根据id查询部门*/public Department findById(String id) {return departmentDao.findById(id).get();}/*** 4.查询全部部门列表*/public List<Department> findAll(String companyId) {/*** 用户构造查询条件*      1.只查询companyId*      2.很多的地方都需要根据companyId查询*      3.很多的对象中都具有companyId**/
//        Specification<Department> spec = new Specification<Department>() {
//            /**
//             * 用户构造查询条件
//             *      root   :包含了所有的对象数据
//             *      cq     :一般不用
//             *      cb     :构造查询条件
//             */
//            public Predicate toPredicate(Root<Department> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
//                //根据企业id查询
//                return cb.equal(root.get("companyId").as(String.class),companyId);
//            }
//        };return departmentDao.findAll(getSpec(companyId));}/*** 5.根据id删除部门*/public void deleteById(String id) {departmentDao.deleteById(id);}
}
(4)控制层

创建控制器类DepartmentController

import com.ihrmmon.controller.BaseController;
import com.ihrmmon.entity.Result;
import com.ihrmmon.entity.ResultCode;
import com.ihrmpany.service.CompanyService;
import com.ihrmpany.service.DepartmentService;
import com.ihrm.domainpany.Company;
import com.ihrm.domainpany.Department;
import com.ihrm.domainpany.response.DeptListResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;//1.解决跨域
@CrossOrigin
//2.声明restContoller
@RestController
//3.设置父路径
@RequestMapping(value="/company")   //  company/deparment
public class DepartmentController extends BaseController{@Autowiredprivate DepartmentService departmentService;@Autowiredprivate CompanyService companyService;/*** 保存*/@RequestMapping(value="/department",method = RequestMethod.POST)public Result save(@RequestBody Department department) {//1.设置保存的企业id/*** 企业id:目前使用固定值1,以后会解决*/department.setCompanyId(companyId);//2.调用service完成保存企业departmentService.save(department);//3.构造返回结果return new Result(ResultCode.SUCCESS);}/*** 查询企业的部门列表* 指定企业id*/@RequestMapping(value="/department",method = RequestMethod.GET)public Result findAll() {//1.指定企业idCompany company = companyService.findById(companyId);//2.完成查询List<Department> list = departmentService.findAll(companyId);//3.构造返回结果DeptListResult deptListResult = new DeptListResult(company,list);return new Result(ResultCode.SUCCESS,deptListResult);}/*** 根据ID查询department*/@RequestMapping(value="/department/{id}",method = RequestMethod.GET)public Result findById(@PathVariable(value="id") String id) {Department department = departmentService.findById(id);return new Result(ResultCode.SUCCESS,department);}/*** 修改Department*/@RequestMapping(value="/department/{id}",method = RequestMethod.PUT)public Result update(@PathVariable(value="id") String id,@RequestBody Department department) {//1.设置修改的部门iddepartment.setId(id);//2.调用service更新departmentService.update(department);return new Result(ResultCode.SUCCESS);}/*** 根据id删除*/@RequestMapping(value="/department/{id}",method = RequestMethod.DELETE)public Result delete(@PathVariable(value="id") String id) {departmentService.deleteById(id);return new Result(ResultCode.SUCCESS);}
}

1.3 前端实现

1.3.1 创建模块

(1)使用命令行创建module-departments模块并引入到工程中
itheima moduleAdd departments
(2) 在src/main.js 中注册模块
import departments from '@/module-departments/' // 组织机构管理
Vue.use(departments, store)
(3)在 /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}}]}
]

1.3.2 配置请求API

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

import {createAPI, createFileAPI
} from '@/utils/request'
export const organList = data => createAPI('/company/departments', 'get', data)
export const add = data => createAPI('/company/departments', 'post', data)
export const update = data => createAPI(`/company/departments/${data.id}`, 'put', data)
export const detail = data => createAPI(`/company/departments/${data.id}`, 'get', data)
export const remove = data => createAPI(`/company/departments/${data.id}`, 'delete', 
data)
export const changeDept = data => createAPI(`/company/departments/changeDept`, 'put', 
data)
export const saveOrUpdate = data => {return data.id?update(data):add(data)}

1.3.3 构造列表

(1)构造基本页面样式

找到 /module-departments/page/index.vue ,使用element-ui提供的Card组件构造卡片式容器

<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.name}}</strong></span></span><div class="fr"><div class="treeRinfo"><span>负责人</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(organizationTree,0)">查看待分配员工</el-button></el-dropdown-item></el-dropdown-menu></el-dropdown></div>  </div></div><!-- 构造树形列表--></div></div>    </el-card></div>
</div>
</template>
<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; }
.boxpad > div:first-child,
.objectTree > div:first-child.el-tree-node > div:first-child {display: none; }
</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; }
anization-main:after,
anization-index-top:after {display: block;clear: both;content: '';visibility: hidden;height: 0; }
anization-main {font-size: 14px;font-size: 14px; }anization-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;}
anization-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)树形机构列表
 <el-tree :data="departData.children" :indent="20"><div class="generalClass" slot-scope="{node,data}" style="width:99%"><span><span><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><strong>{{ data.name }}</strong></span></span>                      </span>                    </span><div class=fr><span class="treeRinfo"><div class="treeRinfo"><span>负责人</span><span>在职  <em class="colGreen" title="在职人数">---
</em>&nbsp;&nbsp;(<em class="colGreen" title="正式员工">---</em>&nbsp;/&nbsp;<em
class="colRed" title="非正式员工">---</em>)</span>   </div><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="handleEdit(data.id)">编辑
部门</el-button></el-dropdown-item><el-dropdown-item><el-button type="text" @click="handleList(treeRoot,1)">查
看员工</el-button></el-dropdown-item><el-dropdown-item><el-button type="text" @click="handleDelete(data)">删除
</el-button></el-dropdown-item></el-dropdown-menu></el-dropdown></span>                  </div></div></el-tree>
(3) 构造数据
//数据绑定模型
data() {return {activeName: 'first',  //激活pane的名称dialogFormVisible:false,//是否显示弹出层标识parentId:'', //父iddepartData:{}, //部门列表formData:{} //表单提交数据}},//自定义方法methods: {getObject(params) {organList().then(res => {this.departData = res.data.data})}},//钩子函数created: function() {this.getObject()},

1.3.4 组织机构的增删改查

(1)新增部门

使用element-ui提供的dialog的弹出层构造弹出添加页面

<el-dialog title="编辑部门" :visible.sync="dialogFormVisible"><el-form ref="dataForm" :model="formData" label-width="120px"><el-form-item label="部门名称"><el-input v-model="formData.name" placeholder='请输入部门名称'></el-input></el-form-item><el-form-item label="部门编码"><el-input v-model="formData.code" placeholder='请输入部门编码'></el-input></el-form-item><el-form-item label="部门负责人"><el-input v-model="formData.manager" placeholder='请输入负责人'></el-input></el-form-item><el-form-item label="部门介绍"><el-input v-model="formData.introduce" placeholder='请输入介绍'></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button type="primary" @click="createData">确定</el-button><el-button @click="dialogFormVisible=false">取消</el-button></div>
</el-dialog>

配置保存方法

  createData:function() {this.formData.parentId = this.parentIdsaveOrUpdate(this.formData).then(res => {this.$message({message:res.data.message,type:res.data.success?"success":"error"});location.reload()this.dialogFormVisible=false})}
(2)修改部门
  1. 根据id查询部门
 handleEdit(id) {detail({id}).then( res=> {this.formData = res.data.datathis.dialogFormVisible = truethis.parentId = res.data.data.parentId})}
  1. 调用方法更新部门
(3)删除部门
  handleDelete(obj) {this.$confirm(`本次操作将删除${obj.name},删除后将不可恢复,您确认删除吗?`).then(() => {remove({id:obj.id}).then( res=> {this.$message({message:res.data.message,type:res.data.success?"success":"error"});location.reload()})})},

1.3.5 抽取组件

组件(Component) 是Vue.js 最强大的功能。可以通过将不同的业务拆分为不同的组件进行开发,让代码更加优雅提供可读性。当然页可以封装可重用的代码,通过传入对象的不同,实现组件的复用。

(1)抽取新增/修改页面到 /module-departments/components/add.vue
<template><el-dialog title="编辑部门" :visible.sync="dialogFormVisible"><el-form ref="dataForm" :model="formData" label-width="120px"><el-form-item label="部门名称"><el-input v-model="formData.name" placeholder='请输入部门名称'></el-input></el-form-item><el-form-item label="部门编码"><el-input v-model="formData.code" placeholder='请输入部门编码'></el-input></el-form-item><el-form-item label="部门负责人"><el-input v-model="formData.manager" placeholder='请输入部门负责人'></el-input></el-form-item> <el-form-item label="部门介绍"><el-input v-model="formData.introduce" placeholder='请输入部门介绍'></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button type="primary" @click="createData">确定</el-button><el-button @click="dialogFormVisible=false">取消</el-button></div></el-dialog>
</template>
<script>
import { saveOrUpdate } from '@/api/base/departments'
export default {name: 'dept-add',data() {return {dialogFormVisible:false,formData:{},parentId:''}},methods: {createData:function() {this.formData.parentId = this.parentIdsaveOrUpdate(this.formData).then(res => {this.$message({message:res.data.message,type:res.data.success?"success":"error"});location.reload()this.dialogFormVisible=false})}}
}</script>
(2) 在 /module-departments/page/index.vue 中引用组件
  • 导入组件
import deptAdd from './../components/add'  //导入组件
export default { //声明引用组件components: { deptAdd }, //声明组件data() {return {deptAdd: 'deptAdd', //配置组件别名activeName: 'first', departData:{},}},}
  • 使用组件
//v-bind:is (绑定的组件名称)
//ref : 引用子组件中内容的别名
<component v-bind:is="deptAdd" ref="deptAdd"></component>
  • 改造新增修改方法
 handlAdd(parentId) {//对子组件中的属性复制this.$refs.deptAdd.formData = {};this.$refs.deptAdd.parentId = parentIdthis.$refs.deptAdd.dialogFormVisible = true;},handleEdit(id) {detail({id}).then( res=> {this.$refs.deptAdd.formData = res.data.datathis.$refs.deptAdd.dialogFormVisible = truethis.$refs.deptAdd.parentId = res.data.data.parentId})},

本文标签: SaaS