项目实战--JsonServer与SideMenu

发布于:2025-03-28 ⋅ 阅读:(28) ⋅ 点赞:(0)

猴爪许愿:希望后端给我接口

项目是有后端的,但是由于是同步的开发,没办法随时随地和后端沟通,和后端约定一个数据的表,前端项目在实现的时候用JsonServer模拟后端数据

要使用 ,先安装

json-server --watch db.json

可以在npm上面搜索

json-server - npmhttps://www.npmjs.com/package/json-serverJson-server是基于node重新封装的一个框架

有查询的功能:比如这样:localhost:8000/posts?id=111

这就是根据属性筛选

开启Jsonserver:

json-server --watch test.json

import React from 'react';
import { Button } from "antd";
import 'axios'
import axios from 'axios';

function Home() {
    const ajax = ()=>{
        //拿取数据
        axios.get("https://localhost:3000/posts/2").then(res=>{
            console.log(res.data)
        })
        // 插入数据
        axios.post("http://localhost:3000/posts",{
            title:"hello",
            author:"lizhi"
        })
    }
    return (
        <div>
           <Button type='primary' onClick={ajax}>Button</Button>
        </div>
    );
}

export default Home;

增数据

import React from 'react';
import { Button } from "antd";
import 'axios'
import axios from 'axios';

function Home() {
    const ajax = ()=>{
        //拿取数据
        // _embed
        axios.get("http://localhost:3000/posts?_embed=comments").then(res=>{
            console.log(res.data)
        })
        // _expand
        // axios.get("http://localhost:3000/comments?_expand=post").then(res=>{
        //     console.log(res.data)
        // })
        // // 插入数据
        // axios.post("http://localhost:3000/posts",{
        //     id:1,
        //     title:"hello",
        //     author:"lizhi"
        // })
    }
    return (
        <div>
           <Button type='primary' onClick={ajax}>Button</Button>
        </div>
    );
}

export default Home;

 

 

 

 

后端SideMenu

import React, { useState, useEffect } from 'react'
import { Layout, theme } from 'antd'
import './index.css'
import {
  UploadOutlined,
  UserOutlined,
  VideoCameraOutlined,
  SettingOutlined,
} from '@ant-design/icons'
import { Switch } from 'antd'
// import SubMenu from 'antd/es/menu/SubMenu'
import { useNavigate } from 'react-router-dom' // 引入 useNavigate
import axios from 'axios'
import { Menu } from 'antd';
const { SubMenu } = Menu;  

const { Header, Content, Sider } = Layout

// const menuList = [
//   {
//     key: '/home',
//     title: '首页',
//     icon: <UserOutlined />,
//   },
//   {
//     key: '/user-manage',
//     title: '用户管理',
//     icon: <UserOutlined />,
//     children: [
//       {
//         key: '/user-manage/list',
//         title: '用户列表',
//         icon: <UserOutlined />,
//       },
//     ],
//   },
//   {
//     key: '/right-manage',
//     title: '权限管理',
//     icon: <UserOutlined />,
//     children: [
//       {
//         key: '/right-manage/role/list',
//         title: '角色列表',
//         icon: <UserOutlined />,
//       },
//       {
//         key: '/right-manage/right/list',
//         title: '权限列表',
//         icon: <UserOutlined />,
//       },
//     ],
//   },
// ]

function SideMenu(props) {
  const [menu, setMenu] = useState([])
  useEffect(() => {
    axios.get('http://localhost:3000/rights?_embed=children').then((res) => {
      console.log(res.data)
      setMenu(res.data)
    })
  },[])
  const [collapsed, setCollapsed] = useState(false)
  const navigate = useNavigate() // 使用 useNavigate 获取 navigate 函数

  const onClick = (e) => {
    console.log('click ', e)
  }

  const renderMenu = (menuList) => {
    return menuList.map((item) => {
      if (item.children) {
        return (
          <SubMenu key={item.key} icon={item.icon} title={item.title}>
            {renderMenu(item.children)}
          </SubMenu>
        )
      }
      return (
        <Menu.Item
          key={item.key}
          icon={item.icon}
          onClick={() => {
            navigate(item.key) // 使用 navigate 进行导航
          }}
        >
          {item.title}
        </Menu.Item>
      )
    })
  }

  return (
    <Sider trigger={null} collapsible collapsed={false}>
      <div className="logo">新闻发布系统</div>
      <Menu
        theme="dark"
        mode="inline"
        defaultSelectedKeys={['3']}
        onClick={onClick}
        defaultOpenKeys={['sub1']}
      >
        {renderMenu(menu)}
      </Menu>
    </Sider>
  )
}

export default SideMenu

用test.json模拟后端的数据:

如果没有配置permission字段的没有必要渲染(没有资格点过去)

import React, { useState, useEffect } from 'react'
import { Layout, Menu } from 'antd'
import { useNavigate } from 'react-router-dom'
import axios from 'axios'
import {
  UserOutlined,
  SettingOutlined,
  UploadOutlined,
  VideoCameraOutlined,
  AuditOutlined,
  FormOutlined,
  HomeOutlined,
} from '@ant-design/icons'

const { SubMenu } = Menu
const { Sider } = Layout

// **手动映射菜单项对应的图标**
const iconMap = {
  首页: <HomeOutlined />,
  用户管理: <UserOutlined />,
  用户列表: <UserOutlined />,
  权限管理: <SettingOutlined />,
  新闻管理: <FormOutlined />,
  审核管理: <AuditOutlined />,
  发布管理: <UploadOutlined />,
}

function SideMenu() {
  const [menu, setMenu] = useState([])
  useEffect(() => {
    axios.get('http://localhost:3000/rights?_embed=children').then((res) => {
      setMenu(res.data)
    })
  }, [])

  const navigate = useNavigate()

  const checkPermission = (item) => {
    // 检查用户是否具有访问权限
    return item.pagepermisson === 1
  }

  const renderMenu = (menuList) => {
    return menuList.map((item) => {
      const icon = iconMap[item.title] || <VideoCameraOutlined /> // 默认图标
      if (item.children && checkPermission(item)) {
        return (
          <SubMenu key={item.key} icon={icon} title={item.title}>
            {renderMenu(item.children)}
          </SubMenu>
        )
      }
      return (
        checkPermission(item) &&
        <Menu.Item key={item.key} icon={icon} onClick={() => navigate(item.key)}>
          {item.title}
        </Menu.Item>
      )
    })
  }

  return (
    <Sider trigger={null} collapsible>
      <div className="logo">新闻发布系统</div>
      <Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
        {renderMenu(menu)}
      </Menu>
    </Sider>
  )
}

export default SideMenu

这样以后就成功了,以及图标也可以正确的显示

接下来要解决的是首页不可折叠的问题

import React, { useState, useEffect } from 'react'
import { Layout, Menu } from 'antd'
import { useNavigate } from 'react-router-dom'
import axios from 'axios'
import {
  UserOutlined,
  SettingOutlined,
  UploadOutlined,
  VideoCameraOutlined,
  AuditOutlined,
  FormOutlined,
  HomeOutlined,
} from '@ant-design/icons'

const { SubMenu } = Menu
const { Sider } = Layout

// **手动映射菜单项对应的图标**
const iconMap = {
  首页: <HomeOutlined />,
  用户管理: <UserOutlined />,
  用户列表: <UserOutlined />,
  权限管理: <SettingOutlined />,
  新闻管理: <FormOutlined />,
  审核管理: <AuditOutlined />,
  发布管理: <UploadOutlined />,
}

function SideMenu() {
  const [menu, setMenu] = useState([])
  useEffect(() => {
    axios.get('http://localhost:3000/rights?_embed=children').then((res) => {
      setMenu(res.data)
    })
  }, [])

  const navigate = useNavigate()

  const checkPermission = (item) => {
    // 检查用户是否具有访问权限
    return item.pagepermisson === 1
  }

  const renderMenu = (menuList) => {
    return menuList.map((item) => {
      const icon = iconMap[item.title] || <VideoCameraOutlined /> // 默认图标
      if (item.children?.length>0 && checkPermission(item)) {
        return (
          <SubMenu key={item.key} icon={icon} title={item.title}>
            {renderMenu(item.children)}
          </SubMenu>
        )
      }
      return (
        checkPermission(item) &&
        <Menu.Item key={item.key} icon={icon} onClick={() => navigate(item.key)}>
          {item.title}
        </Menu.Item>
      )
    })
  }

  return (
    <Sider trigger={null} collapsible>
      <div className="logo">新闻发布系统</div>
      <Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
        {renderMenu(menu)}
      </Menu>
    </Sider>
  )
}

export default SideMenu

进行链式判断,如果没有的话就不判断长度

然后是进行一些样式方面的优化,把滚动条给该样式(需安装antd,因为Vite默认不支持~的写法,那种写法是Webpack的)

@import 'antd/dist/reset.css';

::-webkit-scrollbar{width: 5px;height: 5px;position: absolute;}
::-webkit-scrollbar-thumb{background-color: #1890ff;}
::-webkit-scrollbar-track{background-color: #ddd;}

接下来根据路径选择渲染哪一部分高亮

重定向会导致高亮显示失效,是因为加上default会变成非受控组件,而去掉default就称为受控的了(受控:外部状态改变,内部也会受到影响

非受控:外部状态改变只对第一次的造成影响)

import React, { useState, useEffect } from 'react'
import { Layout, Menu } from 'antd'
import { useNavigate } from 'react-router-dom'
import axios from 'axios'
import {
  UserOutlined,
  SettingOutlined,
  UploadOutlined,
  VideoCameraOutlined,
  AuditOutlined,
  FormOutlined,
  HomeOutlined,
} from '@ant-design/icons'
import './index.css'
import { useLocation } from 'react-router-dom'

const { SubMenu } = Menu
const { Sider } = Layout

// **手动映射菜单项对应的图标**
const iconMap = {
  首页: <HomeOutlined />,
  用户管理: <UserOutlined />,
  用户列表: <UserOutlined />,
  权限管理: <SettingOutlined />,
  新闻管理: <FormOutlined />,
  审核管理: <AuditOutlined />,
  发布管理: <UploadOutlined />,
}

function SideMenu() {
  const [menu, setMenu] = useState([])
  const location = useLocation() // 获取当前的路径
  useEffect(() => {
    axios.get('http://localhost:3000/rights?_embed=children').then((res) => {
      setMenu(res.data)
    })
  }, [])

  const navigate = useNavigate()

  const checkPermission = (item) => {
    // 检查用户是否具有访问权限
    return item.pagepermisson === 1
  }

  const renderMenu = (menuList) => {
    return menuList.map((item) => {
      const icon = iconMap[item.title] || <VideoCameraOutlined /> // 默认图标
      if (item.children?.length > 0 && checkPermission(item)) {
        return (
          <SubMenu key={item.key} icon={icon} title={item.title}>
            {renderMenu(item.children)}
          </SubMenu>
        )
      }
      return (
        checkPermission(item) && (
          <Menu.Item
            key={item.key}
            icon={icon}
            onClick={() => navigate(item.key)}
          >
            {item.title}
          </Menu.Item>
        )
      )
    })
  }

  //找到路径
  const selectKeys = [location.pathname]
  //分割字符串
  const openKeys = ['/' + location.pathname.split('/')[1]]
  return (
    <Sider trigger={null} collapsible>
      <div style={{ display: 'flex', height: '100%', flexDirection: 'column' }}>
        <div className="logo">新闻发布系统</div>
        <div style={{ flex: 1, overflow: 'auto' }}>
          <Menu
            theme="dark"
            mode="inline"
            selectedKeys={selectKeys}
            defaultOpenKeys={openKeys}
          >
            {renderMenu(menu)}
          </Menu>
        </div>
      </div>
    </Sider>
  )
}

export default SideMenu