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',
  },
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章