| @ -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> | |||