React实现打印功能

一、需求分析:

环境:react,antd,antd-pro
将选中的数据进行打印,可以自定义分页的大小。
由于打印的列等多个因素,导致如果写成组件在使用的时候依旧会改变源码,所以采用了写成页面的方式,

二、实现需求:

1、数据传值

进行传值的时候,刚开始使用的是在通过this.props.location进行传值,但是这样数据被写死了,导致再次进入页面的时候无法更新打印的值。
最后采用了一个全局的model进行实现的传值。

2、表格生成

分为四部分进行生成,分别是标题、表头、表格、表尾。其中表头,表尾因为需求的原因写死。
以下为代码:

createTitle = (title)=>(
  <div>
    <h1 style={styleObj.title}>{title}</h1>
  </div>
)

createHeader = (headerData)=>{
  headerData = [
    {
      orderID:'订单编号',
      value:'P201901020002',
    },{
      people:'采购人员',
      value:'xxx',
    },{
      time:'采购时间',
      value:'2019年01月01日',
    }
  ];
  return (
    <table>
      <tbody style={styleObj.header}>
      <tr>
        <th>订单编号:</th>
        <th colSpan="7">
          <input style={styleObj.printInput} value="P201901020002" />
        </th>
      </tr>
      <tr>
        <th>采购员:</th>
        <th colSpan="7">
          <input style={styleObj.printInput} value="xxx" />
        </th>
        <th>采购时间:</th>
        <th colSpan="7">
          <input style={styleObj.printInput} value="2019年01月01日" />
        </th>
      </tr>
      </tbody>
    </table>
  )
}

createForm = (printCol,printData)=>(
  <table style={styleObj.printTable}>
    <tbody>
    {
      (
        <tr style={styleObj.printTableTr}>
          {printCol.map(item=><th style={{...styleObj[item.key],...styleObj.printTableTh}}><div>{item.name}</div></th>)}
        </tr>
      )
    }
    {
      printData.map(item=> (
          <tr style={styleObj.printTableTr}>
            {Object.keys(item).map(i => <th style={styleObj.printTableTh}>{item[i]}</th>)}
          </tr>
        )
      )
    }
    </tbody>
  </table>
)

createFooter = (footerData)=>{
  return (
    <table>
      <tbody style={styleObj.footer}>
      <tr>
        <th>供应商(签字)</th>
        <th>
          <div style={styleObj.footerSpace} />
        </th>
        <th colSpan="4">
          <input style={styleObj.printInputFooter} />
        </th>
        <th>库管员(签字)</th>
        <th>
          <div style={styleObj.footerSpace} />
        </th>
        <th colSpan="4">
          <input style={styleObj.printInputFooter} />
        </th>
        <th>{`第${footerData.current}页`}</th>
        <th>{`共${footerData.total}页`}</th>
      </tr>
      </tbody>
    </table>
  )
}
createPrintArea = (printCol)=>{
  const {printGroupData} = this.state;
  return (
    printGroupData.map((item,index)=>{
      if(item.length){
        return (
          <div style={styleObj.printArea}>
            {this.createTitle('xxxxxxx公司xxx单')}
            {this.createHeader('asd')}
            {this.createForm(printCol,item)}
            {this.createFooter({current:index+1,total:printGroupData.length})}
          </div>
        )
      }
    })
  )
}

最主要的是CSS的调整,因为之后的打印需求将所有的CSS内联:
以下为css:

export const styleObj = {
  printArea:{
    width: '500px',
    fontSize: '12px',
    align:'center'
  },
  printInput:{
    fontWeight:'bold',
    border: 'none',
  },
  title:{
    textAlign: 'center',
    fontSize: '15px',
    fontWeight: '700',
  },
  header:{
    fontWeight:'bold',
    fontSize: '12px',
  },
  printTable:{
    fontSize: '12px',
    fontWeight: '700',
    color: 'black',
    border: '1px black',
    borderCollapse: 'collapse',
    textAlign: 'center',
  },
  printTableTh:{
    padding: '4px',
    border: '1px solid black',
    textAlign: 'center',
  },
  printTableTr:{
    textAlign:'center',
    padding: '4px',
    border: '1px solid black',
  },
  number:{
    width: '60px',
  },
  goodsName:{
    width: '180px',
  },
  unitName:{
    width: '75px',
  },
  specifications:{
    width: '90px',
  },
  goodsType:{
    width: '90px',
  },
  footer:{
    fontSize:'12px',
  },
  footerSpace:{
    width: '20px',
    display: 'block',
  },
  printInputFooter:{
    fontWeight:'bold',
    border:'none',
    width: '85px',
  },
};

3、实现分页

分页即将数据进行分割,然后每次生成表格的时候将分割后的每个表格数据依次传入表格生成函数,从而生成全部表格。
分页函数:

//传入的数据为:分页的大小,需要分页的数据。
page = (pageNumber,printData)=>{
  const printDataBack = printData.concat();
  const printGroupData = [];
  while(printDataBack.length >= pageNumber){
    let tempGroup = [];
    tempGroup = printDataBack.splice(0,pageNumber);
    printGroupData.push(tempGroup);
  }
  if(printDataBack.length){
    printGroupData.push(printDataBack);
  }
  printGroupData.forEach((item)=>{
    item.forEach((i,index)=>{
      i.number = index+1;
    })
  });
  return printGroupData;
}

注意:解构出来的数据是引用,需要进行备份。
设置一个input框以及一个按钮,input框用于输入分页的数字,再点击按钮以及第一次进入页面的时候进行分页。

4、实现打印

实现方法一(不推荐):

直接在本页面进行刷新

优点:css不用内嵌。
缺点:导致本页面刷新,某些数据丢失。

实现方法:直接获取到需要打印的区域,然后将本页面的innerHTML设置为获取的区域,然后调用系统的print,最后调用reload

代码:

print = () => {
    window.document.body.innerHTML = window.document.getElementById('billDetails').innerHTML;  
    window.print(); 
    window.location.reload();
}
实现方法二:

打开一个页面进行打印

优点:打印不在关乎本页面的业务
缺点:CSS需要内联
代码:

handlePrint = () => {
  const win = window.open('','printwindow');
  win.document.write(window.document.getElementById('printArea').innerHTML);
  win.print();
  win.close();
}

三、完整代码

以下为完整代码:
index.js

import React, { PureComponent } from 'react';
import { connect } from 'dva';
import {
  Row,
  Button,
  Col,
  Card,
  Form,
  message,
  InputNumber,
} from 'antd';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
import {styleObj} from './style';


@Form.create()
@connect(({ print }) => ({
  print,
}))
class PrintTable extends PureComponent {
  state = {
    printData:[],
    printCol:[],
    pageNumber:10,
    printGroupData:[],
  }

  componentDidMount() {
    const printCol = [
      {
        key:'number',
        name:'序号',
      },{
        key:'goodsName',
        name:'商品名称',
      },{
        key:'goodsType',
        name:'商品类型',
      },{
        key:'unitName',
        name:'单位',
      },{
        key:'specifications',
        name:'规格',
      }];
    const printData = [];
    const { pageNumber } = this.state;
    const { print:{ payload:{formPrintData} } } = this.props;
    formPrintData.forEach((i,index)=>{
      const colData = {};
      printCol.forEach(j=>{
        colData[j.key] = i[j.key];
      })
      colData.number = index+1;
      printData.push(colData);
    });
    const printGroupData = this.page(pageNumber,printData);
    this.setState({
      printData,
      printCol,
      printGroupData,
    })
  }

  componentWillReceiveProps(nextProps){
    const printCol = [
      {
        key:'number',
        name:'序号',
      },{
        key:'goodsName',
        name:'商品名称',
      },{
        key:'goodsType',
        name:'商品类型',
      },{
        key:'unitName',
        name:'单位',
      },{
        key:'specifications',
        name:'规格',
      }];
    const printData = [];
    const { pageNumber } = this.state;
    const { print:{ payload:{formPrintData} } } = nextProps;
    formPrintData.forEach((i,index)=>{
      const colData = {};
      printCol.forEach(j=>{
        colData[j.key] = i[j.key];
      })
      colData.number = index+1;
      printData.push(colData);
    });
    const printGroupData = this.page(pageNumber,printData);
    this.setState({
      printData,
      printCol,
      printGroupData,
    })
  }

  createTitle = (title)=>(
    <div>
      <h1 style={styleObj.title}>{title}</h1>
    </div>
    )

  createHeader = (headerData)=>{
    headerData = [
      {
        orderID:'订单编号',
        value:'P201901020002',
      },{
        people:'采购人员',
        value:'xxx',
      },{
        time:'采购时间',
        value:'2019年01月01日',
      }
    ];
    return (
      <table>
        <tbody style={styleObj.header}>
          <tr>
            <th>订单编号:</th>
            <th colSpan="7">
              <input style={styleObj.printInput} value="P201901020002" />
            </th>
          </tr>
          <tr>
            <th>采购员:</th>
            <th colSpan="7">
              <input style={styleObj.printInput} value="xxx" />
            </th>
            <th>采购时间:</th>
            <th colSpan="7">
              <input style={styleObj.printInput} value="2019年01月01日" />
            </th>
          </tr>
        </tbody>
      </table>
    )
  }

  createForm = (printCol,printData)=>(
    <table style={styleObj.printTable}>
      <tbody>
        {
          (
            <tr style={styleObj.printTableTr}>
              {printCol.map(item=><th style={{...styleObj[item.key],...styleObj.printTableTh}}><div>{item.name}</div></th>)}
            </tr>
          )
        }
        {
          printData.map(item=> (
            <tr style={styleObj.printTableTr}>
              {Object.keys(item).map(i => <th style={styleObj.printTableTh}>{item[i]}</th>)}
            </tr>
            )
          )
        }
      </tbody>
    </table>
    )

  createFooter = (footerData)=>{
    return (
      <table>
        <tbody style={styleObj.footer}>
          <tr>
            <th>供应商(签字)</th>
            <th>
              <div style={styleObj.footerSpace} />
            </th>
            <th colSpan="4">
              <input style={styleObj.printInputFooter} />
            </th>
            <th>库管员(签字)</th>
            <th>
              <div style={styleObj.footerSpace} />
            </th>
            <th colSpan="4">
              <input style={styleObj.printInputFooter} />
            </th>
            <th>{`第${footerData.current}页`}</th>
            <th>{`共${footerData.total}页`}</th>
          </tr>
        </tbody>
      </table>
    )
  }

  handlePrint = () => {
    const win = window.open('','printwindow');
    win.document.write(window.document.getElementById('printArea').innerHTML);
    win.print();
    win.close();
  }

  createPrintArea = (printCol)=>{
    const {printGroupData} = this.state;
    return (
      printGroupData.map((item,index)=>{
        if(item.length){
          return (
            <div style={styleObj.printArea}>
              {this.createTitle('xxxxxxx公司xxx单')}
              {this.createHeader('asd')}
              {this.createForm(printCol,item)}
              {this.createFooter({current:index+1,total:printGroupData.length})}
            </div>
          )
        }
      })
    )
  }

  handlePage = ()=>{
    const { pageNumber, printData } = this.state;
    if(pageNumber <= 0){
      message.warning('输出正确的分页');
      return;
    }
    this.setState({
      printGroupData:this.page(pageNumber, printData)
    })
  }

  page = (pageNumber,printData)=>{
    const printDataBack = printData.concat();
    const printGroupData = [];
    while(printDataBack.length >= pageNumber){
      let tempGroup = [];
      tempGroup = printDataBack.splice(0,pageNumber);
      printGroupData.push(tempGroup);
    }
    if(printDataBack.length){
      printGroupData.push(printDataBack);
    }
    printGroupData.forEach((item)=>{
      item.forEach((i,index)=>{
        i.number = index+1;
      })
    });
    return printGroupData;
  }

  onChange = (value)=>{
    this.setState({
      pageNumber:value,
    })
  }

  render() {
    const { printCol, printData } = this.state;
    return (
      <PageHeaderWrapper title="查询表格">
        <Card>
          <Row>
            <Col span={6}>
              <Row>
                <Col span={12}>
                  <InputNumber onChange={this.onChange} placeholder='输入自定义分页数量' style={{width:'100%'}}/>
                </Col>
                <Button onClick={this.handlePage}>确认分页</Button>
              </Row>
              <Row>
                <Button onClick={this.handlePrint} type='primary'>打印</Button>
              </Row>
            </Col>
            <Col span={12}>
              <div id='printArea'>
                <div style={styleObj.printArea}>
                  {printCol.length&&printData.length? this.createPrintArea(printCol):null}
                </div>
              </div>
            </Col>
          </Row>
        </Card>
      </PageHeaderWrapper>
    );
  }
}

export default PrintTable;

style.js

export const styleObj = {
  printArea:{
    width: '500px',
    fontSize: '12px',
    align:'center'
  },
  printInput:{
    fontWeight:'bold',
    border: 'none',
  },
  title:{
    textAlign: 'center',
    fontSize: '15px',
    fontWeight: '700',
  },
  header:{
    fontWeight:'bold',
    fontSize: '12px',
  },
  printTable:{
    fontSize: '12px',
    fontWeight: '700',
    color: 'black',
    border: '1px black',
    borderCollapse: 'collapse',
    textAlign: 'center',
  },
  printTableTh:{
    padding: '4px',
    border: '1px solid black',
    textAlign: 'center',
  },
  printTableTr:{
    textAlign:'center',
    padding: '4px',
    border: '1px solid black',
  },
  number:{
    width: '60px',
  },
  goodsName:{
    width: '180px',
  },
  unitName:{
    width: '75px',
  },
  specifications:{
    width: '90px',
  },
  goodsType:{
    width: '90px',
  },
  footer:{
    fontSize:'12px',
  },
  footerSpace:{
    width: '20px',
    display: 'block',
  },
  printInputFooter:{
    fontWeight:'bold',
    border:'none',
    width: '85px',
  },
};
    原文作者:Bonny
    原文地址: https://segmentfault.com/a/1190000017842230
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞