vhr
vue-cli3构建vue项目
vue.js官网,安装,命令行工具,vue-cli文档,npm安装
npm install -g @vue/cli
vue –version
vue create vuehr
default
cd vuehr
npm run serve
项目结构
main.js->App.vue->router.js->login.vue
package.json工具包
安装element插件
npm i element-ui -S
引入
import ElementUI from 'element-ui'; Vue.use(ElementUI);
微人事登录页面制作 处理前端登录事件
login.vue
<template> <div> <el-form :rules="rules" ref="loginForm" v-loading="loading" element-loading-text="正在登录..." element-loading-spinner="el-icon-loading" element-loading-background="rgba(0, 0, 0, 0.8)" :model="loginForm" class="loginContainer"> <h3 class="LoginTitle">系统登录
h3> <el-form-item prop="username"> <el-input type="text" v-model="loginForm.username" auto-complete="off" placeholder="请输入用户名">
el-input>
el-form-item> <el-form-item prop="password"> <el-input size="normal" type="text" v-model="loginForm.password" auto-complete="off" placeholder="请输入密码" @keydown.enter.native="submitLogin">
el-input>
el-form-item> <el-checkbox size="normal" class="LoginRemember" v-model="checked">
el-checkbox> <el-button size="normal" type="primary" style="width:100%;" @click="submitLogin">登录
el-button>
el-form>
div>
template> <script> export default {
name: "Login", data(){
return{
loading:false, checked:true, loginForm:{
username:'admin', password: '123' }, rules:{
username:[{
required:true,message:'请输入用户名',trigger:'blur'}], password:[{
required:true,message:'请输入密码',trigger:'blur'}] } } }, methods:{
submitLogin() {
this.$refs.loginForm.validate((valid) => {
if (valid) {
this.loading = true; // resp服务端返回的json this.postKeyValueRequest('/doLogin',this.loginForm).then(resp=> {
this.loading=false; if(resp){
//用户登陆成功后的数据将被保存再sessionStorage中,防止用户刷新后数据丢失, // 以字符串形式存入,取的时候转为json window.sessionStorage.setItem("user",JSON.stringify(resp)); //跳转后能否到重定向页面中 let path=this.$route.query.redirect; // 登录成功后进行页面跳转 this.$router.replace((path==='/'||path===undefined)?'/home':path) } }) } else {
this.$message.error("请输入所有字段"); return false; } }); } } }
script> <style> .loginContainer{
border-radius:15px; background-clip: padding-box; margin: 180px auto; width:350px; padding:15px 35px 15px 35px; background: #f6f6f6; border:1px solid #eaeaea; box-shadow: 0 0 25px #cac6c6; } .LoginTitle{
margin: 15px auto 20px auto; text-align: center; } .LoginRemember{
text-align: left; margin:0px 0px 15px 0; }
style>
home页页面跳转
<div> home页
div>
服务端环境搭建
创建vhr数据库,导入表
pom.xml文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0
modelVersion> <parent> <groupId>org.springframework.boot
groupId> <artifactId>spring-boot-starter-parent
artifactId> <version>2.1.8.RELEASE
version> <relativePath/>
parent> <groupId>org.javagirl
groupId> <artifactId>vhr_project
artifactId> <version>0.0.1-SNAPSHOT
version> <name>vhr_project
name> <description>Demo project for Spring Boot
description> <properties> <java.version>1.8
java.version>
properties> <dependencies> <dependency> <groupId>org.springframework.boot
groupId> <artifactId>spring-boot-starter-security
artifactId> <version>2.3.9.RELEASE
version>
dependency> <dependency> <groupId>org.springframework.boot
groupId> <artifactId>spring-boot-starter-web
artifactId>
dependency> <dependency> <groupId>org.mybatis.spring.boot
groupId> <artifactId>mybatis-spring-boot-starter
artifactId> <version>2.1.4
version>
dependency> <dependency> <groupId>mysql
groupId> <artifactId>mysql-connector-java
artifactId> <version>8.0.23
version> <scope>runtime
scope>
dependency>
<dependency> <groupId>com.alibaba
groupId> <artifactId>druid-spring-boot-starter
artifactId> <version>1.1.10
version>
dependency> <dependency> <groupId>org.springframework.boot
groupId> <artifactId>spring-boot-starter-test
artifactId> <scope>test
scope>
dependency> <dependency> <groupId>org.springframework.security
groupId> <artifactId>spring-security-test
artifactId> <scope>test
scope>
dependency> <dependency> <groupId>org.springframework.security
groupId> <artifactId>spring-security-web
artifactId>
dependency> <dependency> <groupId>org.junit.jupiter
groupId> <artifactId>junit-jupiter
artifactId> <version>RELEASE
version> <scope>test
scope>
dependency>
dependencies> <build>
<resources> <resource> <directory>src/main/resources
directory>
resource> <resource> <directory>src/main/java
directory> <includes> <include>/*.xml
include>
includes>
resource>
resources> <plugins>
<plugin> <groupId>org.apache.maven.plugins
groupId> <artifactId>maven-compiler-plugin
artifactId> <version>3.8.1
version> <configuration> <source>1.8
source> <target>1.8
target>
configuration>
plugin>
plugins>
build>
project>
使用逆向工程生成mapper和model文件
主运行文件配置mapperScan
@SpringBootApplication @MapperScan(basePackages = "org.javagirl.vhr_project.mapper") public class VhrProjectApplication {
public static void main(String[] args) {
SpringApplication.run(VhrProjectApplication.class, args); } }
配置application文件
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.username=root spring.datasource.password=root spring.datasource.url=jdbc:mysql://localhost:3307/vhr?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai server.port=8085
服务端登录接口制作
model中的hr
package org.javagirl.vhr_project.model; / * Hr类,实现UserDetails接口的方法 * 不涉及到账户的锁定、密码的过期等等,只有账户是否被禁用,因此只处理了isEnabled方法 * * */ import com.fasterxml.jackson.annotation.JsonIgnore; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class Hr implements UserDetails {
private Integer id; private String name; private String phone; private String telephone; private String address; private Boolean enabled; private String username; private String password; private String userface; public Boolean getEnabled() {
return enabled; } private String remark; public List<Role> getRoles() {
return roles; } public void setRoles(List<Role> roles) {
this.roles = roles; } private List<Role> roles; public Integer getId() {
return id; } public void setId(Integer id) {
this.id = id; } public String getName() {
return name; } public void setName(String name) {
this.name = name == null ? null : name.trim(); } public String getPhone() {
return phone; } public void setPhone(String phone) {
this.phone = phone == null ? null : phone.trim(); } public String getTelephone() {
return telephone; } public void setTelephone(String telephone) {
this.telephone = telephone == null ? null : telephone.trim(); } public String getAddress() {
return address; } public void setAddress(String address) {
this.address = address == null ? null : address.trim(); } public void setEnabled(Boolean enabled) {
this.enabled = enabled; } public String getUsername() {
return username; } // 账户是否过期 @Override public boolean isAccountNonExpired() {
return true; } // 账户是否被锁定 @Override public boolean isAccountNonLocked() {
return true; } // 密码是否过期 @Override public boolean isCredentialsNonExpired() {
return true; } // 账户是否允许被使用 @Override public boolean isEnabled() {
return enabled; } public void setUsername(String username) {
this.username = username == null ? null : username.trim(); } //获取当前用户所具有的角色 @Override //生成json时忽略该属性 @JsonIgnore public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>(roles.size()); //roles属性描述当前用户的角色 for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority(role.getName())); } return authorities; } public String getPassword() {
return password; } public void setPassword(String password) {
this.password = password == null ? null : password.trim(); } public String getUserface() {
return userface; } public void setUserface(String userface) {
this.userface = userface == null ? null : userface.trim(); } public String getRemark() {
return remark; } public void setRemark(String remark) {
this.remark = remark == null ? null : remark.trim(); } }
HrService
package org.javagirl.vhr_project.service; import org.javagirl.vhr_project.mapper.HrMapper; import org.javagirl.vhr_project.mapper.HrRoleMapper; import org.javagirl.vhr_project.model.Hr; import org.javagirl.vhr_project.utils.HrUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; / * HrService实现UserDetailsService接口 * 1.根据用户名查找用户 * 2.查找、更新、删除hr * 3.更新hr角色 */ @Service public class HrService implements UserDetailsService {
@Autowired HrMapper hrMapper; @Autowired HrRoleMapper hrRoleMapper; //在执行登陆的过程中,这个方法根据用户名取查找用户 //如果用户不存在,抛出异常,否则返回Hr @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Hr hr = hrMapper.loadUserByUsername(username); if(hr == null){
throw new UsernameNotFoundException("用户名不存在"); } hr.setRoles(hrMapper.getHrRolesById(hr.getId())); return hr; } / * 查找所有Hr * @param keywords * @return List */ public List<Hr> getAllHrs(String keywords) {
return hrMapper.getAllHrs(HrUtils.getCurrentHr().getId(),keywords); } / * 更新hr * @param hr * @return hr.id */ public Integer updateHr(Hr hr) {
return hrMapper.updateByPrimaryKeySelective(hr); } / * 更新hr的角色 * @param hrid * @param rids * @return boolean */ @Transactional public boolean updateHrRole(Integer hrid, Integer[] rids) {
//先删除hr的角色 hrRoleMapper.deleteByHrid(hrid); //再给hr添加角色 return hrRoleMapper.addRole(hrid,rids)==rids.length; } / * 删除hr * @param id * @return int */ public Integer deleteHrById(Integer id) {
return hrMapper.deleteByPrimaryKey(id); } }
HrMapper接口
package org.javagirl.vhr_project.mapper; import org.apache.ibatis.annotations.Param; import org.javagirl.vhr_project.model.Hr; import org.javagirl.vhr_project.model.Role; import java.util.List; public interface HrMapper {
int deleteByPrimaryKey(Integer id); int insert(Hr record); int insertSelective(Hr record); Hr selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(Hr record); int updateByPrimaryKey(Hr record); Hr loadUserByUsername(String username); List<Role> getHrRolesById(Integer id); List<Hr> getAllHrs(@Param("hrid") Integer hrid,String keywords); }
HrMapper.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="org.javagirl.vhr_project.mapper.HrMapper" > <resultMap id="BaseResultMap" type="org.javagirl.vhr_project.model.Hr" > <id column="id" property="id" jdbcType="INTEGER" /> <result column="name" property="name" jdbcType="VARCHAR" /> <result column="phone" property="phone" jdbcType="CHAR" /> <result column="telephone" property="telephone" jdbcType="VARCHAR" /> <result column="address" property="address" jdbcType="VARCHAR" /> <result column="enabled" property="enabled" jdbcType="BIT" /> <result column="username" property="username" jdbcType="VARCHAR" /> <result column="password" property="password" jdbcType="VARCHAR" /> <result column="userface" property="userface" jdbcType="VARCHAR" /> <result column="remark" property="remark" jdbcType="VARCHAR" />
resultMap> <resultMap id="HrWithRoles" type="org.javagirl.vhr_project.model.Hr" extends="BaseResultMap"> <collection property="roles" ofType="org.javagirl.vhr_project.model.Role"> <id column="rid" property="id"/> <result column="rname" property="name"/> <result column="rnameZh" property="nameZh">
result>
collection>
resultMap> <sql id="Base_Column_List" > id, name, phone, telephone, address, enabled, username, password, userface, remark
sql> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" > select <include refid="Base_Column_List" /> from hr where id = #{id,jdbcType=INTEGER}
select> <select id="loadUserByUsername" resultMap="BaseResultMap" > select * from hr where username=#{username}
select> <select id="getHrRolesById" resultType="org.javagirl.vhr_project.model.Role" parameterType="java.lang.Integer"> SELECT r.* FROM role r,hr_role hrr WHERE hrr.`rid`=r.`id` AND hrr.`hrid`=#{id}
select> <select id="getAllHrs" resultMap="HrWithRoles"> SELECT hr.id, hr.name, hr.phone, hr.telephone, hr.address, hr.enabled, hr.username, hr.userface, hr.remark,r.`id` AS rid,r.name AS rname,r.`nameZh` AS rnameZh FROM hr LEFT JOIN hr_role hrr ON hr.`id`=hrr.hrid LEFT JOIN role r ON hrr.`rid`=r.`id` WHERE hr.id!=#{hrid} <if test="keywords!=null">and hr.name like concat('%',#{keywords},'%')
if> order by hr.id
select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" > delete from hr where id = #{id,jdbcType=INTEGER}
delete> <insert id="insert" parameterType="org.javagirl.vhr_project.model.Hr" > insert into hr (id, name, phone, telephone, address, enabled, username, password, userface, remark) values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{phone,jdbcType=CHAR}, #{telephone,jdbcType=VARCHAR}, #{address,jdbcType=VARCHAR}, #{enabled,jdbcType=BIT}, #{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{userface,jdbcType=VARCHAR}, #{remark,jdbcType=VARCHAR})
insert> <insert id="insertSelective" parameterType="org.javagirl.vhr_project.model.Hr" > insert into hr <trim prefix="(" suffix=")" suffixOverrides="," > <if test="id != null" > id,
if> <if test="name != null" > name,
if> <if test="phone != null" > phone,
if> <if test="telephone != null" > telephone,
if> <if test="address != null" > address,
if> <if test="enabled != null" > enabled,
if> <if test="username != null" > username,
if> <if test="password != null" > password,
if> <if test="userface != null" > userface,
if> <if test="remark != null" > remark,
if>
trim> <trim prefix="values (" suffix=")" suffixOverrides="," > <if test="id != null" > #{id,jdbcType=INTEGER},
if> <if test="name != null" > #{name,jdbcType=VARCHAR},
if> <if test="phone != null" > #{phone,jdbcType=CHAR},
if> <if test="telephone != null" > #{telephone,jdbcType=VARCHAR},
if> <if test="address != null" > #{address,jdbcType=VARCHAR},
if> <if test="enabled != null" > #{enabled,jdbcType=BIT},
if> <if test="username != null" > #{username,jdbcType=VARCHAR},
if> <if test="password != null" > #{password,jdbcType=VARCHAR},
if> <if test="userface != null" > #{userface,jdbcType=VARCHAR},
if> <if test="remark != null" > #{remark,jdbcType=VARCHAR},
if>
trim>
insert> <update id="updateByPrimaryKeySelective" parameterType="org.javagirl.vhr_project.model.Hr" > update hr <set > <if test="name != null" > name = #{name,jdbcType=VARCHAR},
if> <if test="phone != null" > phone = #{phone,jdbcType=CHAR},
if> <if test="telephone != null" > telephone = #{telephone,jdbcType=VARCHAR},
if> <if test="address != null" > address = #{address,jdbcType=VARCHAR},
if> <if test="enabled != null" > enabled = #{enabled,jdbcType=BIT},
if> <if test="username != null" > username = #{username,jdbcType=VARCHAR},
if> <if test="password != null" > password = #{password,jdbcType=VARCHAR},
if> <if test="userface != null" > userface = #{userface,jdbcType=VARCHAR},
if> <if test="remark != null" > remark = #{remark,jdbcType=VARCHAR},
if>
set> where id = #{id,jdbcType=INTEGER}
update> <update id="updateByPrimaryKey" parameterType="org.javagirl.vhr_project.model.Hr" > update hr set name = #{name,jdbcType=VARCHAR}, phone = #{phone,jdbcType=CHAR}, telephone = #{telephone,jdbcType=VARCHAR}, address = #{address,jdbcType=VARCHAR}, enabled = #{enabled,jdbcType=BIT}, username = #{username,jdbcType=VARCHAR}, password = #{password,jdbcType=VARCHAR}, userface = #{userface,jdbcType=VARCHAR}, remark = #{remark,jdbcType=VARCHAR} where id = #{id,jdbcType=INTEGER}
update>
mapper>
securityConfig
respBean
package org.javagirl.vhr_project.model; / * 返回数据封装 */ public class RespBean {
private RespBean(Integer status, String msg, Object obj) {
this.status = status; this.msg = msg; this.obj = obj; } private RespBean() {
} public Integer getStatus() {
return status; } public void setStatus(Integer status) {
this.status = status; } public String getMsg() {
return msg; } public void setMsg(String msg) {
this.msg = msg; } public Object getObj() {
return obj; } public void setObj(Object obj) {
this.obj = obj; } private Integer status; private String msg; private Object obj; // 返回json格式文件 public static RespBean ok(String msg){
return new RespBean(200,msg,null); } public static RespBean ok(String msg,Object obj){
return new RespBean(200,msg,obj); } public static RespBean error(String msg){
return new RespBean(500,msg,null); } public static RespBean error(String msg,Object obj){
return new RespBean(500,msg,obj); } }
ctrl+H查看继承类
package org.javagirl.vhr_project.config; import com.fasterxml.jackson.databind.ObjectMapper; import org.javagirl.vhr_project.model.Hr; import org.javagirl.vhr_project.model.RespBean; import org.javagirl.vhr_project.service.HrService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.*; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; / * 对登录页面进行处理 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired HrService hrService; @Autowired CustomFilterInvocationSecurityMetadataSource customFilterInvocationSecurityMetadataSource; @Autowired CustomUrlDecisionManager customUrlDecisionManager; @Bean PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder(); } //加载hrservice安全管理 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(hrService); } // 对登录页面进行放权 @Override public void configure(WebSecurity web) throws Exception {
// web.ignoring().antMatchers("/login","/css/","/js/","/index.html","/img/","/fonts/","favicon.ico"); web.ignoring().antMatchers("/login"); } //通过withObjectPostProcessor将刚刚创建的CustomFilterInvocationSecurityMetadataSource和CustomUrlDecisionManager注入。请求都会经过刚才的过滤器(除了configure(WebSecurity web)方法忽略的请求) @Override protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() // .anyRequest().authenticated() .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override public <O extends FilterSecurityInterceptor> O postProcess(O object) {
object.setAccessDecisionManager(customUrlDecisionManager); object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource); return object; } }) .and() .formLogin() .usernameParameter("username") .passwordParameter("password") .loginProcessingUrl("/doLogin") //重新配置登录页 .loginPage("/login") // 登录成功,配置登录成功时返回的JSON,登录成功时返回当前用户的信息 .successHandler(new AuthenticationSuccessHandler() {
//登录成功的回调 @Override public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8"); PrintWriter out=resp.getWriter(); Hr hr=(Hr)authentication.getPrincipal(); hr.setPassword(null); RespBean ok = RespBean.ok("登陆成功", hr); String s = new ObjectMapper().writeValueAsString(hr); out.write(s); out.flush(); out.close(); } }) // 登录失败,表示登录失败,根据不同的异常输出不同的错误提示 .failureHandler(new AuthenticationFailureHandler() {
@Override public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException exception) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8"); PrintWriter out=resp.getWriter(); RespBean respBean = RespBean.error("登陆失败"); // 查看异常的父类 ctrl H if(exception instanceof LockedException){
respBean.setMsg("账户被锁定"); }else if(exception instanceof CredentialsExpiredException){
respBean.setMsg("密码过期"); }else if(exception instanceof AccountExpiredException){
respBean.setMsg("账户过期"); }else if(exception instanceof DisabledException){
respBean.setMsg("账户被禁用"); }else if(exception instanceof BadCredentialsException){
respBean.setMsg("用户名或者密码输入错误,请重新输入"); } out.write(new ObjectMapper().writeValueAsString(respBean)); out.flush(); out.close(); } }) .permitAll() .and() .logout() // 注销登录 .logoutSuccessHandler(new LogoutSuccessHandler() {
@Override public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8"); PrintWriter out = resp.getWriter(); out.write(new ObjectMapper().writeValueAsString(RespBean.ok("注销成功"))); out.flush(); out.close(); } }) .permitAll() .and() //开启测试 .csrf().disable().exceptionHandling() // 没有认证时,在这里处理结果,不要重定向 .authenticationEntryPoint(new AuthenticationEntryPoint() {
@Override public void commence(HttpServletRequest req, HttpServletResponse resp, AuthenticationException authException) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8"); resp.setStatus(401); PrintWriter out=resp.getWriter(); RespBean respBean = RespBean.error("访问失败"); // 查看异常的父类 ctrl H if(authException instanceof InsufficientAuthenticationException){
respBean.setMsg("请求失败,请联系管理员"); } out.write(new ObjectMapper().writeValueAsString(respBean)); out.flush(); out.close(); } }); } }
package org.javagirl.vhr_project.controller; import org.javagirl.vhr_project.model.RespBean; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; / * 配置登录页面 */ @RestController public class LoginController {
@GetMapping("/login") public RespBean login(){
return RespBean.error("尚未登陆,请登陆"); } }
/doLogin为登录接口,自己定义一个helloController接口进行测试
前端请求方法封装
npm install axios
api.js
import axios from 'axios' import {
Message} from "element-ui"; import router from '../router' / * 响应拦截器 * 采用axios处理网络请求,避免在每次请求时判断各种网络情况(连接超时,服务器内部错误,权限不足等),对axios进行简单的封装,使用axios的拦截器功能 */ axios.interceptors.response.use(success=>{
// 展示错误信息 if(success.status && success.status===200 &&success.data.status===500){
//业务错误 Message.error({
message:success.data.msg}) //返回空 return; } //成功,自动提示服务端message if(success.data.msg){
Message.success({
message:success.data.msg}) } //返回到请求调用的地方 return success.data; },error=>{
if(error.response.status===504||error.response.status===404){
Message.error({
message:'服务器被吃了------'}) }else if(error.response.status===403){
Message.error({
message:'权限不足,请联系管理员'}) }else if(error.response.status===401){
Message.error({
message:'尚未登陆,请登陆'}) router.replace('/') }else{
if(error.response.data.msg){
// 服务端有错误消息 Message.error({
message:error.response.data.msg}) }else{
//服务端没有返回错误信息 Message.error({
message:'未知错误'}) } } return; }) let base=''; //登录请求默认key value形式传递参数,不支持json数据格式传参 export const postKeyValueRequest=(url,params)=>{
return axios({
method:'post', // 不是单引号,表示变量 url:`${
base}${
url}`, data:params, transformRequest:[function (data){
let ret=''; for(let i in data){
ret+=encodeURIComponent(i)+'='+encodeURIComponent(data[i])+'&' } return ret; }], headers:{
'Content-Type':'application/x-www-form-urlencoded' } }); } //封装请求方法,post put get delete,json形式传递数据 // 将其做成插件,不然每次使用都要加载 main.js export const postRequest=(url,params)=>{
return axios({
method:'post', url:`${
base}${
url}`, data:params }) } export const putRequest=(url,params)=>{
return axios({
method:'put', url:`${
base}${
url}`, data:params }) } export const getRequest=(url,params)=>{
return axios({
method:'get', url:`${
base}${
url}`, data:params }) } export const deleteRequest=(url,params)=>{
return axios({
method:'delete', url:`${
base}${
url}`, data:params }) }
main.js中导入方法
/ * 将请求方法挂到vue上,后面需要发送网络请求时,不需要导入api */ //1.导入所有的请求方法 import {
postRequest} from "@/utils/api"; import {
postKeyValueRequest} from "@/utils/api"; import {
putRequest} from "@/utils/api"; import {
deleteRequest} from "@/utils/api"; import {
getRequest} from "@/utils/api"; import {
initMenu} from "@/utils/menu"; import 'font-awesome/css/font-awesome.min.css' //2.将请求方法添加到vue.prototype上 Vue.prototype.postRequest = postRequest; Vue.prototype.postKeyValueRequest = postKeyValueRequest; Vue.prototype.putRequest = putRequest; Vue.prototype.deleteRequest = deleteRequest; Vue.prototype.getRequest = getRequest; Vue.config.productionTip=false
请求转发代理vue.config.js
//代理对象 let proxyObj = {
}; //拦截http请求 proxyObj['/']={
//关闭websocket ws:false, //目标拦截地址 target:'http://localhost:8085', //属性 changeOrigin:true, //请求地址重写 pathRewrite:{
'^/':'' } } module.exports={
//开发环境 devServer:{
host:'localhost', //服务本身信息 port:8081, //代理就是上面的代理对象 proxy:proxyObj } }
Home页title制作 左边导航菜单制作
container容器
NavMenu导航菜单
Home.vue
<template> <div> <el-container> <el-header class="homeHeader"> <div class="title"> 人事管理系统
div>
<el-dropdown class="userInfo" @command="commandHandler"> <span class="el-dropdown-link">
{
{user.name}}<i><img :src="user.userface" alt="">
i>
span> <el-dropdown-menu slot="dropdown">
<el-dropdown-item command="userinfo">个人中心
el-dropdown-item> <el-dropdown-item command="setting">设置
el-dropdown-item> <el-dropdown-item command="logout" divided>注销登录
el-dropdown-item>
el-dropdown-menu>
el-dropdown>
el-header>
<el-container> <el-aside width="200px">
<el-menu router unique-opened>
<el-submenu :index="index+''" v-for="(item,index) in routes" : key="index" > <template slot="title" v-if="!item.hidden"> <i :class="item.iconCls" style="color: #409eff;margin-right: 5px">
i> <span>{
{ item.name }}
span>
template>
<el-menu-item :index="child.path" v-for="(child,indexj) in item.children" :key="indexj">{
{child.name}}
el-menu-item>
el-submenu>
el-menu>
el-aside> <el-container> <el-main> <el-breadcrumb separator-class="el-icon-arrow-right" v-if="this.$router.currentRoute.path!='/home'"> <el-breadcrumb-item :to="{ path: '/home' }">首页
el-breadcrumb-item> <el-breadcrumb-item >{
{ this.$router.currentRoute.name }}
el-breadcrumb-item>
el-breadcrumb> <div class="homeWelcome" v-if="this.$router.currentRoute.path=='/home'"> 欢迎来到人事管理系统
div> <router-view class="homeRouterView"/>
el-main>
el-container>
el-container>
el-container>
div>
template> <script> // import {initMenu} from "@/utils/menu"; export default {
name: "Home", data(){
return{
//从store中获取菜单json,渲染成菜单 user:JSON.parse(window.sessionStorage.getItem("user")) } }, computed:{
routes(){
return this.$store.state.routes; } }, methods:{
// menuClick(index){
// this.$router.push(index); // }, commandHandler(cmd){
// messagebox消息弹框 if(cmd === 'logout'){
this.$confirm('此操作将注销登陆, 是否继续?', '提示', {
confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
// 注销登录 this.getRequest("/logout"); //登录数据清空 window.sessionStorage.removeItem("user" ) //跳转到登录页面 this.$store.commit('initRoutes',[]) this.$router.replace("/") }).catch(() => {
this.$message({
type: 'info', message: '已取消操作' }); }); } } } }
script> <style> .homeRouterView{
margin-top: 10px; } .homeWelcome{
text-align: center; font-size: 30px; font-family: 黑体, cursive; color: #409eff; padding-top: 50px; } .homeHeader{
background-color: #409eff; /*flex布局*/ display: flex; align-items: center; /*水平方向往两边走,空白的地方在中间*/ justify-content: space-between; /*防止两边离得太近*/ padding: 0px 15px; /*padding在框的里面*/ box-sizing: border-box; } .homeHeader .title{
font-size: 25px; font-family: 黑体,cursive; color: #fcfcfc; } .homeHeader .userInfo{
cursor: pointer; } .el-dropdown-link img{
width: 48px; height: 48px; border-radius: 24px; margin-left: 8px; } .el-dropdown-link{
display: flex; align-items: center; }
style>
动态加载菜单,从数据库中加载菜单。后端接口
@RestController @RequestMapping("/system/config") public class SystemConfigController {
@Autowired MenuService menuService; @GetMapping("/menu") public List<Menu> getMenusByHrId(){
return menuService.getMenusByHrId(); } }
id应该通过后端查询,前端传进来的数据时不可信的,可能会传到别人的菜单
封装一个menuService进行查询
package org.javagirl.vhr_project.service; import org.javagirl.vhr_project.mapper.MenuMapper; import org.javagirl.vhr_project.mapper.MenuRoleMapper; import org.javagirl.vhr_project.model.Hr; import org.javagirl.vhr_project.model.Menu; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.GetMapping; import java.util.List; @Service public class MenuService {
@Autowired MenuMapper menuMapper; @Autowired MenuRoleMapper menuRoleMapper; //获取id public List<Menu> getMenusByHrId() {
return menuMapper.getMenusByHrId(((Hr)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getId()); } // @Cacheable 缓存,防止每次请求都查数据库 public List<Menu> getAllMenusWithRole(){
return menuMapper.getAllMenusWithRole(); } public List<Menu> getAllMenus() {
return menuMapper.getAllMenus(); } public List<Integer> getMidsByRid(Integer rid) {
return menuMapper.getMidsByRid(rid); } @Transactional public boolean updateMenuRole(Integer rid, Integer[] mids) {
menuRoleMapper.deleteByRid(rid); Integer result = menuRoleMapper.insertRecord(rid,mids); return result==mids.length; } }
<select id="getMenusByHrId" resultMap="Menus2"> SELECT DISTINCT m1.*,m2.`id` AS id2,m2.`component` AS component2,m2.`enabled` AS enabled2,m2.`iconCls` AS iconCls2,m2.`keepAlive` AS keepAlive2,m2.`name` AS name2,m2.`parentId` AS parentId2,m2.`path` AS path2,m2.`requireAuth` AS requireAuth2 FROM menu m1,menu m2,hr_role hrr,menu_role mr WHERE m1.`id`=m2.`parentId`AND hrr.`hrid`=#{hrid} AND hrr.`rid`=mr.`rid`AND mr.`mid`=m2.`id` AND m2.`enabled`=TRUE ORDER BY m1.`id`,m2.`id`; </select>
<resultMap id="Menus2" type="org.javagirl.vhr_project.model.Menu" extends="BaseResultMap"> <collection property="children" ofType="org.javagirl.vhr_project.model.Menu"> <id column="id2" property="id" jdbcType="INTEGER" /> <result column="path2" property="path" jdbcType="VARCHAR" /> <result column="component2" property="component" jdbcType="VARCHAR" /> <result column="name2" property="name" jdbcType="VARCHAR" /> <result column="iconCls2" property="iconCls" jdbcType="VARCHAR" /> <result column="parentId2" property="parentId" jdbcType="INTEGER" /> <result column="enabled2" property="enabled" jdbcType="BIT" /> <association property="meta" javaType="org.javagirl.vhr_project.model.Meta"> <result column="keepAlive2" property="keepAlive" jdbcType="BIT" /> <result column="requireAuth2" property="requireAuth" jdbcType="BIT" /> </association> </collection> </resultMap>
现在客户端中运行sql成功后再复制到对应的位置,以免出错
根据hr表获取hrid,hr_role表通过hrid获取rid,menu_role中通过rid获取mid,menu表中通过mid获取用户可以操作的menu
得到每个页面的父页面
菜单项接口介绍
菜单项数据加载成功之后,在前端有几个可以存放的地方:
- sessionStorage 不是很安全
- localStorage 不是很安全
- vuex 菜单数据在多个文件引用,放在menu.js。确保数据安全,所有都能访问
vuex 状态管理,数据调用,数据共享。
多个vue文件多次进行切换时使用keepAlive字段管理,但是可能会出现加载混乱的问题
vuex把数据放在公共的地方
vux是一个UI库
安装
npm install vuex
vue.js官网的状态管理中的内容
vuex是一个专为vue.js应用程序开发的状态管理模式,采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
index.js
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) / * 最开始的界面:index.js->main.js->App.vue->router.js * 当用户注销登陆时,将localStorage中的数据清除 */ export default new Vuex.Store({
state:{
routes:[] }, mutations:{
initRoutes(state,data){
state.routes = data; } }, actions:{
} })
在main.js中引入store文件
import Vue from 'vue' import ElementUI from 'element-ui'; import App from './App.vue' import router from './router' import store from './store' Vue.config.productionTip=false Vue.use(ElementUI,{
size:'small'}); new Vue({
router, store, render: h => h(App) }).$mount('#app')
menu.js
import {
getRequest} from "@/utils/api"; // 浏览器刷新后保证菜单栏还在,可以使用路由导航守卫 export const initMenu=(router,store)=>{
//判断store中的数据是否存在,如果存在,说明这次跳转是正常的跳转,而不是用户按F5或者直接在地址栏输入某个地址进入的。否则去加载菜单。 if(store.state.routes.length>0){
return; } getRequest("/system/config/menu").then(data=>{
if(data){
//通过formatRoutes方法将服务器返回的json转为router需要的格式,转component,因为服务端返回的component事一个字符串,router中需要的事一个组件,在firmatRoutes方法中动态的加载需要的组件。 let fmtRoutes = formatRoutes(data); //一方面将数据存入store中,另一方面利用路由中的addRoutes方法将它动态的添加到路由中 router.addRoutes(fmtRoutes); store.commit('initRoutes',fmtRoutes); } }) } export const formatRoutes=(routes)=>{
let fmRoutes=[]; routes.forEach(router=>{
//批量定义 let{
path, component, name, meta, iconCls, children }=router; if(children && children instanceof Array){
children=formatRoutes(children); } let fmRouter={
path:path, name:name, iconCls:iconCls, meta:meta, children:children, component(resolve) {
// if (component.startsWith("Home")) {
// require(['../views/' + component + '.vue'], resolve); // } else if (component == "EmpBasic") {
// require(['../views/emp/' + component + '.vue'], resolve); // } else if (component == "PerEmp") {
// require(['../views/per/' + component + '.vue'], resolve); // }else if (component == "SalSobCfg") {
// require(['../views/sal/' + component + '.vue'], resolve); // } else if (component == "SalSob") {
// require(['../views/sal/' + component + '.vue'], resolve); // } else if (component == "SysHr") {
// require(['../views/sys/' + component + '.vue'], resolve); // } else if (component == "SysBasic") {
// require(['../views/sys/' + component + '.vue'], resolve); // } if (component.startsWith("Home")) {
require(['../views/' + component + '.vue'], resolve); } else if (component.startsWith("Emp")) {
require(['../views/emp/' + component + '.vue'], resolve); } else if (component.startsWith("Per")) {
require(['../views/per/' + component + '.vue'], resolve); } else if (component.startsWith("Sal")) {
require(['../views/sal/' + component + '.vue'], resolve); } else if (component.startsWith("Sta")) {
require(['../views/sta/' + component + '.vue'], resolve); } else if (component.startsWith("Sys")) {
require(['../views/sys/' + component + '.vue'], resolve); } } } fmRoutes.push(fmRouter); }) return fmRoutes; }
前端页面添加并完善菜单请求
views中创建菜单文件.vue
页面刷新,或按f5后菜单还在。
路由导航守卫,全局前置守卫
main.js
import Vue from 'vue' import ElementUI from 'element-ui'; import App from './App.vue' import router from './router' import store from './store' / * 将请求方法挂到vue上,后面需要发送网络请求时,不需要导入api */ //1.导入所有的请求方法 import {
postRequest} from "@/utils/api"; import {
postKeyValueRequest} from "@/utils/api"; import {
putRequest} from "@/utils/api"; import {
deleteRequest} from "@/utils/api"; import {
getRequest} from "@/utils/api"; import {
initMenu} from "@/utils/menu"; import 'font-awesome/css/font-awesome.min.css' //2.将请求方法添加到vue.prototype上 Vue.prototype.postRequest = postRequest; Vue.prototype.postKeyValueRequest = postKeyValueRequest; Vue.prototype.putRequest = putRequest; Vue.prototype.deleteRequest = deleteRequest; Vue.prototype.getRequest = getRequest; Vue.config.productionTip=false Vue.use(ElementUI,{
size:'small'}); // 全局导航首页,页面跳转前监听 router.beforeEach((to, from, next) => {
//1. 如果要去的页面是登录页面,直接过 // 2. 如果不是登录页面,先从store中获取当前的登录状态。如果未登录,通过路由中的meta属性的requireAuth属性判断要去的页面是否需要登录,如果需要登录,调回登录页面,如果不需要登录,直接过。如果已经登陆了,先初始化菜单,再跳转 if(to.path === '/'){
next(); }else{
// 从sessionStorage中拿到用户登陆数据,判断用户是否登陆 if(window.sessionStorage.getItem("user")){
initMenu(router,store); next(); }else{
// 没有登录 next('/?redirect='+to.path); } } }) new Vue({
router, store, render: h => h(App) }).$mount('#app')
Home.vue
computed:{
routes(){
return this.$store.state.routes; } },
<el-aside width="200px">
<el-menu router unique-opened>
<el-submenu :index="index+''" v-for="(item,index) in routes" : key="index" > <template slot="title" v-if="!item.hidden"> <i :class="item.iconCls" style="color: #409eff;margin-right: 5px">
i> <span>{
{ item.name }}
span>
template>
<el-menu-item :index="child.path" v-for="(child,indexj) in item.children" :key="indexj">{
{child.name}}
el-menu-item>
el-submenu>
el-menu>
el-aside>
面包屑作为导航线
前后端分离权限管理
前端跳转页面是为了提高用户体验
真正的权限校验在后端做,SSM框架建议使用shiro,SpringBoot+微服务,建议使用Spring Security
后端接口权限设计
用户发起HTTP请求,后端查询匹配的路径,查看当前用户是否具有该权限
一级菜单不需要用户权限
- 根据用户发送的url地址提取需要的角色,
- 判断当前用户是否具有相应角色
1.这个类的作用,主要是根据用户传来的请求地址,分析出请求需要的角色
CustomFilterInvocationSecurityMetadataSource
package org.javagirl.vhr_project.config; / * 这个类的作用,主要是根据用户传来的请求地址,分析出请求需要的角色 * 获取该地址需要的用户角色 */ @Component public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource{
@Autowired MenuService menuService; AntPathMatcher antPathMatcher = new AntPathMatcher(); // 请求匹配,没匹配上的登录之后访问 @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
//获取请求地址 String requestUrl=((FilterInvocation) object).getRequestUrl(); //查询数据库中url pattern和role的对应关系 List<Menu> menus=menuService.getAllMenusWithRole(); for (Menu menu : menus) {
//提取当前的请求url,将请求和数据库查询出来的所有路径匹配规则进行匹配 if(antPathMatcher.match(menu.getUrl(),requestUrl)){
//获取该路径对应的角色 List<Role> roles = menu.getRoles(); String[] str=new String[roles.size()]; for (int i = 0; i < roles.size(); i++) {
str[i] = roles.get(i).getName(); } return SecurityConfig.createList(str); } } //ROLE_LOGIN标记,没匹配上的,都是登陆访问 return SecurityConfig.createList("ROLE_LOGIN"); } @Override public Collection<ConfigAttribute> getAllConfigAttributes() {
return null; } @Override public boolean supports(Class<?> aClass) {
return false; } }
MenuMapper
//开发中加缓存,使用redis或者@Cacheable List<Menu> getAllMenusWithRole();
<resultMap id="BaseResultMap" type="org.javagirl.vhr_project.model.Menu" > <id column="id" property="id" jdbcType="INTEGER" /> <result column="url" property="url" jdbcType="VARCHAR" /> <result column="path" property="path" jdbcType="VARCHAR" /> <result column="component" property="component" jdbcType="VARCHAR" /> <result column="name" property="name" jdbcType="VARCHAR" /> <result column="iconCls" property="iconCls" jdbcType="VARCHAR" /> <result column="parentId" property="parentId" jdbcType="INTEGER" /> <result column="enabled" property="enabled" jdbcType="BIT" /> <association property="meta" javaType="org.javagirl.vhr_project.model.Meta"> <result column="keepAlive" property="keepAlive" jdbcType="BIT" /> <result column="requireAuth" property="requireAuth" jdbcType="BIT" />
association>
resultMap> <resultMap id="MenuWithRole" type="org.javagirl.vhr_project.model.Menu" extends="BaseResultMap"> <collection property="roles" ofType="org.javagirl.vhr_project.model.Role"> <id column="rid" property="id"/> <result column="rname" property="name"/> <result column="rnameZh" property="nameZh"/>
collection>
resultMap> <select id="getAllMenusWithRole" resultMap="MenuWithRole"> SELECT m.*,r.`id` AS rid,r.`name` AS rname,r.`nameZh` AS rnameZh FROM menu m,menu_role mr,role r WHERE m.`id`=mr.`mid` AND mr.`rid`=r.`id` ORDER BY m.`id`
select>
- 判断当前哟用户是否具备角色
package org.javagirl.vhr_project.config; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Component; import java.util.Collection; / * 判断当前用户是否具有角色,请求是否通过 */ @Component public class CustomUrlDecisionManager implements AccessDecisionManager { @Override public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { for (ConfigAttribute configAttribute : configAttributes) { //当前请求需要的权限 String needRole = configAttribute.getAttribute(); //ROLE_LOGIN标记,没有权限 if("ROLE_LOGIN".equals(needRole)){ if(authentication instanceof AnonymousAuthenticationToken){ throw new AccessDeniedException("尚未登录,请登录"); }else{ return; } } //当前用户具有的权限 Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); //查看当前用户的角色列表中是否具备需要的权限 //假设当前用户具备多个橘色,当前请求需要多个角色。 //只要用户包含一个请求角色就算授权成功 for (GrantedAuthority authority : authorities) { if(authority.getAuthority().equals(needRole)){ return; } } } throw new AccessDeniedException("权限不足,请联系管理员"); } @Override public boolean supports(ConfigAttribute configAttribute) { return true; } @Override public boolean supports(Class<?> aClass) { return true; } }Hr类
private List<Role> roles; //获取当前用户所具有的角色 @Override //生成json时忽略该属性 @JsonIgnore public Collection<? extends GrantedAuthority> getAuthorities() { List<SimpleGrantedAuthority> authorities = new ArrayList<>(roles.size()); //roles属性描述当前用户的角色 for (Role role : roles) { authorities.add(new SimpleGrantedAuthority(role.getName())); } return authorities; }
HrService
//在执行登陆的过程中,这个方法根据用户名取查找用户 //如果用户不存在,抛出异常,否则返回Hr @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Hr hr = hrMapper.loadUserByUsername(username); if(hr == null){
throw new UsernameNotFoundException("用户名不存在"); } hr.setRoles(hrMapper.getHrRolesById(hr.getId())); return hr; }
<select id="getHrRolesById" resultType="org.javagirl.vhr_project.model.Role" parameterType="java.lang.Integer"> SELECT r.* FROM role r,hr_role hrr WHERE hrr.`rid`=r.`id` AND hrr.`hrid`=#{id}
select>
基础信息设计
<template> <el-tabs v-model="activeName" type="card"> <el-tab-pane label="部门管理" name="depmanager"><DepManager></DepManager></el-tab-pane> <el-tab-pane label="职位管理" name="posmanager"><PosManager></PosManager></el-tab-pane> <el-tab-pane label="职称管理" name="joblevelmanager"><JobLevelManager></JobLevelManager></el-tab-pane> <el-tab-pane label="权限组" name="permissmanager"><PermissManager></PermissManager></el-tab-pane> </el-tabs> </template> <script> import DepManager from "@/components/sys/basic/DepManager"; import PosManager from "@/components/sys/basic/PosManager"; import JobLevelManager from "@/components/sys/basic/JobLevelManager"; import PermissManager from "@/components/sys/basic/PermissManager"; export default {
name: "SysBasic", data(){
return{
activeName:'depmanager' } }, components:{
//key:value形式,key value相同时,可以省略key 'DepManager':DepManager, PosManager, JobLevelManager, PermissManager } } </script> <style scoped> </style>
职位管理前端页面
<template> <div> <div> <el-input size="small" class="addPosInput" placeholder="添加职位..." prefix-icon="el-icon-plus" @keydown.enter.native="addPosition" v-model="pos.name">
el-input> <el-button icon="el-icon-plus" size="small" type="primary" @click="addPosition">添加
el-button>
div> <div class="posManagerMain"> <el-table :data="positions" @selection-change="handleSelectionChange" size="small" border stripe style="width: 70%"> <el-table-column type="selection" width="55">
el-table-column> <el-table-column prop="id" label="编号" width="55">
el-table-column> <el-table-column prop="name" label="职位名称" width="180">
el-table-column> <el-table-column prop="createDate" width="120" label="创建时间">
el-table-column> <el-table-column prop="enabled" width="100" label="是否启用"> <template slot-scope="scope"> <el-tag v-if="scope.row.enabled" type="success">已启用
el-tag> <el-tag v-else type="danger">未启用
el-tag>
template>
el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <el-button size="mini" @click="showEditView(scope.$index, scope.row)">编辑
el-button> <el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">删除
el-button>
template>
el-table-column>
el-table> <el-button @click="deleteMany" type="danger" size="small" style="margin-top: 8px;" :disabled="multipleSelection.length===0">批量删除
el-button>
div> <el-dialog title="修改职位名称" :visible.sync="dialogVisible" width="30%" > <div> <div> <el-tag>职位名称
el-tag> <el-input class="updatePosInput" size="small" v-model="updatePos.name">
el-input>
div> <div> <el-switch v-model="updatePos.enabled" active-text="启用" inactive-text="禁用">
el-switch>
div>
div> <span slot="footer" class="dialog-footer"> <el-button size="small" @click="dialogVisible = false">取 消
el-button> <el-button size="small" type="primary" @click="doUpdate">确 定
el-button>
span>
el-dialog>
div>
template> <script> import {
deleteRequest} from "@/utils/api"; export default {
name: "PosManager", data(){
return {
pos:{
name:'' }, positions: [], updatePos:{
name:'', enabled:false }, multipleSelection:[], dialogVisible:false } }, // 组件初始化 mounted() {
this.initPositions(); }, methods:{
deleteMany(){
this.$confirm('此操作将永久删除【'+this.multipleSelection.length+'】条记录, 是否继续?', '提示', {
confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
let ids='?'; this.multipleSelection.forEach(item=>{
ids+='ids='+item.id+'&'; }) this.deleteRequest("/system/basic/pos/"+ids).then(resp=>{
if(resp){
this.initPositions(); } }) }).catch(() => {
this.$message({
type: 'info', message: '已取消删除' }); }); }, handleSelectionChange(val) {
this.multipleSelection = val; }, showEditView(index,data){
this.dialogVisible=true; //数据拷贝,防止修改取消时出错 Object.assign(this.updatePos,data); // this.updatePos=data; }, doUpdate(){
this.putRequest("/system/basic/pos/",this.updatePos).then(resp=>{
if(resp){
this.initPositions(); this.updatePos.name=''; this.dialogVisible=false; } }) }, // 删除职位 handleDelete(index,data){
this.$confirm('此操作将永久删除['+data.name+']职位, 是否继续?', '提示', {
confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
deleteRequest("/system/basic/pos/"+data.id).then(resp=> {
if (resp) {
this.initPositions(); } }) }).catch(() => {
this.$message({
type: 'info', message: '已取消删除' }); }); }, // 添加职位 addPosition(){
if(this.pos.name){
this.postRequest("/system/basic/pos/",this.pos).then(resp=>{
if(resp){
this.initPositions(); this.pos.name=''; } }) }else{
this.$message({
message: '职位名称不可以为空' }); } }, // 初始化数据 initPositions(){
this.getRequest("/system/basic/pos/").then(resp=>{
if(resp){
this.positions=resp; } }) } } }
script> <style> .updatePosInput{
width: 200px; margin-left: 8px; } .addPosInput{
width: 300px; margin-right: 8px } .posManagerMain{
margin-top: 10px; }
style>
职位管理后端接口
package org.javagirl.vhr_project.controller.system.basic; import org.javagirl.vhr_project.model.Position; import org.javagirl.vhr_project.model.RespBean; import org.javagirl.vhr_project.service.PositionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.annotation.RequestScope; import java.util.List; @RestController @RequestMapping("/system/basic/pos") public class PositionController {
@Autowired PositionService positionService; @GetMapping("/") public List<Position> getAllPositions() {
return positionService.getAllPositions(); } @PostMapping("/") public RespBean addPosition(@RequestBody Position position){
if(positionService.addPosition(position) == 1){
return RespBean.ok("添加成功"); } return RespBean.error("添加失败"); } @PutMapping("/") public RespBean updatePositions(@RequestBody Position position){
if(positionService.updatePositions(position)==1){
return RespBean.ok("更新成功"); } return RespBean.error("更新失败"); } @DeleteMapping("/{id}") public RespBean deletePosition(@PathVariable Integer id){
if(positionService.deletePositionById(id)==1){
return RespBean.ok("删除成功"); } return RespBean.error("删除失败"); } @DeleteMapping("/") public RespBean deletePositionByIds(Integer[] ids){
if(positionService.deletePositionByIds(ids)==ids.length){
return RespBean.ok("删除成功"); } return RespBean.ok("删除失败"); } }
Service
package org.javagirl.vhr_project.service; import org.javagirl.vhr_project.mapper.PositionMapper; import org.javagirl.vhr_project.model.Position; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Date; import java.util.List; @Service public class PositionService {
@Autowired PositionMapper positionMapper; public List<Position> getAllPositions() {
return positionMapper.getAllPositions(); } public Integer addPosition(Position position) {
position.setEnabled(true); position.setCreateDate(new Date()); return positionMapper.insertSelective(position); } public Integer updatePositions(Position position) {
return positionMapper.updateByPrimaryKeySelective(position); } public Integer deletePositionById(Integer id) {
return positionMapper.deleteByPrimaryKey(id); } public Integer deletePositionByIds(Integer[] ids) {
return positionMapper.deletePositionsByIds(ids); } }
mapper.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="org.javagirl.vhr_project.mapper.PositionMapper" > <resultMap id="BaseResultMap" type="org.javagirl.vhr_project.model.Position" > <id column="id" property="id" jdbcType="INTEGER" /> <result column="name" property="name" jdbcType="VARCHAR" /> <result column="createDate" property="createDate" jdbcType="TIMESTAMP" /> <result column="enabled" property="enabled" jdbcType="BIT" />
resultMap> <sql id="Base_Column_List" > id, name, createDate, enabled
sql> <select id="getAllPositions" resultMap="BaseResultMap"> select * from position
select> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" > select <include refid="Base_Column_List" /> from position where id = #{id,jdbcType=INTEGER}
select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" > delete from position where id = #{id,jdbcType=INTEGER}
delete> <delete id="deletePositionsByIds"> delete from position where id in <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id}
foreach>
delete> <insert id="insert" parameterType="org.javagirl.vhr_project.model.Position" > insert into position (id, name, createDate, enabled) values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{createDate,jdbcType=TIMESTAMP}, #{enabled,jdbcType=BIT})
insert> <insert id="insertSelective" parameterType="org.javagirl.vhr_project.model.Position" > insert into position <trim prefix="(" suffix=")" suffixOverrides="," > <if test="id != null" > id,
if> <if test="name != null" > name,
if> <if test="createDate != null" > createDate,
if> <if test="enabled != null" > enabled,
if>
trim> <trim prefix="values (" suffix=")" suffixOverrides="," > <if test="id != null" > #{id,jdbcType=INTEGER},
if> <if test="name != null" > #{name,jdbcType=VARCHAR},
if> <if test="createDate != null" > #{createDate,jdbcType=TIMESTAMP},
if> <if test="enabled != null" > #{enabled,jdbcType=BIT},
if>
trim>
insert> <update id="updateByPrimaryKeySelective" parameterType="org.javagirl.vhr_project.model.Position" > update position <set > <if test="name != null" > name = #{name,jdbcType=VARCHAR},
if> <if test="createDate != null" > createDate = #{createDate,jdbcType=TIMESTAMP},
if> <if test="enabled != null" > enabled = #{enabled,jdbcType=BIT},
if>
set> where id = #{id,jdbcType=INTEGER}
update> <update id="updateByPrimaryKey" parameterType="org.javagirl.vhr_project.model.Position" > update position set name = #{name,jdbcType=VARCHAR}, createDate = #{createDate,jdbcType=TIMESTAMP}, enabled = #{enabled,jdbcType=BIT} where id = #{id,jdbcType=INTEGER}
update>
mapper>
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/210855.html原文链接:https://javaforall.net
