scanCodePage.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <!-- H5 扫码页面 -->
  2. <template>
  3. <view class="scanCodePage">
  4. <video id="video-canvas" autoplay :controls="false"></video>
  5. <!-- 扫码遮罩层(中间镂空) -->
  6. <view class="scan-overlay">
  7. <!-- 扫码框 -->
  8. <view class="scan-frame">
  9. <!-- 四角标记线 -->
  10. <view class="corner top-left"></view>
  11. <view class="corner top-right"></view>
  12. <view class="corner bottom-left"></view>
  13. <view class="corner bottom-right"></view>
  14. <!-- 扫描动画线 -->
  15. <view class="scan-line"></view>
  16. </view>
  17. <!-- 提示文字 -->
  18. <text class="scan-tip">将二维码放入框内,即可自动扫描</text>
  19. </view>
  20. </view>
  21. </template>
  22. <script>
  23. import {
  24. BrowserMultiFormatReader
  25. } from '@zxing/library';
  26. export default {
  27. data() {
  28. return {
  29. codeReader: null,
  30. deviceId: null,
  31. }
  32. },
  33. mounted() {
  34. this.initScanner();
  35. },
  36. methods: {
  37. async initScanner() {
  38. let self = this;
  39. const videoDom = document.getElementById('video-canvas').getElementsByTagName('video')[0];
  40. videoDom.id = 'zxing-video';
  41. this.codeReader = new BrowserMultiFormatReader();
  42. try {
  43. const devices = await this.codeReader.listVideoInputDevices();
  44. this.deviceId = devices[devices.length - 1].deviceId;
  45. this.startScanning();
  46. } catch (err) {
  47. uni.showToast({
  48. mask: true,
  49. icon: "none",
  50. position: "center",
  51. title: "请启用摄像头权限",
  52. duration: 2000
  53. });
  54. setTimeout(() => {
  55. uni.navigateBack();
  56. }, 2000);
  57. }
  58. },
  59. startScanning() {
  60. let self = this;
  61. this.codeReader.decodeFromVideoDevice(
  62. this.deviceId,
  63. 'zxing-video',
  64. (result, err) => {
  65. if (result) {
  66. self.codeReader.reset();
  67. self.inspectCode(result.text);
  68. }
  69. }
  70. );
  71. },
  72. inspectCode(result) {
  73. if (result.indexOf('mid') != -1 &&
  74. result.indexOf('sid') != -1 &&
  75. result.indexOf('pid') != -1 &&
  76. result.indexOf('pri') != -1) {
  77. this.getCommodityInfo(result);
  78. } else {
  79. uni.showToast({
  80. mask: true,
  81. icon: "none",
  82. position: "center",
  83. title: "请扫描正确的二维码",
  84. duration: 2000
  85. });
  86. setTimeout(() => {
  87. uni.navigateBack();
  88. }, 2000);
  89. }
  90. },
  91. getCommodityInfo(result) {
  92. uni.navigateTo({
  93. url: '/pages/commodityInfo?q=' +
  94. encodeURIComponent(JSON.stringify(result))
  95. });
  96. },
  97. },
  98. beforeDestroy() {
  99. this.codeReader?.reset();
  100. }
  101. }
  102. </script>
  103. <style lang="stylus" scoped>
  104. .scanCodePage {
  105. flex: 1;
  106. display: flex;
  107. flex-direction: column;
  108. overflow: hidden;
  109. ::v-deep #video-canvas {
  110. height: 100%;
  111. width: 100%;
  112. .uni-video-cover {
  113. display: none;
  114. }
  115. }
  116. .scan-overlay {
  117. position: absolute;
  118. top: 0;
  119. left: 0;
  120. width: 100%;
  121. height: 100%;
  122. z-index: 2;
  123. display: flex;
  124. flex-direction: column;
  125. align-items: center;
  126. justify-content: center;
  127. background: rgba(0, 0, 0, 0.6);
  128. clip-path: polygon(
  129. 0% 0%, 100% 0%, 100% 100%, 0% 100%,
  130. 0% calc(50% - 240rpx),
  131. calc(50% - 240rpx) calc(50% - 240rpx),
  132. calc(50% - 240rpx) calc(50% + 240rpx),
  133. calc(50% + 240rpx) calc(50% + 240rpx),
  134. calc(50% + 240rpx) calc(50% - 240rpx),
  135. 0% calc(50% - 240rpx)
  136. );
  137. }
  138. .scan-frame {
  139. position: absolute;
  140. width: 480rpx;
  141. height: 480rpx;
  142. border: 2rpx solid rgba(255, 255, 255, 0.3);
  143. box-sizing: border-box;
  144. }
  145. .corner {
  146. position: absolute;
  147. width: 36rpx;
  148. height: 36rpx;
  149. border-color: #0183FA;
  150. border-style: solid;
  151. border-width: 0;
  152. }
  153. .top-left {
  154. top: -2rpx;
  155. left: -2rpx;
  156. border-top-width: 6rpx;
  157. border-left-width: 6rpx;
  158. }
  159. .top-right {
  160. top: -2rpx;
  161. right: -2rpx;
  162. border-top-width: 6rpx;
  163. border-right-width: 6rpx;
  164. }
  165. .bottom-left {
  166. bottom: -2rpx;
  167. left: -2rpx;
  168. border-bottom-width: 6rpx;
  169. border-left-width: 6rpx;
  170. }
  171. .bottom-right {
  172. bottom: -2rpx;
  173. right: -2rpx;
  174. border-bottom-width: 6rpx;
  175. border-right-width: 6rpx;
  176. }
  177. .scan-line {
  178. position: absolute;
  179. top: 0;
  180. left: 20rpx;
  181. right: 20rpx;
  182. height: 3rpx;
  183. background: linear-gradient(90deg, transparent, #0183FA, transparent);
  184. animation: scan-move 2s linear infinite;
  185. }
  186. @keyframes scan-move {
  187. 0% {
  188. top: 0;
  189. opacity: 0;
  190. }
  191. 10% {
  192. opacity: 1;
  193. }
  194. 90% {
  195. opacity: 1;
  196. }
  197. 100% {
  198. top: 100%;
  199. opacity: 0;
  200. }
  201. }
  202. .scan-tip {
  203. position: absolute;
  204. bottom: calc(50% - 300rpx);
  205. color: #fff;
  206. font-size: 28rpx;
  207. text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.5);
  208. }
  209. }
  210. </style>