SpringSecurity+vue通用权限系统2

发布于:2025-06-09 ⋅ 阅读:(19) ⋅ 点赞:(0)

SpringSecurity+vue通用权限系统2

用户管理实现

用户管理-列表分页显示实现
分页实体PageBean:

 package com.java.entity;

/**
 * 分页Model类

 */
public class PageBean {


    private int pageNum; // 第几页
    private int pageSize; // 每页记录数
    private int start;  // 起始页
    private String query; // 查询参数

    public PageBean() {
    }

    public PageBean(int pageNum, int pageSize, String query) {
        this.pageNum = pageNum;
        this.pageSize = pageSize;
        this.query = query;
    }

    public PageBean(int pageNum, int pageSize) {
        super();
        this.pageNum = pageNum;
        this.pageSize = pageSize;
    }

    public int getPageNum() {
        return pageNum;
    }

    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getStart() {
        return (pageNum-1)*pageSize;
    }

    public String getQuery() {
        return query;
    }

    public void setQuery(String query) {
        this.query = query;
    }
}

MybatisPlus开启分页功能:

 package com.java.config;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * MybatisPlus配置类

 */
@Configuration
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }
}

SysUserController


    /**
     * 根据条件分页查询用户信息
     * @param pageBean
     * @return
     */
    @PostMapping("/list")
    @PreAuthorize("hasAuthority('system:user:query')")
    public R list(@RequestBody PageBean pageBean){
        String query=pageBean.getQuery().trim();
        Page<SysUser> pageResult = sysUserService
                .page(new Page<>(pageBean.getPageNum(), pageBean.getPageSize()) );
        List<SysUser> userList = pageResult.getRecords();

        Map<String,Object> resultMap=new HashMap<>();
        resultMap.put("userList",userList);
        resultMap.put("total",pageResult.getTotal());
        return R.ok(resultMap);
    }

前端user/index.vue

<template>
  <div class="app-container">
    <el-table :data="tableData" stripe style="width: 100%">
      <el-table-column prop="username" label="用户名" width="180" />
    </el-table>
    <el-pagination
        v-model:currentPage="queryForm.pageNum"
        v-model:page-size="queryForm.pageSize"
        :page-sizes="[10, 20, 30, 40]"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
    />
  </div>
</template>
<script setup>
import requestUtil,{getServerUrl} from "@/util/request";
import { ref } from 'vue'
const tableData=ref([]);
const total=ref(0)
const queryForm=ref({
  query:'',
  pageNum:1,
  pageSize:10
})
const initUserList=async()=>{
  const res=await requestUtil.post("sys/user/list",queryForm.value);
  tableData.value=res.data.userList;
  total.value=res.data.total;
}
initUserList();
const handleSizeChange=(pageSize)=>{
  queryForm.value.pageNum=1;
  queryForm.value.pageSize=pageSize;
  initUserList()
}
const handleCurrentChange=(pageNum)=>{
  queryForm.value.pageNum=pageNum;
  initUserList()
}
</script>
<style lang="scss" scoped>
.header{
  padding-bottom: 16px;
  box-sizing: border-box;
}
.el-pagination{
  float: right;
  padding: 20px;
  box-sizing: border-box;
}
::v-deep th.el-table__cell{
  word-break: break-word;
  background-color: #f8f8f9 !important;
  color: #515a6e;
  height: 40px;
  font-size: 13px;
}
.el-tag--small {
  margin-left: 5px;
}
</style>

在这里插入图片描述
elementplus国际化中文
在这里插入图片描述

// 国际化中文
import zhCn from ‘element-plus/es/locale/lang/zh-cn’
app.use(ElementPlus, {
locale: zhCn,
})

用户管理-搜索功能实现

前端需要显示角色信息
SysUser实体加字段

/**
* 所有角色集合
*/
@TableField(exist = false)
private List<SysRole> sysRoleList;

 
    /**
     * 根据条件分页查询用户信息
     * @param pageBean
     * @return
     */
    @PostMapping("/list")
    @PreAuthorize("hasAuthority('system:user:query')")
    public R list(@RequestBody PageBean pageBean){
        String query=pageBean.getQuery().trim();
        Page<SysUser> pageResult = sysUserService.page(new Page<>(pageBean.getPageNum()
                        , pageBean.getPageSize())
                        ,new QueryWrapper<SysUser>().like(StringUtil.isNotEmpty(query)
                        ,"username",query));
        List<SysUser> userList = pageResult.getRecords();
        for(SysUser user:userList){
            List<SysRole> roleList = sysRoleService
                    .list(new QueryWrapper<SysRole>()
                            .inSql("id",
                                    "select role_id from sys_user_role where user_id=" + user.getId()));
            user.setSysRoleList(roleList);
        }
        Map<String,Object> resultMap=new HashMap<>();
        resultMap.put("userList",userList);
        resultMap.put("total",pageResult.getTotal());
        return R.ok(resultMap);
    }

搜索框

  <el-row :gutter="20" class="header">
      <el-col :span="7">
        <el-input placeholder="请输入用户名..." v-model="queryForm.query" clearable
        ></el-input>
      </el-col>
      <el-button type="primary" :icon="Search" @click="initUserList">搜索</el-button>
    </el-row>

import { Search ,Delete,DocumentAdd ,Edit, Tools, RefreshRight} from '@elementplus/icons-vue'

在这里插入图片描述

 <template>
  <div class="app-container">
    <el-row :gutter="20" class="header">
      <el-col :span="7">
        <el-input placeholder="请输入用户名..." v-model="queryForm.query" clearable
        ></el-input>
      </el-col>
      <el-button type="primary" :icon="Search" @click="initUserList">搜索</el-button>
    </el-row>
    <el-table :data="tableData" stripe style="width: 100%">
      <el-table-column type="selection" width="55" />
      <el-table-column prop="avatar" label="头像" width="80" align="center">
        <template v-slot="scope">
          <img :src="getServerUrl()+'image/userAvatar/'+scope.row.avatar"
               width="50" height="50"/>
        </template>
      </el-table-column>
      <el-table-column prop="username" label="用户名" width="100"
                       align="center"/>
      <el-table-column prop="roles" label="拥有角色" width="200" align="center">
        <template v-slot="scope">
          <el-tag size="small" type="warning" v-for="item in
scope.row.sysRoleList"> {{item.name}}</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="email" label="邮箱" width="200" align="center"/>
      <el-table-column prop="phonenumber" label="手机号" width="120"
                       align="center"/>
      <el-table-column prop="status" label="状态?" width="200" align="center"
      >
        <template v-slot="{row}" >
          <el-switch v-model="row.status" @change="statusChangeHandle(row)"
                     active-text="正常"inactive-text="禁用" active-value="0" inactive-value="1">
          </el-switch>
        </template>
      </el-table-column>
      <el-table-column prop="createTime" label="创建时间" width="200"
                       align="center"/>
      <el-table-column prop="loginDate" label="最后登录时间" width="200"
                       align="center"/>
      <el-table-column prop="remark" label="备注" />
      <el-table-column prop="action" label="操作" width="400" fixed="right"
                       align="center">
        <template v-slot="scope" >
          <el-button type="primary" :icon="Tools" >分配角色</el-button>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination
        v-model:currentPage="queryForm.pageNum"
        v-model:page-size="queryForm.pageSize"
        :page-sizes="[10, 20, 30, 40]"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
    />
  </div>
</template>

在这里插入图片描述

用户管理-添加修改功能实现

在这里插入图片描述
后端提供一个API
添加或者修改:

 /**
     * 添加或者修改
     * @param sysUser
     * @return
     */
    @PostMapping("/save")
    @PreAuthorize("hasAuthority('system:user:add')"+"||"+"hasAuthority('system:user:edit')")
    public R save(@RequestBody SysUser sysUser){
        if(sysUser.getId()==null || sysUser.getId()==-1){
            sysUser.setCreateTime(new Date());
            sysUser.setPassword(bCryptPasswordEncoder.encode(sysUser.getPassword()));
            sysUserService.save(sysUser);

        }else{
            sysUser.setUpdateTime(new Date());
            sysUserService.updateById(sysUser);
        }
        return R.ok();
    }

   

修改功能,需要根据id查询数据

 /**
     * 根据id查询
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @PreAuthorize("hasAuthority('system:user:query')")
    public R findById(@PathVariable(value = "id")Integer id){
        SysUser sysUser = sysUserService.getById(id);
        Map<String,Object> map=new HashMap<>();
        map.put("sysUser",sysUser);
        return R.ok(map);
    }

添加用户,需要验证用户名是否重复

  /**
     * 验证用户名
     * @param sysUser
     * @return
     */
    @PostMapping("/checkUserName")
    @PreAuthorize("hasAuthority('system:user:query')")
    public R checkUserName(@RequestBody SysUser sysUser){
        if(sysUserService.getByUsername(sysUser.getUsername())==null){
            return R.ok();
        }else{
            return R.error();
        }
    }

前端,新建一个 dialog.vue 作为用户添加和修改页面组件
在这里插入图片描述

<template>

  <el-dialog
    :visible="dialogVisible"

    :title="dialogTitle"
    width="30%"
    @close="handleClose"
  >
       <el-form
        ref="formRef"
        :model="form"
        :rules="rules"
        label-width="100px"
    >
      <el-form-item label="用户名" prop="username">
        <el-input v-model="form.username" :disabled="form.id==-1?false:'disabled'"  />
        <el-alert
            v-if="form.id==-1"
            title="默认初始密码:123456"
            :closable="false"
            style="line-height: 10px;"
            type="success" >
        </el-alert>
      </el-form-item>

      <el-form-item label="手机号" prop="phonenumber">
        <el-input v-model="form.phonenumber" />
      </el-form-item>

      <el-form-item label="邮箱" prop="email">
        <el-input v-model="form.email" />
      </el-form-item>

      <el-form-item label="状态" prop="status">
        <el-radio-group v-model="form.status">
          <el-radio :label="'0'">正常</el-radio>
          <el-radio :label="'1'">禁用</el-radio>
        </el-radio-group>
      </el-form-item>


      <el-form-item label="备注" prop="remark">
        <el-input v-model="form.remark" type="textarea" :rows="4"/>
      </el-form-item>


    </el-form>
    <template #footer>
      <span class="dialog-footer">
        <el-button type="primary" @click="handleConfirm">确认</el-button>
        <el-button @click="handleClose"
          >取消</el-button
        >
      </span>
    </template>
  </el-dialog>

</template>

<script setup>
import {defineEmits, defineProps,ref,watch } from "vue"
import requestUtil,{getServerUrl} from "@/util/request";
import { ElMessage } from 'element-plus'

const props=defineProps(
    {
      id:{
        type:Number,
        default:-1,
        required:true
      },
      dialogTitle:{
        type:String,
        default:'',
        required:true
      },
      dialogVisible:{
        type:Boolean,
        default:false,
        required:true
      }
    }
)

const form=ref({
  id:-1,
  username:"",
  password:"123456",
  status:"0",
  phonenumber:"",
  email:"",
  remark:""
})

const checkUsername = async (rule, value, callback) => {
  if(form.value.id==-1){
    const res=await requestUtil.post("sys/user/checkUserName",{username:form.value.username});
    if (res.data.code==500) {
      callback(new Error("用户名已存在!"));
    } else {
      callback();
    }
  }else{
    callback();
  }

}


const rules=ref({
  username:[
    { required: true, message: '请输入用户名'},
    { required: true, validator: checkUsername, trigger: "blur" }
  ],
  email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
  phonenumber: [{ required: true, message: "手机号码不能为空", trigger: "blur" }, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }],
})

const formRef=ref(null)

const initFormData=async(id)=>{
  const res=await requestUtil.get("sys/user/"+id);
  form.value=res.data.sysUser;
}

watch(
    ()=>props.dialogVisible,
    ()=>{
      let id=props.id;
      console.log("id="+id)
      if(id!=-1){
        initFormData(id);
      }else{
        form.value={
          id:-1,
          username:"",
          password:"123456",
          status:"0",
          phonenumber:"",
          email:"",
          remark:""
        }
      }
    }
)

const emits=defineEmits(['update:modelValue','initUserList'])

const handleClose=()=>{
  emits('update:modelValue',false)
}

const handleConfirm=()=>{
  formRef.value.validate(async(valid)=>{
    if(valid){
      let result=await requestUtil.post("sys/user/save",form.value);
      let data=result.data;
      if(data.code==200){
        ElMessage.success("执行成功!")
        formRef.value.resetFields();
        emits("initUserList")
        handleClose();
      }else{
        ElMessage.error(data.msg);
      }
    }else{
      console.log("fail")
    }
  })
}

</script>

<style lang="scss" scoped>

</style>

index.vue页面

 <el-button type="success" :icon="DocumentAdd" @click="handleDialogValue()">新增
</el-button>

在这里插入图片描述

<el-button v-if="scope.row.username!='java1234'" type="primary" :icon="Edit"
@click="handleDialogValue(scope.row.id)" />

在这里插入图片描述
添加和修改按钮

import Dialog from './components/dialog'

<Dialog v-model="dialogVisible" :dialogVisible="dialogVisible" :id="id"
:dialogTitle="dialogTitle" @initUserList="initUserList"/>

在这里插入图片描述

const dialogVisible=ref(false)
const dialogTitle=ref("")
const id=ref(-1)


const handleDialogValue=(userId)=>{
if(userId){
id.value=userId;
dialogTitle.value="用户修改"
}else{
id.value=-1;
dialogTitle.value="用户添加"
}
dialogVisible.value=true
}

用户管理-删除和批量删除功能实现

后端:

    /**
     * 删除
     * @param ids
     * @return
     */
    @Transactional
    @PostMapping("/delete")
    @PreAuthorize("hasAuthority('system:user:delete')")
    public R delete(@RequestBody Long[] ids){
        sysUserService.removeByIds(Arrays.asList(ids));
        sysUserRoleService.remove(new QueryWrapper<SysUserRole>
                ().in("user_id",ids));
        return R.ok();
    }

前端:

 <el-popconfirm title="您确定批量删除这些记录吗?" @confirm="handleDelete(null)">
<template #reference>
<el-button type="danger" :disabled="delBtnStatus" :icon="Delete" >批量删除</elbutton>
</template>
</el-popconfirm>

const delBtnStatus=ref(true)

表格复选框选中事件:

 @selection-change="handleSelectionChange"

定义 选中的行

const multipleSelection=ref([])

const handleSelectionChange=(selection)=>{
console.log("勾选了")
console.log(selection)
multipleSelection.value=selection;
delBtnStatus.value=selection.length==0;
}

行上面的删除按钮

<el-popconfirm v-if="scope.row.username!='java1234'" title="您确定要删除这条记录
吗?" @confirm="handleDelete(scope.row.id)">
<template #reference>
<el-button type="danger" :icon="Delete" />
</template>
</el-popconfirm>
import { ElMessage, ElMessageBox } from 'element-plus'
const handleDelete=async (id)=>{
  var ids = []
  if(id){
    ids.push(id)
  }else{
    multipleSelection.value.forEach(row=>{
      ids.push(row.id)
    })
  }
  const res=await requestUtil.post("sys/user/delete",ids)
  if(res.data.code==200){
    ElMessage({
      type: 'success',
      message: '执行成功!'
    })
    initUserList();
  }else{
    ElMessage({
      type: 'error',
      message: res.data.msg,
    })
  }
}

在这里插入图片描述
在这里插入图片描述

用户管理-用户信息重置密码和状态更新功能实现

后端 重置密码:

     /**
     * 重置密码
     * @param id
     * @return
     */
    @GetMapping("/resetPassword/{id}")
    @PreAuthorize("hasAuthority('system:user:edit')")
    public R resetPassword(@PathVariable(value = "id")Integer id){
        SysUser sysUser = sysUserService.getById(id);
        sysUser.setPassword(bCryptPasswordEncoder.encode(Constant.DEFAULT_PASSWORD));
        sysUser.setUpdateTime(new Date());
        sysUserService.updateById(sysUser);
        return R.ok();
    }
package com.java.common.constant;

/**
 * 通用常量信息

 */
public class Constant {

    public final static String DEFAULT_PASSWORD="123456";
}
    /**
     * 更新status状态
     * @param id
     * @param status
     * @return
     */
    @GetMapping("/updateStatus/{id}/status/{status}")
    @PreAuthorize("hasAuthority('system:user:edit')")
    public R updateStatus(@PathVariable(value = "id")Integer id,@PathVariable(value
            = "status")String status){
        SysUser sysUser = sysUserService.getById(id);
        sysUser.setStatus(status);
        sysUserService.saveOrUpdate(sysUser);
        return R.ok();
    }

前端:

   <el-popconfirm v-if="scope.row.username!='java1234'" title="您确定要对这个用户重置密
码吗?" @confirm="handleResetPassword(scope.row.id)">
            <template #reference>
              <el-button type="warning" :icon="RefreshRight" >重置密码</el-button>
            </template>
          </el-popconfirm>
 
const handleResetPassword=async (id)=>{
  const res=await requestUtil.get("sys/user/resetPassword/"+id)
  if(res.data.code==200){
    ElMessage({
      type: 'success',
      message: '执行成功!'
    })
    initUserList();
  }else{
    ElMessage({
      type: 'error',
      message: res.data.msg,
    })
  }
}


const statusChangeHandle=async (row)=>{
  let res=await
      requestUtil.get("sys/user/updateStatus/"+row.id+"/status/"+row.status);
  if(res.data.code==200){
    ElMessage({
      type: 'success',
      message: '执行成功!'
    })
  }else{
    ElMessage({
      type: 'error',
      message: res.data.msg,
    })
    initUserList();
  }
}

重置密码和状态更新
在这里插入图片描述

用户管理-用户分配角色功能实现

在这里插入图片描述
新建 roleDialog.vue
在这里插入图片描述

 <template>
  <el-dialog
    model-value="roleDialogVisible"
    title="分配角色"
    width="30%"
    @close="handleClose"
  >

    <el-form
        ref="formRef"
        :model="form"
        label-width="100px"
    >

      <el-checkbox-group v-model="form.checkedRoles">
        <el-checkbox v-for="role in form.roleList" :id="role.id" :key="role.id" :label="role.id"  name="checkedRoles" >{{role.name}}</el-checkbox>
      </el-checkbox-group>

    </el-form>

    <template #footer>
      <span class="dialog-footer">
        <el-button type="primary" @click="handleConfirm">确认</el-button>
        <el-button  @click="handleClose">取消</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script setup>

import {defineEmits, defineProps, ref, watch} from "vue";
import requestUtil,{getServerUrl} from "@/util/request";
import { ElMessage } from 'element-plus'


  const props=defineProps(
      {
        id:{
          type:Number,
          default:-1,
          required:true
        },
        roleDialogVisible:{
          type:Boolean,
          default:false,
          required:true
        },
        sysRoleList:{
          type:Array,
          default:[],
          required:true
        }
      }
  )


const form=ref({
  id:-1,
  roleList:[],
  checkedRoles:[]
})


const formRef=ref(null)

const initFormData=async(id)=>{
  const res=await requestUtil.get("sys/role/listAll");
  form.value.roleList=res.data.roleList;
  form.value.id=id;
}


  watch(
      ()=>props.roleDialogVisible,
      ()=>{
        let id=props.id;
        console.log("id="+id)
        if(id!=-1){
          form.value.checkedRoles=[]
          props.sysRoleList.forEach(item=>{
            form.value.checkedRoles.push(item.id);
          })
          initFormData(id)
        }
      }
  )


  const emits=defineEmits(['update:modelValue','initUserList'])

  const handleClose=()=>{
    emits('update:modelValue',false)
  }

  const handleConfirm=()=>{
    formRef.value.validate(async(valid)=>{
      if(valid){
          let result=await requestUtil.post("sys/user/grantRole/"+form.value.id,form.value.checkedRoles);
          let data=result.data;
          if(data.code==200){
            ElMessage.success("执行成功!")

            emits("initUserList")
            handleClose();
          }else{
            ElMessage.error(data.msg);
          }
      }else{
        console.log("fail")
      }
    })
  }

</script>

<style scoped>

</style>

import RoleDialog from './components/roleDialog'

const sysRoleList=ref([])
const roleDialogVisible=ref(false)

后端

/**
     * 用户角色授权
     * @param userId
     * @param roleIds
     * @return
     */
    @Transactional
    @PostMapping("/grantRole/{userId}")
    @PreAuthorize("hasAuthority('system:user:role')")
    public R grantRole(@PathVariable("userId") Long userId,@RequestBody Long[] roleIds){
        List<SysUserRole> userRoleList=new ArrayList<>();
        Arrays.stream(roleIds).forEach(r -> {
            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setRoleId(r);
            sysUserRole.setUserId(userId);
            userRoleList.add(sysUserRole);
        });
        sysUserRoleService.remove(new QueryWrapper<SysUserRole>().eq("user_id",userId));
        sysUserRoleService.saveBatch(userRoleList);
        return R.ok();
    }

新建控制SysRoleController 类

package com.java.controller;

 
import com.java.entity.R;
import com.java.entity.SysRole;
import com.java.service.SysRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 系统角色Controller控制器
 
 */
@RestController
@RequestMapping("/sys/role")
public class SysRoleController {

    @Autowired
    private SysRoleService sysRoleService;

    @GetMapping("/listAll")
    @PreAuthorize("hasAuthority('system:role:query')")
    public R listAll(){
        Map<String,Object> resultMap=new HashMap<>();
        List<SysRole> roleList = sysRoleService.list();
        resultMap.put("roleList",roleList);
        return R.ok(resultMap);
    }
}



可以添加,删除角色
在这里插入图片描述

角色管理实现

角色管理-根据条件列表分页显示实现

后端

 
    /**
     * 根据条件分页查询角色信息
     * @param pageBean
     * @return
     */
    @PostMapping("/list")
    @PreAuthorize("hasAuthority('system:role:query')")
    public R list(@RequestBody PageBean pageBean){
        String query=pageBean.getQuery().trim();
        Page<SysRole> pageResult = sysRoleService.page(new Page<>(pageBean.getPageNum(), pageBean.getPageSize()),new QueryWrapper<SysRole>().like(StringUtil.isNotEmpty(query),"name",query));
        List<SysRole> roleList = pageResult.getRecords();
        Map<String,Object> resultMap=new HashMap<>();
        resultMap.put("roleList",roleList);
        resultMap.put("total",pageResult.getTotal());
        return R.ok(resultMap);
    }

前端 从user index.vue复制一份改改 user改成role User改成Role

 <template>
  <div class="app-container">
    <el-row :gutter="20" class="header">
      <el-col :span="7">
        <el-input placeholder="请输入角色名..." v-model="queryForm.query" clearable
        ></el-input>
      </el-col>
      <el-button type="primary" :icon="Search" @click="initRoleList">搜索</el-button>
      <el-button type="success" :icon="DocumentAdd" @click="handleDialogValue()">新增
      </el-button>

      <el-popconfirm title="您确定批量删除这些记录吗?" @confirm="handleDelete(null)">
        <template #reference>
          <el-button type="danger" :disabled="delBtnStatus" :icon="Delete" >批量删除</el-button>
        </template>
      </el-popconfirm>


    </el-row>
    <el-table :data="tableData" stripe style="width: 100%" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" />

      <el-table-column prop="name" label="角色名" width="100" align="center"/>
      <el-table-column prop="code" label="权限字符" width="200" align="center"/>
      <el-table-column prop="createTime" label="创建时间" width="200" align="center"/>
      <el-table-column prop="remark" label="备注" />
      <el-table-column prop="action" label="操作" width="400" fixed="right" align="center">
        <template v-slot="scope" >
          <el-button type="primary" :icon="Tools" @click="handleRoleDialogValue(scope.row.id,scope.row.sysRoleList)">分配权限</el-button>
          <el-button v-if="scope.row.code!='admin'" type="primary" :icon="Edit"
                     @click="handleDialogValue(scope.row.id)" />

          <el-popconfirm v-if="scope.row.code!='admin'" title="您确定要删除这条记录
吗?" @confirm="handleDelete(scope.row.id)">
            <template #reference>
              <el-button type="danger" :icon="Delete" />
            </template>
          </el-popconfirm>



        </template>
      </el-table-column>
    </el-table>
    <el-pagination
        v-model:currentPage="queryForm.pageNum"
        v-model:page-size="queryForm.pageSize"
        :page-sizes="[10, 20, 30, 40]"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"v
    />
  </div>
  <Dialog v-model="dialogVisible" :dialogVisible="dialogVisible" :id="id" :dialogTitle="dialogTitle" @initRoleList="initRoleList"/>
  <RoleDialog v-model="roleDialogVisible" :sysRoleList="sysRoleList"
              :roleDialogVisible="roleDialogVisible" :id="id" @initRoleList="initRoleList">
  </RoleDialog>
</template>

可正常搜索,显示
在这里插入图片描述

角色管理-添加修改功能实现

后端:

    /**
     * 添加或者修改
     * @param sysRole
     * @return
     */
    @PostMapping("/save")
    @PreAuthorize("hasAuthority('system:role:add')"+"||"+"hasAuthority('system:role:edit')")
    public R save(@RequestBody SysRole sysRole){
        if(sysRole.getId()==null || sysRole.getId()==-1){
            sysRole.setCreateTime(new Date());
            sysRoleService.save(sysRole);
        }else{
            sysRole.setUpdateTime(new Date());
            sysRoleService.updateById(sysRole);
        }
        return R.ok();
    }

    /**
     * 根据id查询
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @PreAuthorize("hasAuthority('system:role:query')")
    public R findById(@PathVariable(value = "id")Integer id){
        SysRole sysRole = sysRoleService.getById(id);
        Map<String,Object> map=new HashMap<>();
        map.put("sysRole",sysRole);
        return R.ok(map);
    }

前端:
dialog.vue

<template>

  <el-dialog
    :visible="dialogVisible"

    :title="dialogTitle"
    width="30%"
    @close="handleClose"
  >
       <el-form
        ref="formRef"
        :model="form"
        :rules="rules"
        label-width="100px"
    >
      <el-form-item label="角色名称" prop="name">
        <el-input v-model="form.name"    />

      </el-form-item>

      <el-form-item label="权限字符" prop="code">
        <el-input v-model="form.code" />
      </el-form-item>




      <el-form-item label="备注" prop="remark">
        <el-input v-model="form.remark" type="textarea" :rows="4"/>
      </el-form-item>


    </el-form>
    <template #footer>
      <span class="dialog-footer">
        <el-button type="primary" @click="handleConfirm">确认</el-button>
        <el-button @click="handleClose"
          >取消</el-button
        >
      </span>
    </template>
  </el-dialog>

</template>

<script setup>
import {defineEmits, defineProps,ref,watch } from "vue"
import requestUtil,{getServerUrl} from "@/util/request";
import { ElMessage } from 'element-plus'

const props=defineProps(
    {
      id:{
        type:Number,
        default:-1,
        required:true
      },
      dialogTitle:{
        type:String,
        default:'',
        required:true
      },
      dialogVisible:{
        type:Boolean,
        default:false,
        required:true
      }
    }
)

const form=ref({
  id:-1,
  name:"",
  code:"",

  remark:""
})




const rules=ref({
  name:[
    { required: true, message: '请输入角色名称'}
  ],
  code:[
    { required: true, message: '请输入权限字符'}
  ],
})

const formRef=ref(null)

const initFormData=async(id)=>{
  const res=await requestUtil.get("sys/role/"+id);
  form.value=res.data.sysRole;
}

watch(
    ()=>props.dialogVisible,
    ()=>{
      let id=props.id;
      console.log("id="+id)
      if(id!=-1){
        initFormData(id);
      }else{
        form.value={
          id:-1,
          name:"",
          code:"",
          remark:""
        }
      }
    }
)

const emits=defineEmits(['update:modelValue','initRoleList'])

const handleClose=()=>{
  emits('update:modelValue',false)
}

const handleConfirm=()=>{
  formRef.value.validate(async(valid)=>{
    if(valid){
      let result=await requestUtil.post("sys/role/save",form.value);
      let data=result.data;
      if(data.code==200){
        ElMessage.success("执行成功!")
        formRef.value.resetFields();
        emits("initRoleList")
        handleClose();
      }else{
        ElMessage.error(data.msg);
      }
    }else{
      console.log("fail")
    }
  })
}

</script>

<style lang="scss" scoped>

</style>

在这里插入图片描述
在这里插入图片描述

角色管理-删除和批量删除功能实现

   /**
     * 删除
     * @param ids
     * @return
     */
    @Transactional
    @PostMapping("/delete")
    @PreAuthorize("hasAuthority('system:role:delete')")
    public R delete(@RequestBody Long[] ids){
        sysRoleService.removeByIds(Arrays.asList(ids));
        sysUserRoleService.remove(new QueryWrapper<SysUserRole>().in("role_id",ids));
        return R.ok();
    }

角色管理-显示权限菜单树

后端:

package com.java.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import com.java.entity.R;
import com.java.entity.SysMenu;
import com.java.service.SysMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.util.*;

/**
 * 系统菜单Controller控制器

 */
@RestController
@RequestMapping("/sys/menu")
public class SysMenuController {

    @Autowired
    private SysMenuService sysMenuService;

    /**
     * 查询所有菜单树信息
     * @return
     */
    @RequestMapping("/treeList")
    @PreAuthorize("hasAuthority('system:menu:query')")
    public R treeList(){
        List<SysMenu> menuList = sysMenuService.list(new QueryWrapper<SysMenu>().orderByAsc("order_num"));
        return R.ok().put("treeMenu",sysMenuService.buildTreeMenu(menuList));
    }

    /**
     * 添加或者修改
     * @param sysMenu
     * @return
     */
    @PostMapping("/save")
    @PreAuthorize("hasAuthority('system:menu:add')"+"||"+"hasAuthority('system:menu:edit')")
    public R save(@RequestBody SysMenu sysMenu){
        if(sysMenu.getId()==null || sysMenu.getId()==-1){
            sysMenu.setCreateTime(new Date());
            sysMenuService.save(sysMenu);
        }else{
            sysMenu.setUpdateTime(new Date());
            sysMenuService.updateById(sysMenu);
        }
        return R.ok();
    }

    /**
     * 根据id查询
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @PreAuthorize("hasAuthority('system:menu:query')")
    public R findById(@PathVariable(value = "id")Long id){
        SysMenu sysMenu = sysMenuService.getById(id);
        Map<String,Object> map=new HashMap<>();
        map.put("sysMenu",sysMenu);
        return R.ok(map);
    }

    /**
     * 删除
     * @param id
     * @return
     */
    @GetMapping("/delete/{id}")
    @PreAuthorize("hasAuthority('system:menu:delete')")
    public R delete(@PathVariable(value = "id")Long id){
        int count = sysMenuService.count(new QueryWrapper<SysMenu>().eq("parent_id", id));
        if(count>0){
            return R.error("请先删除子菜单!");
        }
        sysMenuService.removeById(id);
        return R.ok();
    }


}

前端

 <template>
  <el-dialog
    model-value="menuDialogVisible"
    title="分配权限"
    width="30%"
    @close="handleClose"
  >

    <el-form
        ref="formRef"
        :model="form"
        label-width="100px"
    >

      <el-tree
          ref="treeRef"
          :data="treeData"
          :props="defaultProps"
          show-checkbox
          :default-expand-all=true
          node-key="id"
          :check-strictly=true
      />

    </el-form>

    <template #footer>
      <span class="dialog-footer">
        <el-button type="primary" @click="handleConfirm">确认</el-button>
        <el-button  @click="handleClose">取消</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script setup>

import {defineEmits, defineProps, ref, watch} from "vue";
import requestUtil,{getServerUrl} from "@/util/request";
import { ElMessage } from 'element-plus'


  const props=defineProps(
      {
        id:{
          type:Number,
          default:-1,
          required:true
        },
        menuDialogVisible:{
          type:Boolean,
          default:false,
          required:true
        }
      }
  )


const form=ref({
  id:-1,

})

const treeRef=ref(null)

const treeData=ref([])

const defaultProps = {
  children: 'children',
  label: 'name'
}

const formRef=ref(null)

const initFormData=async(id)=>{
  const res=await requestUtil.get("sys/menu/treeList");
  console.log("res.data.treeMenu="+res.data.treeMenu)
  treeData.value=res.data.treeMenu;
  form.value.id=id;
}


  watch(
      ()=>props.menuDialogVisible,
      ()=>{
        let id=props.id;
        console.log("id="+id)
        if(id!=-1){

          initFormData(id)
        }
      }
  )


  const emits=defineEmits(['update:modelValue','initRoleList'])

  const handleClose=()=>{
    emits('update:modelValue',false)
  }

  const handleConfirm=()=>{
    formRef.value.validate(async(valid)=>{
      if(valid){
          let result=await requestUtil.post("sys/user/grantRole/"+form.value.id,form.value.checkedRoles);
          let data=result.data;
          if(data.code==200){
            ElMessage.success("执行成功!")

            emits("initUserList")
            handleClose();
          }else{
            ElMessage.error(data.msg);
          }
      }else{
        console.log("fail")
      }
    })
  }

</script>

<style scoped>

</style>

index.vue
在这里插入图片描述

<MenuDialog v-model="menuDialogVisible" :menuDialogVisible="menuDialogVisible"
:id="id" @initRoleList="initRoleList"></MenuDialog>
import MenuDialog from './components/menuDialog'
const menuDialogVisible=ref(false);
const handleMenuDialogValue=(roleId)=>{
if(roleId){
id.value=roleId;
}
menuDialogVisible.value=true
}

在这里插入图片描述

角色管理-分配功能实现

后端:

    /**
     * 获取当前角色的权限菜单ID集合
     * @param id
     * @return
     */
    @GetMapping("/menus/{id}")
    @PreAuthorize("hasAuthority('system:role:menu')")
    public R menus(@PathVariable(value = "id")Integer id){
        List<SysRoleMenu> roleMenuList = sysRoleMenuService.list(new QueryWrapper<SysRoleMenu>().eq("role_id", id));
        List<Long> menuIdList = roleMenuList.stream().map(p -> p.getMenuId()).collect(Collectors.toList());
        return R.ok().put("menuIdList",menuIdList);
    }

    /**
     * 更新角色权限信息
     * @param id
     * @param menuIds
     * @return
     */
    @Transactional
    @PostMapping("/updateMenus/{id}")
    @PreAuthorize("hasAuthority('system:role:menu')")
    public R updateMenus(@PathVariable(value = "id")Long id, @RequestBody Long[] menuIds){
        sysRoleMenuService.remove(new QueryWrapper<SysRoleMenu>().eq("role_id",id));
        List<SysRoleMenu> sysRoleMenuList=new ArrayList<>();
        Arrays.stream(menuIds).forEach(menuId->{
            SysRoleMenu roleMenu=new SysRoleMenu();
            roleMenu.setRoleId(id);
            roleMenu.setMenuId(menuId);
            sysRoleMenuList.add(roleMenu);
        });
        sysRoleMenuService.saveBatch(sysRoleMenuList);
        return R.ok();
    }

前端:

<template>
  <el-dialog
    model-value="menuDialogVisible"
    title="分配权限"
    width="30%"
    @close="handleClose"
  >

    <el-form
        ref="formRef"
        :model="form"
        label-width="100px"
    >

      <el-tree
          ref="treeRef"
          :data="treeData"
          :props="defaultProps"
          show-checkbox
          :default-expand-all=true
          node-key="id"
          :check-strictly=true
      />

    </el-form>

    <template #footer>
      <span class="dialog-footer">
        <el-button type="primary" @click="handleConfirm">确认</el-button>
        <el-button  @click="handleClose">取消</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script setup>

import {defineEmits, defineProps, ref, watch} from "vue";
import requestUtil,{getServerUrl} from "@/util/request";
import { ElMessage } from 'element-plus'


  const props=defineProps(
      {
        id:{
          type:Number,
          default:-1,
          required:true
        },
        menuDialogVisible:{
          type:Boolean,
          default:false,
          required:true
        }
      }
  )


const form=ref({
  id:-1,

})

const treeRef=ref(null)

const treeData=ref([])

const defaultProps = {
  children: 'children',
  label: 'name'
}

const formRef=ref(null)

const initFormData=async(id)=>{
  const res=await requestUtil.get("sys/menu/treeList");
  treeData.value=res.data.treeMenu;
  form.value.id=id;

  const res2=await requestUtil.get("sys/role/menus/"+id);
  treeRef.value.setCheckedKeys(res2.data.menuIdList);

}


  watch(
      ()=>props.menuDialogVisible,
      ()=>{
        let id=props.id;
        console.log("id="+id)
        if(id!=-1){

          initFormData(id)
        }
      }
  )


  const emits=defineEmits(['update:modelValue','initRoleList'])

  const handleClose=()=>{
    emits('update:modelValue',false)
  }

const handleConfirm=()=>{
  formRef.value.validate(async(valid)=>{
    if(valid){
      var menuIds=treeRef.value.getCheckedKeys();
      let result=await requestUtil.post("sys/role/updateMenus/"+form.value.id,menuIds);
      let data=result.data;
      if(data.code==200){
        ElMessage.success("执行成功!")

        emits("initRoleList")
        handleClose();
      }else{
        ElMessage.error(data.msg);
      }
    }else{
      console.log("fail")
    }
  })
}


</script>

<style scoped>

</style>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

菜单管理实现

菜单管理-树形表格菜单信息显示

在这里插入图片描述
index.vue

<template>
  <div class="app-container">

    <el-row  class="header">

      <el-button type="success" :icon="DocumentAdd" @click="handleDialogValue()">新增</el-button>

    </el-row>

     <el-table
          :data="tableData"
          row-key="id"
          stripe
          style="width: 100%; margin-bottom: 20px"
          border
          default-expand-all
          :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
      >

        <el-table-column prop="name" label="菜单名称"  width="200"/>
        <el-table-column prop="icon" label="图标"  width="70" align="center">
          <template v-slot="scope">
            <el-icon><svg-icon :icon="scope.row.icon" /></el-icon>
          </template>
        </el-table-column>
        <el-table-column prop="orderNum" label="排序"  width="70" align="center"/>
        <el-table-column prop="perms" label="权限标识"  width="200" />
        <el-table-column prop="path" label="组件路径"  width="180" />
        <el-table-column prop="menuType" label="菜单类型"  width="120" align="center">
          <template v-slot="scope">
            <el-tag size="small" v-if="scope.row.menuType === 'M'" type="danger" effect="dark">目录</el-tag>
            <el-tag size="small" v-else-if="scope.row.menuType === 'C'" type="success" effect="dark">菜单</el-tag>
            <el-tag size="small" v-else-if="scope.row.menuType === 'F'" type="warning" effect="dark">按钮</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="createTime" label="创建时间" align="center"/>
        <el-table-column prop="action" label="操作" width="400" fixed="right" align="center">
        <template v-slot="scope" >
          <el-button  type="primary" :icon="Edit" @click="handleDialogValue(scope.row.id)" />
          <el-popconfirm  title="您确定要删除这条记录吗?" @confirm="handleDelete(scope.row.id)">
            <template #reference>
              <el-button  type="danger" :icon="Delete" />
            </template>
          </el-popconfirm>
        </template>
      </el-table-column>
      </el-table>

  </div>
  <Dialog v-model="dialogVisible" :tableData="tableData" :dialogVisible="dialogVisible" :id="id" :dialogTitle="dialogTitle" @initMenuList="initMenuList"/>

</template>

<script setup>
import {ref} from 'vue';
import requestUtil,{getServerUrl} from "@/util/request";
import { Search ,Delete,DocumentAdd ,Edit, Tools, RefreshRight} from '@element-plus/icons-vue'
import Dialog from './components/dialog'
import { ElMessage, ElMessageBox } from 'element-plus'


const tableData=ref([])



const dialogVisible=ref(false)

const dialogTitle=ref("")

const id=ref(-1)


const initMenuList=async()=>{
  const res=await requestUtil.get("sys/menu/treeList");
  tableData.value=res.data.treeMenu;

}

initMenuList();

const handleDialogValue=(menuId)=>{
  if(menuId){
    id.value=menuId;
    dialogTitle.value="菜单修改"
  }else{
    id.value=-1;
    dialogTitle.value="菜单添加"
  }
  dialogVisible.value=true
}

const handleDelete=async (id)=>{
  const res=await requestUtil.get("sys/menu/delete/"+id)
  if(res.data.code==200){
    ElMessage({
      type: 'success',
      message: '执行成功!'
    })
    initMenuList();
  }else{
    ElMessage({
      type: 'error',
      message: res.data.msg,
    })
  }
}

</script>

<style lang="scss" scoped>

.header{
  padding-bottom: 16px;
  box-sizing: border-box;
}

.el-pagination{
  float: right;
  padding: 20px;
  box-sizing: border-box;
}

::v-deep th.el-table__cell{
  word-break: break-word;
  background-color: #f8f8f9 !important;
  color: #515a6e;
  height: 40px;
  font-size: 13px;

}

.el-tag--small {
  margin-left: 5px;
}
</style>

在这里插入图片描述

菜单管理-添加修改功能实现

后端:

/**
     * 添加或者修改
     * @param sysMenu
     * @return
     */
    @PostMapping("/save")
    @PreAuthorize("hasAuthority('system:menu:add')"+"||"+"hasAuthority('system:menu:edit')")
    public R save(@RequestBody SysMenu sysMenu){
        if(sysMenu.getId()==null || sysMenu.getId()==-1){
            sysMenu.setCreateTime(new Date());
            sysMenuService.save(sysMenu);
        }else{
            sysMenu.setUpdateTime(new Date());
            sysMenuService.updateById(sysMenu);
        }
        return R.ok();
    }

    /**
     * 根据id查询
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @PreAuthorize("hasAuthority('system:menu:query')")
    public R findById(@PathVariable(value = "id")Long id){
        SysMenu sysMenu = sysMenuService.getById(id);
        Map<String,Object> map=new HashMap<>();
        map.put("sysMenu",sysMenu);
        return R.ok(map);
    }

前端:

<template>
  <el-dialog
    model-value="dialogVisible"
    :title="dialogTitle"
    width="30%"
  @close="handleClose"
  >

    <el-form
        ref="formRef"
        :model="form"
        :rules="rules"
        label-width="100px"
    >

      <el-form-item label="上级菜单" prop="parentId">
        <el-select v-model="form.parentId" placeholder="请选择上级菜单" @change="$forceUpdate()">
          <template v-for="item in tableData">
            <el-option :label="item.name" :value="item.id"></el-option>
            <template v-for="child in item.children">
              <el-option :label="child.name" :value="child.id">
                <span>{{ "    -- " + child.name }}</span>
              </el-option>
            </template>
          </template>
        </el-select>
      </el-form-item>

      <el-form-item label="菜单类型" prop="menuType" label-width="100px">
     <el-radio-group v-model="form.menuType">
      <el-radio :label="'M'">目录</el-radio>
      <el-radio :label="'C'">菜单</el-radio>
      <el-radio :label="'F'">按钮</el-radio>
     </el-radio-group>
    </el-form-item>

      <el-form-item label="菜单图标" prop="icon">
        <el-input v-model="form.icon" />
      </el-form-item>

      <el-form-item label="菜单名称" prop="name">
        <el-input v-model="form.name" />
      </el-form-item>

      <el-form-item label="权限标识" prop="perms">
        <el-input v-model="form.perms" />
      </el-form-item>

      <el-form-item label="组件路径" prop="component">
        <el-input v-model="form.component" />
      </el-form-item>

      <el-form-item label="显示顺序" prop="orderNum" >
     <el-input-number v-model="form.orderNum" :min="1" label="显示顺序"></el-input-number>
    </el-form-item>



    </el-form>
    <template #footer>
      <span class="dialog-footer">
        <el-button type="primary" @click="handleConfirm">确认</el-button>
        <el-button  @click="handleClose">取消</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script setup>

import {defineEmits, defineProps, ref, watch} from "vue";
import requestUtil,{getServerUrl} from "@/util/request";
import { ElMessage } from 'element-plus'

  const tableData=ref([])


  const props=defineProps(
      {
        id:{
          type:Number,
          default:-1,
          required:true
        },
        dialogTitle:{
          type:String,
          default:'',
          required:true
        },
        dialogVisible:{
          type:Boolean,
          default:false,
          required:true
        },
        tableData:{
          type:Array,
          default:[],
          required:true
        }
      }
  )


const form=ref({
  id:-1,
  parentId:'',
  menuType:"M",
  icon:'',
  name:'',
  perms:'',
  component:'',
  orderNum:1
})


const rules=ref({
  parentId:[
    { required: true, message: '请选择上级菜单'}
  ],
  name: [{ required: true, message: "菜单名称不能为空", trigger: "blur" }]
})

const formRef=ref(null)

const initFormData=async(id)=>{
 const res=await requestUtil.get("sys/menu/"+id);
 form.value=res.data.sysMenu;
}



  watch(
      ()=>props.dialogVisible,
      ()=>{
        let id=props.id;
        tableData.value=props.tableData;
        if(id!=-1){
          initFormData(id)
        }else{
          form.value={
            id:-1,
            parentId:'',
            menuType:"M",
            icon:'',
            name:'',
            perms:'',
            component:'',
            orderNum:1
          }
        }
      }
  )


  const emits=defineEmits(['update:modelValue','initMenuList'])

  const handleClose=()=>{
    emits('update:modelValue',false)
  }

  const handleConfirm=()=>{
    formRef.value.validate(async(valid)=>{
      if(valid){
          let result=await requestUtil.post("sys/menu/save",form.value);
          let data=result.data;
          if(data.code==200){
            ElMessage.success("执行成功!")
            formRef.value.resetFields();
            emits("initMenuList")
            handleClose();
          }else{
            ElMessage.error(data.msg);
          }
      }else{
        console.log("fail")
      }
    })
  }

</script>

<style scoped>

</style>

菜单管理-删除功能实现

后端:

 
    /**
     * 删除
     * @param id
     * @return
     */
    @GetMapping("/delete/{id}")
    @PreAuthorize("hasAuthority('system:menu:delete')")
    public R delete(@PathVariable(value = "id")Long id){
        int count = sysMenuService.count(new QueryWrapper<SysMenu>().eq("parent_id", id));
        if(count>0){
            return R.error("请先删除子菜单!");
        }
        sysMenuService.removeById(id);
        return R.ok();
    }

前端:index.vue

 const handleDelete=async (id)=>{
  const res=await requestUtil.get("sys/menu/delete/"+id)
  if(res.data.code==200){
    ElMessage({
      type: 'success',
      message: '执行成功!'
    })
    initMenuList();
  }else{
    ElMessage({
      type: 'error',
      message: res.data.msg,
    })
  }
}


网站公告

今日签到

点亮在社区的每一天
去签到