addGuest.vue 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195
  1. <template>
  2. <view class="container">
  3. <view>
  4. <!-- #ifdef MP-WEIXIN -->
  5. <NavigateBar :title="pageTitle" control="back" bgcolor="#a09cc4"></NavigateBar>
  6. <!-- #endif -->
  7. <!-- #ifdef MP-ALIPAY -->
  8. <NavigateBar :title="pageTitle" bgcolor="#a09cc4"></NavigateBar>
  9. <!-- #endif -->
  10. </view>
  11. <view class="step-zero" v-if="step===0">
  12. <view class="tips">
  13. <u-text align="center" color="#333333" text="根据《治安管理处罚法》要求所有入住宾客和访客必须一人一证实名登记"></u-text>
  14. </view>
  15. <u-form labelPosition="left" labelWidth="180rpx">
  16. <u-form-item label="姓名" prop="name">
  17. <u-input v-model="name" border="none" clearable fontSize="36rpx"></u-input>
  18. </u-form-item>
  19. <u-form-item label="手机号" prop="phone">
  20. <u-input v-model="phone" border="none" clearable fontSize="36rpx" type="number"></u-input>
  21. </u-form-item>
  22. <u-form-item label="身份证" prop="idNumber">
  23. <u-input v-model="idNumber" border="none" clearable fontSize="36rpx"></u-input>
  24. </u-form-item>
  25. </u-form>
  26. <view class="btn-container">
  27. <u-button text="立即认证" color="#9e97c3" @click="finishInput"></u-button>
  28. </view>
  29. </view>
  30. <view class="step-one" v-else-if="step===1">
  31. <view class="upload-container">
  32. <u-upload @afterRead="afterIdCardRead" name="Front" :maxCount="1" :maxSize="1024*1024*1.5"
  33. @oversize="oversize">
  34. <image :src="idCardFrontPicPath" mode="aspectFit"></image>
  35. </u-upload>
  36. </view>
  37. <view class="upload-container">
  38. <u-upload @afterRead="afterIdCardRead" name="Back" :maxCount="1" :maxSize="1024*1024*1.5"
  39. @oversize="oversize">
  40. <image :src="idCardBackPicPath" mode="aspectFit"></image>
  41. </u-upload>
  42. </view>
  43. <view class="btn-container">
  44. <u-button :disabled="!getAllInfo" text="完成上传" color="#9e97c3" @click="finishIdCardUpload"></u-button>
  45. </view>
  46. </view>
  47. <view class="step-two" v-else-if="step===2">
  48. <view class="face-area">
  49. <view class="face-area--tips">
  50. <view class="face-area--tips--title">
  51. <u-text text="身份核验" color="#333333" bold size="34rpx"></u-text>
  52. </view>
  53. <view class="face-area--tips--context">
  54. <u-text text="请根据页面提示进行操作" color="#CBCBCB" size="28rpx"></u-text>
  55. </view>
  56. </view>
  57. <view class="face-area--line"></view>
  58. <view class="camera-container">
  59. <camera v-if="showcamera" class="camera" device-position="front" flash="off" resolution="low">
  60. </camera>
  61. </view>
  62. <view class="info">
  63. <u-text v-if="showcamera" :text="tipsText" align="center"></u-text>
  64. </view>
  65. </view>
  66. <view class="precautions">
  67. <view class="precautions--tips">
  68. <view class="precautions--tips--title">
  69. <u-text text="注意事项" color="#333333" bold size="34rpx"></u-text>
  70. </view>
  71. <view class="precautions--tips--context">
  72. <u-text text="请遵守需注意事项" color="#CBCBCB" size="28rpx"></u-text>
  73. </view>
  74. </view>
  75. <view class="precautions--line"></view>
  76. <view class="precautions--context">
  77. <u-text :text="'确认'+name+'本人操作;保持正脸在取景框中根据屏幕指示完成'" color="#666666" size="24rpx"></u-text>
  78. </view>
  79. <view class="img-list">
  80. <view class="img-list--item" v-for="img in imgList" :key="img.src">
  81. <view class="img">
  82. <u-image :src="img.src" :height="img.height" :width="img.width"></u-image>
  83. </view>
  84. <view class="tips">
  85. <u-text :text="img.tips" color="#666666" size="20rpx"></u-text>
  86. </view>
  87. </view>
  88. </view>
  89. </view>
  90. <view class="btn-container">
  91. <u-button text="同意并开始验证" @click="start" color="#9e97c3"></u-button>
  92. </view>
  93. </view>
  94. </view>
  95. </template>
  96. <script>
  97. import {
  98. IMG_BASE_URL,
  99. BASE_URL,
  100. } from '../../config.js'
  101. import {
  102. mapState,
  103. mapMutations
  104. } from 'vuex'
  105. import NavigateBar from '../../components/navigateBar/navigate-bar.vue';
  106. import moment from 'moment/moment.js';
  107. export default {
  108. components: {
  109. NavigateBar
  110. },
  111. data() {
  112. return {
  113. isMainCustomer: false,
  114. //0展示输入姓名、身份证号、手机号页面
  115. //1展示上传身份证照片页面
  116. //2展示身份核验页面
  117. step: 0,
  118. //步骤 0 需要填入的信息
  119. name: '',
  120. idNumber: '',
  121. phone: '',
  122. //步骤 1 需要从身份证获取的信息
  123. idCardInfoInPage: {
  124. name: '',
  125. sex: '',
  126. nation: '',
  127. birth: '',
  128. address: '',
  129. idNumber: '',
  130. issuingAuthority: '',
  131. issuingDate: '',
  132. expiryDate: ''
  133. },
  134. idCardFrontPicPath: '../../static/idcard_upload_front.png',
  135. idCardBackPicPath: '../../static/idcard_upload_back.png',
  136. infoList: ['address', 'nation', 'sex', 'birth', 'issuingAuthority', 'issuingDate', 'expiryDate'],
  137. userInfoId: 0,
  138. // #ifdef MP-WEIXIN
  139. idCardDirection: '',
  140. // img: null,
  141. // imgData: null,
  142. idCardVKSession: null,
  143. idCardFrontUploadInfo: {
  144. imgUrl: '',
  145. imgWidth: 0,
  146. imgHeight: 0,
  147. imgOriginWidth: 0,
  148. imgOriginHeight: 0,
  149. },
  150. idCardFrontDetectInfo: {
  151. detected: false, //检测是否完成
  152. detectSuccess: false, //检测是否成功
  153. isComplete: false, //身份证是否完整
  154. label: '', //身份证方向,0 人像面,1 国徽面
  155. orientation: 0, //身份证朝向 (0 朝上 1 朝下 2 朝下 3 朝左)
  156. box: [], //身份证坐标框点数组 (0 左上点 1 右上点 2 右下点 3 左下点)
  157. },
  158. idCardBackUploadInfo: {
  159. imgUrl: '',
  160. imgWidth: 0,
  161. imgHeight: 0,
  162. imgOriginWidth: 0,
  163. imgOriginHeight: 0,
  164. },
  165. idCardBackDetectInfo: {
  166. detected: false,
  167. detectSuccess: false,
  168. isComplete: false,
  169. label: '',
  170. orientation: 0,
  171. box: [],
  172. },
  173. // showAddGuest: true,
  174. cameraEngine: null,
  175. VKSession: null,
  176. showcamera: false,
  177. tipsText: '开始身份核验',
  178. duringCameraAuth: false,
  179. isVKSupport: false,
  180. imgList: [{
  181. src: '/static/check-face-phone.png',
  182. width: '48rpx',
  183. height: '70rpx',
  184. tips: '正对手机'
  185. },
  186. {
  187. src: '/static/check-face-light.png',
  188. width: '78rpx',
  189. height: '70rpx',
  190. tips: '光线充足'
  191. },
  192. {
  193. src: '/static/check-face-face.png',
  194. width: '80rpx',
  195. height: '80rpx',
  196. tips: '脸部无遮挡'
  197. }
  198. ],
  199. pageTitle: '身份信息录入'
  200. // #endif
  201. };
  202. },
  203. computed: {
  204. ...mapState('m_user', ['userInfo']),
  205. ...mapState('m_business', ['checkinInfo', 'currentHotel', 'idCardInfo']),
  206. getAllInfo() {
  207. let getAll = true
  208. for (const info of this.infoList) {
  209. if (!this.idCardInfoInPage[info]) {
  210. getAll = false
  211. break
  212. }
  213. }
  214. return getAll
  215. }
  216. },
  217. methods: {
  218. ...mapMutations('m_business', ['updateCheckinInfo', 'updateIdCardInfo']),
  219. ...mapMutations('m_user', ['updateUserInfo']),
  220. async finishInput() {
  221. //检查格式是否正确
  222. let nameReg = /\d/
  223. if (this.name === '' || nameReg.test(this.name)) {
  224. uni.$showMsg('姓名格式错误!')
  225. return
  226. }
  227. let idNumberReg =
  228. /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/
  229. if (!idNumberReg.test(this.idNumber)) {
  230. uni.$showMsg('身份证号格式错误!')
  231. return
  232. }
  233. let phoneReg = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/
  234. if (!phoneReg.test(this.phone)) {
  235. uni.$showMsg('手机号格式错误!')
  236. return
  237. }
  238. //检查该顾客是否在当前酒店入住
  239. let res = await uni.$http.post('/checkinInfo/queryByCondition', {
  240. userIdNumber: this.idNumber,
  241. status: 1,
  242. hotelId: this.currentHotel.hotelId,
  243. pageNo: 1,
  244. pageSize: 1
  245. });
  246. if (res.data.code == 200 && res.data.data.records.length > 0) {
  247. uni.$showMsg('该顾客已入住!')
  248. return
  249. }
  250. let pages = getCurrentPages()
  251. let beforePage = pages[pages.length - 2]
  252. //判断此顾客信息是否已添加
  253. if (JSON.stringify(this.checkinInfo) !== '{}' && this.checkinInfo.length > 0) {
  254. for (let info of this.checkinInfo) {
  255. if (info.idNumber === this.idNumber) {
  256. uni.$showMsg('已添加此顾客,请检查填写信息')
  257. return
  258. }
  259. }
  260. }
  261. //判断本地缓存是否有顾客身份证信息,有则跳转step 2
  262. for (const info of this.idCardInfo) {
  263. if (info.idNumber === this.idNumber && info.name === this.name) {
  264. this.idCardInfoInPage = info
  265. this.step = 2
  266. return
  267. }
  268. }
  269. //本地缓存没有顾客身份信息,到后台查询
  270. let idCardInfoQueryRes = await uni.$http.post('/idCardInfo/queryByCondition', {
  271. pageNo: 1,
  272. pageSize: 1,
  273. idNumber: this.idNumber,
  274. name: this.name
  275. })
  276. //查询到,写入local storage,跳转step 2
  277. if (idCardInfoQueryRes.data.data.records.length === 1) {
  278. let info = idCardInfoQueryRes.data.data.records[0]
  279. info.birth = info.birthday
  280. let keys = Object.keys(this.idCardInfoInPage)
  281. for (const key of keys) {
  282. //有数据为空则需重新上传
  283. if (!info[key]) {
  284. this.step = 1
  285. return
  286. }
  287. this.idCardInfoInPage[key] = info[key]
  288. }
  289. let idCardInfoToUpdate = this.idCardInfo
  290. idCardInfoToUpdate.push(this.idCardInfoInPage)
  291. this.updateIdCardInfo(idCardInfoToUpdate)
  292. this.step = 2
  293. return
  294. }
  295. //没查询到,跳转step 1
  296. this.step = 1
  297. },
  298. initIdCardVKSession() {
  299. this.idCardVKSession = wx.createVKSession({
  300. track: {
  301. IDCard: {
  302. mode: 2 // 照片模式
  303. }
  304. },
  305. version: 'v1',
  306. gl: this.gl
  307. })
  308. // VKSession start
  309. this.idCardVKSession.start(err => {
  310. this.idCardVKSession.on('updateAnchors', anchors => {
  311. // console.log('updateAnchors')
  312. // 处理返回的身份证信息
  313. if (anchors && anchors[0]) {
  314. // 存在数组,证明存在身份证信息
  315. const anchor = anchors[0];
  316. // console.log(anchor.isComplete, anchor.label, anchor.orientation, anchor.box);
  317. let info = {
  318. detected: true,
  319. detectSuccess: true,
  320. isComplete: anchor.isComplete,
  321. label: anchor.label,
  322. orientation: anchor.orientation,
  323. box: anchor.box,
  324. }
  325. this['idCard' + this.idCardDirection + 'DetectInfo'] = info
  326. // 裁剪信息
  327. const affineImgWidth = anchor.affineImgWidth;
  328. const affineImgHeight = anchor.affineImgHeight;
  329. const affineMat = anchor.affineMat;
  330. // 存在裁剪信息,进行身份证裁剪处理
  331. if (affineImgWidth && affineImgHeight && affineMat) {
  332. // console.log(affineImgWidth, affineImgHeight, affineMat);
  333. const cropIDcardImg = this.getCropIDcard(affineImgWidth, affineImgHeight,
  334. affineMat);
  335. // console.log(cropIDcardImg)
  336. }
  337. }
  338. })
  339. this.idCardVKSession.on('removeAnchors', anchors => {
  340. // console.log("anchor remove")
  341. // 图片没有识别到身份证
  342. this['idCard' + this.idCardDirection + 'DetectInfo'].detected = true
  343. this['idCard' + this.idCardDirection + 'DetectInfo'].detectSuccess = false
  344. })
  345. });
  346. },
  347. async setImageUploadInfo(url) {
  348. const fixWidth = 300
  349. let imageInfo = await uni.getImageInfo({
  350. src: url
  351. // ,
  352. // success: (image) => {
  353. // console.log(image)
  354. // let height = image.height
  355. // let width = image.width
  356. // let info = {
  357. // imgUrl: url,
  358. // imgWidth: fixWidth,
  359. // imgHeight: (fixWidth / width) * height,
  360. // imgOriginWidth: width,
  361. // imgOriginHeight: height
  362. // }
  363. // this['idCard' + this.idCardDirection + 'UploadInfo'] = info
  364. // }
  365. })
  366. let height = imageInfo[1].height
  367. let width = imageInfo[1].width
  368. let uploadInfo = {
  369. imgUrl: url,
  370. imgWidth: fixWidth,
  371. imgHeight: (fixWidth / width) * height,
  372. imgOriginWidth: width,
  373. imgOriginHeight: height
  374. }
  375. this['idCard' + this.idCardDirection + 'UploadInfo'] = uploadInfo
  376. },
  377. getCropIDcard(affineImgWidth, affineImgHeight, affineMat) {
  378. // console.log('start getCropIDcard')
  379. let infoName = 'idCard' + this.idCardDirection + 'UploadInfo'
  380. const canvas = wx.createOffscreenCanvas({
  381. type: '2d',
  382. width: affineImgWidth,
  383. height: affineImgHeight,
  384. })
  385. const context = canvas.getContext('2d')
  386. context.clearRect(0, 0, affineImgWidth, affineImgHeight);
  387. /*
  388. * affineMat 3x3仿射变换矩阵,行主序
  389. * [0 1 2
  390. * 3 4 5
  391. * 6 7 8]
  392. */
  393. /*
  394. * canvas 2d setTransform
  395. * setTransform(a, b, c, d, e, f)
  396. * [a c e
  397. * b d f
  398. * 0 0 1]
  399. */
  400. context.setTransform(
  401. Number(affineMat[0]), Number(affineMat[3]), Number(affineMat[1]),
  402. Number(affineMat[4]), Number(affineMat[2]), Number(affineMat[5])
  403. );
  404. // console.log(this.img, this[infoName].imgOriginWidth, this[infoName].imgOriginHeight)
  405. context.drawImage(this.img, 0, 0, this[infoName].imgOriginWidth, this[infoName].imgOriginHeight)
  406. const imgUrl = canvas.toDataURL()
  407. this[infoName].imgUrl = imgUrl
  408. this['idCard' + this.idCardDirection + 'PicPath'] = imgUrl
  409. return imgUrl;
  410. },
  411. async detectIdCard() {
  412. let infoName = 'idCard' + this.idCardDirection + 'UploadInfo'
  413. if (this[infoName].imgUrl) {
  414. const canvas = wx.createOffscreenCanvas({
  415. type: '2d',
  416. width: this[infoName].imgOriginWidth,
  417. height: this[infoName].imgOriginHeight,
  418. })
  419. const context = canvas.getContext('2d')
  420. const img = canvas.createImage()
  421. // 使用中的 图片对象
  422. this.img = img;
  423. await new Promise(resolve => {
  424. img.onload = resolve
  425. img.src = this[infoName].imgUrl
  426. })
  427. context.clearRect(0, 0, this[infoName].imgOriginWidth, this[infoName].imgOriginHeight)
  428. context.drawImage(img, 0, 0, this[infoName].imgOriginWidth, this[infoName].imgOriginHeight)
  429. // 使用中的 image ArrayBuffer
  430. this.imgData = context.getImageData(0, 0, this[infoName].imgOriginWidth, this[infoName]
  431. .imgOriginHeight)
  432. // console.log('[frameBuffer] --> ', this.imgData.data.buffer)
  433. // console.log('this.session.detectIDCard', this.idCardVKSession.detectIDCard)
  434. // console.log('width', this[infoName].imgOriginWidth)
  435. // console.log('height', this[infoName].imgOriginHeight)
  436. this.idCardVKSession.detectIDCard({
  437. // 识别身份证图片的信息
  438. frameBuffer: this.imgData.data.buffer,
  439. width: this[infoName].imgOriginWidth,
  440. height: this[infoName].imgOriginHeight,
  441. // 是否获取裁剪图片信息
  442. getAffineImg: true,
  443. })
  444. }
  445. },
  446. async afterIdCardRead(event) {
  447. console.log(event)
  448. //正面还是反面
  449. this.idCardDirection = event.name
  450. await this.setImageUploadInfo(event.file.url)
  451. await this.detectIdCard()
  452. let detectInfo = this['idCard' + this.idCardDirection + 'DetectInfo']
  453. //图片中不包含身份证
  454. if (!detectInfo.detectSuccess) {
  455. uni.showModal({
  456. title: '图片中不包含身份证,请重新上传',
  457. showCancel: false
  458. })
  459. return
  460. }
  461. if (!detectInfo.isComplete) {
  462. uni.showModal({
  463. title: '身份证照片不完整,请重新上传',
  464. showCancel: false
  465. })
  466. return
  467. }
  468. if ((this.idCardDirection === 'Front' && detectInfo.label !== 0) ||
  469. (this.idCardDirection === 'Back' && detectInfo.label !== 1)) {
  470. uni.showModal({
  471. title: '身份证面错误,请重新上传',
  472. showCancel: false
  473. })
  474. return
  475. }
  476. let baseUrl = BASE_URL.slice(0, -11)
  477. uni.showLoading({
  478. title: '上传中',
  479. mask: true
  480. })
  481. let filePath = this['idCard' + this.idCardDirection + 'PicPath']
  482. // uni.getFileSystemManager().readFile({
  483. // filePath: filePath, //选择图片返回的相对路径
  484. // encoding: 'base64',
  485. // success: res => {
  486. let data = {
  487. image: filePath.split(',')[1],
  488. imageType: 'BASE64',
  489. }
  490. data.ocrType = detectInfo.label
  491. uni.request({
  492. url: baseUrl + '/verification/idCardOcr',
  493. method: 'POST',
  494. data: data,
  495. success: res => {
  496. console.log(res)
  497. if (res.data.data.imageStatus !== 'normal') {
  498. uni.showModal({
  499. content: '获取信息失败,请检查图片并重新上传',
  500. showCancel: false,
  501. })
  502. }
  503. if (event.name === 'Front' && (res.data.data.cardNum != this.idNumber || res.data
  504. .data.name != this.name)) {
  505. uni.showModal({
  506. content: '填写信息与身份证不一致',
  507. showCancel: false,
  508. complete: () => {
  509. this.step = 0
  510. this.idCardFrontPicPath =
  511. '../../static/upload_front.png'
  512. return
  513. }
  514. })
  515. }
  516. this['idCard' + this.idCardDirection + 'Base64'] = data.image
  517. if (event.name === 'Front') {
  518. this.idCardInfoInPage.name = res.data.data.name
  519. this.idCardInfoInPage.sex = res.data.data.sex
  520. this.idCardInfoInPage.nation = res.data.data.nation
  521. this.idCardInfoInPage.birth = res.data.data.birth
  522. this.idCardInfoInPage.address = res.data.data.address
  523. this.idCardInfoInPage.idNumber = res.data.data.cardNum
  524. } else {
  525. this.idCardInfoInPage.issuingAuthority = res.data.data.issuingAuthority
  526. this.idCardInfoInPage.issuingDate = res.data.data.issuingDate
  527. this.idCardInfoInPage.expiryDate = res.data.data.expiryDate
  528. }
  529. },
  530. fail: err => {
  531. uni.$showMsg('上传失败')
  532. },
  533. complete: () => {
  534. console.log('complete')
  535. uni.hideLoading()
  536. }
  537. })
  538. // },
  539. // complete: () => {
  540. // uni.hideLoading()
  541. // }
  542. // })
  543. },
  544. oversize() {
  545. uni.$showMsg('图片超出允许大小(1.5M)')
  546. },
  547. async gotoCheckFace() {
  548. //静态检测
  549. if (this.userInfo.staticFaceCheck === true) {
  550. uni.navigateTo({
  551. url: `/subpkg_checkin/staticFaceCheck/staticFaceCheck?idNumber=${this.idNumber}&name=${this.name}&phone=${this.phone}`
  552. })
  553. } else {
  554. // #ifdef MP-WEIXIN
  555. // const accountInfo = wx.getAccountInfoSync();
  556. // const env = accountInfo.miniProgram.envVersion
  557. // if (env !== "release") {
  558. // let faceRes = await uni.request({
  559. // url: IMG_BASE_URL + '/test_face_base64.txt',
  560. // method: 'GET'
  561. // })
  562. // let faceBase64 = faceRes[1].data
  563. // this.checkinInfo[this.checkinInfo.length] = {
  564. // name: this.name,
  565. // idNumber: this.idNumber,
  566. // phone: this.phone,
  567. // faceData: faceBase64
  568. // }
  569. // this.updateCheckinInfo(this.checkinInfo)
  570. // uni.navigateTo({
  571. // url: '/subpkg_checkin/confirmOrder/confirmOrder'
  572. // })
  573. // }
  574. // #endif
  575. // #ifdef MP-ALIPAY
  576. uni.navigateTo({
  577. url: '/subpkg_checkin/alipayCheckPic/alipayCheckPic?idNumber=' + this.idNumber +
  578. '&name=' + this.name + '&phone=' + this.phone
  579. })
  580. // #endif
  581. }
  582. },
  583. finishIdCardUpload() {
  584. if (this.getAllInfo) {
  585. this.step = 2
  586. } else {
  587. uni.$showMsg('请检查身份证照片')
  588. }
  589. },
  590. // #ifdef MP-WEIXIN
  591. start() {
  592. console.log('调用start');
  593. this.idCardVKSession.destroy()
  594. if (!this.duringCameraAuth && this.isVKSupport) {
  595. this.getCameraAuth()
  596. }
  597. },
  598. getCameraAuth() {
  599. console.log('getCameraAuth')
  600. this.duringCameraAuth = true
  601. let that = this
  602. wx.getSetting({
  603. success: (res) => {
  604. console.log('getSetting, res : ')
  605. console.log(res)
  606. if (!res.authSetting['scope.camera']) {
  607. wx.authorize({
  608. scope: 'scope.camera',
  609. success(res) {
  610. console.log('authorize success, res : ')
  611. console.log(res)
  612. that.showcamera = true
  613. },
  614. fail: (res) => {
  615. console.log('authorize fail, res : ')
  616. console.log(res)
  617. wx.showModal({
  618. title: '提示',
  619. content: '尚未进行授权,功能将无法使用',
  620. cancelText: '取消',
  621. confirmText: '授权',
  622. success(res) {
  623. console.log(res);
  624. if (res.confirm) {
  625. wx.openSetting({
  626. //这里的方法是调到一个添加权限的页面,这里可以测试在拒绝授权的情况下设置中是否存在相机选项
  627. success: (res) => {
  628. if (!res.authSetting[
  629. 'scope.camera'
  630. ]) {
  631. this.getCameraAuth()
  632. } else {
  633. that.showcamera =
  634. true
  635. }
  636. },
  637. fail: function() {
  638. console.log(
  639. '相机授权设置失败');
  640. },
  641. });
  642. } else if (res.cancel) {
  643. console.log('用户点击取消');
  644. wx.showToast({
  645. title: '您已取消授权',
  646. icon: 'error',
  647. duration: 1000,
  648. });
  649. setTimeout(function() {
  650. wx.navigateBack();
  651. }, 1000);
  652. }
  653. }
  654. })
  655. }
  656. })
  657. } else {
  658. that.showcamera = true
  659. }
  660. that.initData()
  661. }
  662. })
  663. this.duringCameraAuth = false
  664. },
  665. //visionkit人脸必须函数
  666. async detectFace(frame) {
  667. console.log('frame', frame.data);
  668. this.faceVKSession.detectFace({
  669. frameBuffer: frame.data,
  670. width: frame.width,
  671. height: frame.height,
  672. scoreThreshold: 0.8,
  673. sourceType: 0,
  674. modelMode: 1,
  675. });
  676. },
  677. //初始化相机引擎
  678. initData() {
  679. let that = this;
  680. console.log('before createCameraContext')
  681. //使用visionkit
  682. that.cameraEngine = wx.createCameraContext();
  683. console.log('before createCameraContext')
  684. let count = 0;
  685. //初始化相机
  686. const listener = that.cameraEngine.onCameraFrame((frame) => {
  687. count++;
  688. //每十帧分析一次
  689. if (count === 10) {
  690. this.detectFace(frame);
  691. count = 0;
  692. }
  693. });
  694. //开始监听数据帧
  695. listener.start();
  696. //创建VK对象
  697. that.faceVKSession = wx.createVKSession({
  698. version: 'v1',
  699. track: {
  700. plane: {
  701. mode: 1,
  702. },
  703. face: {
  704. mode: 2,
  705. },
  706. },
  707. });
  708. //启动VK对象
  709. that.faceVKSession.start((errno) => {
  710. console.log('faceVKSession.start errno', errno);
  711. if (errno) {
  712. wx.showModal({
  713. title: '提示',
  714. content: '网络错误,请退出后重试',
  715. showCancel: false,
  716. confirmText: '确定',
  717. success(res) {
  718. console.log(res);
  719. if (res.confirm) {
  720. wx.navigateBack();
  721. }
  722. },
  723. });
  724. return
  725. }
  726. console.log('faceVKSession start')
  727. that.faceVKSession.on('updateAnchors', (anchors) => {
  728. console.log('anchors', anchors);
  729. console.log('anchors.length', anchors.length);
  730. anchors.forEach((anchor) => {
  731. console.log('anchor.points', anchor.points);
  732. console.log('anchor.origin', anchor.origin);
  733. console.log('anchor.size', anchor.size);
  734. console.log('anchor.angle', anchor.angle);
  735. console.log('anchor', anchor.confidence);
  736. if (anchors.length > 1) {
  737. that.tipsText = '请保证只有一个人';
  738. } else {
  739. const {
  740. pitch,
  741. roll,
  742. yaw
  743. } = anchor.angle;
  744. const standard = 0.3;
  745. if (
  746. Math.abs(pitch) >= standard ||
  747. Math.abs(roll) >= standard ||
  748. Math.abs(yaw) >= standard
  749. ) {
  750. that.tipsText = '请平视摄像头';
  751. } else if (
  752. anchor.origin.x < 0.15 ||
  753. anchor.origin.x > 0.3 ||
  754. anchor.origin.y < 0.2 ||
  755. anchor.origin.y > 0.45
  756. ) {
  757. this.tipsText = '请将人脸对准中心位置';
  758. } else if (
  759. anchor.confidence[0] <= 0.8 ||
  760. anchor.confidence[1] <= 0.8 ||
  761. anchor.confidence[2] <= 0.8 ||
  762. anchor.confidence[3] <= 0.8 ||
  763. anchor.confidence[4] <= 0.8
  764. ) {
  765. that.tipsText = '请勿遮挡五官';
  766. } else {
  767. listener.stop();
  768. that.tipsText = '即将拍照,请保持!';
  769. setTimeout(function() {
  770. that.handleTakePhotoClick();
  771. }, 1000);
  772. return;
  773. }
  774. }
  775. });
  776. });
  777. });
  778. },
  779. // 拍照
  780. handleTakePhotoClick() {
  781. this.tipsText = '';
  782. uni.showLoading({
  783. title: '正在身份核验,请稍后',
  784. });
  785. this.cameraEngine.takePhoto({
  786. quality: 'normal',
  787. success: ({
  788. tempImagePath
  789. }) => {
  790. let mybase64 = wx
  791. .getFileSystemManager()
  792. .readFileSync(tempImagePath, 'base64');
  793. this.afterRead(mybase64);
  794. this.showcamera = false
  795. },
  796. });
  797. },
  798. //上传
  799. async afterRead(mybase64) {
  800. let that = this;
  801. uni.$http.post('/faceVerification/checkPic', {
  802. hotelId: that.currentHotel.hotelId,
  803. faceData: mybase64,
  804. })
  805. .then(async (res) => {
  806. uni.hideLoading();
  807. console.log('mybase64', mybase64);
  808. if (res.data.code === 200 && res.data.success === true) {
  809. if (that.userInfo.skipIdMatching === true) {
  810. uni.showModal({
  811. content: '图片检查通过!',
  812. showCancel: false,
  813. success: function(res) {
  814. if (res.confirm) {
  815. console.log('用户点击确定');
  816. let infoFromIdCard = {}
  817. that.infoList.forEach(info => {
  818. infoFromIdCard[info] = that[info]
  819. })
  820. let currentCheckinInfo = {
  821. name: that.name,
  822. idNumber: that.idNumber,
  823. phone: that.phone,
  824. faceData: mybase64
  825. }
  826. currentCheckinInfo = {
  827. ...currentCheckinInfo,
  828. ...infoFromIdCard
  829. }
  830. that.checkinInfo.push(currentCheckinInfo)
  831. that.updateCheckinInfo(that.checkinInfo)
  832. let currentGuestInfo = {
  833. name: that.name,
  834. idNumber: that.idNumber,
  835. }
  836. let i
  837. for (i = 0; i < that.guestInfo.length; i++) {
  838. if (that.guestInfo[i].idNumber == that.idNumber) {
  839. that.guestInfo[i] = {
  840. ...currentGuestInfo,
  841. ...infoFromIdCard
  842. }
  843. that.updateGuestInfo(that.guestInfo)
  844. }
  845. }
  846. if (i === that.guestInfo.length) {
  847. that.guestInfo.push({
  848. ...currentGuestInfo,
  849. ...infoFromIdCard
  850. })
  851. that.updateGuestInfo(that.guestInfo)
  852. }
  853. setTimeout(() => uni.reLaunch({
  854. url: '/subpkg_checkin/confirmOrder/confirmOrder'
  855. }), 1000);
  856. }
  857. },
  858. });
  859. } else {
  860. uni.showLoading({
  861. title: '正在身份核验,请稍后',
  862. });
  863. let yunjiFaceCheckInfo = {
  864. idNo: that.idNumber,
  865. name: that.name,
  866. image: mybase64,
  867. };
  868. let checkRes = await uni.$http.post(
  869. '/faceVerification/yunjiVerification',
  870. yunjiFaceCheckInfo
  871. );
  872. if (checkRes.data.success === true) {
  873. let infoFromIdCard = {}
  874. that.infoList.forEach(info => {
  875. infoFromIdCard[info] = that.idCardInfoInPage[info]
  876. })
  877. let currentCheckinInfo = {
  878. name: that.name,
  879. idNumber: that.idNumber,
  880. faceData: mybase64,
  881. phone: that.phone,
  882. ...infoFromIdCard
  883. }
  884. //更新local storage 中的入住信息
  885. let checkinInfoToUpdate = that.checkinInfo
  886. checkinInfoToUpdate.push(currentCheckinInfo)
  887. that.updateCheckinInfo(checkinInfoToUpdate)
  888. //更新local storage 中的身份证信息
  889. let idCardInfoToUpdate = that.idCardInfo
  890. idCardInfoToUpdate.push(that.idCardInfoInPage)
  891. that.updateIdCardInfo(idCardInfoToUpdate)
  892. //如果上传过照片则更新服务器中的身份证信息
  893. if (that.idCardFrontDetectInfo.detected && that.idCardBackDetectInfo
  894. .detected) {
  895. uni.$http.post('/idCardInfo', {
  896. ...that.idCardInfoInPage,
  897. birthday: that.idCardInfoInPage.birth,
  898. idCardFrontBase64: that.idCardFrontPicPath.split(',')[1],
  899. idCardBackBase64: that.idCardBackPicPath.split(',')[1]
  900. })
  901. }
  902. //如果是主入住人,且当前账号姓名和身份证号信息与所传身份证不一致,则更新账号信息为主入住人信息
  903. if (this.isMainCustomer && (this.userInfo.name !== this.name || this
  904. .userInfo.idNumber !== this.idNumber)) {
  905. this.userInfo.name = this.name
  906. this.userInfo.idNumber = this.idNumber
  907. this.updateUserInfo(this.userInfo)
  908. uni.$http.put(`/userInfo/${this.userInfo.id}`, this.userInfo)
  909. }
  910. uni.hideLoading();
  911. uni.$showMsg('身份核验通过!')
  912. setTimeout(() => uni.reLaunch({
  913. url: '/subpkg_checkin/confirmOrder/confirmOrder'
  914. }), 1000)
  915. } else {
  916. uni.hideLoading()
  917. uni.showModal({
  918. content: '身份核验失败,请重试',
  919. showCancel: false,
  920. success: function(res) {
  921. if (res.confirm) {
  922. console.log('用户点击确定');
  923. that.initData();
  924. }
  925. },
  926. });
  927. }
  928. }
  929. } else {
  930. uni.hideLoading()
  931. uni.showModal({
  932. content: '身份核验失败,请重试',
  933. showCancel: false,
  934. success: function(res) {
  935. if (res.confirm) {
  936. console.log('用户点击确定');
  937. that.initData();
  938. }
  939. },
  940. });
  941. }
  942. console.log('checkPicRes', res);
  943. });
  944. },
  945. // #endif
  946. },
  947. onLoad() {
  948. let pages = getCurrentPages()
  949. let beforePage = pages[pages.length - 2]
  950. //不是从确定订单页面点击添加顾客跳转过来,设置信息为当前账户的信息,清空入住信息
  951. if (beforePage.route !== 'subpkg_checkin/confirmOrder/confirmOrder') {
  952. this.name = this.userInfo.name
  953. this.idNumber = this.userInfo.idNumber
  954. this.phone = this.userInfo.phone
  955. this.updateCheckinInfo([])
  956. this.isMainCustomer = true
  957. } else {
  958. this.isMainCustomer = false
  959. }
  960. // #ifdef MP-WEIXIN
  961. this.isVKSupport = wx.isVKSupport('v1')
  962. if (!this.isVKSupport) {
  963. uni.showModal({
  964. content: '微信版本过低,请升级至最新版本',
  965. showCancel: false,
  966. })
  967. }
  968. // #endif
  969. },
  970. // #ifdef MP-WEIXIN
  971. onShow() {
  972. this.showAddGuest = true
  973. this.pageTitle = '身份信息录入'
  974. },
  975. onReady() {
  976. this.initIdCardVKSession()
  977. },
  978. onShareAppMessage(info) {
  979. return {
  980. title: '源享住',
  981. path: 'pages/quickLogin/quickLogin',
  982. imageUrl: '/static/logo.png'
  983. }
  984. }
  985. // #endif
  986. }
  987. </script>
  988. <style lang="scss">
  989. .btn-container {
  990. position: fixed;
  991. bottom: 90rpx;
  992. left: 30rpx;
  993. right: 30rpx;
  994. .u-button__text {
  995. font-size: 30rpx !important;
  996. }
  997. }
  998. page {
  999. background: #EFEFF4;
  1000. .container {
  1001. .step-zero {
  1002. .tips {
  1003. margin: 30rpx;
  1004. }
  1005. .u-form {
  1006. margin-top: 10rpx;
  1007. background-color: #FFFFFF;
  1008. .u-form-item {
  1009. line-height: 50rpx;
  1010. .u-form-item__body {
  1011. margin-left: 32rpx;
  1012. border-bottom: 2rpx solid #E5E5E5;
  1013. .u-form-item__body__right__content__slot {
  1014. display: flex;
  1015. flex-direction: row;
  1016. justify-content: space-between;
  1017. margin-right: 20rpx;
  1018. align-items: center;
  1019. }
  1020. }
  1021. .u-form-item__body__left__content__label {
  1022. font-size: 30rpx;
  1023. }
  1024. }
  1025. }
  1026. }
  1027. .step-one {
  1028. .upload-container {
  1029. width: 500rpx;
  1030. margin: 30rpx auto;
  1031. .u-upload {
  1032. image {
  1033. width: 500rpx;
  1034. height: 350rpx;
  1035. margin: 0 auto;
  1036. }
  1037. }
  1038. }
  1039. }
  1040. .step-two {
  1041. .face-area {
  1042. background-color: #FFFFFF;
  1043. border-radius: 50rpx;
  1044. overflow: hidden;
  1045. .face-area--tips {
  1046. display: flex;
  1047. padding: 10rpx 20rpx;
  1048. align-items: flex-end;
  1049. .face-area--tips--title {
  1050. margin: 10rpx;
  1051. }
  1052. .face-area--tips--context {
  1053. margin: 10rpx;
  1054. }
  1055. }
  1056. .face-area--line {
  1057. margin-left: 40rpx;
  1058. margin-bottom: 20rpx;
  1059. width: 30rpx;
  1060. border-bottom: solid 6rpx #9e97c3;
  1061. }
  1062. .camera-container {
  1063. width: 80%;
  1064. height: 80vw;
  1065. margin: 0 auto;
  1066. margin-bottom: 30rpx;
  1067. .camera {
  1068. width: 100%;
  1069. height: 80vw;
  1070. margin: 0 auto;
  1071. margin-bottom: 30rpx;
  1072. border: 2rpx solid black;
  1073. border-radius: 50%;
  1074. // box-sizing: border-box;
  1075. }
  1076. }
  1077. }
  1078. .precautions {
  1079. overflow: hidden;
  1080. .precautions--tips {
  1081. display: flex;
  1082. padding: 10rpx 20rpx;
  1083. align-items: flex-end;
  1084. .precautions--tips--title {
  1085. margin: 10rpx;
  1086. }
  1087. .precautions--tips--context {
  1088. margin: 10rpx;
  1089. }
  1090. }
  1091. .precautions--line {
  1092. margin-left: 40rpx;
  1093. margin-bottom: 20rpx;
  1094. width: 30rpx;
  1095. border-bottom: solid 6rpx #9e97c3;
  1096. }
  1097. .precautions--context {
  1098. margin: 0 20rpx;
  1099. margin-bottom: 20rpx;
  1100. }
  1101. .img-list {
  1102. display: flex;
  1103. justify-content: space-around;
  1104. .img-list--item {
  1105. display: flex;
  1106. flex-direction: column;
  1107. align-items: center;
  1108. width: 20%;
  1109. .img {
  1110. display: flex;
  1111. flex-direction: row;
  1112. border-radius: 20rpx;
  1113. background-color: #dcdce9;
  1114. width: 100%;
  1115. height: 140rpx;
  1116. justify-content: center;
  1117. align-items: center;
  1118. }
  1119. }
  1120. }
  1121. }
  1122. }
  1123. }
  1124. }
  1125. </style>