0.3.14 • Published 7 months ago

@pluve/lego-excel-react v0.3.14

Weekly downloads
-
License
MIT
Repository
gitlab
Last release
7 months ago

@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 > templateUrlstring-
download自定义下载导入模板React.ReactNode-
tableKeys配置描述Array<LegoExcelTypes.TableKey>-
maxLength最大一次导入行数number-
fileSizeLimit文件大小限制(M)number-
size导入弹窗内元素的尺寸(不包含导入按钮的尺寸)small \| middle \| large \|middle
beforeUploadStartbeforeUpload 前置校验(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-
loadingTiploading 展示文案React.ReactNode-
readExcelOptionsXLSX.utils.sheet_to_json(worksheet: WorkSheet, opts?: Sheet2JSONOpts)中的第二个参数Sheet2JSONOpts-
errorModalOptionsLegoExcelUtils.showErrorByModal(errorList: string[], options: LegoExcelTypes.ErrorModalOptions \| number = 520) 中的第二个参数LegoExcelTypes.ErrorModalOptions-
syncValidateRules是否同步进行规则校验booleanfalse

校验顺序

  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 的额外 propsLegoExcelTypes.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>-
optionsXLSX.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})

参数说明类型默认值
headersExcel 表头描述{ 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

0.3.9

8 months ago

0.3.14

7 months ago

0.3.13

8 months ago

0.3.12

8 months ago

0.3.11

8 months ago

0.3.10

8 months ago

0.3.6

9 months ago

0.3.5

10 months ago

0.3.8

8 months ago

0.3.7

9 months ago

0.3.4

1 year ago

0.3.3

1 year ago

0.3.2

1 year ago

0.3.1

1 year ago

0.3.0

1 year ago

0.2.0

1 year ago

0.1.0

1 year ago

0.0.3

1 year ago

0.0.2

1 year ago

0.0.1

1 year ago