npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@pluve/lego-excel-react

v0.3.14

Published

lego excel components with react

Downloads

2

Readme

@pluve/lego-excel-react

乐高系列之 excel 文件处理

npm (scoped)

@pluve/lego-excel-react 已经投入了我们的生产环境中使用,经受住了来自真实业务的考验,并伴随着我们的业务需求不断完善。

安装

# npm
npm i @pluve/lego-excel-react

# yarn
yarn add @pluve/lego-excel-react

公共类型 LegoExcelTypes

LegoExcelTypes.TableKey

导入数据对象描述

| 参数 | 说明 | 类型 | | --------- | ------------------------------------- | ----------------------------------- | | label | 表头列名称 | string | | value | 表头列属性(表头列名称所对应的 key) | string | | rules | 列规则校验 | Array<LegoExcelTypes.Rule> | | formatter | 格式化该列数据(在解析 excel 时执行) | (value?: string \| number) => any |

LegoExcelTypes.Rule

| 参数 | 说明 | 类型 | | --------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | required | 是否必填 | boolean | | pattern | 正则校验 | RegExp | | message | 错误提示 | string | | validator | 自定义函数校验,syncValidateRulestrue 时返回 boolean,为 false 时返回 Promise<boolean> | (value: any, rowData: { [key: string]: any }) => Promise<boolean> \| boolean |

LegoExcelTypes.ErrorModalOptions

export type ErrorModalOptions = Omit<ModalFuncProps, 'content'>;

LegoExcelImport

通常用于批量导入数据场景

API

| 参数 | 说明 | 类型 | 默认值 | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | | title | 导入模态框标题 | string \| React.ReactNode | 批量导入 | | btnText | 导入按钮文案 | string | 批量导入 | | renderBtn | 自定义导入按钮(调用 show 打开导入模态框) | (params: { show: () => void }) => React.ReactNode | - | | templateUrl | 导入模版地址,优先级download > templateUrl | string | - | | download | 自定义下载导入模板 | React.ReactNode | - | | tableKeys | 配置描述 | Array<LegoExcelTypes.TableKey> | - | | maxLength | 最大一次导入行数 | number | - | | fileSizeLimit | 文件大小限制(M) | number | - | | size | 导入弹窗内元素的尺寸(不包含导入按钮的尺寸) | small \| middle \| large \| | middle | | beforeUploadStart | beforeUpload 前置校验 | (file: RcFile, FileList: RcFile[]) => Promise<boolean> | - | | beforeOk | 点击【确定】按钮前置校验 | (data: any[]) => Promise<boolean> | - | | customRequest | 自定义校验,一般用于业务校验(如调用后端接口校验) | (data: any[]) => Promise<boolean> | - | | onFinish | 导入完成回调 | (data: any[]) => void | - | | onFileChange | 文件变化的事件 | (data: { file?: RcFile }) => void | - | | onCancel | 导入弹窗关闭事件 | () => void | - | | modalProps | 透传 antd Modal 组件属性 | interface ILegoExcelImportModalProps extends Omit<ModalProps, "onOk" \| "onCancel" \| "open" \| "destroyOnClose" \| "closable" \| "keyboard" \| "maskClosable">; | - | | uploadProps | 透传 antd Upload 组件属性 | interface ILegoExcelImportUploadProps extends Omit<UploadProps, "accept" \| "fileList" \| "maxCount" \| "customRequest" \| "onRemove" \| "beforeUpload">; | - | | bodyTop | 导入文件上面自定义内容 | React.ReactNode | - | | bodyBottom | 导入文件下面自定义内容 | React.ReactNode | - | | loadingTip | loading 展示文案 | React.ReactNode | - | | readExcelOptions | XLSX.utils.sheet_to_json(worksheet: WorkSheet, opts?: Sheet2JSONOpts)中的第二个参数 | Sheet2JSONOpts | - | | errorModalOptions | LegoExcelUtils.showErrorByModal(errorList: string[], options: LegoExcelTypes.ErrorModalOptions \| number = 520) 中的第二个参数 | LegoExcelTypes.ErrorModalOptions | - | | syncValidateRules | 是否同步进行规则校验 | boolean | false |

校验顺序

  1. 上传时校验顺序:

    • beforeUploadStart 函数校验
    • 导入文件是否是 xlsx 格式
    • 导入文件大小是否超过 fileSizeLimit M
  2. 点击确定时校验顺序:

    • beforeOk 函数校验
    • 上传表格是否无数据
    • 表头校验
    • 最大限制一次导入 maxLength 条数据
    • 单元格必填等数据校验
    • 自定义校验

效果展示

demo 截图

使用示例

import {
  LegoExcelImport,
  LegoExcelTypes,
  LegoExcelUtils,
} from '@pluve/lego-excel-react';
import { Button } from 'antd';
import { SizeType } from 'antd/es/config-provider/SizeContext';
import { isNumber, toNumber } from 'lodash-es';
import { FC, useState } from 'react';

const Demo: FC = () => {
  const [size, setSize] = useState<SizeType>('middle');
  // excel 单元格转为数字
  const convertExcelCellToNumber = (value?: string | number) =>
    isNumber(value) ? value : !!value ? toNumber(value) : undefined;

  const CHANNEL_GOODS_TABLE: LegoExcelTypes.TableKey[] = [
    {
      label: '*商品编码',
      value: 'goodsCode',
      rules: [{ required: true, message: '请填写商品编码' }],
      formatter: (value) => `${value}`,
    },
    {
      label: '*商品名称',
      value: 'goodsName',
      rules: [{ required: true, message: '请填写商品名称' }],
      formatter: (value) => `${value}`,
    },
    {
      label: '*供货价',
      value: 'oldSaleAmount',
      rules: [
        { required: true, message: '请填写供货价' },
        {
          pattern:
            /(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^0\.0[1-9])|(^0\.[1-9]([0-9])?$)/,
          message: '限制正两位小数',
        },
        {
          validator: (val) => {
            if (val > 999999999.99) {
              return false;
            }
            return true;
          },
          message: '供货价输入不合法',
        },
      ],
      formatter: convertExcelCellToNumber,
    },
    {
      label: '*上架状态',
      value: 'isShelves',
      rules: [{ required: true, message: '请选择上架状态' }],
      formatter: (value) => `${value}`,
    },
  ];

  const customRequest = (data: any[]): Promise<boolean> =>
    new Promise((resolve) => {
      // 模拟服务端校验
      setTimeout(() => {
        resolve(true);
      }, 500);
    });

  const onImportFinish = (data: any[]) => {
    console.log('获取导入数据 --> ', data);
  };

  const onFileChange = (data: { file?: File }) => {
    console.log('监听文件变化 --> ', data.file);
  };

  const toggleSize = (size) => {
    setSize(size);
  };

  const exportData = () => {
    const headers = [
      {
        key: 'no',
        title: '序号',
      },
      {
        key: 'name',
        title: '姓名',
      },
      {
        key: 'age',
        title: '年龄',
      },
      {
        key: 'desc',
        title: '描述',
      },
    ];
    const data = [
      {
        no: 1,
        name: 'john',
        age: 12,
        desc: '我是一个随机的描述我是一个随机的描述我是一个随机的描述我是一个随机的描述',
      },
      {
        no: 2,
        name: 'lucy',
        age: 19,
      },
    ];
    for (let index = 0; index < 10000; index++) {
      data.push({
        no: index + 3,
        name: Math.random() + '',
        age: Math.floor(Math.random() * 100),
        desc: Math.random() > 0.5 ? '开' : '关',
      });
    }
    LegoExcelUtils.exportExcel({
      headers,
      data,
      fileName: '测试导出.xlsx',
      colWidths: [{ wpx: 50 }, { wpx: 50 }, { wpx: 50 }, { wpx: 130 }],
    });
  };

  const downloadTemplateUrl = () => {
    LegoExcelUtils.exportExcel({
      headers: [
        {
          key: 'storeCode',
          title: '*门店编码',
        },
      ],
      data: [
        {
          storeCode: 'xxxx',
        },
      ],
      fileName: '通用门店导入模板.xlsx',
    });
  };

  const beforeUploadStart = async () => {
    console.log('1111');
    return true;
  };

  const beforeOk = async (data: any[]) => {
    console.log('获取导入数据 --> ', data);
    return true;
  };

  return (
    <div style={{ width: '100%' }}>
      <p>
        <Button
          type={size === 'small' ? 'primary' : 'default'}
          onClick={() => toggleSize('small')}
        >
          small
        </Button>
        <Button
          type={size === 'middle' ? 'primary' : 'default'}
          onClick={() => toggleSize('middle')}
        >
          middle
        </Button>
        <Button
          type={size === 'large' ? 'primary' : 'default'}
          onClick={() => toggleSize('large')}
        >
          large
        </Button>
      </p>
      <LegoExcelImport
        templateUrl="https://yf-test-oss.yifengx.com/webtest/pluve/static/lego-excel-import-file.xlsx"
        tableKeys={CHANNEL_GOODS_TABLE}
        fileSizeLimit={2}
        customRequest={customRequest}
        onFileChange={onFileChange}
        onFinish={onImportFinish}
        renderBtn={({ show }) => {
          return <Button onClick={show}>导入</Button>;
        }}
        size={size}
        errorModalOptions={{ cancelText: '取消' }}
        download={
          <Button
            type="link"
            style={{ padding: 0 }}
            onClick={downloadTemplateUrl}
          >
            自定义下载导入模板
          </Button>
        }
        beforeUploadStart={beforeUploadStart}
        beforeOk={beforeOk}
        uploadProps={{ disabled: false }}
        syncValidateRules={true}
      ></LegoExcelImport>
      <Button onClick={() => exportData()} style={{ marginLeft: 10 }}>
        导出
      </Button>
    </div>
  );
};

export default Demo;

LegoExcelUtils

提供一系列 Excel 相关方法

showErrorByModal

公用展示错误弹窗

Params

showErrorByModal(errorList, options)

| 参数 | 说明 | 类型 | 默认值 | | --------- | --------------------------- | -------------------------------------------------- | ------ | | errorList | 需要展示的错误信息 | string[] | - | | options | 错误弹窗 Modal 的额外 props | LegoExcelTypes.ErrorModalOptions \| number = 520 | - |

示例

import { LegoExcelUtils } from '@pluve/lego-excel-react';

const errorMessages = [
  {
    rowIndex: 1,
    message: '某某某不存在',
  },
];

LegoExcelUtils.showErrorByModal(
  errorMessages.map((item) => `第${item.rowIndex}行:${item.message}`),
  { cancelText: '取消' },
);

validateFields

用于读取到 Excel 数据后的校验(异步)。若 tableKeys 中的 rules 规则存在异步校验,则使用该方式。

Params

validateFields({ data, tableKeys })

| 参数 | 说明 | 类型 | 默认值 | | --------- | -------------------- | -------------------------------- | ------ | | data | 表格数据(不含表头) | any[] | - | | tableKeys | 配置描述 | Array<LegoExcelTypes.TableKey> | - |

示例

import { LegoExcelUtils, LegoExcelTypes } from '@pluve/lego-excel-react';

const result: {
  rowIndex: number; // 行数
  message: string; // 该行所有错误信息(多条以;分割)
}[] = await LegoExcelUtils.validateFields(params: {
  data: any[];
  tableKeys: LegoExcelTypes.TableKey[];
});

validateFieldsSync

用于读取到 Excel 数据后的校验(同步)。若 tableKeys 中的 rules 规则全为同步校验,则使用该方式。

Params

validateFieldsSync({ data, tableKeys })

| 参数 | 说明 | 类型 | 默认值 | | --------- | -------------------- | -------------------------------- | ------ | | data | 表格数据(不含表头) | any[] | - | | tableKeys | 配置描述 | Array<LegoExcelTypes.TableKey> | - |

示例

import { LegoExcelUtils, LegoExcelTypes } from '@pluve/lego-excel-react';

const result: {
  rowIndex: number; // 行数
  message: string; // 该行所有错误信息(多条以;分割)
}[] = LegoExcelUtils.validateFieldsSync(params: {
  data: any[];
  tableKeys: LegoExcelTypes.TableKey[];
});

readExcel

读取 Excel 文件

Params

readExcel({fileReaderRes, tableKeys, options})

| 参数 | 说明 | 类型 | 默认值 | | ------------- | ------------------------------------------------------------------------------------- | -------------------------------- | ------ | | fileReaderRes | - | any | - | | tableKeys | 配置描述 | Array<LegoExcelTypes.TableKey> | - | | options | XLSX.utils.sheet_to_json(worksheet: WorkSheet, opts?: Sheet2JSONOpts)中的第二个参数 | Sheet2JSONOpts | - |

Result

| 参数 | 说明 | 类型 | 默认值 | | ------- | -------------------- | --------------- | ------ | | data | 表格数据(不含表头) | Array | - | | headers | 表头数据 | Array<String> | - |

示例

import { LegoExcelUtils } from '@pluve/lego-excel-react';

const reader = new FileReader();
reader.readAsBinaryString(file);
reader.onload = (e) => {
  const result = e.target?.result;
  if (!!result) {
    // 读取excel
    const excelRes = LegoExcelUtils.readExcel({
      fileReaderRes: result,
      tableKeys: tableKeys,
      options: { raw: true }, // 可选
    });
  }
};

exportExcel

前端导出 Excel 文件。

Params

exportExcel({headers, data, fileName, colWidths})

| 参数 | 说明 | 类型 | 默认值 | | --------- | --------------------------- | ------------------------------------------------------- | ----------- | | headers | Excel 表头描述 | { key: string; title: string; [key: string]: any; }[] | - | | data | 需要导出的数据 | any[] | - | | fileName | 导出 excel 的文件名 | string | 导出.xlsx | | colWidths | 导出 excel 每一列宽度的集合 | { wpx: number }[] | - |

注意:colWidths 未设置,则 exportExcel 方法中默认设置为 wpx 为 120,length 为 headers.length 的数组

示例

import { LegoExcelUtils } from '@pluve/lego-excel-react';
import { Button } from 'antd';
import { FC } from 'react';

const Demo: FC = () => {
  const exportData = () => {
    const headers = [
      {
        key: 'no',
        title: '序号',
      },
      {
        key: 'name',
        title: '姓名',
      },
      {
        key: 'age',
        title: '年龄',
      },
      {
        key: 'desc',
        title: '描述',
      },
    ];
    const data = [
      {
        no: 1,
        name: 'john',
        age: 12,
        desc: '我是一个随机的描述我是一个随机的描述我是一个随机的描述我是一个随机的描述',
      },
      {
        no: 2,
        name: 'lucy',
        age: 19,
      },
    ];
    for (let index = 0; index < 10000; index++) {
      data.push({
        no: index + 3,
        name: Math.random() + '',
        age: Math.floor(Math.random() * 100),
        desc: Math.random() > 0.5 ? '开' : '关',
      });
    }
    LegoExcelUtils.exportExcel({
      headers,
      data,
      fileName: '测试导出.xlsx',
      // 可传可不传,不传则取每一列 wpx 为 100
      colWidths: [{ wpx: 50 }, { wpx: 50 }, { wpx: 50 }, { wpx: 130 }],
    });
  };

  return (
    <div style={{ width: '100%' }}>
      <Button onClick={exportData}>点我导出</Button>
    </div>
  );
};

export default Demo;

xlsx

xlsx