print.vue 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. <template>
  2. <div class="flex flex-col items-stretch w-[24cm] max-w-[24cm]">
  3. <div class="flex justify-between">
  4. <div class="mb-2">
  5. <el-tooltip
  6. content="其实二维码在提交SO后就会自动刷新, 除非你点了右边的Clean QR Code"
  7. >
  8. <el-button
  9. class="custom-button"
  10. @click="reGenerateQrCode"
  11. >
  12. Generate QR Code
  13. </el-button>
  14. </el-tooltip>
  15. <el-tooltip content="除非你不想打印二维码">
  16. <el-button @click="qrcodeVisible = false">Clean QR Code</el-button>
  17. </el-tooltip>
  18. </div>
  19. <div>
  20. <el-button
  21. type="danger"
  22. @click="clean"
  23. >
  24. Clean Input Job
  25. </el-button>
  26. <el-tooltip content="确认下面的预览内容没问题再点打印">
  27. <el-button
  28. class="custom-button"
  29. :loading="printing"
  30. @click="onBtnPrint"
  31. >
  32. Print Page
  33. </el-button>
  34. </el-tooltip>
  35. </div>
  36. </div>
  37. <div class="">
  38. <el-form inline>
  39. <el-form-item
  40. v-if="scene === 'SO'"
  41. label="QR Code Content"
  42. class="text-left w-[100%]"
  43. >
  44. <div
  45. class="el-input el-input__wrapper el-input__inner cursor-not-allowed hover:cursor-not-allowed"
  46. >
  47. {{ selectedContent.map((i) => i.Reference).join(';') }}
  48. </div>
  49. </el-form-item>
  50. <el-form-item
  51. label="Total Page"
  52. style="width: 20%"
  53. prop="total"
  54. >
  55. <el-input
  56. v-model.number="total"
  57. :type="'number'"
  58. ></el-input>
  59. </el-form-item>
  60. <el-form-item
  61. label="Text direction"
  62. style="width: 15%"
  63. prop="rotated"
  64. >
  65. <el-switch v-model="rotated"></el-switch>
  66. </el-form-item>
  67. <el-form-item
  68. label="Page width(CM)"
  69. style="width: 20%"
  70. prop="pageWidth"
  71. >
  72. <el-input
  73. v-model.number="pageWidth"
  74. :type="'number'"
  75. ></el-input>
  76. </el-form-item>
  77. <el-form-item
  78. label="Page Height(CM)"
  79. style="width: 20%"
  80. prop="pageHeight"
  81. >
  82. <el-input
  83. v-model.number="pageHeight"
  84. :type="'number'"
  85. ></el-input>
  86. </el-form-item>
  87. <el-form-item
  88. label="Font Size"
  89. style="width: 50%"
  90. >
  91. <el-slider
  92. v-model="pdfFontSize"
  93. :step="1"
  94. ></el-slider>
  95. </el-form-item>
  96. <el-form-item
  97. style="width: 100%"
  98. label="箱唛"
  99. >
  100. <el-input
  101. v-model="mai"
  102. type="textarea"
  103. ></el-input>
  104. </el-form-item>
  105. <div class="flex gap-1">
  106. <div
  107. v-for="(item, index) in logoList"
  108. :key="item"
  109. class="w-[60px] h-[60px] bg-contain bg-center bg-no-repeat cursor-pointer"
  110. :style="{ backgroundImage: `url(${item})` }"
  111. @click="selectlogo(index)"
  112. ></div>
  113. </div>
  114. </el-form>
  115. </div>
  116. <br />
  117. <div class="flex flex-wrap">
  118. <div
  119. v-for="i in total"
  120. :key="i"
  121. :style="{
  122. width: `${pageWidth}cm`,
  123. height: `${pageHeight}cm`,
  124. }"
  125. class="pdf-wrap relative border border-solid border-gray-200 mb-2 mr-2"
  126. >
  127. <div
  128. :id="`pdfItem-${i}`"
  129. :ref="(el) => setElement(el, i)"
  130. class="pdf-area w-[100%] h-[100%] relative text-center p-4 flex flex-col items-stretch justify-center"
  131. :class="{}"
  132. :style="{
  133. width: rotated ? `${pageHeight}cm` : `${pageWidth}cm`,
  134. height: rotated ? `${pageWidth}cm` : `${pageHeight}cm`,
  135. transformOrigin: 'top left',
  136. transform: rotated ? 'rotate(90deg)' : '',
  137. right: rotated ? `-${pageWidth}cm` : '',
  138. }"
  139. >
  140. <!-- bottom: rotated ? `-${(i - 1) * (pageWidth - pageHeight)}cm` : '', -->
  141. <div
  142. class="mai font-medium break-all mb-4"
  143. :style="{
  144. fontSize: `${pdfFontSize}pt`,
  145. }"
  146. @dblclick="ElMessage.info('hello')"
  147. >
  148. {{ mai }}
  149. </div>
  150. <div class="flex justify-center">
  151. <div class="font-medium text-5xl">{{ i }} / {{ total }}</div>
  152. </div>
  153. <div class="flex gap-1 absolute bottom-4 left-4">
  154. <div
  155. v-for="item in selectedLogo"
  156. :key="item"
  157. class="w-[90px] h-[90px] bg-contain bg-center bg-no-repeat"
  158. :style="{ backgroundImage: `url(${item})` }"
  159. ></div>
  160. </div>
  161. <div
  162. v-show="qrcodeVisible"
  163. class="qrcode absolute bottom-4 right-4 w-[110px] h-[110px] border border-solid border-gray-400"
  164. >
  165. <img
  166. v-show="qrcodeURL.length"
  167. style="width: 100%; height: auto"
  168. :src="qrcodeURL"
  169. alt="qr-code"
  170. />
  171. </div>
  172. </div>
  173. </div>
  174. </div>
  175. </div>
  176. </template>
  177. <!-- todo 后续so search如果有更新, 引用这个组件. -->
  178. <script setup lang="ts">
  179. import {
  180. defineComponent,
  181. defineProps,
  182. nextTick,
  183. ref,
  184. watch,
  185. computed,
  186. } from 'vue'
  187. import {
  188. ElMessage,
  189. ElInput,
  190. ElButton,
  191. ElForm,
  192. ElFormItem,
  193. ElSlider,
  194. ElTooltip,
  195. ElSwitch,
  196. } from 'element-plus'
  197. import jspdf from 'jspdf'
  198. import qrcode from 'qrcode'
  199. import html2canvas from 'html2canvas'
  200. import debounce from 'lodash.debounce'
  201. defineComponent({
  202. name: 'ComponentPrint',
  203. })
  204. const { content = [], scene = 'SO', autoOpenQRCode = true } = defineProps<{
  205. content: any[]
  206. scene?: string
  207. autoOpenQRCode?: boolean
  208. }>()
  209. let selectedContent = ref([] as any[])
  210. const mai = ref('')
  211. watch(
  212. () => content,
  213. () => {
  214. selectedContent.value = content.slice()
  215. if (scene === 'SO') {
  216. mai.value = selectedContent.value
  217. .map((c: any) => `${c.Reference}_${c.Contract_Title}`)
  218. .join('; ')
  219. } else if (scene === 'QC') {
  220. mai.value = selectedContent.value.join('')
  221. }
  222. if (mai.value.length && autoOpenQRCode) {
  223. nextTick(() => {
  224. reGenerateQrCode()
  225. })
  226. }
  227. },
  228. { deep: true, immediate: true },
  229. )
  230. const clean = () => {
  231. mai.value = ''
  232. selectedContent.value = []
  233. }
  234. let qrcodeVisible = ref(false)
  235. let qrcodeURL = ref('')
  236. async function reGenerateQrCode() {
  237. await qrcode.toDataURL(
  238. mai.value,
  239. { errorCorrectionLevel: 'H' },
  240. function (err: any, url: any) {
  241. qrcodeURL.value = url
  242. },
  243. )
  244. qrcodeVisible.value = true
  245. }
  246. const pdfList = ref({} as any)
  247. const setElement = (el: any, index: number) => {
  248. pdfList.value[`${index}`] = el
  249. }
  250. const getLogoPath = (path: string) => {
  251. return new URL(path, import.meta.url).href
  252. }
  253. const logoList = ref([] as any[])
  254. async function getLogoList() {
  255. (
  256. [
  257. getLogoPath('/assets/label/heavy.png'),
  258. getLogoPath('/assets/label/keep_dry.png'),
  259. getLogoPath('/assets/label/place_up.png'),
  260. getLogoPath('/assets/label/care.png'),
  261. getLogoPath('/assets/label/glass.png'),
  262. ] as string[]
  263. ).forEach((i: string, index: number) => {
  264. fetch(i)
  265. .then((res) => res.blob())
  266. .then((blob) => (logoList.value[index] = URL.createObjectURL(blob)))
  267. })
  268. }
  269. getLogoList()
  270. const selectlogo = (index: number) => {
  271. const i = selectedLogoIndex.value.indexOf(index)
  272. if (i > -1) {
  273. selectedLogoIndex.value.splice(i, 1)
  274. } else {
  275. selectedLogoIndex.value.push(index)
  276. }
  277. }
  278. let selectedLogoIndex = ref([] as number[])
  279. let selectedLogo = computed(() =>
  280. logoList.value.filter((i: any, index: number) =>
  281. selectedLogoIndex.value.includes(index),
  282. ),
  283. )
  284. let total = ref(1 as number)
  285. let pdfFontSize = ref(32)
  286. let pageWidth = ref(20) // 目标pdf的页宽
  287. let pageHeight = ref(10) // 目标pdf的页高
  288. /**
  289. * 宽大于等于高
  290. */
  291. let orientation = computed(() => pageWidth.value >= pageHeight.value)
  292. let rotated = ref(false)
  293. let pdf: any = null
  294. let printing = ref(false)
  295. const reGeneratePDF = debounce(function () {
  296. printing.value = true
  297. pdf = null
  298. pdf = new jspdf({
  299. orientation: orientation.value ? 'l' : 'p',
  300. unit: 'cm',
  301. format: [pageWidth.value, pageHeight.value],
  302. })
  303. const pool = []
  304. for (const key in pdfList.value) {
  305. if (Object.prototype.hasOwnProperty.call(pdfList.value, key)) {
  306. pool.push(generatePDF(pdfList.value[key], key))
  307. }
  308. }
  309. Promise.all(pool).then(() => {
  310. printing.value = false
  311. })
  312. }, 300)
  313. watch(
  314. [
  315. total,
  316. mai,
  317. pdfFontSize,
  318. pageWidth,
  319. pageHeight,
  320. qrcodeVisible,
  321. rotated,
  322. selectedLogo,
  323. ],
  324. () => {
  325. nextTick(() => reGeneratePDF())
  326. },
  327. )
  328. async function onBtnPrint() {
  329. let fileName = ''
  330. switch (scene) {
  331. case 'QC':
  332. fileName = ''
  333. break
  334. default:
  335. fileName = selectedContent.value.map((i: any) => i.Reference).join('_')
  336. }
  337. window.open(
  338. // @ts-ignore 照着jspdf文档搬的...ts报错
  339. pdf.output('bloburl', {
  340. fileName,
  341. }),
  342. )
  343. }
  344. function generatePDF(pdfItem: HTMLElement, key: string) {
  345. return html2canvas(pdfItem, {
  346. allowTaint: true,
  347. useCORS: true,
  348. backgroundColor: '#fff', // 一定要设背景颜色,否则有的浏览器就会变花,比如Edge
  349. scale: 3, // 缩放倍率调整清晰度
  350. }).then((canvas) => {
  351. const width = canvas.width
  352. const height = canvas.height
  353. const image = new Image()
  354. const target = document.createElement('canvas')
  355. target.height = height
  356. target.width = width
  357. image.onload = function () {
  358. const ctx = target.getContext('2d')
  359. ctx?.drawImage(image, 0, 0)
  360. if (Number(key) > 1) pdf.addPage()
  361. pdf.addImage(
  362. target.toDataURL('image/jpeg', 1.0),
  363. 'JPEG',
  364. 0,
  365. 0,
  366. pageWidth.value,
  367. pageHeight.value,
  368. )
  369. }
  370. image.src = canvas.toDataURL('image/jpeg', 1.0)
  371. })
  372. }
  373. </script>
  374. <style scoped></style>