uniapp:身份证上传ocr识别

一、主体流程

  1. 点击icon
  2. 跳转到拍照页面(路由:/packageCommon/pages/camera/index),此页面使用<camera><camera/>区域相机组件
  3. 点击拍照按钮,进行拍照,使用const ctx = uni.createCameraContext()创建并返回 camera 组件的上下文 cameraContext 对象;调用ctx.takePhoto方法,它的success回调中可以获取到临时路径。
  • 3.1 uniapp中当前页面如何调用上一个页面的方法?----可以使用getCurrentPages()方法获取到pages,再拿到prevPage:const prevPage = pages.length > 1 ? pages[pages.length - 2] : nullprevPage.$vm.xxx()就可以调用上一个页面的方法了
  1. 临时路径作为uni.uploadFilefilePath,上传至文件服务器,这里文件服务器的url是/common/ocr
  2. 文件服务器(ocr)返回真实的图片地址,并且返回身份证上的信息,用来回显页面

二、相关代码

js 复制代码
    <u-popup
      v-model="popupShow"
      closeable
      safe-area-inset-bottom
      border-radius="14"
      mode="center"
    >
      <view>
        <view class="pop-title">担保征信查询</view>
        <scroll-view
          scroll-y="true"
          style="padding: 0 40rpx; box-sizing: border-box"
        >
          <view style="max-height: 800rpx">
            <u-form-item
              label-width="200"
              label-position='top'
              label="身份证人像面"
              required
              :border-bottom="false"
            >
              <view class="idCrad_card">
                <view class="idCrad_card__item" @click="cameraFront('front')">
                  <image :src="idCardFront.image" mode="aspectFit" class="img1">
                  </image>
                  <image :src="idCardFront.image2" class="img2">
                  </image>
                </view>
              </view>
            </u-form-item>
            <u-form-item
              v-show="form.idCard"
              label-width="200"
              label="姓名"
              required
              :border-bottom="false"
            >
              <view style="display: flex; align-items: center;">
                <u-input
                  v-model="form.name"
                  placeholder="请输入姓名"
                  :border="true"
                  maxlength="10"
                />
                <image style="width: 20px; height: 20px; margin-left: 10px;" :src="imagesConfig.realName.ic_edit" mode="aspectFit" />
              </view>
            </u-form-item>
            <u-form-item
              v-show="form.idCard"
              label-width="200"
              label="身份证号"
              required
              :border-bottom="false"
            >
              <view>{{form.idCard}}</view>
            </u-form-item>
          </view>
        </scroll-view>
        <view class="pop-footer" style="justify-content: center">
          <app-button
            borderRadius="3rpx"
            width="160rpx"
            height="60rpx"
            type="info"
            @click="handleAdd"
            text="提交"
          ></app-button>
        </view>
      </view>
    </u-popup>
    
    
    
    data:
    
        // 新增征信查询表单数据
        form: {
            idCardAUrl: '',
            idCardBUrl: '',
            address: '',
            nation: '',
            signOrg: '',
            startDate: '',
            endDate: '',
            name: '',
            idCard: '',
	},
        
        
		computed: {
                        // 身份证正面展示图片
			idCardFront() {
				return {
					image: this.form.idCardAUrl ? this.form.idCardAUrl : imagesConfig.realName.ic_idcard_a,
					image2: imagesConfig.realName.ic_take_phone,
				}
			},
			// 身份证反面展示图片
			idCardBack() {
				return {
					image: this.form.idCardBUrl ? this.form.idCardBUrl : imagesConfig.realName.ic_idcard_b,
					image2: imagesConfig.realName.ic_take_phone,
				}
			},
		},
                
                
                
                
                
                
    methods:
    
// 身份证上传-开始
      // 打开摄像头
			cameraFront(ocrType) {
        // front-人像面   back-国徽面
				uni.showLoading({
					title: '努力加载中',
					mask: true
				});
				this.$utils.canOpenCamera().then(() => {
					uni.hideLoading()
					const data = {
						uploadCallBackHandler: 'onUploadHandle',
						isIdCardOcr: 1,
						ocrType,
						ref: "photoWay",
						title: "拍照识别",
						callBackParams: {
							type: ocrType == 'front' ? '0' : '1'
						}
					}
					uni.navigateTo({
						url: `/packageCommon/pages/camera/index?data=${encodeURIComponent(JSON.stringify(data))}`
					})
				}).catch(() => {
					uni.hideLoading()
				})
			},
			// 身份证识别
			onUploadHandle(filePath, data) {
        console.log('onUploadHandle被调用',filePath, data)
				this.curOcr = data.type
				let type = data.type
				if (type == 1) { // 国徽面
					this.frontStartTime = this.$utils.dateToStr(new Date())
				} else {
					this.backStartTime = this.$utils.dateToStr(new Date())
				}
				uni.showLoading({
					title: '努力加载中',
					mask: true
				});
				this.uniUploadFile({
					url: "/common/ocr",
					// url: "/kfbjd/common/ocr.do",
					filePath,
					name: 'fileUpload',
					formData: {
						fileType: type,
						wxId: this.unionId,
						token: this.userInfo && this.userInfo.token,
						bankCode: this.bankCode,
						openId: this.openId,
						memberType:  '0',
						needEncrypt: '1',
						phone:  this.userInfo.phone,
					},
					success: (uploadFileRes) => {
                                            console.log('ocr上传返回结果',uploadFileRes)
                                                                                uni.hideLoading()
                                            if(uploadFileRes.flag){
                                              this.handleUpload(uploadFileRes)
                                            }else{
                                              uni.showModal({
								title: '提示',
								content: uploadFileRes.errMsg || '服务器异常,请稍后重试。',
								showCancel: false,
								confirmText: '我知道了'
							})
                                    }
					},
					fail: (err) => {
						uni.hideLoading();
					}
				})
			},
      /**
       * 上传文件到服务器(包含加解密功能)
       * @param {Object} options 配置项:url 接口请求地址  filePath待上传的图片本地路径 name文件流名称 formData额外参数 success成功回调 fail失败回调
       */
      uniUploadFile(options) {
        let needEncrypt;
        let url = ""
        if (options.url.indexOf('kfbjd') != -1) {
          needEncrypt = options.formData.needEncrypt
          // url = config.baseUrl + options.url
          url = 'https://www.hfkxjf.com/lydev/fins-appsply-sso' + options.url
        } else {
          needEncrypt = config.needEncrypt
          // url = config.baseUrl + options.url
          url = 'https://www.hfkxjf.com/lydev/fins-appsply-sso' + options.url
        }
        // 加密
        if (needEncrypt == '1') {
          const req = aes.encrypt(JSON.stringify(options.formData))
          if (options.url.indexOf('kfbjd') != -1) {
            options.formData = {
              data: req
            }
          } else {
            options.formData = {
              data: queryString.stringify({
                data: req
              })
            }
          }
        }
        console.log("文件上传参数", options.formData)
        uni.uploadFile({
          url: url,
          filePath: options.filePath,
          name: options.name,
          formData: options.formData,
          headers: {
            'Content-Type': 'multipart/form-data'
          },
          success: (res) => {
            console.log(res)
            let result = null
            if (needEncrypt == '1') {
              result = JSON.parse(aes.decrypt(res.data) || '{}')
            } else {
              result = this.$utils.isJSON(res.data) ? JSON.parse(res.data) : {}
            }
            console.log("文件上传结果:", result)
            options.success && options.success(result)
          },
          fail: (err) => {
            console.log("文件上传失败:", err)
            options.fail && options.fail(err)
          }
        })
      },
      
      // ocr获取身份证信息
			handleUpload(uploadFileRes) {
					let result = uploadFileRes.data || {}
					if (result && result.idCard && result.username) {
						wx.getImageInfo({
							src: result.imagePath,
							success: (image) => {
								console.log("上传后的图片方向", image.orientation)
							}
						})
						// 人像面
						this.form.idCard = result.idCard
						this.form.idCardAUrl = result.imagePath
						this.form.nation = result.nation
						this.form.name = result.username
						this.form.address = result.address
					} else if (result && result.startDate && result.endDate) {
						// 国徽面
						this.form.startDate = result.startDate
						this.form.endDate = result.endDate
						this.form.signOrg = result.signOrg
						this.form.idCardBUrl = result.imagePath
						let start = this.form.startDate ? this.formatD(this.form.startDate) : ''
						let end = this.form.endDate ? this.formatD(this.form.endDate) : ''
						this.period = start && end ? start + '-' + end : ''
					}
			},
			// 身份证起止日期过滤
			formatD(day) {
				let isNum = /^\d+$/.test(day)
				if (isNum) {
					return day.slice(0, 4) + '.' + day.slice(4, 6) + '.' + day.slice(6)
				} else {
					return day
				}
			},
      // 身份证上传-结束

utils/index.js

js 复制代码
	/**
	 * 判断是否有打开摄像头的权限,没有的话会提示用户打开摄像头
	 */
	canOpenCamera: () => {
		return new Promise((resolve, reject) => {
			wx.getSetting({
				success: (res) => {
          console.log(res, 'canOpenCamera---res');
					let flag = res.authSetting['scope.camera']
					if (flag === false) {
						// 相册
						uni.showModal({
							content: "请打开小程序摄像头功能!",
							cancelText: "取消",
							confirmText: "去设置",
							success: res => {
								if (res.confirm) {
									wx.openSetting()
								} else if (res.cancel) {}
							}
						})
					} else {
            console.log('canOpenCamera---else');
						resolve()
					}
				}
			})
		})
	},

camera/index.vue:

js 复制代码
<!-- 人脸识别:拍照 -->
<template>
  <view class="bg ocr">
    <!-- 相机拍照 -->
    <view v-if="isCamera" style="display: flex;
    align-items: center;
    justify-content: center;">
      <camera :device-position="devicePosition" flash="off" @error="error" class="camera"></camera>
      <view v-if="isIdCardOcr">
        <image class="icon icon--left-top" :src="iconMap.ic_right_top" mode="aspectFill"></image>
        <image class="icon icon--right-top" :src="iconMap.ic_left_top" mode="aspectFill"></image>
        <image class="icon icon--right-bottom" :src="iconMap.ic_right_bottom" mode="aspectFill"></image>
        <image class="icon icon--left-bottom" :src="iconMap.ic_left_bottom" mode="aspectFill"></image>
        <image v-if="ocrType=='front'" class="geren" :src="iconMap.ic_geren" mode="aspectFill"></image>
        <image v-if="ocrType=='back'" class="guohui" :src="iconMap.ic_guohui" mode="aspectFill"></image>
      </view>
      <view class="action">
        <view class="action__button" data-monitor-key="cancelPhoto" @click="backEvt">
          <!-- <van-icon name="close" color="#fff" size="44rpx" /> -->
          <u-icon name="close-circle" color="#fff" size="44rpx" />
          <view class="mt-1">取消拍摄</view>
        </view>
        <view class="action__camera" data-monitor-key="takePhoto" @click="takePhotoEvt">
          <image :src="iconMap.ic_photo" mode="aspectFill"></image>
        </view>
        <view>
        </view>
      </view>
    </view>
    <!-- 照片查看 -->
    <view v-else style="display: flex;
    align-items: center;
    justify-content: center;">
      <view class="img">
        <image :src="src" mode="aspectFit"></image>
      </view>
      <view class="action action--success">
        <view @click="rePhotoEvt" data-monitor-key="reTakePhoto">
          <!-- <van-icon name="close" color="#fff" size="70rpx" /> -->
          <u-icon name="close-circle" color="#fff" size="70rpx" />
        </view>
        <view @click="uploadEvt" data-monitor-key="confirmPhoto">
          <!-- <van-icon name="success" color="#fff" size="80rpx" /> -->
          <u-icon name="checkbox-mark" color="#fff" size="80rpx" />
        </view>
      </view>
    </view>
  </view>

</template>

<script>
  import imagesConfig from '@/config/images.config.js'
  export default {
    data() {
      return {
        iconMap: imagesConfig.mine,
        isCamera: true,
        devicePosition: 'back',
        isIdCardOcr: false, // 是否是上传身份证
        ocrType: "", // 身份证正面还是反面 front正面  back反面
        src: null,
        uploadCallBackHandler: "", // 回调函数
        callBackParams: null, // 回调函数入参
        ref: ""
      }
    },
    onLoad(query) {
      const data = JSON.parse(decodeURIComponent(query.data || "{}"))
      this.isIdCardOcr = data.isIdCardOcr == '1'
      this.ocrType = data.ocrType || ""
      this.uploadCallBackHandler = data.uploadCallBackHandler || ""
      this.callBackParams = data.callBackParams || {}
      this.ref = data.ref || ""
      uni.setNavigationBarTitle({
        title: data.title || ""
      });
    },
    methods: {
      //拍照
      takePhotoEvt() {
        const ctx = uni.createCameraContext();
        ctx.takePhoto({
          quality: 'high',
          success: (res) => {
            console.log(res)
            this.src = res.tempImagePath
            if (this.src != null) {
              this.isCamera = false
            }
          }
        });
      },
      //上传
      uploadEvt() {
        const pages = getCurrentPages()
        const prevPage = pages.length > 1 ? pages[pages.length - 2] : null
        console.log('pages',{pages,prevPage})
        uni.navigateBack({
          delta: 1,
          success: () => {
            console.log('触发onUploadHandle',this.src, this.callBackParams)
            prevPage.$vm.onUploadHandle(this.src, this.callBackParams)
            // if (this.uploadCallBackHandler) {
            //   if (this.ref) {
            //     prevPage.$vm.$refs[this.ref][this.uploadCallBackHandler](this.src, this.callBackParams)
            //   } else {
            //     prevPage.$vm[this.uploadCallBackHandler](this.src, this.callBackParams)
            //   }
            // }
          }
        });
      },
      //摄像头启动失败
      error(e) {
        console.log(e.detail);
      },
      //返回
      backEvt() {
        uni.navigateBack()
      },
      //重拍
      rePhotoEvt() {
        this.isCamera = true
      }
    }
  }
</script>

<style lang="less" scoped>
  .ocr {
    // .flex-display();
    padding-top: 30rpx;
    box-sizing: border-box;
    background-color: #000000FF;
  }

  .camera {
    position: relative;
    width: 690rpx;
    height: 75vh;
    box-sizing: border-box;
    border: 5rpx solid orange;
  }

  .icon {
    position: absolute;
    width: 65rpx;
    height: 65rpx;

    &--left-top {
      top: 78rpx;
      left: 67rpx;
    }

    &--right-top {
      top: 78rpx;
      right: 67rpx;
    }

    &--left-bottom {
      bottom: calc(25vh + 33rpx);
      right: 67rpx;
    }

    &--right-bottom {
      bottom: calc(25vh + 33rpx);
      left: 67rpx;
    }
  }

  .geren {
    position: absolute;
    width: 232rpx;
    height: 245rpx;
    right: 200rpx;
    bottom: calc(25vh + 140rpx);
  }

  .guohui {
    position: absolute;
    width: 233rpx;
    height: 221rpx;
    right: 170rpx;
    top: 190rpx;
  }

  .action {
    // .flex-display(@flexDirection: row, @justifyContent: center);
    display: flex;
    flex-direction: row;
    justify-content: center;
    width: 100%;
    height: calc(25vh - 30rpx);
    position: fixed;
    left: 0;
    bottom: -70rpx;
    box-sizing: border-box;

    &__button {
      position: absolute;
      left: 100rpx;
      color: #fff;
      font-size: 22rpx;
      text-align: center;
    }

    &__camera {
      width: 110rpx;
      height: 110rpx;

      image {
        width: 100%;
        height: 100%;
      }
    }

    &--success {
      padding: 0 100rpx;
      justify-content: space-between;
    }
  }

  .img {
    width: 690rpx;
    height: 75vh;
    // .flex-display();

    image {
      width: 100%;
      height: 100%;
    }
  }
</style>
相关推荐
王哈哈^_^1 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie2 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic2 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿3 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具3 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
qq_390161774 小时前
防抖函数--应用场景及示例
前端·javascript
John.liu_Test4 小时前
js下载excel示例demo
前端·javascript·excel
Yaml44 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事4 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
哟哟耶耶4 小时前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json