React Hooks实现自定义表格列

React中使用react-sortablejs实现自定义表单设计器,实现拖拽,排序功能 本文,主要讲解了如何使用react-sortablejs自定义实现一个表单设计器,文章中的代码涉及到拖拽和排序。 阅读详情

一、前言

目标

在ant design的Table组件基础之上利用react-dnd实现表格列的拖拽排序、并自定义列的显示隐藏。

Tips
  • 请先了解ant design组件库中Table组件的用法,本文不再展开
  • 本文不展开介绍react-dnd的基础知识,不太了解它的同学可以先参考文末的文章学习
当前版本
  • react: 16.14.0
  • react-dom: 16.14.0
  • antd: 3.26.20
  • react-dnd: 11.1.3
  • react-dnd-html5-backend: 11.1.3

二、实现代码

为自定义的表格组件取名CustomColumnTable

拖拽方案说明
  • 在组件之外还得用DndProvider包裹,否则无法使用拖拽功能
  • 拖拽实现方案是拖拽表头列实现整列位置替换
  • 表头列既可以被拖拽也可以接受被拖拽的列
  • 缺陷:无法有效过滤 表格行可选择(rowSelection)时的选择列 和 固定列(fixed)
传入参数说明

这里是分了两种情况:

当外部组件传入的columns数组不会发生变化
  • 此时dynamicColumns为false,意味着之后表格列的拖拽排序与显示隐藏全由封装的CustomColumnTable组件来控制
当columns数组会发生变化时
  • 比如在不同场景下,显示的表格列名不同,展示方式不同,这时外部组件传入的columns可能会发生变化,无法完全交由CustomColumnTable组件控制。此时外部组件传入columns时可以为每个子项添加selected属性,表示CustomColumnTable组件是否能控制该列的显示隐藏和拖拽
  • 同时外部组件需传递dynamicColumns onChangeColumn两个参数,且onChangeColumn函数的参数是已处理好的新columns数组,外部组件拿到后可以用来替换原columns数组
区分selected和visible
  • selected表示CustomColumnTable组件是否能控制该列显示隐藏和拖拽位置,默认为true,false表示CustomColumnTable组件暂时无法控制它
  • visible表示列是否显示,这由完全CustomColumnTable组件来控制,true显示,false隐藏
  • selected的优先级比visible高,在列的selected属性为false下,无论visible属性是否为true,表格都不会显示该列
import React, { useState, useRef } from 'react';
import { Button, Checkbox, Popover, Table } from 'antd';
import { createDndContext, DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';

const type = 'DragTableHeadCol';
const DNDContext = createDndContext(HTML5Backend);

const swapArray = (arr, index1, index2) => {
  const dragCol = arr[index1];
  arr.splice(index1, 1);
  arr.splice(index2, 0, dragCol);
  return arr;
};

/**
 * @param  index    表头列的位置下标
 * @param  moveCol  拖拽结束时排序方法
 */
const dragdrop = (index, moveCol) => {
  const ref = React.useRef();
  const [, drop] = useDrop({
    accept: type,
    drop: item => {
      // item.index表示被拖拽组件的下标
      // index是接受被拖拽组件的下标
      moveCol(item.index, index);
    },
  });
  const [, drag] = useDrag({
    item: { type, index },
  });
  // 让组件既可以被拖拽也可以接受被拖拽组件
  drop(drag(ref));
  return { ref };
};

// 对tableHeadRow进行封装
const DragTableHeadRow = ({ children, moveCol }) => {
  // 对列进行处理,使其可拖拽
  const Ths = children.map((th, index) => {
    const {
      props: { className, style, children: thChildren },
    } = th;
    const { ref } = dragdrop(index, moveCol);

    const cloneTh = React.cloneElement(
      th,
      {
        ...th.props,
        ref,
        style: { cursor: 'move', ...style },
      },
      thChildren,
    );
    return cloneTh;
  });
  return <tr>{Ths}</tr>;
};

/**
 * @param {columns} 传入的列
 * @param {dynamicColumns} 若传入的columns是动态变化的(columns数组的元素有动态增减),传true
 * @param {onChangeColumn} 由外部管理列的变化 dynamicColumns = true时必传,参数为新的表格列
 */
const CustomColumnTable = ({
  columns,
  dynamicColumns = false,
  onChangeColumn,
  ...props
}) => {
  // 过滤不被选择的列(默认全选)
  const initColumns = columns.filter(item => item.selected ?? true);
  // dynamicColumns = true 下使用
  const visibleColumns = initColumns.filter(item => item.visible ?? true) || [];
  // 初始化nowColumns (dynamicColumns = false 下使用,默认全选)
  const [nowColumns, setColumns] = useState(initColumns.map(item => ({ visible: true, ...item })));
  // 真正渲染的列
  const realColumns = dynamicColumns
    ? visibleColumns
    : nowColumns.filter(item => item.visible) || [];
  // 可拖拽的列
  const dragColumns = dynamicColumns ? columns : nowColumns;
  // 初始化checkBoxChecked (dynamicColumns = false 下使用,默认全选)
  const [checkBoxChecked, setCheckBoxChecked] = useState(initColumns.map(item => item.title));
  // 真正显示列的title数组
  const realCheckBoxChecked = dynamicColumns
    ? visibleColumns.map(item => item.title)
    : checkBoxChecked;

  // 拖拽结束处理列位置的函数
  const moveCol = (dragIndex, hoverIndex) => {
    const newColumns = swapArray(
      dragColumns,
      dragColumns.findIndex(item => realColumns[dragIndex].title === item.title),
      dragColumns.findIndex(item => realColumns[hoverIndex].title === item.title),
    );
    if (dynamicColumns) {
      onChangeColumn(newColumns);
    } else {
      setColumns([].concat(newColumns));
    }
  };

  /** 
   * @param targetItem 点击的目标元素
   * @param visible    是否显示
   * @param list       显示(选中)的元素列表
   */
  const checkCol = (targetItem, visible, list) => {
    if (dynamicColumns) {
      const tempColumns = columns.map(item => {
        if (targetItem === item.title) return { ...item, visible };
        return item;
      });
      onChangeColumn(tempColumns);
    } else {
      setColumns(state =>
        state.map(item => {
          if (targetItem === item.title) return { ...item, visible };
          return item;
        }),
      );
      setCheckBoxChecked(list);
    }
  };

  /**
   * @param isCheckAll 是否全选或全不选
   */
  const checkAll = isCheckAll => {
    if (dynamicColumns) {
      const tempColumns = columns.map(item =>
        item.selected ?? true ? { ...item, visible: isCheckAll } : item,
      );
      onChangeColumn(tempColumns);
    } else {
      const checked = nowColumns.map(item => item.title);
      setCheckBoxChecked(isCheckAll ? checked : []);
      setColumns(state => state.map(item => ({ ...item, visible: isCheckAll })));
    }
  };

  const components = {
    header: {
      row: prop => {
        return <DragTableHeadRow {...prop} moveCol={moveCol} />;
      },
    },
  };

  const menu = (
    <>
      <Checkbox
        checked={realCheckBoxChecked.length === initColumns.length}
        indeterminate={
          realCheckBoxChecked.length !== initColumns.length && realCheckBoxChecked.length > 0
        }
        onClick={() => {
          const isCheckAll = realCheckBoxChecked.length !== initColumns.length;
          checkAll(isCheckAll);
        }}
      >
        全部
      </Checkbox>
      <Checkbox.Group
        style={{ width: '100%' }}
        value={realCheckBoxChecked}
        onChange={values => {
          if (values.length > realCheckBoxChecked.length) {
            const showItem = values.find(item => !realCheckBoxChecked.includes(item));
            checkCol(showItem, true, values);
          }
          if (values.length < realCheckBoxChecked.length) {
            const hideItem = realCheckBoxChecked.find(item => !values.includes(item));
            checkCol(hideItem, false, values);
          }
        }}
      >
        {initColumns.map(item => (
          <div key={item.title} style={{ minWidth: '200px' }}>
            <Checkbox value={item.title} disabled={item.title === '操作' || item.title === '序号'}>
              {item.title}
            </Checkbox>
          </div>
        ))}
      </Checkbox.Group>
    </>
  );

  const manager = useRef(DNDContext);

  return (
    <>
      <div style={{ textAlign: 'left', margin: '4px' }}>
        <Popover content={menu} placement="bottomLeft" trigger="click">
       	  <Button icon="filter" size="small" />
        </Popover>
       </div>
      <DndProvider manager={manager.current.dragDropManager}>
         <Table {...props} columns={realColumns} components={components} />
      </DndProvider>
    </>
  );
};

export default CustomColumnTable;

三、拓展

自定义接受组件的样式

react-dnd的useDrop函数返回的第一个参数是其collect函数返回的对象,在collect函数里可以返回几个需要用到的属性
注意:这里添加了自定义类名drop-over

const dragdrop = (index, moveCol) => {
  const ref = React.useRef();
  const [{ isOver }, drop] = useDrop({
    accept: type,
    collect: monitor => {
      // 获取被拖拽的元素
      const { index: dragIndex } = monitor.getItem() || {};
      // 若被拖拽的元素和接受元素是同一个,则返回为空
      if (dragIndex === index) return {};
      return {
        // 返回 isOver
        isOver: monitor.isOver()
      };
    },
    drop: item => {
      moveCol(item.index, index);
    },
  });
  const [, drag] = useDrag({
    item: { type, index },
  });
  drop(drag(ref));
  // 返回 isOver
  return { ref, isOver };
};
 ...
 
const DragTableHeadRow = ({ children, moveCol }) => {
  // 对列进行处理,使其可拖拽
  const Ths = children.map((th, index) => {
    const {
      props: { className, style, children: thChildren },
    } = th;
    const { ref, isOver } = dragdrop(index, moveCol);

   // 拿到 isOver后,便可以用来判断添加自定义样式
    const cloneTh = React.cloneElement(
      th,
      {
        ...th.props,
        ref,
        className: `${className} ${isOver ? 'drop-over' : ''}`,
        style: { cursor: 'move', ...style },
      },
      thChildren,
    );
    return cloneTh;
  });
  return <tr>{Ths}</tr>;
};
自定义被拖拽组件样式

方法原理同上,这次使用到的是useDrag函数

const [{ isDragging }, drag] = useDrag({
    item: { type, index },
    collect: monitor => ({
      isDragging: monitor.isDragging(),
    }),
  });

四、实现效果




五、参考文章

对于react-dnd如何应用可参考 用 React Hooks 的方式使用 react-dnd

react 封装表格组件_基于 Antd Protable 及 ahooks 的表格组件业务封装 React hooks Protable 业务封装基于 Antd Protable 及 ahooks 的表格组件业务封装Protable:https://procomponents.ant.design/components/tableahooks:https://ahooks.js.org/zh-CN/hooks/async取消 request 属性,增加 url、moreLoading 字段。url: 为接口 url... 阅读详情

相关推荐

2021哈工大网安837考研初试经验贴

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Mar

weixin_41797880的博客 6523

react中使用antd中的 Table 组件(数据展示,分页,设置可选项)

React是由Facebook开发的一种用于构建用户界面的JavaScript库,它提供了一种声明式的编程模型,使得构建复杂的UI变得更加简单和高效。React的组件化开发模式使得代码可维护性更强,而Antd的可重用组件则减少了开发者的重复劳动,同时保证了应用的一致性和用户体验。在使用表格时不知大家有没有被里面各种各样的功能所困扰,本来你只需要使用其中一个功能,但官网的查看代码总是多个功能重合在一次展示,不利于大家去学习,面对初学者很不友好,这也是我写这篇的目的,我将里面常用的功能分离拿出来介绍。

g2255yuuhgy的博客 1万+

SRAM compiler中 Number of banks 设置

的值时,需要考虑应用的性能需求、功耗要求和面积限制。设计者应根据具体的应用场景选择合适的bank数量,以优化整体系统性能。bank是一种用于分离存储器访问的设计结构,允许同时对不同的存储区域进行读写操作,从而提高存储器的并发访问性能。选项用于指定 SRAM 存储器的 并发存储区域。在 SRAM 编译器中,

hzmscut的博客 1872

react 基于ant.design如何完成自定义Table列组件

react 自定义table列组件 react 自定义table列组件,没有进一步放入缓存,仅线上配置自定义列 新建一个组件文件 : ToCustomColumns.js 代码如下: import React, { Component } from 'react'; import { Popover, Button, Checkbox, Row, Col, Card } from 'antd'; import styles from './style.less'; class ToCustomCol

weixin_47433173的博客 1626

creo二次开发使用映射

在creo进行二次开发时,一些无法直接通过访问底层代码来对特征进行操作,比如“镜像装配”,这个时候就可以使用映射(Mapkey)来完成一键镜像。 注:这是镜像装配的代码 ProError status; ProMacro macro; wstring Macro; strMacro = L"mapkey(continued) ~ Command ProCmdMirrorAssem ; mapkey(continued) ~ Activate mirror_component_dialog Sy

qq_49328512的博客 1626

Scratch 3.0角色控制避坑指南:为什么你的键盘移动总卡顿?

本文深入剖析了Scratch 3.0中角色移动卡顿的根本原因,指出依赖`当按下`事件积木的局限性。通过对比分析,推荐采用`重复执行`结合`如果<键被按下?>`的循环控制方案,并引入速度变量模拟惯性,从而实现流畅、实时的键盘控制优化,显著提升游戏操作体验。

dd012的博客 1014

react antd design columns 配置解析

只上干货,其中有些可以参考官方文档 import { history } from 'umi' const columns: any[] = [ { title: '订单号',//列头显示的文字 fixed: 'left', //整列固定在左侧(right等参数 其中fixed:true ==fixed:left) width: 200,//列头的宽度 order: 7,//列头的排序 key:"id",//搜索时候重定义搜索参数..

qq_37554314的博客 3376

ABAP 大规模数据并行处理实战,六套 Parallel Processing Framework 如何选,性能调优真正该盯住什么

如果今天真的面对那份执行 14 个小时的生产报表,我不会马上把原来的 Loop 改成 50 个aRFC。我会先用 SAT、ST12、SQL Monitor 和 HANA 侧工具确认时间到底花在哪里。如果 90% 时间其实消耗在一条低效 SQL 上,Parallel Processing 只是让 50 个 Worker 同时执行那条低效 SQL,数据库会比原来更痛苦。应该先把串行逻辑本身优化到合理水平。该做 Set-based Processing 的地方先交给 HANA。该避免SELECT。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 149

react--自定义列表字段

支持react表格自由选择展示的字段,支持全选 2、组件使用 页面导入 import CustomColumns from "@/components/CustomColumns"; 设置默认状态: 3、定义tableColumns 4、在table组件中过滤出操作选项和默认项 5、组件调用

sunlight_01的博客 974

基于matlab的阈值分割的车牌定位识别系统

车牌自动识别模块是现代社会智能交通系统(ITS)的重要组成部分,是图像处理和模式识别技术研究的热点,具有非常广泛的应用。车牌识别主要包括以下三个主要步骤:车牌区域定位、车牌字符分割、车牌字符识别。本案例通过对采集的车牌图像进行灰度变换、边缘检测、腐蚀及平滑等过程进行车牌图像预处理,并由此得到一种基于车牌颜色纹理特征的车牌定位方法,最终实现了车牌区域定位。车牌字符分割是为了方便后续对车牌字符进行匹配,从而对车牌进行识别。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 969

SpringBoot实战教程-springboot概论

Spring Boot 是一个快速开发框架,能够快速的整合第三方框架,简化XML配置,全部采用注解形式,以及自动配置。内置web容器,帮助开发者能够实现快速开发,简化了应用系统的初始搭建以及开发过程。

鸟哥哥的专栏 4155

react-antd-column-resize 实现 Ant Design 表格列宽拖动

这是一个基于React Hooks开发的组件,专门用于实现Ant Design表格(table)的列宽度拖动调整功能。如果你对该组件感兴趣并打算使用它,我强烈建议你给它一个star,以支持这个开源项目的发展。通过使用这个Hooks,你可以轻松地定义拖动调整列宽度的最小宽度、最大宽度和默认宽度。当今的Web应用开发中,表格是一个非常常见且重要的组件。然而,许多开发者在使用表格组件时常常面临一个问题:如何让表格的列宽度可以拖动调整?在你的React组件中,你可以按照下面的代码示例来使用。宽度所需的必要数据。

weixin_46446590的博客 2052

基于Python的军事装备数据爬取与分析:从网页抓取到数据可视化实战

本文将详细介绍如何使用Python最新技术栈构建一个完整的军事装备数据爬虫系统。我们将从爬虫基础讲起,逐步深入到异步爬取、反反爬策略、数据存储与分析等高级主题,最终实现一个能够自动抓取、清洗、存储和分析军事装备数据的完整解决方案。文章包含大量实战代码示例,涉及requests-html、aiohttp、Playwright等现代爬虫库,以及MongoDB存储、Pandas分析和Pyecharts可视化等技术。关键词:Python爬虫、军事数据、异步爬取、反反爬、数据可视化。

2201_76125261的博客 1189

react antd对话框里面input的数据回显

最开始使用initialValues设置里里面的值,然后用setName修改,但是这样导致了当你点击张三的名字后,再点击李四的名字,出现的还是张三的名字,然后点击取消的一瞬间,才会重新渲染成李四的名字。............

qq_48211147的博客 3948

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

React 自定义hooks最佳实践

react hooks推出已经有一段时间了,而自定义hooks在hooks中其实是非常关键的一环,它是抽离业务逻辑和UI逻辑,复用代码的关键

zmjのblog 4414

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

玩转react-hooks,自定义hooks设计模式及其实战

前言 自从react16.8,react-hooks诞生以来,在工作中一直使用hooks,一年多的时间里,接触的react项目,渐渐使用function无状态组件代替了classs声明的有状态组件,期间也总结了一些心得。尤其对于近期三个月的项目里,一点点用自定义hooks来处理公司项目中重复逻辑,总体感觉还不错。今天给大家讲讲我在工作中对react-hooks心得,和一些自定义hooks的设计思想,把在工作中的经验分享给大家。 自定义hooks设计 又回到那个问题?什么是hooks。 react-hooks

zl_Alien的博客 2695

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

使用React Hooks实现表格搜索功能

React Hooks是React 16.8版本引入的新特性,它的作用是为函数组件提供了状态管理和副作用处理的能力。在React之前,函数组件被限制在只能使用无状态的函数组件,无法使用状态和生命周期方法。Hooks的引入解决了这个限制,使得函数组件可以拥有和类组件相似的功能。React Hooks的主要作用包括:状态管理:通过useState Hook,函数组件可以定义和使用状态。useState...

测试游记 927

react 树形表格合并,基于hooks

react 树形表格合并,基于hooks

qq_42957120的博客 982

react hooks使用_如何开始使用React Hooks:受控表格

react hooks使用by Kevin Okeh 由Kevin Okeh 如何开始使用React Hooks:受控表格 (How to Get Started With React Hooks: Controlled Forms) React Hooks are a shiny new proposal that will allow you to write 90% cleaner Re...

cumi7754的博客 453

【项目实战篇】React强大且灵活hooks库——ahooks

在源码中它首先使用了一个getBoundingClientRect观察目标元素得到位置,判断它的top,bottom,left,right是否在可视区域内,用IntersectionObserver去监听当目标元素的位置发生变化时,目标元素是否还在可视区域内。ahooks 所有的输出函数,引用地址都是不会变化的( 通过useMemoizedFn(ahooks 封装的)实现的,其实现也是通过 useRef实现 )useRequest 的第一个参数是一个异步函数,在组件初次加载时,会自动触发该函数执行。

weixin_47002803的博客 1422

【React】668- React Hooks的优缺点

https://zhihu.com/people/zhan-zi-zhen-84下面我谈一下我认为的react hooks的优缺点,优缺点通过和传统的React.Component进行对...

pingan8787 1977

React强大且灵活hooks库——ahooks入门实践之常用场景hook

ahooks是一个React Hooks库,提供丰富的自定义hooks解决特定业务场景问题。核心场景类hooks包括:useRequest处理异步请求(支持轮询、防抖、缓存),useAntdTable简化表格表单联动,useLocalStorageState实现状态持久化,以及生命周期管理、事件监听、性能优化等实用hooks。这些hooks显著提升React开发效率,简化复杂逻辑,优化用户体验。ahooks还提供虚拟列表、网络状态监听、主题管理等特色功能,是React开发者必备的高效工具集。

前端开发-前端技术分享 1156

利用React 自定义Hooks实现业务逻辑复用实例

前端CSS面试题文档,JavaScript面试题文档,Vue面试题文档,大厂面试题文档大家可以在本示例的基础上进行扩展,比如加上请求方式、请求头、请求参数之类的变量,还可以加上缓存数据,停止请求或重新触发请求的代码,另外像组件的事件处理(如分页)都可定义到自定义的 hooks 中,然后所有需要(如分页)的组件都可以使用它完成相应的功能。前端CSS面试题文档,JavaScript面试题文档,Vue面试题文档,大厂面试题文档[外链图片转存中…(img-MGHu52eq-1720120429932)]

2401_85156881的博客 834

React-Blog:深入解析基于React Hooks的前端架构设计

在当今前端开发领域,React Hooks已经成为构建现代Web应用的重要范式。React-Blog项目作为一个基于React Hooks的个人博客系统,展示了如何利用Hooks构建高效、可维护的前端架构。本文将深入探讨这个开源项目的架构设计,帮助开发者理解如何在实际项目中应用React Hooks的最佳实践。 ## 📋 项目概述与技术栈 React-Blog是一个前后端分离的个人博客系统,

gitblog_00524的博客 918

react hooks 表格实战

需求 antd表格 组件划分 Table index.js Pagination.js Table.js Hooks useTableData.js userPagination.js ###Pagination.js:用于展示分页组件,有如下参数 page: 当前页面 pageSize:每页展示条数 total: 总数据量 setPage : 设置页面和每页条数 import React from 'react' import { Pagination } from 'antd'

追随大神的脚步 1070

React Spreadsheet Grid完全指南:打造高性能Excel风格React表格组件

React Spreadsheet Grid是一个功能强大的Excel风格React表格组件,它支持自定义单元格编辑器、高性能滚动和可调整大小的列,为开发者提供了构建专业数据表格的完整解决方案。无论是构建数据管理系统、财务报表还是数据分析工具,这个组件都能满足你的需求。 ## 🚀 核心功能一览 React Spreadsheet Grid之所以成为开发者的理想选择,源于其丰富而实用的功能集:

gitblog_00157的博客 884
上一篇: ant design pro 权限管理
立青FTK
博客等级 码龄8年 51粉丝 14原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值