liyongli 2 anos atrás
pai
commit
79c0133c40

+ 3 - 3
miniprogram/app.scss

@@ -17,9 +17,9 @@ page {
     color: #ffffff;
     width: 500rpx;
     text-align: center;
-    height: 72rpx;
-    line-height: 72rpx;
-    border-radius: 36rpx;
+    height: 92rpx;
+    line-height: 92rpx;
+    border-radius: 56rpx;
     background: linear-gradient(180deg, #26B4EC 0%, #2F8AFF 100%);
   }
 }

+ 379 - 0
miniprogram/components/camera-face/index copy.ts

@@ -0,0 +1,379 @@
+// components/camera-face/index.js
+
+import { getAuthorize, setAuthorize, throttle, checkVersion } from './utils'
+
+// 顶部提示信息
+const topTips = {
+  ready: '请正对手机,保持光线充足',
+  front: '请正对屏幕',
+  left: '请向左转头',
+  right: '请向右转头',
+}
+
+// 底部提示信息
+const bottomTips = {
+  recording: '脸部信息录入中...',
+  complete: '脸部信息录入成功',
+  error: '脸部信息录入失败'
+}
+
+const innerAudioContext = wx.createInnerAudioContext()
+let ctx: any = undefined;
+let listener: any = undefined;
+let interval: any = undefined;
+
+Component({
+
+  // 组件的属性列表
+  properties: {
+    // 人脸整体可信度 [0-1], 参考wx.faceDetect文档的res.confArray.global
+    // 当超过这个可信度且正脸时开始录制人脸, 反之停止录制
+    faceCredibility: {
+      type: Number,
+      value: 0.3
+    },
+    // 人脸偏移角度正脸数值参考wx.faceDetect文档的res.angleArray
+    // 越接近0越正脸,包括p仰俯角(pitch点头), y偏航角(yaw摇头), r翻滚角(roll左右倾)
+    faceAngle: {
+      type: Object,
+      value: { p: 1, y: 1, r: 1 }
+    },
+    // 录制视频时长,不能超过30s
+    duration: {
+      type: Number,
+      value: 15000
+    },
+    // 是否压缩视频
+    compressed: {
+      type: Boolean,
+      value: false
+    },
+    // 前置或者后置 front,back
+    devicePosition: {
+      type: String,
+      value: 'front'
+    },
+    // 指定期望的相机帧数据尺寸 small,medium,large
+    frameSize: {
+      type: String,
+      value: 'medium'
+    },
+    // 分辨率 low,medium,high
+    resolution: {
+      type: String,
+      value: 'medium'
+    },
+    // 闪光灯 auto,on,off,torch
+    flash: {
+      type: String,
+      value: 'off'
+    },
+    // 检测视频帧的节流时间,默认500毫秒执行一次
+    throttleFrequency: {
+      type: Number,
+      value: 500
+    }
+  },
+
+  // 组件页面的生命周期
+  pageLifetimes: {
+    // 页面被隐藏
+    hide: function () {
+      this.stop()
+    },
+  },
+
+  detached: function () {
+    // 在组件实例被从页面节点树移除时执行
+    this.stop()
+  },
+
+  // 组件的初始数据
+  data: {
+    isReading: false, // 是否在准备中
+    isRecoding: false, // 是否正在录制中
+    isStopRecoding: false, // 是否正在停止录制中
+
+    topTips: topTips.ready, // 顶部提示文字
+    bottomTips: '', // 底部提示文字
+    seconds: '',
+    videoSrc: '',
+    isCompleteRecoding: false
+  },
+
+  /**
+   * 组件的方法列表
+   */
+  methods: {
+
+    // 开启相机ctx
+    async start() {
+      const result = await this.initAuthorize()
+      if (!result) return false
+      if (!ctx) ctx = wx.createCameraContext()
+      return true
+    },
+
+    // 准备录制
+    async readyRecord() {
+      if (this.data.isReading) return
+      this.setData({ isReading: true })
+      wx.showLoading({ title: '加载中..', mask: true })
+      // 检测版本号
+      const canUse = checkVersion('2.18.0', () => {
+        this.triggerEvent('cannotUse')
+      })
+      if (!canUse) {
+        wx.hideLoading()
+        this.setData({ isReading: false })
+        return
+      }
+
+      // 启用相机
+      try {
+        const result = await this.start()
+        if (!result || !ctx) throw new Error()
+      } catch (e) {
+        wx.hideLoading()
+        this.setData({ isReading: false })
+        return
+      }
+      console.log('准备录制');
+      this.setData({ topTips: topTips.ready })
+      // 视频帧回调节流函数
+      let fn = throttle((frame: any) => {
+        // 人脸识别
+        wx.faceDetect({
+          frameBuffer: frame.data,
+          width: frame.width,
+          height: frame.height,
+          enableConf: true,
+          enableAngle: true,
+          success: (res) => this.processFaceData(res),
+          fail: () => this.cancel()
+        })
+      }, this.properties.throttleFrequency)
+
+      // 初始化人脸识别
+      wx.initFaceDetect({
+        success: () => {
+          wx.hideLoading()
+          listener = ctx.onCameraFrame((frame: any) => fn(frame))
+          listener.start()
+        },
+        fail: (err) => {
+          wx.hideLoading()
+          console.log('初始人脸识别失败', err)
+          // this.setData({ topTips: '' })
+          wx.showToast({ title: '未识别到人脸', icon: 'none', duration: 2000 });
+          this.stop();
+        },
+        complete: () => {
+          this.setData({ isReading: false })
+        }
+      })
+    },
+
+    // 处理人脸识别数据
+    processFaceData(res: any) {
+      if (res.confArray && res.angleArray) {
+        const { global } = res.confArray
+        const g = this.properties.faceCredibility
+        const { pitch, yaw, roll } = res.angleArray
+        const { p, y, r } = this.properties.faceAngle
+        const isGlobal = global >= g
+        const isPitch = Math.abs(pitch) <= p
+        const isYaw = Math.abs(yaw) <= y
+        const isRoll = Math.abs(roll) <= r
+
+        if (isGlobal && isPitch && isYaw && isRoll) {
+          console.log('人脸可信,且是正脸')
+          if (this.data.isRecoding || this.data.isCompleteRecoding) return
+          this.setData({ isRecoding: true })
+          this.startRecord() // 开始录制
+        } else {
+          console.log('人脸不可信,或者不是正脸')
+          this.cancel()
+        }
+      } else {
+        console.log('获取人脸识别数据失败', res)
+        this.cancel()
+      }
+    },
+
+    // 开始录制
+    startRecord() {
+      console.log('开始录制')
+      ctx.startRecord({
+        success: () => {
+          this.setRecordingTips()
+          let timer = setTimeout(() => {
+            this.completeRecord()
+            clearTimeout(timer);
+          }, this.properties.duration)
+        },
+        timeoutCallback: () => {
+          // 超过30s或页面 onHide 时会结束录像
+          this.stop()
+        },
+        fail: () => this.stop()
+      })
+    },
+
+    // 设置录制中的提示文字和倒计时
+    setRecordingTips() {
+      this.setData({
+        bottomTips: bottomTips.recording
+      })
+
+      let second = (this.properties.duration / 1000)
+      if (interval) interval = clearInterval(interval)
+      interval = setInterval(() => {
+        console.log('xxxxxx', second)
+
+        // 音频播放
+        if (second == 15) {
+          innerAudioContext.src = "assets/voice/front.mp3"
+          innerAudioContext.play()
+        }
+
+        else if (second == 10) {
+          innerAudioContext.src = "assets/voice/left.mp3"
+          innerAudioContext.play()
+        }
+
+        else if (second == 5) {
+          innerAudioContext.src = "assets/voice/right.mp3"
+          innerAudioContext.play()
+        }
+
+        // 修改提示文字
+        if (second > 10)
+          this.setData({
+            topTips: topTips.front,
+          })
+        else if (second > 5 && second <= 10)
+          this.setData({
+            topTips: topTips.left,
+          })
+        else {
+          this.setData({
+            topTips: topTips.right,
+          })
+        }
+        this.setData({
+          seconds: second-- + 's'
+        })
+        if (second <= 0) {
+          interval = clearInterval(interval)
+          this.setData({
+            seconds: '1s',
+            topTips: topTips.ready,
+            bottomTips: bottomTips.complete
+          })
+        }
+      }, 1000)
+    },
+
+    // 完成录制
+    completeRecord() {
+      console.log('完成录制')
+      this.setData({ isCompleteRecoding: true })
+      ctx.stopRecord({
+        compressed: this.properties.compressed,
+        success: (res: any) => {
+          this.setData({
+            videoSrc: res.tempVideoPath
+          })
+          console.log("++++++++")
+          // 向外触发完成录制的事件
+          this.triggerEvent('complete', res.tempVideoPath)
+          // this.uploadVideo()
+        },
+        fail: () => this.stop(),
+        complete: () => {
+          listener.stop()
+          wx.stopFaceDetect()
+          interval = clearInterval(interval);
+          this.setData({ isCompleteRecoding: false })
+        }
+      })
+
+    },
+
+    // 人脸移出等取消录制
+    cancel() {
+      console.log('取消录制')
+      // 如果不在录制中或者正在录制完成中就不能取消
+      if (!this.data.isRecoding || this.data.isCompleteRecoding) return
+      interval = clearInterval(interval);
+      ctx.stopRecord({
+        complete: () => {
+          console.log('取消录制成功')
+          this.setData({
+            topTips: topTips.ready,
+            bottomTips: '',
+            seconds: '',
+            isRecoding: false
+          })
+          wx.showToast({ title: '录制失败,请重新录制', icon: 'none', duration: 2000 })
+        }
+      })
+    },
+
+    // 用户切入后台等停止使用摄像头
+    stop() {
+      console.log('停止录制')
+      interval = clearInterval(interval);
+      if (listener) listener.stop()
+      if (ctx && !this.data.isCompleteRecoding) ctx.stopRecord()
+      wx.stopFaceDetect()
+      setTimeout(() => {
+        this.setData({ topTips: '', isRecoding: false })
+      }, 500)
+    },
+    // 用户不允许使用摄像头
+    error() {
+      // const cameraName = 'scope.camera';
+      // this.triggerEvent('noAuth', cameraName)
+    },
+
+    // 初始相机和录音权限
+    async initAuthorize() {
+      const cameraName = 'scope.camera'
+      const recordName = 'scope.record'
+      const scopeCamera = await getAuthorize(cameraName)
+      // 未授权相机
+      if (!scopeCamera) {
+        // 用户拒绝授权相机
+        if (!(await setAuthorize(cameraName))) this.openSetting()
+        return false
+      }
+      const scopeRecord = await getAuthorize(recordName)
+      if (!scopeRecord) {
+        // 用户拒绝授权录音
+        if (!(await setAuthorize(recordName))) {
+          this.openSetting()
+          return false
+        }
+      }
+      return true
+    },
+
+    // 打开设置授权
+    openSetting() {
+      wx.showModal({
+        title: '开启摄像头和录音权限',
+        showCancel: true,
+        content: '是否打开?',
+        success: (res) => {
+          this.triggerEvent('noAuth', '打开设置授权')
+          if (res.confirm) {
+            wx.openSetting()
+          }
+        }
+      })
+    },
+
+  }
+})

+ 195 - 244
miniprogram/components/camera-face/index.ts

@@ -1,29 +1,27 @@
-// components/camera-face/index.js
-
 import { getAuthorize, setAuthorize, throttle, checkVersion } from './utils'
-
 // 顶部提示信息
+// const app = getApp<IAppOption>();
 const topTips = {
   ready: '请正对手机,保持光线充足',
   front: '请正对屏幕',
   left: '请向左转头',
   right: '请向右转头',
 }
-
-// 底部提示信息
 const bottomTips = {
-  recording: '人脸录制中...',
-  complete: '人脸录制成功',
-  error: '人脸录制失败'
+  recording: '脸部信息采集中...',
+  complete: '脸部信息采集成功',
+  error: '脸部信息采集失败'
 }
-
-const innerAudioContext = wx.createInnerAudioContext()
-let ctx: any = undefined;
-let listener: any = undefined;
-let interval: any = undefined;
+let ctx: WechatMiniprogram.CameraContext;
+let listener: WechatMiniprogram.CameraFrameListener;
+let startTime: number = 0;
+let innerAudioContext: WechatMiniprogram.InnerAudioContext;
 
 Component({
-
+  ready() {
+    innerAudioContext = wx.createInnerAudioContext();
+    innerAudioContext.loop = false;
+  },
   // 组件的属性列表
   properties: {
     // 人脸整体可信度 [0-1], 参考wx.faceDetect文档的res.confArray.global
@@ -68,296 +66,249 @@ Component({
       type: String,
       value: 'off'
     },
-    // 检测视频帧的节流时间,默认500毫秒执行一次
-    throttleFrequency: {
-      type: Number,
-      value: 500
-    }
   },
 
   // 组件页面的生命周期
   pageLifetimes: {
     // 页面被隐藏
     hide: function () {
-      this.stop()
+      this.stopRecord();
+      this.stopUI();
     },
   },
 
   detached: function () {
     // 在组件实例被从页面节点树移除时执行
-    this.stop()
+    this.stopRecord();
+    this.stopUI();
   },
 
+
+
   // 组件的初始数据
   data: {
-    isReading: false, // 是否在准备中
-    isRecoding: false, // 是否正在录制中
-    isStopRecoding: false, // 是否正在停止录制中
-
-    topTips: topTips.ready, // 顶部提示文字
-    bottomTips: '', // 底部提示文字
-    seconds: '',
-    videoSrc: '',
-    isCompleteRecoding: false
+    topTips: topTips.ready, // 顶部提示信息
+    bottomTips: "", //底部提示信息
+    gather: 0, // 采集状态:0 -未开始 1 -加载中 2 -录制中 3 -录制结束
+    seconds: 0
   },
 
   /**
    * 组件的方法列表
    */
   methods: {
+    // 准备采集人脸信息
+    readyRecord() {
+      if (this.data.gather !== 0 && this.data.gather !== 3) return; // 开始采集
+      this.setData({ bottomTips: bottomTips.recording, gather: 1 }); // 状态转换为加载中
+      wx.nextTick(async () => {
+        wx.showLoading({ title: '加载中..', mask: true })
+        if (!checkVersion('2.18.0', () => this.triggerEvent('cannotUse'))) {
+          // 基础库不支持 faceDate
+          wx.hideLoading();
+          this.setData({ gather: 0, bottomTips: "" });
+          return;
+        }
 
-    // 开启相机ctx
-    async start() {
-      const result = await this.initAuthorize()
-      if (!result) return false
-      if (!ctx) ctx = wx.createCameraContext()
-      return true
-    },
-
-    // 准备录制
-    async readyRecord() {
-      if (this.data.isReading) return
-      this.setData({ isReading: true })
-      wx.showLoading({ title: '加载中..', mask: true })
-      // 检测版本号
-      const canUse = checkVersion('2.18.0', () => {
-        this.triggerEvent('cannotUse')
+        // 调用相机
+        await this.openCamera();
+        // 准备采集
+        startTime = Date.now();
+        this.initFace()
+        // 开始录制
+        this.startRecord()
       })
-      if (!canUse) {
-        wx.hideLoading()
-        this.setData({ isReading: false })
-        return
-      }
-
-      // 启用相机
-      try {
-        const result = await this.start()
-        if (!result || !ctx) throw new Error()
-      } catch (e) {
-        wx.hideLoading()
-        this.setData({ isReading: false })
-        return
-      }
-      console.log('准备录制')
-      this.setData({ topTips: topTips.ready })
-      // 视频帧回调节流函数
-      let fn = throttle((frame: any) => {
-        // 人脸识别
+    },
+    // init 人脸识别能力
+    initFace() {
+      // 视频帧数据读取节流
+      const fn = (frame: any) => {
+        const s = 15 - Math.floor((Date.now() - startTime) / 1000);
+        if (this.data.gather !== 2 || s % 500 > 100) return;
+        let tip = "";
+        switch (true) {
+          case s > 10:
+            tip = topTips.front
+            break;
+          case s > 5 && s <= 10:
+            tip = topTips.left;
+            break
+          default:
+            tip = topTips.right;
+            break;
+        }
+        if (s == 15) innerAudioContext.src = "assets/voice/front.mp3";
+        if (s == 10) innerAudioContext.src = "assets/voice/left.mp3";
+        if (s == 5) innerAudioContext.src = "assets/voice/right.mp3";
+        if (s % 5 === 0 && s !== 0) innerAudioContext.play();
+        this.setData({
+          seconds: s,
+          topTips: tip
+        })
+        if (s <= 0) {
+          // 结束监听
+          this.stopRecord(true);// 停止录像逻辑
+          this.stopUI(true); // 重置ui逻辑;
+          return
+        }
+        // 识别人脸是否在画面种
         wx.faceDetect({
           frameBuffer: frame.data,
           width: frame.width,
           height: frame.height,
           enableConf: true,
           enableAngle: true,
-          success: (res) => this.processFaceData(res),
-          fail: () => this.cancel()
+          success: (res: WechatMiniprogram.FaceDetectSuccessCallbackResult) => this.processFaceData(res),
+          fail: (err) => {
+            wx.showToast({ title: '未识别到人脸', icon: 'error', duration: 2000 });
+            this.setData({
+              seconds: 0,
+              bottomTips: bottomTips.error
+            })
+            this.stopRecord();
+            this.stopUI();
+          }
         })
-      }, this.properties.throttleFrequency)
-
+      }
       // 初始化人脸识别
       wx.initFaceDetect({
         success: () => {
+          wx.hideLoading();
           listener = ctx.onCameraFrame((frame: any) => fn(frame))
-          listener.start()
-        },
-        fail: (err) => {
-          console.log('初始人脸识别失败', err)
-          // this.setData({ topTips: '' })
-          wx.showToast({ title: '未识别到人脸', icon: 'none' })
+          listener?.start()
         },
-        complete: () => {
-          wx.hideLoading()
-          this.setData({ isReading: false })
+        fail: () => {
+          wx.hideLoading();
+          // 初始化人脸识别失败
+          wx.showToast({ title: '未识别到人脸', icon: 'error', duration: 2000 });
+          this.setData({
+            seconds: 0,
+            bottomTips: bottomTips.error
+          })
+          this.stopRecord();
+          this.stopUI();
         }
       })
     },
-
-    // 处理人脸识别数据
-    processFaceData(res: any) {
-      if (res.confArray && res.angleArray) {
-        const { global } = res.confArray
-        const g = this.properties.faceCredibility
-        const { pitch, yaw, roll } = res.angleArray
-        const { p, y, r } = this.properties.faceAngle
-        const isGlobal = global >= g
-        const isPitch = Math.abs(pitch) <= p
-        const isYaw = Math.abs(yaw) <= y
-        const isRoll = Math.abs(roll) <= r
-
-        if (isGlobal && isPitch && isYaw && isRoll) {
-          console.log('人脸可信,且是正脸')
-          if (this.data.isRecoding || this.data.isCompleteRecoding) return
-          this.setData({ isRecoding: true })
-          this.startRecord() // 开始录制
-        } else {
-          console.log('人脸不可信,或者不是正脸')
-          this.cancel()
-        }
-      } else {
-        console.log('获取人脸识别数据失败', res)
-        this.cancel()
+    // 人脸识别数据
+    processFaceData(res: WechatMiniprogram.FaceDetectSuccessCallbackResult) {
+      if (!res.confArray || !res.angleArray) {
+        // 人脸数据获取失败
+        wx.showToast({ title: '人脸数据获取失败', icon: 'error', duration: 2000 });
+        this.stopRecord();
+        this.stopUI();
+        return
+      }
+      const { global } = res.confArray;
+      const { pitch, yaw, roll } = res.angleArray
+      const { p, y, r } = this.properties.faceAngle; // 偏移角度
+      const g = this.properties.faceCredibility;// 自定义可信度;
+      const isGlobal = global >= g
+      const isPitch = Math.abs(pitch) <= p
+      const isYaw = Math.abs(yaw) <= y
+      const isRoll = Math.abs(roll) <= r
+      if (!isGlobal || !isPitch || !isYaw || !isRoll) {
+        // 人脸不可信,且非正脸
+        wx.showToast({ title: '未检测到人脸', icon: 'error', duration: 2000 });
+        this.setData({
+          seconds: 0,
+          bottomTips: bottomTips.error
+        })
+        this.stopRecord();
+        this.stopUI();
+        return
       }
     },
-
     // 开始录制
     startRecord() {
-      console.log('开始录制')
       ctx.startRecord({
+        timeout: 15,
         success: () => {
-          this.setRecordingTips()
-          let timer = setTimeout(() => {
-            this.completeRecord()
-            clearTimeout(timer);
-          }, this.properties.duration)
+          console.log("开始录制")
+          this.setData({ gather: 2, seconds: 15 });// 录制中
         },
         timeoutCallback: () => {
-          // 超过30s或页面 onHide 时会结束录像
-          this.stop()
+          if (this.data.gather !== 2) return;
+          this.stopRecord(); // 停止录像逻辑
+          this.stopUI(); // 重置ui逻辑;
+        }, // 超出录制时长
+        fail: () => {
+          this.stopRecord(); // 停止录像逻辑
+          this.stopUI(); // 重置ui逻辑;
         },
-        fail: () => this.stop()
-      })
-    },
-
-    // 设置录制中的提示文字和倒计时
-    setRecordingTips() {
-      this.setData({
-        bottomTips: bottomTips.recording
-      })
-
-      let second = (this.properties.duration / 1000)
-      if (interval) interval = clearInterval(interval)
-      interval = setInterval(() => {
-        console.log('xxxxxx', second)
-
-        // 音频播放
-        if (second == 15) {
-          innerAudioContext.src = "assets/voice/front.mp3"
-          innerAudioContext.play()
-        }
-
-        else if (second == 10) {
-          innerAudioContext.src = "assets/voice/left.mp3"
-          innerAudioContext.play()
-        }
-
-        else if (second == 5) {
-          innerAudioContext.src = "assets/voice/right.mp3"
-          innerAudioContext.play()
-        }
-
-        // 修改提示文字
-        if (second > 10)
-          this.setData({
-            topTips: topTips.front,
-          })
-        else if (second > 5 && second <= 10)
-          this.setData({
-            topTips: topTips.left,
-          })
-        else {
-          this.setData({
-            topTips: topTips.right,
-          })
-        }
-        this.setData({
-          seconds: second-- + 's'
-        })
-        if (second <= 0) {
-          interval = clearInterval(interval)
-          this.setData({
-            seconds: '1s',
-            topTips: '',
-            bottomTips: bottomTips.complete
-          })
+        complete: () => {
+          wx.hideLoading();
         }
-      }, 1000)
+      })
     },
-
-    // 完成录制
-    completeRecord() {
-      console.log('完成录制')
-      this.setData({ isCompleteRecoding: true })
-      ctx.stopRecord({
+    // 结束录制
+    stopRecord(isVadeo?: boolean) {
+      this.setData({ gather: isVadeo ? 3 : 0 });
+      console.log('+++++++++>')
+      ctx?.stopRecord({
         compressed: this.properties.compressed,
-        success: (res: any) => {
+        success: (res) => {
+          wx.hideLoading();
+          /**
+           * tempThumbPath 视频封面
+           * tempVideoPath 视频临时文件路径
+           */
+          console.log("录制结束", res, isVadeo);
+          if (!isVadeo) return;
+          this.triggerEvent('complete', res.tempVideoPath)
+        },
+        fail: (err) => {
+          console.log("录制失败+++>", err);
+          wx.hideLoading();
+          wx.showToast({ title: "采集失败,请重试", icon: "none" });
+          wx.navigateBack();
           this.setData({
-            videoSrc: res.tempVideoPath
+            seconds: 0,
+            bottomTips: bottomTips.error
           })
-          // 向外触发完成录制的事件
-          this.triggerEvent('complete', res.tempVideoPath)
-          // this.uploadVideo()
         },
-        fail: () => this.stop(),
         complete: () => {
-          listener.stop()
-          wx.stopFaceDetect()
-          interval = clearInterval(interval);
-          this.setData({ isCompleteRecoding: false })
+          console.log("取消录制");
+          listener?.stop();
+          wx.stopFaceDetect({});
         }
       })
-
     },
-
-    // 人脸移出等取消录制
-    cancel() {
-      console.log('取消录制')
-      // 如果不在录制中或者正在录制完成中就不能取消
-      if (!this.data.isRecoding || this.data.isCompleteRecoding) return
-      interval = clearInterval(interval);
-      ctx.stopRecord({
-        complete: () => {
-          console.log('取消录制成功')
-          this.setData({
-            topTips: topTips.ready,
-            bottomTips: '',
-            seconds: '',
-            isRecoding: false
-          })
-          wx.showToast({ title: '录制失败,请重新录制', icon: 'none' })
-        }
+    // 停止ui变化
+    stopUI(isEnd?: boolean) {
+      this.setData({
+        topTips: topTips.ready,
+        bottomTips: isEnd ? bottomTips.complete : bottomTips.error
       })
     },
-
-    // 用户切入后台等停止使用摄像头
-    stop() {
-      console.log('停止录制')
-      interval = clearInterval(interval);
-      if (listener) listener.stop()
-      if (ctx && !this.data.isCompleteRecoding) ctx.stopRecord()
-      wx.stopFaceDetect()
-      setTimeout(() => {
-        this.setData({ topTips: '', isRecoding: false })
-      }, 500)
-    },
-    // 用户不允许使用摄像头
-    error() {
-      // const cameraName = 'scope.camera';
-      // this.triggerEvent('noAuth', cameraName)
+    // 开启相机
+    async openCamera() {
+      if (!(await this.initAuthorize())) {
+        this.setData({ gather: 0 });
+        throw new Error("未能正常启动摄像头");
+      }
+      try {
+        !ctx && (ctx = wx.createCameraContext());
+      } catch (error) {
+        wx.hideLoading()
+        wx.showToast({
+          title: "设备不支持视频采集",
+          icon: "error"
+        })
+        this.setData({ gather: 0 });
+      }
     },
-
-    // 初始相机和录音权限
+    // 初始化相机和录音权限
     async initAuthorize() {
-      const cameraName = 'scope.camera'
-      const recordName = 'scope.record'
-      const scopeCamera = await getAuthorize(cameraName)
-      // 未授权相机
-      if (!scopeCamera) {
-        // 用户拒绝授权相机
-        if (!(await setAuthorize(cameraName))) this.openSetting()
+      const noauthorize = !(await getAuthorize('scope.camera'))
+      // 相机或录音未授权
+      if (noauthorize) {
+        this.openSetting();
         return false
       }
-      const scopeRecord = await getAuthorize(recordName)
-      if (!scopeRecord) {
-        // 用户拒绝授权录音
-        if (!(await setAuthorize(recordName))) {
-          this.openSetting()
-          return false
-        }
-      }
       return true
     },
-
-    // 打开设置授权
+    // 打开摄像头或录音权限授权框
     openSetting() {
       wx.showModal({
         title: '开启摄像头和录音权限',
@@ -365,12 +316,12 @@ Component({
         content: '是否打开?',
         success: (res) => {
           this.triggerEvent('noAuth', '打开设置授权')
-          if (res.confirm) {
-            wx.openSetting()
-          }
+          if (res.confirm) wx.openSetting();
         }
       })
-    },
-
+    }
   }
 })
+
+
+export { }

+ 4 - 6
miniprogram/components/camera-face/index.wxml

@@ -6,15 +6,13 @@
     <view class="top-tips">{{topTips}}</view>
 
     <camera mode="normal" device-position="{{devicePosition}}" frame-size="{{frameSize}}" resolution="{{resolution}}" flash="{{flash}}" binderror="error" bindstop="stop" class="camera">
-      <view wx:if="{{seconds}}" class="half-circle">
-        {{seconds}}
+      <view wx:if="{{seconds>0}}" class="half-circle">
+        {{seconds}}s
       </view>
     </camera>
   </view>
 
   <!-- 开始人脸验证——人脸验证中——人脸验证成功 -->
-  <view wx:if="{{!bottomTips}}" class="faceBtn" bindtap="readyRecord">开始人脸验证</view>
-  <view wx:else class="faceBtn">{{bottomTips}}</view>
-
-  
+  <view wx:if="{{gather === 0}}" class="faceBtn" bindtap="readyRecord">开始人脸采集</view>
+  <view wx:if="{{gather !== 0}}" class="bottom-tips">{{bottomTips}}</view>
 </view>

+ 11 - 8
miniprogram/components/camera-face/index.wxss

@@ -6,7 +6,7 @@
   overflow: auto
 }
 
-.head{
+.head {
   padding-top: 154rpx;
   padding-bottom: 262rpx;
   background-image: url(https://cxzx.smcic.net/topic/tool/img/miniprogram/faceBg.jpg);
@@ -57,20 +57,23 @@
 
 /* 底部按钮和提示文字 */
 .bottom-tips {
-  width: 100%;
-  height: 35rpx;
-  color: black;
-  line-height: 35rpx;
   text-align: center;
+  height: 188rpx;
+  font-size: 32rpx;
+  font-family: PingFangSC-Regular, PingFang SC;
+  font-weight: 400;
+  color: #333333;
+  line-height: 188rpx;
 }
+
 .faceBtn {
   color: #ffffff;
   margin: 81rpx auto 0 auto;
   width: 500rpx;
   text-align: center;
-  height: 72rpx;
-  line-height: 72rpx;
-  border-radius: 36rpx;
+  height: 92rpx;
+  line-height: 92rpx;
+  border-radius: 56rpx;
   background: linear-gradient(180deg, #26B4EC 0%, #2F8AFF 100%);
 }
 

+ 8 - 4
miniprogram/pages/downloadPage/index.scss

@@ -39,14 +39,18 @@
 
     .btn {
       width: 500rpx;
-      height: 72rpx;
-      line-height: 72rpx;
+      height: 92rpx;
+      line-height: 92rpx;
       text-align: center;
       background: linear-gradient(180deg, #26B4EC 0%, #2F8AFF 100%);
-      border-radius: 36rpx;
+      border-radius: 56rpx;
       color: #FFFFFF;
-      font-size: 24rpx;
+      font-size: 34rpx;
       margin: 188rpx auto 0 auto;
     }
+    .duanluo {
+      text-indent: 2em;
+      line-height: 1.8em;
+    }
   }
 }

+ 22 - 12
miniprogram/pages/downloadPage/index.ts

@@ -17,10 +17,13 @@ Page({
         data: {},
         success: (res: any) => {
           if (!res.RESULT) return wx.showToast({
-            "title": "视频正在生成中,请耐心等待"
+            "title": "视频正在生成中,请耐心等待",
+            icon: "none",
+            duration: 2000
           })
           res.PAY ? this.downloadVideo() : this.toPAY();
-        }
+        },
+
       })
     }, 200);
 
@@ -29,11 +32,11 @@ Page({
   downloadVideo() {
     if (t) t = clearTimeout(t);
     t = setTimeout(() => {
+      wx.showLoading({
+        title: ""
+      })
       app.getVideo({
         success: (r: any) => {
-          wx.showLoading({
-            title: ""
-          })
           wx.downloadFile({
             url: r.url,
             success: (res) => {
@@ -43,19 +46,25 @@ Page({
               wx.saveVideoToPhotosAlbum({
                 filePath: res.tempFilePath,
                 success: () => {
+                  wx.hideLoading();
                   wx.showToast({
                     title: "保存成功",
-                    icon: "none"
+                    icon: "none",
+                    duration: 2000
                   })
                 },
-                complete: () => {
+                fail: () => {
                   wx.hideLoading();
+                },
+                complete: () => {
+                  apiPay = false;
                 }
               })
             },
-            complete: () => {
+            fail: () => {
               wx.hideLoading();
-            }
+            },
+            complete: () => { }
           })
         },
       })
@@ -77,7 +86,8 @@ Page({
           failL: () => {
             wx.showToast({
               icon: "none",
-              title: "支付调用失败"
+              title: "支付调用失败",
+              duration: 2000
             })
             apiPay = false;
           }
@@ -90,7 +100,7 @@ Page({
    * 生命周期函数--监听页面加载
    */
   onLoad() {
-    wx.hideHomeButton({})
+    wx.hideHomeButton({});
   },
 
   /**
@@ -143,4 +153,4 @@ Page({
   }
 })
 
-export {}
+export { }

+ 11 - 5
miniprogram/pages/downloadPage/index.wxml

@@ -1,15 +1,21 @@
 <!--pages/downloadPage/index.wxml-->
 <view class="page">
-  <image src="https://cxzx.smcic.net/topic/tool/img/miniprogram/download.png" mode="scaleToFill" class="headImage"/>
+  <image src="https://cxzx.smcic.net/topic/tool/img/miniprogram/download.png" mode="scaleToFill" class="headImage" />
   <view class="main">
     <view class="main_title">
       马拉松简介
     </view>
-    这是一个小小的简介这是一个小小的简介这是这是一个小小的简介这是一个小小的简介这是这是一个小小的简介这是一个小小的简介这是这是一个小小的简介这是一个小小的简介这是这是一个小小的简介这是一个小小的简介这是这是一个小小的简介这是一个小小的简介这是这是一个小小的简介这是一个小小的简介这是这是一个小小的简介这是一个小小的简介这是这是一个小小的简介这是一个小小的简介这是
-    
+    <view class="duanluo">
+      荣耀·2023陕西大学生马拉松联赛是陕西省学生体育协会及西安曲江体育集团面向高校学子重磅打造的品牌路跑赛事IP,本次赛事将于3月——11月,以“一校一站”的形式,在陕西省众多高校相继举办。
+    </view>
+    <view class="duanluo">
+      首场赛事荣耀·2023陕西大学生马拉松联赛-西安交通大学站将于3月25日在西安交通大学创新港校区举办,本次赛事将充分展示交大学子拼搏不息的体育精神通过赛事,让学子们重温西迁历史,感悟“西迁精神”!
+    </view>
+
+
     <view class="btn" bindtap="toDownload">
-      <image class="iconImage" src="https://cxzx.smcic.net/topic/tool/img/miniprogram/video.png" mode="scaleToFill"/>
+      <image class="iconImage" src="https://cxzx.smcic.net/topic/tool/img/miniprogram/video.png" mode="scaleToFill" />
       下载个人视频
     </view>
   </view>
-</view>
+</view>

+ 4 - 2
miniprogram/pages/entry/index.ts

@@ -27,7 +27,8 @@ Page({
     if (!this.data.phoneNumber) {
       return wx.showToast({
         "icon": "none",
-        "title": "未获取手机号"
+        "title": "未获取手机号",
+        duration: 2000
       })
     }
     app.seaveUserData({
@@ -56,7 +57,8 @@ Page({
           wx.setStorageSync("userphone", "");
           wx.showToast({
             icon:"none",
-            title: "未获取手机号,请手动输入"
+            title: "未获取手机号,请手动输入", 
+            duration: 2000
           })
           return;
         }

+ 6 - 2
miniprogram/pages/faceRecognition/index.ts

@@ -132,7 +132,9 @@ Page({
       success: function () {
         console.log('视频上传成功')
         wx.showToast({
-          title: "人脸采集完成"
+          title: "人脸采集完成",
+          icon: "none", 
+          duration: 2000
         })
         let time = setTimeout(() => {
           clearTimeout(time);
@@ -144,7 +146,9 @@ Page({
       fail: function (err:any) {
         console.log('接口调用失败')
         wx.showToast({
-          title: err.message || "人脸采集失败"
+          title: err.message || "人脸采集失败",
+          icon: "none", 
+          duration: 2000
         })
       }
     })

+ 21 - 12
miniprogram/pages/faceRecognitionVk/index.ts

@@ -38,17 +38,18 @@ Page({
         },
         success: () => {
           // 调用成功,开始录制
+          wx.hideLoading();
         },
         fail: () => {
           // 调用失败
+          wx.hideLoading();
           wx.showToast({
             icon: "none",
-            title: "相机调用失败"
+            title: "相机调用失败",
+            duration: 2000
           })
         },
-        complete: () => {
-          wx.hideLoading();
-        }
+        complete: () => { }
       })
     })
   },
@@ -69,7 +70,7 @@ Page({
     })
 
     createVKSession.on("updateAnchors", anchors => {
-      console.log("检测到人脸",anchors)
+      console.log("检测到人脸", anchors)
       if (!this.data.isTranscribe) {
         this.setData({
           isTranscribe: true
@@ -81,7 +82,8 @@ Page({
     createVKSession.on('removeAnchors', () => {
       wx.showToast({
         icon: "none",
-        title: "未检测到人脸,请重新录制"
+        title: "未检测到人脸,请重新录制",
+        duration: 2000
       })
       //  录制失败
       this.overVideo();
@@ -103,17 +105,19 @@ Page({
       compressed: true,
       success: res => {
         // 成功结束
+        wx.hideLoading();
         this.getUrl(res);
       },
       fail: () => {
         // 结束失败
+        wx.hideLoading();
         wx.showToast({
           icon: "none",
-          title: "相机关闭失败"
+          title: "相机关闭失败",
+          duration: 2000
         })
       },
       complete: () => {
-        wx.hideLoading();
         this.setData({
           isStartVideo: false
         })
@@ -140,21 +144,26 @@ Page({
       },
       success: function (res) {
         console.log(res)
+        wx.hideLoading();
         wx.showToast({
-          title: "上传成功"
+          title: "上传成功",
+          duration: 2000,
+          icon: "none"
         })
       },
       fail: function (res) {
         console.log(res)
+        wx.hideLoading();
         wx.showToast({
-          title: "上传失败"
+          title: "上传失败",
+          icon: "none",
+          duration: 2000
         })
       },
       complete: () => {
         this.setData({
           isUpLoad: false
         })
-        wx.hideLoading();
       }
     })
   },
@@ -226,4 +235,4 @@ Page({
   },
 })
 
-export {}
+export { }

+ 7 - 3
miniprogram/pages/home/index.scss

@@ -5,6 +5,7 @@
   background-repeat: no-repeat;
   background-image: url(https://cxzx.smcic.net/topic/tool/img/miniprogram/bg.jpg);
   padding-top: 86rpx;
+  box-sizing: border-box;
 
   .title {
     width: 626rpx;
@@ -13,7 +14,7 @@
     display: block;
   }
 
-  .buttom{
+  .buttom {
     background-size: 100% 100%;
     background-repeat: no-repeat;
     background-image: url(https://cxzx.smcic.net/topic/tool/img/miniprogram/input.png);
@@ -23,8 +24,11 @@
     transform: translateX(-50%);
     color: #FFFFFF;
     text-align: center;
-    padding: 18rpx 86rpx;
-    font-size: 14px;
+    font-size: 28rpx;
     font-weight: 500;
+    width: 340rpx;
+    height: 80rpx;
+    line-height: 80rpx;
+    box-sizing: border-box;
   }
 }

+ 3 - 4
miniprogram/pages/home/index.ts

@@ -12,9 +12,6 @@ Page({
       app.getUser({
       data: {},
       success: (res: any) => {
-        if (!res.RESULT) return wx.showToast({
-          "title": "视频正在生成中,请耐心等待"
-        })
         if (!res.SAVE_USER_INFO) {
           // 未上传用户信息,跳转用户信息页
           wx.navigateTo({
@@ -28,8 +25,10 @@ Page({
             url: "/pages/faceRecognition/index"
           })
           return;
-
         }
+        wx.redirectTo({
+          url: "/pages/downloadPage/index"
+        })
       }
     })
   },

+ 9 - 7
miniprogram/utils/request.ts

@@ -1,7 +1,7 @@
-import {base} from "../config/index"
+import { base } from "../config/index"
 import { errHttp } from "./util";
 
-export default function request(params:WechatMiniprogram.requestParamsData) {
+export default function request(params: WechatMiniprogram.requestParamsData) {
   wx.showLoading({
     title: ""
   })
@@ -10,8 +10,9 @@ export default function request(params:WechatMiniprogram.requestParamsData) {
     data: params.data,
     header: params.header,
     dataType: params.dataType || 'json',
-    success: (res:any)=>{
-      if(res.statusCode !== 200 || res.data.code !== 0 ) {
+    success: (res: any) => {
+      wx.hideLoading()
+      if (res.statusCode !== 200 || res.data.code !== 0) {
         // code 401  token过期
         errHttp(res.data.message || res.data.message);
         return;
@@ -19,14 +20,15 @@ export default function request(params:WechatMiniprogram.requestParamsData) {
       params.success && params.success(res.data.data);
     },
     method: params.method || "GET",
-    fail: err =>{
+    fail: err => {
+      wx.hideLoading();
       wx.showToast({
         title: err.errMsg || "请稍后再试",
-        icon:"none"
+        icon: "none",
+        duration: 2000
       })
     },
     complete: () => {
-      wx.hideLoading()
     }
   })
 }

+ 2 - 1
miniprogram/utils/util.ts

@@ -21,7 +21,8 @@ const formatNumber = (n: number) => {
 export function errHttp(msg: string) {
   wx.showToast({
     title: msg || "请稍后在试!",
-    icon: "error"
+    icon: "error", 
+    duration: 2000
   })
 }
 

+ 1 - 1
project.private.config.json

@@ -4,7 +4,7 @@
   "setting": {
     "compileHotReLoad": true
   },
-  "libVersion": "2.30.0",
+  "libVersion": "2.30.2",
   "condition": {
     "miniprogram": {
       "list": [