id.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. <template>
  2. <section class="cert-id">
  3. <section class="cert-id-box">
  4. <template v-if="detail.name">
  5. <h1>{{detail.name}}证书</h1>
  6. <div class="hr"></div>
  7. <canvas ref="canvas" width="850" height="520">对不起, 您的浏览器不支持绘图功能, 请更新浏览器.</canvas>
  8. <img ref="img" class="img" src="~@/assets/img/cert/bg.png" alt="img">
  9. <img ref="qrImg" class="img" src="/api/cert/certQrCode" alt="qrImg">
  10. <section style="width: 850px;">
  11. <a :href="userinfo.seo_uri">开发者个人主页 ></a>
  12. <br>
  13. <a :href="`/cert/freelancer?id=${detail.id}`">{{detail.name}}认证标准 ></a>
  14. </section>
  15. </template>
  16. <h1 v-else>未查询到证书</h1>
  17. </section>
  18. </section>
  19. </template>
  20. <script>
  21. import http from '@/plugins/http'
  22. import bg from '@/assets/img/cert/bg.png'
  23. import { mapState } from 'vuex'
  24. let canvas = null
  25. , ctx = null
  26. , img = null
  27. , interval = null
  28. , qrLoaded = false
  29. , imgLoaded = false
  30. , userinfoLoaded = false
  31. export default {
  32. async asyncData(ctx) {
  33. let res = await http.post(`/api/cert/getCertDetail`, { cert_no: ctx.query.no })
  34. , detail = {}
  35. if(res) {
  36. detail = res.data
  37. }
  38. return {
  39. detail
  40. }
  41. },
  42. head() {
  43. return {
  44. title: `${this.detail.name}证书-程序员客栈`,
  45. }
  46. },
  47. data() {
  48. return { detail: {} }
  49. },
  50. computed: {
  51. ...mapState(['userinfo']),
  52. },
  53. mounted() {
  54. if(!this.detail.name) return
  55. this.initCanvas()
  56. this.checkInfoFull()
  57. interval = setInterval(this.checkInfoFull, 500)
  58. },
  59. methods: {
  60. checkInfoFull() {
  61. this.$refs.qrImg.onload = () => qrLoaded = true
  62. this.$refs.img.onload = () => imgLoaded = true
  63. if(this.userinfo.uid) userinfoLoaded = true
  64. if(qrLoaded && imgLoaded && userinfoLoaded) {
  65. this.fillingCanvas()
  66. clearInterval(interval)
  67. }
  68. },
  69. initCanvas() {
  70. canvas = this.$refs.canvas
  71. ctx = canvas.getContext('2d')
  72. img = this.$refs.img
  73. },
  74. fillingCanvas() {
  75. let detail = this.detail
  76. , userinfo = this.userinfo
  77. , canvasWidth = 850
  78. , textWidth = 0
  79. , text = ''
  80. // 背景
  81. ctx.drawImage(img, 0, 0, 850, 520)
  82. // 真实姓名
  83. let realname = detail.realname
  84. ctx.font = '43px 微软雅黑'
  85. textWidth = ctx.measureText(realname).width
  86. ctx.fillText(realname, canvasWidth / 2 - textWidth / 2, 280)
  87. // 用户信息
  88. let id = userinfo.uid
  89. , nickname = userinfo.nickname
  90. ctx.font = '14px 微软雅黑'
  91. ctx.fillStyle = '#666'
  92. text = `ID ${id} | 昵称 ${nickname}`
  93. textWidth = ctx.measureText(text).width
  94. ctx.fillText(text, canvasWidth / 2 - textWidth / 2, 320)
  95. // 内容
  96. let content = detail.content
  97. ctx.font = '16px 微软雅黑'
  98. ctx.fillStyle = '#000'
  99. textWidth = 660
  100. let lineWidth = 0
  101. , fullWidth = 0
  102. for(let i = 0; i < content.length; i++) {
  103. lineWidth += ctx.measureText(content[i]).width
  104. if(lineWidth < textWidth) {
  105. ctx.fillText(content[i], canvasWidth / 2 - textWidth / 2 + lineWidth, 350)
  106. fullWidth = lineWidth
  107. } else {
  108. ctx.fillText(content[i], canvasWidth / 2 + lineWidth - fullWidth - 76, 370)
  109. }
  110. }
  111. // 二维码
  112. let qr = detail.qr
  113. ctx.drawImage(this.$refs.qrImg, 70, 380, 76, 76)
  114. // 证书编号
  115. let certNO = detail.cert_no
  116. ctx.font = '12px 微软雅黑'
  117. ctx.fillStyle = '#666'
  118. ctx.fillText(`证书编号 ${certNO}`, 160, 410)
  119. // 起始时间
  120. let startDate = detail.start_date
  121. , endDate = detail.end_date
  122. , statusName = detail.cert_status_name
  123. text = `有效时间 ${startDate} - ${endDate} ${statusName}`
  124. ctx.font = '12px 微软雅黑'
  125. ctx.fillText(text, 160, 430)
  126. // 状态框
  127. let boxWidth = ctx.measureText(statusName).width + 10
  128. ctx.rect(ctx.measureText(text).width - boxWidth + 165, 414, boxWidth, 22)
  129. ctx.stroke()
  130. }
  131. }
  132. }
  133. </script>
  134. <style scoped>
  135. .cert-id-box {
  136. display: flex;
  137. flex-direction: column;
  138. align-items: center;
  139. width: 1000px;
  140. padding-bottom: 30px;
  141. margin-top: 20px;
  142. background: white;
  143. }
  144. h1 {
  145. font-size: 26px;
  146. font-family: PingFangSC-Medium;
  147. font-weight: 500;
  148. color: #1d2a3a;
  149. margin: 28px 0 24px;
  150. }
  151. .hr {
  152. width: 960px;
  153. height: 1px;
  154. background: #eee;
  155. margin-bottom: 12px;
  156. }
  157. .img {
  158. width: 850px;
  159. height: 520px;
  160. display: none;
  161. }
  162. a {
  163. line-height: 28px;
  164. margin-left: 12px;
  165. text-decoration: underline;
  166. font-size: 14px;
  167. font-family: PingFangSC-Regular;
  168. color: #666;
  169. }
  170. </style>