南山模板网站建设公司,wordpress点击下载,怎么查询建设通网站,网站后台管理系统SaaS系统用户权限设计 学习目标#xff1a; 理解RBAC模型的基本概念及设计思路 了解SAAS-HRM中权限控制的需求及表结构分析完成组织机构的基本CRUD操作 完成用户管理的基本CRUD操作完成角色管理的基本CRUD操作 组织机构管理 需求分析 需求分析 实现企业组织结构管理#xff0… 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)
) ENGINEInnoDB DEFAULT CHARSETutf8mb4 微服务实现 抽取公共代码 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
* 获取requestresponse
* 获取企业id获取企业名称
*/
public class BaseController {protected HttpServletRequest request;protected HttpServletResponse response;ModelAttributepublic 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 BaseServiceT {protected SpecificationT getSpecification(String companyId) {return new SpecificationT() {Overridepublic Predicate toPredicate(RootT root, CriteriaQuery? criteriaQuery, CriteriaBuilder cb) {return cb.equal(root.get(companyId).as(String.class),companyId);}};}
} 骚戴理解:在SpringData里面提供的findAll方法如果是没有任何参数就是获取所有的数据如果需要查询所有符合条件的数据那么将就需要传入一个参数SpecificationSpecification参数其实就是条件 这里的getSpecification方法是用来创建这个条件的这里是抽出来了这个条件的意思是查询companyId这个公司id下的所有部门组织架构 criteriabuilder.equal(root.get(companyid).as(string.class), companyid)中的两个companyid指的是不同的变量。 第一个companyid表示root对象所代表的实体类中的属性名该属性用于和后面传递进来的companyid进行比较。第二个companyid表示方法参数中传递进来的某个值用于与实体的companyid属性进行比较。 简而言之这个代码片段的作用是使用criteriabuilder构造一个查询条件即根据传入的companyid值筛选出实体类中属性名为companyid的值等于该值的记录。 public predicate topredicate(roott root, criteriaquery? criteriaquery, criteriabuilder cb) 是一个接口方法用于创建 criteria api 中的查询谓语predicate它接收三个参数 roott 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 ListDepartment depts;public DeptListResult(Company company,ListDepartment 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());ListDepartment 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;//IDIdprivate 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 JpaRepositoryDepartment, String,
JpaSpecificationExecutorDepartment {
} 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 {Autowiredprivate IdWorker idWorker;Autowiredprivate 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 ListDepartment 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{Autowiredprivate DepartmentService departmentService;Autowiredprivate 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());ListDepartment 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构造页面样式 templatediv classdashboard-containerdiv classapp-containerel-card shadowneverdiv classorganization-indexdiv classorganization-index-topdiv classmain-top-titleel-tabs v-modelactiveNameel-tab-pane label组织结构 namefirst/el-tab-panediv classel-tabs-reporta classel-button el-button--primary el-button--mini title导出 导入/aa classel-button el-button--primary el-button--mini title导出 导出/a/div/el-tabs/div/divdiv styleoverflow: scroll;white-space:nowrap classtreBoxdiv classtreeCon clearfixspani classfa fa-university aria-hiddentrue/ispan strong{{departData.companyName}}/strong/span/spandiv classfrspan classtreeRinfodiv classtreeRinfospan{{departData.companyManage}}/spanspan在职 em classcolGreen title在职人数---/emnbsp;nbsp;(em classcolGreen title正式员工---/emnbsp;/nbsp;em classcolRed title非正式员工---/em)/span/divdiv classtreeRinfoel-dropdown classitemspan classel-dropdown-link操作i classel-icon-arrow-down el-icon--right/i/spanel-dropdown-menu slotdropdownel-dropdown-itemel-button typetext clickhandlAdd()添加子部门/el-button/el-dropdown-itemel-dropdown-itemel-button typetext clickhandleList()查看待分配员工/el-button/el-dropdown-item/el-dropdown-menu/el-dropdown/div/span /div/div!-- 构造树形列表叶子 i classfa fa-male/i非叶子 展开 i classfa fa-minus-square-o闭合 i classfa fa-plus-square-odiv classgeneralClass slot-scope{node,data} stylewidth:99%--el-tree :props{label:name} :datadepts node-keyid default-expand-all!--node : 是否展开是否叶子节点data部门对象idname--div classgeneralClass slot-scope{node,data} stylewidth:99%spani v-ifnode.isLeaf classfa fa-male/ii v-else :classnode.expanded?fa fa-minus-square-o:fa fa-plus-square-o/ispan{{ node.label }}/span/spandiv classfrspan classtreeRinfodiv classtreeRinfospan{{departData.companyManage}}/spanspan在职 em classcolGreen title在职人数---/emnbsp;nbsp;(em classcolGreen title正式员工---/emnbsp;/nbsp;em classcolRed title非正式员工---/em)/span/divdiv classtreeRinfoel-dropdown classitemspan classel-dropdown-link操作i classel-icon-arrow-down el-icon--right/i/spanel-dropdown-menu slotdropdownel-dropdown-itemel-button typetext clickhandlAdd(data.id)添加子部门/el-button/el-dropdown-itemel-dropdown-itemel-button typetext clickhandUpdate(data.id)查看部门/el-button/el-dropdown-itemel-dropdown-itemel-button typetext clickhandleList()查看待分配员工/el-button/el-dropdown-itemel-dropdown-itemel-button typetext clickhandleDelete(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:isdeptAdd refaddDept/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 relstylesheet/scss langscss
.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 relstylesheet/scss langscss 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;
}
/style2树形机构列表 !-- 构造树形列表叶子 i classfa fa-male/i非叶子 展开 i classfa fa-minus-square-o闭合 i classfa fa-plus-square-odiv classgeneralClass slot-scope{node,data} stylewidth:99%--el-tree :props{label:name} :datadepts node-keyid default-expand-all!--node : 是否展开是否叶子节点data部门对象idname--div classgeneralClass slot-scope{node,data} stylewidth:99%spani v-ifnode.isLeaf classfa fa-male/ii v-else :classnode.expanded?fa fa-minus-square-o:fa fa-plus-square-o/ispan{{ node.label }}/span/spandiv classfrspan classtreeRinfodiv classtreeRinfospan{{departData.companyManage}}/spanspan在职 em classcolGreen title在职人数---/emnbsp;nbsp;(em classcolGreen title正式员工---/emnbsp;/nbsp;em classcolRed title非正式员工---/em)/span/divdiv classtreeRinfoel-dropdown classitemspan classel-dropdown-link操作i classel-icon-arrow-down el-icon--right/i/spanel-dropdown-menu slotdropdownel-dropdown-itemel-button typetext clickhandlAdd(data.id)添加子部门/el-button/el-dropdown-itemel-dropdown-itemel-button typetext clickhandUpdate(data.id)查看部门/el-button/el-dropdown-itemel-dropdown-itemel-button typetext clickhandleList()查看待分配员工/el-button/el-dropdown-itemel-dropdown-itemel-button typetext clickhandleDelete(data.id)删除部门/el-button/el-dropdown-item/el-dropdown-menu/el-dropdown/div/span /div/div/el-tree 骚戴理解 解释el-tree :props{label:name} :datadepts node-keyid default-expand-all 这是vue.js中使用的一个el-tree组件用于显示层次结构的树形数据。以下是对代码中每个属性的解释 :props{label:name}这个属性设置了要显示在树节点上的文本内容的属性名为“name”即树节点将显示data数组中每个元素的name属性。 后面可以通过{{ node.label }}去获取name也就是部门的名称:datadepts这个属性设置了树形数据的来源为一个名为“depts”的数据数组即树的每个节点由数组中的一个元素表示。node-keyid这个属性设置了树节点的唯一标识符为data数组中每个元素的id属性这将有助于在树中添加、删除或更新节点时进行准确定位。default-expand-all这个属性设置了默认情况下所有树节点都将展开显示。 解释 i v-ifnode.isLeaf classfa fa-male/i 和i v-else :classnode.expanded?fa fa-minus-square-o:fa fa-plus-square-o/i 如果 node.isleaf 为真(判断是不是叶子节点)则渲染一个男性符号的图标 i classfa fa-male/i。否则就渲染一个可以展开和折叠的矩形图标。该矩形图标的样式取决于 node.expanded 属性的值 如果 node.expanded 为 true节点展开的话则渲染带有减号的图标 i classfa fa-minus-square-o/i。如果 node.expanded 为 false节点关闭的话则渲染带有加号的图标 i classfa fa-plus-square-o 解释以下代码 span在职 em classcolGreen title在职人数---/emnbsp;nbsp;(em classcolGreen title正式员工---/emnbsp;/nbsp;em classcolRed title非正式员工---/em)/span em 标签用于在文本中强调某些词语使其在视觉上与其他文本内容区别开来。nbsp;表示空格实现效果如下所示 el-dropdown classitem
span classel-dropdown-link操作i classel-icon-arrow-down el-icon--right/i
/span
el-dropdown-menu slotdropdownel-dropdown-itemel-button typetext clickhandlAdd(data.id)添加子部门/el-button/el-dropdown-itemel-dropdown-itemel-button typetext clickhandUpdate(data.id)查看部门/el-button/el-dropdown-itemel-dropdown-itemel-button typetext clickhandleList()查看待分配员工/el-button/el-dropdown-itemel-dropdown-itemel-button typetext clickhandleDelete(data.id)删除部门/el-button/el-dropdown-item
/el-dropdown-menu
/el-dropdown 通过以上代码实现以下效果 3 构造数据 !-- 引入组件 --
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 4树形组件 export default {timestampToTime: (timestamp) {let date new Date(timestamp * 1000)let Y date.getFullYear() -let M (date.getMonth() 1 10 ? 0 (date.getMonth() 1) : date.getMonth() 1) -let D date.getDate() let h date.getHours() :let m date.getMinutes() :let s date.getSeconds()return Y M D h m s},transformTozTreeFormat: function (sNodes) {var i, l;var r [];var tmpMap {};for (i 0, l sNodes.length; i l; i) {tmpMap[sNodes[i].id] sNodes[i];}for (i 0, l sNodes.length; i l; i) {var p tmpMap[sNodes[i].parentId];if (p sNodes[i].id ! sNodes[i].parentId) {var children this.nodeChildren(p);if (!children) {children this.nodeChildren(p, []);}children.push(sNodes[i]);} else {r.push(sNodes[i]);}}return r;},nodeChildren: function (node, newChildren) {if (typeof newChildren ! undefined) {node.children newChildren;}return node.children;}
}骚戴理解这是个好东西可以把后端传过来的List集合解析成树状的数据结构 组织机构的增删改查 新增部门 使用element-ui提供的dialog的弹出层构造弹出添加页面 el-dialog title编辑部门 :visible.syncdialogFormVisibleel-form refdataForm :modelformData label-width120pxel-form-item label部门名称el-input v-modelformData.name placeholder请输入部门名称/el-input/el-form-itemel-form-item label部门编码el-input v-modelformData.code placeholder请输入部门编码/el-input/el-form-itemel-form-item label部门负责人el-input v-modelformData.manager placeholder请输入负责人/el-input/el-form-itemel-form-item label部门介绍el-input v-modelformData.introduce placeholder请输入介绍/el-input/el-form-item/el-formdiv slotfooter classdialog-footerel-button typeprimary clickcreateData确定/el-buttonel-button clickdialogFormVisiblefalse取消/el-button/div
/el-dialog 配置保存方法 script
import {list,saveOrupdate,find,deleteById} from /api/base/dept
export default {data () {return {//添加部门的模型parentId:,dialogFormVisible:false,dept:{}}},methods: {saveDept() {this.dept.parentId this.parentIdsaveOrupdate(this.dept).then(res {this.$message({message: res.data.message,type: res.data.success?success:error});//保存成功if(res.data.success) {//如果成功location.reload();}})}}
}
/script 骚戴理解新增有两个步骤弹出新增框和点击保存。这里有两种类型的新增分别是有父节点的新增和没有父节点的新增只需要把父节点的id传过来即可 saveOrupdate(this.dept)这是其中的一个API如果传过去的this.dept有id那就是修改操作没有id就是新增操作。这里可以发现this.dept模型是没有赋值id的所以是新增操作这样搞应该是为了让新增和修改都使用同一个组件也就是都用这个对话框res.data.success?success:error的意思的res.data.success返回的是false就是error类型如果返回的是true那就是success类型。 location.reload();刷新页面有很明显的迟钝所以我还是用的getList方法 来获取新的数据但是在组件中怎么引用父类Vue的方法呢 this.$parent.getList();这样写就可以调父类的方法了 修改部门 根据id查询部门 //修改部门handUpdate(id) {//根据id查询部门find({id:id}).then(res {//数据绑定到dept对象中this.$refs.addDept.dept res.data.data;this.$refs.addDept.dialogFormVisible true})} 骚戴理解新增和修改用的都是同一个弹窗后面也抽离成为一个add.vue页面新增和修改都是调用的saveOrupdate这个API这个API可以根据是否有id值才判断调用save或update这两个API。 在前端{id:id}就表示一个对象只要是{}这样的格式就是对象所以下面的update接口用的是data对象而不是id export const saveOrupdate data {return data.id?update(data):save(data)}
export const update data createAPI(/company/department/${data.id}, put, data) 这里我一开始搞不懂为什么前端只传了一个id后端却可以接收到Department对象其实是我搞混淆了上面API的data其实对应的是后端的Department对象然后后端的id是从url路径中获取的前端传过去的id是从data中获取的 删除部门 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();}})})}, 抽取组件 组件Component是Vue.js 最强大的功能。可以通过将不同的业务拆分为不同的组件进行开发让代码更加优雅提供可读性。当然页可以封装可重用的代码通过传入对象的不同实现组件的复用。 1抽取新增/修改页面到 /module-departments/components/add.vue 中 templateel-dialog title编辑部门 :visible.syncdialogFormVisible!-- model : 数据模型 --el-form :modeldept label-width120pxel-form-item label部门名称el-input v-modeldept.name autocompleteoff/el-input/el-form-itemel-form-item label部门编码el-input v-modeldept.code autocompleteoff/el-input/el-form-itemel-form-item label部门负责人el-input v-modeldept.manager autocompleteoff/el-input/el-form-itemel-form-item label部门介绍el-input v-modeldept.introduce autocompleteoff/el-input/el-form-item/el-formdiv slotfooter classdialog-footerel-button clickdialogFormVisible false取 消/el-buttonel-button typeprimary clicksaveDept确 定/el-button/div/el-dialog
/templatescript
import {list,saveOrupdate,find,deleteById} from /api/base/dept
export default {data () {return {//添加部门的模型parentId:,dialogFormVisible:false,dept:{}}},methods: {saveDept() {this.dept.parentId this.parentIdsaveOrupdate(this.dept).then(res {this.$message({message: res.data.message,type: res.data.success?success:error});//保存成功if(res.data.success) {//如果成功location.reload();}})}}
}
/scriptstyle/style2 在 /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:isdeptAdd refdeptAdd/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})}, 骚戴理解this.$refs.deptAdd.formData这样写可以在父Vue中调用组件的属性formData RBAC模型 什么是RBAC RBAC全称Role-Based Access Control基于角色的权限访问控制作为传统访问控制自主访问强制访问的有前景的代替受到广泛的关注。在RBAC中权限与角色相关联用户通过成为适当角色的成员而得到这些 角色的权限。这就极大地简化了权限的管理。在一个组织中角色是为了完成各种工作而创造用户则依据它的责 任和资格来被指派相应的角色用户可以很容易地从一个角色被指派到另一个角色。角色可依新的需求和系统的合 并而赋予新的权限而权限也可根据需要而从某角色中回收。角色与角色的关系可以建立起来以囊括更广泛的客观情况。 访问控制是针对越权使用资源的防御措施目的是为了限制访问主体如用户等 对访问客体如数据库资源等 的访问权限。企业环境中的访问控制策略大部分都采用基于角色的访问控制RBAC模型是目前公认的解决大型企业的统一资源访问控制的有效方法 基于RBAC的设计思路 基于角色的访问控制基本原理是在用户和访问权限之间加入角色这一层实现用户和权限的分离用户只有通过激 活角色才能获得访问权限。通过角色对权限分组大大简化了用户权限分配表间接地实现了对用户的分组提高 了权限的分配效率。且加入角色层后访问控制机制更接近真实世界中的职业分配便于权限管理。 在RBAC模型中角色是系统根据管理中相对稳定的职权和责任来划分每种角色可以完成一定的职能。用户通过 饰演不同的角色获得角色所拥有的权限一旦某个用户成为某角色的成员则此用户可以完成该角色所具有的职能。通过将权限指定给角色而不是用户在权限分派上提供了极大的灵活性和极细的权限指定粒度。 骚戴理解RBAC模型其实就是给角色分配固定的权限然后给用户去分配角色这样用户就具有了这个角色有的所有权限从而达到权限控制的目的如果不这样的话那就需要手动的为用户添加一个个的权限来实现权限管理 表结构分析 一个用户拥有若干角色每一个角色拥有若干权限。这样就构造成“用户-角色-权限”的授权模型。在这种模型中用户与角色之间角色与权限之间一般者是多对多的关系。 SAAS-HRM中的权限设计 需求分析 SAAS平台的基本元素 SAAS平台管理员负责平台的日常维护和管理包括用户日志的管理、租户账号审核、租户状态管理、租户费用 的管理要注意的是平台管理员不能对租户的具体业务进行管理 企业租户指访问SaaS平台的用户企业在SaaS平台中各租户之间信息是独立的。 租户管理员为租户角色分配权限和相关系统管理、维护。 租户角色根据业务功能租户管理员进行角色划分划分好角色后租户管理员可以对相应的角色进行权限分配 租户用户需对租户用户进行角色分配租户用户只能访问授权的模块信息。 需求分析 在应用系统中权限是以什么样的形式展现出来的对菜单的访问页面上按钮的可见性后端接口的控制都要进行充分考虑 前端 前端菜单根据是否有请求菜单权限进行动态加载按钮根据是否具有此权限点进行显示/隐藏的控制 后端 前端发送请求到后端接口有必要对接口的访问进行权限的验证 权限设计 针对这样的需求在有些设计中可以将菜单按钮后端API请求等作为资源这样就构成了基于RBAC的另一种授权模型用户-角色-权限-资源。在SAAS-HRM系统的权限设计中我们就是才用了此方案 针对此种权限模型其中权限究竟是属于菜单按钮还是API的权限呢那就需要在设计数据库权限表的时候添加类型加以区分如权限类型 1为菜单 2为功能 3为API。 表结构分析 这里要注意的是权限表与权限菜单表、页面元素表与API接口表都是一对一的关系与传统的RBAC模型对比不难发现此种设计的好处 不需要区分哪些是操作哪些是资源方便扩展当系统要对新的东西进行权限控制时我只需要建立一个新的资源表并确定这类权限的权限类 型标识即可。 骚戴理解这里的权限可能是菜单页面元素API接口具体这个权限是什么由权限表里的权限字段来控制权限字段如果是1那就是菜单如果是2那就是页面元素如果是3那就是API接口然后再去对应的表中去查询这个权限有哪些菜单有哪些页面元素有哪些API接口这里我一开始理解的是一对多得到关系而它这里是其实是指的权限表和一套菜单一套页面元素一套API所以是一对一关系 用户管理 需求分析 用户其实就是saas企业访问的员工对企业员工完成基本的CRUD操作表结构如下 CREATE TABLE bs_user (
id varchar(40) NOT NULL COMMENT ID,
mobile varchar(40) NOT NULL COMMENT 手机号码,
username varchar(255) NOT NULL COMMENT 用户名称,
password varchar(255) DEFAULT NULL COMMENT 密码,
enable_state int(2) DEFAULT 1 COMMENT 启用状态 0是禁用1是启用,
create_time datetime DEFAULT NULL COMMENT 创建时间,
department_id varchar(40) DEFAULT NULL COMMENT 部门ID,
time_of_entry datetime DEFAULT NULL COMMENT 入职时间,
form_of_employment int(1) DEFAULT NULL COMMENT 聘用形式,
work_number varchar(20) DEFAULT NULL COMMENT 工号,
form_of_management varchar(8) DEFAULT NULL COMMENT 管理形式,
working_city varchar(16) DEFAULT NULL COMMENT 工作城市,
correction_time datetime DEFAULT NULL COMMENT 转正时间,
in_service_status int(1) DEFAULT NULL COMMENT 在职状态 1.在职 2.离职,
company_id varchar(40) DEFAULT NULL COMMENT 企业ID,
company_name varchar(40) DEFAULT NULL,
department_name varchar(40) DEFAULT NULL, PRIMARY KEY (id),
UNIQUE KEY idx_user_phone (mobile) USING BTREE
) ENGINEInnoDB DEFAULT CHARSETutf8mb4 配置系统微服务 搭建系统微服务模块ihrm_systempom引入依赖 dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-jpa/artifactId/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependencydependencygroupIdcom.ihrm/groupIdartifactIdihrm_common/artifactIdversion1.0-SNAPSHOT/version/dependency/dependencies 配置application.yml server:port: 9002
spring:application:name: ihrm-system #指定服务名datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/ihrm?useUnicodetruecharacterEncodingutf8username: rootpassword: 111111jpa:database: MySQLshow-sql: trueopen-in-view: true 配置启动类 package com.ihrm.system;
import com.ihrm.common.utils.IdWorker;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
SpringBootApplication(scanBasePackages com.ihrm)
EntityScan(com.ihrm.domain.system)
public class SystemApplication {public static void main(String[] args) {SpringApplication.run(SystemApplication.class, args);}Beanpublic IdWorker idWorkker() {return new IdWorker(1, 1);}
} 后端用户基本操作 实体类 package com.ihrm.domain.system;import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;/*** 用户实体类*/
Entity
Table(name bs_user)
Getter
Setter
public class User implements Serializable {private static final long serialVersionUID 4297464181093070302L;/*** ID*/Idprivate String id;/*** 手机号码*/private String mobile;/*** 用户名称*/private String username;/*** 密码*/private String password;/*** 启用状态 0为禁用 1为启用*/private Integer enableState;/*** 创建时间*/private Date createTime;private String companyId;private String companyName;/*** 部门ID*/private String departmentId;/*** 入职时间*/private Date timeOfEntry;/*** 聘用形式*/private Integer formOfEmployment;/*** 工号*/private String workNumber;/*** 管理形式*/private String formOfManagement;/*** 工作城市*/private String workingCity;/*** 转正时间*/private Date correctionTime;/*** 在职状态 1.在职 2.离职*/private Integer inServiceStatus;private String departmentName;/*** JsonIgnore* : 忽略json转化*/JsonIgnoreManyToManyJoinTable(namepe_user_role,joinColumns{JoinColumn(nameuser_id,referencedColumnNameid)},inverseJoinColumns{JoinColumn(namerole_id,referencedColumnNameid)})private SetRole roles new HashSetRole();//用户与角色 多对多
}骚戴理解 jsonignore注解用于忽略指定的属性字段即在序列化/反序列化过程中不将该字段作为json数据的一部分。该注解通常用于保护敏感信息或避免无限循环引用等问题。这里是为了避免循环引用的情况因为在User里private SetRole roles new HashSetRole();而在Role里private SetUser users new HashSetUser(0);所以循环依赖了加这个注解就没事了manytomany注解表示多对多关系即一个实体类entity实例可以关联到多个其他实体类例同时一个实体类实例也可以被多个其他实体类实例所关联。在jpa中多对多关系需要通过中间表来实现。jointable注解则用于定义多对多关系中的中间表的结构细节和关联方式。其中包括中间表的名称、关联字段、外键等信息。这些信息可以通过joincolumns和inversejoincolumns子注解进行详细配置。 name: 指定关联表中间表的名称这里为pe_user_role。joinColumns{JoinColumn(nameuser_id,referencedColumnNameid)}里的joinColumns表示关联当前类和中间表和外部表的字段的关系name表示当前类所对应表的主键在关联表中间表中的字段名referencedColumnName表示外部表的字段也就是角色表中的idinverseJoinColumns{JoinColumn(namerole_id,referencedColumnNameid)}里的inverseJoinColumns表示关联外部表和当前类所对应的表和中间表的字段的关系name表示外部表主键在关联表中间表中的字段名referencedColumnName表示当前类所对应的表的字段也就是用户表中的id 持久化层 package com.ihrm.system.dao;
import com.ihrm.system.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/*** 企业数据访问接口*/
public interface UserDao extends JpaRepositoryUser, String,
JpaSpecificationExecutorUser {
} 业务逻辑层 package com.ihrm.system.service;
import com.ihrm.common.utils.IdWorker;
import com.ihrm.domain.system.Role;
import com.ihrm.domain.system.User;
import com.ihrm.system.dao.RoleDao;
import com.ihrm.system.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;
/**
* 部门操作业务逻辑层
*/
Service
public class UserService {Autowiredprivate IdWorker idWorker;Autowiredprivate UserDao userDao;Autowiredprivate RoleDao roleDao;public User findByMobileAndPassword(String mobile, String password) {User user userDao.findByMobile(mobile);if (user ! null password.equals(user.getPassword())) {return user;} else {return null;}}/*** 添加用户*/public void save(User user) {//填充其他参数user.setId(idWorker.nextId() );user.setCreateTime(new Date()); //创建时间user.setPassword(123456);//设置默认登录密码user.setEnableState(1);//状态userDao.save(user);}/*** 更新用户*/public void update(User user) {User targer userDao.getOne(user.getId());targer.setPassword(user.getPassword());targer.setUsername(user.getUsername());targer.setMobile(user.getMobile());targer.setDepartmentId(user.getDepartmentId());targer.setDepartmentName(user.getDepartmentName());userDao.save(targer);}/*** 根据ID查询用户*/public User findById(String id) {return userDao.findById(id).get();}/*** 删除部门** param id 部门ID*/public void delete(String id) {userDao.deleteById(id);}public PageUser findSearch(MapString,Object map, int page, int size) {return userDao.findAll(createSpecification(map), PageRequest.of(page-1, size));}/*** 调整部门*/public void changeDept(String deptId,String deptName,ListString ids) {for (String id : ids) {User user userDao.findById(id).get();user.setDepartmentName(deptName);user.setDepartmentId(deptId);userDao.save(user);}}/*** 分配角色*/public void assignRoles(String userId,ListString roleIds) {User user userDao.findById(userId).get();SetRole roles new HashSet();for (String id : roleIds) {Role role roleDao.findById(id).get();roles.add(role);}//设置用户和角色之间的关系user.setRoles(roles);userDao.save(user);}/*** 动态条件构建* param searchMap* return*/private SpecificationUser createSpecification(Map searchMap) {return new SpecificationUser() {Overridepublic Predicate toPredicate(RootUser root, CriteriaQuery? query,
CriteriaBuilder cb) {ListPredicate predicateList new ArrayListPredicate();// IDif (searchMap.get(id) !null !.equals(searchMap.get(id))) {predicateList.add(cb.equal(root.get(id).as(String.class),
(String)searchMap.get(id)));}// 手机号码if (searchMap.get(mobile)!null
!.equals(searchMap.get(mobile))) {predicateList.add(cb.equal(root.get(mobile).as(String.class),
(String)searchMap.get(mobile)));}// 用户IDif (searchMap.get(departmentId)!null
!.equals(searchMap.get(departmentId))) {
predicateList.add(cb.like(root.get(departmentId).as(String.class),
(String)searchMap.get(departmentId)));}// 标题if (searchMap.get(formOfEmployment)!null
!.equals(searchMap.get(formOfEmployment))) {predicateList.add(cb.like(root.get(formOfEmployment).as(String.class),
(String)searchMap.get(formOfEmployment)));}if (searchMap.get(companyId)!null
!.equals(searchMap.get(companyId))) {predicateList.add(cb.like(root.get(companyId).as(String.class),
(String)searchMap.get(companyId)));}if (searchMap.get(hasDept)!null
!.equals(searchMap.get(hasDept))) {if(0.equals((String)searchMap.get(hasDept))) {predicateList.add(cb.isNull(root.get(departmentId)));}else{predicateList.add(cb.isNotNull(root.get(departmentId)));}}return cb.and( predicateList.toArray(new
Predicate[predicateList.size()]));}};}
} 骚戴理解在SpringData的JPA中save方法是保持数据当实体中包含主键时JPA的save方法会进行更新操作。这个系统的所有新增都是给了id的所以新增其实都是修改操作。 new PageRequest(page-1, size)中page-1是因为下标是从0开始的 criteriabuilder.and(list.toarray(new predicate[list.size()])); 是一个java语言的代码片段用于创建一个and条件的jpa查询。 该代码的解释如下 criteriabuilder代表一个criteriabuilder实例它是jpa criteria api中的一个辅助类。criteriabuilder可以用于创建一些查询相关的对象比如predicate查询条件。list是一个listpredicate类型的集合用于存放对某个字段的多个查询条件例如相等大于小于等多个条件之间的关系是“且”的关系。toarray()方法将predicate集合转换成数组类型。new predicate[list.size()] 创建了一个长度为list.size()的predicate数组。list.toarray(new predicate[list.size()])简单理解就是把list转化成predicate数组criteriabuilder.and(...)表示将predicate数组中的所有条件使用“且”的关系连接在一起。 因此该代码片段的作用是在jpa查询中添加多个and条件 控制器层 package com.ihrm.system.controller;import com.ihrm.common.controller.BaseController;
import com.ihrm.common.entity.PageResult;
import com.ihrm.common.entity.Result;
import com.ihrm.common.entity.ResultCode;import com.ihrm.domain.system.User;
import com.ihrm.system.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;import javax.websocket.server.PathParam;
import java.util.List;
import java.util.Map;//1.解决跨域
CrossOrigin
//2.声明restContoller
RestController
//3.设置父路径
RequestMapping(value/sys)
public class UserController extends BaseController {Autowiredprivate UserService userService;/*** 保存*/RequestMapping(value /user, method RequestMethod.POST)public Result save(RequestBody User user) {//1.设置保存的企业iduser.setCompanyId(parseCompanyId());user.setCompanyName(parseCompanyName());//2.调用service完成保存企业userService.save(user);//3.构造返回结果return new Result(ResultCode.SUCCESS);}/*** 查询企业的用户列表* 指定企业id*/RequestMapping(value /user, method RequestMethod.GET)public Result findAll(int page, int size, RequestParam Map map) {//1.获取当前的企业idmap.put(companyId,parseCompanyId());//2.完成查询PageUser pageUser userService.findAll(map,page,size);//3.构造返回结果PageResult pageResult new PageResult(pageUser.getTotalElements(),pageUser.getContent());return new Result(ResultCode.SUCCESS, pageResult);}/*** 根据ID查询user*/RequestMapping(value /user/{id}, method RequestMethod.GET)public Result findById(PathVariable(value id) String id) {User user userService.findById(id);return new Result(ResultCode.SUCCESS, user);}/*** 修改User*/RequestMapping(value /user/{id}, method RequestMethod.PUT)public Result update(PathVariable(value id) String id, RequestBody User user) {//1.设置修改的部门iduser.setId(id);//2.调用service更新userService.update(user);return new Result(ResultCode.SUCCESS);}/*** 根据id删除*/RequestMapping(value /user/{id}, method RequestMethod.DELETE)public Result delete(PathVariable(value id) String id) {userService.deleteById(id);return new Result(ResultCode.SUCCESS);}
}前端用户基本操作 由于时间有限,本着不浪费时间的原则页面部分的基本功能都是大致相似的。使用提供的基本模块代码构建模块信息。 配置接口请求路径 由于后端是微服务每个工程的端口号都不一样所以这里就需要用到vue提供的代理修改config\dev.env.js use strict
const merge require(webpack-merge)
const prodEnv require(./prod.env)module.exports merge(prodEnv, {NODE_ENV: development,BASE_API: api
})在config/index.js中通过proxyTable配置代理转发的请求后端地址 proxyTable: {//企业信息请求的远程服务/api/company: {target: http://localhost:9001/company/,changeOrigin: true,pathRewrite: {^/api/company: }},//api/sys/ user/api/sys: {target: http://localhost:9002/sys/,changeOrigin: true,pathRewrite: {^/api/sys: }}}, 骚戴理解上面这段代码是一个vue.js项目中的配置文件用于将本地请求代理到对应的远程服务上以解决跨域问题。其中proxytable是一个对象它包含了需要代理的不同路径及其对应的远程服务。 例如当有请求发起至/api/company时在本地服务器上不能直接访问远程服务 http://localhost:9001/company/因此我们在这里通过配置proxytable将其代理到对应的远程服务上。changeorigin为true表示是否改变源pathrewrite是一个映射规则将请求的路径中的 /api/company部分替换为即去除了/api/company的前缀。同样的方式适用于/api/sys的请求。 导入员工模块 注册模块 import employees from /module-employees/ // 员工管理
Vue.use(employees, store) 在/src/api/base/下配置APIuser.js import {createAPI} from /utils/request
export const list data createAPI(/sys/user, get, data)
export const simple data createAPI(/sys/user/simple, get, data)
export const add data createAPI(/sys/user, post, data)
export const update data createAPI(/sys/user/${data.id}, put, data)
export const remove data createAPI(/sys/user/${data.id}, delete, data)
export const detail data createAPI(/sys/user/${data.id}, get, data) 用户列表展示 页面代码 templatediv classdashboard-containerdiv classapp-containerel-cardspan classseleInfoel-select v-modelrequestParameters.stausInfoel-option v-foritem in baseData.stausInfos :keyitem.value :labelitem.label :valueitem.value/el-option/el-select/spanspan classposInfo本月nbsp;nbsp;(--)nbsp;nbsp;span在职em--/em/spanspan入职em classactive--/em/spanspan离职em classdisabled--/em/span/spandiv classfrrouter-link :to{path:/employees/import/,query: {name: 员工}} classel-button el-button--primary el-button--mini title导入导入/router-linkel-button typeprimary sizemini title设置设置/el-buttonrouter-link :to{path:/employees/archiving/} classel-button el-button--primary el-button--mini title历史归档历史归档/router-linkrouter-link :to{path:/employees/report/1} classel-button el-button--primary el-button--mini 1月份报表/router-linkel-button typeprimary sizemini iconel-icon-plus clickhandlAdd新增员工/el-button/div/el-cardel-card shadownever classboxMarel-table :datadataList fit stylewidth: 100%; borderel-table-column typeindex :index1 label序号 width150 /el-table-columnel-table-column sortable propusername label姓名 width150/el-table-columnel-table-column sortable propmobile label手机号 width150/el-table-columnel-table-column sortable propworkNumber label工号 width120/el-table-columnel-table-column sortable propformOfEmployment label聘用形势 width200/el-table-columnel-table-column sortable propdepartmentName label部门 width200/el-table-columnel-table-column sortable proptimeOfEntry label入职时间 width150/el-table-columnel-table-column sortable label状态 width120template slot-scopescopeel-switch v-modelscope.row.accountStatus active-color#13ce66inactive-color#ff4949changehandleStatus(scope.row)/el-switch/template/el-table-columnel-table-column fixedright label操作 aligncenter width220template slot-scopescoperouter-link :to{path:/employees/details/ scope.row.id} classel-button el-button--text el-button--small查看/router-linkel-button clickhandleRole(scope.row) typetext sizesmall分配角色/el-buttonel-button clickhandleDelete(scope.row) typetext sizesmall删除/el-button/template/el-table-column/el-table!-- 分页 --div classpaginationPageTool :paginationPagerequestParameters.page :paginationPagesizerequestParameters.size :totalcounts pageChangehandleCurrentChange pageSizeChangehandleSizeChange/PageTool/div!-- end --!-- 新增员工弹层 --component v-bind:isemployeesAdd refaddUser doQuerydoQuery/component!--分配角色组件 --/el-card/div/div
/template js构造数据 script
import constantApi from /api/constant/employees
import {list,remove} from /api/base/users
import PageTool from ./../../components/page/page-tool
import employeesAdd from ./../components/add
export default {name: employeesList,components: {PageTool,employeesAdd},data() {return {employeesAdd: employeesAdd,baseData: constantApi,dataList: [],counts: ,requestParameters:{page: 1,size: 10,} }},methods: {// 业务方法doQuery(params) {list(this.requestParameters).then(res {this.dataList res.data.data.rowsthis.counts res.data.data.total})},// 每页显示信息条数handleSizeChange(size) {this.requestParameters.size sizeif (this.requestParameters.page 1) {this.doQuery(this.requestParameters)}},// 进入某一页handleCurrentChange(val) {this.requestParameters.page valthis.doQuery()},// 添加新员工handlAdd() {this.$refs.addUser.dialogFormVisibletrue},// 删除handleDelete(item) {this.$confirm(本次操作将删除${item.username}删除后账号将不可恢复您确认删除吗,{type: warning}).then(() {remove({ id: item.id }).then(response {this.$message.success(删除成功 !)this.doQuery();})})}},// 创建完毕状态created: function() {this.doQuery()},
}
/scriptstyle relstylesheet/scss langscss scoped
.iconInfo {.fa {color: #999;font-size: 14px;cursor: pointer;}a {padding: 0 5px;}
}
.serachInput {.el-input--medium {width: 150px;.el-input__inner {}}
}
.serachInput .el-input--medium .el-input__inner {height: 26px;line-height: 26px;
}
/style 用户详情 配置路由 {path: details/:id,component: _import(employees/pages/employees-details),// hidden: true // 是否显示在左侧菜单name: details,meta: {title: 详情}} 完成用户详情页面 templatediv classdashboard-containerdiv classapp-containerel-card :style{minHeight:boxHeight}el-tabs v-modelactiveName classinfoPosinel-tab-pane namefirst classrInfospan slotlabel登录账户设置/spancomponent v-bind:isaccountInfo :objIdobjId refuser/component/el-tab-paneel-tab-pane nametwo classrInfospan slotlabel个人详情/span/el-tab-paneel-tab-pane namethird classrInfospan slotlabel岗位信息/span/el-tab-pane/el-tabs/el-card/div/div
/template
script
import accountInfo from ./../components/details-account-info
export default {name: employeesDetails,components: { accountInfo},data() {return {accountInfo:accountInfo,activeName: first,objId: this.$route.params.id,dataList: []}}
}
/script 用户信息组件 templatediv classboxInfo!-- 表单内容 --div classformInfodiv!-- 头部信息 --div classuserInfodiv classheadInfo clearfixdiv classheadTextel-form refformData :modelformData label-width215pxel-form-item label姓名el-input v-modelformData.username placeholder请输入
classinputW/el-input/el-form-itemel-form-item label密码el-input v-modelformData.password placeholder请输入
classinputW/el-input/el-form-itemel-form-item label部门el-inputplaceholder请选择v-modelformData.departmentNameiconcaret-bottomclassinputWclick.nativeisShowSelect !isShowSelect/el-inputinput v-modelformData.departmentId typehidden el-tree v-ifisShowSelect:expand-on-click-nodefalse:datainspectionObjectOptions:props{label:name}default-expand-all:filter-node-methodfilterNodenode-clickhandleNodeClickclassobjectTreereftree2/el-tree/el-form-itemel-form-itemel-button typeprimary clicksaveData更新/el-buttonrouter-link :to{path:/employees/index} classel-button
el-button--text el-button--small取消/router-link/el-form-item/el-form/div/div/div/div/div/div
/template
script
import constantApi from /api/constant/employees
import {detail,update} from /api/base/users
import { organList } from /api/base/departments
export default {name: accountInfo,props: [objId],data() {return {baseData: constantApi,inspectionObjectOptions: [],isShowSelect:false,formData: {id: this.objId,}}},methods: {handleNodeClick(data) {this.formData.departmentName data.namethis.formData.departmentId data.idthis.isShowSelect false},// 获取详情getObjInfo() {detail({ id: this.objId }).then(res {this.formData res.data.data})},saveData(obj) {update(this.formData).then(res {this.formData res.datathis.$message.success(保存成功)this.getObjInfo()})},},// 创建完毕状态created: function() {this.getObjInfo()organList().then(ret {this.inspectionObjectOptions.push(ret.data.data)})}
}
/script 用户的新增 和组织机构的增删改查大同小异 作业-角色管理 需求分析 完成角色的基本CRUD操作 后端实现 实体类 package com.ihrm.domain.system;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
Entity
Table(name pe_role)
Getter
Setter
public class Role implements Serializable {private static final long serialVersionUID 594829320797158219L;Idprivate String id;/*** 角色名*/private String name;/*** 说明*/private String description;/*** 企业id*/private String companyId;JsonIgnoreManyToMany(mappedByroles)private SetUser users new HashSetUser(0);//角色与用户 多对多JsonIgnoreManyToManyJoinTable(namepe_role_permission,joinColumns{JoinColumn(namerole_id,referencedColumnNameid)},inverseJoinColumns
{JoinColumn(namepermission_id,referencedColumnNameid)})private SetPermission permissions new HashSetPermission(0);//角色与模块 多对多
} 持久化层 package com.ihrm.system.dao;
import com.ihrm.system.domain.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/*** 企业数据访问接口*/
public interface RoleDao extends JpaRepositoryRole, String,
JpaSpecificationExecutorRole {
} 业务逻辑层 package com.ihrm.system.service;
import com.ihrm.common.utils.IdWorker;
import com.ihrm.system.dao.CompanyDao;
import com.ihrm.system.dao.RoleDao;
import com.ihrm.system.dao.UserDao;
import com.ihrm.system.domain.Company;
import com.ihrm.system.domain.Role;
import com.ihrm.system.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 角色操作业务逻辑层
*/
Service
public class RoleService {Autowiredprivate IdWorker idWorker;Autowiredprivate RoleDao roleDao;/*** 添加角色*/public void save(Role role) {//填充其他参数role.setId(idWorker.nextId() );roleDao.save(role);}/*** 更新角色*/public void update(Role role) {Role targer roleDao.getOne(role.getId());targer.setDescription(role.getDescription());targer.setName(role.getName());roleDao.save(targer);}/*** 根据ID查询角色*/public Role findById(String id) {return roleDao.findById(id).get();}/*** 删除角色*/public void delete(String id) {roleDao.deleteById(id);}public PageRole findSearch(String companyId, int page, int size) {SpecificationRole specification new SpecificationRole() {Overridepublic Predicate toPredicate(RootRole root, CriteriaQuery? query,
CriteriaBuilder cb) {return cb.equal(root.get(companyId).as(String.class),companyId);}};return roleDao.findAll(specification, PageRequest.of(page-1, size));}
} 控制器层 package com.ihrm.system.controller;
import com.ihrm.common.entity.PageResult;
import com.ihrm.common.entity.Result;
import com.ihrm.common.entity.ResultCode;
import com.ihrm.system.domain.Role;
import com.ihrm.system.domain.User;
import com.ihrm.system.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.*;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.HashMap;
import java.util.Map;
RestController
RequestMapping(/sys)
public class RoleController {Autowiredprivate RoleService roleService;//添加角色RequestMapping(value /role, method RequestMethod.POST)public Result add(RequestBody Role role) throws Exception {String companyId 1;role.setCompanyId(companyId);roleService.save(role);return Result.SUCCESS();}//更新角色RequestMapping(value /role/{id}, method RequestMethod.PUT)public Result update(PathVariable(name id) String id, RequestBody Role role)
throws Exception {roleService.update(role);return Result.SUCCESS();}//删除角色RequestMapping(value /role/{id}, method RequestMethod.DELETE)public Result delete(PathVariable(name id) String id) throws Exception {roleService.delete(id);return Result.SUCCESS();}/*** 根据ID获取角色信息*/RequestMapping(value /role/{id}, method RequestMethod.GET)public Result findById(PathVariable(name id) String id) throws Exception {Role role roleService.findById(id);return new Result(ResultCode.SUCCESS,role);}/*** 分页查询角色*/RequestMapping(value /role, method RequestMethod.GET)public Result findByPage(int page,int pagesize,Role role) throws Exception {String companyId 1;PageRole searchPage roleService.findSearch(companyId, page, pagesize);PageResultRole pr new
PageResult(searchPage.getTotalElements(),searchPage.getContent());return new Result(ResultCode.SUCCESS,pr);}
} 前端实现 参考资料自行实现