React+React-Route+Antd+Recharts+Excel(獲取Excel數據,繪製Charts)

轉眼間2018年就過去了,來到的2019新的一年,在這裏,祝大家新年快樂。

知乎
個人博客
Github
Demo
Repo

開發緣由:因爲一個很重要的朋友需要繪製一些Charts,但是嫌棄手繪太慢,因此這次放假寫了這個小東西

當前進度:簡單的Demo Charts展示,包括AreaChart, BarChart, ComposedChart, LineChart, PieChart

測試文件:src/common/files/info.xlsx

附上

先上兩張照片吧

Charts Demo

Charts

Charts

Charts

Excel Table

Charts

1、版本

Antd

React,Recharts, TS

2、創建項目

因爲公司使用的 react+antd+ts, 雖然antd前兩天搞了個聖誕驚嚇,但是毋庸置疑,這個組件庫做的確實很好啊,我不怕被噴,辯證一分爲二,不能因爲別人犯一點的錯誤,就否認人家吧,廢話不多說,還是講本文的主題吧
首先先安裝create-react-app

npm i -g create-react-app
create-react-app Charts --scripts-version=react-scripts-ts-antd

然後安裝react-route-dom和recharts

yarn add react-route-dom
or
npm i react-route-dom --save
npm i recharts --save

因爲TS檢查較爲嚴格,所以,我對TS有一些我自己需要rules的配置
tslint.js

{
  "extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"],
  "linterOptions": {
    "exclude": [
      "config/**/*.js",
      "node_modules/**/*.ts",
      "coverage/lcov-report/*.js"
    ]
  },
  "rules": {
    "no-string-throw": true,
    "no-unused-expression": false,
        "no-unused-variable": false,
    "no-use-before-declare": false,
    "no-duplicate-variable": false,
    "curly": true,
    "class-name": true,
    "triple-equals": [true, "allow-null-check"],
    "comment-format": [false, "check-space"],
    "eofline": true,
    "forin": false,
    "indent": [true, "spaces", 2],
    "label-position": true,
    "max-line-length": [true, 150],
    "member-access": false,
    "no-arg": true,
    "no-bitwise": false,
    "no-console": [true,
      "debug",
      "info",
      "time",
      "timeEnd",
      "trace"
    ],
    "no-construct": true,
    "no-debugger": true,
    "no-empty": false,
    "no-eval": true,
    "no-inferrable-types": true,
    "no-shadowed-variable": false,
    "no-string-literal": false,
    "no-switch-case-fall-through": false,
    "no-trailing-whitespace": true,
    "no-var-keyword": false,
    "object-literal-sort-keys": false,
    "one-line": [true,
      "check-open-brace",
      "check-catch",
      "check-else"
    ],
    "radix": false,
    "typedef-whitespace": [true, {
      "call-signature": "nospace",
      "index-signature": "nospace",
      "parameter": "nospace",
      "property-declaration": "nospace",
      "variable-declaration": "nospace"
    }],
    "variable-name": [true, "ban-keywords"],
    "whitespace": [true,
      "check-branch",
      "check-decl",
      "check-type",
      "check-preblock"
    ],
    "ordered-imports": false,
    "jsx-no-lambda": false,
    "interface-name": [true, "never-prefix"],
    "prefer-const": false
  }
}

TS初試

React-Route 4.x提供給我們使用的都是以組件形式存在的。我們使用的時候就像我們以前使用組件那樣使用就行了,詳見React-Route官方文檔

router配置

菜單欄,我覺得日後可能還會增加其他的Charts,所以我將菜單通過配置文件來控制,增加複用性。

SideMenu.tsx

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import classnames from 'classnames';
import moment from 'moment';
import './index.scss';
import { menus } from './menus';
import { Layout, Menu, Icon } from 'antd';
import Timer from 'src/components/Timer/Timer';

const { Header, Footer, Sider, Content } = Layout;
const { SubMenu } = Menu;

interface SideMenuProps {
  children?: any;
}

export default class SideMenu extends Component<SideMenuProps, any> {
  public state = {
    collapsed: false,
    selectedKeys: [menus[0].key],
  };

  public toggle = () => {
    this.setState({
      collapsed: !this.state.collapsed
    });
  };
  render() {
    const { collapsed, selectedKeys, } = this.state

    return (
      <Layout className="side-menu">
        <Sider trigger={null} collapsible={true} collapsed={collapsed}>
          <div className="logo">
            <img src={require('src/common/images/logo.png')} />
            <a href="https://github.com/Rain120/charts" target="_blank">
              <span className={classnames("title")}><Icon type="github" /></span>
            </a>
            <a href="https://www.zhihu.com/people/yan-yang-nian-hua-120/activities" target="_blank">
              <span className={classnames("title")}><Icon type="zhihu" /></span>
            </a>
          </div>
          <Menu theme="dark" mode="inline" defaultSelectedKeys={selectedKeys}>
            {
              menus && menus.map(menu => (
                menu.children
                  ? (
                    <SubMenu key={menu.key} title={<span><Icon type={menu.icon} /><span>{menu.text}</span></span>}>
                      {
                        menu.children.map(item => (
                          <Menu.Item key={item.key}>
                            <Link to={item.path}>{item.text}</Link>
                          </Menu.Item>
                        ))
                      }
                    </SubMenu>
                  ) : (
                    <Menu.Item key={menu.key}>
                      <Link to={menu.path} style={{ overflow: 'hidden' }}><Icon type={menu.icon} />{menu.text}</Link>
                    </Menu.Item>
                  )
              ))
            }
          </Menu>
        </Sider>
        <Layout className="r-content">
          <Header>
            <Icon
              className="trigger"
              type={collapsed ? 'menu-unfold' : 'menu-fold'}
              onClick={this.toggle}
            />
            <Timer timerStyle="timer" />
          </Header>
          <Content style={{ margin: '1rem', padding: '1rem', background: '#fff', minHeight: '25rem', }}>
            {this.props.children}
          </Content>
          <Footer style={{ textAlign: 'center' }}>
            ©{moment().format('YYYY')} Created by Rainy
          </Footer>
        </Layout>
      </Layout>
    );
  }
}

Menu.ts

/*
 * @Author: Rainy
 * @Github: https://github.com/Rain120
 * @Date: 2018-12-30 15:43:12
 * @LastEditTime: 2018-12-31 13:28:04
 */

export const menus = [
  {
    key: 'menu-0',
    icon: 'bar-chart',
    text: 'Charts Demo Show',
    path: '/',
  },
  {
    key: 'menu-1',
    icon: 'dashboard',
    text: 'ReCharts',
    children: [
      {
        key: '1',
        text: 'Charts Drawer',
        path: '/charts/charts-drawer'
      },
    ]
  }
] as any;

獲取Excel的數據是通過使用大佬的xlsx插件來實現的,詳見XLSX官網

npm i xlsx -S

XLSX

因爲這次項目沒有後端,所以其實我們對Excel文件的解析是在upload之前完成的

public beforeUpload = (file: any, fileList: any) => {
    var rABS = true;
    const f = fileList[0];
    var reader = new FileReader();
    reader.onload = (e: any) => {
        let data: any = e.target.result;
        if (!rABS) {
          data = new Uint8Array(data);
        }
        var workbook = XLSX.read(data, {
            type: rABS ? 'binary' : 'array'
        });
        // more sheet
        workbook.SheetNames.map(item => {
          var worksheet = workbook.Sheets[item];
          var jsonArr = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
          this.handleImpotedJson(jsonArr);
        })
    };
    if (rABS) {
      reader.readAsBinaryString(f);
    } else {
      reader.readAsArrayBuffer(f);
    }
    return false;
}

upload config

const props = {
      accept: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      name: 'file',
      headers: {
        authorization: 'authorization-text',
      },
      multiple: false,
      action: '',
      beforeUpload: (file, fileList) => this.beforeUpload(file, fileList),
      onChange(info) {
        const status = info.file.status;
        if (status !== 'uploading') {
          console.log(info.file, info.fileList);
        }
        if (status === 'done') {
          message.success(`${info.file.name} file uploaded successfully.`);
        } else if (status === 'error') {
          message.error(`${info.file.name} file upload failed.`);
        }
      },
    };

Charts組件
因爲使用的Charts比較多,所以使用recharts提供的組件ResponsiveContainer爲了使得這些Charts不夠縮放的影響。但是當前做的這些Charts大部分都是相同的結構,所以相同的部分應該抽離出來。

WrapperCharts

因爲其他的Charts都差不多,這裏我只說一下LineCharts

import React, { Component } from 'react';
import WrapperCharts from './WrapperCharts';
import './index.scss';
import {
  Line,
  Legend,
  Tooltip,
  XAxis,
  YAxis,
  CartesianGrid,
  LineChart,
  Label,
} from "recharts";

export const COLOR_LISTS = ['#8884d8', '#cf6868', '#3fb549', '#a6d41f', '#8ad4d8', '#cfdd68', '#354449', '#a75d1f'];

interface LineChartsProps {
  data?: any;
  names?: any;
}

export default class LineCharts extends Component<LineChartsProps, any> {
  render() {
    const { data, names } = this.props;
    return (
      <WrapperCharts class_name="line-charts">
        <LineChart data={data}>
          <CartesianGrid />
          {
            names && <XAxis dataKey={names[0].dataKey} name={names[0].name} />
          }
          <YAxis />
          <Tooltip />
          <Legend />
          {
            names && names.slice(1).map((item, index) => (
              <Line type="monotone" dataKey={item.dataKey} key={index} name={item.name} stroke={COLOR_LISTS[index]} />
            ))
          }
          <Label />
        </LineChart>
      </WrapperCharts>
    )
  }
}

引入圖片

引入圖片

以上就是這兩天做的小東西,寫的和講的都很潦草,請看管輕噴。

溜了溜了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章