@ -0,0 +1,268 @@ | |||
<template> | |||
<view class="page__view"> | |||
<navbar title="人脸验证" leftClick @leftClick="$utils.navigateBack" color="#191919" bgColor="#FFFFFF" /> | |||
<view class="main"> | |||
<view class="card"> | |||
<view class="card-header"> | |||
<view>为保证本人操作,请进行人脸验证</view> | |||
<view class="card-desc">请保持正脸在取景框根据屏幕提示完成识别</view> | |||
</view> | |||
<view class="camera-box"> | |||
<!-- todo: check clear --> | |||
<image class="camera-bg" src="@/pages_order/static/order/camera-bg.png" mode="scaleToFill" /> | |||
<template v-if="countdown"> | |||
<view class="camera-countdown"> | |||
<uv-count-down | |||
:time="3 * 1000" | |||
autoStart | |||
@change="onCountDownChange" | |||
@finish="onCountDownFinish" | |||
> | |||
<view class="camera-countdown-text">{{ timeData.seconds }}s</view> | |||
</uv-count-down> | |||
</view> | |||
</template> | |||
<template v-if="scanning"> | |||
<view class="flex camera-fg"> | |||
<view style="position: relative;"> | |||
<camera class="camera" device-position="front" flash="auto" @initdone="onCameraInited" @error="onCameraError"></camera> | |||
<cover-image class="camera-tag" src="@/pages_order/static/order/tag.png"></cover-image> | |||
</view> | |||
</view> | |||
</template> | |||
<!-- todo: delete --> | |||
<template v-else-if="src"> | |||
<view class="flex camera-fg"> | |||
<image class="camera" :src="src" mode="scaleToFill" /> | |||
</view> | |||
</template> | |||
</view> | |||
<view class="flex tips"> | |||
<view class="flex flex-column tips-item"> | |||
<image class="tips-icon" src="@/pages_order/static/order/tips-1.png" mode="widthFix"></image> | |||
<view class="tips-text">保持光线充足</view> | |||
</view> | |||
<view class="flex flex-column tips-item"> | |||
<image class="tips-icon" src="@/pages_order/static/order/tips-2.png" mode="widthFix"></image> | |||
<view class="tips-text">脸在取景框内</view> | |||
</view> | |||
<view class="flex flex-column tips-item"> | |||
<image class="tips-icon" src="@/pages_order/static/order/tips-3.png" mode="widthFix"></image> | |||
<view class="tips-text">面部正对平面</view> | |||
</view> | |||
</view> | |||
</view> | |||
</view> | |||
<view class="bottom"> | |||
<button class="btn" @click="onVerify">开始识别</button> | |||
</view> | |||
</view> | |||
</template> | |||
<script> | |||
export default { | |||
data() { | |||
return { | |||
src: null, | |||
scanning: false, | |||
countdown: false, | |||
timeData: {}, | |||
} | |||
}, | |||
methods: { | |||
onVerify() { | |||
// todo | |||
this.scanning = true | |||
}, | |||
onCameraInited() { | |||
this.countdown = true | |||
}, | |||
onCountDownChange(e) { | |||
this.timeData = e | |||
}, | |||
onCountDownFinish() { | |||
this.countdown = false | |||
this.takePhoto() | |||
}, | |||
takePhoto() { | |||
const ctx = uni.createCameraContext(); | |||
ctx.takePhoto({ | |||
quality: 'high', | |||
success: (res) => { | |||
this.src = res.tempImagePath | |||
this.scanning = false | |||
// todo: fetch match | |||
setTimeout(() => { | |||
this.$utils.navigateBack() | |||
}, 1500) | |||
} | |||
}); | |||
}, | |||
onCameraError(e) { | |||
console.log(e.detail); | |||
} | |||
}, | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.page__view { | |||
width: 100vw; | |||
min-height: 100vh; | |||
background-color: $uni-bg-color; | |||
position: relative; | |||
/deep/ .nav-bar__view { | |||
position: fixed; | |||
top: 0; | |||
left: 0; | |||
} | |||
} | |||
.main { | |||
padding: calc(var(--status-bar-height) + 144rpx) 32rpx 224rpx 32rpx; | |||
} | |||
.card { | |||
padding: 32rpx; | |||
background: #FAFAFF; | |||
border: 2rpx solid #FFFFFF; | |||
border-radius: 32rpx; | |||
& + & { | |||
margin-top: 40rpx; | |||
} | |||
&-header { | |||
font-family: PingFang SC; | |||
font-weight: 500; | |||
font-size: 36rpx; | |||
line-height: 1.4; | |||
color: #252545; | |||
margin-bottom: 48rpx; | |||
} | |||
&-desc { | |||
margin-top: 4rpx; | |||
font-family: PingFang SC; | |||
font-weight: 400; | |||
font-size: 26rpx; | |||
line-height: 1.4; | |||
color: #4E4E69; | |||
} | |||
} | |||
.camera { | |||
&-box { | |||
width: 622rpx; | |||
height: 678rpx; | |||
position: relative; | |||
} | |||
&-bg { | |||
width: 100%; | |||
height: 100%; | |||
} | |||
&-countdown { | |||
position: absolute; | |||
top: 14rpx; | |||
left: 50%; | |||
transform: translateX(-50%); | |||
&-text { | |||
font-family: PingFang SC; | |||
font-weight: 600; | |||
font-size: 72rpx; | |||
line-height: 1.4; | |||
color: $uni-color; | |||
} | |||
} | |||
&-fg { | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
width: 100%; | |||
height: 100%; | |||
} | |||
& { | |||
width: 310rpx; | |||
height: 310rpx; | |||
border-radius: 50%; | |||
} | |||
&-tag { | |||
position: absolute; | |||
left: 50%; | |||
bottom: 0; | |||
transform: translate(-50%, 60%); | |||
width: 248rpx; | |||
height: 160rpx; | |||
} | |||
} | |||
.tips { | |||
margin-top: 32rpx; | |||
padding: 0 32rpx; | |||
column-gap: 24rpx; | |||
&-item { | |||
flex: 1; | |||
row-gap: 24rpx; | |||
} | |||
&-icon { | |||
width: 72rpx; | |||
height: auto; | |||
} | |||
&-text { | |||
font-family: PingFang SC; | |||
font-weight: 400; | |||
font-size: 22rpx; | |||
line-height: 1.4; | |||
color: #989898; | |||
} | |||
} | |||
.bottom { | |||
position: fixed; | |||
left: 0; | |||
bottom: 0; | |||
width: 100vw; | |||
height: 200rpx; | |||
padding: 24rpx 40rpx; | |||
background: #FFFFFF; | |||
box-sizing: border-box; | |||
.btn { | |||
width: 100%; | |||
padding: 16rpx 0; | |||
box-sizing: border-box; | |||
font-family: PingFang SC; | |||
font-weight: 500; | |||
font-size: 36rpx; | |||
line-height: 1; | |||
color: #FFFFFF; | |||
background-image: linear-gradient(to right, #4B348F, #845CFA); | |||
border-radius: 41rpx; | |||
} | |||
} | |||
</style> |
@ -0,0 +1,242 @@ | |||
<template> | |||
<view class="page__view"> | |||
<navbar title="身份认证" leftClick @leftClick="$utils.navigateBack" color="#191919" bgColor="#FFFFFF" /> | |||
<view class="main"> | |||
<view class="card"> | |||
<view class="card-header"> | |||
<view>身份认证</view> | |||
<view class="card-desc">信息仅用于身份认证,平台将保障你的信息安全</view> | |||
</view> | |||
<view class="form"> | |||
<uv-form | |||
ref="form" | |||
:model="form" | |||
:rules="rules" | |||
errorType="toast" | |||
> | |||
<view class="form-item"> | |||
<uv-form-item prop="idFront" :customStyle="formItemStyle"> | |||
<view class="form-item-content"> | |||
<view class="upload" @click="onUpload('idFront')"> | |||
<image v-if="form.idFront" class="upload-img" :src="form.idFront" mode="scaleToFill" /> | |||
<view v-else class="flex upload-default"> | |||
<image class="upload-default-img" src="@/pages_order/static/order/id-front-default.png" mode="aspectFill" /> | |||
</view> | |||
</view> | |||
</view> | |||
<view class="form-item-label">身份证国徽面(点击上传)</view> | |||
</uv-form-item> | |||
</view> | |||
<view class="form-item"> | |||
<uv-form-item prop="idBack" :customStyle="formItemStyle"> | |||
<view class="form-item-content"> | |||
<view class="upload" @click="onUpload('idBack')"> | |||
<image v-if="form.idBack" class="upload-img" :src="form.idBack" mode="scaleToFill" /> | |||
<view v-else class="flex upload-default"> | |||
<image class="upload-default-img" src="@/pages_order/static/order/id-back-default.png" mode="aspectFill" /> | |||
</view> | |||
</view> | |||
</view> | |||
<view class="form-item-label">身份证人像面(点击上传)</view> | |||
</uv-form-item> | |||
</view> | |||
</uv-form> | |||
</view> | |||
</view> | |||
</view> | |||
<view class="bottom"> | |||
<button class="btn" @click="onConfirm">确认</button> | |||
</view> | |||
</view> | |||
</template> | |||
<script> | |||
export default { | |||
data() { | |||
return { | |||
form: { | |||
idFront: null, | |||
idBack: null, | |||
}, | |||
rules: { | |||
'idFront': { | |||
type: 'string', | |||
required: true, | |||
message: '请上传身份证国徽面', | |||
}, | |||
'idBack': { | |||
type: 'string', | |||
required: true, | |||
message: '请上传身份证人像面', | |||
}, | |||
}, | |||
formItemStyle: { padding: 0 }, | |||
} | |||
}, | |||
methods: { | |||
onUpload(key) { | |||
uni.chooseImage({ | |||
count: 1, | |||
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有 | |||
success: res => { | |||
let avatarUrl = res.tempFilePaths[0] // 将选择的图片赋值给我们定义的cover | |||
this.$Oss.ossUpload(avatarUrl) | |||
.then(url => { | |||
this.form[key] = url | |||
}) | |||
} | |||
}); | |||
}, | |||
async onConfirm() { | |||
try { | |||
const res = await this.$refs.form.validate() | |||
console.log('onSave res', res) | |||
// todo: save | |||
// todo: check | |||
// this.$utils.redirectTo(`/pages_order/order/userInfo/facialVerify`) | |||
this.$utils.redirectTo('/pages_order/order/userInfo/facialVerifyCustom') | |||
} catch (err) { | |||
console.log('onSave err', err) | |||
} | |||
}, | |||
}, | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.page__view { | |||
width: 100vw; | |||
min-height: 100vh; | |||
background-color: $uni-bg-color; | |||
position: relative; | |||
/deep/ .nav-bar__view { | |||
position: fixed; | |||
top: 0; | |||
left: 0; | |||
} | |||
} | |||
.main { | |||
padding: calc(var(--status-bar-height) + 144rpx) 32rpx 224rpx 32rpx; | |||
} | |||
.card { | |||
padding: 32rpx; | |||
background: #FAFAFF; | |||
border: 2rpx solid #FFFFFF; | |||
border-radius: 32rpx; | |||
& + & { | |||
margin-top: 40rpx; | |||
} | |||
&-header { | |||
font-family: PingFang SC; | |||
font-weight: 500; | |||
font-size: 36rpx; | |||
line-height: 1.4; | |||
color: #252545; | |||
margin-bottom: 48rpx; | |||
} | |||
&-desc { | |||
margin-top: 4rpx; | |||
font-family: PingFang SC; | |||
font-weight: 400; | |||
font-size: 26rpx; | |||
line-height: 1.4; | |||
color: #4E4E69; | |||
} | |||
} | |||
.form { | |||
padding: 8rpx 0 0 0; | |||
&-item { | |||
& + & { | |||
margin-top: 48rpx; | |||
} | |||
&-label { | |||
margin-top: 32rpx; | |||
text-align: center; | |||
font-family: PingFang SC; | |||
font-weight: 400; | |||
font-size: 30rpx; | |||
line-height: 1.4; | |||
color: #4E4E69; | |||
} | |||
} | |||
} | |||
.upload { | |||
position: relative; | |||
width: 100%; | |||
height: 386rpx; | |||
overflow: hidden; | |||
border-radius: 32rpx; | |||
box-shadow: -5rpx -5rpx 10rpx 0 #FFFFFF, | |||
10rpx 10rpx 20rpx 0 #AAAACC80, | |||
4rpx 4rpx 10rpx 0 #AAAACC40, | |||
-2rpx -2rpx 5rpx 0 #FFFFFF; | |||
&-img { | |||
width: 100%; | |||
height: 100%; | |||
} | |||
&-default { | |||
width: 100%; | |||
height: 100%; | |||
background-image: linear-gradient(#FAFAFF, #F3F3F3); | |||
&-img { | |||
width: 240rpx; | |||
height: 240rpx; | |||
} | |||
} | |||
} | |||
.bottom { | |||
position: fixed; | |||
left: 0; | |||
bottom: 0; | |||
width: 100vw; | |||
height: 200rpx; | |||
padding: 24rpx 40rpx; | |||
background: #FFFFFF; | |||
box-sizing: border-box; | |||
.btn { | |||
width: 100%; | |||
padding: 16rpx 0; | |||
box-sizing: border-box; | |||
font-family: PingFang SC; | |||
font-weight: 500; | |||
font-size: 36rpx; | |||
line-height: 1; | |||
color: #FFFFFF; | |||
background-image: linear-gradient(to right, #4B348F, #845CFA); | |||
border-radius: 41rpx; | |||
} | |||
} | |||
</style> |
@ -0,0 +1,269 @@ | |||
<template> | |||
<view class="page__view"> | |||
<navbar title="填写个人信息" leftClick @leftClick="$utils.navigateBack" color="#191919" bgColor="#FFFFFF" /> | |||
<view class="main"> | |||
<view class="card"> | |||
<view class="card-header">申请信息</view> | |||
<view class="form"> | |||
<uv-form | |||
ref="form" | |||
:model="form" | |||
:rules="rules" | |||
errorType="toast" | |||
> | |||
<view class="form-item"> | |||
<uv-form-item prop="name" :customStyle="formItemStyle"> | |||
<view class="form-item-label">联系人</view> | |||
<view class="form-item-content"> | |||
<formInput v-model="form.name"></formInput> | |||
</view> | |||
</uv-form-item> | |||
</view> | |||
<view class="form-item"> | |||
<uv-form-item prop="phone" :customStyle="formItemStyle"> | |||
<view class="form-item-label">手机号</view> | |||
<view class="form-item-content"> | |||
<formInput v-model="form.phone"></formInput> | |||
</view> | |||
</uv-form-item> | |||
</view> | |||
<view class="form-item"> | |||
<uv-form-item prop="phone" :customStyle="formItemStyle"> | |||
<view class="form-item-label">所在地区</view> | |||
<view class="form-item-content"> | |||
<picker mode="region" @change="onAreaChange" :value="form.area"> | |||
<view class="flex region"> | |||
<view v-if="form.area">{{ form.area.join('') }}</view> | |||
<view v-else class="placeholder">选择省市区街道</view> | |||
</view> | |||
</picker> | |||
</view> | |||
</uv-form-item> | |||
</view> | |||
<view class="form-item"> | |||
<uv-form-item prop="address" :customStyle="formItemStyle"> | |||
<view class="form-item-label">详细地址</view> | |||
<view class="form-item-content"> | |||
<formInput v-model="form.address" placeholder="小区楼栋、门牌号、村等"></formInput> | |||
</view> | |||
</uv-form-item> | |||
</view> | |||
</uv-form> | |||
</view> | |||
</view> | |||
</view> | |||
<view class="bottom"> | |||
<button class="btn" @click="onConfirm">确认</button> | |||
</view> | |||
</view> | |||
</template> | |||
<script> | |||
import { mapState } from 'vuex' | |||
import util from '@/utils/utils.js' | |||
import formInput from '@/pages_order/components/formInput.vue' | |||
export default { | |||
components: { | |||
formInput, | |||
}, | |||
data() { | |||
return { | |||
form: { | |||
name: null, | |||
phone: null, | |||
area: null, | |||
address: null, | |||
}, | |||
rules: { | |||
'name': { | |||
type: 'string', | |||
required: true, | |||
message: '请输入联系人', | |||
}, | |||
'phone': { | |||
type: 'string', | |||
required: true, | |||
message: '请输入正确的手机号', | |||
validator: (rule, value, callback) => { | |||
return util.validatePhone(value) | |||
}, | |||
}, | |||
'area': { | |||
type: 'array', | |||
required: true, | |||
message: '请选择省市区', | |||
}, | |||
'address': { | |||
type: 'string', | |||
required: true, | |||
message: '请输入详细地址', | |||
}, | |||
}, | |||
formItemStyle: { padding: 0 }, | |||
} | |||
}, | |||
onReady() { | |||
this.$refs.form.setRules(this.rules) | |||
}, | |||
methods: { | |||
onAreaChange(e) { | |||
this.form.area = e.detail.value | |||
}, | |||
async onConfirm() { | |||
try { | |||
const res = await this.$refs.form.validate() | |||
console.log('onSave res', res) | |||
// todo: save | |||
this.$utils.redirectTo(`/pages_order/order/userInfo/idUpload`) | |||
} catch (err) { | |||
console.log('onSave err', err) | |||
} | |||
}, | |||
}, | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.page__view { | |||
width: 100vw; | |||
min-height: 100vh; | |||
background-color: $uni-bg-color; | |||
position: relative; | |||
/deep/ .nav-bar__view { | |||
position: fixed; | |||
top: 0; | |||
left: 0; | |||
} | |||
} | |||
.main { | |||
padding: calc(var(--status-bar-height) + 144rpx) 32rpx 224rpx 32rpx; | |||
} | |||
.card { | |||
padding: 32rpx; | |||
background: #FAFAFF; | |||
border: 2rpx solid #FFFFFF; | |||
border-radius: 32rpx; | |||
& + & { | |||
margin-top: 40rpx; | |||
} | |||
&-header { | |||
font-family: PingFang SC; | |||
font-weight: 500; | |||
font-size: 36rpx; | |||
line-height: 1.4; | |||
color: #252545; | |||
margin-bottom: 32rpx; | |||
} | |||
} | |||
.row { | |||
justify-content: space-between; | |||
font-family: PingFang SC; | |||
font-weight: 400; | |||
line-height: 1.4; | |||
column-gap: 24rpx; | |||
& + & { | |||
margin-top: 32rpx; | |||
} | |||
&-label { | |||
flex: none; | |||
font-size: 26rpx; | |||
color: #8B8B8B; | |||
} | |||
&-content { | |||
font-size: 32rpx; | |||
color: #181818; | |||
} | |||
} | |||
.form { | |||
padding: 8rpx 0 0 0; | |||
&-item { | |||
border-bottom: 2rpx solid #EEEEEE; | |||
&:last-child { | |||
border: none; | |||
} | |||
& + & { | |||
margin-top: 40rpx; | |||
} | |||
&-label { | |||
font-family: PingFang SC; | |||
font-weight: 400; | |||
font-size: 26rpx; | |||
line-height: 1.4; | |||
color: #181818; | |||
} | |||
&-content { | |||
margin-top: 14rpx; | |||
padding: 6rpx 0; | |||
.placeholder { | |||
color: #C6C6C6; | |||
font-size: 32rpx; | |||
font-weight: 400; | |||
} | |||
.region { | |||
min-height: 44rpx; | |||
justify-content: flex-start; | |||
} | |||
} | |||
} | |||
} | |||
.bottom { | |||
position: fixed; | |||
left: 0; | |||
bottom: 0; | |||
width: 100vw; | |||
height: 200rpx; | |||
padding: 24rpx 40rpx; | |||
background: #FFFFFF; | |||
box-sizing: border-box; | |||
.btn { | |||
width: 100%; | |||
padding: 16rpx 0; | |||
box-sizing: border-box; | |||
font-family: PingFang SC; | |||
font-weight: 500; | |||
font-size: 36rpx; | |||
line-height: 1; | |||
color: #FFFFFF; | |||
background-image: linear-gradient(to right, #4B348F, #845CFA); | |||
border-radius: 41rpx; | |||
} | |||
} | |||
</style> |