React中实战入手实现菜单管理

举报
林太白 发表于 2025/03/05 17:53:20 2025/03/05
55 0 0
【摘要】 React中实战入手实现菜单管理

❤ React-菜单管理

使用场景

使用背景 有时候我们需要给不同的用户赋予不同的权限进行系统的访问,这个时候我们就需要用到角色权限,通过角色来控制角色可以访问哪些菜单和路由

先来看一下我们最后搭建的效果

1、接口封装

封装相关的接口,还是跟我们之前差不多,但是这次我们新建文件src\api\system\role.js

import request from '@/utils/request.js'

const apiTypeurl='/api/menu';
// 查询菜单列表
export function getMenuList (n) {
    return request({
      url: apiTypeurl,
      method: 'get',
      params:n
    })
}

// 添加菜单
export function addMenu (n) {
    return request({
            url: apiTypeurl,
            method: 'post',
            data:n
    })
}

// 删除菜单
export function delMenu (id) {
    return request({
      url:apiTypeurl+ '/'+id,
      method: 'delete'
    })      
}

// 修改菜单
export function editMenu(n) {
    return request({
            url: apiTypeurl,
            method: 'put',
            data:n
    })
}

// 获取菜单详情
export function getMenu(id) {
    return request({
      url:apiTypeurl+ '/'+id,
      method: 'get'
    })
}
// 修改菜单状态 
export function editMenuStatus (MenuId,status) {
    return request({
            url: apiTypeurl+'/status/'+MenuId,
            method: 'put',
            data:status
    })
}

2、页面路由的增加

在页面的路由之中添加相关页面的路由部分src\router\index.tsx

// 权限部分
const PermissionMenu  = lazy(() => import('@/views/system/permission/menu'));

{
      key: '7',
      path: "/admin/permission/menu",
      element: <PermissionMenu/>,
      label: '菜单管理',
      // 该路由不出现在左侧菜单栏
      meta: { 
        hidden: true
      }
    },

3、查询和页面搭建

整个页面的部分我们都写到这个里面 src\views\system\permission\menu.tsx

先来搭建一个简单的页面列表

👉表格部分搭建

表单引入,这里的表单以及结构的搭建还和我们之前角色的部分差不多

//引入我们相关的接口
import {getMenuList, addMenu, delMenu, editMenu, getMenu, editMenuStatus} from '@/api/system/menu'; //菜单接口

  useEffect(() => {
    handleQuery(); 
  }, [queryParams]);
  
   const handleQuery = () => {
    console.log('handleQuery');
    getList();
}

查看一下我们的查询接口

// 获取列表
const getList = async () => {
try {
  const res:any = await getMenuList(queryForm); // 假设getUser函数能够正确返回Promise对象
  if(res.code == 200){
    message.success('查询成功!');
    setDatatable(res.data);
  }else{
    console.log('查询失败,请重试!',res, 'getUser');
    message.error(res.message);
  }
} catch (error) {
  console.log('查询失败,请重试!');
}
}

查看一下我们返回的数据

查询已经完整了证明!

接下来完善一下我们的表单部分

表单部分我们增加一下

const [data, setData] = useState([]);
  const columns = [
    {
      title: '菜单编号',
      dataIndex: 'menu_id',
      key: 'menu_id',
      render: (_: any, row) => {return (<> {row?.menu_id|| '--'}</>)},
    },
    {
      title: '菜单名称',
      dataIndex: 'menu_name',
      key: 'menu_id',
      render: (_: any, row) => {return (<> {row?.menu_name|| '--'}</>)},
    },
    {
        title: '图标',
        dataIndex: 'icon',
        key: 'icon',
        render: (_: any, row) => {return (<> {row?.icon|| '--'}</>)},
    },
    //   <svg-icon :icon-class="scope.row.icon"  />
    {
        title: '排序',
        dataIndex: 'order_num',
        key: 'order_num',
        render: (_: any, row) => {return (<> {row?.order_num|| '--'}</>)},
    },
   
    {
        title: '权限标识',
        dataIndex: 'perms',
        key: 'perms',
        render: (_: any, row) => {return (<> {row?.perms|| '--'}</>)},
    },

    {
        title: '组件路径',
        dataIndex: 'component',
        key: 'component',
        render: (_: any, row) => {return (<> {row?.component|| '--'}</>)},
    },
    {
        title: '可见',
        dataIndex: 'visible',
        key: 'visible',
        render: (_: any, row) => {return (<> {row?.visible|| '--'}</>)},
    },
    {
        title: '创建时间',
        dataIndex: 'createTime',
        key: 'createTime',
        render: (_: any, row) => {return (<> {row?.createTime|| '--'}</>)},
    },
    {
      title: '操作',
      key: 'action',
      render: (_, row) => {
        return (
          <Flex gap="middle" wrap>
            <Button type="dashed"
              onClick={() => handleEdit(row)}
              autoInsertSpace={false}>
              修改
            </Button>
            <Button type="dashed"
              onClick={() => handleEnable(row)}
              autoInsertSpace={false} style={{color:row.state == 1 ? 'red' : "green"}}>
                 新增
            </Button>
            <Popconfirm
              title="确定要删除吗?"
              onConfirm={() => handleDelete(row)}
              okText="确定"
              cancelText="取消"
            >
              <Button style={{ marginLeft: 8 }} type="dashed" autoInsertSpace>
              删除
              </Button>
            </Popconfirm>
          </Flex>
        )
      },
    },
  ]; 
<div className='comback' style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
   <Table rowKey="id" dataSource={data} columns={columns} />
</div>

👉核对表单参数

其实也就是更改表单的这部分

页面搭建部分先看看

4、antd table 树形数据展示

这里我们需要注意的就是我们想要的不是普通的这种表格样式数据,其实是antd table 树形数据展示

接下来我们就把写一下这种树形数据

现在我们的数据是这样子的,也就是我们需要自己手动转化一下

为了使table能显示子节点 所以要重新数据处理

// import Menu from "@/services/menu"
import { map } from "lodash"
function getMenuData(data) {
    console.log(data);
    if (data[0].level === undefined) {
        const rows = map(data, (item, index) => ({
            key: "key" + index,
            item
        }))
        return rows
    }

    var result = [], len = 0
    data.forEach(item => {
        if (item.id > len)
            len = item.id
        return len
    })

    const rows = map(data, (item, index) => ({
        key: "row" + item.name,
        item,
        children: []
    }))

    for (let i = 0; i < len; i++) {
        for (let j = 0; j < len; j++) {
            if (rows[i] && rows[j] && rows[i].item.parent_id && rows[i].item.parent_id === rows[j].item.id) {
                rows[j].children.push(rows[i])
            }
        }
    }
    rows.forEach(e=>{
        if(e.children.length===0){
            delete e.children
        }
    })

    rows.forEach(elem => {
        if (elem.item.parent_id === null) {
            result.push(elem)
        }
    })
    return result

}
export default getMenuData

5、新增功能部分

接下来搭建我们的新增部分,这里还是跟我们之前用户部分的搭建弹窗差不多!

我们先来简单的写一些参数然后看看我们的效果是什么样子的

我们的接口部分请求的地址部分方面是这样子的

这里我们可以先增加一个数据试试

直接就成功了,看一下我们的参数部分

完善一下我们的角色方面的逻辑

增加逻辑还是我们之前的那一部分,成功!

5、修改部分

const handleEdit = async (row) => {
    setTitle("编辑");
    try {
      setLoading(true);
      let data = await handleDetail(row); // 接口的方式查询
      form.setFieldsValue(data); // 填充数据到表单中
    } catch (error) {
      message.error('获取数据详情失败,请重试!');
    } finally {
      setLoading(false);
    }
    setVisible(true);
  };
  /**
 * 处理详情
 *
 * @param row 表格行数据
 * @returns 返回获取到的详情数据
 */
  const handleDetail = async (row) => {
    try {
      const response = await getDictType(row.dict_id);
      const data = response.data;
      return data;
    } catch (error) {
      message.error('获取数据详情失败,请重试!');
    }
  }

这边修改部分我们可以看到已经好了,完善一下功能就可以了,注意这部分我们稍后也是进行抽离的!

6、删除部分

<Button
          type="link"
          size="small"
          danger
          key="batchRemove"
          // hidden={!access.hasPerms('system:dictType:remove')}
          onClick={async () => {
            console.log(row,'row--1');          
            Modal.confirm({
              title: '删除',
              content: '确定删除该项吗?',
              okText: '确认',
              cancelText: '取消',
              onOk:  () => {
                handleDelete(row);
              },
            });
          }}
        >
          删除
 </Button>
  const handleDelete = async (record) => {
    try {
      await delMenu(record.menu_id);
      message.success('删除成功');
      handleSearch();
    } catch (error) {
      message.error('删除失败,请重试!');
    }
  };

这边我们尝试一下,我们的删除已经成功了 !

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。