pdfViewModal.vue 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. <template>
  2. <a-modal
  3. centered
  4. v-drag
  5. class="common-pdfView-modal"
  6. :footer="null"
  7. :maskClosable="false"
  8. v-model="isShow"
  9. :title="modalTit"
  10. :zIndex="1001"
  11. :destroyOnClose="true"
  12. @cancel="handleCommonCancel"
  13. width="1000px">
  14. <a-spin :spinning="spinning" tip="Loading...">
  15. <div class="common-main">
  16. <div id="canvas"></div>
  17. <div class="tools">
  18. <div>
  19. 调整比例:{{ Math.round(scalVal*100) }}%
  20. <div>
  21. <a-button :disabled="scalLoading" style="margin-right: 10px;" size="small" @click="setScalVal(0)"><a-icon type="minus" /> 缩小</a-button>
  22. <a-button :disabled="scalLoading" size="small" @click="setScalVal(1)"><a-icon type="plus" /> 放大</a-button>
  23. </div>
  24. </div>
  25. <div>
  26. <div v-if="hasClipboard">
  27. <a-button :disabled="scalLoading" size="small" @click="handleCapture('export')"><a-icon type="export" /> 导出图片</a-button>
  28. </div>
  29. </div>
  30. </div>
  31. <vue-scroll :ops="ops" :style="{width: '100%',height: '70vh',margin:'0 auto',padding: '10px 0'}">
  32. <div v-if="loadedRatio > 0 && loadedRatio < 1" style="background-color: green; color: white; text-align: center" :style="{ width: loadedRatio * 100 + '%' }">{{ Math.floor(loadedRatio * 100) }}%</div>
  33. <pdf
  34. v-for="i in numPages"
  35. :key="i"
  36. :src="src"
  37. :page="i"
  38. style="display: inline-block; width: 25%"
  39. ></pdf>
  40. </vue-scroll>
  41. </div>
  42. <div class="btn-box">
  43. <a-button size="large" style="margin-right: 25px;" @click="handleCommonCancel" v-if="isCancel">{{ cancelText }}</a-button>
  44. <a-button size="large" style="margin-right: 25px;" type="default" @click="handleCapture(hasClipboard?'copy':'export')">
  45. {{ hasClipboard?'复制截图':'导出图片' }}
  46. </a-button>
  47. <a-button size="large" type="primary" :loading="$store.state.app.printLoading" @click="handleCommonOk">
  48. {{ $store.state.app.printLoading?'打印中...':okText }}
  49. </a-button>
  50. </div>
  51. </a-spin>
  52. </a-modal>
  53. </template>
  54. <script>
  55. import moment from 'moment'
  56. import pdf from 'vue-pdf-signature'
  57. import CMapReaderFactory from 'vue-pdf-signature/src/CMapReaderFactory.js'
  58. import { pdfPrint } from '@/libs/JGPrint'
  59. export default {
  60. components: {
  61. pdf
  62. },
  63. name: 'PdfViewModal',
  64. props: {
  65. openModal: { // 弹框显示状态
  66. type: Boolean,
  67. default: false
  68. },
  69. modalTit: { // 弹框标题
  70. type: String,
  71. default: '打印预览'
  72. },
  73. okText: { // 确定 按钮文字
  74. type: String,
  75. default: '打印'
  76. },
  77. cancelText: { // 取消 按钮文字
  78. type: String,
  79. default: '取消'
  80. },
  81. isCancel: { // 是否显示 取消 按钮
  82. type: Boolean,
  83. default: true
  84. },
  85. spinning: { // 加载中
  86. type: Boolean,
  87. default: false
  88. }
  89. },
  90. data () {
  91. return {
  92. scalLoading: false,
  93. pdfAllLoad: 0,
  94. scalVal: 0.75,
  95. isShow: this.openModal, // 是否打开弹框
  96. show: true,
  97. src: '',
  98. loadedRatio: 0,
  99. page: 1,
  100. numPages: undefined,
  101. rotate: 0,
  102. curpage: 1,
  103. total: 3,
  104. ops: {
  105. vuescroll: {},
  106. scrollPanel: {},
  107. rail: {
  108. keepShow: true
  109. },
  110. bar: {
  111. hoverStyle: true,
  112. onlyShowBarOnScroll: false, // 是否只有滚动的时候才显示滚动条
  113. background: '#9d9d9d', // 滚动条颜色
  114. opacity: 0.5, // 滚动条透明度
  115. size: '10px',
  116. 'overflow-x': 'hidden'
  117. }
  118. },
  119. printIndex: undefined,
  120. printList: []
  121. }
  122. },
  123. computed: {
  124. hasClipboard () {
  125. return typeof ClipboardItem !== 'undefined'
  126. }
  127. },
  128. methods: {
  129. // 选择打印机
  130. changePrint (v) {
  131. this.printIndex = v
  132. this.$store.commit('SET_printDefNeedle', this.printIndex)
  133. this.$store.state.app.printTaskID = undefined
  134. },
  135. // 缩放
  136. setScalVal (t) {
  137. if (!this.scalLoading) {
  138. this.scalLoading = true
  139. this.scalVal = this.scalVal + (t ? 0.05 : -0.05)
  140. if (this.scalVal > 1) {
  141. this.scalVal = 1
  142. }
  143. if (this.scalVal < 0.5) {
  144. this.scalVal = 0.5
  145. }
  146. setTimeout(() => {
  147. this.scalLoading = false
  148. }, 1000)
  149. }
  150. },
  151. getUrl (item) {
  152. const src = pdf.createLoadingTask({ url: item, CMapReaderFactory })
  153. return src
  154. },
  155. setData (data) {
  156. // console.log(data)
  157. this.curpage = 1
  158. this.total = data.length
  159. const defPrint = this.$store.state.app.printDefNeedle
  160. this.printIndex = defPrint ? (defPrint == 'undefined' ? undefined : defPrint) : undefined
  161. },
  162. error: function (err) {
  163. console.log(err)
  164. },
  165. // 确定
  166. handleCommonOk () {
  167. const _this = this
  168. const hasPrint = this.printList.find(item => item.name == this.printIndex)
  169. // 已选择打印机
  170. if (hasPrint) {
  171. pdfPrint(_this.$store.state.app.pdfPrintList, 0)
  172. } else {
  173. this.$message.info('请选择打印机')
  174. }
  175. _this.$emit('ok')
  176. },
  177. // 取消
  178. handleCommonCancel () {
  179. this.isShow = false
  180. this.$store.commit('SET_pdfPrintList', [])
  181. this.$store.commit('SET_showPdfPrint', false)
  182. this.$emit('cancel')
  183. this.scalVal = 0.75
  184. },
  185. // 截图
  186. handleCapture (type) {
  187. if (this.scalLoading) { return }
  188. const pdf = this.$refs.pdf
  189. const len = pdf.length
  190. const imgsList = []
  191. const canvas = document.createElement('canvas')
  192. let maxHeight = 0
  193. const maxWidth = pdf[0].$refs.canvas.width
  194. for (let i = 0; i < len; i++) {
  195. const imgs = pdf[i].$refs.canvas
  196. maxHeight = maxHeight + imgs.height
  197. imgsList.push(imgs)
  198. }
  199. // 设置画布宽度和高度
  200. canvas.width = maxWidth
  201. canvas.height = maxHeight
  202. const ctx = canvas.getContext('2d')
  203. // 开始合并
  204. let y = 0
  205. imgsList.map(item => {
  206. ctx.drawImage(item, 0, y, item.width, item.height)
  207. y = y + item.height
  208. })
  209. const base64Str = canvas.toDataURL('image/png')
  210. // 复制图片
  211. if (type == 'copy') {
  212. this.copyImgBtn(base64Str)
  213. }
  214. // 下载图片
  215. if (type == 'export') {
  216. const fileName = moment().format('YYYYMMDDHHmmss')
  217. this.downloadFile(fileName, base64Str)
  218. }
  219. // 清除画布
  220. ctx.clearRect(0, 0, maxWidth, maxHeight)
  221. },
  222. downloadFile (fileName, base64Data) {
  223. const aLink = document.createElement('a')
  224. const blob = this.convertBase64ToBlob(base64Data.split(',')[1], 'image/png')
  225. const evt = document.createEvent('HTMLEvents')
  226. // initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为
  227. evt.initEvent('click', true, true)
  228. aLink.download = fileName
  229. aLink.href = URL.createObjectURL(blob)
  230. aLink.click()
  231. },
  232. // 将照片复制到剪贴版
  233. copyImgBtn (base64Data) {
  234. const blobInput = this.convertBase64ToBlob(base64Data.split(',')[1], 'image/png')
  235. if (navigator.clipboard) {
  236. const clipboardItemInput = new ClipboardItem({ 'image/png': blobInput })
  237. navigator.clipboard.write([clipboardItemInput])
  238. this.$message.success('截图成功,可以右键去粘贴图片!')
  239. } else {
  240. const image = nativeImageElectron.createFromDataURL(base64Data)
  241. clipboardElectron.writeImage(image)
  242. this.$message.success('截图成功,可以右键去粘贴图片!')
  243. }
  244. },
  245. convertBase64ToBlob (base64, type) {
  246. var bytes = window.atob(base64)
  247. var ab = new ArrayBuffer(bytes.length)
  248. var ua = new Uint8Array(ab)
  249. for (var i = 0; i < bytes.length; i++) {
  250. ua[i] = bytes.charCodeAt(i)
  251. }
  252. return new Blob([ab], { type: type })
  253. }
  254. },
  255. watch: {
  256. // 父页面传过来的弹框状态
  257. openModal (newValue, oldValue) {
  258. this.isShow = newValue
  259. },
  260. // 重定义的弹框状态
  261. isShow (newValue, oldValue) {
  262. if (!newValue) {
  263. this.$emit('cancel')
  264. }else{
  265. this.setData()
  266. }
  267. }
  268. }
  269. }
  270. </script>
  271. <style lang="less">
  272. .common-pdfView-modal{
  273. .ant-modal-body{
  274. padding: 0 0 20px 0;
  275. }
  276. .common-main{
  277. background-color: #666;
  278. .tools{
  279. padding: 10px;
  280. display: flex;
  281. align-items: center;
  282. border-bottom: 1px solid #eee;
  283. background-color: #f8f8f8;
  284. justify-content: space-between;
  285. > div{
  286. display: flex;
  287. align-items: center;
  288. >div{
  289. padding: 0 10px;
  290. }
  291. }
  292. }
  293. }
  294. .btn-box{
  295. text-align: center;
  296. margin-top: 20px;
  297. }
  298. }
  299. </style>