scan-frame.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <template>
  2. <view class="cameraBg">
  3. <!-- position: absolute;left: 100000px; 隐藏canvas画布 -->
  4. <canvas style="width: 100%; height: 100vh;position: absolute;left: 100000px;" canvas-id="canvas" id="canvas"></canvas>
  5. <!-- 相机组件 -->
  6. <camera device-position="back" flash="auto" style="width: 100%; height: 100vh">
  7. <cover-image src="@/static/scan-frame/cameraBg.png" class="cover-img"> </cover-image>
  8. <cover-view class="scanBtn" v-if="scanShow">
  9. <cover-view class="beat" @click="scan">
  10. <cover-image class="beatImg" src="@/static/scan-frame/album.png"></cover-image>
  11. <cover-view> 相册 </cover-view>
  12. </cover-view>
  13. <cover-view class="album" @click="takePhoto">
  14. <cover-image class="albumImg" src="@/static/scan-frame/beat.png"></cover-image>
  15. <cover-view> 拍照 </cover-view>
  16. </cover-view>
  17. <cover-view class="inputs" @click="inputs">
  18. <cover-image class="beatImg" src="@/static/scan-frame/inputs.png"></cover-image>
  19. <cover-view> 手动输入 </cover-view>
  20. </cover-view>
  21. </cover-view>
  22. </camera>
  23. </view>
  24. </template>
  25. <script>
  26. import { vinCode } from '@/api/car.js'
  27. export default {
  28. data() {
  29. return {
  30. scanShow: true, // 显示操作按钮
  31. sysinfo: null, // 设备信息
  32. imageInfo: null // 拍照图片信息
  33. }
  34. },
  35. onReady() {
  36. const sysinfo = uni.getSystemInfoSync()
  37. this.sysinfo = sysinfo
  38. },
  39. methods: {
  40. // 手动输入
  41. inputs(){
  42. uni.redirectTo({
  43. url:"/pages/vinInput/vinInput"
  44. })
  45. },
  46. // 相册
  47. scan() {
  48. // 选择图片
  49. uni.chooseImage({
  50. count: 1,
  51. sizeType: ['original', 'compressed'],
  52. sourceType: ['album'],
  53. success: (res) => {
  54. this.compress(res.tempFilePaths[0],false)
  55. }
  56. })
  57. },
  58. // 裁剪图片
  59. cutImg(fileUrl){
  60. const vm = this
  61. const ctx = uni.createCanvasContext('canvas')
  62. // 将拍照的图片绘制到canvas,宽高等于窗口宽高和手机窗口保持一致
  63. ctx.drawImage(fileUrl,0,0,this.sysinfo.windowWidth,this.sysinfo.windowHeight)
  64. ctx.draw(true,()=>{
  65. const cutW = 650 // 裁剪框宽度
  66. const cutH = 160 // 裁剪框高度
  67. const cutT = 400 // 裁剪框距离顶部距离
  68. const cutL = 50 // 裁剪框距离左侧距离
  69. // 以上裁剪框宽高及坐标是相对于实际蒙板图片的位置
  70. const canvasW=cutW/750*this.sysinfo.windowWidth; // canvas 画布实际宽度
  71. const canvasH=cutH/1624*this.sysinfo.windowHeight; // canvas 画布实际高度
  72. const canvasL =cutL/750*this.sysinfo.windowWidth; // canvas 画布开始坐标x
  73. const canvasT= cutT/1624*this.sysinfo.windowHeight; // canvas 画布开始坐标y
  74. // 开始裁剪
  75. uni.canvasToTempFilePath({
  76. x: canvasL,
  77. y: canvasT,
  78. width: canvasW,
  79. height: canvasH,
  80. canvasId: 'canvas',
  81. success: function(res) {
  82. // 在H5平台下,tempFilePath 为 base64
  83. vm.parseImgs(res.tempFilePath)
  84. }
  85. })
  86. })
  87. },
  88. // 启动图片压缩
  89. compress(tempFilePaths,isCut) {
  90. const vm = this
  91. vm.scanShow = false
  92. uni.showLoading({
  93. title: '智能识别中...'
  94. })
  95. uni.compressImage({
  96. src: tempFilePaths,
  97. quality: 100,
  98. success: (imageRes) => {
  99. // 获取类型
  100. uni.getImageInfo({
  101. src: imageRes.tempFilePath,
  102. success(imageInfo) {
  103. console.log(imageInfo)
  104. vm.imageInfo = imageInfo
  105. if(isCut){
  106. // 裁剪图片
  107. vm.cutImg(imageRes.tempFilePath)
  108. }else{
  109. // 相册选择的图片直接解析,这里如果需要裁剪可以跳转到裁剪图片页面在继续下一步
  110. vm.parseImgs(imageRes.tempFilePath)
  111. }
  112. }
  113. })
  114. }
  115. })
  116. },
  117. // 拿到图片转base64后在开始进行识别
  118. parseImgs(filePath) {
  119. // 图片转base64格式
  120. uni.getFileSystemManager().readFile({
  121. filePath: filePath,
  122. encoding: 'base64',
  123. success: (base) => {
  124. const base64Str = 'data:image/' + this.imageInfo.type + ';base64,' + base.data
  125. // 拿到base64,不需要base64 就把上层的转换去掉
  126. this.scanShow = true
  127. // 此处为后端接口 传base64图片 进行ocr识别
  128. this.parseVinNo(base64Str)
  129. },
  130. fail: (err) => {
  131. uni.hideLoading()
  132. console.log(err)
  133. }
  134. })
  135. },
  136. // 点击开始拍照
  137. takePhoto() {
  138. // 获取相机上下文对象
  139. const ctx = uni.createCameraContext()
  140. ctx.takePhoto({
  141. quality: 'high',
  142. success: (res) => {
  143. this.compress(res.tempImagePath,true)
  144. }
  145. })
  146. },
  147. // 识别vin
  148. parseVinNo(data) {
  149. const _this = this
  150. vinCode({
  151. imageByte: data
  152. }).then(res => {
  153. console.log(res, '识别VIN')
  154. uni.hideLoading()
  155. uni.redirectTo({
  156. url: "/pages/vinInput/confirmVin?verifyCode=123456ads11234567&filePath="+base64Str + "&verifyCode="+res.data
  157. })
  158. }).catch(err => {
  159. uni.hideLoading()
  160. uni.showToast({
  161. icon: 'none',
  162. title: '系统错误或超时,请重试'
  163. })
  164. })
  165. },
  166. error(e) {
  167. console.log(e.detail)
  168. }
  169. }
  170. }
  171. </script>
  172. <style lang="scss" scoped>
  173. .cameraBg {
  174. width: 100%;
  175. height: 100vh;
  176. position: fixed;
  177. .cover-img {
  178. width: 100%;
  179. height: 100vh;
  180. z-index: 1;
  181. }
  182. .scanBtn {
  183. width: 100%;
  184. z-index: 99999;
  185. position: fixed;
  186. bottom: 100rpx;
  187. display: flex;
  188. flex-direction: column;
  189. justify-content: center;
  190. align-items: center;
  191. .beat {
  192. position: absolute;
  193. bottom: 0rpx;
  194. left: 100rpx;
  195. display: flex;
  196. flex-direction: column;
  197. justify-content: center;
  198. align-items: center;
  199. font-size: 24rpx;
  200. font-weight: 400;
  201. color: #ffffff;
  202. .beatImg {
  203. width: 88rpx;
  204. height: 88rpx;
  205. margin-bottom: 30rpx;
  206. }
  207. }
  208. .inputs{
  209. position: absolute;
  210. bottom: 0rpx;
  211. right: 100rpx;
  212. display: flex;
  213. flex-direction: column;
  214. justify-content: center;
  215. align-items: center;
  216. font-size: 24rpx;
  217. font-weight: 400;
  218. color: #ffffff;
  219. .beatImg {
  220. width: 88rpx;
  221. height: 88rpx;
  222. margin-bottom: 30rpx;
  223. }
  224. }
  225. .album {
  226. display: flex;
  227. flex-direction: column;
  228. justify-content: center;
  229. align-items: center;
  230. font-size: 24rpx;
  231. font-weight: 400;
  232. color: #ffffff;
  233. .albumImg {
  234. width: 120rpx;
  235. height: 120rpx;
  236. margin-bottom: 30rpx;
  237. }
  238. }
  239. }
  240. }
  241. </style>