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

  前言


本文将引导开发者从零基础开始,运用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 (<Contentstyle={{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/mockenable: 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.xdnf.cn/news/148922.html

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

相关文章

SAP B1 认证考试习题 - 基础主数据(解析版)

感谢投喂*罒▽罒* 一、基础 1. 下列哪个产品不是以中小型企业为目标客户的 A. mySAP All-in-One B. SAP Business One C. mySAP Business Suite 答案&#xff1a;C 解析&#xff1a;SAP Business One -- 为小型企业定制的解决方案&#xff08;250人以下&#xff09;&…

【论文】FunAudioLLM:一个旨在增强人类与大型语言模型(LLMs)之间自然语音交互的模型家族

研究背景 1.研究问题&#xff1a;这篇文章要解决的问题是如何增强人类与大型语言模型&#xff08;LLMs&#xff09;之间的自然语音交互。具体来说&#xff0c;研究集中在语音识别、情感识别和音频事件检测&#xff08;多语言&#xff09;以及语音生成&#xff08;多语言、零样…

Python模拟真人鼠标轨迹

一.API跨语言平台支持 鼠标轨迹API 底层实现采用 C/C 语言&#xff0c;利用其高性能和系统级访问能力&#xff0c;开发出高效的鼠标轨迹模拟算法。通过将算法封装为 DLL&#xff08;动态链接库&#xff09;&#xff0c;可以方便地在不同的编程环境中调用&#xff0c;实现跨语言…

【C++】容器适配器,stack,queue,priority_queue详解,模拟实现

目录 1. stack和queue的介绍 1.1 stack的成员函数 1.2 queue的成员函数 1.3 stack的使用 1.4 queue的使用 1.5 Container模板参数&#xff0c;deque 2. priority_queue优先级队列的介绍 3. stack模拟实现 3.1 初始结构 3.2 push 3.3 pop 3.4 top 3.5 empty 3.6 s…

C++笔试强训15、16、17

文章目录 笔试强训15一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训16一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训17一、选择题1-5题6-10题 二、编程题题目一题目二 笔试强训15 一、选择题 1-5题 共有派生下&#xff0c;派生类的成员函数只能访问基类的…

揭秘智能派单流程:如何利用AI实现高效的自动化任务分配?

前言 在当今的企业管理和服务行业中&#xff0c;高效的工作分配与任务管理是提升企业竞争力的重要因素。智能派单流程通过结合先进的算法和人工智能技术&#xff0c;实现了工作任务的自动化分配和优化管理&#xff0c;不仅帮助企业提升了工作效率&#xff0c;降低了运营成本&a…

Kubernetes强制删除terminating状态的namespace

Kubernetes中的Namespace处于Terminating状态并且常规删除不起作用。 1.Namespace长时间处于Terminating状态往往是因为某些finalizers阻止了它的删除。 kubectl get namespace <namespace-name> -o json > namespace.json 2.编辑生成的 namespace.json文件&#xff…

在 Vue 3 中实现“折叠”与“展开”文本内容

偶然间遇到一个场景&#xff0c;怎么判断一段文本是否超过 5 行或者指定行数&#xff0c;并在超过时显示 "展开/收起" 按钮。那应该如何实现呢&#xff1f; 在 Vue 3 的项目下实现&#xff1a; <template><div class"text-container"><di…

计算机毕业设计 学院网站系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

Java框架学习(Spring)(ioc)(01)

简介&#xff1a;以本片记录在尚硅谷学习ssm-spring-ioc时遇到的小知识 详情移步&#xff1a;想参考的朋友建议全部打开相互配合学习&#xff01; 视频&#xff1a; 014-spring-框架概念理解_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1AP411s7D7?p14&vd_sou…

【楚怡杯】职业院校技能大赛 “云计算应用” 赛项样题九

某企业根据自身业务需求&#xff0c;实施数字化转型&#xff0c;规划和建设数字化平台&#xff0c;平台聚焦“DevOps开发运维一体化”和“数据驱动产品开发”&#xff0c;拟采用开源OpenStack搭建企业内部私有云平台&#xff0c;开源Kubernetes搭建云原生服务平台&#xff0c;选…

GIS留学院校介绍-英国篇

看前须知 关于语言成绩要求&#xff1a; 通常英国院校的雅思成绩要求分为5个等级&#xff0c;标准分别如下&#xff1a; 1级&#xff1a;总分6.5分&#xff0c;每个部分最低6.0分 2级&#xff1a;总分7.0&#xff0c;每个部分至少6.5分 3级&#xff1a;总分7.0分&#xff…

2024年有什么开放式耳机推荐?盘点开放式蓝牙耳机排行榜前五名

​到了2024年&#xff0c;开放式耳机无疑成为了耳机市场的宠儿。它们的优势在于&#xff0c;不仅佩戴舒适&#xff0c;还能在保护听力的同时&#xff0c;让你保持对周围环境的警觉&#xff0c;这对于爱好户外探险的朋友来说&#xff0c;无疑是一个巨大的安全加分项。作为一名资…

(附源码)微信小程序的拼车设计-计算机毕设19413

微信小程序的拼车设计 摘 要 在微信小程序的拼车服务中&#xff0c;后端架构巧妙地运用了SSM&#xff08;Spring、SpringMVC、MyBatis&#xff09;框架&#xff0c;为用户带来了流畅、高效的体验。Spring框架作为整个系统的核心&#xff0c;不仅管理着业务逻辑&#xff0c;还通…

分布式光伏的发电监控

国拥有丰富的清洁可再生能源资源储量&#xff0c;积极开发利用可再生能源&#xff0c;为解决当前化石能源短缺与环境污染严重的燃眉之急提供了有效途径[1]。但是可再生能源的利用和开发&#xff0c;可再生能源技术的发展和推广以及可再生能源资源对环境保护的正向影响&#xff…

PCB生产,在钻咀和成品孔径之间,你会优先满足哪一项呢

高速先生成员--王辉东 曹梦总是说&#xff0c;人生如《忐忑》&#xff0c;虽然没有准确的歌词&#xff0c;却演绎的惊心动魄…… 她是工厂的工程评估员&#xff0c;对于PCB的热爱&#xff0c;就像拖拉机上山&#xff0c;轰轰烈烈&#xff0c;不知疲倦。 她一向秉承的原则是&…

Excel名字查重筛选,查找重复内容原来这么简单

大家好&#xff0c;这里是效率办公指南&#xff01; &#x1f50d; 在处理大量数据时&#xff0c;尤其是人员名单或客户信息时&#xff0c;确保没有重复的名字是非常重要的。在Excel中&#xff0c;有几种方法可以帮助我们快速查找和处理重复的名字。今天&#xff0c;我们将介绍…

[linux 驱动]块设备驱动详解与实战

目录 1 描述 2 结构体 2.1 block_device_operations 2.2 gendisk 2.3 block_device 2.4 request_queue 2.5 request 2.6 bio 3.7 blk_mq_tag_set 3.8 blk_mq_ops 3 相关函数 3.1 注册注销块设备 3.1.1 register_blkdev 3.1.2 unregister_blkdev 3.2 gendisk 结构…

算法思想之前缀和

前缀和&#xff1a;快速求出数组中某连续区间的和 一.一维前缀和(模板) 1.题目&#xff1a;【模板】前缀和_牛客题霸_牛客网 (nowcoder.com) 给定一个长度为n的数组a1,a2,....ana1​,a2​,....an​.&#xff0c;接下来有q次查询, 每次查询有两个参数l, r&#xff0c;对于每个…

打造你的专属主题-VitePress保姆级教程

本篇为vitepress系列教程&#xff0c;在开始前&#xff0c;若还不了解vitepress的小伙伴可以看一下以往文章&#xff1a; 不敲一行代码&#xff01;助你快速搭建属于自己的官网博客&#xff01;-VitePress保姆级教程 文章目录 VitePress主题配置准备自定义主题配置标题配置图标…