react-ant-admin Documentation

website·Indexed 31 days ago

https://z3web.cn/doc-react-ant-admin/

Documentation for react-ant-admin, a framework for building administrative back-ends. Includes guides on path configuration, theme color customization using antd@5, menu creation, user permission association, and project file structure for both Webpack and Vite builds.

Tokens
25.8K
Snippets
120
Records
137
Agent score
62%

What's inside react-ant-admin

  1. Overview of react-ant-admin features

    react-ant-admin is a framework designed for the secondary development of admin web projects to enable rapid setup. Key features include:

    • Menu Configuration: Flat data organization for easy writing, storage, and synchronization across page menus, sidebars, and top navigation.
    • Page Lazy Loading: Reduces initial load times.
    • Ajax Requests: Follows RESTful standards with configurable automatic error prompts and automatic cancellation of unfinished requests.
    • Permission Control: Menu visibility based on user roles and route page interception.
    • Custom Themes: Ability to define custom interface colors.
    • Proxy Forwarding: Solves frontend cross-origin (CORS) request issues.
    • Automated Routing: Decentralized automated routing implementation.
  2. Create an Express server to handle file uploads with Multer

    To support file uploads from a frontend, use express and multer. The following setup includes CORS headers to allow cross-origin requests from a React development server and uses multer to process the incoming multipart/form-data files.
    const express = require('express');
    const multer = require('multer');
    const path = require('path');
    const fs = require('fs');
    const app = express();
    
    // CORS Configuration
    app.all("*", (req, res, next) => {
        res.header("Access-Control-Allow-Origin", req.headers.origin || '*');
        res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
        res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
        res.header("Access-Control-Allow-Credentials", true);
        if (req.method == 'OPTIONS') res.sendStatus(200);
        else next();
    });
    
    // File Upload Endpoint
    app.post('/upload', (req, res, next) => {
        const upload = multer({ dest: 'upload/' }).any();
        upload(req, res, err => {
          if (err) return console.log(err);
          
          let file = req.files[0];
          let newPath = path.join(process.cwd(), "upload/" + new Date().getTime() + file.originalname);
          
          fs.createReadStream(file.path).pipe(fs.createWriteStream(newPath)).on("end", () => {
            fs.unlink(file.path, err => {
              if (err) return console.log(err);
              res.send("ok");
            });
          });
        });
    });
    
    app.listen(5000);
  3. Dependencies for Node.js image scraping

    The following npm packages are required for implementing the image scraping logic described in this guide:
    npm install axios cheerio request
    • axios: Used for making HTTP requests to fetch page content.
    • cheerio: Used for parsing HTML and manipulating the DOM using jQuery-like syntax.
    • request: Used for downloading the image files from the extracted URLs.
    • fs, path, url: Built-in Node.js modules for file system operations and URL handling.
  4. Render nested routes with Outlet

    When a <Route> has child routes, use the <Outlet /> component within the parent's element to specify where the child route content should be rendered.
    import { Outlet, Link } from 'react-router-dom';
    
    function User() {
      return (
        <div>
          <h1>User page</h1>
          <Link to="abc">go User abc</Link>
          <hr />
          {/* Child routes (like :id) render here */}
          <Outlet />
        </div>
      );
    }
  5. Manage menu access permissions via API response

    Menu permissions are controlled by the data returned from the backend. If a specific menu item is missing from the returned list, the user is considered to have no permission for that path and will be intercepted when attempting to access it.
    // User 1: Can access both /parent/child1 and /parent/child2
    [
      { "id": 1, "name": "Main Menu", "parent_id": 0, "url": "/parent" },
      { "id": 2, "name": "Sub Menu 1", "parent_id": 1, "url": "/child1" },
      { "id": 3, "name": "Sub Menu 2", "parent_id": 1, "url": "/child2" }
    ]
    
    // User 2: Can access /parent/child1, but /parent/child2 is missing and will be blocked
    [
      { "id": 1, "name": "Main Menu", "parent_id": 0, "url": "/parent" },
      { "id": 2, "name": "Sub Menu 1", "parent_id": 1, "url": "/child1" }
    ]
  6. Set the default landing page route

    To change the default page that opens when the project starts (which defaults to /), configure the defaultArr variable in src/router/list.js to redirect the root path to a specific destination.
    // ./src/router/list.js
    import auto from "./auto";
    import loadable from "@loadable/component";
    import { Redirect } from "react-router-dom";
    const Error = loadable(() => import("@pages/err"));
    
    const defaultArr = [
      {
        path: "/", // Access address
        key: "index",
        to: "/details/person", // Redirect to this path
        components: Redirect, // Redirection component
      },
    ];
    const list = auto.map((c) => ({ ...c, components: loadable(c.components) }));
    
    list.push(...defaultArr);
    
    export default list;
  7. Define and use custom theme Less variables

    Custom theme variables can be added to your project/theme/var.less. To use these variables dynamically in components, define a TypeScript interface for the custom tokens and use the useThemeToken hook with createStyles from antd-style.
    // your project/theme/var.less
    @test-color: red;
    @test-size: 22;
    // src/theme.ts
    interface CustomThemeToken {
      'test-color': string;
      'test-size': number;
      [key: string]: string | undefined;
    }
    import { useThemeToken } from '@/hooks';
    import { createStyles } from 'antd-style';
    
    const createStyle = createStyles((({ css }, token) => ({
      color: css`
        color: ${token['test-color']};
      `,
      font: css`
        font-size: ${token['test-size']}px;
      `
    })));
    
    function Comp() {
      const { styles } = createStyle(useThemeToken());
      return (
        <div>
          <p className={styles.color}>Used test-color</p>
          <span className={styles.font}>Used test-size</span>
        </div>
      );
    }
  8. Capture a single frame from a video and save it as an image

    To capture a specific frame from a video, you can use a hidden <video> element to load the source and a <canvas> element to draw the current frame. The process involves setting the currentTime of the video, waiting for the frame to buffer, and then using canvas.toDataURL() to convert the frame into a base64 image string for downloading via an <a> tag.

    Key implementation details:

    • Use crossOrigin = 'anonymous' on the video element to handle CORS issues if the video is hosted on a different domain.
    • Use a setTimeout (buffering time) after setting currentTime to ensure the frame is fully loaded before drawing to the canvas, otherwise, the resulting image may be black.
    • The drawImage method of the canvas 2D context is used to capture the video frame.
    <button id="btn">Start Capture</button>
    <img id="img">
    <a id="a" download="" style="display: none;">Download</a>
    function videoCover(src, width, height, second, bufftime) {
        const canvas = document.createElement('canvas');
        const video = document.createElement('video');
        const img = document.getElementById('img');
        const a = document.getElementById('a');
    
        video.setAttribute('crossOrigin', 'anonymous');
        document.body.appendChild(video);
        video.setAttribute('src', src);
        video.style.visibility = "hidden";
    
        video.onplay = function() {
            video.pause();
            video.currentTime = second || 1;
            video.height = height || video.clientHeight;
            canvas.width = width || video.clientWidth;
            canvas.height = height || video.height;
    
            setTimeout(() => {
                canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
                const IMG_TYPE = "png";
                const b64 = canvas.toDataURL('image/' + IMG_TYPE);
                
                img.setAttribute('src', b64);
                a.setAttribute('href', b64);
                a.setAttribute('download', "screenshot." + IMG_TYPE);
                a.click();
                
                document.body.removeChild(video);
            }, bufftime * 1000 || 3000);
        };
    
        video.onloadeddata = () => {
            video.play();
        };
    }
    
    // Usage
    document.getElementById("btn").onclick = function() {
        videoCover("video_url_here", null, 400, 1, 3);
    };
  9. Implement route lazy loading in React using @loadable/component

    To reduce the initial bundle size and improve first-screen loading speed in React, use @loadable/component to dynamically import page components. This ensures that JavaScript files for specific routes are only loaded when the user navigates to those routes.
    npm install @loadable/component -D
    # or
    yarn add @loadable/component -D
  10. Copy text to clipboard using the modern navigator.clipboard API

    The navigator.clipboard.writeText() API is the modern, asynchronous standard for clipboard access.

    Constraints:

    • It is only available in secure contexts (HTTPS or localhost). In non-secure environments, navigator.clipboard will be undefined.
    • Supported in modern mainstream browsers (roughly 2018 onwards).
    /**
     * Copy text to clipboard using navigator.clipboard
     * @param {string} text
     * @returns {Promise} Returns a promise
     */
    function clipboardCopyText(text) {
        if (navigator.clipboard) {
            return navigator.clipboard.writeText(text)
        }
        return Promise.reject("Unable to access navigator.clipboard object")
    }
    
    // Usage
    clipboardCopyText("Hello World").then(() => {
        console.log("Copied successfully");
    }).catch(err => {
        console.log("Copy failed", err);
    });
  11. Understand JavaScript parameter passing for reference types

    JavaScript passes reference types (such as Objects) by passing the pointer address.

    1. Mutating properties: If you modify a property of an object passed as an argument, the change affects the original object because both the original variable and the function parameter point to the same memory address in the heap.
    2. Re-assigning the object: If you re-assign the parameter variable to a new object (e.g., obj = {}), you change the pointer for that local variable only. This breaks the link to the original object, and subsequent modifications to the local variable will not affect the original object.
    var p1 = { name: "Tom" };
    
    function b(obj) {
        // Step 1: Mutating a property affects the original object
        obj.name = "Jerry"; 
        
        // Step 2: Re-assigning the variable changes the local pointer
        obj = {}; 
        obj.name = "Jack"; 
        
        return obj;
    }
    
    b(p1); // returns { name: "Jack" }
    console.log(p1.name); // "Jerry" (affected by Step 1, but not Step 2)