5.系统权限管理

-------角色管理-------

一.关系分析

对应的是数据库表的名称

地址:https://pixso.cn/app/board/z2L2uMd_39sSJBKPMv-xiA?showQuickFrame=true&icon_type=3&file_type=20 邀请您加入 Pixso 白板文件「用户权限管理表」

二.sql编写

边写边学

1.通过用户id 查对应的角色职务信息

-- 查询,结果显示角色里的所有数据
SELECT ro.* 
-- 把表1起个别名叫ro  和  表2别名us 关联起来
FROM sys_role ro INNER JOIN sys_user_role us
-- 关联条件是 ro的id = us的rolo_id
ON ro.id = us.role_id
-- 查询条件
WHERE us.user_id = 5

 白话:查询角色信息里的  (角色为5)  并且里面的  (roleid和role表的id相等)  的us表  

也就是说 关系表 永远只作为   join on的条件

 

2.通过用户id 查对应的菜单职务信息

从下往上看:  包含条件的关系表 -->关系表-->关系表-->X个关系表 -->结果表

从上往下看:  结果表-->关系表-->关系表-->X个关系表--> 包含条件的关系表

-- DISTINCT去重  要查谁就 FROM 谁  ,通过关系表一张一张的连到 条件表
SELECT DISTINCT me.* FROM sys_menu me
INNER  JOIN sys_role_menu rome ON  rome.menu_id = me.id
INNER  JOIN sys_user_role usro ON  usro.role_id = rome.role_id
WHERE usro.user_id = 5

 

 

 

三.准备工作

这里前端代码很简单就不写了,直接复制文档

1.修改动态菜单为固定菜单:

const menus = getFilterMenus(fixedRoutes)
setMenus(menus)

2、在views目录下创建一个system文件夹在该文件夹下创建3个.vue文件

也就是最终的这三个界面:

3、在router/modules目录下创建system.js文件,配置路由规则

创建一个大标签的管理菜单

// 导入组件
const Layout = () => import('@/layout/index.vue')
const sysRole = () => import('@/views/system/sysRole.vue')
const sysUser = () => import('@/views/system/sysUser.vue')
const sysMenu = () => import('@/views/system/sysMenu.vue')

// 导出该组件
export default([
    {
        path: "/system",
        component: Layout,
        name: 'system',
        meta: {
            title: '系统管理',
        },
        icon: 'Location',
        children: [
            {
                path: '/sysRole',
                name: 'sysRole',
                component: sysRole,
                meta: {
                    title: '角色管理',
                },
                hidden: false
            },
            {
                path: '/sysUser',
                name: 'sysUser',
                component: sysUser,
                meta: {
                    title: '用户管理',
                },
                hidden: false
            },
            {
                path: '/menu',
                name: 'sysMenu',
                component: sysMenu,
                meta: {
                    title: '菜单管理',
                },
                hidden: false
            }
        ]
    }
])

4、修改router/index.js导入路由配置

把大标签放到菜单里面

import system from './modules/system'
export const fixedRoutes = [...home,...system]

 

四.搜索列表功能

 1.后端:

在resources里创建目录不能用.要用/

首先准备好:

  • Controller的API接口
  • mybatis的mapper
  • mybatis的xml
  • service接口
  • service的impl

2.1.根据名称查role

2.1.1.编写sql层

我们先做这一个:

 

2.1.1.1.首先通过可视化工具编写sql

用LIKE关键词,模糊查询"管理员"

但是我们后端需要字符串拼接所有,用sql的一个CONCAT函数进行拼接

加一个ORDER BY id DESC 通过id降序排序

SELECT * FROM sys_role
-- 模糊搜索
WHERE  role_name LIKE "%管理员%"
-- 降序查询
ORDER BY id DESC
2.1.1.2.改为mybatis代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--对应的接口别弄错了-->
<mapper namespace="com.zhexin.manage.mapper.SysRole.getRoleListMapper">
    <!--映射到SysRole里面, autoMapping为自动映射-->
    <resultMap id="ListSysRole" type="com.zhexin.model.entity.system.SysRole" autoMapping="true" />
    <!--查询结果的列-->
    <sql id="ListSysRoleJG">
        id,role_name,role_code,description,create_time,update_time,is_deleted
    </sql>

    <!--查询-->
    <select id="SelectRoleListByname" resultMap="ListSysRole">
        SELECT <include refid="ListSysRoleJG"/>  FROM sys_role
        <where>
            <if test="RoleName != null and RoleName != ''">
                and role_name LIKE CONCAT('%',#{RoleName},'%')
            </if>
        </where>
        -- 降序查询
        ORDER BY id DESC
    </select>

</mapper>

2.1.2 controller接口

因为这里需要分页:

我们用导入的配置:  这个插件来实现

 

前端需要传来的参数: 

通过分页插件进行分页

  • 当前页数
  • 每页存在的数量
  • 上传到查询对象
@Operation(summary = "角色查询接口") //文档注释
@PostMapping("/getRoleList/{page}/{quantity}")
public Result getRoleList(@PathVariable("page") String page,  //路径中的页数
                          @PathVariable("quantity") String quantity,  //路径中的页面数量
                          @RequestBody SysRoleDto sysRoleDto  //请求的json参数
                         ){
    PageInfo<SysRole> info = getRoleListService.byName(page,quantity,sysRoleDto)
        return Result.build(info, ResultCodeEnum.SUCCESS);
}

 

2.1.3 业务实现

package com.zhexin.manage.service.SysRole.Impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zhexin.manage.mapper.SysRole.getRoleListMapper;
import com.zhexin.manage.service.SysRole.getRoleListService;
import com.zhexin.model.dto.system.SysRoleDto;
import com.zhexin.model.entity.system.SysRole;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class getRoleListServiceImpl implements getRoleListService {
	@Autowired
	getRoleListMapper getRoleListMapper;
	
	@Override
	public PageInfo<SysRole> byName(int page, int quantity, SysRoleDto sysRoleDto) {
		PageInfo<SysRole> pageInfo = null;
		
		//因为我们在mybatis里注册了这个插件,
		//用这个方法  运行插件,他会自动拦截查询,进行分页
		PageHelper.startPage(page,quantity);
		
		//开始查询
		List<SysRole> sysRoles = getRoleListMapper.SelectRoleListByname(sysRoleDto.getRoleName());
		
		//封装返回结果对象:
		pageInfo = new PageInfo<>(sysRoles);

		return pageInfo;
	}
}

 

 

2.前端:

1.1界面代码

  • 1、搜索表单
  • 2、添加按钮
  • 3、数据展示表格
  • 4、分页条组件

sysRole.vue 代码实现如下所示:

<template>
    <div class="search-div">
        <!-- 搜索表单 -->
        <el-form label-width="70px" size="small">
            <el-form-item label="角色名称">
                <el-input
                style="width: 100%"
                placeholder="角色名称"
                ></el-input>
            </el-form-item>
            <el-row style="display:flex">
                <el-button type="primary" size="small">
                搜索
                </el-button>
                <el-button size="small">重置</el-button>
            </el-row>
        </el-form>
​
        <!-- 添加按钮 -->
        <div class="tools-div">
            <el-button type="success" size="small">添 加</el-button>
        </div>
        
        <!--- 角色表格数据 -->
        <el-table :data="list" style="width: 100%">
            <el-table-column prop="roleName" label="角色名称" width="180" />
            <el-table-column prop="roleCode" label="角色code" width="180" />
            <el-table-column prop="createTime" label="创建时间" />
            <el-table-column label="操作" align="center" width="280">
            <el-button type="primary" size="small">
                修改
            </el-button>
            <el-button type="danger" size="small">
                删除
            </el-button>
            </el-table-column>
        </el-table>
​
        <!--分页条-->
        <el-pagination
            :page-sizes="[10, 20, 50, 100]"
            layout="total, sizes, prev, pager, next"
            :total="total"
        />
  </div>
​
</template>
​
<script setup>
import { ref } from 'vue';
​
// 分页条总记录数
let total = ref(0)
​
// 定义表格数据模型
let list = ref([
    {"id":9 ,  "roleName": "系统管理员" , "roleCode":"xtgly","createTime": '2023-07-31'},
    {"id":10 , "roleName": "商品管理员" , "roleCode":"spgly","createTime": '2023-07-31'}
])
​
</script>
​
<style scoped>
​
.search-div {
  margin-bottom: 10px;
  padding: 10px;
  border: 1px solid #ebeef5;
  border-radius: 3px;
  background-color: #fff;
}
​
.tools-div {
  margin: 10px 0;
  padding: 10px;
  border: 1px solid #ebeef5;
  border-radius: 3px;
  background-color: #fff;
}
​
</style>

1.2.最终效果

 

1.3.创建api接口

在api目录下,有我们之前登录的接口

我们按照他的写法写一个role的接口

//引入统一请求封装
import request from '@/utils/request'
//api接口常量
const url = '/zx/admin/system/role/'
// 登录接口
export const GetRole = (page,quantity,datas) => {
  return request({
    //在js里`表示字符串拼接${}是个表达式
    url: `${url}/getRoleList/${page}/${quantity}`,
    method: 'post',
    //如果后端是@RequestBody则这里要加data
    //否则是 params
    data : datas ,
  })
}

导入到sysRole.vue界面

import {
    ref,
    reactive,
    onMounted
} from 'vue';
import {GetRole} from '@/api/role';

1.4.调用

1.4.1创建变量

// 分页条总记录数
// let total = ref(0)
// 定义表格数据模型
// let list = ref([
//     {"id":9 ,  "roleName": "系统管理员" , "roleCode":"xtgly","createTime": '2023-07-31'},
//     {"id":10 , "roleName": "商品管理员" , "roleCode":"spgly","createTime": '2023-07-31'}
// ])
//
//我们按照vue2的习惯创建一个存放数据的对象
let thiss =reactive({
    // 定义表格数据模型
    list:[],
    //当前页码
    page:1,
    // 分页条总记录数
    total:10,
    //查询问题的文本,结果刚好可以跟后台的sysRoleDto类对应
    sysRoleDto:{
        roleName:''
    }
})

1.4.2改掉界面里的变量

1.4.3创建访问方法

//查询接口
const getrolelest = async ()=>{
    //从后台查到数据,匹配到参数里
   const { code ,message ,data } = await GetRole(thiss.page , thiss.total ,thiss.sysRoleDto);
   thiss.list = data.list
   thiss.page = data.pageNum
   thiss.total = data.pageNum
}

1.4.4 创建启动钩子, 调用访问方法

// 组件加载钩子,也就是启动函数
onMounted(()=>{
    // 设置为空,即为查询所有
    thiss.sysRoleDto.roleName = '';
    getrolelest();
})

 

1.4.5 监听输入框内容,连接到变量

关于v-model: 全网最详细的v-model讲解_zayyo的博客-CSDN博客

关于v-on: Vue - v-on的使用_vue的v-on用法_学习中的小菜鸟.的博客-CSDN博客

输入框文档: 翻译 | Element Plus (element-plus.org)

 

修改界面

 将钩子里的方法  ,提出重置方法

// 组件加载钩子,也就是启动函数
onMounted(()=>{
    reset()
})
const reset = () => {
    // 设置为空,即为查询所有
    thiss.sysRoleDto.roleName = '';
    getrolelest();
}

 运行后发现, 输入的值只被赋值了一次,后面的赋值没有效果

后台日志看到这里不一样, 这个有定像我们的每页条数啊

 

 看一下抓包:  两次的数字果然不一样

那么我猜测应该是

运维我们的钩子是,组件加载前调用

在钩子的时候使用的是我们的默认参数是10 , 当组件启动完成后, 数字就变成组件的数字了

如果是这样的话我们不用管他,继续下一步,,下一步完成就不会出现这个情况了!

1.4.6 修改分页条

Pagination 分页 | Element Plus (element-plus.org)

 原来是这里写错了,修改就好了,分页条也解决了

也正符合我的推理逻辑

//查询接口
const getrolelest = async ()=>{
    console.log( thiss.sysRoleDto.roleName );
    //从后台查到数据,匹配到参数里
   const { code ,message ,data } = await GetRole(thiss.page , thiss.total ,thiss.sysRoleDto);
   thiss.list = data.list
   thiss.page = data.pageNum
   thiss.pageSize = data.pageSize
   thiss.total = data.total
}

 

 这时候又出现个新问题, 当我们点击重置的时候,  先设置搜索框为空  ,这时候并没有进行重置,而是为空的时候 再一次调用接口才会重置

 也就是说第一次被设置搜索框顶掉了

1.4.6 最终代码:

bug都是分页条引起的
 <template>
    <div class="search-div">
        <!-- 搜索表单 -->
        <el-form label-width="70px" size="small">
            <el-form-item label="角色名称">
                <el-input
                style="width: 100%"
                placeholder="角色名称"
                v-model="thiss.sysRoleDto.roleName" 
                ></el-input>
            </el-form-item>
            <el-row style="display:flex">
                <el-button type="primary" size="small" @click="getrolelest()">
                搜索
                </el-button>
                <el-button size="small" @click=" reset()">重置</el-button>
            </el-row>
        </el-form>

        <!-- 添加按钮 -->
        <div class="tools-div">
            <el-button type="success" size="small">添 加</el-button>
        </div>
        
        <!--- 角色表格数据 -->
        <el-table :data="thiss.list" style="width: 100%">
            <el-table-column prop="roleName" label="角色名称" width="180" />
            <el-table-column prop="roleCode" label="角色code" width="180" />
            <el-table-column prop="createTime" label="创建时间" />
            <el-table-column label="操作" align="center" width="280">
            <el-button type="primary" size="small">
                修改
            </el-button>
            <el-button type="danger" size="small">
                删除
            </el-button>
            </el-table-column>
        </el-table>

        <!--分页条-->
        <!--监听分页,换页,和字符-->
        <el-pagination
            :page-sizes="[10,2, 20, 50, 100]"
            layout="total, sizes, prev, pager, next"
            :total="thiss.total"
            @size-change="getrolelest"
            @current-change="getrolelest"
            v-model:current-page="thiss.pageNum"
            v-model:page-size="thiss.pageSize"
              />
  </div>

</template>

<script setup>
import {
    ref,
    reactive,
    onMounted,
    watch
} from 'vue';
import {GetRole} from '@/api/role';
// 分页条总记录数
// let total = ref(0)
// 定义表格数据模型
// let list = ref([
//     {"id":9 ,  "roleName": "系统管理员" , "roleCode":"xtgly","createTime": '2023-07-31'},
//     {"id":10 , "roleName": "商品管理员" , "roleCode":"spgly","createTime": '2023-07-31'}
// ])
//
//我们按照vue2的习惯创建一个存放数据的对象
let thiss =reactive({
    // 定义表格数据模型
    list:[],
    //当前页码
    pageNum:1,
    //当前显示页码
    pageSize:10,
    // 分页条总记录数
    total:'',
    //查询问题的文本,结果刚好可以跟后台的sysRoleDto类对应
    sysRoleDto:{
        roleName:''
    }
})




// 组件加载钩子,也就是启动函数
onMounted(()=>{
    reset()
})
const reset = () => {
    // 设置为空,即为查询所有
    thiss.sysRoleDto.roleName = '';
    getrolelest();
}
//查询接口
const getrolelest = async ()=>{
    console.log( thiss.sysRoleDto.roleName );
    //从后台查到数据,匹配到参数里
   const { code ,message ,data } = await GetRole(thiss.pageNum , thiss.pageSize ,thiss.sysRoleDto);
   thiss.list = data.list
   thiss.pageNum = data.pageNum
   thiss.pageSize = data.pageSize
   thiss.total = data.total
}







</script>

<style scoped>

.search-div {
  margin-bottom: 10px;
  padding: 10px;
  border: 1px solid #ebeef5;
  border-radius: 3px;
  background-color: #fff;
}

.tools-div {
  margin: 10px 0;
  padding: 10px;
  border: 1px solid #ebeef5;
  border-radius: 3px;
  background-color: #fff;
}

</style>


<!-- 

{
    "code": 200,
    "message": "操作成功",
    "data": {
        "total": 7,
        "list": [
            {
                "id": 39,
                "createTime": "2023-09-04 02:04:41",
                "updateTime": "2023-09-04 02:30:01",
                "isDeleted": 1,
                "roleName": "运维人员",
                "roleCode": "yw",
                "description": null
            },
            {
                "id": 38,
                "createTime": "2023-09-03 15:24:32",
                "updateTime": "2023-09-04 02:30:06",
                "isDeleted": 1,
                "roleName": "开发人员",
                "roleCode": "dev",
                "description": null
            }
        ],
        "pageNum": 1,
        "pageSize": 2,
        "size": 2,
        "startRow": 1,
        "endRow": 2,
        "pages": 4,
        "prePage": 0,
        "nextPage": 2,
        "isFirstPage": true,
        "isLastPage": false,
        "hasPreviousPage": false,
        "hasNextPage": true,
        "navigatePages": 8,
        "navigatepageNums": [
            1,
            2,
            3,
            4
        ],
        "navigateFirstPage": 1,
        "navigateLastPage": 4
    }
}

 -->

总结:

在vue3中 ,属性绑定参数要用 v-model:属性

事件绑定方法要用@绑定

五.添加功能

1.后端

1.Controller

@Operation(summary = "新增角色") //文档注释
@PostMapping("/DataUpRole")
public Result getRoleList( @RequestBody SysRole SysRole){  //请求的json参数

    boolean jg = dataUpRoleService.insert(SysRole);

    return Result.build(jg, ResultCodeEnum.SUCCESS);
}

2.Mybatis

先测试:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--对应的接口别弄错了-->
<mapper namespace="com.zhexin.manage.mapper.SysRole.DataUpRoleMapper">
    <!--映射到SysRole里面, autoMapping为自动映射-->

    <insert id="insert" parameterType="com.zhexin.model.entity.system.SysRole">
        INSERT INTO sys_role
--             表字段
            (  role_name ,  role_code , description )
        VALUES
--             对应的对象属性
            (#{roleName}, #{roleCode} ,#{description})
    </insert>



</mapper>

3.Service

@Service
public class DataUpRoleServiceImpl implements DataUpRoleService {
	@Autowired
	DataUpRoleMapper dataUpRoleMapper;
	@Override
	public boolean insert(SysRole sysRole) {
		if (StrUtil.isEmpty(sysRole.getRoleName()) ||StrUtil.isEmpty( sysRole.getRoleCode()) || StrUtil.isEmpty(sysRole.getDescription())){
			throw new zhexinException(201 , "添加失败,信息不完整!");
		}
		boolean insert = dataUpRoleMapper.insert(sysRole);
		return insert;
	}
}

 

2.前端

2.1 再里编写接口

// 添加角色请求方法
export const SaveSysRole = (data) => {
  return request({
      url: `${url}/DataUpRole`,
      method: 'post',
      data
  })
}

2.2 在界面中引入

import { GetRole , SaveSysRole } from '@/api/role';

2.3 编写一个弹窗,用于输入新的信息

<!-- 添加按钮 -->
<div class="tools-div">
    <el-button type="success" size="small" @click="addShow">添 加</el-button>
</div>
<!-- 添加角色表单对话框 -->
<el-dialog v-model="thiss.dialogVisible" title="添加或修改角色" width="30%">
    <el-form label-width="120px">
        <el-form-item label="角色名称" >
            <el-input v-model="thiss.uprole.roleName"/>
        </el-form-item>
        <el-form-item label="角色Code" >
            <el-input v-model="thiss.uprole.roleCode"/>
        </el-form-item>
        <el-form-item label="描述">
            <el-input  v-model="thiss.uprole.description"/>
        </el-form-item>
        <el-form-item>
            <el-button type="primary" @click="upRole">提交</el-button>
            <el-button @click="thiss.dialogVisible = false">取消</el-button>
        </el-form-item>
    </el-form>
</el-dialog>

 

 2.4 加入到变量

    //弹窗显示控制开关
    dialogVisible:false,
    //添加数据
    uprole:{
        roleName:'',
        roleCode:'' ,
        description:''
    }

 

 2.4 点击打开弹窗方法

//弹窗打开方法
const addShow = ()=>{
    thiss.dialogVisible = true
    //清空
    thiss.uprole = {
        roleName:'',
        roleCode:'' ,
        description:''
    }
}

 

 2.5 调用请求接口

//添加接口
const upRole = async () => {
    console.log(thiss.sysRoleDto.roleName);
    //从后台查到数据,匹配到参数里
    const { code, message, data } = await SaveSysRole(thiss.uprole);
    if (+code == 200) {
        ctx.$message.success(message)
        //关闭弹窗
        thiss.dialogVisible = false
        //刷新界面
        getrolelest();
    } else {
        ctx.$message.error(message)
    }

}

 

2.6 测试

成功, 我又加了其他显示项

 

<el-table-column prop="id" label="用户ID" />
<el-table-column prop="roleName" label="角色名称" width="180" />
<el-table-column prop="roleCode" label="角色code" width="180" />
<el-table-column prop="description" label="描述" />
<el-table-column prop="createTime" label="创建时间" />
<el-table-column prop="updateTime" label="更新时间" />
<el-table-column prop= "isDeleted"  label="启用" />

 

最后最后。。。。。。。。

剩下的增删改查功能都差不多,就不一个个说了,总结一下步骤

后端:

  1. 编写sql语句
  2. 创建service文件、mapper文件、和conrtoller接口
  3. 把sql转为mybatis
  4. 业务层判断是否空,然后调用mybatis接口

前端:

  1. 创建api接口,并引入
  2. 创建操作组件
  3. 创建组件变量
  4. 创建对应方法
  5. 组件和功能、属性一一绑定
  6. 编写显示效果,和提示

 

 

下面是我的主要代码:java层只放mybatis ,前端所有都在了

后端:

controller
 package com.zhexin.manage.controller;

import com.github.pagehelper.PageInfo;
import com.zhexin.manage.service.SysRole.DataUpRoleService;
import com.zhexin.manage.service.SysRole.DeleteSysRoleService;
import com.zhexin.manage.service.SysRole.aMendRoleService;
import com.zhexin.manage.service.SysRole.getRoleListService;
import com.zhexin.model.dto.system.SysRoleDto;
import com.zhexin.model.entity.system.SysRole;
import com.zhexin.model.vo.common.Result;
import com.zhexin.model.vo.common.ResultCodeEnum;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;

@Tag(name = "角色管理接口")   //swagger文档注释
@RestController  //注册到bean
@RequestMapping("/admin/system/role")  //注册访问路径
public class SysRoleController {
	@Autowired
	getRoleListService getRoleListService;
	@Autowired
	DataUpRoleService dataUpRoleService;
	@Autowired
	aMendRoleService amendRoleService;
	@Autowired
	DeleteSysRoleService deleteSysRoleService;
	
	@Operation(summary = "角色查询接口") //文档注释
	@PostMapping("/getRoleList/{page}/{quantity}")
	public Result getRoleList(@PathVariable("page") int page,  //路径中的页数
	                          @PathVariable("quantity") int quantity,  //路径中的页面数量
	                          @RequestBody SysRoleDto sysRoleDto  //请求的json参数
	                          ){
		
		PageInfo<SysRole> info = getRoleListService.byName(page,quantity,sysRoleDto);
		return Result.build(info, ResultCodeEnum.SUCCESS);
	}
	
	@Operation(summary = "新增角色") //文档注释
	@PostMapping("/DataUpRole")
	public Result getRoleList( @RequestBody SysRole SysRole){  //请求的json参数
		
		boolean jg = dataUpRoleService.insert(SysRole);
		
		return Result.build(jg, ResultCodeEnum.SUCCESS);
	}
	

	@Operation(summary = "修改角色") //文档注释
	@PutMapping("/aMendRole")
	public Result aMendRole( @RequestBody SysRole SysRole){  //请求的json参数
		
		boolean jg = amendRoleService.aMend(SysRole);
		
		return Result.build(jg, ResultCodeEnum.SUCCESS);
	}
	
	
	@Operation(summary = "修改角色状态") //文档注释
	@GetMapping("/aMendRoleState/{roleId}/{roleState}")
	public Result aMendRole( @PathVariable(value = "roleId") Long roleId ,
                             @PathVariable(value = "roleState") Integer roleState
	){  //请求的json参数
		
		boolean jg = amendRoleService.state(roleId , roleState);
		
		return Result.build(jg, ResultCodeEnum.SUCCESS);
	}
	
	
	@Operation(summary = "删除角色") //文档注释
	@DeleteMapping("/DeleteSysRoleById/{roleId}")
	public Result DeleteSysRoleById( @PathVariable(value = "roleId") Long roleId){  //请求的json参数
		
		boolean jg = deleteSysRoleService.DeleteSysRole(roleId);
		
		return Result.build(jg, ResultCodeEnum.SUCCESS);
	}
	
	@Operation(summary = "批量删除角色") //文档注释
	@DeleteMapping("/DeleteSysRoleByIdList")
	public Result DeleteSysRoleByIdList( @RequestBody List<SysRole> roleId){  //请求的json参数
		
		boolean jg = deleteSysRoleService.DeleteSysRolelist(roleId);
		
		return Result.build(jg, ResultCodeEnum.SUCCESS);
	}
}
修改和修改状态
 <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--对应的接口别弄错了-->
<mapper namespace="com.zhexin.manage.mapper.SysRole.aMendRoleMapper">
    <!--映射到SysRole里面, autoMapping为自动映射-->

    <update id="aMendRole" parameterType="com.zhexin.model.entity.system.SysRole">
        UPDATE sys_role
        SET
        <if test="roleName != null || roleName != ''">
        role_name = #{roleName} ,
        </if>

        <if test="roleCode != null || roleCode != ''">
        role_code = #{roleCode},
        </if>

        <if test="description != null || description != ''">
        description = #{description},
        </if>
        -- 更新修改时间,now为当前时间
        update_time =  NOW()

        WHERE id = #{id}

    </update>


    <update id="state">
        UPDATE sys_role
        SET  is_deleted= #{roleState}
        WHERE id= #{roleId}
    </update>

</mapper>
删除
 <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--对应的接口别弄错了-->
<mapper namespace="com.zhexin.manage.mapper.SysRole.DeleteSysRoleMapper">
    <!--映射到SysRole里面, autoMapping为自动映射-->

    <delete id="DeleteSysRole">
        DELETE FROM sys_role
        WHERE id= #{roleId}
    </delete>

</mapper>
删除和批量删除Service
 package com.zhexin.manage.service.SysRole.Impl;

import com.zhexin.common.service.exception.zhexinException;
import com.zhexin.manage.mapper.SysRole.DeleteSysRoleMapper;
import com.zhexin.manage.service.SysRole.DeleteSysRoleService;
import com.zhexin.model.entity.system.SysRole;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class DeleteSysRoleServiceImpl implements DeleteSysRoleService {
	@Autowired
	DeleteSysRoleMapper deleteSysRoleMapper;
	@Override
	public boolean DeleteSysRole(Long roleId) {
		if ( roleId == null ){
			throw new zhexinException(201 , "删除失败,信息不完整!");
		}
		return deleteSysRoleMapper.DeleteSysRole(roleId);
	}
	
	//批量删除
	@Override
	public boolean DeleteSysRolelist(List<SysRole> roleId) {
		for (SysRole sysRole : roleId) {
			if (!this.DeleteSysRole(sysRole.getId())){
				return false;
			}
		}
		return true;
	}
}

前端:

sysRole.vue
 <template>
    <div class="search-div">
        <!-- 搜索表单 -->
        <el-form label-width="70px" size="small">
            <el-form-item label="角色名称">
                <el-input style="width: 100%" placeholder="角色名称" v-model="thiss.sysRoleDto.roleName"></el-input>
            </el-form-item>
            <el-row style="display:flex">
                <el-button type="primary" size="small" @click="getrolelest()">
                    搜索
                </el-button>
                <el-button size="small" @click=" reset()">重置</el-button>
            </el-row>
        </el-form>

        <!-- 添加按钮 -->
        <div class="tools-div">
            <el-button type="success" size="small" @click="addShow">添 加</el-button>
            <el-button type="danger" size="small" @click="Statebutter()">批量删除</el-button>
        </div>
        <!-- 添加角色表单对话框 -->
        <el-dialog v-model="thiss.dialogVisible" title="添加或修改角色" width="30%">
            <el-form label-width="120px">
                <el-form-item label="角色名称">
                    <el-input v-model="thiss.uprole.roleName" />
                </el-form-item>
                <el-form-item label="角色Code">
                    <el-input v-model="thiss.uprole.roleCode" />
                </el-form-item>
                <el-form-item label="描述">
                    <el-input v-model="thiss.uprole.description" />
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="upRole">提交</el-button>
                    <el-button @click="thiss.dialogVisible = false">取消</el-button>
                </el-form-item>
            </el-form>
        </el-dialog>



        <!--- 角色表格数据 -->
        <el-table :data="thiss.list" ref="ElTableref" style="width: 100%">
            <el-table-column type="selection" width="55" />
            <el-table-column type="index" label="序列" width="80" />
            <el-table-column prop="id" label="用户ID" :sortable="true" />
            <el-table-column prop="roleName" label="角色名称" width="180" />
            <el-table-column prop="roleCode" label="角色code" width="180" />
            <el-table-column prop="description" label="描述" />
            <el-table-column prop="createTime" label="创建时间" :sortable="true" />
            <el-table-column prop="updateTime" label="更新时间" :sortable="true" />
            <el-table-column prop="isDeleted" label="状态" :formatter="statusFilter()" /> <!-- 过滤器 -->
            <el-table-column label="操作" align="center" width="280" #default="scope"> <!-- #default 当前组件-->
                <el-button type="primary" size="small" @click="amends(scope)">
                    修改
                </el-button>
                <el-button  size="small" :color="thiss.StateButter" @click="setRoleState(scope)">
                    {{ scope.row.isDeleted == 1 ? '禁用' : '启用' }}
                </el-button>
                <el-button type="danger" size="small" @click="deletes(scope)">
                    删除
                </el-button>
            </el-table-column>
        </el-table>







        <!--分页条-->
        <!--监听分页,换页,和字符-->
        <el-pagination :page-sizes="[10, 2, 20, 50, 100]" layout="total, sizes, prev, pager, next" :total="thiss.total"
            @size-change="getrolelest" @current-change="getrolelest" v-model:current-page="thiss.pageNum"
            v-model:page-size="thiss.pageSize" />
    </div>
</template>

<script setup>
import {
    ref,
    reactive,
    onMounted,
    getCurrentInstance
} from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus'
import { GetRole, SaveSysRole, aMendRole, DeleteSysRoleById , DeleteSysRoleByIdList, aMendRoleState } from '@/api/role';
// 分页条总记录数
// let total = ref(0)
// 定义表格数据模型
// let list = ref([
//     {"id":9 ,  "roleName": "系统管理员" , "roleCode":"xtgly","createTime": '2023-07-31'},
//     {"id":10 , "roleName": "商品管理员" , "roleCode":"spgly","createTime": '2023-07-31'}
// ])
//
const ElTableref =ref();
const { proxy: ctx } = getCurrentInstance() // 可以把ctx当成vue2中的this
//我们按照vue2的习惯创建一个存放数据的对象
let thiss = reactive({
    // 定义表格数据模型
    list: [],
    //当前页码
    pageNum: 1,
    //当前显示页码
    pageSize: 10,
    // 分页条总记录数
    total: 0,
    //查询问题的文本,结果刚好可以跟后台的sysRoleDto类对应
    sysRoleDto: {
        roleName: ''
    },
    //弹窗显示控制开关
    dialogVisible: false,
    centerDialogVisible: false,
    //添加数据
    uprole: {
        roleName: '',
        roleCode: '',
        description: ''
    },
    //按钮颜色
    StateButter: "#134"

})

// 组件加载钩子,也就是启动函数
onMounted(() => {
    reset()
})

//状态过滤器
const statusFilter = () => {
    // 这里是你的过滤逻辑,根据 isDeleted 字段的值进行过滤   
    return (value, row) => {
        if (+value.isDeleted === 1) {
            thiss.StateButter = "#134"
            return '✅';
        } else {
            thiss.StateButter = "#689"
            return '❌';
        }
    };
}

const reset = () => {
    // 设置为空,即为查询所有
    thiss.sysRoleDto.roleName = '';
    getrolelest();
}
//查询接口
const getrolelest = async () => {
    //从后台查到数据,匹配到参数里
    const { code, message, data } = await GetRole(thiss.pageNum, thiss.pageSize, thiss.sysRoleDto);
    +code == 200 ? ctx.$message.success('列表刷新:' + message) : ctx.$message.error('列表刷新:' + message)
    thiss.list = data.list
    thiss.pageNum = data.pageNum
    thiss.pageSize = data.pageSize
    thiss.total = data.total
}
//弹窗打开方法
const addShow = () => {
    thiss.dialogVisible = true
    //清空
    thiss.uprole = {
        roleName: '',
        roleCode: '',
        description: ''
    }
}
//添加接口
const upRole = async () => {
    console.log(thiss.sysRoleDto.roleName);
    //从后台查到数据,匹配到参数里
    //!thiss.uprole.id等同于:thiss.uprole.id == null
    //判断是修改还是添加
    const { code, message, data } = !thiss.uprole.id ? await SaveSysRole(thiss.uprole) : await aMendRole(thiss.uprole);

    if (+code == 200) {
        ctx.$message.success(message)
        //关闭弹窗
        thiss.dialogVisible = false
        //刷新界面
        getrolelest();
    } else {
        ctx.$message.error(message)
    }

}


//修改
const amends = information => {
    console.log(information);
    //打开弹窗
    addShow()
    //获取本列数据,传递给弹窗
    thiss.uprole = information.row
}

//修改状态接口
const setRoleState = async information => {
    const { code } = await aMendRoleState(information.row.id, information.row.isDeleted)
    if (+code === 200) {
        ElMessage.success('修改成功')
        //刷新界面
        getrolelest();
    }
}

//删除
const deletes = information => {
    ElMessageBox.confirm('此操作将永久删除该记录, 是否继续?', 'Warning', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
    }).then(async () => {
        const { code } = await DeleteSysRoleById(information.row.id)
        if (code === 200) {
            //刷新界面
            getrolelest();
            ElMessage.success('删除成功')
        }
    })
}

//批量删除
const Statebutter = () => {
    ElMessageBox.confirm('此操作将永久删除, 是否继续?', 'Warning', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
    }).then(async () => {
        const { code } = await DeleteSysRoleByIdList(ElTableref.value.getSelectionRows())
        if (code === 200) {
            //刷新界面
            getrolelest();
            ElMessage.success('删除成功')
        }
    })
  
}


</script>

<style scoped>
.search-div {
    margin-bottom: 10px;
    padding: 10px;
    border: 1px solid #ebeef5;
    border-radius: 3px;
    background-color: #fff;
}

.tools-div {
    margin: 10px 0;
    padding: 10px;
    border: 1px solid #ebeef5;
    border-radius: 3px;
    background-color: #fff;
}
</style>


<!-- 

{
    "code": 200,
    "message": "操作成功",
    "data": {
        "total": 7,
        "list": [
            {
                "id": 39,
                "createTime": "2023-09-04 02:04:41",
                "updateTime": "2023-09-04 02:30:01",
                "isDeleted": 1,
                "roleName": "运维人员",
                "roleCode": "yw",
                "description": null
            },
            {
                "id": 38,
                "createTime": "2023-09-03 15:24:32",
                "updateTime": "2023-09-04 02:30:06",
                "isDeleted": 1,
                "roleName": "开发人员",
                "roleCode": "dev",
                "description": null
            }
        ],
        "pageNum": 1,
        "pageSize": 2,
        "size": 2,
        "startRow": 1,
        "endRow": 2,
        "pages": 4,
        "prePage": 0,
        "nextPage": 2,
        "isFirstPage": true,
        "isLastPage": false,
        "hasPreviousPage": false,
        "hasNextPage": true,
        "navigatePages": 8,
        "navigatepageNums": [
            1,
            2,
            3,
            4
        ],
        "navigateFirstPage": 1,
        "navigateLastPage": 4
    }
}

 -->

 

role.js
 //引入统一请求封装
import request from '@/utils/request'
//api接口常量
const url = '/zx/admin/system/role'
// 登录接口
export const GetRole = (page,quantity,datas) => {
  return request({
    //在js里`表示字符串拼接${}是个表达式
    url: `${url}/getRoleList/${page}/${quantity}`,
    method: 'post',
    //如果后端是@RequestBody则这里要加data
    //否则是 params
    data : datas ,
  })
}

// 添加角色请求方法
export const SaveSysRole = (data) => {
  return request({
      url: `${url}/DataUpRole`,
      method: 'post',
      data
  })
}

// 修改角色请求方法
export const aMendRole = (data) => {
  return request({
      url: `${url}/aMendRole`,
      method: 'put',
      data
  })
}
// 修改角色请求方法
export const aMendRoleState = (roleId,roleState) => {
  return request({
      url: `${url}/aMendRoleState/${roleId}/${roleState}`,
      method: 'get',
  })
}


// 删除角色
export const DeleteSysRoleById = (roleId) => {
    return request({
        url: `${url}/DeleteSysRoleById/${roleId}`,
        method: 'delete'
    })
}


// 批量删除角色
export const DeleteSysRoleByIdList = (rolelist) => {
  return request({
      url: `${url}/DeleteSysRoleByIdList`,
      method: 'delete',
      data : rolelist 
  })
}

 

补充:

1. 获取控件的所有属性,这里是获取每个迭代列的属性

 Vue3 动态列 <el-table-column> 实现 formatter 的两种方法_eltablecolumn formatter-CSDN博客

 

 

 

-------用户管理-------

用户管理和角色管理一样,只是换了一个数据库表,  多了个上传头像, 就不细讲了,只说关键的

 

1.准备工作

创建:

编写界面:

代码
 <template>

    <!---搜索表单-->
    <div class="search-div">
        <el-form label-width="70px" size="small">
            <el-row>
                <el-col :span="12">
                <el-form-item label="关键字">
                    <el-input
                    style="width: 100%"
                    placeholder="用户名"
                    ></el-input>
                </el-form-item>
                </el-col>
                <el-col :span="12">
                <el-form-item label="创建时间">
                    <el-date-picker
                    type="daterange"
                    range-separator="To"
                    start-placeholder="开始时间"
                    end-placeholder="结束时间"
                    format="YYYY-MM-DD"
                    value-format="YYYY-MM-DD"
                    />
                </el-form-item>
                </el-col>
            </el-row>
            <el-row style="display:flex">
                <el-button type="primary" size="small">
                搜索
                </el-button>
                <el-button size="small">重置</el-button>
            </el-row>
        </el-form>
    </div>

    <!--添加按钮-->
    <div class="tools-div">
        <el-button type="success" size="small">添 加</el-button>
    </div>

    <!---数据表格-->
    <el-table :data="list" style="width: 100%">
        <el-table-column prop="userName" label="用户名" />
        <el-table-column prop="name" label="姓名" />
        <el-table-column prop="phone" label="手机" />
        <el-table-column prop="avatar" label="头像" #default="scope">
            <img :src="scope.row.avatar" width="50" />
        </el-table-column>
        <el-table-column prop="description" label="描述" />
        <el-table-column prop="status" label="状态" #default="scope">
            {{ scope.row.status == 1 ? '正常' : '停用' }}
        </el-table-column>
        <el-table-column prop="createTime" label="创建时间" />
        <el-table-column label="操作" align="center" width="280" >
            <el-button type="primary" size="small">
                修改
            </el-button>
            <el-button type="danger" size="small">
                删除
            </el-button>
            <el-button type="warning" size="small">
                分配角色
            </el-button>
        </el-table-column>
    </el-table>

    <el-pagination
        :page-sizes="[10, 20, 50, 100]"
        layout="total, sizes, prev, pager, next"
        :total="total"
    />

</template>

<script setup>
import { ref } from 'vue'; 

// 表格数据模型
const list = ref([
    {"id":1 , "userName":"admin" , "name": "admin" , "phone":"13121034567" , "status":1 , "createTime": "2023-05-11"} ,
    {"id":2 , "userName":"admin" , "name": "admin" , "phone":"13121034567" , "status":1 , "createTime": "2023-05-11"} 
]);

// 分页条数据模型
const total = ref(0)

</script>

<style scoped>
.search-div {
  margin-bottom: 10px;
  padding: 10px;
  border: 1px solid #ebeef5;
  border-radius: 3px;
  background-color: #fff;
}
.tools-div {
  margin: 10px 0;
  padding: 10px;
  border: 1px solid #ebeef5;
  border-radius: 3px;
  background-color: #fff;
}
.avatar-uploader .avatar {
  width: 178px;
  height: 178px;
  display: block;
}
.avatar-uploader .el-upload {
  border: 1px dashed var(--el-border-color);
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: var(--el-transition-duration-fast);
}
.avatar-uploader .el-upload:hover {
  border-color: var(--el-color-primary);
}
.el-icon.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  text-align: center;
}
</style>

 

2.前端代码

界面
 <template>
    <!---搜索表单-->
    <div class="search-div">
        <el-form label-width="70px" size="small">
            <el-row>
                <el-col :span="12">
                    <el-form-item label="关键字">
                        <el-input style="width: 100%" placeholder="用户名" v-model="thiss.SysUserDto.keyword"></el-input>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="创建时间">
                        <el-date-picker 
                            v-model="thiss.createTimes"
                            type="daterange" 
                            range-separator="To" 
                            start-placeholder="开始时间"
                            end-placeholder="结束时间" 
                            format="YYYY-MM-DD" 
                            value-format="YYYY-MM-DD" />
                    </el-form-item>
                </el-col>
            </el-row>
            <el-row style="display:flex">
                <el-button type="primary" size="small" @click="gerthiss">
                    搜索
                </el-button>
                <el-button size="small" @click="breaks">重置</el-button>
            </el-row>
        </el-form>
    </div>

    <!--添加按钮-->
    <div class="tools-div">
        <el-button type="success" size="small" @click="newUsers">添 加</el-button>
    </div>

    <el-dialog v-model="thiss.dialogVisible" title="添加或修改" width="40%">
        <el-form label-width="120px">
            <el-form-item label="用户名">
                <el-input v-model="thiss.user.username"/>
            </el-form-item>
            <el-form-item label="密码">
                <el-input type="password" show-password v-model="thiss.user.password"/>
            </el-form-item>
            <el-form-item label="姓名">
                <el-input v-model="thiss.user.name"/>
            </el-form-item>
            <el-form-item label="手机">
                <el-input v-model="thiss.user.phone"/>
            </el-form-item>
                <el-form-item label="头像">
                    <!-- 获取请求token上传图片 -->
                    <el-upload
                        class="avatar-uploader"
                        action="http://localhost:10001/admin/system/user/userTX"
                        :show-file-list="false"
                        :on-success="handleAvatarSuccess"
                        :headers="headers"
                        >  
                        <img v-if="thiss.user.avatar" :src="thiss.user.avatar" class="avatar" />
                        <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
                    </el-upload>
                </el-form-item>
            <el-form-item label="描述">
                <el-input  v-model="thiss.user.description"/>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="newandup">提交</el-button>
                <el-button @click="thiss.dialogVisible = false">取消</el-button>
            </el-form-item>
        </el-form>
    </el-dialog> 

    <!---数据表格-->
    <el-table :data="thiss.userl" style="width: 100%">
        <el-table-column type=index label="序列"  width="80"/>
        <el-table-column prop="id" label="ID" />
        <el-table-column prop="username" label="用户名" />
        <el-table-column prop="name" label="姓名" />
        <el-table-column prop="phone" label="手机" />
        <el-table-column prop="avatar" label="头像" #default="scope">
            <img :src="scope.row.avatar" width="50" />
        </el-table-column>
        <el-table-column prop="description" label="描述" />
        <el-table-column prop="status" label="状态" #default="scope">
            {{ scope.row.status == 1 ? '正常' : '停用' }}
        </el-table-column>
        <el-table-column prop="createTime" label="创建时间" />
        <el-table-column label="操作" align="center" width="280" #default="scope">
            <el-button type="primary" size="small" @click="dataUpUsers(scope)">
                修改
            </el-button>
            <el-button type="danger" size="small" @click="deleteUsers(scope)"> 
                删除
            </el-button>
            <el-button type="warning" size="small" @click="fpjs(scope)">
                分配角色
            </el-button>
        </el-table-column>
    </el-table>

  <!-- 分配弹窗 -->
    <el-dialog v-model="thiss.dialogRoleVisible" title="分配角色" width="40%">
    <el-form label-width="80px">
        <el-form-item label="用户名">
            <el-input disabled :value="thiss.user.username"></el-input>
        </el-form-item>

        <el-form-item label="角色列表">
            <el-checkbox-group v-model="thiss.userRoleId">
                <el-checkbox v-for="role in thiss.userRoleIds" :key="role.id" :label="role.id">
                    {{ role.roleName }}
                </el-checkbox>
            </el-checkbox-group>
        </el-form-item>

        <el-form-item>
            <el-button type="primary" @click="fpjstj">提交</el-button>
            <el-button @click="thiss.dialogRoleVisible = false">取消</el-button>
        </el-form-item>
    </el-form>
</el-dialog>


    <!--分页条  @是绑定按钮的方法 , v-model是绑定参数-->
    <!--监听分页,换页,和字符-->
    <el-pagination :page-sizes="[10, 2, 20, 50, 100]" layout="total, sizes, prev, pager, next" 
    :total="thiss.total"
    @size-change="getrolelest"
    @current-change="getrolelest" 
    v-model:current-page="thiss.pageNum"
    v-model:page-size="thiss.pageSize" />






</template>

<script setup>
import {
    ref,
    reactive,
    getCurrentInstance,
    onMounted
} from 'vue';
import { Md5 } from 'ts-md5';
//引入api接口  
import { GetUser , newUser ,deleteUser,dataUpUser ,inUserByRole , selectUserByRole} from '@/api/user';
import { GetRole } from '@/api/role';
//token接口
import { useApp } from '@/pinia/modules/app'
const { proxy: ctx } = getCurrentInstance() // 可以把ctx当成vue2中的this
// // 表格数据模型
// const list = ref([
//     { "id": 1, "username": "admin", "name": "admin", "phone": "13121034567", "status": 1, "createTime": "2023-05-11" },
//     { "id": 2, "username": "admin", "name": "admin", "phone": "13121034567", "status": 1, "createTime": "2023-05-11" }
// ]);
// // 分页条数据模型
// const total = ref(0)

//用户相关参数
const thiss = reactive({
    //user列表
    userl:[],
    //一个user,上传使用
    user: {
        id: null,
        password:'',
        username: "",
        name: "",
        phone: "",
        avatar:"",
        status: 1
    },

    //当前页码
    pageNum: 1,
    //当前显示页码
    pageSize: 10,
    // 分页条总记录数
    total: 0,

    //搜索  -- 对于搜索接口
    SysUserDto:{
        //搜索文本
        keyword:'',
        //开始接受时间  createTimes的数组下标0是开始,1是介结束
        createTimeBegin:'',
        createTimeEnd:''
    },
    //搜索时间页面参数
    createTimes:[],
    //控制添加框框
    dialogVisible:false,
    //分配角色框框
    dialogRoleVisible:false,
    //所有分配角色列表
    userRoleIds:[],
    //选中的角色
    userRoleId:[],


})
    

// 组件加载钩子,也就是启动函数
onMounted(async () => {
    gerthiss()

    //调用角色列表接口
     const { code, message, data } = await GetRole(1, 0,{roleName: ''});
    if (+code == 200) {
        thiss.userRoleIds=data.list
        ctx.$message.success(message)
    } else {
        ctx.$message.error(message)
    }
})
//分页器按钮:
const getrolelest = () => {
    //重置搜索文本和时间
    thiss.SysUserDto. keyword = ''
    thiss.createTimes = []
    gerthiss()
}


//重置
const breaks = () => {
    //重置搜索文本和时间,和分页器
    thiss.SysUserDto. keyword = ''
    thiss.createTimes = []
    thiss.pageNum=1
    thiss.pageSize=10
    thiss.total=0
    gerthiss()
}
//搜索接口,也是列表
const gerthiss = async ()=>{
    thiss.SysUserDto.createTimeBegin = thiss.createTimes[0]
    thiss.SysUserDto.createTimeEnd   = thiss.createTimes[1]
    //发送  thiss.SysUserDto
    const { code, message, data } =  await GetUser(thiss.pageNum , thiss.pageSize,  thiss.SysUserDto)
   
    if (+code == 200) {
        //更新列表
        thiss.userl = data.list
        thiss.pageNum = data.pageNum
        thiss.pageSize = data.pageSize
        thiss.total = data.total
        ctx.$message.success(message)
    } else {
        ctx.$message.error(message)
    }
}



//删除按钮
const deleteUsers = async data =>{
    //发送取出来的  id

    const { code, message } =  await deleteUser(data.row.id)
    if (+code == 200) {
        //更新列表
        breaks()
        ctx.$message.success(message)
    } else {
        ctx.$message.error(message)
    }
}



//修啊按钮
const dataUpUsers =  data =>{
    thiss.user= data.row 
    thiss.user.password = '' 
    thiss.dialogVisible = true
}
// 添加按钮
const newUsers =  ()=>{
    thiss.user = {
        id: null,
        password:"",
        username: "",
        name: "",
        phone: "",
        status: 1
    }
    thiss.dialogVisible = true
}
//修改和,添加功能
const newandup =  async ()=>{
    // 密码转md5
    const md5 = new Md5()
    thiss.user.password =     md5.appendAsciiStr(thiss.user.password ).end()
    // 等于空是添加,不是就是修改
    const { code, message } = thiss.user.id == null ?  await newUser(thiss.user) : await dataUpUser(thiss.user)
    if (+code == 200) {
        //更新列表
        breaks()
        thiss.dialogVisible = false
        ctx.$message.success(message)
    } else {
        ctx.$message.error(message)
    }
}



const headers = {
  token: useApp().authorization.token     // 从pinia中获取token,在进行文件上传的时候将token设置到请求头中
}

// 图像上传成功以后的事件处理函数
const handleAvatarSuccess = (response, uploadFile) => {
    console.log(response.data);
    thiss.user.avatar = response.data
}



//分配角色
const fpjs = async datas =>{
    thiss.user= datas.row 
    //查询现在的分配

     const { code, message, data } = await selectUserByRole(datas.row.id);
    if (+code == 200) {
        thiss.userRoleId = data
        ctx.$message.success(message)
    } else {
        ctx.$message.error(message)
    }

    thiss.dialogRoleVisible = true
}

//分配角色提交
const fpjstj = async () =>{
//thiss.user角色信息
     const { code, message, data } = await inUserByRole(thiss.user.id , thiss.userRoleId );
    if (+code == 200) {
        ctx.$message.success(message)
    } else {
        ctx.$message.error(message)
    }


}






</script>

<style scoped>
.search-div {
    margin-bottom: 10px;
    padding: 10px;
    border: 1px solid #ebeef5;
    border-radius: 3px;
    background-color: #fff;
}

.tools-div {
    margin: 10px 0;
    padding: 10px;
    border: 1px solid #ebeef5;
    border-radius: 3px;
    background-color: #fff;
}

.avatar-uploader .avatar {
    width: 178px;
    height: 178px;
    display: block;
}

.avatar-uploader .el-upload {
    border: 1px dashed var(--el-border-color);
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    transition: var(--el-transition-duration-fast);
}

.avatar-uploader .el-upload:hover {
    border-color: var(--el-color-primary);
}

.el-icon.avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 178px;
    height: 178px;
    text-align: center;
}
</style>

<!-- 
       if (+code == 200) {
        ctx.$message.success(message)

    } else {
        ctx.$message.error(message)
    }
 -->
接口
 //引入统一请求封装
import request from '@/utils/request'
//api接口常量
const url = '/zx/admin/system/user'
// 查询接口
export const GetUser = (page,quantity,datas) => {
  return request({
    //在js里`表示字符串拼接${}是个表达式
    url: `${url}/getUserList/${page}/${quantity}`,
    method: 'post',
    //如果后端是@RequestBody则这里要加data
    //否则是 params
    data : datas ,
  })
}

// 添加角色请求方法
export const newUser = (data) => {
  return request({
      url: `${url}/newUser`,
      method: 'post',
      data
  })
}


// 修改角色请求方法
export const dataUpUser = (data) => {
  return request({
      url: `${url}/dataUpUser`,
      method: 'put',
      data
  })
}

// 删除角色
export const deleteUser = (roleId) => {
    return request({
        url: `${url}/deleteUser/${roleId}`,
        method: 'delete'
    })
}



// 查询用户分配的角色返回[]
export const selectUserByRole = (userid) => {
  return request({
      url: `${url}/selectUserByRole/${userid}`,
      method: 'get',
  })
}

//修改用户的角色信息
export const inUserByRole = (userid , userList) => {
  return request({
      url: `${url}/inUserByRole/${userid}`,
      method: 'post',
      data: userList
  })
}

 

学习技术:

  • 头像上传
  • 时间搜索
  • 分配角色的回调显示

3.后端:

controller接口
 package com.zhexin.manage.controller;

import com.github.pagehelper.PageInfo;
import com.zhexin.manage.service.SysRoleUserService;
import com.zhexin.manage.service.SysUser.*;
import com.zhexin.model.dto.system.SysUserDto;
import com.zhexin.model.entity.system.SysUser;
import com.zhexin.model.vo.common.Result;
import com.zhexin.model.vo.common.ResultCodeEnum;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@Tag(name = "角色管理接口")   //swagger文档注释
@RestController  //注册到bean
@RequestMapping("/admin/system/user")  //注册访问路径
public class SysUserController {

    @Autowired
    SysUserSelectService sysUserSelectService;
    @Autowired
    DeleteSysUserService deleteSysUserService;
    @Autowired
    NewUserService newUserService;
    @Autowired
    DataUpUserService dataUpUserService;
    @Autowired
    UserTX userTX;
    @Autowired
    SysRoleUserService sysRoleUserService;

    @Operation(summary = "用户查询接口") //文档注释
    @PostMapping("/getUserList/{page}/{quantity}")
    public Result getUserList(
            @PathVariable Integer page,
            @PathVariable Integer quantity,
            @RequestBody SysUserDto sysUserDto
    ) {

        PageInfo<SysUser> jg = sysUserSelectService.SysUserByName(page, quantity, sysUserDto);
        return Result.build(jg, ResultCodeEnum.SUCCESS);
    }

    @Operation(summary = "删除用户接口") //文档注释
    @DeleteMapping("/deleteUser/{id}")
    public Result deleteUser(@PathVariable Long id) {
        boolean jg = deleteSysUserService.DeleteSysUser(id);
        return Result.build(jg, ResultCodeEnum.SUCCESS);
    }

    @Operation(summary = "新增用户") //文档注释
    @PostMapping("/newUser")
    public Result newUser(@RequestBody SysUser sysUser) {  //请求的json参数

        boolean jg = newUserService.NewUser(sysUser);

        return Result.build(jg, ResultCodeEnum.SUCCESS);
    }


    @Operation(summary = "修改用户") //文档注释
    @PutMapping("/dataUpUser")
    public Result dataUpUser(@RequestBody SysUser sysUser) {  //请求的json参数

        boolean jg = dataUpUserService.dataUpUser(sysUser);
        return Result.build(jg, ResultCodeEnum.SUCCESS);
    }


    @Operation(summary = "上传用户头像") //文档注释
    @RequestMapping("/userTX")  //接收符合条件的任何请求 方式
    public Result<String> userTX(@RequestParam MultipartFile file) {  //这里的参数名必须是file
        String jg = userTX.FileUploader(file);
        return  Result.build(jg , ResultCodeEnum.SUCCESS) ;
    }

    @Operation(summary = "查询用户的角色信息") //文档注释
    @GetMapping("/selectUserByRole/{userid}")  //接收符合条件的任何请求 方式
    public Result<String> selectUserByRole(@PathVariable  Long userid) {
        List<Long> sysRoleUser = sysRoleUserService.selectUserByRole(userid);
        return  Result.build(sysRoleUser , ResultCodeEnum.SUCCESS) ;
    }

    @Operation(summary = "删除用户的角色信息") //文档注释
    @DeleteMapping("/deleteUserByID/{userid}")  //接收符合条件的任何请求 方式
    public Result<String> deleteUserByID(@PathVariable  Long userid) {
        boolean  sysRoleUser = sysRoleUserService.deleteUserByID(userid);
        return  Result.build(sysRoleUser , ResultCodeEnum.SUCCESS) ;
    }

    @Operation(summary = "修改新的用户角色信息") //文档注释
    @PostMapping("/inUserByRole/{userId}")  //接收符合条件的任何请求 方式
    public Result<String> inUserByRole(@PathVariable("userId") Long userId ,
                                       @RequestBody List<Long> userRoleId) {   //这里的参数名,必须与前端保持一致
        boolean jg = sysRoleUserService.inUserByRole(userId , userRoleId);
        return  Result.build(jg , ResultCodeEnum.SUCCESS) ;
    }

}

 

用户头像上传service, minio
package com.zhexin.manage.service.SysUser.Impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import com.zhexin.common.service.exception.zhexinException;
import com.zhexin.manage.properties.MinioProperties;
import com.zhexin.manage.service.SysUser.UserTX;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.errors.MinioException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;

@Service
public class UserTXImpl implements UserTX {
    @Autowired
    MinioProperties minioProperties;
    //springMVC中的文件上传工具MultipartFile
    public String FileUploader(MultipartFile multipartFile) {

        try {

            //创建连接
            MinioClient minioClient =
                    MinioClient.builder()
                            .endpoint(minioProperties.getUrl())
                            .credentials(minioProperties.getZh(), minioProperties.getMm())
                            .build();

            // 判断是否存在容器,否则创建
            boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioProperties.getRqname()).build());
            if (!found) {
                // 创建容器
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioProperties.getRqname()).build());
            }


            // 设置存储对象名称
            String dateDir = DateUtil.format(new Date(), "yyyyMMdd");
            String uuid = UUID.randomUUID().toString().replace("-", "");
            //20230801/443e1e772bef482c95be28704bec58a901.jpg
            String fileName = dateDir+"/"+uuid+multipartFile.getOriginalFilename();
            //把文件写入容器
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                            .bucket(minioProperties.getRqname())
                            .object(fileName)
                            .stream(multipartFile.getInputStream(),multipartFile.getSize(),-1)
                            .build();
            //发送
            minioClient.putObject(putObjectArgs) ;
            //要在Minio里设置这个桶的权限为public 才能访问到图片,如果访问地址得不到图片的话
            //http://127.0.0.1:9000/usertx/20231212/8f4cf52b13f74d8a87511e147c67cbedv2-4c534c96dc62e9f541473ea2f5e73fd5_r.png
            return minioProperties.getUrl() + "/" + minioProperties.getRqname() + "/" + fileName ;
        } catch (MinioException |
                 IOException |
                 NoSuchAlgorithmException |
                 InvalidKeyException |
                 MaxUploadSizeExceededException
                ee ){
            throw new zhexinException(201, "图片上传失败!大小不得超过5MB");
        }

    }
}

 

 

分配角色service
 package com.zhexin.manage.service.Impl;

import com.zhexin.common.service.exception.zhexinException;
import com.zhexin.manage.mapper.SysRoleUser.SysRoleUserMapper;
import com.zhexin.manage.service.SysRoleUserService;
import com.zhexin.model.entity.system.SysRoleUser;
import com.zhexin.model.vo.common.ResultCodeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class SysRoleUserServiceImpl implements SysRoleUserService {
    @Autowired
    SysRoleUserMapper sysRoleUserMapper;

    //查
    @Override
    public List<Long> selectUserByRole(Long userid) {
        List<Long> LongList = new ArrayList<>();  //初始化

        if (userid == null ) {   //判断不为空
            throw new zhexinException(ResultCodeEnum.CSWK);
        }

        List<SysRoleUser> sysRoleUsersList = sysRoleUserMapper.selectUserByRole(userid);  //调用查询
        sysRoleUsersList.forEach(sysRoleUser -> {
            LongList .add(sysRoleUser.getRoleId());
        });
        return LongList;
    }

    //添加
    @Override
    public boolean inUserByRole(Long userId, List<Long> roleidl) {
        //初始化
        boolean jg = false;
        if (roleidl.isEmpty() || userId == null ) {   //判断不为空
            throw new zhexinException(ResultCodeEnum.CSWK);
        }

        //查询原先userid的id
        List<SysRoleUser> yid = sysRoleUserMapper.selectUserByRole(userId);

        if (!yid.isEmpty()) {   //判断不为空
            //删除所有原先的  -- id
            boolean deleteJg = this.deleteUserByID(userId);
            if (!deleteJg) {
                throw new zhexinException(ResultCodeEnum.CSWK);
            }
        }


        //放入最新的   -- list
        for (Long roleid : roleidl) {
            jg = sysRoleUserMapper.inUserByRole(userId, roleid);
            if (!jg) {
                throw new zhexinException(ResultCodeEnum.CSWK);
            }
        }
        return jg;
    }

    @Override
    public boolean deleteUserByID(Long userid) {
        boolean jg = false;  //初始化
        if (userid == null ) {   //判断不为空
            throw new zhexinException(ResultCodeEnum.CSWK);
        }

        jg = sysRoleUserMapper.deleteUserByID(userid);
        return jg;
    }
}
分配角色mapper
 <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--对应的接口别弄错了-->
<mapper namespace="com.zhexin.manage.mapper.SysRoleUser.SysRoleUserMapper">
    <!--映射到SysRole里面, autoMapping为自动映射-->

    <select id="selectUserByRole" resultType="com.zhexin.model.entity.system.SysRoleUser"  >
        SELECT role_id roleId , user_id userId
        FROM sys_user_role
        WHERE user_id = #{userid}
    </select>

    <insert id="inUserByRole" parameterType="long">
        INSERT INTO sys_user_role
        (role_id,  user_id )
        VALUES
        (#{roleId}, #{userId} )
    </insert>

    <delete id="deleteUserByID" >
        DELETE FROM sys_user_role WHERE user_id = #{userid}
    </delete>
</mapper>

 

 

 

 

-------菜单管理-------

后端主要代码:

1.递归实现分类列表格式:

 

工具类
 package com.zhexin.common.uilt.Service;

import com.zhexin.model.entity.system.SysMenu;

import java.util.ArrayList;
import java.util.List;

public class MenuHelper {
    private List<SysMenu> menuLs = null;
    /**
     * 使用递归方法建菜单
     * @param sysMenuList
     * @return
     */
    public static List<SysMenu> buildTree(List<SysMenu> sysMenuList) {
        List<SysMenu> trees = new ArrayList<>();
        for (SysMenu sysMenu : sysMenuList) {
            if (sysMenu.getParentId().longValue() == 0) {
                trees.add(findChildren(sysMenu,sysMenuList));
            }
        }
        return trees;
    }

    /**
     * 递归查找子节点
     * @param treeNodes
     * @return
     */
    public static SysMenu findChildren(SysMenu sysMenu, List<SysMenu> treeNodes) {
        sysMenu.setChildren(new ArrayList<SysMenu>());
        for (SysMenu it : treeNodes) {
            if(sysMenu.getId().longValue() == it.getParentId().longValue()) {
                //if (sysMenu.getChildren() == null) {
                //    sysMenu.setChildren(new ArrayList<>());
                //}
                sysMenu.getChildren().add(findChildren(it,treeNodes));
            }
        }
        return sysMenu;
    }






    //递归查询列表开始
    public static List<SysMenu> selectList(List<SysMenu> sysMenuList){
        //初始化结果容器
        List<SysMenu> wai  = new ArrayList<>();
        //循环所有,作为子列表
        for (SysMenu zmenuL : sysMenuList) {
            if (zmenuL.getParentId() == 0) { //第一列
                //添加2级的子列
                selectSysMenu(zmenuL ,sysMenuList);
                //加入队列
                wai.add(zmenuL);
            }
        }
        return wai;
    }


    private static SysMenu selectSysMenu(SysMenu fsysMenu ,List<SysMenu> sysMenuList){
        if (fsysMenu.getChildren() == null){
            //初始化容器
            fsysMenu.setChildren(new ArrayList<>());
        }
        //循环所有,作为子列表
        for (SysMenu zmenuL : sysMenuList) {
            if (zmenuL.getParentId() == fsysMenu.getId()) {
                //递归添加X级的子列
                selectSysMenu(zmenuL,sysMenuList);
                //加入X队列
                fsysMenu.getChildren().add(zmenuL);
            }
        }
        //本次递归的层级
        return fsysMenu;
    }
}
service
 package com.zhexin.manage.service.sysMenu.Impl;

import cn.hutool.core.util.StrUtil;
import com.zhexin.common.service.exception.zhexinException;
import com.zhexin.common.uilt.Service.MenuHelper;
import com.zhexin.common.uilt.Thread.ThreadlocalUser;
import com.zhexin.manage.mapper.sysMenu.sysMenuMapper;
import com.zhexin.manage.service.sysMenu.sysMenuService;
import com.zhexin.model.entity.system.SysMenu;
import com.zhexin.model.entity.system.SysUser;
import com.zhexin.model.vo.common.ResultCodeEnum;
import com.zhexin.model.vo.system.SysMenuVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.LinkedList;
import java.util.List;

@Service
public class sysMenuServiceImpl implements sysMenuService {
    @Autowired
    private sysMenuMapper sysMenuMapper;

    //递归查询列表开始
    @Override
    public List<SysMenu> selectList(){
        //初始化结果容器
        List<SysMenu> wai  = null;
        //数据库查询结果
        List<SysMenu> sysMenus = sysMenuMapper.selectSysMenu();
        //循环所有,作为子列表
        wai = MenuHelper.selectList(sysMenus);
        return wai;
    }

    @Override
    public void save(SysMenu sysMenu) {
        if (sysMenu.getStatus() == null ||
            sysMenu.getSortValue() == null ||
            StrUtil.isEmpty(sysMenu.getTitle()) ||
            StrUtil.isEmpty(sysMenu.getComponent())  ){
            throw new zhexinException(ResultCodeEnum.CSCW);
        }
        // 添加新的节点
        sysMenuMapper.insert(sysMenu) ;

        // 新添加一个菜单,那么此时就需要将该菜单所对应的父级菜单设置为半开
        updateSysRoleMenuIsHalf(sysMenu) ;
    }

    @Override
    public void updateById(SysMenu sysMenu) {
        sysMenuMapper.updateById(sysMenu) ;
    }

    @Override
    public void removeById(Long id) {
        int count = sysMenuMapper.countByParentId(id);  // 先查询是否存在子菜单,如果存在不允许进行删除
        if (count > 0) {
            throw new zhexinException(ResultCodeEnum.NODE_ERROR);
        }
        sysMenuMapper.deleteById(id);		// 不存在子菜单直接删除
    }

    @Override
    public List<SysMenuVo> findUserMenuList() {

        SysUser sysUser = ThreadlocalUser.getSysUser();
        Long userId = sysUser.getId();          // 获取当前登录用户的id

        List<SysMenu> sysMenuList = sysMenuMapper.selectListByUserId(userId) ;

        //构建树形数据
        List<SysMenu> sysMenuTreeList = MenuHelper.buildTree(sysMenuList);
        return this.buildMenus(sysMenuTreeList);
    }

    // 将List<SysMenu>对象转换成List<SysMenuVo>对象
    private List<SysMenuVo> buildMenus(List<SysMenu> menus) {

        List<SysMenuVo> sysMenuVoList = new LinkedList<SysMenuVo>();
        for (SysMenu sysMenu : menus) {
            SysMenuVo sysMenuVo = new SysMenuVo();
            sysMenuVo.setTitle(sysMenu.getTitle());
            sysMenuVo.setName(sysMenu.getComponent());
            List<SysMenu> children = sysMenu.getChildren();
            if (!CollectionUtils.isEmpty(children)) {
                sysMenuVo.setChildren(buildMenus(children));
            }
            sysMenuVoList.add(sysMenuVo);
        }
        return sysMenuVoList;
    }




    private void updateSysRoleMenuIsHalf(SysMenu sysMenu) {

        // 查询是否存在父节点
        SysMenu parentMenu = sysMenuMapper.selectById(sysMenu.getParentId());

        if(parentMenu != null) {

            // 将该id的菜单设置为半开
            sysMenuMapper.updateSysRoleMenuIsHalf(parentMenu.getId()) ;

            // 递归调用
            updateSysRoleMenuIsHalf(parentMenu) ;

        }

    }

}

 

2.多表查询sql

 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--对应的接口别弄错了-->
<mapper namespace="com.zhexin.manage.mapper.sysMenu.sysMenuMapper">

    <resultMap id="sysMenuMap" type="com.zhexin.model.entity.system.SysMenu" autoMapping="true"></resultMap>
    <!-- 用于select查询公用抽取的列 -->
    <sql id="columns">
        id,parent_id,title,component,sort_value,status,create_time,update_time,is_deleted
    </sql>
    <select id="selectSysMenu" resultType="com.zhexin.model.entity.system.SysMenu">
        SELECT * FROM sys_menu
        WHERE   is_deleted = 0
    </select>

    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        insert into sys_menu
            (id, parent_id, title, component, sort_value, status)
        values
            (#{id}, #{parentId}, #{title}, #{component}, #{sortValue}, #{status})
    </insert>

    <update id="updateById" >
        update sys_menu set
        <if test="parentId != null and parentId != ''">
            parent_id = #{parentId},
        </if>
        <if test="title != null and title != ''">
            title = #{title},
        </if>
        <if test="component != null and component != ''">
            component = #{component},
        </if>
        <if test="sortValue != null">
            sort_value = #{sortValue},
        </if>
        <if test="status != null">
            status = #{status},
        </if>
        update_time =  now()
        where
        id = #{id}
    </update>



    <select id="countByParentId" resultType="Integer">
        select count(id)
        from sys_menu
        where
            parent_id = #{parentId}
          and is_deleted = 0
    </select>

    <update id="deleteById">
        update sys_menu set
                            update_time = now() ,
                            is_deleted = 1
        where
            id = #{id}
    </update>

    <select id="selectById" resultMap="sysMenuMap">
        select <include refid="columns" /> from sys_menu where id = #{id}
    </select>



    <select id="updateSysRoleMenuIsHalf">
        update sys_role_menu srm set srm.is_half = 1 where menu_id = #{menuId}
    </select>

    <select id="selectListByUserId" resultMap="sysMenuMap">
        SELECT DISTINCT m.* FROM sys_menu m
                                     INNER JOIN sys_role_menu rm ON rm.menu_id = m.id
                                     INNER JOIN sys_user_role ur ON ur.role_id = rm.role_id
        WHERE ur.user_id=#{userId} and m.is_deleted = 0
    </select>

</mapper>

 用户菜单:

Mapper
 <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhexin.manage.mapper.Sysjh.SysRoleMenuMapper">

    <select id="findSysRoleMenuByRoleId" resultType="long">
        select menu_id
        from sys_role_menu
        where role_id = #{roleId}
        and is_deleted = 0
        and is_half = 0
    </select>


    <delete id="deleteByRoleId">
        delete from sys_role_menu where role_id = #{roleId}
    </delete>

    <insert id="doAssign">
        insert into sys_role_menu (
        role_id,menu_id,create_time , update_time , is_deleted , is_half
        ) values
        <foreach collection="menuIdList" item="menuInfo" separator=",">
            (#{roleId} , #{menuInfo.id} , now() , now() , 0 , #{menuInfo.isHalf})
        </foreach>
    </insert>
</mapper>
service
package com.zhexin.manage.service.Sysjh.Impl;

import com.zhexin.common.uilt.Service.MenuHelper;
import com.zhexin.manage.mapper.Sysjh.SysRoleMenuMapper;
import com.zhexin.manage.mapper.sysMenu.sysMenuMapper;
import com.zhexin.manage.service.Sysjh.SysRoleMenuService;
import com.zhexin.model.dto.system.AssginMenuDto;
import com.zhexin.model.entity.system.SysMenu;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
public class SysRoleMenuServiceImpl implements SysRoleMenuService {

    @Autowired
    private sysMenuMapper sysMenuMapper;

    @Autowired
    private SysRoleMenuMapper sysRoleMenuMapper;

    @Override
    public Map<String, Object> findSysRoleMenuByRoleId(Long roleId) {

        // 查询所有的菜单数据
        List<SysMenu> sysMenuList = sysMenuMapper.selectSysMenu();
        //列表加工
        sysMenuList = MenuHelper.selectList(sysMenuList);
        // 查询当前角色的菜单数据
        List<Long> roleMenuIds = sysRoleMenuMapper.findSysRoleMenuByRoleId(roleId) ;

        // 将数据存储到Map中进行返回
        Map<String , Object> result = new HashMap<>() ;
        result.put("sysMenuList" , sysMenuList) ;
        result.put("roleMenuIds" , roleMenuIds) ;

        // 返回
        return result;
    }

    @Transactional
    @Override
    public void doAssign(AssginMenuDto assginMenuDto) {

        // 根据角色的id删除其所对应的菜单数据
        sysRoleMenuMapper.deleteByRoleId(assginMenuDto.getRoleId());

        // 获取菜单的id
        List<Map<String, Number>> menuInfo = assginMenuDto.getMenuIdList();
        if(menuInfo != null && menuInfo.size() > 0) {
            sysRoleMenuMapper.doAssign(assginMenuDto) ;
        }

    }
}

Controller

controller
 package com.zhexin.manage.controller;

import com.zhexin.manage.service.sysMenu.sysMenuService;
import com.zhexin.model.entity.system.SysMenu;
import com.zhexin.model.vo.common.Result;
import com.zhexin.model.vo.common.ResultCodeEnum;
import com.zhexin.model.vo.system.SysMenuVo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;


@Tag(name = "用户接口")   //swagger文档注释
@RestController  //注册到bean
@RequestMapping("/admin/system/menu")  //注册访问路径
public class SysMenuController {

    @Autowired
    private sysMenuService sysMenuService;



    @Operation(summary = "获取树形列表") //文档注释
    @GetMapping("/getlist")
    public Result getlist(){
        List<SysMenu> sysMenus = sysMenuService.selectList();
        return Result.build(sysMenus , ResultCodeEnum.SUCCESS);
    }


    @Operation(summary = "添加子菜单") //文档注释
    @PostMapping("/save")
    public Result save(@RequestBody SysMenu sysMenu) {
        sysMenuService.save(sysMenu);
        return Result.build(null , ResultCodeEnum.SUCCESS) ;
    }
    @Operation(summary = "修改菜单") //文档注释
    @PutMapping("/updateById")
    public Result updateById(@RequestBody SysMenu sysMenu) {
        sysMenuService.updateById(sysMenu);
        return Result.build(null , ResultCodeEnum.SUCCESS) ;
    }


    @Operation(summary = "删除菜单") //文档注释
    @DeleteMapping("/removeById/{id}")
    public Result removeById(@PathVariable Long id) {
        sysMenuService.removeById(id);
        return Result.build(null , ResultCodeEnum.SUCCESS) ;
    }

    @Operation(summary = "动态菜单") //文档注释
    @GetMapping("/menus")
    public Result menus() {
        List<SysMenuVo> sysMenuVoList =  sysMenuService.findUserMenuList() ;
        return Result.build(sysMenuVoList , ResultCodeEnum.SUCCESS) ;
    }

}

 

 还有一部分在之前的写的controller里,实现分配角色或者分配菜单,都是普通的增删改查,就不必显示了

 

 前端:

由于需要分配角色,所以前面的代码都有所改变:

首先修改路由,静态路由改成动态路由:

 

 menu.js

修改/src/api/menu.js中的请求地址,如下所示:

// 获取菜单
export const GetMenus = params => {
  return request({
    url: '/admin/system/index/menus',
    method: 'get',
    params,
  })
}

index.js

更改src/router/index.js固定参数和异步菜单的路由加载:

// 固定菜单
export const fixedRoutes = [...home]
​
// 动态菜单
export const asyncRoutes = [...system]

menu.js

修改pinia/modules/menu.js中的generateMenus方法,注释掉方式一菜单加载,打开方式二菜单加载。

const generateMenus = async () => {
    // // 方式一:只有固定菜单
    // const menus = getFilterMenus(fixedRoutes)
    // setMenus(menus)
​
    // 方式二:有动态菜单
    // 从后台获取菜单
    const { code, data } = await GetMenus()
​
    if (+code === 200) {
        // 添加路由之前先删除所有动态路由
        asyncRoutes.forEach(item => {
            router.removeRoute(item.name)
        })
        // 过滤出需要添加的动态路由
        const filterRoutes = getFilterRoutes(asyncRoutes, data)
        filterRoutes.forEach(route => router.addRoute(route))
​
        // 生成菜单
        const menus = getFilterMenus([...fixedRoutes, ...filterRoutes])
        setMenus(menus)
    }
}

 

 

 

 界面:

sysMenu
 <template>
    <div class="tools-div">
        <el-button type="success" size="small" @click="addShow">添 加</el-button>
    </div>

    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="30%">
        <el-form label-width="120px">
            <el-form-item label="菜单标题">
                <el-input v-model="sysMenu.title"/>
            </el-form-item>
            <el-form-item label="路由名称">
                <el-input v-model="sysMenu.component"/>
            </el-form-item>
            <el-form-item label="排序" >
                <el-input v-model="sysMenu.sortValue"/>
            </el-form-item>
            <el-form-item label="状态">
                <el-radio-group v-model="sysMenu.status">
                    <el-radio :label="1">正常</el-radio>
                    <el-radio :label="0">停用</el-radio>
                </el-radio-group>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="saveOrUpdate">提交</el-button>
                <el-button @click="dialogVisible = false">取消</el-button>
            </el-form-item>
        </el-form>
    </el-dialog>
    <el-table
        :data="list"
        style="width: 100%; margin-bottom: 20px"
        row-key="id"
        border
        default-expand-all
    >
    <el-table-column prop="title" label="菜单标题" />
    <el-table-column prop="component" label="路由名称" />
    <el-table-column prop="sortValue" label="排序" />
    <el-table-column prop="status" label="状态" #default="scope">
      {{ scope.row.status == 1 ? '正常' : '停用' }}
    </el-table-column>
    <el-table-column prop="createTime" label="创建时间" />

    <el-table-column label="操作" align="center" width="280" #default="scope" >
        <el-button type="success" size="small" @click="addShow(scope.row)">
            添加下级节点
        </el-button>
        <el-button type="primary" size="small" @click="editShow(scope.row)">
            修改
        </el-button>
        <el-button type="danger" size="small" @click="remove(scope.row.id)">
            删除
        </el-button>
    </el-table-column>
  </el-table>
</template>

<script setup>
//引入调用的方法
import { ref , onMounted } from "vue"
import { FindNodes , SaveMenu , UpdateSysMenuById ,RemoveSysMenuById } from '@/api/sysMenu'
import { ElMessage, ElMessageBox } from 'element-plus'

// 定义表格数据模型
const list = ref([])

// 定义添加表单菜单表单相关数据模型
const dialogTitle = ref('添加')
const dialogVisible = ref(false)

//页面表单数据
const defaultForm = {
    id: '',
    parentId: 0,
    title: '',
    url: '',
    component: '',
    icon: '',
    sortValue: 1,
    status: 1,
}
// 表单响应式数据模型对象
const sysMenu = ref(defaultForm)

//=======================加载数据=========================
onMounted(() => {
    fetchData()
})

//=======================添加和修改功能====================
//进入添加
const addShow = (row) => {
  sysMenu.value = {}
  dialogVisible.value = true
  if(!row.id) {
    dialogTitle.value = '添加'
  }else {
    dialogTitle.value = '添加下级节点'
    sysMenu.value.parentId = row.id
  }
}

//进入修改
const editShow = row => {
  sysMenu.value = row
  dialogVisible.value = true
}

//提交保存与修改
const saveOrUpdate = () => {
    if (!sysMenu.value.id) {
        if(!sysMenu.value.parentId) {
            sysMenu.value.parentId = 0
        }
        saveData()
    }  else {
        updateData()
    }
}

// 修改
const updateData = async () => {
  await UpdateSysMenuById(sysMenu.value)
  dialogVisible.value = false
  ElMessage.success('操作成功')
  fetchData()
}

// 新增
const saveData = async () => {
    const { code, message } =   await SaveMenu(sysMenu.value)
    if (+code == 200) {
        //更新列表
        dialogVisible.value = false
        fetchData()
        ElMessage.success(message)
    } else {
        ElMessage.error(message)
    }
}

//=======================分页列表====================
const fetchData = async () => {
  const { code, data, message } = await FindNodes()
  list.value = data
}

//=======================删除功能====================
const remove = async id => {
  console.log('removeDataById:' + id)
  ElMessageBox.confirm('此操作将永久删除该记录, 是否继续?', 'Warning', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning',
  }).then(async () => {
      const { code , message } = await RemoveSysMenuById(id)
      if(code === 200) {
        ElMessage.success('删除成功')
        fetchData()
      }else {
        ElMessage.error(message)
      }     
    })
}
</script>

<style scoped>
.search-div {
  margin-bottom: 10px;
  padding: 10px;
  border: 1px solid #ebeef5;
  border-radius: 3px;
  background-color: #fff;
}
.tools-div {
  margin: 10px 0;
  padding: 10px;
  border: 1px solid #ebeef5;
  border-radius: 3px;
  background-color: #fff;
}
</style>
sysRole
 <template>
    <div class="search-div">
        <!-- 搜索表单 -->
        <el-form label-width="70px" size="small">
            <el-form-item label="角色名称">
                <el-input style="width: 100%" placeholder="角色名称" v-model="thiss.sysRoleDto.roleName"></el-input>
            </el-form-item>
            <el-row style="display:flex">
                <el-button type="primary" size="small" @click="getrolelest()">
                    搜索
                </el-button>
                <el-button size="small" @click=" reset()">重置</el-button>
            </el-row>
        </el-form>

        <!-- 添加按钮 -->
        <div class="tools-div">
            <el-button type="success" size="small" @click="addShow">添 加</el-button>
            <el-button type="danger" size="small" @click="Statebutter()">批量删除</el-button>
        </div>
        <!-- 添加角色表单对话框 -->
        <el-dialog v-model="thiss.dialogVisible" title="添加或修改角色" width="30%">
            <el-form label-width="120px">
                <el-form-item label="角色名称">
                    <el-input v-model="thiss.uprole.roleName" />
                </el-form-item>
                <el-form-item label="角色Code">
                    <el-input v-model="thiss.uprole.roleCode" />
                </el-form-item>
                <el-form-item label="描述">
                    <el-input v-model="thiss.uprole.description" />
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="upRole">提交</el-button>
                    <el-button @click="thiss.dialogVisible = false">取消</el-button>
                </el-form-item>
            </el-form>
        </el-dialog>



        <!--- 角色表格数据 -->
        <el-table :data="thiss.list" ref="ElTableref" style="width: 100%">
            <el-table-column type="selection" width="55" />
            <el-table-column type="index" label="序列" width="80" />
            <el-table-column prop="id" label="用户ID" :sortable="true" />
            <el-table-column property="roleName" label="角色名称" width="180" />
            <el-table-column prop="roleCode" label="角色code" width="180" />
            <el-table-column prop="description" label="描述" />
            <el-table-column prop="createTime" label="创建时间" :sortable="true" />
            <el-table-column prop="updateTime" label="更新时间" :sortable="true" />
            <el-table-column prop="isDeleted" label="状态" :formatter="statusFilter()" /> <!-- 过滤器 -->
            <el-table-column label="操作" align="center" width="280" #default="scope"> <!-- #default 当前组件-->
                <el-button type="primary" size="small" @click="amends(scope)">
                    修改
                </el-button>
                <el-button  size="small" :color="thiss.StateButter" @click="setRoleState(scope)">
                    {{ scope.row.isDeleted == 1 ? '禁用' : '启用' }}
                </el-button>
                <el-button type="danger" size="small" @click="deletes(scope)">
                    删除
                </el-button>
                <el-button type="warning" size="small" @click="showAssignMenu(scope.row)">
                分配菜单
            </el-button>
            </el-table-column>
        </el-table>









        <!-- 分配菜单的对话框 
        // tree组件添加ref属性,后期方便进行tree组件对象的获取
           default-expand-all
        -->
        <el-dialog v-model="dialogMenuVisible" title="分配菜单" width="40%">
            <el-form label-width="80px">
                <el-tree
                        :data="sysMenuTreeList"
                        ref="tree"   
                        show-checkbox
                        default-expand-all
                        :check-on-click-node="true"
                        node-key="id"
                        :props="defaultProps"
                />
                <el-form-item>
                    <el-button type="primary" @click="doAssign">提交</el-button>
                    <el-button @click="dialogMenuVisible = false">取消</el-button>
                </el-form-item>
            </el-form>
        </el-dialog>




        <!--分页条-->
        <!--监听分页,换页,和字符-->
        <el-pagination :page-sizes="[10, 2, 20, 50, 100]" layout="total, sizes, prev, pager, next" :total="thiss.total"
            @size-change="getrolelest" @current-change="getrolelest" v-model:current-page="thiss.pageNum"
            v-model:page-size="thiss.pageSize" />
    </div>
</template>

<script setup>
import {
    ref,
    reactive,
    onMounted,
    getCurrentInstance
} from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus'
import { GetRole,
        SaveSysRole, 
        aMendRole, 
        DeleteSysRoleById , 
        DeleteSysRoleByIdList,
        aMendRoleState ,
        GetSysRoleMenuIds,
        DoAssignMenuIdToSysRole
        } from '@/api/role';
// 分页条总记录数
// let total = ref(0)
// 定义表格数据模型
// let list = ref([
//     {"id":9 ,  "roleName": "系统管理员" , "roleCode":"xtgly","createTime": '2023-07-31'},
//     {"id":10 , "roleName": "商品管理员" , "roleCode":"spgly","createTime": '2023-07-31'}
// ])
//
const ElTableref =ref();
const { proxy: ctx } = getCurrentInstance() // 可以把ctx当成vue2中的this
//我们按照vue2的习惯创建一个存放数据的对象
let thiss = reactive({
    // 定义表格数据模型
    list: [],
    //当前页码
    pageNum: 1,
    //当前显示页码
    pageSize: 10,
    // 分页条总记录数
    total: 0,
    //查询问题的文本,结果刚好可以跟后台的sysRoleDto类对应
    sysRoleDto: {
        roleName: ''
    },
    //弹窗显示控制开关
    dialogVisible: false,
    centerDialogVisible: false,
    //添加数据
    uprole: {
        roleName: '',
        roleCode: '',
        description: ''
    },
    //按钮颜色
    StateButter: "#134"

})
    
const defaultProps = {
  children: 'children',
  label: 'title',
}
const dialogMenuVisible = ref(false)
const sysMenuTreeList = ref([])
// 树对象变量
const tree = ref() 
// 默认选中的菜单数据集合
let roleId = ref()







// 组件加载钩子,也就是启动函数
onMounted(() => {
    reset()
})

//状态过滤器
const statusFilter = () => {
    // 这里是你的过滤逻辑,根据 isDeleted 字段的值进行过滤   
    return (value, row) => {
        if (+value.isDeleted === 1) {
            thiss.StateButter = "#134"
            return '✅';
        } else {
            thiss.StateButter = "#689"
            return '❌';
        }
    };
}

const reset = () => {
    // 设置为空,即为查询所有
    thiss.sysRoleDto.roleName = '';
    getrolelest();
}
//查询接口
const getrolelest = async () => {
    //从后台查到数据,匹配到参数里
    const { code, message, data } = await GetRole(thiss.pageNum, thiss.pageSize, thiss.sysRoleDto);
    +code == 200 ? ctx.$message.success('列表刷新:' + message) : ctx.$message.error('列表刷新:' + message)
    thiss.list = data.list
    thiss.pageNum = data.pageNum
    thiss.pageSize = data.pageSize
    thiss.total = data.total
}
//弹窗打开方法
const addShow = () => {
    thiss.dialogVisible = true
    //清空
    thiss.uprole = {
        roleName: '',
        roleCode: '',
        description: ''
    }
}
//添加接口
const upRole = async () => {
    console.log(thiss.sysRoleDto.roleName);
    //从后台查到数据,匹配到参数里
    //!thiss.uprole.id等同于:thiss.uprole.id == null
    //判断是修改还是添加
    const { code, message, data } = !thiss.uprole.id ? await SaveSysRole(thiss.uprole) : await aMendRole(thiss.uprole);

    if (+code == 200) {
        ctx.$message.success(message)
        //关闭弹窗
        thiss.dialogVisible = false
        //刷新界面
        getrolelest();
    } else {
        ctx.$message.error(message)
    }

}


//修改
const amends = information => {
    console.log(information);
    //打开弹窗
    addShow()
    //获取本列数据,传递给弹窗
    thiss.uprole = information.row
}

//修改状态接口
const setRoleState = async information => {
    const { code } = await aMendRoleState(information.row.id, information.row.isDeleted)
    if (+code === 200) {
        ElMessage.success('修改成功')
        //刷新界面
        getrolelest();
    }
}

//删除
const deletes = information => {
    ElMessageBox.confirm('此操作将永久删除该记录, 是否继续?', 'Warning', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
    }).then(async () => {
        const { code } = await DeleteSysRoleById(information.row.id)
        if (code === 200) {
            //刷新界面
            getrolelest();
            ElMessage.success('删除成功')
        }
    })
}

//批量删除
const Statebutter = () => {
    ElMessageBox.confirm('此操作将永久删除, 是否继续?', 'Warning', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
    }).then(async () => {
        const { code } = await DeleteSysRoleByIdList(ElTableref.value.getSelectionRows())
        if (code === 200) {
            //刷新界面
            getrolelest();
            ElMessage.success('删除成功')
        }
    })
  
}

const showAssignMenu = async row => { 
  dialogMenuVisible.value = true
  roleId = row.id
  const { data } = await GetSysRoleMenuIds(row.id)   // 请求后端地址获取所有的菜单数据,以及当前角色所对应的菜单数据
  sysMenuTreeList.value = data.sysMenuList
  tree.value.setCheckedKeys(data.roleMenuIds)   // 进行数据回显
}

const doAssign = async () => {
    const checkedNodes = tree.value.getCheckedNodes() ; // 获取选中的节点
    const checkedNodesIds = checkedNodes.map(node => {  // 获取选中的节点的id
        return {
            id: node.id,
            isHalf: 0
        }
    })
    
    // 获取半选中的节点数据,当一个节点的子节点被部分选中时,该节点会呈现出半选中的状态
    const halfCheckedNodes = tree.value.getHalfCheckedNodes() ; 
    const halfCheckedNodesIds = halfCheckedNodes.map(node => {   // 获取半选中节点的id
        return {
            id: node.id,
            isHalf: 1
        }
    })
        
    // 将选中的节点id和半选中的节点的id进行合并
    const menuIds = [...checkedNodesIds , ...halfCheckedNodesIds]  
    console.log(menuIds);

    // 构建请求数据
    const assignMenuDto = {
        roleId: roleId,
        menuIdList: menuIds
    }
 
    // 发送请求
    await DoAssignMenuIdToSysRole(assignMenuDto) ;
    ElMessage.success('操作成功')
    dialogMenuVisible.value = false

} 









</script>

<style scoped>
.search-div {
    margin-bottom: 10px;
    padding: 10px;
    border: 1px solid #ebeef5;
    border-radius: 3px;
    background-color: #fff;
}

.tools-div {
    margin: 10px 0;
    padding: 10px;
    border: 1px solid #ebeef5;
    border-radius: 3px;
    background-color: #fff;
}
</style>


<!-- 

{
    "code": 200,
    "message": "操作成功",
    "data": {
        "total": 7,
        "list": [
            {
                "id": 39,
                "createTime": "2023-09-04 02:04:41",
                "updateTime": "2023-09-04 02:30:01",
                "isDeleted": 1,
                "roleName": "运维人员",
                "roleCode": "yw",
                "description": null
            },
            {
                "id": 38,
                "createTime": "2023-09-03 15:24:32",
                "updateTime": "2023-09-04 02:30:06",
                "isDeleted": 1,
                "roleName": "开发人员",
                "roleCode": "dev",
                "description": null
            }
        ],
        "pageNum": 1,
        "pageSize": 2,
        "size": 2,
        "startRow": 1,
        "endRow": 2,
        "pages": 4,
        "prePage": 0,
        "nextPage": 2,
        "isFirstPage": true,
        "isLastPage": false,
        "hasPreviousPage": false,
        "hasNextPage": true,
        "navigatePages": 8,
        "navigatepageNums": [
            1,
            2,
            3,
            4
        ],
        "navigateFirstPage": 1,
        "navigateLastPage": 4
    }
}

 -->
sysUser
 <template>
    <!---搜索表单-->
    <div class="search-div">
        <el-form label-width="70px" size="small">
            <el-row>
                <el-col :span="12">
                    <el-form-item label="关键字">
                        <el-input style="width: 100%" placeholder="用户名" v-model="thiss.SysUserDto.keyword"></el-input>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="创建时间">
                        <el-date-picker 
                            v-model="thiss.createTimes"
                            type="daterange" 
                            range-separator="To" 
                            start-placeholder="开始时间"
                            end-placeholder="结束时间" 
                            format="YYYY-MM-DD" 
                            value-format="YYYY-MM-DD" />
                    </el-form-item>
                </el-col>
            </el-row>
            <el-row style="display:flex">
                <el-button type="primary" size="small" @click="gerthiss">
                    搜索
                </el-button>
                <el-button size="small" @click="breaks">重置</el-button>
            </el-row>
        </el-form>
    </div>

    <!--添加按钮-->
    <div class="tools-div">
        <el-button type="success" size="small" @click="newUsers">添 加</el-button>
    </div>

    <el-dialog v-model="thiss.dialogVisible" title="添加或修改" width="40%">
        <el-form label-width="120px">
            <el-form-item label="用户名">
                <el-input v-model="thiss.user.username"/>
            </el-form-item>
            <el-form-item label="密码">
                <el-input type="password" show-password v-model="thiss.user.password"/>
            </el-form-item>
            <el-form-item label="姓名">
                <el-input v-model="thiss.user.name"/>
            </el-form-item>
            <el-form-item label="手机">
                <el-input v-model="thiss.user.phone"/>
            </el-form-item>
                <el-form-item label="头像">
                    <!-- 获取请求token上传图片 -->
                    <el-upload
                        class="avatar-uploader"
                        action="http://localhost:10001/admin/system/user/userTX"
                        :show-file-list="false"
                        :on-success="handleAvatarSuccess"
                        :headers="headers"
                        >  
                        <img v-if="thiss.user.avatar" :src="thiss.user.avatar" class="avatar" />
                        <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
                    </el-upload>
                </el-form-item>
            <el-form-item label="描述">
                <el-input  v-model="thiss.user.description"/>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="newandup">提交</el-button>
                <el-button @click="thiss.dialogVisible = false">取消</el-button>
            </el-form-item>
        </el-form>
    </el-dialog> 

    <!---数据表格-->
    <el-table :data="thiss.userl" style="width: 100%">
        <el-table-column type=index label="序列"  width="80"/>
        <el-table-column prop="id" label="ID" />
        <el-table-column prop="username" label="用户名" />
        <el-table-column prop="name" label="姓名" />
        <el-table-column prop="phone" label="手机" />
        <el-table-column prop="avatar" label="头像" #default="scope">
            <img :src="scope.row.avatar" width="50" />
        </el-table-column>
        <el-table-column prop="description" label="描述" />
        <el-table-column prop="status" label="状态" #default="scope">
            {{ scope.row.status == 1 ? '正常' : '停用' }}
        </el-table-column>
        <el-table-column prop="createTime" label="创建时间" />
        <el-table-column label="操作" align="center" width="280" #default="scope">
            <el-button type="primary" size="small" @click="dataUpUsers(scope)">
                修改
            </el-button>
            <el-button type="danger" size="small" @click="deleteUsers(scope)"> 
                删除
            </el-button>
            <el-button type="warning" size="small" @click="fpjs(scope)">
                分配角色
            </el-button>
        </el-table-column>
    </el-table>

  <!-- 分配弹窗 -->
    <el-dialog v-model="thiss.dialogRoleVisible" title="分配角色" width="40%">
    <el-form label-width="80px">
        <el-form-item label="用户名">
            <el-input disabled :value="thiss.user.username"></el-input>
        </el-form-item>

        <el-form-item label="角色列表">
            <el-checkbox-group v-model="thiss.userRoleId">
                <el-checkbox v-for="role in thiss.userRoleIds" :key="role.id" :label="role.id">
                    {{ role.roleName }}
                </el-checkbox>
            </el-checkbox-group>
        </el-form-item>

        <el-form-item>
            <el-button type="primary" @click="fpjstj">提交</el-button>
            <el-button @click="thiss.dialogRoleVisible = false">取消</el-button>
        </el-form-item>
    </el-form>
</el-dialog>


    <!--分页条  @是绑定按钮的方法 , v-model是绑定参数-->
    <!--监听分页,换页,和字符-->
    <el-pagination :page-sizes="[10, 2, 20, 50, 100]" layout="total, sizes, prev, pager, next" 
    :total="thiss.total"
    @size-change="getrolelest"
    @current-change="getrolelest" 
    v-model:current-page="thiss.pageNum"
    v-model:page-size="thiss.pageSize" />






</template>

<script setup>
import {
    ref,
    reactive,
    getCurrentInstance,
    onMounted
} from 'vue';
import { Md5 } from 'ts-md5';
//引入api接口  
import { GetUser , newUser ,deleteUser,dataUpUser ,inUserByRole , selectUserByRole} from '@/api/user';
import { GetRole } from '@/api/role';
//token接口
import { useApp } from '@/pinia/modules/app'
const { proxy: ctx } = getCurrentInstance() // 可以把ctx当成vue2中的this
// // 表格数据模型
// const list = ref([
//     { "id": 1, "username": "admin", "name": "admin", "phone": "13121034567", "status": 1, "createTime": "2023-05-11" },
//     { "id": 2, "username": "admin", "name": "admin", "phone": "13121034567", "status": 1, "createTime": "2023-05-11" }
// ]);
// // 分页条数据模型
// const total = ref(0)

//用户相关参数
const thiss = reactive({
    //user列表
    userl:[],
    //一个user,上传使用
    user: {
        id: null,
        password:'',
        username: "",
        name: "",
        phone: "",
        avatar:"",
        status: 1
    },

    //当前页码
    pageNum: 1,
    //当前显示页码
    pageSize: 10,
    // 分页条总记录数
    total: 0,

    //搜索  -- 对于搜索接口
    SysUserDto:{
        //搜索文本
        keyword:'',
        //开始接受时间  createTimes的数组下标0是开始,1是介结束
        createTimeBegin:'',
        createTimeEnd:''
    },
    //搜索时间页面参数
    createTimes:[],
    //控制添加框框
    dialogVisible:false,
    //分配角色框框
    dialogRoleVisible:false,
    //所有分配角色列表
    userRoleIds:[],
    //选中的角色
    userRoleId:[],


})
    

// 组件加载钩子,也就是启动函数
onMounted(async () => {
    gerthiss()

    //调用角色列表接口
     const { code, message, data } = await GetRole(1, 0,{roleName: ''});
    if (+code == 200) {
        thiss.userRoleIds=data.list
        ctx.$message.success(message)
    } else {
        ctx.$message.error(message)
    }
})
//分页器按钮:
const getrolelest = () => {
    //重置搜索文本和时间
    thiss.SysUserDto. keyword = ''
    thiss.createTimes = []
    gerthiss()
}


//重置
const breaks = () => {
    //重置搜索文本和时间,和分页器
    thiss.SysUserDto. keyword = ''
    thiss.createTimes = []
    thiss.pageNum=1
    thiss.pageSize=10
    thiss.total=0
    gerthiss()
}
//搜索接口,也是列表
const gerthiss = async ()=>{
    thiss.SysUserDto.createTimeBegin = thiss.createTimes[0]
    thiss.SysUserDto.createTimeEnd   = thiss.createTimes[1]
    //发送  thiss.SysUserDto
    const { code, message, data } =  await GetUser(thiss.pageNum , thiss.pageSize,  thiss.SysUserDto)
   
    if (+code == 200) {
        //更新列表
        thiss.userl = data.list
        thiss.pageNum = data.pageNum
        thiss.pageSize = data.pageSize
        thiss.total = data.total
        ctx.$message.success(message)
    } else {
        ctx.$message.error(message)
    }
}



//删除按钮
const deleteUsers = async data =>{
    //发送取出来的  id

    const { code, message } =  await deleteUser(data.row.id)
    if (+code == 200) {
        //更新列表
        breaks()
        ctx.$message.success(message)
    } else {
        ctx.$message.error(message)
    }
}



//修啊按钮
const dataUpUsers =  data =>{
    thiss.user= data.row 
    thiss.user.password = '' 
    thiss.dialogVisible = true
}
// 添加按钮
const newUsers =  ()=>{
    thiss.user = {
        id: null,
        password:"",
        username: "",
        name: "",
        phone: "",
        status: 1
    }
    thiss.dialogVisible = true
}
//修改和,添加功能
const newandup =  async ()=>{
    // 密码转md5
    // const md5 = new Md5()
    // 等于空是添加,不是就是修改
    const { code, message } = thiss.user.id == null ?  await newUser(thiss.user) : await dataUpUser(thiss.user)
    if (+code == 200) {
        //更新列表
        breaks()
        thiss.dialogVisible = false
        ctx.$message.success(message)
    } else {
        ctx.$message.error(message)
    }
}



const headers = {
  token: useApp().authorization.token     // 从pinia中获取token,在进行文件上传的时候将token设置到请求头中
}

// 图像上传成功以后的事件处理函数
const handleAvatarSuccess = (response, uploadFile) => {
    console.log(response.data);
    thiss.user.avatar = response.data
}



//分配角色
const fpjs = async datas =>{
    thiss.user= datas.row 
    //查询现在的分配

     const { code, message, data } = await selectUserByRole(datas.row.id);
    if (+code == 200) {
        thiss.userRoleId = data
        ctx.$message.success(message)
    } else {
        ctx.$message.error(message)
    }

    thiss.dialogRoleVisible = true
}

//分配角色提交
const fpjstj = async () =>{
//thiss.user角色信息
     const { code, message, data } = await inUserByRole(thiss.user.id , thiss.userRoleId );
    if (+code == 200) {
        thiss.dialogRoleVisible = false
        ctx.$message.success(message)
    } else {
        ctx.$message.error(message)
    }


}






</script>

<style scoped>
.search-div {
    margin-bottom: 10px;
    padding: 10px;
    border: 1px solid #ebeef5;
    border-radius: 3px;
    background-color: #fff;
}

.tools-div {
    margin: 10px 0;
    padding: 10px;
    border: 1px solid #ebeef5;
    border-radius: 3px;
    background-color: #fff;
}

.avatar-uploader .avatar {
    width: 178px;
    height: 178px;
    display: block;
}

.avatar-uploader .el-upload {
    border: 1px dashed var(--el-border-color);
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    transition: var(--el-transition-duration-fast);
}

.avatar-uploader .el-upload:hover {
    border-color: var(--el-color-primary);
}

.el-icon.avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 178px;
    height: 178px;
    text-align: center;
}
</style>

<!-- 
       if (+code == 200) {
        ctx.$message.success(message)

    } else {
        ctx.$message.error(message)
    }
 -->

 

 apijs接口:

 

Menu
 import request from '@/utils/request'

const api_name = '/zx/admin/system/menu'


// 分页列表
export const FindNodes = () => {
    return request({
        url: `${api_name}/getlist`,
        method: 'get',
    })
}

// 保存信息
export const SaveMenu = sysMenu => {
    return request({
        url: `${api_name}/save`,
        method: 'post',
        data: sysMenu,
    })
}

// 修改信息
export const UpdateSysMenuById = sysMenu => {
    return request({
        url: `${api_name}/updateById`,
        method: 'put',
        data: sysMenu,
    })
}

// 根据id删除数据
export const RemoveSysMenuById = id => {
    return request({
        url: `${api_name}/removeById/${id}`,
        method: 'delete',
    })
}
User
 //引入统一请求封装
import request from '@/utils/request'
//api接口常量
const url = '/zx/admin/system/user'
// 查询接口
export const GetUser = (page,quantity,datas) => {
  return request({
    //在js里`表示字符串拼接${}是个表达式
    url: `${url}/getUserList/${page}/${quantity}`,
    method: 'post',
    //如果后端是@RequestBody则这里要加data
    //否则是 params
    data : datas ,
  })
}

// 添加角色请求方法
export const newUser = (data) => {
  return request({
      url: `${url}/newUser`,
      method: 'post',
      data
  })
}


// 修改角色请求方法
export const dataUpUser = (data) => {
  return request({
      url: `${url}/dataUpUser`,
      method: 'put',
      data
  })
}

// 删除角色
export const deleteUser = (roleId) => {
    return request({
        url: `${url}/deleteUser/${roleId}`,
        method: 'delete'
    })
}



// 查询用户分配的角色返回[]
export const selectUserByRole = (userid) => {
  return request({
      url: `${url}/selectUserByRole/${userid}`,
      method: 'get',
  })
}

//修改用户的角色信息
export const inUserByRole = (userid , userList) => {
  return request({
      url: `${url}/inUserByRole/${userid}`,
      method: 'post',
      data: userList
  })
}














// 批量删除角色
export const DeleteSysRoleByIdList = (rolelist) => {
  return request({
      url: `${url}/DeleteSysRoleByIdList`,
      method: 'delete',
      data : rolelist 
  })
}
Role
 //引入统一请求封装
import request from '@/utils/request'
//api接口常量
const url = '/zx/admin/system/role'
// 查询接口
export const GetRole = (page,quantity,datas) => {
  return request({
    //在js里`表示字符串拼接${}是个表达式
    url: `${url}/getRoleList/${page}/${quantity}`,
    method: 'post',
    //如果后端是@RequestBody则这里要加data
    //否则是 params
    data : datas ,
  })
}

// 添加角色请求方法
export const SaveSysRole = (data) => {
  return request({
      url: `${url}/DataUpRole`,
      method: 'post',
      data
  })
}

// 修改角色请求方法
export const aMendRole = (data) => {
  return request({
      url: `${url}/aMendRole`,
      method: 'put',
      data
  })
}
// 修改角色请求方法
export const aMendRoleState = (roleId,roleState) => {
  return request({
      url: `${url}/aMendRoleState/${roleId}/${roleState}`,
      method: 'get',
  })
}


// 删除角色
export const DeleteSysRoleById = (roleId) => {
    return request({
        url: `${url}/DeleteSysRoleById/${roleId}`,
        method: 'delete'
    })
}


// 批量删除角色
export const DeleteSysRoleByIdList = (rolelist) => {
  return request({
      url: `${url}/DeleteSysRoleByIdList`,
      method: 'delete',
      data : rolelist 
  })
}


// 查询指定角色所对应的菜单id
export const GetSysRoleMenuIds = (roleId) => {
  return request({
      url: `${url}/findSysRoleMenuByRoleId/`+ roleId,
      method: 'get'
  })
}

// 根据角色分配菜单请求方法
export const DoAssignMenuIdToSysRole = (assignMenuDto) => {
  return request({
      url: `${url}/doAssign`,
      method: 'post',
      data: assignMenuDto
  })
}

 

 

 至此这部分功能就搞定了:

 主要学习菜单的内容:

  • 递归查询子目录
  • 为角色分配菜单的多表查询

 

 


 总结:

这篇文章写的时候很卡不知道为什么,

所以没怎么好好写

 

 主要学习:

  1. 数据库的关联查询
  2. 数据库的事务关联
  3. 前端的列表操作
  4. 前端数据绑定
  5. 递归思想

到这里的代码:

前端里的: 忘记删除了,可以有点大

posted @ 2023-12-01 15:15  哲_心  阅读(8)  评论(0编辑  收藏  举报