您好,欢迎来到华佗小知识。
搜索
您的当前位置:首页el-table复选框父子关联(联级)选择

el-table复选框父子关联(联级)选择

来源:华佗小知识


在做权限分配的时候,遇到的问题,在使用element plus中,el-table复选框父子联级选择,在这里记录一下。详情看element plus官网, ,这里使用的是2.3.4版本的element plu

具体结果如下gif图所示

需要提前知道的注意事项

在element plus的表格中,这里我使用的是,2.3.4的版本
这里使用到的事件有

全选的时候需要提前知道的注意事项

因为父子级并没有联系,所以会有问题,这个就需要自己去实现了,只有勾选了,就会项上面说的那样,会自动添加到对应的数组中的。这个不用担心是否会添加到那个数组中,勾选多个也会同时添加到对应的数组中的。通过给的默认事件获取就行

使用到的方法

这就是一个组件,可以直接复制这代码到某个组件中进行试着展示一下就知道了

<template>
  <el-table ref="multipleTableRef" :data="tableData" style="width: 100%" @selection-change="handleSelectionChange">
    <el-table-column type="selection" width="55" />
    <el-table-column label="Date" width="120">
      <template #default="scope">{{ scope.row.date }}</template>
    </el-table-column>
    <el-table-column property="name" label="Name" width="120" />
    <el-table-column property="address" label="Address" show-overflow-tooltip />
  </el-table>
  <div style="margin-top: 20px">
    <!-- 这里的[tableData[1], tableData[2]]是与table绑定的数组tableData的数据项 -->
    <el-button @click="toggleSelection([tableData[1], tableData[2]])">
      选中第二第三行是与table绑定的数组数据
    </el-button>

    <el-button @click="toggleSelection()">
      清除以选中的选型
    </el-button>

    <!-- 这里的[userList[1], userList[2]]不是与table绑定的数组的数据项,这个点击的时候可以勾选,但复选框样式失效 -->
    <el-button @click="userListToggleSelection([userList[1], userList[2]])">不是与table绑定的数组项,可以勾选得到数据,但复选框样式失效</el-button>

    <!-- 这里的[userListFormTableData[1], userListFormTableData[2]],数据是从与table绑定的数据tableData遍历得来的,所以复选框生效-->
    <el-button @click="userListFormTableDataToggleSelection([userListFormTableData[1], userListFormTableData[2]])">
      数据是从与table绑定的数据tableData遍历得来的,即可以得到数据,复选框样式也生效
    </el-button>

  </div>
</template>

<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import { ElTable } from 'element-plus'

interface User {
  date: string
  name: string
  address: string
}

const tableData: User[] = [
  {
    date: '2016-05-03',
    name: 'Tom',
    address: 'No. 1, Grove St, Los Angeles',
  },
  {
    date: '2016-05-02',
    name: 'Tom',
    address: 'No. 1, Grove St, Los Angeles',
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 1, Grove St, Los Angeles',
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 1, Grove St, Los Angeles',
  },
  {
    date: '2016-05-08',
    name: 'Tom',
    address: 'No. 1, Grove St, Los Angeles',
  },
  {
    date: '2016-05-06',
    name: 'Tom',
    address: 'No. 1, Grove St, Los Angeles',
  },
  {
    date: '2016-05-07',
    name: 'Tom',
    address: 'No. 1, Grove St, Los Angeles',
  }
]

const multipleTableRef = ref<InstanceType<typeof ElTable>>()
const multipleSelection = ref<User[]>([])
const toggleSelection = (rows?: User[]) => {
  if (rows) {
    rows.forEach((row) => {
      /**
       * 这里要特别注意,这row必须是与表格绑定的tableData的数据的项,具体看上放方传入的参数
       * 是:[tableData[1], tableData[2]]
       * 不是随便一个数据都行的
       */
      // @ts-expect-error,这个可以忽略ts语法的错误提示
      multipleTableRef.value!.toggleRowSelection(row, undefined)
    })
  } else {
    multipleTableRef.value!.clearSelection()
  }
}
const handleSelectionChange = (val: User[]) => {
  console.log(val)
  multipleSelection.value = val
}

/**
 * 
 * @param rows 非跟table绑定的数组项
 */
const userListToggleSelection = (rows?: User[]) => {
  if (rows) {
    rows.forEach((row) => {
      /**
       * 这里要特别注意,这row必须是与表格绑定的tableData的数据的项,具体看上放方传入的参数
       * 是:[userList[1], userList[2]],并不是table绑定的数组中的tableData的数子,所以这里
       * multipleTableRef.value!.toggleRowSelection(row, undefined),生效了,得到数据了,但是复选框样式不生效
       * 与userList是否是ref修饰的响应式数据无关
       */
      // @ts-expect-error,这个可以忽略ts语法的错误提示
      multipleTableRef.value!.toggleRowSelection(row, undefined)
    })
  } else {
    multipleTableRef.value!.clearSelection()
  }
}



const userList: User[] = [
  {
    date: '2016-05-03',
    name: 'Tom',
    address: 'No. 1, Grove St, Los Angeles',
  },
  {
    date: '2016-05-02',
    name: 'Tom',
    address: 'No. 1, Grove St, Los Angeles',
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 1, Grove St, Los Angeles',
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 1, Grove St, Los Angeles',
  },
  {
    date: '2016-05-08',
    name: 'Tom',
    address: 'No. 1, Grove St, Los Angeles',
  },
  {
    date: '2016-05-06',
    name: 'Tom',
    address: 'No. 1, Grove St, Los Angeles',
  },
  {
    date: '2016-05-07',
    name: 'Tom',
    address: 'No. 1, Grove St, Los Angeles',
  }
]

const userListFormTableData: User[] = []

const getUserListFormTableData = () => {
  tableData.forEach((item: User) => {
    userListFormTableData.push(item)
  })
}

const userListFormTableDataToggleSelection = (rows: User[]) => {
  if (rows) {
    rows.forEach((row) => {
      /**
       * 这里要特别注意,这row必须是与表格绑定的tableData的数据的项,具体看上放方传入的参数
       * 是:[userListFormTableData[1], userListFormTableData[2]],虽然不是tableData
       * 但是这个数组的数据是从tableData中遍历得来的,所以里面的数据项,跟tableData指向同一个地址
       * multipleTableRef.value!.toggleRowSelection(row, undefined),也是可以生效的,复选框样式也生效
       * 与userListFormTableData是否是ref修饰的响应式数据无关
       */
      // @ts-expect-error,这个可以忽略ts语法的错误提示
      multipleTableRef.value!.toggleRowSelection(row, undefined)
    })
  } else {
    multipleTableRef.value!.clearSelection()
  }
}

onMounted(() => {
  getUserListFormTableData()
})

</script>

代码以及结果如下所示


这里吐槽一手,直接使用element plus的 更好,什么父子级联级选择,都实现了,只要稍微配置一下属性就好了。比在表格中使用这个更加实在,
建议不要使用这种方式,还有自己写父子级关联的逻辑。真心不建议使用上述gif图中的方式,如果是做权限分配的话。

主要核心代码

这里先把代码,贴出来,具体代码的解释看后面,注意了,以下代码都是基于表格的树形结构只有两层的情况下进行的(也就是只有一个children),如果是多层,需要在代码中进行处理,递归的方式就挺不错的,注意看代码中使用到children的地方。

<template>
  <div class="role-container">
      <div style="height: 82%;">
        <el-row style="height: 100%;">
          <el-table :data="tableData" border style="width: 100%; height: 100%;">
            <el-table-column width="225" label="操作">
              <template #default="scope">
                <!-- 注意了这里  handleCommand(command, scope.row) 传入了row,这里自己表格对应的行内容,根据自己表格来修改
                这里是为了保证完整性我才没去掉的-->
                <el-dropdown style="margin-left: 15px"
                  @command="(command: string | number | object) => handleCommand(command, scope.row)">
                  <!-- 表格的点击事件 -->
                  <el-button size="small" type="primary" plain>》更多</el-button>
                  <template #dropdown>
                    <el-dropdown-menu>
                      <el-dropdown-item command="handleRoleAuth">分配权限</el-dropdown-item>
                    </el-dropdown-menu>
                  </template>
                </el-dropdown>
              </template>
            </el-table-column>
          </el-table>
        </el-row>
  
        <!-- 分配权限弹窗 -->
        <el-dialog v-model="dialogVisibleRoleAuth" label-width="80px" title="角色菜单权限分配" width="600px"
          @close="dialogVisibleRoleAuthClose">
          <el-from>
            <el-from-item>
              <el-table ref="multipleTableRef" :data="roleMenuListTableData" border row-key="menuId" default-expand-all
                @selection-change="handleSelectionChange" @select="handleSelect" @select-all="handleSelectAll">
                 <!-- 
                 @selection-change="handleSelectionChange"
                 @select="handleSelect" 
                 @select-all="handleSelectAll"
                 是官网的内置事件
               -->
                <el-table-column type="selection" width="55" />
                <el-table-column prop="menuName" label="菜单名称" />
                <el-table-column label="菜单类型">
                  <template #default="scope">
                    <el-tag type="info" v-if="scope.row.menuType === 'M'">目录</el-tag>
                    <el-tag v-else-if="scope.row.menuType === 'C'">运维菜单</el-tag>
                    <el-tag type="success" v-else>前端菜单</el-tag>
                  </template>
                </el-table-column>
              </el-table>
            </el-from-item>
          </el-from>
          <template #footer>
            <span class="dialog-footer">
              <el-button @click="dialogVisibleRoleAuth = false">取消</el-button>
              <el-button type="primary" @click="fenPeiQuanXian">分配权限</el-button>
            </span>
          </template>
        </el-dialog>

      </div>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
// 这个是api接口,具体看后面的代码解释的内容,不需要关心这个
import { updateRoleAuthMenu, roleAuthMenuListByRoleId } from '../api/role';
// 这个是api接口,具体看后面的代码解释的内容,不需要关心这个
import { menuList } from '../api/menu'
// 数据类型定义,具体看后面的代码解释的内容,不需要关心这个
// import { Menu } from '../responsemodel/responseMenu.ts';
// 数据类型定义,具体看后面的代码解释的内容,不需要关心这个
// import { Role } from '../responsemodel/responseRole.ts';

// 菜单类型定义
/**
 * Menu
 */
 interface Menu {
  /**
   * 组件路径
   */
  component?: string;
  /**
   * 创建者
   */
  createBy?: string;
  /**
   * 创建时间
   */
  createTime?: string;
  /**
   * 菜单图标
   */
  icon?: string;
  /**
   * 菜单ID
   */
  menuId?: number;
  /**
   * 菜单名称
   */
  menuName?: string;
  /**
   * 菜单类型(M目录 C运维菜单 F前端菜单)
   */
  menuType?: string;
  /**
   * 显示顺序
   */
  orderNum?: number;
  /**
   * 父菜单ID
   */
  parentId?: number;
  /**
   * 路由地址
   */
  path?: string;
  /**
   * 权限标识
   */
  perms?: string;
  /**
   * 路由参数
   */
  query?: string;
  /**
   * 备注
   */
  remark?: string;
  /**
   * 菜单状态(0正常 1停用)
   */
  status?: string;
  /**
   * 更新者
   */
  updateBy?: string;
  /**
   * 更新时间
   */
  updateTime?: string;
  /**
   * 菜单子集
   */
  children?: Menu[]
}

// 角色类型定义
/**
 * Role
 */
 export interface Role {
  /**
   * 创建者
   */
  createBy?: string;
  /**
   * 创建时间
   */
  createTime?: string;
  /**
   * 删除标志(0代表存在 1代表删除)
   */
  delFlag?: string;
  /**
   * 备注
   */
  remark?: string;
  /**
   * 角色ID
   */
  roleId?: number;
  /**
   * 角色权限字符串
   */
  roleKey?: string;
  /**
   * 角色名称
   */
  roleName?: string;
  /**
   * 显示顺序
   */
  roleSort?: number;
  /**
   * 角色状态(0正常 1停用)
   */
  status?: string;
  /**
   * 更新者
   */
  updateBy?: string;
  /**
   * 更新时间
   */
  updateTime?: string;
}



import { ElMessage, ElTable } from 'element-plus'

let tableData = ref([])
/**分配配权限 */
const dialogVisibleRoleAuth = ref(false)
const queryMenuParams = reactive({
  menuName: undefined,
  menuType: undefined
})
const roleMenuListTableData = ref([])
const multipleTableRef = ref<InstanceType<typeof ElTable>>()
const menuIds = ref<(number | undefined)[]>([])
const roleId = ref()
let selectRoleMenuList = reactive<Menu[]>([])

/**
 * 用户回显的,加弹窗展示,不需要回显的可以不用看这个
 */
const handleCommand = async (command: string | number | object, row: Role) => {
  if (command === 'handleRoleAuth') {
    selectRoleMenuList = []
    // 这里是发送axios请求,得到的表格的菜单列表数据,树形结构,具体数据看后面说明
    const result = await menuList(queryMenuParams)
    roleMenuListTableData.value = result.data.data
    dialogVisibleRoleAuth.value = true
    roleId.value = row.roleId
    // 发送axios请求,这个是传入角色id,然后根据id获取角色具有的菜单,这个是非树形结构,
    // 这个回显角色具有的菜单选项时需要用到,具体数据看后面的代码说明,不同的角色具有的菜单权限是不一样的
    const result2 = await roleAuthMenuListByRoleId(row.roleId)
    selectRoleMenuList = result2.data.data
    const selectRomeMenuLists: Menu[] = []
    // 下面这个就是将菜单列表和角色权限列表,找出相同的,然后放到一个数组中,非树形结构
    if (selectRoleMenuList) {
      selectRoleMenuList.forEach((row: Menu) => {
        // 这里的数据值考虑两层,也就是只有一个Children的情况,如果是多层,可以考虑递归
        roleMenuListTableData.value.forEach((item: Menu) => {
          if (item.children) {
            item.children.forEach((childrenItem: Menu) => {
              if (childrenItem.menuId === row.menuId && childrenItem.menuName === row.menuName) {
                selectRomeMenuLists.push(childrenItem)
              }
            })
          }
          if (row.menuId === item.menuId && row.menuName === item.menuName) {
            selectRomeMenuLists.push(item)
          }
        })
      })
    }
    toggleSelection(selectRomeMenuLists)
  }
}

/**
 * 选择复选框,这里面处理了父子间的联级选择的问题
 */
const toggleSelection = (rows?: Menu[]) => {
  if (rows) {
    rows.forEach((item: Menu) => {
      multipleTableRef.value!.toggleRowSelection(item, true)
    })
    // 获取当前复选框勾选的行,因为表格中,如果勾选了父选项,子选项也会全部勾选的,上面的注意事项中提到过,所以这里需要
    // 排除某个角色中,没有的子选项权限,然后将 (选择状态 )变为 (未选择状态),这样回显才正确,这个
    // multipleTableRef.value!.getSelectionRows()获得的是已经选中的,非树形结构的,数据
    const selectionRows: Menu[] = multipleTableRef.value!.getSelectionRows()
    // 获取对应的id为数组
    const selectionRowPIds = selectionRows.map((item: Menu) => item.menuId)
    // 获取对应的id为数组,这个selectRoleMenuList是上面提到的角色具有的权限的菜单列表,是非树形的结构,具体看后面代码解释中的数据
    const selectRoleMenuListPIds = selectRoleMenuList.map((item: Menu) => item.menuId)
    // 求差集,从选中的selectionRowPIds数组选项中,找到selectRoleMenuList数组不具有的数据选项,这就找到了对应角色不具有的菜单项的差集了,
    // 然后把复选框的选择状态去掉,变为非选择状态
    let diff = selectionRowPIds.filter(item => selectRoleMenuListPIds.indexOf(item) == -1);
    // 将差集的选项勾选状态变为非选择状态
    selectionRows.forEach((item: Menu) => {
      diff.forEach(item2 => {
        if (item.menuId === item2) {
          multipleTableRef.value!.toggleRowSelection(item, false)
        }
      })
    })

  } else {
    multipleTableRef.value!.clearSelection()
  }
}

// 这两个是一个标记,为什么要加这两个,具体看后面的代码解释就知道了
let isFirstParentId = -1
let deleteParentId = -1

const handleSelect = (selection: Menu[], row: Menu) => {

  // 这个selection是一个非树形结构的数组,上面的注意事项已经说明了
  // 点击勾选的时候,或自动将勾选的项添加到selection这个数组中,点击取消勾选的时候会自动
  // 将对应的(当前取消选择的)项从selection数组中移除,所以通过判断在selection数组中是否有对应的项
  // 就可以判断中是否勾选了

  // 复选框选中状态
  if (selection.some((item: Menu) => item.menuId == row.menuId)) {  // 复选框选中状态

    // deleteParentId = -1、 isFirstParentId = -1。这两个标记,看后面解释代码的内容
    deleteParentId = -1
    isFirstParentId = -1

    // 后端返回的数据中,这个就是,根节点的parentId就是0
    if (row.parentId !== 0) {
      // 判断选择的子节点中对应的父节点是否已经选择了
      if (selection.some((item: Menu) => item.menuId == row.parentId)) {
        isFirstParentId = -1
        // console.log("已勾选父节点")
      } else {
        // 找父节点,点击某个子节点时候,如果父节点没有选上,则自动选择父节点,
        // 记住了:这里值考虑两层数据的情况,也就是只有一个Children的情况,如果是多层数据,可以写一个方法,递归处理
        const parentNode: any = roleMenuListTableData.value.find((item: Menu) => item.menuId == row.parentId)
        multipleTableRef.value!.toggleRowSelection(parentNode, true)
        parentNode.children.forEach((item: Menu) => {
          if (item.menuId != row.menuId) {
            multipleTableRef.value!.toggleRowSelection(item, false)
          }
        })
        // ts数据类型断言,我这里知道了对应数据的id是number类型的
        isFirstParentId = row.parentId as number
      }
    }
    // console.log(selection)
    // console.log("选中")

  }  else {   // 非选中状态,也就是说取消复选框选中状态,执行这里的代码

    // 判断所有选中的selection数组选项中,查找与当前取消选择时同一个父级parentId的选项,
    // 判断是否有数据,如果没有存在一个父级选项相同的项,说明这个父级选项所有的子选项已经
    // 是非选中状态了,这是就需要把对应的父级选项的(勾选状态)改为(非勾选状态)
    // 也就是如果取消子选项选择状态的时候,子选项没有一个勾选的时,父选项就取消勾选
    if (!(selection.some((item: Menu) => item.parentId == row.parentId))) {
      selection.forEach((item: Menu) => {
        if (item.menuId == row.parentId) {
          multipleTableRef.value!.toggleRowSelection(item, false)
          deleteParentId = item.menuId as number
        }
      })
    }
    // console.log(selection)
    // console.log("取消选中")
  }
}

/**
 * 
 * @param val 勾选项变化的数组,这个官网有说明
 */
const handleSelectionChange = (val: Menu[]) => {
  // 这里面是重新复制,而不是数组的push,所以这个数组会跟着val更新而更新的,
  // 就不需要考虑后面push的时候的数据了。因为下一次就重新复制了,有没有push
  // 对下一次进入这个方法都没有影响,更何况push之前还加了判断呢
  menuIds.value = val.map(item => item.menuId)
  if (isFirstParentId != -1) {
    menuIds.value.push(isFirstParentId)
  }
  if (deleteParentId != -1) {
    const index = menuIds.value.findIndex(item => item == deleteParentId)
    if (index != -1) {
      menuIds.value.splice(index, 1)
    }
  }
  // console.log("选中长度:", menuIds.value.length)
  // console.log(menuIds.value)
}

/**
 * 全选,非全选
 */
// @ts-ignore
const handleSelectAll = (selection: Menu[]) => {

  /**
   * 全选和非全选无非就两种状态
   * 1、要么selection > 0 全选,
   * 2、要么selection < 0 非全选
   * 或者使用element-plus中内置的标志判断是否需要进行全选,在对应的ref实例中可以找到
   * multipleTableRef.value!.store.states.isAllSelected.value
   */

  if (multipleTableRef.value!.store.states.isAllSelected.value) {
    // console.log("全选");
    // 因为这里只有两层数据,所以这里可以直接这样写,如果是多层数据,考虑递归的方式实现
    roleMenuListTableData.value.forEach((item: Menu) => {
      if (item.children!.length > 0) {
        item.children!.forEach((item2: Menu) => {
          multipleTableRef.value!.toggleRowSelection(item2, true)
        })
      }
    })
  }

  // if (selection.length > 0) {
  //   roleMenuListTableData.value.forEach((item: Menu) => {
  //     if (item.children!.length > 0) {
  //       item.children!.forEach((item2: Menu) => {
  //         // 判断这个选中的selection中是否有对应的选,如果没有就设置选中,让其放入到selection中
  //         const index = selection.findIndex((selectionItem: Menu) => selectionItem.menuId == item2.menuId)
  //         if (index == -1) {
  //           multipleTableRef.value!.toggleRowSelection(item2, true)
  //         }
  //       })
  //     }
  //   })
  // }

  // console.log(selection)
}

const fenPeiQuanXian = async () => {
  // 这个menuIds就是勾选得到的数组,handleSelectionChange()这个方法中,赋值的。
  const menuIdToString = menuIds.value.join(',')
  // 这个是发送一个axios请求,将角色id,和勾选的菜单选项的id数组,传给后端。然后后端根据,角色id先删除关联表的
  // 内容,重新见角色id和菜单id循环插入数据关联表
  const result = await updateRoleAuthMenu({ roleId: roleId.value, menuIds: menuIdToString })
  if (result.data.code === 200) {
    ElMessage({
      type: 'success',
      message: '分配菜单成功',
    })
    dialogVisibleRoleAuth.value = false
  }
}

/**
 * 关闭弹窗时的回调,具体看element plus的官网关于el-dialog的部分
 */
const dialogVisibleRoleAuthClose = () => {
  isFirstParentId = -1
  deleteParentId = -1
}


onMounted(() => {

})
</script>

<style lang="scss" scoped>
.role-container {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  padding: 10px;
  flex: 1;
}
</style>

代码解释以及使用到的数据

代码使用到的数据

菜单数据,也就是表格展示的数据,树形结构,在代码的位置如下所示

树形结构

[
    {
        "menuId": 100,
        "menuName": "用户管理",
        "parentId": 0,
        "orderNum": 1,
        "path": "user",
        "component": "views/UserManager.vue",
        "query": "",
        "menuType": "C",
        "status": "0",
        "perms": "system:user:list",
        "icon": "user",
        "createBy": "admin",
        "createTime": "2023-05-22T09:10:35",
        "updateBy": "",
        "updateTime": "2023-05-24T09:20:51",
        "remark": "用户管理菜单",
        "children": []
    },
    {
        "menuId": 101,
        "menuName": "角色管理",
        "parentId": 0,
        "orderNum": 2,
        "path": "role",
        "component": "views/RoleManager.vue",
        "query": "",
        "menuType": "C",
        "status": "0",
        "perms": "system:role:list",
        "icon": "peoples",
        "createBy": "admin",
        "createTime": "2023-05-22T09:10:35",
        "updateBy": "",
        "updateTime": "2023-05-24T09:21:15",
        "remark": "角色管理菜单",
        "children": []
    },
    {
        "menuId": 2003,
        "menuName": "菜单管理",
        "parentId": 0,
        "orderNum": 3,
        "path": "menus",
        "component": "views/MenuManager.vue",
        "query": null,
        "menuType": "C",
        "status": "0",
        "perms": "system:menu",
        "icon": "#",
        "createBy": "",
        "createTime": "2023-05-24T09:14:42",
        "updateBy": "",
        "updateTime": "2023-05-24T09:14:42",
        "remark": "菜单管理",
        "children": []
    },
    {
        "menuId": 103,
        "menuName": "部门管理",
        "parentId": 0,
        "orderNum": 4,
        "path": "dept",
        "component": "views/DeptManager.vue",
        "query": "",
        "menuType": "C",
        "status": "0",
        "perms": "system:dept:list",
        "icon": "tree",
        "createBy": "admin",
        "createTime": "2023-05-22T09:10:35",
        "updateBy": "",
        "updateTime": "2023-05-24T09:21:46",
        "remark": "部门管理菜单",
        "children": []
    },
    {
        "menuId": 500,
        "menuName": "操作日志",
        "parentId": 0,
        "orderNum": 5,
        "path": "operatelog",
        "component": "views/OperateLogManager.vue",
        "query": "",
        "menuType": "C",
        "status": "0",
        "perms": "monitor:operlog:list",
        "icon": "form",
        "createBy": "admin",
        "createTime": "2023-05-22T09:10:35",
        "updateBy": "",
        "updateTime": "2023-05-23T18:26:16",
        "remark": "操作日志菜单说明",
        "children": []
    },
    {
        "menuId": 2007,
        "menuName": "职位管理",
        "parentId": 0,
        "orderNum": 7,
        "path": "post",
        "component": "views/PostManager.vue",
        "query": null,
        "menuType": "C",
        "status": "0",
        "perms": "post:list",
        "icon": "#",
        "createBy": "",
        "createTime": "2023-05-29T22:46:54",
        "updateBy": "",
        "updateTime": "2023-05-29T22:46:54",
        "remark": "职位管理",
        "children": []
    },
    {
        "menuId": 2028,
        "menuName": "进度圈管理",
        "parentId": 0,
        "orderNum": 8,
        "path": "ProgressCircle",
        "component": "views/ProgressCircle.vue",
        "query": null,
        "menuType": "C",
        "status": "0",
        "perms": "ProgressCircle:admin",
        "icon": null,
        "createBy": null,
        "createTime": "2023-06-29T16:07:15.622081",
        "updateBy": null,
        "updateTime": "2023-06-29T16:07:15.622081",
        "remark": "进度圈管理",
        "children": []
    },
    {
        "menuId": 2004,
        "menuName": "项目管理",
        "parentId": 0,
        "orderNum": 9,
        "path": "",
        "component": null,
        "query": null,
        "menuType": "M",
        "status": "0",
        "perms": "adfadaa",
        "icon": "#",
        "createBy": "",
        "createTime": "2023-05-24T09:49:19",
        "updateBy": "",
        "updateTime": "2023-05-24T09:49:19",
        "remark": "系统管理",
        "children": [
            {
                "menuId": 2005,
                "menuName": "项目数据管理",
                "parentId": 2004,
                "orderNum": 1,
                "path": "xiangmushuju",
                "component": "views/ProjectData.vue",
                "query": null,
                "menuType": "C",
                "status": "0",
                "perms": "xiangmushuju",
                "icon": "#",
                "createBy": "",
                "createTime": "2023-05-24T14:35:32",
                "updateBy": "",
                "updateTime": "2023-05-24T14:35:32",
                "remark": "项目数据",
                "children": []
            },
            {
                "menuId": 2006,
                "menuName": "空间数据管理",
                "parentId": 2004,
                "orderNum": 2,
                "path": "kongjianshujuguanli",
                "component": "views/SpatialData.vue",
                "query": null,
                "menuType": "C",
                "status": "0",
                "perms": null,
                "icon": "#",
                "createBy": "",
                "createTime": "2023-05-24T15:02:23",
                "updateBy": "",
                "updateTime": "2023-05-24T15:02:23",
                "remark": "项目空间数据管理",
                "children": []
            },
            {
                "menuId": 2010,
                "menuName": "机械设备管理",
                "parentId": 2004,
                "orderNum": 3,
                "path": "jixieshebei",
                "component": "views/MechanicalData.vue",
                "query": null,
                "menuType": "C",
                "status": "0",
                "perms": null,
                "icon": "#",
                "createBy": "",
                "createTime": "2023-06-05T14:51:19",
                "updateBy": "",
                "updateTime": "2023-06-05T14:51:19",
                "remark": "机械设备管理",
                "children": []
            },
            {
                "menuId": 2008,
                "menuName": "无人机数据",
                "parentId": 2004,
                "orderNum": 4,
                "path": "wurenjishuju",
                "component": "views/DroneData.vue",
                "query": null,
                "menuType": "C",
                "status": "0",
                "perms": null,
                "icon": "#",
                "createBy": "",
                "createTime": "2023-06-05T14:47:39",
                "updateBy": "",
                "updateTime": "2023-06-05T14:47:39",
                "remark": "无人机数据",
                "children": []
            },
            {
                "menuId": 2009,
                "menuName": "民工管理",
                "parentId": 2004,
                "orderNum": 5,
                "path": "mingongguanli",
                "component": "views/MigrantWorkerData.vue",
                "query": null,
                "menuType": "C",
                "status": "0",
                "perms": null,
                "icon": "#",
                "createBy": "",
                "createTime": "2023-06-05T14:49:32",
                "updateBy": "",
                "updateTime": "2023-06-05T14:49:32",
                "remark": "民工管理",
                "children": []
            },
            {
                "menuId": 2011,
                "menuName": "进度数据",
                "parentId": 2004,
                "orderNum": 6,
                "path": "jindushuju",
                "component": "views/ProgressData.vue",
                "query": null,
                "menuType": "C",
                "status": "0",
                "perms": null,
                "icon": "#",
                "createBy": "",
                "createTime": "2023-06-05T14:53:39",
                "updateBy": "",
                "updateTime": "2023-06-05T14:53:39",
                "remark": "项目进度数据",
                "children": []
            }
        ]
    },
    {
        "menuId": 2027,
        "menuName": "app管理员中心",
        "parentId": 0,
        "orderNum": 11,
        "path": "FrontAppUserManager",
        "component": "views/front/FrontAppUserManager.vue",
        "query": null,
        "menuType": "F",
        "status": "0",
        "perms": "app:admin:center",
        "icon": null,
        "createBy": null,
        "createTime": "2023-06-28T14:52:50",
        "updateBy": null,
        "updateTime": "2023-06-28T14:52:50",
        "remark": "app管理员中心",
        "children": []
    },
    {
        "menuId": 2033,
        "menuName": "目录测试",
        "parentId": 0,
        "orderNum": 12,
        "path": null,
        "component": null,
        "query": null,
        "menuType": "M",
        "status": "0",
        "perms": null,
        "icon": null,
        "createBy": null,
        "createTime": "2023-09-13T09:34:23.762803",
        "updateBy": null,
        "updateTime": "2023-09-13T09:34:23.762803",
        "remark": null,
        "children": [
            {
                "menuId": 2034,
                "menuName": "菜单1",
                "parentId": 2033,
                "orderNum": 1,
                "path": "/aaaaa",
                "component": null,
                "query": null,
                "menuType": "C",
                "status": "0",
                "perms": null,
                "icon": null,
                "createBy": null,
                "createTime": "2023-09-13T09:34:43.3276",
                "updateBy": null,
                "updateTime": "2023-09-13T09:34:43.3276",
                "remark": null,
                "children": []
            },
            {
                "menuId": 2035,
                "menuName": "菜单2",
                "parentId": 2033,
                "orderNum": 2,
                "path": "/bbbbb",
                "component": null,
                "query": null,
                "menuType": "C",
                "status": "0",
                "perms": null,
                "icon": null,
                "createBy": null,
                "createTime": "2023-09-13T09:35:15.784714",
                "updateBy": null,
                "updateTime": "2023-09-13T09:35:15.784714",
                "remark": null,
                "children": []
            }
        ]
    }
]

某个具有的权限列表,这个是用于弹窗的时候的回显的,这个数据不是硬编码的,后台接口回根据传入的角色id,让其具有对应的菜单的。在代码中的位置

const result2 = await roleAuthMenuListByRoleId(row.roleId)
selectRoleMenuList = result2.data.data

[
    {
        "menuId": 103,
        "menuName": "部门管理",
        "parentId": 0,
        "orderNum": 4,
        "path": "dept",
        "component": "views/DeptManager.vue",
        "query": "",
        "menuType": "C",
        "status": "0",
        "perms": "system:dept:list",
        "icon": "tree",
        "createBy": "admin",
        "createTime": "2023-05-22T09:10:35",
        "updateBy": "",
        "updateTime": "2023-05-24T09:21:46",
        "remark": "部门管理菜单"
    },
    {
        "menuId": 2003,
        "menuName": "菜单管理",
        "parentId": 0,
        "orderNum": 3,
        "path": "menus",
        "component": "views/MenuManager.vue",
        "query": null,
        "menuType": "C",
        "status": "0",
        "perms": "system:menu",
        "icon": "#",
        "createBy": "",
        "createTime": "2023-05-24T09:14:42",
        "updateBy": "",
        "updateTime": "2023-05-24T09:14:42",
        "remark": "菜单管理"
    },
    {
        "menuId": 2004,
        "menuName": "项目管理",
        "parentId": 0,
        "orderNum": 9,
        "path": "",
        "component": null,
        "query": null,
        "menuType": "M",
        "status": "0",
        "perms": "adfadaa",
        "icon": "#",
        "createBy": "",
        "createTime": "2023-05-24T09:49:19",
        "updateBy": "",
        "updateTime": "2023-05-24T09:49:19",
        "remark": "系统管理"
    },
    {
        "menuId": 2005,
        "menuName": "项目数据管理",
        "parentId": 2004,
        "orderNum": 1,
        "path": "xiangmushuju",
        "component": "views/ProjectData.vue",
        "query": null,
        "menuType": "C",
        "status": "0",
        "perms": "xiangmushuju",
        "icon": "#",
        "createBy": "",
        "createTime": "2023-05-24T14:35:32",
        "updateBy": "",
        "updateTime": "2023-05-24T14:35:32",
        "remark": "项目数据"
    },
    {
        "menuId": 2006,
        "menuName": "空间数据管理",
        "parentId": 2004,
        "orderNum": 2,
        "path": "kongjianshujuguanli",
        "component": "views/SpatialData.vue",
        "query": null,
        "menuType": "C",
        "status": "0",
        "perms": null,
        "icon": "#",
        "createBy": "",
        "createTime": "2023-05-24T15:02:23",
        "updateBy": "",
        "updateTime": "2023-05-24T15:02:23",
        "remark": "项目空间数据管理"
    },
    {
        "menuId": 2010,
        "menuName": "机械设备管理",
        "parentId": 2004,
        "orderNum": 3,
        "path": "jixieshebei",
        "component": "views/MechanicalData.vue",
        "query": null,
        "menuType": "C",
        "status": "0",
        "perms": null,
        "icon": "#",
        "createBy": "",
        "createTime": "2023-06-05T14:51:19",
        "updateBy": "",
        "updateTime": "2023-06-05T14:51:19",
        "remark": "机械设备管理"
    },
    {
        "menuId": 2027,
        "menuName": "app管理员中心",
        "parentId": 0,
        "orderNum": 11,
        "path": "FrontAppUserManager",
        "component": "views/front/FrontAppUserManager.vue",
        "query": null,
        "menuType": "F",
        "status": "0",
        "perms": "app:admin:center",
        "icon": null,
        "createBy": null,
        "createTime": "2023-06-28T14:52:50",
        "updateBy": null,
        "updateTime": "2023-06-28T14:52:50",
        "remark": "app管理员中心"
    },
    {
        "menuId": 2033,
        "menuName": "目录测试",
        "parentId": 0,
        "orderNum": 12,
        "path": null,
        "component": null,
        "query": null,
        "menuType": "M",
        "status": "0",
        "perms": null,
        "icon": null,
        "createBy": null,
        "createTime": "2023-09-13T09:34:23.762803",
        "updateBy": null,
        "updateTime": "2023-09-13T09:34:23.762803",
        "remark": null
    },
    {
        "menuId": 2035,
        "menuName": "菜单2",
        "parentId": 2033,
        "orderNum": 2,
        "path": "/bbbbb",
        "component": null,
        "query": null,
        "menuType": "C",
        "status": "0",
        "perms": null,
        "icon": null,
        "createBy": null,
        "createTime": "2023-09-13T09:35:15.784714",
        "updateBy": null,
        "updateTime": "2023-09-13T09:35:15.784714",
        "remark": null
    }
]

为什么要使用isFirstParentId和deleteParentId这两个标志

使用isFirstParentId这个是因为通过,父子联级的时候,选择子选项,复选项虽然也勾选了,但是数组中并没有数据,第二次点击其他选项的时候,或者再点击的时候才有数据。

以下内容需要在代码查看handleSelect()和handleSelectionChange()这个两个函数,必要的时候,自己打一下断点就知道了。提交给后端数组的处理在,handleSelectionChange()中,这里就是提交给后端的数据数据。具体看:handleSelectionChange()。理解这两个逻辑,需要先看下面的gif,了解出现问题的原因,然后再,结合代码中的handleSelect()和handleSelectionChange()这个两个函数,有条件的断点调试一下

如果没有:isFirstParentId,得到的数据内容会有缺失,所以在第一次点击子选项的时候,需要勾选父级选项,需要记录下来,点击其他选项的时候,数组会从新赋值的,也就是父选项的也会添加进去选择的数组中,但是如果像下面GIF图所示的情况,第一次进来我就点击一次,那分配权限的时候就就缺少了一个字段。使用isFirstParentId就是为了防止第一次点击,不勾选其他选项然后导致提交给后端的数组中,数据缺失的情况。


使用了:isFirstParentId和对应的代码逻辑,这个之后,就可以防止第一次点击的时候数据缺失的问题了。

使用了:deleteParentId和对应的代码逻辑,就可以解决上述问题了,把多余的数据清除了

这个不需要看

数据类型定义
import { Role } from ‘…/responsemodel/responseRole’;

responseRole.ts,注意这里有一个export导出

/**
 * Role
 */
export interface Role {
  /**
   * 创建者
   */
  createBy?: string;
  /**
   * 创建时间
   */
  createTime?: string;
  /**
   * 删除标志(0代表存在 1代表删除)
   */
  delFlag?: string;
  /**
   * 备注
   */
  remark?: string;
  /**
   * 角色ID
   */
  roleId?: number;
  /**
   * 角色权限字符串
   */
  roleKey?: string;
  /**
   * 角色名称
   */
  roleName?: string;
  /**
   * 显示顺序
   */
  roleSort?: number;
  /**
   * 角色状态(0正常 1停用)
   */
  status?: string;
  /**
   * 更新者
   */
  updateBy?: string;
  /**
   * 更新时间
   */
  updateTime?: string;
}

// 数据类型定义
import { Menu } from ‘…/responsemodel/responseMenu’;

responseMenu.ts,注意这里有一个export导出

/**
 * Menu
 */
export interface Menu {
  /**
   * 组件路径
   */
  component?: string;
  /**
   * 创建者
   */
  createBy?: string;
  /**
   * 创建时间
   */
  createTime?: string;
  /**
   * 菜单图标
   */
  icon?: string;
  /**
   * 菜单ID
   */
  menuId?: number;
  /**
   * 菜单名称
   */
  menuName?: string;
  /**
   * 菜单类型(M目录 C运维菜单 F前端菜单)
   */
  menuType?: string;
  /**
   * 显示顺序
   */
  orderNum?: number;
  /**
   * 父菜单ID
   */
  parentId?: number;
  /**
   * 路由地址
   */
  path?: string;
  /**
   * 权限标识
   */
  perms?: string;
  /**
   * 路由参数
   */
  query?: string;
  /**
   * 备注
   */
  remark?: string;
  /**
   * 菜单状态(0正常 1停用)
   */
  status?: string;
  /**
   * 更新者
   */
  updateBy?: string;
  /**
   * 更新时间
   */
  updateTime?: string;
  /**
   * 菜单子集
   */
  children?: Menu[]
}

打发

不需要看这个,我只是记录一下而已
代码中的 import { updateRoleAuthMenu, roleAuthMenuListByRoleId } from ‘…/api/role’;

role.ts,注意这里有一个export导出

import axios from './request'

export function roleList(query: any) {
  return axios({
    url: '/role',
    method: 'get',
    params: query
  })
}

export function roleSave<T>(t: T) {
  return axios.post('/role', t)
}

export function roleUpdate<T>(t: T) {
  return axios.put('/role', t)
}

export function getRoleInfoById(id: number) {
  return axios.get(`/role/${id}`)
}

export function roleRemoveById(id: number) {
  return axios.delete(`/role/${id}`)
}

// 获取角色菜单列表
export function roleAuthMenuListByRoleId(roleId: number | undefined) {
  return axios.get(`/role/authMenu/${roleId}`)
}

export function updateRoleAuthMenu(data: any) {
  return axios({
    url: '/role/authMenu/',
    method: 'put',
    params: data
  })
}

// 这个是api接口,具体看后面的代码解释的内容,不需要关心这个,也可以不用看,这里只是记录一下而已
import { menuList } from ‘…/api/menu’

menu.ts,注意这里有一个export导出

import axios from './request'

export function menuList(query: any) {
  return axios({
    url: '/menu',
    method: 'get',
    params: query
  })
}

export function menuSave<T>(t: T) {
  return axios.post('/menu',t)
}

export function menuUpdate<T>(t: T) {
  return axios.put('/menu',t)
}

export function getMenuInfoById(id: number) {
  return axios.get(`/menu/${id}`)
}

export function menuRemoveById(id: number) {
  return axios.delete(`/menu/${id}`)
}

export function menuTreeSelect() {
  return axios.get('/menu/menuTreeSelect')
}

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo0.cn 版权所有 湘ICP备2023017654号-2

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务