学员端小程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

492 lines
11 KiB

12 months ago
  1. <template>
  2. <view class="page-content">
  3. <!-- #ifdef MP-WEIXIN -->
  4. <!-- <u-navbar title="人脸识别" :is-back="true" :background="{backgroundColor: '#ffffff'}"></u-navbar> -->
  5. <topNavbar title="人脸识别"></topNavbar>
  6. <!-- #endif -->
  7. <view class="containerV">
  8. <view class="headerV">
  9. <view class="top-tips1">
  10. <view>请将正对手机头部匹配摄像区域</view>
  11. </view>
  12. <view class="top-tips2">
  13. 为了便于识别认证请拍摄本人头像
  14. </view>
  15. </view>
  16. <!-- <u-circle-progress active-color="#2979ff" :percent="80"> -->
  17. <view class="contentV">
  18. <view class="mark"></view>
  19. <image v-if="tempImg" mode="widthFix" :src="tempImg" />
  20. <camera v-if='isAuthCamera' class="camera" :device-position="devicePosition ?'front': 'back'"
  21. flash="off" resolution='high'>
  22. </camera>
  23. <view v-show="!tempImg && tipsText" class="tipV">{{ tipsText }}</view>
  24. </view>
  25. <!-- </u-circle-progress> -->
  26. <view class="footerV">
  27. <view style="width: 100%;">
  28. <view v-if="!tempImg" style="width: 100%;">
  29. <view class="bottom-tips-2">该照片仅作为你认证的凭证</view>
  30. </view>
  31. </view>
  32. </view>
  33. </view>
  34. </view>
  35. </template>
  36. <script>
  37. import { WX_API, H5_API, prefix } from '@/config/site.config.js';
  38. import { createconsult } from '@/config/api.js'
  39. var _url = H5_API+ WX_API
  40. export default {
  41. name: 'index',
  42. components: {},
  43. data() {
  44. return {
  45. tipsText: '', // 错误文案提示
  46. tempImg: '', // 本地图片路径
  47. BASE_API: '',
  48. cameraEngine: null, // 相机引擎
  49. devicePosition: true, // 摄像头朝向
  50. isAuthCamera: true, // 是否拥有相机权限
  51. uploadUrl: _url + 'app-api/infra/file/upload',
  52. faceGetResult: '',
  53. count: 0,
  54. isload:false,//判断连拍
  55. }
  56. },
  57. onLoad(options) {
  58. this.initData()
  59. },
  60. mounted() {
  61. },
  62. methods: {
  63. // 初始化相机引擎
  64. initData() {
  65. // #ifdef MP-WEIXIN
  66. // 1、初始化人脸识别
  67. wx.initFaceDetect()
  68. // 2、创建 camera 上下文 CameraContext 对象
  69. this.cameraEngine = wx.createCameraContext()
  70. // 3、获取 Camera 实时帧数据
  71. const listener = this.cameraEngine.onCameraFrame((frame) => {
  72. if (this.tempImg) {
  73. return;
  74. }
  75. // 4、人脸识别,使用前需要通过 wx.initFaceDetect 进行一次初始化,推荐使用相机接口返回的帧数据
  76. wx.faceDetect({
  77. frameBuffer: frame.data,
  78. width: frame.width,
  79. height: frame.height,
  80. enablePoint: true,
  81. enableConf: true,
  82. enableAngle: true,
  83. enableMultiFace: true,
  84. success: (faceData) => {
  85. let face = faceData.faceInfo[0]
  86. if (faceData.x == -1 || faceData.y == -1) {
  87. this.tipsText = '检测不到人'
  88. }
  89. if (faceData.faceInfo.length > 1) {
  90. this.tipsText = '请保证只有一个人'
  91. } else {
  92. const {
  93. pitch,
  94. roll,
  95. yaw
  96. } = face.angleArray;
  97. const standard = 0.5
  98. if (Math.abs(pitch) >= standard || Math.abs(roll) >= standard ||
  99. Math.abs(yaw) >= standard) {
  100. this.tipsText = '请平视摄像头'
  101. } else if (face.confArray.global <= 0.8 || face.confArray.leftEye <=
  102. 0.8 || face.confArray.mouth <= 0.8 || face.confArray.nose <= 0.8 ||
  103. face.confArray.rightEye <= 0.8) {
  104. this.tipsText = '请勿遮挡五官'
  105. } else {
  106. // this.tipsText = '请拍照'
  107. // 这里可以写自己的逻辑了
  108. this.handleTakePhotoClick()
  109. }
  110. }
  111. },
  112. fail: (err) => {
  113. if (err.x == -1 || err.y == -1) {
  114. this.tipsText = '检测不到人'
  115. } else {
  116. this.tipsText = err.errMsg || '网络错误,请退出页面重试'
  117. }
  118. },
  119. })
  120. })
  121. // 5、开始监听帧数据
  122. listener.start()
  123. // #endif
  124. },
  125. // 拍照
  126. handleTakePhotoClick() {
  127. // if (this.tipsText != "" && this.tipsText != "请拍照") {
  128. // return;
  129. // }
  130. if(this.isload) return
  131. this.isload = true
  132. uni.getSetting({
  133. success: (res) => {
  134. if (!res.authSetting['scope.camera']) {
  135. this.isAuthCamera = false
  136. uni.openSetting({
  137. success: (res) => {
  138. if (res.authSetting['scope.camera']) {
  139. this.isAuthCamera = true;
  140. }
  141. }
  142. })
  143. }
  144. }
  145. })
  146. this.cameraEngine.takePhoto({
  147. quality: "high",
  148. success: ({
  149. tempImagePath
  150. }) => {
  151. this.tempImg = tempImagePath
  152. // this.isAuthCamera = false
  153. uni.showLoading({
  154. title: '人脸核身中,请耐心等待....'
  155. })
  156. this.upLoad(tempImagePath)
  157. console.log('tempImagePath', tempImagePath)
  158. }
  159. })
  160. },
  161. async upLoad(imgPath) {
  162. console.log('然后这里imgPath1111', imgPath)
  163. // this.againEvent()
  164. // return
  165. const url = await this.UpImgResolve(imgPath, this.uploadUrl)
  166. this.faceGetResultEvent(url)
  167. console.log('然后这里imgPath', url, '9d1b7998-4949-46c7-a39f-726aad966664')
  168. // 然后这里imgPath 传过来的是 要上传的临时本地图片的路径
  169. // 具体上传方法根据自己的请求方式 请求自己的接口
  170. },
  171. UpImgResolve(file, url) {
  172. let that = this
  173. let token = 'Bearer '+ this.$store.state.user.vuex_loginInfo.accessToken
  174. let _this = this
  175. let timer = new Date() * 1
  176. // 处理接口超时异常
  177. setTimeout((res => {
  178. console.log('3122223')
  179. uni.hideLoading();
  180. that.againEvent()
  181. }), 10000)
  182. return new Promise(function(resolve, reject) {
  183. uni.uploadFile({
  184. url: _url + 'app-api/infra/file/upload',//接口
  185. filePath: file,//要上传的图片的本地路径
  186. name: 'file',
  187. formData: {
  188. path: 'complain/'+ uni.$u.date(timer, 'yyyy-mm-dd')+timer,
  189. type: 1,
  190. fileSuffix: "png"
  191. },
  192. header: {
  193. Authorization: token,
  194. 'tenant-id': 1
  195. },
  196. success: function(uploadFileRes) {
  197. if (!uploadFileRes.data) return that.againEvent()
  198. const urlJosn = JSON.parse(uploadFileRes.data)
  199. console.log('拿到照片地址---',urlJosn.data)
  200. resolve(urlJosn.data)
  201. },
  202. error(res) {
  203. console.log('313', res)
  204. uni.hideLoading();
  205. that.againEvent()
  206. },
  207. catach(res) {
  208. console.log('3122223', res)
  209. uni.hideLoading();
  210. that.againEvent()
  211. }
  212. });
  213. })
  214. },
  215. faceGetResultEvent(url) {
  216. let that = this
  217. console.log('走进核身---',url)
  218. uni.request({
  219. url: that.faceGetResult,
  220. method: "post",
  221. data: {
  222. 'livenessType': 'SILENT',
  223. 'type': 3,
  224. 'imageUrl': url,
  225. 'identityType':2,
  226. 'url':url
  227. },
  228. header: {
  229. 'token': uni.getStorageSync('accessToken')
  230. },
  231. success: (res) => {
  232. uni.hideLoading()
  233. console.log('走进核身返回数据---',res )
  234. if (res.data.code != 200) {
  235. uni.showToast({
  236. title: '核身失败',
  237. icon: "none",
  238. mask: true,
  239. })
  240. that.againEvent()
  241. return
  242. } else {
  243. that.$u.route({
  244. type: 'tab',
  245. url: '/pages/tabbar/index/index'
  246. });
  247. }
  248. console.log('res', 1111, res)
  249. },
  250. fail:(res)=>{
  251. console.log('核身失败原因---', res)
  252. }
  253. })
  254. },
  255. // 失败后重新拉起人脸识别
  256. againEvent() {
  257. uni.hideLoading()
  258. this.count++
  259. if (this.count >= 5) {
  260. uni.showToast({
  261. title: '核身失败',
  262. icon: "none",
  263. mask: true,
  264. })
  265. return uni.navigateBack()
  266. }
  267. setTimeout((res => {
  268. this.isload = false
  269. this.tempImg = false
  270. this.isAuthCamera = true
  271. this.devicePosition = true
  272. }), 1500)
  273. }
  274. }
  275. }
  276. </script>
  277. <style lang="scss" scoped>
  278. page {
  279. background: #fff;
  280. }
  281. .page-content {
  282. width: 100%;
  283. height: 100%;
  284. .containerV {
  285. width: 100%;
  286. height: 100%;
  287. .headerV {
  288. .top-tips1 {
  289. margin-top: 60rpx;
  290. color: #1C1C1C;
  291. font-size: 36rpx;
  292. text-align: center;
  293. }
  294. .top-tips2 {
  295. margin-top: 20rpx;
  296. color: #00AAFF;
  297. font-size: 28rpx;
  298. text-align: center;
  299. }
  300. }
  301. .contentVLine {
  302. // border-radius:50%;
  303. // width: 450rpx;
  304. // height: 450rpx;
  305. // overflow: hidden;
  306. // margin: 0 auto;
  307. // border:100rpx solid #fff;
  308. // position: absolute;
  309. // left: 0;
  310. // top: 0;
  311. // z-index: 99;
  312. }
  313. .contentV {
  314. position: relative;
  315. display: flex;
  316. flex-direction: column;
  317. align-items: center;
  318. justify-content: center;
  319. margin-top: 30rpx;
  320. width: 450rpx;
  321. height: 450rpx;
  322. margin: 80rpx auto;
  323. border-radius: 50%;
  324. border: 20px solid #eee;
  325. border-radius: 50%;
  326. .tipV {
  327. bottom: 30rpx;
  328. position: absolute;
  329. line-height: 90rpx;
  330. padding-left: 24rpx;
  331. padding-right: 24rpx;
  332. max-width: calc(100vw - 50rpx * 2);
  333. text-align: center;
  334. font-size: 30rpx;
  335. background: #000000;
  336. opacity: 0.75;
  337. color: #FFFFFF;
  338. border-radius: 16rpx;
  339. overflow: hidden;
  340. white-space: nowrap;
  341. text-overflow: ellipsis;
  342. z-index: 5;
  343. }
  344. .camera {
  345. width: 450rpx !important;
  346. height: 450rpx !important;
  347. border-radius: 50% !important;
  348. overflow: hidden;
  349. position: absolute;
  350. left: -38rpx;
  351. top: -38rpx;
  352. z-index: -1;
  353. }
  354. .mark {
  355. position: absolute;
  356. left: 0;
  357. top: 0;
  358. z-index: 2;
  359. width: 100%;
  360. height: 100%;
  361. // background:deeppink;
  362. background-size: 750rpx 661rpx;
  363. }
  364. image {
  365. position: absolute;
  366. width: 450rpx;
  367. height: 450rpx;
  368. z-index: 3;
  369. border-radius: 50%;
  370. }
  371. }
  372. .footerV {
  373. width: 100%;
  374. display: flex;
  375. flex-direction: row;
  376. align-items: center;
  377. justify-content: center;
  378. .privacyV {
  379. padding-top: 30rpx;
  380. display: flex;
  381. flex-direction: row;
  382. align-items: center;
  383. justify-content: center;
  384. .text {
  385. font-size: 30rpx;
  386. color: #1C1C1C;
  387. text-align: center;
  388. line-height: 42rpx;
  389. margin-left: 15rpx;
  390. text {
  391. font-size: 30rpx;
  392. color: #00AAFF;
  393. text-align: center;
  394. line-height: 42rpx;
  395. }
  396. }
  397. .icon {
  398. width: 40rpx;
  399. height: 47rpx;
  400. background: green;
  401. background-size: 100% auto;
  402. }
  403. }
  404. .bottom-tips-2 {
  405. margin-top: 20rpx;
  406. color: #999999;
  407. text-align: center;
  408. font-size: 26rpx;
  409. }
  410. .take-photo-bgV {
  411. width: 100%;
  412. margin-top: 30rpx;
  413. display: flex;
  414. flex-direction: row;
  415. align-items: center;
  416. justify-content: center;
  417. .btn-take-photo {
  418. margin: 0rpx 80rpx 0rpx 80rpx;
  419. width: 196rpx;
  420. height: 196rpx;
  421. background: yellow;
  422. background-size: 100% auto;
  423. }
  424. .btn-change-upload {
  425. left: 130rpx;
  426. width: 80rpx;
  427. height: 80rpx;
  428. background: blue;
  429. background-size: 100% auto;
  430. }
  431. .btn-change-camera {
  432. right: 130rpx;
  433. width: 80rpx;
  434. height: 80rpx;
  435. background: red;
  436. background-size: 100% auto;
  437. }
  438. }
  439. .confirmV {
  440. margin: 200rpx 100rpx 0rpx 100rpx;
  441. display: flex;
  442. flex-direction: row;
  443. align-items: center;
  444. justify-content: space-between;
  445. .btn-cancel {
  446. font-size: 32rpx;
  447. color: #1C1C1C;
  448. }
  449. .btn-ok {
  450. font-size: 32rpx;
  451. color: #00AAFF;
  452. }
  453. }
  454. }
  455. }
  456. }
  457. </style>