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

377 lines
8.6 KiB

7 months ago
7 months ago
6 months ago
6 months ago
7 months ago
6 months ago
7 months ago
7 months ago
7 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
7 months ago
7 months ago
6 months ago
7 months ago
6 months ago
7 months ago
7 months ago
6 months ago
7 months ago
6 months ago
7 months ago
6 months ago
7 months ago
7 months ago
6 months ago
7 months ago
7 months ago
6 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
6 months ago
6 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="https://tennis-oss.xzaiyp.top/2024-10-22/508376c5-64b2-472f-8e25-a2469b632a39.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 }}{{ tempImg }}
  32. </view>
  33. <!-- 说明 -->
  34. <view class="desc">
  35. <view class="desc-item">
  36. <image src="https://tennis-oss.xzaiyp.top/2024-10-22/a0bf2da9-c25a-4d5c-8c77-7318bd86227d.png"
  37. mode="widthFix"></image>
  38. <view class="text">避免遮挡</view>
  39. </view>
  40. <view class="desc-item">
  41. <image src="https://tennis-oss.xzaiyp.top/2024-10-22/feb72bfb-8271-4e48-8081-d72811543918.png"
  42. mode="widthFix"></image>
  43. <view class="text">光线充足</view>
  44. </view>
  45. <view class="desc-item">
  46. <image src="https://tennis-oss.xzaiyp.top/2024-10-22/5fd8a6a7-a13e-44d6-acf1-a7ada94c699d.png"
  47. mode="widthFix"></image>
  48. <view class="text">正对充足</view>
  49. </view>
  50. </view>
  51. <div class="btn"
  52. @click="handleTakePhotoClick"
  53. v-if="tipsText == '请拍照'">
  54. <uv-upload
  55. multiple
  56. :maxCount="1"
  57. capture="camera"
  58. height="180rpx"
  59. @afterRead="afterRead">
  60. <view style="display: flex;justify-content: center;
  61. width: 600rpx;">
  62. 立即核验
  63. </view>
  64. </uv-upload>
  65. </div>
  66. </view>
  67. </template>
  68. <script>
  69. import position from '@/utils/position.js'
  70. import {
  71. mapState
  72. } from 'vuex'
  73. export default {
  74. name: "HumanFace",
  75. data() {
  76. return {
  77. isPhoto: false,
  78. form : {
  79. pic : '',
  80. },
  81. isLocationSubmit : false,//定位完成时提交
  82. tipsText: '', // 错误文案提示
  83. tempImg: '', // 本地图片路径
  84. cameraEngine: null, // 相机引擎
  85. devicePosition: true, // 摄像头朝向
  86. isAuthCamera: true, // 是否拥有相机权限
  87. }
  88. },
  89. computed: {
  90. ...mapState(['teamList', 'userInfo']),
  91. },
  92. onLoad() {
  93. this.initData()
  94. },
  95. onShow() {
  96. let self = this
  97. position.getLocationDetail()
  98. .then(res => {
  99. console.log(res);
  100. self.form.lat = res.position.latitude
  101. self.form.lon = res.position.longitude
  102. self.form.address = res.address
  103. if(self.isLocationSubmit){
  104. self.submit()
  105. }
  106. })
  107. },
  108. methods: {
  109. afterRead(e) {
  110. let self = this
  111. e.file.forEach(file => {
  112. self.$Oss.ossUpload(file.url).then(url => {
  113. self.form.pic = url
  114. if(self.form.lat){
  115. self.submit()
  116. }else{
  117. uni.showLoading({
  118. title: '定位中...'
  119. })
  120. self.isLocationSubmit = true
  121. }
  122. })
  123. })
  124. },
  125. // 人脸认证通过后拍照
  126. photo() {
  127. let self = this
  128. uni.chooseImage({
  129. count: 1, //默认9
  130. sizeType: ['compressed'], //可以指定是原图还是压缩图,默认二者都有
  131. // sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
  132. sourceType: ['camera '], //这要注意,camera掉拍照,album是打开手机相册
  133. success: function(res) {
  134. console.log(JSON.stringify(res.tempFilePaths));
  135. //后续在这里上传文件
  136. self.$Oss.ossUpload(res.tempFilePaths[0]).then(url => {
  137. self.form.pic = url
  138. if(self.form.lat){
  139. self.submit()
  140. }else{
  141. uni.showLoading({
  142. title: '定位中...'
  143. })
  144. self.isLocationSubmit = true
  145. }
  146. })
  147. // uni.navigateTo({
  148. // url: "/pages/subPack/punchCard/punchCard"
  149. // })
  150. }
  151. });
  152. },
  153. async submit(){
  154. if (this.$utils.verificationAll(this.form, {
  155. lat : '经纬度缺失,请打开GPS',
  156. lon : '经纬度缺失,请打开GPS',
  157. address : '获取地址失败',
  158. pic : '请拍照',
  159. })) {
  160. return
  161. }
  162. this.$api('clock', this.form, res => {
  163. if(res.code == 200){
  164. uni.showToast({
  165. title: '打卡成功',
  166. icon:'none'
  167. })
  168. setTimeout(uni.navigateBack, 1000, -1)
  169. }
  170. })
  171. },
  172. // 初始化相机引擎
  173. initData() {
  174. // #ifdef MP-WEIXIN
  175. // 1、初始化人脸识别
  176. wx.initFaceDetect()
  177. // 2、创建 camera 上下文 CameraContext 对象
  178. this.cameraEngine = wx.createCameraContext()
  179. // 3、获取 Camera 实时帧数据
  180. const listener = this.cameraEngine.onCameraFrame((frame) => {
  181. if (this.tempImg) {
  182. return;
  183. }
  184. // 4、人脸识别,使用前需要通过 wx.initFaceDetect 进行一次初始化,推荐使用相机接口返回的帧数据
  185. wx.faceDetect({
  186. frameBuffer: frame.data,
  187. width: frame.width,
  188. height: frame.height,
  189. enablePoint: true,
  190. enableConf: true,
  191. enableAngle: true,
  192. enableMultiFace: true,
  193. success: (faceData) => {
  194. let face = faceData.faceInfo[0]
  195. if (faceData.x == -1 || faceData.y == -1) {
  196. this.tipsText = '检测不到人'
  197. }
  198. if (faceData.faceInfo.length > 1) {
  199. this.tipsText = '请保证只有一个人'
  200. } else {
  201. const {
  202. pitch,
  203. roll,
  204. yaw
  205. } = face.angleArray;
  206. const standard = 0.5
  207. if (Math.abs(pitch) >= standard || Math.abs(roll) >= standard ||
  208. Math.abs(yaw) >= standard) {
  209. this.tipsText = '请平视摄像头'
  210. } else if (face.confArray.global <= 0.8 || face.confArray.leftEye <=
  211. 0.8 || face.confArray.mouth <= 0.8 || face.confArray.nose <= 0.8 ||
  212. face.confArray.rightEye <= 0.8) {
  213. this.tipsText = '请勿遮挡五官'
  214. } else {
  215. this.tipsText = '请拍照'
  216. // 这里可以写自己的逻辑了
  217. // this.handleTakePhotoClick()
  218. }
  219. }
  220. },
  221. fail: (err) => {
  222. if (err.x == -1 || err.y == -1) {
  223. this.tipsText = '检测不到人'
  224. } else {
  225. this.tipsText = err.errMsg || '网络错误,请退出页面重试'
  226. }
  227. },
  228. })
  229. })
  230. // 5、开始监听帧数据
  231. listener.start()
  232. this.isPhoto = true
  233. // #endif
  234. },
  235. // 拍照
  236. handleTakePhotoClick() {
  237. if (this.tipsText != ""
  238. && this.tipsText != "请拍照"
  239. && !this.tempImg) {
  240. return;
  241. }
  242. uni.getSetting({
  243. success: (res) => {
  244. if (!res.authSetting['scope.camera']) {
  245. this.isAuthCamera = false
  246. uni.openSetting({
  247. success: (res) => {
  248. if (res.authSetting['scope.camera']) {
  249. this.isAuthCamera = true;
  250. }
  251. }
  252. })
  253. }
  254. }
  255. })
  256. this.cameraEngine.takePhoto({
  257. quality: "high",
  258. success: ({
  259. tempImagePath
  260. }) => {
  261. this.tempImg = tempImagePath
  262. console.log("=======tempImg:", this.tempImg)
  263. }
  264. })
  265. },
  266. }
  267. }
  268. </script>
  269. <style lang="scss" scoped>
  270. .human-face {
  271. min-height: 100vh;
  272. background: white;
  273. // 人脸识别说明标题
  274. .human-face-title {
  275. font-size: 35rpx;
  276. font-weight: bold;
  277. margin: 60rpx 0rpx;
  278. text-align: center;
  279. }
  280. // 人脸识别图片
  281. .human-face-img {
  282. display: flex;
  283. justify-content: center;
  284. image {
  285. width: 500rpx;
  286. border-radius: 50%;
  287. }
  288. .camera{
  289. width: 500rpx;
  290. height: 500rpx;
  291. border-radius: 50%;
  292. }
  293. }
  294. // 人脸识别说明
  295. .human-face-desc {
  296. color: #707070;
  297. width: 55%;
  298. font-size: 30rpx;
  299. margin: 30rpx auto;
  300. text-align: center;
  301. .name {
  302. color: $main-color;
  303. }
  304. }
  305. // 说明
  306. .desc {
  307. width: 80%;
  308. display: flex;
  309. flex-wrap: wrap;
  310. margin: 100rpx auto;
  311. .desc-item {
  312. width: 33.33%;
  313. display: flex;
  314. flex-direction: column;
  315. align-items: center;
  316. justify-content: center;
  317. image {
  318. width: 50%;
  319. }
  320. .text {
  321. font-size: 30rpx;
  322. color: #707070;
  323. margin-top: 10rpx;
  324. }
  325. }
  326. }
  327. // 立即核验
  328. .btn {
  329. display: flex;
  330. align-items: center;
  331. justify-content: center;
  332. width: 83%;
  333. background: $main-color;
  334. color: white;
  335. height: 100rpx;
  336. border-radius: 50rpx;
  337. margin: 0rpx auto;
  338. }
  339. }
  340. </style>