Bootstrap

硅谷甄选(11)角色管理

角色管理模块

10.1 角色管理模块静态搭建

还是熟悉的组件:el-card、el-table 、el-pagination、el-form

<template>
  <el-card>
    <el-form :inline="true" class="form">
      <el-form-item label="职位搜索">
        <el-input placeholder="请你输入搜索职位关键字"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" size="default">搜索</el-button>
        <el-button type="primary" size="default">重置</el-button>
      </el-form-item>
    </el-form>
  </el-card>
  <el-card>
    <el-button type="primary" size="default" icon="Plus">添加职位</el-button>
    <el-table border style="margin: 10px 0px">
      <el-table-column type="index" align="center" label="#"></el-table-column>
      <el-table-column label="ID" align="center" prop="id"></el-table-column>
      <el-table-column
        label="职位名称"
        align="center"
        prop="roleName"
        show-overflow-tooltip
      ></el-table-column>
      <el-table-column
        label="创建时间"
        align="center"
        show-overflow-tooltip
        prop="createTime"
      ></el-table-column>
      <el-table-column
        label="更新时间"
        align="center"
        show-overflow-tooltip
        prop="updateTime"
      ></el-table-column>
      <el-table-column label="操作" width="280px" align="center">
        <!-- row:已有的职位对象 -->
        <template #="{ row, $index }">
          <el-button type="primary" size="small" icon="User">
            分配权限
          </el-button>
          <el-button type="primary" size="small" icon="Edit">编辑</el-button>
          <el-popconfirm :title="`你确定要删除${row.roleName}?`" width="260px">
            <template #reference>
              <el-button type="primary" size="small" icon="Delete">
                删除
              </el-button>
            </template>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>
  </el-card>
  <el-pagination
    v-model:current-page="pageNo"
    v-model:page-size="pageSize"
    :page-sizes="[10, 20, 30, 40]"
    :background="true"
    layout="prev, pager, next, jumper,->,sizes,total"
    :total="400"
    @current-change="getHasRole"
    @size-change="sizeChange"
  />
</template>

10.2 角色管理数据展示

10.2.1 API&&type

api:

//角色管理模块的的接口
import request from '@/utils/request'
import type { RoleResponseData, RoleData, MenuResponseData } from './type'
//枚举地址
enum API {
  //获取全部的职位接口
  ALLROLE_URL = '/admin/acl/role/',
}
//获取全部的角色
export const reqAllRoleList = (
  page: number,
  limit: number,
  roleName: string,
) => {
  return request.get<any, RoleResponseData>(
    API.ALLROLE_URL + `${page}/${limit}/?roleName=${roleName}`,
  )
}

type:

export interface ResponseData {
  code: number
  message: string
  ok: boolean
}
//职位数据类型
export interface RoleData {
  id?: number
  createTime?: string
  updateTime?: string
  roleName: string
  remark?: null
}


//全部职位的数组的ts类型
export type Records = RoleData[]
//全部职位数据的相应的ts类型
export interface RoleResponseData extends ResponseData {
  data: {
    records: Records
    total: number
    size: number
    current: number
    orders: []
    optimizeCountSql: boolean
    hitCount: boolean
    countId: null
    maxLimit: null
    searchCount: boolean
    pages: number
  }
}
10.2.2 组件获取数据
//当前页码
let pageNo = ref<number>(1)
//一页展示几条数据
let pageSize = ref<number>(10)
//搜索职位关键字
let keyword = ref<string>('')
//组件挂载完毕
onMounted(() => {
  //获取职位请求
  getHasRole()
})
//获取全部用户信息的方法|分页器当前页码发生变化的回调
const getHasRole = async (pager = 1) => {
  //修改当前页码
  pageNo.value = pager
  let result: RoleResponseData = await reqAllRoleList(
    pageNo.value,
    pageSize.value,
    keyword.value,
  )
  if (result.code == 200) {
    total.value = result.data.total
    allRole.value = result.data.records
  }
}
10.2.3 表格数据
<el-table border style="margin: 10px 0px" :data="allRole">
        <el-table-column
          type="index"
          align="center"
          label="#"
        ></el-table-column>
        <el-table-column label="ID" align="center" prop="id"></el-table-column>
        <el-table-column
          label="职位名称"
          align="center"
          prop="roleName"
          show-overflow-tooltip
        ></el-table-column>
        <el-table-column
          label="创建时间"
          align="center"
          show-overflow-tooltip
          prop="createTime"
        ></el-table-column>
        <el-table-column
          label="更新时间"
          align="center"
          show-overflow-tooltip
          prop="updateTime"
        ></el-table-column>
        <el-table-column label="操作" width="280px" align="center">
          <!-- row:已有的职位对象 -->
          <template #="{ row, $index }">
            <el-button type="primary" size="small" icon="User">
              分配权限
            </el-button>
            <el-button type="primary" size="small" icon="Edit">编辑</el-button>
            <el-popconfirm
              :title="`你确定要删除${row.roleName}?`"
              width="260px"
            >
              <template #reference>
                <el-button type="primary" size="small" icon="Delete">
                  删除
                </el-button>
              </template>
            </el-popconfirm>
          </template>
        </el-table-column>
      </el-table>
10.2.4 分页器数据

同样的@current-change与@size-change函数回调。

<el-pagination
      v-model:current-page="pageNo"
      v-model:page-size="pageSize"
      :page-sizes="[10, 20, 30, 40]"
      :background="true"
      layout="prev, pager, next, jumper,->,sizes,total"
      :total="total"
      @current-change="getHasRole"
      @size-change="sizeChange"
    />
//下拉菜单的回调
const sizeChange = () => {
  getHasRole()
}
10.2.5 搜索按钮

//搜索按钮的回调
const search = () => {
  //再次发请求根据关键字
  getHasRole()
  keyword.value = ''
}
10.2.6 重置按钮

重置模块我在用户管理模块仔细解释过。

import useLayOutSettingStore from '@/store/modules/setting'
let settingStore = useLayOutSettingStore()
//重置按钮的回调
const reset = () => {
  settingStore.refresh = !settingStore.refresh
}

10.3 添加&&修改职位

10.3.1 静态
<!-- 添加职位与更新已有职位的结构:对话框 -->
    <el-dialog v-model="dialogVisite" title="添加职位">
      <el-form>
        <el-form-item label="职位名称">
          <el-input placeholder="请你输入职位名称"></el-input>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button type="primary" size="default" @click="dialogVisite = false">
          取消
        </el-button>
        <el-button type="primary" size="default">确定</el-button>
      </template>
    </el-dialog>
10.3.2 API&&TYPE
//新增岗位的接口地址
  ADDROLE_URL = '/admin/acl/role/save',
  //更新已有的职位
  UPDATEROLE_URL = '/admin/acl/role/update',
//添加职位与更新已有职位接口
export const reqAddOrUpdateRole = (data: RoleData) => {
  if (data.id) {
    return request.put<any, any>(API.UPDATEROLE_URL, data)
  } else {
    return request.post<any, any>(API.ADDROLE_URL, data)
  }
}
10.3.3 添加&&修改按钮绑定点击函数

10.3.4 添加&&修改按钮回调
//添加职位按钮的回调
const addRole = () => {
  //对话框显示出来
  dialogVisite.value = true
  //清空数据
  Object.assign(RoleParams, {
    roleName: '',
    id: 0,
  })
  //清空上一次表单校验错误结果
  nextTick(() => {
    form.value.clearValidate('roleName')
  })
}
//更新已有的职位按钮的回调
const updateRole = (row: RoleData) => {
  //显示出对话框
  dialogVisite.value = true
  //存储已有的职位----带有ID的
  Object.assign(RoleParams, row)
  //清空上一次表单校验错误结果
  nextTick(() => {
    form.value.clearValidate('roleName')
  })
}
10.3.5 表单校验

:model:要校验的数据

:rules:校验的规则

ref:获取表单实例,方便后面调用validate函数来确保校验通过才放行

prop:绑定数据的属性

//自定义校验规则的回调
const validatorRoleName = (rule: any, value: any, callBack: any) => {
  if (value.trim().length >= 2) {
    callBack()
  } else {
    callBack(new Error('职位名称至少两位'))
  }
}
//职位校验规则
const rules = {
  roleName: [{ required: true, trigger: 'blur', validator: validatorRoleName }],
}
10.3.6 保存按钮的回调
//确定按钮的回调
const save = async () => {
  //表单校验结果,结果通过在发请求、结果没有通过不应该在发生请求
  await form.value.validate()
  //添加职位|更新职位的请求
  let result: any = await reqAddOrUpdateRole(RoleParams)
  if (result.code == 200) {
    //提示文字
    ElMessage({
      type: 'success',
      message: RoleParams.id ? '更新成功' : '添加成功',
    })
    //对话框显示
    dialogVisite.value = false
    //再次获取全部的已有的职位
    getHasRole(RoleParams.id ? pageNo.value : 1)
  }
}

10.4 分配角色权限业务

10.4.1 API&&type(获取全部菜单)
//获取全部的菜单与按钮的数据
  ALLPERMISSTION = '/admin/acl/permission/toAssign/',
  //获取全部菜单与按钮权限数据
export const reqAllMenuList = (roleId: number) => {
  return request.get<any, MenuResponseData>(API.ALLPERMISSTION + roleId)
}

注意:type这里MenuData与MenuList互相调用,适合这种树状的数据结构

//菜单与按钮数据的ts类型
export interface MenuData {
  id: number
  createTime: string
  updateTime: string
  pid: number
  name: string
  code: string
  toCode: string
  type: number
  status: null
  level: number
  children?: MenuList
  select: boolean
}
export type MenuList = MenuData[]
10.4.2 获取数据

分配权限按钮:

获取&&存储数据

//准备一个数组:数组用于存储勾选的节点的ID(四级的)
let selectArr = ref<number[]>([])
//已有的职位的数据
const setPermisstion = async (row: RoleData) => {
  //抽屉显示出来
  drawer.value = true
  //收集当前要分类权限的职位的数据
  Object.assign(RoleParams, row)
  //根据职位获取权限的数据
  let result: MenuResponseData = await reqAllMenuList(RoleParams.id as number)
  if (result.code == 200) {
    menuArr.value = result.data
    // selectArr.value = filterSelectArr(menuArr.value, [])
  }
}
10.4.3 展示数据

我们重点关注el-tree组件

data:展示的数据

show-checkbox:节点是否可被选择

node-key:每个树节点用来作为唯一标识的属性,整棵树应该是唯一的

default-expand-all:默认展开所有节点

default-checked-keys:默认勾选的节点的 key 的数组

props:属性: label:指定节点标签为节点对象的某个属性值 children:指定子树为节点对象的某个属性值

const defaultProps = {
  //子树为节点对象的children
  children: 'children',
  //节点标签为节点对象的name属性
  label: 'name',
}

10.4.4 展示数据(已分配的权限)

获取已分配权限的id,这里我们只需要收集最后一层的id即可,因为组件会自动更具最后一层的选择情况决定上层的选择状况。

注意:获取最后一层id的函数filterSelectArr使用了递归。

//分配权限按钮的回调
//已有的职位的数据
const setPermisstion = async (row: RoleData) => {
  //抽屉显示出来
  drawer.value = true
  //收集当前要分类权限的职位的数据
  Object.assign(RoleParams, row)
  //根据职位获取权限的数据
  let result: MenuResponseData = await reqAllMenuList(RoleParams.id as number)
  if (result.code == 200) {
    menuArr.value = result.data
    selectArr.value = filterSelectArr(menuArr.value, [])
  }
}
const filterSelectArr = (allData: any, initArr: any) => {
  allData.forEach((item: any) => {
    if (item.select && item.level == 4) {
      initArr.push(item.id)
    }
    if (item.children && item.children.length > 0) {
      filterSelectArr(item.children, initArr)
    }
  })

  return initArr
} 
10.4.5 API&&type(分配权限)
//给相应的职位分配权限
SETPERMISTION_URL = '/admin/acl/permission/doAssign/?',
//给相应的职位下发权限
export const reqSetPermisstion = (roleId: number, permissionId: number[]) => {
  return request.post(
    API.SETPERMISTION_URL + `roleId=${roleId}&permissionId=${permissionId}`,
  )
}
10.4.6 收集用户分配的权限(每个权限的id)&&发请求

我们这里收集主要用到了2个方法:getCheckedKeys、getHalfCheckedKeys。它们会返回已选择以及半选择用户的id数组

//抽屉确定按钮的回调
const handler = async () => {
  //职位的ID
  const roleId = RoleParams.id as number
  //选中节点的ID
  let arr = tree.value.getCheckedKeys()
  //半选的ID
  let arr1 = tree.value.getHalfCheckedKeys()
  let permissionId = arr.concat(arr1)
  //下发权限
  let result: any = await reqSetPermisstion(roleId, permissionId)
  if (result.code == 200) {
    //抽屉关闭
    drawer.value = false
    //提示信息
    ElMessage({ type: 'success', message: '分配权限成功' })
    //页面刷新
    window.location.reload()
  }
}
10.4.7删除业务

API&&TYPE

//删除已有的职位
export const reqRemoveRole = (roleId: number) => {
  return request.delete<any, any>(API.REMOVEROLE_URL + roleId)
}

删除的回调

//删除已有的职位
const removeRole = async (id: number) => {
  let result: any = await reqRemoveRole(id)
  if (result.code == 200) {
    //提示信息
    ElMessage({ type: 'success', message: '删除成功' })
    getHasRole(allRole.value.length > 1 ? pageNo.value : pageNo.value - 1)
  }
}

;