React(五):XLS、XLSX文件在线预览

效果

依赖

bash 复制代码
$ yarn add xlsx

源码

css 复制代码
.xlsx-wrap {
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #fafafa;

  .ant-tabs {
    width: 100%;
    height: 100%;

    .ant-tabs-nav {
      height: 50px;
      padding: 0 10px;
      margin-bottom: 0;
    }

    .ant-tabs-content-holder {
      border-top: 1px solid #000000;
      height: calc(100% - 50px);

      .ant-tabs-content {
        height: 100%;

        .ant-tabs-tabpane {
          height: 100%;

          .table-container {
            width: 100%;
            height: 100%;
            padding: 10px;
            overflow: auto;

            table {
              border-top: 1px solid red;
              border-left: 1px solid red;
              border-collapse: collapse;

              td {
                border-bottom: 1px solid red;
                border-right: 1px solid red;
                padding: 5px 10px;
              }
            }
          }
        }
      }
    }
  }
}
js 复制代码
import './index.scss';
import {useCallback, useMemo, useRef, useState} from 'react';
import type {UploadRequestOption} from 'rc-upload/lib/interface';
import {Button, Flex, Tabs, Upload} from 'antd';
import {read, utils, WorkBook, writeFileXLSX} from 'xlsx';

function PreviewXLSX() {
    const tableContainerRef = useRef<HTMLDivElement>(null);

    const [workBook, setWorkBook] = useState<WorkBook>({
        SheetNames: [],
        Sheets: {},
    });

    const items = workBook.SheetNames
        .map((d, i) => ({
            key: `${i}`,
            label: d,
            children: <div className="table-container" ref={tableContainerRef} dangerouslySetInnerHTML={{__html: utils.sheet_to_html(workBook.Sheets[d])}}/>
        }));

    const fileUpload = async (options: UploadRequestOption) => {
        const fileReader = new FileReader();
        fileReader.readAsArrayBuffer(options.file as File);
        fileReader.onload = e => {
            const buffer = e.target!.result;
            console.log(read(buffer));
            setWorkBook({...workBook, ...read(buffer)});
        };
    };

    const exportFile = useCallback(() => {
        const table = tableContainerRef.current!.getElementsByTagName('TABLE')[0];
        const wb = utils.table_to_book(table);
        writeFileXLSX(wb, 'table_to_xlsx.xlsx');
    }, [tableContainerRef]);

    return (<>
        <div className="xlsx-wrap">
            <Tabs tabBarExtraContent={
                <Flex justify="flex-end" align="center" gap={10}>
                    <Button onClick={exportFile}>导出</Button>
                    <Upload action="#" customRequest={fileUpload} showUploadList={false}>
                        <Button type="primary">点击上传</Button>
                    </Upload>
                </Flex>
            } items={useMemo(() => items, [items])}/>
        </div>
    </>);
}

export default PreviewXLSX;
相关推荐
风清扬雨7 分钟前
Vue3具名插槽用法全解——从零到一的详细指南
前端·javascript·vue.js
海盗强32 分钟前
Vue 3 常见的通信方式
javascript·vue.js·ecmascript
oscar9991 小时前
JavaScript与TypeScript
开发语言·javascript·typescript
橘子味的冰淇淋~2 小时前
【解决】Vue + Vite + TS 配置路径别名成功仍爆红
前端·javascript·vue.js
leluckys2 小时前
flutter 专题 六十三 Flutter入门与实战作者:xiangzhihong8Fluter 应用调试
前端·javascript·flutter
shoa_top3 小时前
JavaScript 数组方法总结
javascript
JiangJiang3 小时前
🚀 Vue 人如何玩转 React 自定义 Hook?从 Mixins 到 Hook 的华丽转身
前端·react.js·面试
鱼樱前端3 小时前
让人头痛的原型和原型链知识
前端·javascript
lianghj3 小时前
前端高手必备:深度解析高频场景解决方案与性能优化实战
前端·javascript·面试
OpenIM4 小时前
Electron Demo 的快速编译与启动
前端·javascript·electron