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

news/2024/9/25 20:07:17/

  前言


本文将引导开发者从零基础开始,运用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

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

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

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

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

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

路由配置

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

javascript">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文件

javascript">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文件

javascript">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 

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

菜单mock数据

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

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

 新增menu.ts

javascript">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文件

javascript">//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

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

菜单完善

菜单组件

在layout文件夹下新增menu.tsx

javascript">//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

javascript">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.ppmy.cn/news/1530431.html

相关文章

一分钟掌握 Java13 新特性

1. 文本块(Text Blocks,预览特性) Java 13 引入了文本块,允许开发者使用多行字符串字面量,简化了多行字符串的书写,尤其在处理 JSON、XML 等格式时更为方便。 示例: public class TextBlockDemo {public static void main(String[] args) {String json = ""…

使用 Docker 部署 RStudio 的终极教程

一.介绍 在现代数据科学和统计分析领域&#xff0c;RStudio 是一个广受欢迎的集成开发环境&#xff08;IDE&#xff09;&#xff0c;为用户提供了强大的工具来编写、调试和可视化 R 代码。然而&#xff0c;传统的 RStudio 安装可能面临环境配置复杂、版本兼容性等问题。Docker…

解锁高效工作的秘密武器

一、引言 ----  在快节奏的IT行业中&#xff0c;高效的工作方式对于开发者的成功至关重要。随着技术的发展&#xff0c;编程工具的种类和功能日益丰富&#xff0c;为开发者提供了更多的选择。那么&#xff0c;哪款编程工具能让你的工作效率翻倍&#xff1f;是智能的代码编辑器…

Delta Lake如何使用

1. 安装 Java 确保你的系统上安装了 Java 8 或更高版本。可以通过以下命令检查 Java 是否已安装&#xff1a; java -version2. 安装 Apache Spark 下载 Spark&#xff1a; 从 Apache Spark 官方网站 下载适合的版本&#xff0c;建议下载预编译的版本&#xff08;例如&#xf…

Linux 第四章 进程通信

文章目录 一、引言二、进程的同步与互斥2.1 进程合作2.2 共享资源2.3 临界区与临界资源2.4 同步机构设计准则 三、互斥的软件方法四、硬件指令机制五、信号量机制5.1 整型信号量5.2 结构型信号量5.3 用信号量实现互斥5.4 用信号量实现同步 六、经典进程同步问题6.1 生产者—消费…

linux静态路由表

使用ip route命令临时添加静态路由表 使用ip route命令添加静态路由。例如&#xff0c;要添加一条到达192.168.1.0/24网络的静态路由&#xff0c;通过网关192.168.0.1&#xff0c;您可以执行&#xff1a; ip route add 192.168.1.0/24 via 192.168.0.1 dev enp1s0编辑网络配置…

数据结构之结构体

1.求 sizeof(name1)&#xff1f;(晟安信息) struct name1{ char str; //1 short x;//2 int num;//4 }; 答案&#xff1a;8字节 2.(电工时代) typedef struct _a { char c1;//1 long i;//8 char c2;//1 double f;//8 }a; typedef struct _b { char c1;//1 char c2;//1 long i;//…

浅拷贝和深拷贝(Java 与 JavaScript)

一、Java 浅拷贝和深拷贝 在Java中&#xff0c;浅拷贝和深拷贝的主要区别在于对对象的引用和内容的复制方式。 浅拷贝 Java 的类型有基本数据类型和引用类型&#xff0c;基本数据类型是可以由 CPU 直接操作的类型&#xff0c;无论是深拷贝还是浅拷贝&#xff0c;都是会复制出…