scan-frame.vue 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  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" @initdone="cameraInit" 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="noAuth" v-if="showAuth" @click="openAuth">
  9. 您未开启摄像机权限,<cover-view class="btns">请点击开启权限</cover-view>
  10. </cover-view>
  11. <cover-image class="tips" v-if="userInfo.identifyType=='VISITOR'">个人用户限<cover-view class="text">10次/天</cover-view></cover-image>
  12. <cover-view class="scanBtn" v-if="scanShow">
  13. <cover-view class="beat" @click="scan">
  14. <cover-image class="beatImg" src="@/static/scan-frame/album.png"></cover-image>
  15. <cover-view> 相册 </cover-view>
  16. </cover-view>
  17. <cover-view class="album" @click="takePhoto">
  18. <cover-image class="albumImg" src="@/static/scan-frame/beat.png"></cover-image>
  19. <cover-view> 拍照 </cover-view>
  20. </cover-view>
  21. <cover-view class="inputs" @click="inputs">
  22. <cover-image class="beatImg" src="@/static/scan-frame/inputs.png"></cover-image>
  23. <cover-view> 手动输入 </cover-view>
  24. </cover-view>
  25. </cover-view>
  26. </camera>
  27. </view>
  28. </template>
  29. <script>
  30. import {
  31. mapState,
  32. mapMutations,
  33. } from 'vuex'
  34. import { vinCode } from '@/api/car.js'
  35. export default {
  36. data() {
  37. return {
  38. scanShow: true, // 显示操作按钮
  39. sysinfo: null, // 设备信息
  40. imageInfo: null ,// 拍照图片信息
  41. showAuth: false
  42. }
  43. },
  44. onReady() {
  45. const sysinfo = uni.getSystemInfoSync()
  46. this.sysinfo = sysinfo
  47. },
  48. onShow() {
  49. const _this = this
  50. uni.getSetting({
  51. success(res) {
  52. _this.showAuth = !res.authSetting['scope.camera']
  53. }
  54. })
  55. },
  56. computed: {
  57. ...mapState(['hasLogin','vuex_vinScanNums']),
  58. userInfo(){
  59. return this.$store.state.vuex_userInfo
  60. }
  61. },
  62. methods: {
  63. cameraInit(e){
  64. this.showAuth = false
  65. },
  66. openAuth(){
  67. uni.openSetting({
  68. success(res) {
  69. if(res.authSetting['scope.camera']){
  70. uni.navigateBack()
  71. }
  72. }
  73. })
  74. },
  75. // 手动输入
  76. inputs(){
  77. uni.redirectTo({
  78. url: "/pages/vinInput/confirmVin?verifyCode=" + "&type=input"
  79. })
  80. },
  81. // 相册
  82. scan() {
  83. // 选择图片
  84. uni.chooseImage({
  85. count: 1,
  86. sizeType: ['original', 'compressed'],
  87. sourceType: ['album'],
  88. success: (res) => {
  89. this.compress(res.tempFilePaths[0],false)
  90. }
  91. })
  92. },
  93. delImgs(filePath){
  94. // #ifdef MP-WEIXIN
  95. const fs = wx.getFileSystemManager();
  96. try {
  97. const res = fs.unlinkSync(filePath)
  98. console.log(res)
  99. } catch(e) {
  100. console.error(e)
  101. }
  102. // #endif
  103. },
  104. // 裁剪图片
  105. cutImg(fileUrl){
  106. const vm = this
  107. const ctx = uni.createCanvasContext('canvas')
  108. // 将拍照的图片绘制到canvas,宽高等于窗口宽高和手机窗口保持一致
  109. ctx.drawImage(fileUrl,0,0,this.sysinfo.windowWidth,this.sysinfo.windowHeight)
  110. ctx.draw(true,()=>{
  111. const cutW = 650 // 裁剪框宽度
  112. const cutH = 160 // 裁剪框高度
  113. const cutT = 400 // 裁剪框距离顶部距离
  114. const cutL = 50 // 裁剪框距离左侧距离
  115. // 以上裁剪框宽高及坐标是相对于实际蒙板图片的位置
  116. const canvasW=cutW/750*this.sysinfo.windowWidth; // canvas 画布实际宽度
  117. const canvasH=cutH/1624*this.sysinfo.windowHeight; // canvas 画布实际高度
  118. const canvasL =cutL/750*this.sysinfo.windowWidth; // canvas 画布开始坐标x
  119. const canvasT= cutT/1624*this.sysinfo.windowHeight; // canvas 画布开始坐标y
  120. // 开始裁剪
  121. uni.canvasToTempFilePath({
  122. x: canvasL,
  123. y: canvasT,
  124. width: canvasW,
  125. height: canvasH,
  126. canvasId: 'canvas',
  127. success: function(res) {
  128. // 在H5平台下,tempFilePath 为 base64
  129. vm.parseImgs(res.tempFilePath)
  130. // 删除临时文件
  131. // vm.delImgs(fileUrl)
  132. }
  133. })
  134. })
  135. },
  136. // 启动图片压缩
  137. compress(tempFilePaths,isCut) {
  138. const vm = this
  139. vm.scanShow = false
  140. uni.showLoading({
  141. title: '智能识别中...'
  142. })
  143. uni.compressImage({
  144. src: tempFilePaths,
  145. quality: 100,
  146. success: (imageRes) => {
  147. // 获取类型
  148. uni.getImageInfo({
  149. src: imageRes.tempFilePath,
  150. success(imageInfo) {
  151. console.log(imageInfo)
  152. vm.imageInfo = imageInfo
  153. if(isCut){
  154. // 裁剪图片
  155. vm.cutImg(imageRes.tempFilePath)
  156. }else{
  157. // 相册选择的图片直接解析,这里如果需要裁剪可以跳转到裁剪图片页面在继续下一步
  158. vm.parseImgs(imageRes.tempFilePath)
  159. }
  160. }
  161. })
  162. }
  163. })
  164. },
  165. // 拿到图片转base64后在开始进行识别
  166. parseImgs(filePath) {
  167. // 图片转base64格式
  168. uni.getFileSystemManager().readFile({
  169. filePath: filePath,
  170. encoding: 'base64',
  171. success: (base) => {
  172. // 拿到base64,不需要base64 就把上层的转换去掉
  173. this.scanShow = true
  174. // 此处为后端接口 传base64图片 进行ocr识别
  175. if(this.userInfo.identifyType=='VISITOR'){
  176. if(this.vuex_vinScanNums < 10){
  177. this.parseVinNo(base.data)
  178. }else{
  179. uni.showModal({
  180. title: '提示',
  181. content: '个人用户扫描VIN限10次/天,您的次数已用完!',
  182. confirmText: '去申请认证',
  183. success(res) {
  184. if(res.confirm) {
  185. uni.redirectTo({
  186. url: '/pages/storeManage/storeAuth'
  187. })
  188. }
  189. }
  190. })
  191. }
  192. }else{
  193. this.parseVinNo(base.data)
  194. }
  195. },
  196. fail: (err) => {
  197. uni.hideLoading()
  198. console.log(err)
  199. }
  200. })
  201. },
  202. // 点击开始拍照
  203. takePhoto() {
  204. // 获取相机上下文对象
  205. const ctx = uni.createCameraContext()
  206. ctx.takePhoto({
  207. quality: 'high',
  208. success: (res) => {
  209. this.compress(res.tempImagePath,true)
  210. }
  211. })
  212. },
  213. // 识别vin
  214. parseVinNo(data) {
  215. const _this = this
  216. const base64Str = 'data:image/' + this.imageInfo.type + ';base64,' + data
  217. vinCode({
  218. imageByte: data
  219. }).then(res => {
  220. console.log(res, '识别VIN')
  221. if(res.status == 200){
  222. uni.redirectTo({
  223. url: "/pages/vinInput/confirmVin?verifyCode=123456ads11234567&filePath="+base64Str + "&verifyCode="+res.data + "&type=carmen"
  224. })
  225. }else{
  226. uni.showToast({
  227. icon: 'none',
  228. title: res.message
  229. })
  230. }
  231. uni.hideLoading()
  232. }).catch(err => {
  233. uni.hideLoading()
  234. uni.showToast({
  235. icon: 'none',
  236. title: '系统错误或超时,请重试'
  237. })
  238. })
  239. },
  240. error(e) {
  241. console.log(e.detail)
  242. }
  243. }
  244. }
  245. </script>
  246. <style lang="scss" scoped>
  247. .cameraBg {
  248. width: 100%;
  249. height: 100vh;
  250. position: fixed;
  251. .cover-img {
  252. width: 100%;
  253. height: 100vh;
  254. z-index: 1;
  255. }
  256. .noAuth,.tips{
  257. width: 100%;
  258. z-index: 999998;
  259. position: fixed;
  260. bottom: 55vh;
  261. text-align: center;
  262. color: #999;
  263. .btns{
  264. color: dodgerblue;
  265. }
  266. .text{
  267. color:#ffff00;
  268. }
  269. }
  270. .tips{
  271. bottom: 90vh;
  272. }
  273. .scanBtn {
  274. width: 100%;
  275. z-index: 99999;
  276. position: fixed;
  277. bottom: 100rpx;
  278. display: flex;
  279. flex-direction: column;
  280. justify-content: center;
  281. align-items: center;
  282. .beat {
  283. position: absolute;
  284. bottom: 0rpx;
  285. left: 100rpx;
  286. display: flex;
  287. flex-direction: column;
  288. justify-content: center;
  289. align-items: center;
  290. font-size: 24rpx;
  291. font-weight: 400;
  292. color: #ffffff;
  293. .beatImg {
  294. width: 88rpx;
  295. height: 88rpx;
  296. margin-bottom: 30rpx;
  297. }
  298. }
  299. .inputs{
  300. position: absolute;
  301. bottom: 0rpx;
  302. right: 100rpx;
  303. display: flex;
  304. flex-direction: column;
  305. justify-content: center;
  306. align-items: center;
  307. font-size: 24rpx;
  308. font-weight: 400;
  309. color: #ffffff;
  310. .beatImg {
  311. width: 88rpx;
  312. height: 88rpx;
  313. margin-bottom: 30rpx;
  314. }
  315. }
  316. .album {
  317. display: flex;
  318. flex-direction: column;
  319. justify-content: center;
  320. align-items: center;
  321. font-size: 24rpx;
  322. font-weight: 400;
  323. color: #ffffff;
  324. .albumImg {
  325. width: 120rpx;
  326. height: 120rpx;
  327. margin-bottom: 30rpx;
  328. }
  329. }
  330. }
  331. }
  332. </style>