【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.

499 lines
10 KiB

1 year ago
1 year ago
10 months ago
1 year ago
11 months ago
11 months ago
1 year ago
11 months ago
8 months ago
11 months ago
10 months ago
1 year ago
10 months ago
1 year ago
10 months ago
1 year ago
11 months ago
11 months ago
11 months ago
10 months ago
11 months ago
11 months ago
10 months ago
11 months ago
10 months ago
10 months ago
11 months ago
11 months ago
10 months ago
11 months ago
11 months ago
10 months ago
11 months ago
1 year ago
1 year ago
11 months ago
11 months ago
10 months ago
1 year ago
11 months ago
1 year ago
11 months ago
11 months ago
1 year ago
10 months ago
11 months ago
11 months ago
1 year ago
11 months ago
11 months ago
9 months ago
1 year ago
9 months ago
1 year ago
11 months ago
1 year ago
11 months ago
11 months ago
1 year ago
11 months ago
10 months ago
11 months ago
10 months ago
11 months ago
10 months ago
8 months ago
10 months ago
11 months ago
10 months ago
11 months ago
10 months ago
11 months ago
10 months ago
11 months ago
8 months ago
11 months ago
8 months ago
11 months ago
8 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
10 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
9 months ago
9 months ago
9 months ago
10 months ago
10 months ago
11 months ago
10 months ago
11 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. <!-- tipsText == successText -->
  52. <!-- 实名认证 -->
  53. <view class="btn"
  54. @click="handleTakePhotoClick"
  55. v-if="type == 'auth'">
  56. 立即核验
  57. </view>
  58. <!-- 人脸核验 -->
  59. <view class="btn"
  60. v-else-if="!isVerifyFace"
  61. @click="handleTakePhotoClick">
  62. 立即核验
  63. </view>
  64. <!-- <view class="btn">
  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. this.form.pic = ''
  168. if (this.$utils.verificationAll(this.form, {
  169. lat : '经纬度缺失,请打开GPS',
  170. lon : '经纬度缺失,请打开GPS',
  171. address : '获取地址失败',
  172. // pic : '请拍照',
  173. })) {
  174. return
  175. }
  176. this.$api('clock', this.form, res => {
  177. if(res.code == 200){
  178. uni.showToast({
  179. title: '打卡成功',
  180. icon:'none'
  181. })
  182. setTimeout(uni.navigateBack, 1000, -1)
  183. }else{
  184. this.$nextTick(() => {
  185. this.form.image = ''
  186. this.tempImg = ''
  187. })
  188. }
  189. })
  190. },
  191. // 初始化相机引擎
  192. initData() {
  193. let createTime = new Date().getTime()
  194. let num = 0
  195. // #ifdef MP-WEIXIN
  196. // 1、初始化人脸识别
  197. wx.initFaceDetect()
  198. // 2、创建 camera 上下文 CameraContext 对象
  199. this.cameraEngine = wx.createCameraContext()
  200. // 3、获取 Camera 实时帧数据
  201. const listener = this.cameraEngine.onCameraFrame((frame) => {
  202. if (this.tempImg) {
  203. return;
  204. }
  205. // 4、人脸识别,使用前需要通过 wx.initFaceDetect 进行一次初始化,推荐使用相机接口返回的帧数据
  206. wx.faceDetect({
  207. frameBuffer: frame.data,
  208. width: frame.width,
  209. height: frame.height,
  210. enablePoint: true,
  211. enableConf: true,
  212. enableAngle: true,
  213. enableMultiFace: true,
  214. success: (faceData) => {
  215. let face = faceData.faceInfo[0]
  216. if (faceData.x == -1 || faceData.y == -1) {
  217. this.tipsText = '检测不到人'
  218. }
  219. if (faceData.faceInfo.length > 1) {
  220. this.tipsText = '请保证只有一个人'
  221. } else {
  222. const {
  223. pitch,
  224. roll,
  225. yaw
  226. } = face.angleArray;
  227. const standard = 0.5
  228. let faseP = 0.8
  229. if (Math.abs(pitch) >= standard || Math.abs(roll) >= standard ||
  230. Math.abs(yaw) >= standard) {
  231. this.tipsText = '请平视摄像头'
  232. } else if (face.confArray.global <= faseP || face.confArray.leftEye <=
  233. faseP || face.confArray.mouth <= faseP || face.confArray.nose <= faseP ||
  234. face.confArray.rightEye <= faseP) {
  235. this.tipsText = '请勿遮挡五官'
  236. } else {
  237. this.tipsText = this.successText
  238. // 这里可以写自己的逻辑了
  239. // this.handleTakePhotoClick()
  240. // if(num > 2){
  241. // uni.showToast({
  242. // title: '人脸核验'
  243. // })
  244. // return
  245. // }
  246. // if(createTime - new Date().getTime() > 2000){
  247. // }
  248. }
  249. }
  250. // 不管是否有人脸都是成功
  251. this.tipsText = this.successText
  252. },
  253. fail: (err) => {
  254. if (err.x == -1 || err.y == -1) {
  255. // this.tipsText = '检测不到人'
  256. } else {
  257. this.tipsText = err.errMsg || '网络错误,请退出页面重试'
  258. }
  259. },
  260. })
  261. })
  262. // 5、开始监听帧数据
  263. listener.start()
  264. this.isPhoto = true
  265. // #endif
  266. },
  267. // 拍照 && this.tipsText != this.successTextthis.tipsText != ""
  268. handleTakePhotoClick() {
  269. console.log('handleTakePhotoClick');
  270. if (this.tempImg) {
  271. console.log('handleTakePhotoClick return', this.tempImg);
  272. return;
  273. }
  274. uni.showLoading({
  275. title: '打卡中...'
  276. })
  277. uni.getSetting({
  278. success: (res) => {
  279. if (!res.authSetting['scope.camera']) {
  280. this.isAuthCamera = false
  281. uni.openSetting({
  282. success: (res) => {
  283. if (res.authSetting['scope.camera']) {
  284. this.isAuthCamera = true;
  285. }
  286. }
  287. })
  288. }
  289. }
  290. })
  291. this.cameraEngine.takePhoto({
  292. quality: "high",
  293. success: ({
  294. tempImagePath
  295. }) => {
  296. this.tempImg = tempImagePath
  297. // 上传图片
  298. this.$Oss.ossUpload(tempImagePath).then(url => {
  299. // 实名认证,上传人脸
  300. if(this.type == 'auth'){
  301. let form = {
  302. ...this.authInfo,
  303. pic : url
  304. }
  305. this.$api('authApply', form, res => {
  306. if(res.code == 200){
  307. setTimeout(uni.reLaunch, 1000, {
  308. url: '/pages/index/index'
  309. })
  310. }else{
  311. setTimeout(uni.navigateBack, 1000, -1)
  312. }
  313. })
  314. return
  315. }
  316. this.form.image = url
  317. if(!this.isVerifyFace){
  318. this.clockVerifyFace()
  319. }
  320. })
  321. }
  322. })
  323. },
  324. // 人脸核验
  325. clockVerifyFace(){
  326. let self = this
  327. this.$api('clockVerifyFace',
  328. this.form, res => {
  329. if(res.code == 200){
  330. // uni.showToast({
  331. // title: '核验成功!',
  332. // icon: 'icon'
  333. // })
  334. this.isVerifyFace = true
  335. // this.$refs.successPopup.open()
  336. if(self.form.lat){
  337. self.submit()
  338. }else{
  339. uni.showLoading({
  340. title: '定位中...'
  341. })
  342. self.isLocationSubmit = true
  343. }
  344. }else{
  345. this.tempImg = ''
  346. this.form.pic = ''
  347. }
  348. })
  349. },
  350. }
  351. }
  352. </script>
  353. <style lang="scss" scoped>
  354. .human-face {
  355. min-height: 100vh;
  356. background: white;
  357. .successPopup{
  358. display: flex;
  359. align-items: center;
  360. flex-direction: column;
  361. justify-content: center;
  362. width: 520rpx;
  363. color: #20D76D;
  364. gap: 50rpx;
  365. padding: 40rpx;
  366. .text{
  367. font-weight: 900;
  368. font-size: 40rpx;
  369. }
  370. }
  371. // 人脸识别说明标题
  372. .human-face-title {
  373. font-size: 35rpx;
  374. font-weight: bold;
  375. margin: 60rpx 0rpx;
  376. text-align: center;
  377. }
  378. // 人脸识别图片
  379. .human-face-img {
  380. display: flex;
  381. justify-content: center;
  382. image {
  383. width: 500rpx;
  384. border-radius: 50%;
  385. }
  386. .camera{
  387. width: 500rpx;
  388. height: 500rpx;
  389. border-radius: 50%;
  390. }
  391. }
  392. // 人脸识别说明
  393. .human-face-desc {
  394. color: #707070;
  395. width: 55%;
  396. font-size: 30rpx;
  397. margin: 30rpx auto;
  398. text-align: center;
  399. .name {
  400. color: $main-color;
  401. }
  402. }
  403. // 说明
  404. .desc {
  405. width: 80%;
  406. display: flex;
  407. flex-wrap: wrap;
  408. margin: 100rpx auto;
  409. .desc-item {
  410. width: 33.33%;
  411. display: flex;
  412. flex-direction: column;
  413. align-items: center;
  414. justify-content: center;
  415. image {
  416. width: 50%;
  417. }
  418. .text {
  419. font-size: 30rpx;
  420. color: #707070;
  421. margin-top: 10rpx;
  422. }
  423. }
  424. }
  425. // 立即核验
  426. .btn {
  427. display: flex;
  428. align-items: center;
  429. justify-content: center;
  430. width: 83%;
  431. background: $main-color;
  432. color: white;
  433. height: 100rpx;
  434. border-radius: 50rpx;
  435. margin: 0rpx auto;
  436. }
  437. }
  438. </style>