authentication.vue 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. <template>
  2. <view class="container">
  3. <view class="notes">
  4. 拍摄您本人人脸,确保对准手机,光线充足
  5. </view>
  6. <view class="msg">
  7. {{name}} {{cardNumber}}
  8. </view>
  9. <view class="info">
  10. <span v-if="tipsText">{{tipsText}}</span>
  11. </view>
  12. <view class="photo">
  13. <camera class="img" v-if='isAuthCamera' device-position="front" flash="off" resolution='high' />
  14. <img v-else :src="tempImg">
  15. </view>
  16. <view class="button" @click="handleTakePhotoClick">
  17. 开启身份认证
  18. </view>
  19. <!-- 认证结果弹窗 -->
  20. <uni-popup ref="popup" :is-mask-click="false">
  21. <view class="popup-content">
  22. <view class="title">
  23. <view class="icon">
  24. <img src="./imgs/success.png">
  25. </view>
  26. <view class="title_info">
  27. 打卡成功
  28. </view>
  29. </view>
  30. <view class="time">
  31. {{nowTime}}
  32. </view>
  33. <view class="popup_button" @click="handleGoHome">
  34. 我知道了
  35. </view>
  36. </view>
  37. </uni-popup>
  38. </view>
  39. </template>
  40. <script>
  41. export default {
  42. data() {
  43. return {
  44. // 错误文案提示
  45. tipsText: '人脸检测初始化失败,请退出重试',
  46. // 当前时间
  47. nowTime: "",
  48. // 本地图片路径 被匹对照片
  49. tempImg: '',
  50. // 场景照片
  51. sceneImage: "",
  52. // 经纬度
  53. lat: "",
  54. lng: "",
  55. // 地址
  56. location: "",
  57. // 规则id
  58. id: "",
  59. // 相机引擎
  60. cameraEngine: null,
  61. // 是否拥有相机权限
  62. isAuthCamera: true,
  63. // 姓名
  64. name: "",
  65. // 身份证号
  66. cardNumber: "",
  67. // 是否需要场景照片,值为1时是不需要
  68. flag: ""
  69. };
  70. },
  71. onLoad(options) {
  72. let userInfo = uni.getStorageSync("userInfo")
  73. if (userInfo) {
  74. this.name = userInfo.name
  75. this.cardNumber = userInfo.cardNumber
  76. }
  77. this.id = options.id
  78. this.lat = options.latitude
  79. this.lng = options.longitude
  80. this.location = options.address
  81. if (options.imgUrl) {
  82. this.sceneImage = options.imgUrl
  83. }
  84. if (options.flag) {
  85. this.flag = options.flag
  86. }
  87. this.initData()
  88. },
  89. methods: {
  90. // 初始化相机引擎
  91. initData() {
  92. // #ifdef MP-WEIXIN
  93. // 1、初始化人脸识别
  94. wx.initFaceDetect()
  95. // 2、创建 camera 上下文 CameraContext 对象
  96. this.cameraEngine = wx.createCameraContext()
  97. // 3、获取 Camera 实时帧数据
  98. const listener = this.cameraEngine.onCameraFrame((frame) => {
  99. if (this.tempImg) {
  100. return;
  101. }
  102. // 4、人脸识别,使用前需要通过 wx.initFaceDetect 进行一次初始化,推荐使用相机接口返回的帧数据
  103. wx.faceDetect({
  104. frameBuffer: frame.data,
  105. width: frame.width,
  106. height: frame.height,
  107. enablePoint: true,
  108. enableConf: true,
  109. enableAngle: true,
  110. enableMultiFace: true,
  111. success: (faceData) => {
  112. let face = faceData.faceInfo[0]
  113. if (faceData.x == -1 || faceData.y == -1) {
  114. this.tipsText = '检测不到人'
  115. }
  116. if (faceData.faceInfo.length > 1) {
  117. this.tipsText = '请保证只有一个人'
  118. } else {
  119. const {
  120. pitch,
  121. roll,
  122. yaw
  123. } = face.angleArray;
  124. const standard = 0.5
  125. if (Math.abs(pitch) >= standard || Math.abs(roll) >= standard ||
  126. Math.abs(yaw) >= standard) {
  127. this.tipsText = '请平视摄像头'
  128. } else if (face.confArray.global <= 0.8 || face.confArray.leftEye <=
  129. 0.8 || face.confArray.mouth <= 0.8 || face.confArray.nose <= 0.8 ||
  130. face.confArray.rightEye <= 0.8) {
  131. this.tipsText = '请勿遮挡五官'
  132. } else {
  133. this.tipsText = '请点击下方按钮开始认证'
  134. // 这里可以写自己的逻辑了
  135. }
  136. }
  137. },
  138. fail: (err) => {
  139. if (err.x == -1 || err.y == -1) {
  140. this.tipsText = '检测不到人'
  141. } else {
  142. this.tipsText = '网络错误,请退出页面重试'
  143. }
  144. },
  145. })
  146. })
  147. // 5、开始监听帧数据
  148. listener.start()
  149. // #endif
  150. },
  151. // 拍照点击
  152. handleTakePhotoClick() {
  153. if (this.tipsText != "" && this.tipsText != "请点击下方按钮开始认证") {
  154. return;
  155. }
  156. uni.getSetting({
  157. success: (res) => {
  158. if (!res.authSetting['scope.camera']) {
  159. this.isAuthCamera = true
  160. uni.openSetting({
  161. success: (res) => {
  162. if (res.authSetting['scope.camera']) {
  163. this.isAuthCamera = true;
  164. }
  165. }
  166. })
  167. }
  168. }
  169. })
  170. this.cameraEngine.takePhoto({
  171. quality: "high",
  172. success: ({
  173. tempImagePath
  174. }) => {
  175. this.tempImg = tempImagePath
  176. this.isAuthCamera = false
  177. this.tipsText = ""
  178. this.handleOkClick()
  179. }
  180. })
  181. },
  182. // 上传
  183. handleOkClick() {
  184. // 这里的 this.tempImg 是经过人脸检测后 拍照拿到的路径
  185. this.upLoadOne(this.tempImg)
  186. },
  187. upLoadOne(url) {
  188. uni.showLoading({
  189. title: "检测中,请稍后...",
  190. });
  191. setTimeout(() => {
  192. uni.uploadFile({
  193. url: `https://chtech.ncjti.edu.cn/campusclock/attendance/api/file/upload`,
  194. filePath: url,
  195. name: 'file',
  196. header: {
  197. 'platform': 2,
  198. 'Accept-Language': 'zh-CN,zh;q=0.9'
  199. },
  200. success: (uploadFileRes) => {
  201. let imgUrl = JSON.parse(uploadFileRes.data)
  202. .data
  203. this.handleUploading(imgUrl)
  204. },
  205. fail: () => {
  206. uni.showToast({
  207. title: "上传失败",
  208. icon: 'error'
  209. })
  210. }
  211. });
  212. }, 1000)
  213. },
  214. async handleUploading(imgUrl) {
  215. let res = await this.$myRequest_clockIn({
  216. url: "/attendance/api/sign/check/in/update",
  217. method: "put",
  218. header: {
  219. 'Authorization': uni.getStorageSync("token"),
  220. 'platform': 2,
  221. 'Accept-Language': 'zh-CN,zh;q=0.9'
  222. },
  223. data: {
  224. id: this.id,
  225. lat: this.lat,
  226. lng: this.lng,
  227. location: this.location,
  228. matchFaceImage: imgUrl,
  229. sceneImage: this.flag == 1 ? "" : this.sceneImage,
  230. }
  231. })
  232. // console.log(res);
  233. if (res.code == 200) {
  234. this.getNowTime()
  235. this.$refs.popup.open()
  236. }
  237. },
  238. // 点击 我知道了按钮 跳回首页
  239. handleGoHome() {
  240. this.$refs.popup.close()
  241. uni.reLaunch({
  242. url: "/pagesClockIn/home/home"
  243. })
  244. },
  245. // 获取当前时间
  246. getNowTime() {
  247. let date = new Date()
  248. let hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
  249. let minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
  250. let seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
  251. this.nowTime = hours + ':' + minutes + ':' + seconds
  252. }
  253. },
  254. }
  255. </script>
  256. <style lang="scss" scoped>
  257. .container {
  258. .notes {
  259. margin-top: 110rpx;
  260. text-align: center;
  261. font-size: 28rpx;
  262. }
  263. .msg {
  264. margin-top: 10rpx;
  265. text-align: center;
  266. font-size: 28rpx;
  267. }
  268. .info {
  269. margin-top: 100rpx;
  270. height: 40rpx;
  271. text-align: center;
  272. color: #5393FF;
  273. }
  274. .photo {
  275. margin: 0 auto;
  276. margin-top: 40rpx;
  277. width: 375rpx;
  278. height: 375rpx;
  279. border-radius: 188rpx;
  280. overflow: hidden;
  281. .img {
  282. width: 100%;
  283. height: 100%;
  284. border-radius: 50%;
  285. }
  286. img {
  287. width: 100%;
  288. height: 100%;
  289. }
  290. }
  291. .button {
  292. margin: 0 auto;
  293. margin-top: 127rpx;
  294. width: 430rpx;
  295. height: 100rpx;
  296. line-height: 100rpx;
  297. text-align: center;
  298. color: #fff;
  299. border-radius: 21rpx;
  300. font-size: 32rpx;
  301. background: linear-gradient(180deg, rgba(0, 172, 252, 1) 0%, rgba(0, 130, 252, 1) 100%);
  302. }
  303. .popup-content {
  304. display: flex;
  305. flex-direction: column;
  306. justify-content: space-around;
  307. align-items: center;
  308. width: 630rpx;
  309. height: 376rpx;
  310. border-radius: 33rpx;
  311. background-color: #fff;
  312. .title {
  313. display: flex;
  314. justify-content: center;
  315. align-items: center;
  316. .icon {
  317. width: 40rpx;
  318. height: 40rpx;
  319. img {
  320. width: 100%;
  321. height: 100%;
  322. }
  323. }
  324. .title_info {
  325. margin-left: 8rpx;
  326. font-size: 36rpx;
  327. font-weight: 800;
  328. color: #0082FC;
  329. }
  330. }
  331. .time {
  332. font-size: 32rpx;
  333. }
  334. .popup_button {
  335. width: 50%;
  336. text-align: center;
  337. font-size: 32rpx;
  338. color: #0082FC;
  339. }
  340. }
  341. }
  342. </style>