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... 阅读详情

相关推荐

【Claude Code】Claude Code 使用指南

本文介绍了AI编码助手Claude的基础和进阶操作指南。基础部分涵盖启动命令、模式切换(自动编辑/Plan/Yolo模式)、资源监控等核心功能,重点讲解了斜杠命令的使用方法。进阶操作详细说明了CLAUDE.md文件的创建与管理,包括项目规范、代码风格等内容的配置,以及如何通过该文件建立持久上下文。同时提醒了使用过程中的安全风险,建议通过配置文件限制AI对关键代码的访问权限。全文提供了完整的操作指引和安全建议,帮助开发者高效安全地使用Claude进行编码辅助。

大家好,我是敖云岚,一个沉迷代码的 “技术玩家”。擅长 Java、C++ 开发,对 MySQL、Redis、RabbitMQ 等技术也颇有研究。博客里既有硬核技术干货,也有诙谐幽默的编程日常分享。愿和你一起,笑着学技术,快乐敲代码! 1万+

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

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

g2255yuuhgy的博客 1万+

论文阅读笔记--Grid Anchor based Image Cropping: A New Benchmark and An Efficient Model

基于网格锚的图片裁剪:一种新的标准和高效的模型摘要1.介绍2.相关工作2.1 图像裁剪数据集和评估指标 摘要 图像裁剪的目的是通过去除图像中无关的内容来改善图像的组成和美学质量。现有的图像裁剪数据库大多只提供一个或多个人工标注的边界框作为基准,难以反映实际中图像裁剪的非唯一性和灵活性。 采用IoU等评价指标也不能可靠地反映裁剪模型的真实性能。本文回顾了图像裁剪的问题,并通过考虑图像裁剪的特殊特性和要求(如局部冗余、内容保存、高宽比),提出了基于网格锚的公式。 我们的公式将候选裁剪窗口的搜索空间从数百万减少到

qq_22565865的博客 2948

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

从零开始:用ADS仿真设计一个2.4GHz Wi-Fi低噪声放大器(含完整电路图)

本文提供了一份详细的ADS软件仿真指南,手把手教你从零开始设计一个2.4GHz Wi-Fi低噪声放大器。内容涵盖核心指标理解、晶体管选型与建模、匹配网络设计、偏置电路集成,并通过S参数仿真、噪声优化及线性度验证等步骤,最终呈现完整的优化电路图,助力射频初学者掌握LNA设计全流程。

weixin_29197835的博客 807

(C#脚本)Unity中角色移动的几种实现方法,以及心得。(适合新手入门)

PS:首先我们要知道,在C#中Transform组件是可以直接调用的,而其他的(例如:Rigidbody,Animator)都需要通过 GetConpoment<组件的名称>(); 来获取进行实例化。 例子:调用Rigidbody2D private Rigidbody2D rb2D;//声明变量实例化 void Start() { rb2d = GetComponent<Rigidbody2D>();//获取 } 1.对Unity脚本写入有一定的思维了解...

qq_48860282的博客 1万+

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

Python通用编程规范-09 安全编程规范

1.9 安全编程规范 1.9.1 异常行为 1.9.1.1 禁止抑制或者忽略已检查异常 【说明】: 编码人员常常会通过一个空的或者无意义的catch块来抑制捕获的已检查异常。每一个catch块都应该确保程序只会在继续有效的情况下才会继续运行下去。因此,catch块必须要么从异常情况中恢复,要么重新抛出适合当前catch块上下文的另一个异常以允许最邻近的外层try-catch语句块来进行恢复工作。异常会打断应用原本预期的控制流程。例如,try块中位于异常发生点之后的任何表达式和语句都不会被执行。因此,异常必须

zhao12501的博客 2871

react--自定义列表字段

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

sunlight_01的博客 974

【STM32实战】利用STM32CubeMX配置PWM互补输出驱动电机

本文详细介绍了如何利用STM32CubeMX配置STM32高级定时器的PWM互补输出功能,以驱动电机等需要半桥/全桥电路的应用。通过图形化界面设置定时器参数、使能互补通道并配置关键的死区时间,结合代码示例演示了如何启动PWM输出和动态调整占空比,确保输出安全、精确的互补信号,避免MOS管直通风险,提升驱动可靠性。

weixin_29161785的博客 535

Python实现ARIMA模型

以下是一个使用Python实现ARIMA(自回归移动平均模型)的简单示例代码。在运行代码之前,请确保已经安装了pandasnumpy和库。ARIMAorder(2,0,2)p = 2d = 0q = 2forecastmatplotlib在实际应用中,你需要将模拟数据替换为真实的时间序列数据,并且通过合适的方法(如信息准则等)来确定pdq的最佳值。除了auto_arima在使用pmdarima。

huanghm88的专栏 1736

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

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

weixin_46446590的博客 2052

piamon实战-- 如何使用 Paimon 的 Java API 实现数据的点查

点查(Point Lookup)是指通过 ​​唯一键(Primary Key)​​ 快速定位并返回单条或少量数据的查询模式。例如:根据用户 ID 查询用户信息、根据订单号查询订单详情等。点查的核心诉求是 ​​低延迟​​ 和 ​​高吞吐​​,适用于实时风控、在线分析等场景。​​定义主键表结构​​,优化数据分布。​​构建过滤条件​​,使用指定查询键。​​执行查询并处理结果​​,结合流式更新实现实时查询。

阿华田的博客 532

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

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

qq_48211147的博客 3948

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

React 自定义hooks最佳实践

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

zmjのblog 4414

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

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

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

zl_Alien的博客 2695

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

使用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、付费专栏及课程。

余额充值