【PT.SCC实名制管理系统】24.10.01 -30天,考勤打卡小程序
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.

480 lines
10 KiB

11 months ago
10 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
10 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
10 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
10 months ago
9 months ago
10 months ago
  1. <!-- 人脸识别 -->
  2. <template>
  3. <view class="human-face">
  4. <!-- 自定义导航栏 -->
  5. <uni-nav-bar dark :fixed="true" shadow background-color="var(--main-color)" status-bar left-icon="left"
  6. title="人脸识别" @clickLeft="$utils.navigateBack" />
  7. <!-- 人脸识别说明标题 -->
  8. <view class="human-face-title">为保证本人操作请进行人脸验证</view>
  9. <!-- 人脸识别图片 -->
  10. <view class="human-face-img">
  11. <image v-if="!isPhoto"
  12. src="../static/1.png" mode="widthFix">
  13. </image>
  14. <image v-else-if="!!tempImg"
  15. :src="tempImg" mode="widthFix">
  16. </image>
  17. <camera
  18. v-else
  19. :device-position="devicePosition ?'front': 'back'"
  20. class="camera"
  21. flash="off"
  22. resolution='high' />
  23. </view>
  24. <!-- 人脸识别说明 -->
  25. <view class="human-face-desc"
  26. v-if="!isPhoto">
  27. 人脸认证仅能由<text class="name">{{ userInfo.auth.name }}</text>本人完成验证时请将镜头对准您的脸部
  28. </view>
  29. <view class="human-face-desc"
  30. v-else>
  31. {{ tipsText }}
  32. </view>
  33. <!-- 说明 -->
  34. <view class="desc">
  35. <view class="desc-item">
  36. <image src="../static/2.png"
  37. mode="widthFix"></image>
  38. <view class="text">避免遮挡</view>
  39. </view>
  40. <view class="desc-item">
  41. <image src="../static/3.png"
  42. mode="widthFix"></image>
  43. <view class="text">光线充足</view>
  44. </view>
  45. <view class="desc-item">
  46. <image src="../static/4.png"
  47. mode="widthFix"></image>
  48. <view class="text">正对充足</view>
  49. </view>
  50. </view>
  51. <!-- 实名认证 -->
  52. <view class="btn"
  53. @click="handleTakePhotoClick"
  54. v-if="tipsText == successText && type == 'auth'">
  55. 立即核验
  56. </view>
  57. <!-- 人脸核验 -->
  58. <view class="btn"
  59. v-else-if="tipsText == successText && !isVerifyFace"
  60. @click="handleTakePhotoClick">
  61. 立即核验
  62. </view>
  63. <view class="btn"
  64. v-else-if="tipsText == successText">
  65. <uv-upload
  66. multiple
  67. :maxCount="1"
  68. capture="camera"
  69. height="180rpx"
  70. @afterRead="afterRead">
  71. <view style="display: flex;justify-content: center;
  72. width: 600rpx;">
  73. 拍照打卡
  74. </view>
  75. </uv-upload>
  76. </view>
  77. <uv-popup ref="successPopup" :round="30"
  78. >
  79. <view class="successPopup">
  80. <image src="/static/image/success.png"
  81. style="width: 320rpx;"
  82. mode="widthFix"></image>
  83. <view class="text">
  84. 核验成功
  85. </view>
  86. <view class="btn">
  87. <uv-upload
  88. multiple
  89. :maxCount="1"
  90. capture="camera"
  91. height="180rpx"
  92. @afterRead="afterRead">
  93. <view style="display: flex;justify-content: center;
  94. width: 600rpx;">
  95. 拍照打卡
  96. </view>
  97. </uv-upload>
  98. </view>
  99. </view>
  100. </uv-popup>
  101. </view>
  102. </template>
  103. <script>
  104. import position from '@/utils/position.js'
  105. import {
  106. mapState
  107. } from 'vuex'
  108. export default {
  109. name: "HumanFace",
  110. data() {
  111. return {
  112. isPhoto: false,
  113. form : {
  114. pic : '',
  115. },
  116. isLocationSubmit : false,//定位完成时提交
  117. tipsText: '', // 错误文案提示
  118. tempImg: '', // 本地图片路径
  119. cameraEngine: null, // 相机引擎
  120. devicePosition: true, // 摄像头朝向
  121. isAuthCamera: true, // 是否拥有相机权限
  122. type : '',
  123. isVerifyFace : false,//人脸核验是否成功
  124. successText : '请保持不动',//成功文案
  125. }
  126. },
  127. computed: {
  128. ...mapState(['teamList', 'userInfo', 'authInfo']),
  129. },
  130. onLoad(args) {
  131. if(args.type){
  132. this.type = args.type
  133. }
  134. this.initData()
  135. },
  136. onShow() {
  137. let self = this
  138. position.getLocationDetail()
  139. .then(res => {
  140. console.log(res);
  141. self.form.lat = res.latitude
  142. self.form.lon = res.longitude
  143. self.form.address = res.address
  144. if(self.isLocationSubmit){
  145. self.submit()
  146. }
  147. })
  148. },
  149. methods: {
  150. afterRead(e) {
  151. let self = this
  152. e.file.forEach(file => {
  153. self.$Oss.ossUpload(file.url).then(url => {
  154. self.form.pic = url
  155. if(self.form.lat){
  156. self.submit()
  157. }else{
  158. uni.showLoading({
  159. title: '定位中...'
  160. })
  161. self.isLocationSubmit = true
  162. }
  163. })
  164. })
  165. },
  166. async submit(){
  167. if (this.$utils.verificationAll(this.form, {
  168. lat : '经纬度缺失,请打开GPS',
  169. lon : '经纬度缺失,请打开GPS',
  170. address : '获取地址失败',
  171. pic : '请拍照',
  172. })) {
  173. return
  174. }
  175. this.$api('clock', this.form, res => {
  176. if(res.code == 200){
  177. uni.showToast({
  178. title: '打卡成功',
  179. icon:'none'
  180. })
  181. setTimeout(uni.navigateBack, 1000, -1)
  182. }else{
  183. this.$nextTick(() => {
  184. this.form.image = ''
  185. this.tempImg = ''
  186. })
  187. }
  188. })
  189. },
  190. // 初始化相机引擎
  191. initData() {
  192. let createTime = new Date().getTime()
  193. let num = 0
  194. // #ifdef MP-WEIXIN
  195. // 1、初始化人脸识别
  196. wx.initFaceDetect()
  197. // 2、创建 camera 上下文 CameraContext 对象
  198. this.cameraEngine = wx.createCameraContext()
  199. // 3、获取 Camera 实时帧数据
  200. const listener = this.cameraEngine.onCameraFrame((frame) => {
  201. if (this.tempImg) {
  202. return;
  203. }
  204. // 4、人脸识别,使用前需要通过 wx.initFaceDetect 进行一次初始化,推荐使用相机接口返回的帧数据
  205. wx.faceDetect({
  206. frameBuffer: frame.data,
  207. width: frame.width,
  208. height: frame.height,
  209. enablePoint: true,
  210. enableConf: true,
  211. enableAngle: true,
  212. enableMultiFace: true,
  213. success: (faceData) => {
  214. let face = faceData.faceInfo[0]
  215. if (faceData.x == -1 || faceData.y == -1) {
  216. this.tipsText = '检测不到人'
  217. }
  218. if (faceData.faceInfo.length > 1) {
  219. this.tipsText = '请保证只有一个人'
  220. } else {
  221. const {
  222. pitch,
  223. roll,
  224. yaw
  225. } = face.angleArray;
  226. const standard = 0.5
  227. let faseP = 0.98
  228. if (Math.abs(pitch) >= standard || Math.abs(roll) >= standard ||
  229. Math.abs(yaw) >= standard) {
  230. this.tipsText = '请平视摄像头'
  231. } else if (face.confArray.global <= faseP || face.confArray.leftEye <=
  232. faseP || face.confArray.mouth <= faseP || face.confArray.nose <= faseP ||
  233. face.confArray.rightEye <= faseP) {
  234. this.tipsText = '请勿遮挡五官'
  235. } else {
  236. this.tipsText = this.successText
  237. // 这里可以写自己的逻辑了
  238. // this.handleTakePhotoClick()
  239. // if(num > 2){
  240. // uni.showToast({
  241. // title: '人脸核验'
  242. // })
  243. // return
  244. // }
  245. // if(createTime - new Date().getTime() > 2000){
  246. // }
  247. }
  248. }
  249. },
  250. fail: (err) => {
  251. if (err.x == -1 || err.y == -1) {
  252. this.tipsText = '检测不到人'
  253. } else {
  254. this.tipsText = err.errMsg || '网络错误,请退出页面重试'
  255. }
  256. },
  257. })
  258. })
  259. // 5、开始监听帧数据
  260. listener.start()
  261. this.isPhoto = true
  262. // #endif
  263. },
  264. // 拍照
  265. handleTakePhotoClick() {
  266. if (this.tipsText != ""
  267. && this.tipsText != this.successText
  268. && !this.tempImg) {
  269. return;
  270. }
  271. uni.getSetting({
  272. success: (res) => {
  273. if (!res.authSetting['scope.camera']) {
  274. this.isAuthCamera = false
  275. uni.openSetting({
  276. success: (res) => {
  277. if (res.authSetting['scope.camera']) {
  278. this.isAuthCamera = true;
  279. }
  280. }
  281. })
  282. }
  283. }
  284. })
  285. this.cameraEngine.takePhoto({
  286. quality: "high",
  287. success: ({
  288. tempImagePath
  289. }) => {
  290. this.tempImg = tempImagePath
  291. // 上传图片
  292. this.$Oss.ossUpload(tempImagePath).then(url => {
  293. // 实名认证,上传人脸
  294. if(this.type == 'auth'){
  295. let form = {
  296. ...this.authInfo,
  297. pic : url
  298. }
  299. this.$api('authApply', form, res => {
  300. if(res.code == 200){
  301. setTimeout(uni.reLaunch, 1000, {
  302. url: '/pages/index/index'
  303. })
  304. }else{
  305. setTimeout(uni.navigateBack, 1000, -1)
  306. }
  307. })
  308. return
  309. }
  310. this.form.image = url
  311. if(!this.isVerifyFace){
  312. this.clockVerifyFace()
  313. }
  314. })
  315. }
  316. })
  317. },
  318. // 人脸核验
  319. clockVerifyFace(){
  320. this.$api('clockVerifyFace',
  321. this.form, res => {
  322. if(res.code == 200){
  323. // uni.showToast({
  324. // title: '核验成功!',
  325. // icon: 'icon'
  326. // })
  327. this.isVerifyFace = true
  328. this.$refs.successPopup.open()
  329. }else{
  330. this.tempImg = ''
  331. this.form.pic = ''
  332. }
  333. })
  334. },
  335. }
  336. }
  337. </script>
  338. <style lang="scss" scoped>
  339. .human-face {
  340. min-height: 100vh;
  341. background: white;
  342. .successPopup{
  343. display: flex;
  344. align-items: center;
  345. flex-direction: column;
  346. justify-content: center;
  347. width: 520rpx;
  348. color: #20D76D;
  349. gap: 50rpx;
  350. padding: 40rpx;
  351. .text{
  352. font-weight: 900;
  353. font-size: 40rpx;
  354. }
  355. }
  356. // 人脸识别说明标题
  357. .human-face-title {
  358. font-size: 35rpx;
  359. font-weight: bold;
  360. margin: 60rpx 0rpx;
  361. text-align: center;
  362. }
  363. // 人脸识别图片
  364. .human-face-img {
  365. display: flex;
  366. justify-content: center;
  367. image {
  368. width: 500rpx;
  369. border-radius: 50%;
  370. }
  371. .camera{
  372. width: 500rpx;
  373. height: 500rpx;
  374. border-radius: 50%;
  375. }
  376. }
  377. // 人脸识别说明
  378. .human-face-desc {
  379. color: #707070;
  380. width: 55%;
  381. font-size: 30rpx;
  382. margin: 30rpx auto;
  383. text-align: center;
  384. .name {
  385. color: $main-color;
  386. }
  387. }
  388. // 说明
  389. .desc {
  390. width: 80%;
  391. display: flex;
  392. flex-wrap: wrap;
  393. margin: 100rpx auto;
  394. .desc-item {
  395. width: 33.33%;
  396. display: flex;
  397. flex-direction: column;
  398. align-items: center;
  399. justify-content: center;
  400. image {
  401. width: 50%;
  402. }
  403. .text {
  404. font-size: 30rpx;
  405. color: #707070;
  406. margin-top: 10rpx;
  407. }
  408. }
  409. }
  410. // 立即核验
  411. .btn {
  412. display: flex;
  413. align-items: center;
  414. justify-content: center;
  415. width: 83%;
  416. background: $main-color;
  417. color: white;
  418. height: 100rpx;
  419. border-radius: 50rpx;
  420. margin: 0rpx auto;
  421. }
  422. }
  423. </style>