diff --git a/bundle/face/face-info.vue b/bundle/face/face-info.vue
new file mode 100644
index 0000000..9820524
--- /dev/null
+++ b/bundle/face/face-info.vue
@@ -0,0 +1,214 @@
+
+
+ 请确保本人操作
+ 为了防范身份信息被冒用,保障资金安全,请保持正脸在取景框中根据屏幕指示完成识别
+
+
+
+
+ 1.为了确保您账户的安全和真实性,我们需要对您进行身份验证;
+ 2.请您本人亲自完成,请将脸部置于提示框内,并按提示做动作。
+
+
+
+
+
+
+
+ 正对手机
+
+
+
+
+
+
+ 正对手机
+
+
+
+
+
+
+ 正对手机
+
+
+
+ 开始录入
+
+
+
+
+
+
+
diff --git a/bundle/face/face-photo.vue b/bundle/face/face-photo.vue
new file mode 100644
index 0000000..a2ca619
--- /dev/null
+++ b/bundle/face/face-photo.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/bundle/reserve/details.vue b/bundle/reserve/details.vue
index 55cfd62..6b83f09 100644
--- a/bundle/reserve/details.vue
+++ b/bundle/reserve/details.vue
@@ -688,7 +688,7 @@ export default {
uni.$off("payment")
if (params.result) {
uni.redirectTo({
- url: `/bundle/reserve/notice?order_id=${params.order_id}`
+ url: `/bundle/reserve/notice?order_id=${params.order_id}&ground_type=${self.venue.type_id}`
})
} else {
uni.redirectTo({
@@ -761,11 +761,11 @@ export default {
uni.$off("payment")
if (params.result) {
uni.redirectTo({
- url: `/bundle/reserve/notice?order_id=${params.order_id}`
+ url: `/bundle/reserve/notice?order_id=${params.order_id}&ground_type=${self.venue.type_id}`
})
} else {
uni.redirectTo({
- url: '/pages/order/cg-my-order'
+ url: '/pages/order/cg-my-order?type=2'
})
}
}, 500)
diff --git a/bundle/reserve/notice.vue b/bundle/reserve/notice.vue
index 8eb036c..80113ea 100644
--- a/bundle/reserve/notice.vue
+++ b/bundle/reserve/notice.vue
@@ -12,18 +12,39 @@
查看订单
完成
+
+
+
+
+
@@ -111,4 +145,35 @@
}
}
}
+
+ .notice-popup {
+ padding-bottom: 20rpx;
+
+ .title {
+ font-size: 36rpx;
+ color: #303133;
+ line-height: 50rpx;
+ text-align: center;
+ }
+
+ .desc {
+ margin-top: 48rpx;
+ font-weight: 400;
+ font-size: 32rpx;
+ color: #303133;
+ line-height: 52rpx;
+ text-align: center;
+ }
+
+ .btn {
+ font-size: 32rpx;
+ background: #365A9A;
+ border-radius: 8rpx;
+ margin: 92rpx 44rpx 0;
+ height: 80rpx;
+ line-height: 80rpx;
+ text-align: center;
+ color: #fff;
+ }
+ }
diff --git a/manifest.json b/manifest.json
index 4d4aebd..fb06817 100644
--- a/manifest.json
+++ b/manifest.json
@@ -157,6 +157,9 @@
"permission" : {
"scope.userLocation" : {
"desc" : "获取您与体育场馆的距离"
+ },
+ "scope.camera": {
+ "desc": "需要获取摄像头权限进行人脸录入"
}
}
},
diff --git a/pages.json b/pages.json
index a49ddff..91d7df9 100644
--- a/pages.json
+++ b/pages.json
@@ -1013,6 +1013,18 @@
"style": {
"navigationStyle": "custom"
}
+ },
+ {
+ "path": "face/face-info",
+ "style": {
+ "navigationBarTitleText": ""
+ }
+ },
+ {
+ "path": "face/face-photo",
+ "style": {
+ "navigationBarTitleText": ""
+ }
}
]
}],
diff --git a/pages/order/cg-my-order.vue b/pages/order/cg-my-order.vue
index 9036015..3167377 100644
--- a/pages/order/cg-my-order.vue
+++ b/pages/order/cg-my-order.vue
@@ -133,6 +133,11 @@
+
+
+
+
+
@@ -251,6 +256,10 @@
}
},
onLoad(e) {
+ if (e.type) {
+ this.ballType = e.type;
+ }
+
if (typeof e.dataType != 'undefined') {
this.dataType = e.dataType;
}
@@ -572,6 +581,12 @@
uni.navigateTo({
url: `/bundle/reserve/details?id=${ground_id}&typeId=${this.ballType}`
});
+ },
+
+ onTakePhoto(order_id) {
+ uni.navigateTo({
+ url: `/bundle/face/face-info?order_id=${order_id}`
+ });
}
}
};
diff --git a/uni_modules/face-bio-assay/changelog.md b/uni_modules/face-bio-assay/changelog.md
new file mode 100644
index 0000000..ba72fba
--- /dev/null
+++ b/uni_modules/face-bio-assay/changelog.md
@@ -0,0 +1,36 @@
+## 1.1.1(2023-04-13)
+文档更新:动作容器的使用案例
+## 1.1.0(2023-04-13)
+整理代码,补充文档信息
+## 1.0.14(2023-04-13)
+更新文档
+## 1.0.13(2023-04-13)
+更新文档信息
+## 1.0.12(2023-04-13)
+修复授权弹窗bug
+## 1.0.11(2023-04-11)
+文档调整
+## 1.0.10(2023-04-11)
+更新文档
+## 1.0.9(2023-04-11)
+1.优化点头摇头动作
+2.解决页面回退在ios出现空白页
+3.调整ui布局
+## 1.0.8(2023-04-03)
+修改文档,并提出在ios中二次调用bug的解决方案
+## 1.0.7(2023-03-28)
+未通过核验停留两秒,对未授权拍照bug修复
+## 1.0.6(2023-03-27)
+提示用户开启摄像头权限
+## 1.0.5(2023-03-22)
+bug修复,文档修订
+## 1.0.4(2023-03-21)
+更新文档
+## 1.0.3(2023-03-21)
+新增 动作容器(ActionContainer) 类 开发者可通过自定义动作 添加到该容器中管理。也可使用已有动作进行排列组合创建动作组
+## 1.0.2(2023-03-16)
+调整通过条件,一次行为就可以通过验证
+## 1.0.1(2023-03-15)
+参数调整,提高通过率
+## 1.0.0(2023-03-14)
+基于微信小程序的人脸识别做的简易版活体检测
diff --git a/uni_modules/face-bio-assay/components/face-bio-assay/ActionContainer.js b/uni_modules/face-bio-assay/components/face-bio-assay/ActionContainer.js
new file mode 100644
index 0000000..c2f2117
--- /dev/null
+++ b/uni_modules/face-bio-assay/components/face-bio-assay/ActionContainer.js
@@ -0,0 +1,59 @@
+class ActionContainer {
+ //动作组 actions
+ //起始动作下标 index
+ //初始提示 tip
+ //绑定 完成所有动作的回调 endFun
+ //绑定 动作进行中 ingFun
+ //绑定 动作完成时 successFun
+ //绑定 动作失败时 failFun
+ constructor(actions, index, tip, endFun, ingFun, successFun, failFun) {
+ this.actions = actions || []
+ this.index = index || 0
+ this.tip = tip || '检测不到人脸'
+ this.endFun = endFun || this.__tempFun
+ this.ingFun = ingFun || this.__tempFun
+ this.successFun = successFun || this.__tempFun
+ this.failFun = failFun || this.__tempFun
+ }
+ __tempFun(){
+
+ }
+ next(faceData) {
+ if (this.index >= this.actions.length) {
+ this.endFun()
+ return this
+ }
+ if (this.actions[this.index].state === 'ing') {
+ this.tip = this.actions[this.index].tip
+ this.actions[this.index].check(faceData)
+ this.ingFun()
+ return this
+ } else if (this.actions[this.index].state === 'success') {
+ this.index++;
+ this.successFun()
+ return this
+ } else if (this.actions[this.index].state === 'fail') {
+ this.failFun()
+ return this
+ }
+ return this
+ }
+ end(fun) { //绑定 完成所有动作的回调
+ this.endFun = fun || this.__tempFun
+ return this
+ }
+ ing(fun) { //绑定 动作进行中
+ this.ingFun = fun || this.__tempFun
+ return this
+ }
+ success(fun) { //绑定 动作完成时
+ this.successFun = fun || this.__tempFun
+ return this
+ }
+ fail(fun) { //绑定 动作失败时
+ this.failFun = fun || this.__tempFun
+ return this
+ }
+}
+
+export default ActionContainer
diff --git a/uni_modules/face-bio-assay/components/face-bio-assay/actions/Action.js b/uni_modules/face-bio-assay/components/face-bio-assay/actions/Action.js
new file mode 100644
index 0000000..8190fa5
--- /dev/null
+++ b/uni_modules/face-bio-assay/components/face-bio-assay/actions/Action.js
@@ -0,0 +1,57 @@
+const STATE = {
+ ING:'ing',
+ SUCCESS:'success',
+ FAIL:'fail'
+}
+export default class Action {
+ constructor(second=10,fun,limit,initTip) {
+ this.second = second
+ this.endTime = Infinity
+ this.frames = []
+ this.tip = initTip
+ this.initTip = initTip
+ this.state = STATE.ING
+ this.fun = fun
+ this.limit = limit
+ }
+ end(){
+ if(this.fun){
+ this.fun(this.state)
+ }
+ }
+ check(faceData){
+ if(this.endTime === Infinity){
+ this.endTime = new Date().getTime() + (this.second*1000)
+ }
+ if(this.state !== STATE.ING ){
+ return
+ }
+ if(new Date().getTime()>this.endTime){
+ this.state = STATE.FAIL
+ this.end()
+ return
+ }
+ if(this.frames.length>=this.limit){
+ this.state = STATE.SUCCESS
+ this.end()
+ return
+ }
+ this.takeFrameAfter(faceData)?.takeFrame(faceData)
+ }
+ takeFrame(faceData){
+
+ }
+ takeFrameAfter(faceData){
+ let face = faceData.faceInfo[0]
+ this.tip = this.initTip
+ if(faceData.x == -1 || faceData.y == -1) {
+ this.tip = '检测不到人脸'
+ return null
+ }
+ if(faceData.faceInfo.length > 1) {
+ this.tip = '请保证只有一人做核验'
+ return null
+ }
+ return this
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/face-bio-assay/components/face-bio-assay/actions/NodHead.js b/uni_modules/face-bio-assay/components/face-bio-assay/actions/NodHead.js
new file mode 100644
index 0000000..319a2a0
--- /dev/null
+++ b/uni_modules/face-bio-assay/components/face-bio-assay/actions/NodHead.js
@@ -0,0 +1,24 @@
+import Action from "./Action.js"
+class NodHead extends Action {
+ constructor(second=10,fun) {
+ super(second,fun,1,'请点头')
+ this.maxPitch = 0
+ this.minPitch = 0
+ }
+ takeFrame(faceData){
+ let face = faceData.faceInfo[0]
+ if(face.angleArray.pitch>this.maxPitch){
+ this.maxPitch = face.angleArray.pitch
+ }
+ if(face.angleArray.pitch0.45 && (this.minPitch || this.maxPitch) ){
+ this.frames.push('点头')
+ this.maxPitch = 0
+ this.minPitch = 0
+ }
+ }
+}
+
+export default NodHead
\ No newline at end of file
diff --git a/uni_modules/face-bio-assay/components/face-bio-assay/actions/ShakeHead.js b/uni_modules/face-bio-assay/components/face-bio-assay/actions/ShakeHead.js
new file mode 100644
index 0000000..fbb7eff
--- /dev/null
+++ b/uni_modules/face-bio-assay/components/face-bio-assay/actions/ShakeHead.js
@@ -0,0 +1,24 @@
+import Action from "./Action.js"
+class ShakeHead extends Action {
+ constructor(second = 10, fun) {
+ super(second,fun,1,'请摇头')
+ this.minYaw = 0
+ this.maxYaw = 0
+ }
+ takeFrame(faceData) {
+ let face = faceData.faceInfo[0]
+ if(face.angleArray.yaw>this.maxYaw){
+ this.maxYaw = face.angleArray.yaw
+ }
+ if(face.angleArray.yaw0.45 && (this.minYaw || this.maxYaw) ){
+ this.frames.push('摇头')
+ this.minYaw = 0
+ this.maxYaw = 0
+ }
+ }
+}
+
+export default ShakeHead
diff --git a/uni_modules/face-bio-assay/components/face-bio-assay/actions/StraightenHead.js b/uni_modules/face-bio-assay/components/face-bio-assay/actions/StraightenHead.js
new file mode 100644
index 0000000..7266472
--- /dev/null
+++ b/uni_modules/face-bio-assay/components/face-bio-assay/actions/StraightenHead.js
@@ -0,0 +1,27 @@
+import Action from "./Action.js"
+class StraightenHead extends Action {
+ constructor(second = 10, fun) {
+ super(second, fun, 10, '请平视摄像头')
+ }
+ takeFrame(faceData) {
+ let face = faceData.faceInfo[0]
+ if (Math.abs(face.angleArray.pitch) >= 0.3 || Math.abs(face.angleArray.roll) >= 0.2 || Math.abs(face
+ .angleArray.yaw) >= 0.2) {
+ this.frames = []
+ return
+ }
+ if (Math.abs(face.confArray.global) <= 0.8 || Math.abs(face.confArray.leftEye) <= 0.8 || Math.abs(
+ face.confArray.mouth) <=
+ 0.8 || Math.abs(face.confArray.nose) <= 0.8 || Math.abs(face.confArray.rightEye) <= 0.8) {
+ this.tip = '请勿遮挡五官'
+ this.frames = []
+ return
+ }
+ this.tip = '正在核验,请保持'
+ this.frames.push('正')
+
+ }
+}
+
+
+export default StraightenHead
diff --git a/uni_modules/face-bio-assay/components/face-bio-assay/actions/index.js b/uni_modules/face-bio-assay/components/face-bio-assay/actions/index.js
new file mode 100644
index 0000000..3bc3ca3
--- /dev/null
+++ b/uni_modules/face-bio-assay/components/face-bio-assay/actions/index.js
@@ -0,0 +1,9 @@
+import NodHead from "./NodHead.js"
+import ShakeHead from "./ShakeHead.js"
+import StraightenHead from "./StraightenHead.js"
+
+export {
+ NodHead,
+ ShakeHead,
+ StraightenHead,
+}
\ No newline at end of file
diff --git a/uni_modules/face-bio-assay/components/face-bio-assay/face-bio-assay.vue b/uni_modules/face-bio-assay/components/face-bio-assay/face-bio-assay.vue
new file mode 100644
index 0000000..aeb30ae
--- /dev/null
+++ b/uni_modules/face-bio-assay/components/face-bio-assay/face-bio-assay.vue
@@ -0,0 +1,436 @@
+
+
+
+
+
+
+ 关闭
+
+
+
+
+
+
+ {{ face.pitch ? face.pitch.toFixed(2): 'null'}}
+
+
+ {{ face.roll ? face.roll.toFixed(2):'null'}}
+
+
+ {{ face.yaw ? face.yaw.toFixed(2):'null'}}
+
+
+
+
+ {{ isSuccess ? '人脸检测成功' : tipsText}}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/uni_modules/face-bio-assay/package.json b/uni_modules/face-bio-assay/package.json
new file mode 100644
index 0000000..50f561d
--- /dev/null
+++ b/uni_modules/face-bio-assay/package.json
@@ -0,0 +1,81 @@
+{
+ "id": "face-bio-assay",
+ "displayName": "face-bio-assay",
+ "version": "1.1.1",
+ "description": "纯前端简易版活体检测,易扩展,开发者可通过自定义动作 添加到该容器中管理。也可使用已有动作(点头、摇头、平视)进行排列组合创建动作组",
+ "keywords": [
+ "人脸识别",
+ "活体检测",
+ "易扩展"
+],
+ "repository": "",
+"engines": {
+ },
+ "dcloudext": {
+ "category": [
+ "前端组件",
+ "通用组件"
+ ],
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "u"
+ },
+ "App": {
+ "app-vue": "u",
+ "app-nvue": "u"
+ },
+ "H5-mobile": {
+ "Safari": "u",
+ "Android Browser": "u",
+ "微信浏览器(Android)": "u",
+ "QQ浏览器(Android)": "u"
+ },
+ "H5-pc": {
+ "Chrome": "u",
+ "IE": "u",
+ "Edge": "u",
+ "Firefox": "u",
+ "Safari": "u"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "u",
+ "百度": "u",
+ "字节跳动": "u",
+ "QQ": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/face-bio-assay/readme.md b/uni_modules/face-bio-assay/readme.md
new file mode 100644
index 0000000..aeb3ac3
--- /dev/null
+++ b/uni_modules/face-bio-assay/readme.md
@@ -0,0 +1,185 @@
+# props
+|参数名|类型|默认值|可选择值|描述|
+|----|----|----|---|---|
+|buildActionContainer|Function|null|""|创建ActionContainer类的函数,要求返回值必需是ActionContainer类|
+|actions|Array|[]|""|动作组(目前已有动作:点头,摇头,平视)|
+|isDev|boolen|false|true|是否是开发者模式,开启后可显示人脸的三个角度|
+
+# emits
+|方法名|参数|描述|
+|----|---|---|
+|detectFailed|[]|核验失败|
+|photoChange|[url:'照片路径']|拍照后的回调|
+|detectOver|[]|检测完成|
+|showData|[faceData:'人脸数据']|每一帧的人脸数据|
+# slots
+|插槽名|参数|描述|
+|----|---|---|
+|default|无|用户可配合showData钩子展示人脸数据方便调试|
+
+# 方法
+## initData
+开始进行人脸核验 使用案例:
+```
+//html
+
+//js
+//调用
+this.$refs.faceDetect.initData()
+```
+## takePhoto
+拍照获取照片,配合动作使用:
+ ```
+ 在每个动作创建时第二个参数是动作完成的回调 如平视动作的使用:
+ const fun = (state) => {
+ //state 有成功success和fail,ing(进行时不会触发该函数,忽略)
+ if (state === 'success') {
+ this.$refs.faceDetect.takePhoto() //调用拍照方法
+ }
+ }
+ let straightenHead = new StraightenHead(10, fun)
+ ```
+
+# 使用建议
+```
+ios中bug解决方案:
+在ios中,二次进入使用该组件有问题,解决办法:单独将该组件作为一个页面(或者下载demo查看),代码如下:
+
+//主页面
+
+
+
+
+
+
+
+
+
+
+
+//face.vue页
+
+
+
+
+
+
+
+
+
+```
+
+# 类的使用
+## Action
+ 动作类,开发者可继承该类重写 takeFrame 方法 如:
+```
+//点头动作
+import Action from "./Action.js"
+class NodHead extends Action {
+ constructor(second=10,fun) {
+ //时间限制(s),结束时回调,完成次数(limit),基本提示
+ super(second,fun,1,'请点头')
+ this.maxPitch = 0
+ this.minPitch = 0
+ }
+ takeFrame(faceData){
+ let face = faceData.faceInfo[0]
+ if(face.angleArray.pitch>this.maxPitch){
+ this.maxPitch = face.angleArray.pitch
+ }
+ if(face.angleArray.pitch0.45 && (this.minPitch || this.maxPitch) ){
+ this.frames.push('点头') //frames 完成的帧数组 根据该数组长度和limit判断是否完成
+ this.maxPitch = 0
+ this.minPitch = 0
+ }
+ }
+}
+
+export default NodHead
+```
+## ActionContainer
+ 动作容器的使用案例
+```
+buildActions() {
+ if (this.buildActionContainer) {
+ return this.buildActionContainer()
+ }
+ let actions = []
+ if (!this.actions?.length) {
+ let nodHead = new NodHead()
+ const fun = (state) => {
+ if (state === 'success') {
+ this.takePhoto() //拍照
+ }
+ }
+ let straightenHead = new StraightenHead(10, fun) //平视 结束拍照
+ let straightenHead2 = new StraightenHead(10) //平视
+ let shakeHead = new ShakeHead()
+ actions = [straightenHead2, nodHead, shakeHead, straightenHead]
+ } else {
+ actions = this.actions
+ }
+ //new ActionContainer(actions,...)
+ //动作组 actions
+ //起始动作下标 index
+ //初始提示 tip
+ //绑定 完成所有动作的回调 endFun
+ //绑定 动作进行中 ingFun
+ //绑定 动作完成时 successFun
+ //绑定 动作失败时 failFun
+ let act = new ActionContainer(actions)
+ act.end(() => { //也可用该方法绑定endFun方法
+ this.detectOver()
+ }).fail(() => { //也可用该方法绑定failFun方法
+ this.cameraError()
+ })
+ //....其他方法类似
+ return act
+ }
+```
\ No newline at end of file
diff --git a/uni_modules/face-bio-assay/static/images/cover.png b/uni_modules/face-bio-assay/static/images/cover.png
new file mode 100644
index 0000000..b41f26a
Binary files /dev/null and b/uni_modules/face-bio-assay/static/images/cover.png differ