使用vite+react+ts+Ant Design开发后台管理项目(四)

news2024/11/17 11:39:37

  前言


本文将引导开发者从零基础开始,运用vite、react、react-router、react-redux、Ant Design、less、tailwindcss、axios等前沿技术栈,构建一个高效、响应式的后台管理系统。通过详细的步骤和实践指导,文章旨在为开发者揭示如何利用这些技术工具,从项目构思到最终实现的全过程,提供清晰的开发思路和实用的技术应用技巧。

 项目gitee地址:lbking666666/enqi-admin

 本系列文章:

  • 使用vite+react+ts+Ant Design开发后台管理项目(一)
  • 使用vite+react+ts+Ant Design开发后台管理项目(二)
  • 使用vite+react+ts+Ant Design开发后台管理项目(三)
  • 使用vite+react+ts+Ant Design开发后台管理项目(四)

本章节添加菜单对应的路由页面、菜单的数据使用mock模拟接口,菜单数据的请求

菜单页面

路由页面

在src文件夹下新增pages文件夹,新增三个文件夹home\setting\shop

1.新增首页文件,在home文件夹下新增index.tsx

const Home = ()=>{
    return (
        <div>
        <h1>Home</h1>
    </div>
    )
}
export default Home;

2.新增用户管理和角色管理页面,在setting文件夹下新增role.tsx和user.tsx

//role.tsx
const Role = () => {
  return (<div>Role</div>);
};
export default Role;
//user.tsx
const user = ()=>{
    return(<div>User</div>)
}
export default user;

 3. 新增商品分类、订单管理、商品管理页面,在shop文件夹下新增category.tsx、order.tsx和product.tsx

//category
const Category = ()=>{
    return(
        <div>
            <h1>Category</h1>
        </div>
    )
}
export default Category;
//order.tsx
const Order = ()=>{
    return (
        <div>Order</div>
    )
}
export default Order;
//product.tsx
const Product = ()=>{
    return(
        <div>
            Product
        </div>
    )   
}
export default Product;

路由配置

把新建的路由页面引入到路由文件中并添加一个重定向访问/时候重定向到/home,使用react-router中的Navigte组件

import { createBrowserRouter, Navigate } from "react-router-dom";

import AppLayout from "@/layout/index";
import Home from "@/pages/home";
import Role from "@/pages/setting/role";
import User from "@/pages/setting/user";
import Category from "@/pages/shop/category";
import Product from "@/pages/shop/product";
import Order from "@/pages/shop/order";

const routers = createBrowserRouter([
  {
    path: "/",
    element: <AppLayout />,
    children: [
      {
        path: "/",
        element: <Navigate to="/home" />,
      },
      {
        path: "/home",
        element: <Home />,
      },
      {
        path: "/setting/role",
        element: <Role />,
      },
      {
        path: "/setting/user",
        element: <User />,
      },
      {
        path: "/shop/category",
        element: <Category />,
      },
      {
        path: "/shop/product",
        element: <Product />,
      },
      {
        path: "/shop/order",
        element: <Order />,
      },
    ],
  },
]);

export default routers;

路由呈现

路由配置完成怎么能在游览器中输入不同的路由地址显示当前路由对应的内容呢,这里使用react-router中的Outlet组件。

修改layout文件夹下的main.tsx文件

import { Layout, theme } from "antd";
import {Outlet}  from 'react-router-dom'

const { Content } = Layout;
const AppMain = () => {
  const {
    token: { colorBgContainer, borderRadiusLG },
  } = theme.useToken();
  return (
    <Content
      style={{
        margin: "24px 16px",
        padding: 24,
        minHeight: 280,
        background: colorBgContainer,
        borderRadius: borderRadiusLG,
      }}
    >
      <Outlet />
    </Content>
  );
};
export default AppMain;

菜单数据

这里使用mock来模拟接口数据

引入mock

npm i mockjs @types/mockjs vite-plugin-mock -D

配置mock

vite-plugin-mock提供本地和生产模拟服务。

vite 的数据模拟插件,是基于 vite.js 开发的。 并同时支持本地环境和生产环境。 Connect 服务中间件在本地使用,mockjs 在生产环境中使用。

修改vite.config.ts文件

import * as path from 'path';
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { viteMockServe } from 'vite-plugin-mock'
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react(),
    viteMockServe({ 
      mockPath: './src/mock', // mock文件夹路径默认是 src/mock
      enable: true, // 默认是 false,可以根据环境变量开启
    }),
  ],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, './src') // 路径别名
    }
  }
})

菜单项属性

在src文件夹下新增types文件夹,在types文件夹下新增menu.d.ts 

// 菜单项属性
export interface MenuItemProps {
    id?: string;
    key:string;
    icon?:string;
    label:string;
    children?:MenuItemProps[];
  }

菜单mock数据

在src文件夹下新增mock文件夹

这里的文件夹名称需要和vite配置中viteMockServe的地址一致

 新增menu.ts

import Mock from "mockjs";
import { MenuItemProps } from "@/types/menu.d";
// 修正icon的类型问题,因为JSX元素不能作为JSON对象的一部分,这里已经改为字符串
const items:MenuItemProps[] = [
  {
    id: Mock.mock("@id"),
    key: "home",
    icon: "home",
    label: "首页",
  },
  {
    id: Mock.mock("@id"),
    key: "setting",
    icon: "setting",
    label: "系统管理",
    children: [
      {
        key: "user",
        label: "用户管理",
      },
      {
        key: "role",
        label: "角色管理",
      },
    ],
  },
  {
    id: Mock.mock("@id"),
    key: "shop",
    icon: "shop",
    label: "商城管理",
    children: [
      {
        key: "category",
        label: "商品分类",
      },
      {
        key: "product",
        label: "商品管理",
      },
      {
        key: "order",
        label: "订单管理",
      }
    ],
  }
];

export default [
  // 用户登录
  {
    url: "/api/menu",
    method: "GET",
    response: () => {
      return {
        code: 200,
        success: true,
        message: "请求成功。",
        data: items,
      };
    },
  },
];

菜单请求

引入axios

npm install axios

封装axios

在src文件夹下新增api文件夹,在api文件夹下新增request.ts文件

//request.ts
import axios, { AxiosRequestConfig } from "axios";
//接口返回数据
export interface ApiRes<T> {
  success: boolean;
  code: number;
  data?: T;
  message: string;
}
const instance = axios.create({
  baseURL: "/api",
  timeout: 5000,
});
// 添加请求拦截器
instance.interceptors.request.use(
  function (config) {
    // 请求成功做点什么
    return config;
  },
  function (error) {
    // 对请求错误做点什么
    return Promise.reject(error);
  }
);
// 添加响应拦截器
instance.interceptors.response.use(
  function (response) {
    // 对响应成功做点什么
    return response.data;
  },
  function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
  }
);

export default async <T>(config: AxiosRequestConfig) => {
  const response: ApiRes<T> = await instance(config);
  return response;
};

菜单请求

在api文件夹下新增menu.ts

//menu.ts
import request from './request'

// 获取当前用户信息
export const getMenu = () => {
  return request({
    method: 'GET',
    url: '/menu'
  })
}

菜单完善

菜单组件

在layout文件夹下新增menu.tsx

//layout/menu.tsx
import React from "react";
import { HomeOutlined, SettingOutlined, ShopOutlined } from "@ant-design/icons";
import { Menu } from "antd";
import { MenuItemProps } from "@/types/menu";
import { useNavigate } from "react-router-dom";

// 图标映射
const Icons = {
  home: HomeOutlined,
  setting: SettingOutlined,
  shop: ShopOutlined,
};
// 获取图标组件
const IconByName: React.FC<{ iconName: string }> = ({ iconName }) => {
  // 获取图标组件
  const IconComponent = Icons[iconName as keyof typeof Icons];
  // 返回图标组件
  return IconComponent ? <IconComponent /> : null;
};

// 侧边栏
const AppMenu: React.FC<{ menu: MenuItemProps[] }> = ({ menu }) => {
  const navigate = useNavigate();

  const handleMenu = ({ keyPath }: { keyPath: string[] }) => {
    const routerKey: string = keyPath.reverse().join("/");
    navigate( routerKey );
  };
  const menuData = menu.map((item: MenuItemProps) => {
    return {
      key: item.key,
      label: item.label,
      icon: item.icon ? <IconByName iconName={item.icon} /> : undefined,
      children: item.children?.map((child) => ({
        key: child.key,
        label: child.label,
      })),
    };
  });
  return (
    <Menu onClick={handleMenu} theme="dark" mode="inline" items={menuData} />
  );
};

export default AppMenu;

接口请求

修改layout文件夹下的sider.tsx

import React, { useEffect, useState } from "react";
import { Layout } from "antd";
import { getMenu } from "../api/menu";
import AppMenu from "./menu";
import { MenuItemProps } from "@/types/menu";
const { Sider } = Layout;

// 侧边栏
const AppSider: React.FC<{ collapsed: boolean }> = ({ collapsed }) => {
  // 菜单数据
  const [menu, setMenu] = useState([] as MenuItemProps[]);
  // 获取菜单数据
  useEffect(() => {
    // 获取菜单数据
    const getData = async () => {
      const res = await getMenu();
      const menuData = res?.data as MenuItemProps[];
      // 设置菜单数据
      setMenu([...menuData]);
    };
    getData();
  }, []);
  // 返回侧边栏
  return (
    <Sider trigger={null} collapsible collapsed={collapsed}>
      <div className="demo-logo-vertical" />
      <AppMenu menu={menu} />
    </Sider>
  );
};

export default AppSider;

 效果预览

在network中可以看到接口请求成功,菜单渲染出来,点击后可以正常跳转页面

一些说明

1.菜单中点击后路由跳转使用了react-router中的useNavigate

2.请求接口使用useEffect

3.查看接口请求发现请求了两次这里是因为使用了严格模式StrictMode

严格模式启用了以下仅在开发环境下有效的行为:

  • 组件将 重新渲染一次,以查找由于非纯渲染而引起的错误。
  • 组件将 重新运行 Effect 一次,以查找由于缺少 Effect 清理而引起的错误。
  • 组件将被 检查是否使用了已弃用的 API。

后续 

本篇文章为项目使用axios和mock模拟接口,代码已经同步到了gitee仓库,下一篇丰富具体的页面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2162710.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

将Pytorch环境打包,快速部署到另一台机器上(在没有网络,或者网络环境不好的情况下推荐使用)

打包PyTorch环境 当您需要在不同的机器上快速部署包含PyTorch的Python环境时&#xff0c;使用conda-pack是一个很好的选择。conda-pack可以打包一个完整的Conda环境&#xff0c;包括所有已安装的包和依赖项&#xff0c;使其能够轻松地在其他机器上还原。 步骤一&#xff1a;…

Django 对数据库的增删改查

新增 使用方法&#xff1a;类模型.objects.create 类模型 from django.db import models# Create your models here. class Car(models.Model):user models.CharField(max_length200)plate_number models.CharField(max_length20)def __str__(self):return f{self.user} -…

Linux之我不会

一、常用命令 1.系统管理 1.1 systemctl start | stop | restart | status 服务名 案例实操 1 查看防火墙状态 systemctl status firewalld2 停止防火墙服务 systemctl stop firewalld3 启动防火墙服务 systemctl start firewalld4 重启防火墙服务 systemctl restart f…

构建高可用和高防御力的云服务架构第一部分:深入解析DDoS高防(1/5)

引言 在数字化时代&#xff0c;网络安全已成为全球关注的焦点。随着互联网技术的快速发展和应用的广泛深入&#xff0c;网络安全形势日益严峻。特别是分布式拒绝服务&#xff08;DDoS&#xff09;攻击&#xff0c;以其破坏性强、难以防范的特点&#xff0c;对个人、企业乃至国…

Django操作ES实现搜索功能

Django操作ES实现题目的高亮搜索功能 一、基础配置二、使用ES完成题目的高亮搜索1. ES的初始化接口2. 使用ES实现题目的增删改查1. 题目的高亮搜索2. 题目的高亮搜索优化3. 将数据存储到MYSQL中持久化存储并同步到ES中一、基础配置 下载依赖,与之前配置的ES版本一致。 ES的配置…

Mac安装manim

文章目录 0.关于Manim1.官方安装方法2.使用conda2.1 下载Anaconda2.2 创建环境2.3 Required Dependencies 3.实验 今天发现Mac电脑以前安装的manim不能用了&#xff0c;索性重新装一次&#xff0c;顺便把过程记录下来。 0.关于Manim Manim 主要分为两个主要版本&#xff1a; …

使用VSCode远程调试AutoDL上的程序

在租用的AutoDL服务器上基于终端不便进行代码调试&#xff0c;而使用远程桌面控制部署也相对麻烦。pycharm 则需要专业版才能进行远程开发&#xff0c;如果使用Shell启动的程序也没有办法调试&#xff0c;最终选择利用 VScode 来进行远程代码调试。 一、远程租用并启动服务器 …

一步到位:如何在卡内基梅隆大学计算机科学学院官网下载并安装ACME软件

想要在卡内基梅隆大学计算机科学学院官网下载ACME软件&#xff1f;下面是详细的操作步骤&#xff01; 1. 访问官网 首先&#xff0c;打开卡内基梅隆大学计算机科学学院的官方网站。 2. 搜索ACME软件 在官网首页的搜索框中输入“acme”&#xff0c;然后按下回车键。 3. 找到下载…

大数据Flink(一百二十四):案例实践——淘宝母婴数据加速查询

文章目录 案例实践——淘宝母婴数据加速查询 一、​​​​​​​创建数据库表并导入数据 二、​​​​​​​​​​​​​​创建session集群 三、​​​​​​​​​​​​​​源表查询 四、​​​​​​​​​​​​​​指标计算 案例实践——淘宝母婴数据加速查询 随着…

【全新课程】正点原子《ESP32基础及项目实战入门》培训课程上线!

正点原子《ESP32基础及项目实战入门》全新培训课程上线啦&#xff01;正点原子工程师手把手教你学&#xff01;熟练掌握ESP-IDF开发&#xff0c;突破ESP32入门难题&#xff01; 一、课程介绍 本课程针对ESP32的入门和基础外设进行系统教学&#xff0c;内容包括环境搭建、编程…

C#和数据库高级:密封类和方法覆盖

文章目录 一、密封类关键字&#xff1a;sealed方法覆盖 面向对象三大特性总结 一、密封类 关键字&#xff1a;sealed 方法覆盖 面向对象三大特性总结

类与对象【中】

1.类的六个默认构造函数 如果一个类中什么成员都没有简称空类 但空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么都不写时&#xff0c;编译器会自动生成以下6个默认成员函数。 默认成员函数&#xff1a;用户没有显示实现&#xff0c;编译器会生成的成员函数…

公安局软件管理平台建设方案和必要性,论文-3-———未来之窗行业应用跨平台架构

三、平台功能设计 四、技术架构 1. 前端界面 - 采用简洁、易用的设计风格&#xff0c;适应不同终端设备的访问。 - 基于 HTML5、CSS3 和 JavaScript 构建。 2. 后端服务 - 选择主流的 Web 开发框架&#xff0c;如 未来之窗跨平台架构&#xff0c;VUE。 - 数据库…

IIS HTTPS 网页可能暂时无法连接,或者它已永久性地移动到了新网址 ERR_HTTP2_INADEQUATE_TRANSPORT_SECURITY

问题描述&#xff1a;站点突然无法访问&#xff0c;经排查发现&#xff0c;HTTP协议的网址可以继续访问&#xff0c;HTTPS的网址不可以访问。 问题分析&#xff1a;在Windows更新和滚动之后&#xff0c;由于 HTTP/2&#xff0c;当站点启动了 HTTP/2 连接&#xff0c;会出现一个…

【春秋云境】CVE-2024-23897-Jenkins 2.441之前版本存在任意文件读取漏洞

一、靶场介绍 Jenkins 2.441及更早版本&#xff0c;以及LTS 2.426.2及更早版本没有禁用其CLI命令解析器的一个功能&#xff0c;该功能会将参数中’字符后跟的文件路径替换为该文件的内容&#xff0c;允许未经身份验证的攻击者读取Jenkins控制器文件系统上的任意文件。 二、P…

Linux——HTTPS协议

HTTPS也是一个应用层协议&#xff1a;只是是在 HTTP 协议的基础上引入了一个加密层而已~ 目录 概念准备 1什么是加密 2为什么要加密 3常见的加密方式 对称加密 非对称加密 数据摘要(数据指纹) 一HTTPS加密方式 方案1只使用对称加密 方案2只使用非对称加密 方案3双…

C#|.net core 基础 - 深拷贝的五大类N种实现方式

在实际应用中经常会有这样的需求&#xff1a;获取一个与原对象数据相同但是独立于原对象的精准副本&#xff0c;简单来说就是克隆一份&#xff0c;拷贝一份&#xff0c;复制一份和原对象一样的对象&#xff0c;但是两者各种修改不能互相影响。这一行为也叫深克隆&#xff0c;深…

第6章 常用UI组件库

一.Element Plus组件库 1. 安装Element Plus 什么是Element Plus&#xff1f; Element Plus是基于Vue 3开发的优秀的PC端开源UI组件库&#xff0c;它是Element的升级版&#xff0c;对于习惯使用Element的人员来说&#xff0c;在学习Element Plus时&#xff0c;不用花费太多的…

CC面试准备

半导体基础 半导体是介于导体和绝缘体之间的一种介质&#xff0c;在不同条件下表现出不同的导电性或者不导电特性&#xff0c; 电子半导体器件材料大部分为硅&#xff0c;锗等元素 本征半导体&#xff1a;完全不含杂质的纯净半导体&#xff0c;因为不含杂质&#xff0c;其中…

[笔记]一组电缆、定位相关产品的技术参数

csdn不允许做广告&#xff0c;这里的那家定位供应商的技术看起来是可以的。很有希望。它的原理并不复杂&#xff0c;这家企业在处理业务领域以外的新型产品时&#xff0c;是查过资料的&#xff0c;这就超过了60%的同行。 1.电缆 仅给出现在市面供应的铠装电缆结构&#xff0c…