如何實現最真實的web打印

如何實現最真實的web打印javascript

公司組織了體檢,在醫院發現可使用身份證快捷打印體檢單,以爲很方便,但仔細一看,打印效果極差。css

現狀

注:如直接調用Ctrl + P,則是打印當前的視口,能夠選擇打印機及打印機紙張,這顯然不知足業務要求;若調用window.print(),則只能將css寫在html上一塊兒傳到打印機,且沒法預設打印機及紙張和其餘設置,也不能保存模板。翻閱了一下大部分打印,都是調用window.print()去實現,都存在一下問題:html

  • 打印樣式、排版問題。
    打印樣式與瀏覽器書寫不一樣,頁面顯示書寫一套css,傳給打印機的html字符串也要書寫一套css,並且沒法兼容全部紙張。
  • 沒法限定預設打印機、打印機紙張及保存爲模板。
    使用ActiveX(僅在IE下),顯然不知足業務要求。
    使用Websocket與本機創建鏈接獲取本機打印信息。
  • 自定義紙張問題。
    沒法作到自定義紙張。
  • 瀏覽器顯示與打印結果不一致。
    這個是最爲常見也亟待解決的問題。

恰巧須要作打印功能,因而決定重構,解決上面的問題。我採用的是第二種方案並集成PAZU雲打印,使用c++開發的一個exe腳本,在運行時與本機Websocket鏈接返回打印信息。須要詳細瞭解的同窗請移步:PAZU官網前端

打印效果

打印結果在測試後趨向於真實打印,與web顯示幾乎一致。vue

打印思路

整體的思路是將打印機設備參數可視化,可配置,可預設到點擊打印時傳到打印設備上。不支持自定義就先同步到打印機,而後選擇。java

打印模板設置

1、獲取打印數據

比較典型的ERP詳情頁面,包含主表Form和子表Table明細node

一、獲取Form
GetSlideInfo () {
  let CondiData = []
  let SlideInfoVnode = this.$refs.SlideInfo.$children[0]
  let FormItems = findComponentsDownward(SlideInfoVnode, 'FormItem')
  FormItems.forEach((item, itemIndex) => {
    let field = item.$children
    if (field.length) {
      const fieldVnode = item.$children[0]
      let fieldValue = this.GetExportFieldValue(fieldVnode)
      let formItem = {
        key: item.prop,
        label: item.label.split(':')[0],
        value: fieldValue,
        index: itemIndex
      }
      Object.assign(formItem, fieldVnode.print)
      CondiData.push(formItem)
    }
  })
  return CondiData
}
複製代碼

使用findComponentsDownward獲取全部的FormItem,而後獲取FormItemVueComponment,在組件實例裏獲取到打印信息。
爲了支持列表批量打印詳情,因而註冊了一個v-printc++

import { typeOf } from '@/libs/util'
export default {
  inserted: (el, { value, arg }, vnode) => {
    if (!value) return
    if (typeOf(value) != 'object') value = {}
    vnode.componentInstance.print = Object.assign(value, { renderType: arg || 'input' })
  },
  update: (el, { value, arg }, vnode) => {
    if (!value) return
    if (typeOf(value) != 'object') value = {}
    vnode.componentInstance.print = Object.assign(value, { renderType: arg || 'input' })
  }
}
複製代碼

而後在頁面這樣書寫,便可在inserted時掛載到組件實例data上。web

v-print:pick="{id:'orderBranchId',code:'orderBranchCode',name:'orderBranchName'}"
複製代碼
二、構造打印路由數據

在構造數據以前,咱們要造好動態路由並加入前端路由白名單:api

{
  path: '/print',
  name: 'print',
  component: Main,
  children: [{
    path: 'print-setting/:sheetType',
    name: 'print-setting',
    meta: {
      title: '打印設置'
    },
    component: () => import('@/view/main-components/print-setting.vue')
  }]
}
複製代碼

要保持數據持久化,因而我將數據保存到store並同時set到sessionStorage:

import { setSession } from '@/libs/session.js'
export default {
  state: {
    printTabs: {}
  },
  mutations: {
    setPrintTab (state, tab) {
      state.printTabs[tab.route] = tab.data
      setSession(tab.route, tab.data)
    }
  }
}
複製代碼

構造好數據以後進入打印頁面:

this.setPrintTab({ route: this.$route.meta.code, data: params })
this.$router.push({ name: 'print-setting', params: { sheetType: this.$route.meta.code } })
複製代碼

2、建立打印模板

一、拖拽集成及自適應

打印拖拽項由四個可拖拽的draggable組成,可相互拖拽,這裏採用的是vuedraggable。

<Row style="height:100%">
  <Form style="height:100%" justify :label-position="labelPositon" ref="formList">
    <draggable id="formList" :list="formList" style="height:100%" group="people" :animation="150" :ghostClass="cls+'-left-item-chosen'">
      <Col
        :span="8*item.span"
        :class="[cls+'-form-item',{'item-select':item.selected}]"
        v-for="(item, index) in formList"
        :key="index"
        @click.native="handleFormItemClick(item, index)"
      >

        <form-item v-if="!item.blank" :label="item.label" >{{item.value}}</form-item>
        <div :class="cls+'-blank'" v-else></div>
      </Col>
    </draggable>
  </Form>
</Row>
複製代碼

設置4個可拖拽group相同便可相互拖拽。

二、打印機信息

讀取本機打印機:

PAZU.TPrinter.getPrinters()
複製代碼

讀取當前打印機紙張:

PAZU.TPrinter.getPaperForms()
複製代碼

打印及打印校驗:

validatePrintExe () {
  return new Promise((resolve, reject) => {
    PAZU.TPrinter.getPrinters(res => {
      if (res === 'err') {
        resolve(false)
      } else {
        resolve(true)
      }
    })
  })
},
async handlePrint () {
  if (await this.validatePrintExe()) {
    this.doPageSetup()
    PAZU.print('printContent'nullnulltrue)
    api.updatePrintCount({ idsObject.values(this.printData.ids).join('~&z') }, this.printData.action)
  } else {
    this.showPrintDownLoad = true
  }
}
複製代碼

同步自定義紙張到打印機:

handleSynchronizeComfirm () {
  this.$refs.synchronizeForm.validate(valid => {
    if (valid) {
      PAZU.TPrinter.createPaper(this.baseSetting.width, this.baseSetting.height, (res) => {
        if (res) {
          this.$Message.warning(`規格【${res}】的紙張已存在。`)
        } else {
          this.showPrintor = false
          this.$Message.info('同步成功。')
        }
      }, this.synchronize.printName, this.synchronize.pageName)
    }
  })
}
複製代碼

問題彙總

一套打印下來問題重重,需求也巨大,但在寫文檔章時發現不知該如何把可移植的思想及技術難點單方面的開放出來。這以後的文章我也不知如何寫起。

1、如何作到web顯示和打印結果一致?

  • web顯示和打印樣式使用同一套css並配置自適應。

2、如何打印出圖片?

  • 必須使用img標籤,圖片轉base64便可。

3、打印空白頁問題及處理?

  • 我是嚴格按照web顯示,精確到1px,這樣打印下來確實是不會出現空白頁,並且排版正確,但當打印頁數超過100頁時,囿於瀏覽器px計算小數處理問題,到100頁時居然有大概10mm的偏差。而後在每一頁加上page-break-after: always以後居然會出現莫名的空白頁。

4、keep-alive動態路由緩存問題

詳細能夠看我以前寫的《修改vue源碼實現對key的keep-alive》)後面我會針對源碼詳細講解爲什麼要修改源碼,爲什麼動態路由keep-alive失敗。vue3.0正式發佈以後我會第一之間測試3.0keep-alive。

相關文章
相關標籤/搜索