index.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. <template>
  2. <canvas ref="shareCanvas" :width="canvasWidth" :height="canvasHeight"></canvas>
  3. <Loading :visible="loading" />
  4. </template>
  5. <script setup>
  6. import { ref, watch } from 'vue'
  7. import { getJobAdvertisedShareQrcode } from '@/api/position'
  8. import { saveShareQuery } from '@/api/recruit/personal/jobFair'
  9. const emit = defineEmits(['success'])
  10. const props = defineProps({
  11. canvasWidth: {
  12. type: Number,
  13. default: 540
  14. },
  15. canvasHeight: {
  16. type: Number,
  17. default: 788
  18. },
  19. show: Boolean,
  20. enterpriseName: String,
  21. logoUrl: String,
  22. positionList: Array,
  23. jobFairId: String,
  24. enterpriseId: String,
  25. bgImg: String
  26. })
  27. const loading = ref(false)
  28. const shareCanvas = ref(null)
  29. const imgSrc = ref('')
  30. const drawCanvas = () => {
  31. loading.value = true
  32. const canvas = shareCanvas.value
  33. const ctx = canvas.getContext('2d')
  34. const img = new Image()
  35. img.crossOrigin = 'anonymous'
  36. img.onload = async () => {
  37. //清空画布
  38. ctx.clearRect(0, 0, props.canvasWidth, props.canvasHeight)
  39. // 设置背景图片
  40. ctx.drawImage(img, 0, 0, props.canvasWidth, props.canvasHeight)
  41. // 分享二维码
  42. const secondImg = new Image()
  43. secondImg.crossOrigin = 'anonymous'
  44. secondImg.onload = () => {
  45. // 分享二维码
  46. ctx.drawImage(secondImg, 80, 550, 110, 110)
  47. ctx.font = 'bold 18px Arial'
  48. ctx.fillStyle = '#000'
  49. ctx.fillText('海量职位火热招聘中', 280, 605)
  50. ctx.font = '15px Arial'
  51. ctx.fillStyle = '#999'
  52. ctx.fillText('长按图片识别二维码', 290, 625)
  53. const thirdImg = new Image()
  54. thirdImg.crossOrigin = 'anonymous'
  55. thirdImg.onload = () => {
  56. // 企业头像
  57. ctx.save()
  58. const secondImgWidth = 80
  59. const secondImgHeight = 80
  60. const x = (canvas.width - secondImgWidth) / 2
  61. const y = canvas.height - secondImgHeight - 460
  62. ctx.beginPath()
  63. ctx.arc(x + secondImgWidth / 2, y + secondImgHeight / 2, secondImgWidth / 2, 0, Math.PI * 2, true)
  64. ctx.clip()
  65. ctx.drawImage(thirdImg, x, y, secondImgWidth, secondImgHeight)
  66. ctx.restore()
  67. // 企业名称
  68. const maxTextWidth = 400
  69. const text = props.enterpriseName
  70. const fontStyle = 'bold 18px Arial'
  71. ctx.font = fontStyle
  72. let truncatedText = text
  73. while (ctx.measureText(truncatedText + '...').width > maxTextWidth && truncatedText.length > 0) {
  74. truncatedText = truncatedText.slice(0, -1)
  75. }
  76. if (truncatedText !== text) truncatedText += '...'
  77. const textX = x + (secondImgWidth - ctx.measureText(truncatedText).width) / 2
  78. const textY = y + secondImgHeight + 30
  79. ctx.fillStyle = '#000'
  80. ctx.fillText(truncatedText, textX, textY)
  81. // 职位标签
  82. const tagPaddingLeftRight = 20
  83. const tagPaddingTopBottom = 10
  84. const tagRadius = 8
  85. const tagSpacing = 30
  86. let tagY = textY + tagSpacing
  87. props.positionList.forEach((tag) => {
  88. let truncatedTag = tag
  89. while (ctx.measureText(truncatedTag + '...').width > maxTextWidth - 2 * tagPaddingLeftRight && truncatedTag.length > 0) {
  90. truncatedTag = truncatedTag.slice(0, -1)
  91. }
  92. if (truncatedTag !== tag) truncatedTag += '...'
  93. const tagWidth = ctx.measureText(truncatedTag).width + 2 * tagPaddingLeftRight
  94. const tagX = x + (secondImgWidth - tagWidth) / 2
  95. ctx.fillStyle = '#246a6c'
  96. ctx.beginPath()
  97. ctx.moveTo(tagX + tagRadius, tagY)
  98. ctx.lineTo(tagX + tagWidth - tagRadius, tagY)
  99. ctx.quadraticCurveTo(tagX + tagWidth, tagY, tagX + tagWidth, tagY + tagRadius)
  100. ctx.lineTo(tagX + tagWidth, tagY + tagPaddingTopBottom * 2)
  101. ctx.quadraticCurveTo(tagX + tagWidth, tagY + tagPaddingTopBottom * 2 + tagRadius, tagX + tagWidth - tagRadius, tagY + tagPaddingTopBottom * 2 + tagRadius)
  102. ctx.lineTo(tagX + tagRadius, tagY + tagPaddingTopBottom * 2 + tagRadius)
  103. ctx.quadraticCurveTo(tagX, tagY + tagPaddingTopBottom * 2 + tagRadius, tagX, tagY + tagPaddingTopBottom * 2)
  104. ctx.lineTo(tagX, tagY + tagRadius);
  105. ctx.quadraticCurveTo(tagX, tagY, tagX + tagRadius, tagY)
  106. ctx.closePath()
  107. ctx.fill()
  108. ctx.font = '16px Arial'
  109. ctx.fillStyle = '#fff'
  110. ctx.fillText(truncatedTag, tagX + tagPaddingLeftRight, tagY + tagPaddingTopBottom + 10)
  111. tagY += tagPaddingTopBottom * 2 + tagSpacing
  112. })
  113. imgSrc.value = canvas.toDataURL('image/png')
  114. loading.value = false
  115. emit('success', imgSrc.value)
  116. }
  117. thirdImg.src = props.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'
  118. }
  119. // 保存分享参数
  120. const result = await saveShareQuery({ id: '14304' })
  121. const query = {
  122. scene: 'id=' + result,
  123. path: 'pagesB/positionDetail/index',
  124. width: 200,
  125. autoColor: false,
  126. checkPath: true,
  127. hyaline: false
  128. }
  129. const data = await getJobAdvertisedShareQrcode(query)
  130. secondImg.src = 'data:image/png;base64,' + data
  131. }
  132. img.onerror = (error) => {
  133. console.error('Failed to load image:', error)
  134. loading.value = false
  135. }
  136. img.src = props.bgImg
  137. }
  138. watch(
  139. () => props.show,
  140. (val) => {
  141. if (val) drawCanvas()
  142. }
  143. )
  144. </script>