IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 山东大学项目实训开发日志——基于vue+springboot的医院耗材管理系统(11) -> 正文阅读

[JavaScript知识库]山东大学项目实训开发日志——基于vue+springboot的医院耗材管理系统(11)

????????近期也新增了一项功能:打印单据

????????这一功能并不复杂,但是要在很多地方都上线,所以写出其中的一个,剩余的只需稍作修改即可正常使用。因此我只以打印中心库库存列表为例写此篇博客。

????????首先从网上学习并借鉴了网页上打印的方法并稍做修改。

optionsDefault 的说明:
? ?表头数据 - 列属性
? ?Array - 必填
? ?{
? ?? ? field, ? ? ? ? ?// 列字段
? ?? ? name, ? ? ? ? ? // 表头名称
? ?? ? style, ? ? ? ? ?// 表头样式
? ?? ? columnSize, ? ? // 列宽 单位为 % - 默认 100%
? ??}

properties的说明:
? ??data
? ??Array
? ??{
? ?? ? [表头字段]: T,
? ??}
? ?

import {Message} from 'element-ui';
import {number2chinese} from './common';

// 默认配置
const optionsDefault = {
  
  properties: [],
  
  data: null,
  // 单据名
  // HTML 或 String
  // 例如:随货同行单
  title: '',
  // 订单号 - 必填
  orderNumber: '',
  // 单据创建时间
  // String - 必填
  // 格式必须为 xxxx-xx-xx
  createTime: '',
  // 制单人
  creator: '',
  //制单人所属科室
  billBranch: '',
  // 单据来源
  // HTML 或 String
  // 例如:供货单位、申领人
  fromWho: '',
  fromBranch: '',
  // 单据二维码
  qrcode: null,
  // 是否显示序号
  showIds: false,
  /**
   * 统计 - 累加 - 某条属性(必须为 number)
   * 一般为总价
   *
   * {
   *    val: '',      // 是否已有计算好的值,若没有则不填,可指定字段进行累加统计
   *    unit: '',     // 合计的单位,默认为 「元」
   *    field: '',    // 要累加的字段值(val 有值则不会累加统计)
   *    name: '',     // 对应的名字,默认为 「合计」
   *    colspan: 2,   // 字段名所占的单元格数,默认为 「2」
   *    showChinese: false, // 是否显示中文大写统计
   * }
   *
   */
  total: {},

  mulTotal: [],

  // header
  // HTML 或 String
  // 最上方的文字,例如医院名称、公司名称
  header: '临沂市河东区妇幼保健院',
  headerStyle: '',
  // 自定义 footer
  // HTML
  footer: '',
  // table item style
  formStyle: `border: 1px solid #000; padding: 5px 0; word-break: break-word;`,
  // table header style
  formHeaderStyle: `border: 1px solid #000; padding: 5px 0; word-break: break-word;`,
  // 序号列的宽度
  idsWidth: '2rem',
  // 文本对齐
  // center, left, right
  textAlign: 'center',

  // 空数据填充
  stuffing: '-',
  showStuffing:false,

};


const CONFIG = {
  a4: ['210mm', '297mm'],
  // 页边距 [上 右 下 左]
  pageMargin: ['0', '12.7mm', '12.7mm', '12.7mm'],
  // 二维码大小
  qrCodeSize: 70,
}

class PrintForm {

  options = {};
  _id = 1; // 起始序号
  _html = '';
  _class = 'print-form';  // 标识 div ,用于清除
  _iframe = null; // 当前 iframe
  _columnSize = '100%'; // 默认列宽
  _flagError = false;

  constructor(opts) {
    this.options = {...optionsDefault, ...opts};

    if (this.options.properties === null || this.options.length === 0) {
      console.error('表头为空');
      this.showMessage();
      return;
    }

    this.generateHtml();
  }

  /**
   * 生成 HTML 片段
   */
  generateHtml() {
    let page = 0;
    let flagStop = false; // 终止生成新页面

    if (this._html !== '') this._html = '';

    while (!flagStop) {
      flagStop = this.generateHtmlPage(page);
    }

    // 单据二维码
    if (this.options.qrcode) {
      this._html += `
        <script src="static/qrcode.min.js"></script>
        <script>
          var qrcode = new QRCode(document.getElementById("printFormQrCode"), {
            width : ${CONFIG.qrCodeSize},
            height : ${CONFIG.qrCodeSize}
          });
          qrcode.makeCode("${this.options.qrcode}");
        </script>
      `;
    }
  }

  /**
   * 生成页面
   * @param {int} page 页码
   * @return {boolean} 是否停止继续生成新页面
   */
  generateHtmlPage(page) {
    this._html += `
      <div
        id="printFormDiv${page}"
        style="
          padding: ${CONFIG.pageMargin[0]} ${CONFIG.pageMargin[1]} ${CONFIG.pageMargin[2]} ${CONFIG.pageMargin[3]}
        ">
    `;

    // 1级标题
    this._html += `
      <div
        style="
          font-weight: bold;
          font-size: 1.6rem;
          text-align: center;
          ${this.options.headerStyle ? this.options.headerStyle : ''}"
      >${this.options.header}</div>
    `;

    // 2级标题
    this._html += `
      <div style="margin-top: .5rem; text-align: center; font-size: 1.6rem;">
        ${this.options.title}
      </div>
    `;

    // 单据二维码
    if (this.options.qrcode && page === 0) {
      this._html += `<div id="printFormQrCode" style="position: absolute; top: 0;"></div>`;
    }

    /** start: 顶部基本信息 */
    let textCreateTime = `制单时间:${this.options.createTime}`;

    // 单据编号
    this._html += `
      <div style="margin-top: .8rem; text-align: right;">
        单据编号:
        <span style="color: red;">
          NO${this.options.orderNumber ? this.options.orderNumber : this.options.stuffing}
        </span>
      </div>
    `;
    // 来源和创建时间
    if (this.options.billBranch === '中心库') {
      this._html += `
        <div style="margin-top: .3rem; display: flex; justify-content: space-between;">
         <div style="margin-top: .3rem;">申请科室:${this.options.fromBranch}</div>
        <div>${this.options.fromWho}</div>
        <div>${textCreateTime}</div>
      </div>
      `;
    } else {
      this._html += `
      <div style="margin-top: .3rem; display: flex; justify-content: space-between;">
        <div>${this.options.fromWho}</div>
        <div>${textCreateTime}</div>
      </div>
    `;
    }

    /** end: 顶部基本信息 */

    this._html += `<table style="width: 100%; margin-top: .5rem; table-layout: fixed;" border="1" cellspacing="0">`;


    /** start: 表头 */
    this._html += `<thead style="display: table-header-group;">`;
    // 显示序号
    if (this.options.showIds) {
      this._html += `<th style="${this.options.formHeaderStyle}; width: ${this.options.idsWidth};">序号</th>`;
    }
    if (this.options.showStuffing){
      this.options.stuffing = ' ';
    }
    for (let i = 0; i < this.options.properties.length; i++) {
      this._html += `
        <th
          style="
            ${this.options.formHeaderStyle ? this.options.formHeaderStyle : ''};
            ${this.options.properties[i].style ? this.options.properties[i].style : ''};
            width: ${this.options.properties[i].columnSize ? this.options.properties[i].columnSize : this._columnSize};"
        >${this.options.properties[i].name}</th>`;
    }
    this._html += `</thead>`;
    /** end: 表头 */


    /** start: 表行数据 */
    this._html += `<tbody>`;
    if (this.options.data !== null) {
      for (let i = 0; i < this.options.data.length; i++) {
        this._html += `<tr>`;

        // 显示序号
        if (this.options.showIds) {
          this._html += `
            <td
              style="${this.options.formStyle};
                width: ${this.options.idsWidth};
                text-align: ${this.options.textAlign}"
            >
              ${this._id}
            </td>
          `;

          this._id++;
        }

        const rowData = this.options.data[i];
        let textData = null;

        // 只打印表头中存在的字段
        for (let j = 0; j < this.options.properties.length; j++) {
          const property = this.options.properties[j];

          textData = rowData[property.field] ? rowData[property.field] : this.options.stuffing;

          this._html += `
            <td
              style="
                ${this.options.formStyle ? this.options.formStyle : ''};
                width: ${property.columnSize ? property.columnSize : this._columnSize};
                text-align: ${this.options.textAlign};"
            >${textData}</td>
          `;
        }

        this._html += `</tr>`;
      }

      // 合计某个属性
      if (Object.keys(this.options.total).length > 0 && this.options.total.showChinese) {
        let __total = 0;
        let __totalChinese = '';  // 统计 - 中文大写

        // 已传入计算好的值,则无需累加操作
        if (this.options.total.val) {
          __total = this.options.total.val;
        } else {
          for (let i = 0; i < this.options.data.length; i++) {
            __total += this.options.data[i][this.options.total.field];
          }
        }
        __total = __total.toFixed(2)

        const __totalName = this.options.total.name ? this.options.total.name : '合计';
        const __unit = this.options.total.unit ? this.options.total.unit : '元';
        const __colspanName = this.options.total.colspan ? this.options.total.colspan : 2;
        let __colspanVal = this.options.properties.length - __colspanName;
        let __colspanChinese = 0;

        if (this.options.showIds) __colspanVal++;

        // 统计 - 金额 - 中文大写
        if (this.options.total.showChinese) {
          __colspanVal = Math.ceil((this.options.properties.length - __colspanName) / 2);
          __colspanChinese = this.options.properties.length - __colspanName - __colspanVal;
          if (this.options.showIds) __colspanVal++;
        }

        this._html += `
          <tr>
            <td colspan="${__colspanName}" style="padding: .3rem; text-align: center;">${__totalName}</td>
            <td colspan="${__colspanVal}" style="padding: .3rem; text-align: center;">${__total} ${__unit}</td>
        `;

        if (__colspanChinese > 0) {
          __totalChinese = number2chinese(__total);

          this._html += `
            <td colspan="${__colspanChinese}" style="padding: .3rem; text-align: center;">${__totalChinese}</td>
          `;
        }
        this._html += '</tr>';
      }

      //展示试剂出入库统计中的合计
      if (this.options.mulTotal.length > 0) {
        const __totalName = '合计';
        const __colspanName = this.options.mulTotal.colspan ? this.options.mulTotal.colspan : 3;
        let __colspanVal = 1;
        this._html += `
          <tr>
           <td colspan="${__colspanName}" style="padding: .3rem; text-align: center;">${__totalName}</td>
        `;

        for (let i = 0; i < this.options.mulTotal.length; i++) {
          this._html += `
           <td colspan="${__colspanVal}" style="padding: .3rem; text-align: center;">${this.options.mulTotal[i]}</td>
        `;
        }
        this._html += '</tr>';
      }
    }

    this._html += `</tbody>`;
    /** end: 表行数据 */

    // this._html += `<tfoot style="display: table-footer-group; font-weight: bold">
    //   <tr>
    //       <td colspan="2" align="center" style="font-weight: bold; border: 3px double blue">每页都有的表尾</td>
    //   </tr>`;
    this._html += `</table>`;

    /** start: 底部数据 */
    this._html += `<div id="printFormFooter${page}">`;

    if (this.options.creator !== '') {
      this._html += `
        <div style="margin-top: .5rem;">制表人:${this.options.creator}</div>
      `;
    }



    this._html += this.options.footer;
    // todo 分页与总页码
    // this._html += `<div style="text-align: right; font-size: .8rem;">${page + 1} / <span id="printFormPageTotal${page}">1</span></div>`;
    this._html += `</div>`;
    /** 底部数据 */

    this._html += `</div>`;

    return true;
  }

  calculateHeight() {
    // todo 计算高度,实现分页
    // new UnitConversion().px2mm(value)
  }

  /**
   * 挂载代码片
   */
  mountHtml() {
    if (this._flagError) return;

    this.clearAll();

    let iframe = document.createElement('iframe');
    iframe.setAttribute('class', this._class);
    // iframe.setAttribute('style', 'width: 900px; height: 500px; margin-left: 300px;');
    iframe.setAttribute('style', 'position: absolute; width: 0; height: 0; left: -500px; top: -500px;');

    window.document.body.appendChild(iframe);
    iframe.contentWindow.document.write(this._html);
    iframe.contentWindow.document.body.style.margin = '0';

    this._iframe = iframe;
  }

  /**
   * 打印
   */
  async toPrint() {
    if (this._flagError) return;

    this.mountHtml();

    // 需要等待二维码生成
    if (this.options.qrcode) {
      const _divQrCode = this._iframe.contentWindow.document.querySelector("#printFormQrCode");

      await this._waitGenerateQrcode(_divQrCode);
    }

    this._iframe.contentWindow.print();

    this._iframe.parentNode.removeChild(this._iframe);
    this._iframe = null;
    // this.clearAll();
  }

  /**
   * 等待二维码生成
   */
  _waitGenerateQrcode(_divQrCode) {

    return new Promise(resolve => {
      setTimeout(() => {
        resolve();
      }, 300);
    });
    // const _this = this;

    // if (!this._iframe) return;

    // if (_divQrCode.getAttribute('title')) {
    //   this._iframe.contentWindow.print();
    //   return new Promise.resolve();
    // } else {
    //   setTimeout(() => {
    //     const rr  = _this._waitGenerateQrcode(_divQrCode);
    //     return _this._waitGenerateQrcode(_divQrCode);
    //   }, 100);
    // }

    // return new Promise(resolve => {
    //   if (_divQrCode.getAttribute('title')) {
    //     resolve();
    //   } else {
    //     setTimeout(() => {
    //       _this._waitGenerateQrcode(_divQrCode);
    //     }, 100);
    //   }
    // })
  }

  /**
   * 清除挂载的所有单据
   */
  clearAll() {
    const arrDom = document.body.querySelectorAll(`.${this._class}`);
    for (let i = 0; i < arrDom.length; i++) {
      window.document.body.removeChild(arrDom[i]);
    }

    this._iframe = null;
  }

  /**
   * 给用户的提醒
   */
  showMessage() {
    // 目前只用到 error
    this._flagError = true;
    Message.error('打印单数据处理错误,请联系管理员');
  }
}

export {
  PrintForm
}

????????然后在前端页面确定要打印的数据

const printFormColumn = [ 
  { 
    field: 'reagentName', 
    name: '耗材名称', 
    columnSize: '200%' 
  }, 
  { 
    field: 'reagentType', 
    name: '型号规格', 
    columnSize: '100%' 
  }, 
  { 
    field: 'factory', 
    name: '生产厂家', 
  }, 
  { 
    field: 'supplierName', 
    name: '供货商', 
  }, 
  { 
    field: 'reagentUnit', 
    name: '单位', 
    columnSize: '60%' 
  }, 
  { 
    field: 'quantity', 
    name: '数量', 
    columnSize: '60%' 
  }, 
  { 
    field: 'reagentTemp', 
    name: '储存温度', 
    columnSize: '50%' 
  }, 
]; 

????????在前端调用打印方法?

handlePrint() { 
      if (this.list === null) { 
        Message.error('库存数据获取失败'); 
        return; 
      } 
 
      const _data = this.list; 

      const _createTime = formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss'); 

      const pf = new PrintForm({ 
        orderNumber: Math.floor((Math.random() + Math.floor(Math.random() * 9 + 1)) * Math.pow(10, 9)), 
        creator: `${this.trueName}`, 
        createTime: _createTime, 
        title: '库存汇总', 
        properties: printFormColumn, 
        data: _data, 
        showIds: true, 
        total: { 
          field: 'quantity', 
          showChinese: false, 
        }, 
      }); 
      pf.toPrint(); 
    }, 

????????同时记得import相应的方法,并且在按钮里设置onclick方法。

????????这样打印的方法就完成了,稍作修改便可在所有需要的地方进行打印。

? ? ? ? 打印效果预览如图:

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-24 18:03:56  更:2022-05-24 18:07:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:50:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码