react 后台管理权限路由选择框,antd表格魔改,实现将选中和半选中的权限框的id传给后端

首先看一下效果

然后再说一下数据的格式和字段

其中IsObtain为是否选中字段,halfchecked为半选状态,permission_id为id,permission_pid为父id,其他的根据自己的业务需求来。

然后看一下层级关系

 最后,废话不多说,直接上代码

其中一到三步就是对antd表格的魔改,就对应自己数据的字段改一下就ok,不用去深究。

为了方便大家看懂,代码里大量的注释,并且先看哪个方法的步骤都已标明,所以我就步赘述了,直接上完整代码:

index.tsx: 数据是自己造的

import React, { useState } from 'react'
import Child from "./Child"
// import Child1 from "./Child1"

export default function Index() {
  const [data, stedata] = useState([
    {
      IsObtain: false,
      halfchecked: false,
      permission_id: "1",
      permission_name: "权限1",
      permission_pid: null,
      childPermissions: [
        {
          IsObtain: false,
          halfchecked: false,
          permission_id: "1-1",
          permission_name: "权限1-1",
          permission_pid: "1",
          childPermissions: [
            {
              IsObtain: false,
              halfchecked: false,
              permission_id: "1-1-1",
              permission_name: "权限1-1-1",
              permission_pid: "1-1",
              childPermissions: [
                {
                  IsObtain: false,
                  halfchecked: false,
                  permission_id: "1-1-1-1",
                  permission_name: "权限1-1-1-1",
                  permission_pid: "1-1-1",
                  childPermissions: null
                },
                {
                  IsObtain: false,
                  halfchecked: false,
                  permission_id: "1-1-1-2",
                  permission_name: "权限1-1-1-2",
                  permission_pid: "1-1-1",
                  childPermissions: null
                }
              ]
            },
            {
              IsObtain: false,
              halfchecked: false,
              permission_id: "1-1-2",
              permission_name: "权限1-1-2",
              permission_pid: "1-1",
              childPermissions: null
            }
          ]
        },
        {
          IsObtain: false,
          halfchecked: false,
          permission_id: "1-2",
          permission_name: "权限1-2",
          permission_pid: "1",
          childPermissions: [
            {
              IsObtain: false,
              halfchecked: false,
              permission_id: "1-2-1",
              permission_name: "权限1-2-1",
              permission_pid: "1-2",
              childPermissions: null
            }
          ]
        },
      ],
    },
    {
      IsObtain: false,
      halfchecked: false,
      permission_id: "2",
      permission_name: "权限2",
      permission_pid: null,
      childPermissions: [
        {
          IsObtain: false,
          halfchecked: false,
          permission_id: "2-1",
          permission_name: "权限2-1",
          permission_pid: "2",
          childPermissions: [
            {
              IsObtain: false,
              halfchecked: false,
              permission_id: "2-1-1",
              permission_name: "权限2-1-1",
              permission_pid: "2-1",
              childPermissions: null
            },
            {
              IsObtain: false,
              halfchecked: false,
              permission_id: "2-1-2",
              permission_name: "权限1-1-2",
              permission_pid: "2-1",
              childPermissions: null
            }
          ]
        },
      ],
    }
  ]);
  return (
    <div>
      <Child dataList={data} setdataList={stedata} />
      {/* <Child1 dataList={data} setdataList={stedata} /> */}
    </div>
  )
}

 Child.tsx:

//@ts-nocheck
import { useEffect, useState } from "react";
import { Checkbox, Table, Button, message } from "antd";
// import { postaddpermission } from "../../../api/permissionGroup";
export default function RoleList(props) {
  const [dataSource1, setdataSource] = useState()
  const [columns1, setcolumns] = useState()
  const [ccType, setccType] = useState(false)
  const columnWidthArray = ["10%", "10%", "30%", "25%", "25%"]

  //第二步,编码Data, 转成表格格式
  const encodeData = (data, i = 0, addData = {}) => {
    let ret = [];
    // eslint-disable-next-line array-callback-return
    data?.map((item) => {
      let next = Object.assign({ [i]: item.permission_id }, addData);
      //如果有子数据
      if (item.childPermissions) {
        ret.push(...encodeData(item.childPermissions, i + 1, next));
      } else {
        ret.push(next);
      }
    });
    return ret;
  };

  //第三步,获取最深的深度以确定列数
  const getMaxDepth = (data) => {
    let max = 1;
    data?.map((item) => {
      if (item.childPermissions) {
        let childDepth = getMaxDepth(item.childPermissions);
        if (max < 1 + childDepth) max = 1 + childDepth;
      }
    });
    return max;
  };

  //第一步,这里面就是将antd表格改成权限组选择想要的样子,并且调了一下方法
  const generateData = (list) => {
    //转换格式后生成的表格数据
    const dataSource = encodeData(list);
    //最大深度, 用于确认表格列数
    const max = getMaxDepth(list);
    const columns = [];
    for (let i = 0; i < max; i++) {
      columns.push({
        key: i,
        dataIndex: i,
        title: i,
        width: columnWidthArray[i],
        render: (t, r, rowIndex) => {
          const obj = {
            children: t ? getCheckBox(t, list) : "",
            props: {},
          };
          //列合并
          if (r[i] === undefined) {
            obj.props.colSpan = 0;
          } else if (r[i + 1] === undefined && i < max - 1) {
            obj.props.colSpan = max - i;
          }
          //行合并
          if (
            dataSource[rowIndex - 1] &&
            dataSource[rowIndex - 1][i] === dataSource[rowIndex][i]
          ) {
            obj.props.rowSpan = 0;
          } else {
            let rowSpan = 1;
            for (
              let j = 1;
              dataSource[rowIndex + j] &&
              dataSource[rowIndex + j][i] === dataSource[rowIndex][i];
              j++
            ) {
              rowSpan++;
            }
            obj.props.rowSpan = rowSpan;
          }
          return obj;
        },
      });
    }
    setdataSource(dataSource)
    setcolumns(columns)
  };

  //第五步,利用id获取整个对象
  //obj:传入的一个空对象
  //id:传入的数据id
  //list:整个权限的数据,是一个数组对象的格式
  const mapData = (obj, id, list) => {
    list.map(item => {
      //如果id相等,则把整个对象赋给ogj,否则如果有子数据就递归
      if (item.permission_id === id) {
        obj = item
      } else if (item.childPermissions) {
        obj = mapData(obj, id, item.childPermissions)
      }
    })
    //最后将对象返回
    return obj
  }

  //第九步,半选
  //list:总数据
  const halfchecked = (list) => {
    list.map(item => {
      if (item.childPermissions) {
        // 用数组的方法,有一个为true就为true,这里一定要加上|| item.halfchecked,因为如果是半选状态,它的父级也应该是半选状态,不加会导致三个层级以上出现bug
        let flag = item.childPermissions.some(item => item.IsObtain || item.halfchecked)
        item.halfchecked = flag
        // 再递归
        halfchecked(item.childPermissions)
      }
    })
  }

  //第八步,反选
  //list:总数据
  const reversecheck = (list) => {
    list.map(item => {
      if (item.childPermissions) {
        // 用数组的方法,全部都为true才为true
        let flag = item.childPermissions.every(item => item.IsObtain)
        item.IsObtain = flag
        // 再递归
        reversecheck(item.childPermissions)
      }
    })
  }

  //第七步,全选
  //checked:选择框状态,data:数据
  const checkAllbtn = (checked, data) => {
    data.map(item => {
      item.IsObtain = checked
      if (item.childPermissions) {
        checkAllbtn(checked, item.childPermissions)
      }
    })
  }

  //第六步,点击选择框
  //data:总数据,checked:选择框的状态,id:当前数据的id
  const checkedItem = (data, checked, id) => {
    //把传过来的数据进行遍历
    return data.map(item => {
      //如果id相等就把数据的IsObtain字段改成当前选择框的状态
      if (item.permission_id === id) {
        item.IsObtain = checked
        //全选,如果有子数据就调全选的方法
        if (item.childPermissions) {
          checkAllbtn(checked, item.childPermissions)
        }
        //如果父id不会空,那就说明点击的是子选择框,就要进行反选和半选
        if (item.permission_pid !== "null") {
          //反选/半选
          reversecheck(props.dataList)
          halfchecked(props.dataList)
        }
      }
      //递归,如果有子数据就进行递归
      else if (item.childPermissions) {
        checkedItem(item.childPermissions, checked, id)
      }
      return item
    })
  }


  //第四步,渲染选择框
  //t:当前数据的id, data:总的数据
  const getCheckBox = (t, data) => {
    let obj = {}
    // //页面加载的时候调用一次
    // halfchecked(props.dataList)
    // reversecheck(props.dataList)
    return (
      <Checkbox
        //是否选中,mapData(obj, t, props.dataList)为获取id相等的对象,IsObtain为数据里面的一个字段,默认为false,当点击选择框的时候会改为true
        checked={mapData(obj, t, props.dataList).IsObtain}
        //是否为半选状态,halfchecked也是数据里的一个字段,作用是判断是否为半选状态,
        //IsObtain和halfchecked这两个字段都是前端拿到数据之后自己递归加进去的,如果后端给就更好了,
        //首先判断数据里的IsObtain字段是否为true,如果是就选中了,就没有半选什么事了,否则才看halfchecked字段是否为true
        indeterminate={mapData(obj, t, props.dataList).IsObtain ? false : mapData(obj, t, props.dataList).halfchecked}
        onChange={(e) => {
          // ccType这个的作用就是在点击选择框后重新渲染一下,默认是false,然后每次取反,在useEffect里面监听ccType,就可以取到强制刷新的作用了
          setccType(!ccType)
          //点击选择框后调用的方法
          checkedItem(data, e.target.checked, mapData(obj, t, props.dataList).permission_id)
          // console.log(props.dataList);
          //每次点击完都会改变数据里面IsObtain和halfchecked的状态,所有需要再重置一下数据,如果在useEffect里面也监听了props.dataList,就可以不用上面那个ccType了,我是两个都保留了
          props.setdataList(props.dataList)
        }}
      >
        {/*这里显示权限的名字*/}
        {mapData(obj, t, props.dataList).permission_name}
      </Checkbox>
    );
  };

  //挂载
  useEffect(() => {
    //初始化页面将数据传入
    generateData(props.dataList)
    //页面加载的时候调用一次
    halfchecked(props.dataList)
    reversecheck(props.dataList)
  }, [props.dataList, ccType]);

  //第十一步,获取选择的数据id
  //list,总数据,arr:空数组
  const getData = (list, arr) => {
    list.map(item => {
      if (item.IsObtain || item.halfchecked) {
        arr.push(item.permission_id)
      }
      if (item.childPermissions) {
        getData(item.childPermissions, arr)
      }
    })
  }

  //第十步,点击保存,这里就是点击保存将全选或者半选的id都传给后端
  const addPermission = async () => {
    const arr = []
    getData(props.dataList, arr)
    //打印获取到的id
    console.log(arr);
  }

  return (
    <div>
      <div
        style={{
          position: "sticky",
          textAlign: "right",
          height: "40px",
          lineHeight: "40px",
        }}
      >
        <Button type="primary" onClick={addPermission}>
          保存
        </Button>
      </div>
      <div>
        <Table
          bordered
          pagination={false}
          scroll={{ y: true }}
          showHeader={false}
          dataSource={dataSource1}
          columns={columns1}
        />
      </div>
    </div>
  )
}

打印出来的id是一个数组

如果大家觉得对表格的魔改不太喜欢或者看不懂的,又想要自己写亿点点样式的,(手动狗头),我还有另一种方法,这种可是自己纯手工打造的,但是我懒得写样式,就放弃了,其实核心代码都差不多。

先看看效果吧:

 丑是丑了点,但这可是纯手工打造的,如果谁能把样式改好记得@我一下哟

代码奉上,Child1.tsx

//@ts-nocheck
import { useEffect, useState } from "react";
import { Checkbox, Button, message } from "antd";

const AllPermission = (props: any) => {
  const [ccType, setccType] = useState(false)

  //半选
  const halfchecked = (list) => {
    list.map(item => {
      if (item.childPermissions) {
        let flag = item.childPermissions.some(item => item.IsObtain || item.halfchecked)
        item.halfchecked = flag
        halfchecked(item.childPermissions)
      }
    })
  }

  //反选
  const reversecheck = (list) => {
    list.map(item => {
      if (item.childPermissions) {
        let flag = item.childPermissions.every(item => item.IsObtain)
        item.IsObtain = flag
        reversecheck(item.childPermissions)
      }
    })
  }

  //全选
  const checkAllbtn = (checked, data) => {
    data.map(item => {
      item.IsObtain = checked
      if (item.childPermissions) {
        checkAllbtn(checked, item.childPermissions)
      }
    })
  }

  //点击选择框
  const checkedItem = (data, checked, id) => {
    return data.map(item => {
      if (item.permission_id === id) {
        item.IsObtain = checked
        //全选
        if (item.childPermissions) {
          checkAllbtn(checked, item.childPermissions)
        }
        if (item.permission_pid !== "null") {
          //反选/半选
          reversecheck(props.dataList)
          halfchecked(props.dataList)
        }
      }
      //递归
      if (item.childPermissions) {
        checkedItem(item.childPermissions, checked, id)
        //反选/半选 这里不能少
        reversecheck(props.dataList)
        halfchecked(props.dataList)
      }
      //反选/半选 这里不能少
      reversecheck(props.dataList)
      halfchecked(props.dataList)
      return item
    })
  }

  //渲染选择框
  const treeMethod = (data: any) => {
    // let a=[]
    return data?.map((item: any, index: any) => {
      if (item.childPermissions) {
        return (
          <div style={{ border: "1px solid black", display: "flex", boxSizing: "border-box" }}>
            <div>
              <Checkbox value={item.permission_id}
                checked={item.IsObtain}
                indeterminate={item.IsObtain ? false : item.halfchecked}
                onChange={(e) => {
                  setccType(!ccType)
                  checkedItem(data, e.target.checked, item.permission_id)
                  props.setdataList(props.dataList)
                }}
              >{item.permission_name}
              </Checkbox>
            </div>
            <div>{treeMethod(item.childPermissions)}</div>
          </div>
        )
      } else {
        return (
          <div style={{ border: "1px solid black" }}>
            <Checkbox value={item.permission_id}
              checked={item.IsObtain}
              indeterminate={item.IsObtain ? false : item.halfchecked}
              onChange={(e) => {
                setccType(!ccType)
                checkedItem(data, e.target.checked, item.permission_id)
                console.log(props.dataList);
                props.setdataList(props.dataList)
              }}
            >{item.permission_name}</Checkbox>
          </div>
        );
      }
    });
  };

  useEffect(() => {
    //页面加载的时候调用一次
    halfchecked(props.dataList)
    reversecheck(props.dataList)
  }, [ccType]);

  //获取选择的数据id
  const getData = (list, arr) => {
    list.map(item => {
      if (item.IsObtain || item.halfchecked) {
        arr.push(item.permission_id)
      }
      if (item.childPermissions) {
        getData(item.childPermissions, arr)
      }
    })
  }

  //点击保存
  const addData = async () => {
    const arr = []
    getData(props.dataList, arr)
    console.log(arr);
  }

  return (
    <div>
      <div><Button type="primary" onClick={addData}>保存</Button></div>
      <div>{treeMethod(props.dataList)}</div>
    </div>
  );
};
export default AllPermission;

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值