Browse Source

贵茶 1 爱签短链改长连

Handy_Cao 1 năm trước cách đây
mục cha
commit
6f66189faa

+ 29 - 1
src/business/user/account.ts

@@ -1,6 +1,6 @@
 import { shallowRef, ref, reactive } from 'vue'
 import { useLoginStore, useAccountStore, useUserStore } from '@/stores'
-import { investorDel, modifyPassword, requestAddAuth, requestAddUser, requestBankCard4, requestCaptcaResend, requestCaptchaVerify, requestCreateContract, requestCreateContractAndAddSigner, requestCreateFlowByTemplateDirectly, requestSignCompleted, requestWillFace, queryTencentUsereSignRecords } from '@/services/api/account'
+import { investorDel, modifyPassword, requestAddAuth, requestAddUser, requestBankCard4, requestCaptcaResend, requestCaptchaVerify, requestCreateContract, requestCreateContractAndAddSigner, requestCreateFlowByTemplateDirectly, requestSignCompleted, requestWillFace, queryTencentUsereSignRecords, requestAsignWillFace } from '@/services/api/account'
 import cryptojs from 'crypto-js'
 import { decryptAES } from '@/services/websocket/package/crypto'
 
@@ -205,6 +205,34 @@ export function useRequestWillFace() {
 }
 
 /**
+ * 个人意愿核身认证-爱签长连
+ */
+export function useRequestAsignWillFace() {
+    const loading = shallowRef(false)
+
+    const asignWillFaceFormData = reactive<Model.WillFaceReq>({})
+
+    const asignWillFace = async () => {
+        try {
+            loading.value = true
+            return await  requestAsignWillFace({
+                data: {
+                    ...asignWillFaceFormData
+                }
+            })
+        } finally {
+            loading.value = false
+        }
+    }
+
+    return {
+        loading,
+        asignWillFaceFormData,
+        asignWillFace
+    }
+}
+
+/**
  * 通过合同名和模板 id 直接发起签署流程
  */
 export function useRequestCreateFlowByTemplateDirectly(memberId?: number) {

+ 46 - 9
src/packages/gcszt/views/account/protocol/Index.vue

@@ -16,13 +16,14 @@
                 <template v-for="(item, index) in dataList" :key="index">
                     <Cell :title="item.templatename" :icon="iconName(item.recordstatus)" @click="signer(item)" is-link />
                 </template>
+                <template v-if="thirdStepList.length != 0">
+                    <template v-for="(item, index) in thirdStepList" :key="index">
+                        <Cell title="视频认证" :icon="iconName(item.recordstatus)" @click="faceAuth(item.recordstatus)"
+                            :is-link="item.recordstatus !== 3" />
+                    </template>
+                </template>
             </CellGroup>
         </div>
-        <!-- <template #footer>
-            <div class="g-form__footer inset">
-                <Button type="danger" @click="onClose()" round block>完成合同签署</Button>
-            </div>
-        </template> -->
     </app-view>
 </template>
 
@@ -33,7 +34,7 @@ import { fullloading, dialog } from '@/utils/vant';
 import { useNavigation } from '@mobile/router/navigation'
 import { useRequest } from '@/hooks/request'
 import { queryUsereSignRecords } from '@/services/api/account';
-import { useRequestCreateContract } from '@/business/user/account';
+import { useRequestCreateContract, useRequestAsignWillFace } from '@/business/user/account';
 import plus from '@/utils/h5plus'
 import { getUserId } from '@/services/methods/user'
 import { useUserStore } from '@/stores'
@@ -47,20 +48,24 @@ const memberUserId = getQueryStringToNumber('memberUserId')
 const userStore = useUserStore()
 /// 创建电子签合同
 const { createTemplate, createFormData } = useRequestCreateContract()
+/// 意愿视频认证
+const { asignWillFace, asignWillFaceFormData } = useRequestAsignWillFace()
 /// 电子签合同信息
 const dataList = shallowRef<Model.UsereSignRecordsRsq[]>([])
+// 步骤3列表
+const thirdStepList = shallowRef<Model.UsereSignRecordsRsq[]>([])
 /// 用户信息
 const { customername, cardnum, bankaccount, bankbankname, mobile2 } = userStore.userInfo
 /// 查询
 const { run } = useRequest(queryUsereSignRecords, {
     params: {
         userId: getUserId(),
-        memberUserId: memberUserId,
-        templatetype: 2
+        memberUserId: memberUserId
     },
     onSuccess: (res) => {
         if (res.data != null && res.data.length != 0) {
-            dataList.value = res.data
+            dataList.value = res.data.filter(e => e.templatetype === 2 )
+            thirdStepList.value = res.data.filter(e => e.templatetype === 3 )
         }
     }
 })
@@ -90,6 +95,38 @@ const openWebview = (url: string) => {
     }
 }
 
+// 视频认证
+const faceAuth = (status: number) => {
+    if (status !== 3) {
+        plus.requestPermissionCamera({
+            onSuccess: () => {
+                plus.requestPermissionRecordAudio({
+                    onSuccess: () => {
+                        /// 进行视频认证
+                        asignWillFaceFormData.idCardNo = decryptAES(cardnum)
+                        asignWillFaceFormData.realName = customername
+                        /// loading
+                        fullloading((hideLoading) => {
+                            asignWillFace().then((res) => {
+                                hideLoading()
+                                openWebview(res.data.faceUrl)
+                            }).catch((err) => {
+                                hideLoading(err, 'fail')
+                            })
+                        })
+                    },
+                    onError: (err) => {
+                        showFailToast(err)
+                    }
+                })
+            },
+            onError: (err) => {
+                showFailToast(err)
+            }
+        })
+    }
+}
+
 const signer = (item: Model.UsereSignRecordsRsq) => {
     ///  如果是已签署
     if ([2, 3].includes(item.recordstatus)) {

+ 2 - 2
src/packages/gcszt/views/mine/Index.vue

@@ -150,10 +150,10 @@ import { useNavigation } from '@mobile/router/navigation'
 import { AuthStatus } from '@/constants/account'
 import { queryBankAccountSign } from '@/services/api/bank'
 import { useLoginStore, useAccountStore, useUserStore } from '@/stores'
-import eventBus from '@/services/bus'
-import Iconfont from '@/components/base/iconfont/index.vue'
 import { getMemberUserId } from '@/services/methods/user'
 import { queryUsereSignRecords } from '@/services/api/account'
+import eventBus from '@/services/bus'
+import Iconfont from '@/components/base/iconfont/index.vue'
 
 const { router, routerTo } = useNavigation()
 const loginStore = useLoginStore()

+ 46 - 4
src/packages/gzcj/views/account/protocol/Index.vue

@@ -16,6 +16,12 @@
                 <template v-for="(item, index) in dataList" :key="index">
                     <Cell :title="item.templatename" :icon="iconName(item.recordstatus)" @click="signer(item)" is-link />
                 </template>
+                <template v-if="thirdStepList.length != 0">
+                    <template v-for="(item, index) in thirdStepList" :key="index">
+                        <Cell title="视频认证" :icon="iconName(item.recordstatus)" @click="faceAuth(item.recordstatus)"
+                            :is-link="item.recordstatus !== 3" />
+                    </template>
+                </template>
             </CellGroup>
         </div>
     </app-view>
@@ -28,7 +34,7 @@ import { fullloading, dialog } from '@/utils/vant';
 import { useNavigation } from '@mobile/router/navigation'
 import { useRequest } from '@/hooks/request'
 import { queryUsereSignRecords } from '@/services/api/account';
-import { useRequestCreateContract } from '@/business/user/account';
+import { useRequestCreateContract, useRequestAsignWillFace } from '@/business/user/account';
 import plus from '@/utils/h5plus'
 import { getUserId } from '@/services/methods/user'
 import { useUserStore } from '@/stores'
@@ -42,20 +48,24 @@ const memberUserId = getQueryStringToNumber('memberUserId')
 const userStore = useUserStore()
 /// 创建电子签合同
 const { createTemplate, createFormData } = useRequestCreateContract()
+/// 意愿视频认证
+const { asignWillFace, asignWillFaceFormData } = useRequestAsignWillFace()
 /// 电子签合同信息
 const dataList = shallowRef<Model.UsereSignRecordsRsq[]>([])
+// 步骤3列表
+const thirdStepList = shallowRef<Model.UsereSignRecordsRsq[]>([])
 /// 用户信息
 const { customername, cardnum, bankaccount, bankbankname, mobile2 } = userStore.userInfo
 /// 查询
 const { run } = useRequest(queryUsereSignRecords, {
     params: {
         userId: getUserId(),
-        memberUserId: memberUserId,
-        templatetype: 2
+        memberUserId: memberUserId
     },
     onSuccess: (res) => {
         if (res.data != null && res.data.length != 0) {
-            dataList.value = res.data
+            dataList.value = res.data.filter(e => e.templatetype === 2 )
+            thirdStepList.value = res.data.filter(e => e.templatetype === 3 )
         }
     }
 })
@@ -85,6 +95,38 @@ const openWebview = (url: string) => {
     }
 }
 
+// 视频认证
+const faceAuth = (status: number) => {
+    if (status !== 3) {
+        plus.requestPermissionCamera({
+            onSuccess: () => {
+                plus.requestPermissionRecordAudio({
+                    onSuccess: () => {
+                        /// 进行视频认证
+                        asignWillFaceFormData.idCardNo = decryptAES(cardnum)
+                        asignWillFaceFormData.realName = customername
+                        /// loading
+                        fullloading((hideLoading) => {
+                            asignWillFace().then((res) => {
+                                hideLoading()
+                                openWebview(res.data.faceUrl)
+                            }).catch((err) => {
+                                hideLoading(err, 'fail')
+                            })
+                        })
+                    },
+                    onError: (err) => {
+                        showFailToast(err)
+                    }
+                })
+            },
+            onError: (err) => {
+                showFailToast(err)
+            }
+        })
+    }
+}
+
 const signer = (item: Model.UsereSignRecordsRsq) => {
     ///  如果是已签署
     if ([2, 3].includes(item.recordstatus)) {

+ 18 - 2
src/packages/mobile/views/account/authresult/Index.vue

@@ -11,10 +11,10 @@
                 <Cell title="证件正面照片">
                     <Image fit="contain" :src="getFileUrl(userInfo.cardfrontphotourl)" width="100" height="100" />
                 </Cell>
-                <Cell title="证件反面照片">
+                <Cell title="证件反面照片" v-if="showCardBackPhoto === 1">
                     <Image fit="contain" :src="getFileUrl(userInfo.cardbackphotourl)" width="100" height="100" />
                 </Cell>
-                <Cell title="手持证件照">
+                <Cell title="手持证件照" v-if="showHalfBodyPhoto === 1">
                     <Image fit="contain" :src="getFileUrl(userInfo.halfbodyphotourl)" width="100" height="100" />
                 </Cell>
                 <Cell title="实名状态" :value="getAuthStatusName(2)" />
@@ -32,8 +32,11 @@ import { queryWrDraftUserInfo } from '@/services/api/account'
 import { useRequest } from '@/hooks/request'
 import { getCertificateTypeCodeName } from '@/constants/account'
 import { getFileUrl } from '@/filters'
+import { getWskhOpenAccountConfigs } from '@/services/api/account'
 
 const userInfo = shallowRef<Model.UserInfo>()
+const showHalfBodyPhoto = shallowRef(0)
+const showCardBackPhoto = shallowRef(0)
 
 /// 查询托管银行信息
 useRequest(queryWrDraftUserInfo, {
@@ -42,4 +45,17 @@ useRequest(queryWrDraftUserInfo, {
         userInfo.value = res.data[0]
     }
 })
+
+// 获取网上开户配置
+useRequest(getWskhOpenAccountConfigs, {
+    params: {
+        configs: '53,54'
+    },
+    onSuccess: (res) => {
+        /// 是否显示半身照和 证件背面照
+        showHalfBodyPhoto.value = res.data.filter(e => { e.configid === 54})[0].configid ?? 0
+        showCardBackPhoto.value = res.data.filter(e => { e.configid === 53})[0].configid ?? 0
+    }
+})
+
 </script>

+ 5 - 0
src/packages/qxst/router/index.ts

@@ -122,6 +122,11 @@ const routes: Array<RouteRecordRaw> = [
         name: 'account-authresult',
         component: () => import('@mobile/views/account/authresult/Index.vue'),
       },
+      {
+        path: 'protocol',
+        name: 'account-protocol',
+        component: () => import('../views/account/protocol/Index.vue'),
+      }
     ],
   },
   {

+ 195 - 85
src/packages/qxst/views/account/certification/Index.vue

@@ -4,30 +4,41 @@
             <app-navbar title="实名认证" />
         </template>
         <Form ref="formRef" class="g-form__container" @submit="onCheckCardNum" :loading="loading">
-            <CellGroup inset>
-                <Field v-model="formData.name" name="name" label="姓名" placeholder="请输入用户姓名" :rules="formRules.name"
-                    :readonly="isReadonly" />
-                <Field v-model="formData.mobile" name="mobile" readonly label="手机号码" />
-                <Field name="idCardType" label="证件类型" :rules="formRules.idCardType" is-link>
+            <!-- 个人 -->
+            <CellGroup v-if="getUserInfoType() === 1" inset>
+                <Field v-model="person.realName" :readonly="isReadonly && realName != ''" name="realName" label="姓名" placeholder="请输入用户姓名" :rules="PFormRules.realName" />
+                <Field v-model="person.mobile" name="mobile" readonly label="预留手机号" :rules="PFormRules.mobile"/>
+                <Field name="bankId" label="开户银行" :rules="PFormRules.bankId" is-link>
                     <template #input>
-                        <app-select v-model="formData.idCardType" placeholder="请选择证件类型"
-                            :options="getAQCertificateTypeList()" :readonly="isReadonly" />
+                        <app-select v-model="person.bankId" placeholder="请选择开户银行" :readonly="isReadonly && bankaccount != ''" :options="banklist"
+                            :optionProps="{ label: 'bankname', value: 'bankid' }" />
                     </template>
                 </Field>
-                <Field v-model="formData.idCard" name="cardnum" label="证件号码" placeholder="请输入证件号码" :rules="formRules.idCard"
-                    :readonly="isReadonly" />
-                <Field name="idCardPhoto" label="证件正面照片" :rules="formRules.idCardPhoto">
+                <Field label="银行卡号" v-model="person.bankCard" :readonly="isReadonly && bankaccount != ''" name="bankCard" placeholder="请输入银行卡号" :rules="PFormRules.bankCard" />
+                <span style="font-size: 12px; padding-left: 15px; color: #FF162F;">此银行卡作为后续签约银行卡,请谨慎填写</span>
+                <Field v-model="person.idCardNo" :readonly="isReadonly && cardnum != ''" name="idCardNo" label="证件号码" placeholder="请输入证件号码" :rules="PFormRules.idCardNo" />
+                <Field name="idCardPhoto" label="证件正面照片" :rules="PFormRules.idCardPhoto">
                     <template #input>
-                        <Image fit="contain" :src="getFileUrl(formData.idCardPhoto)" width="100" height="100"
-                            v-if="isReadonly" />
-                        <app-uploader @success="f_afterRead" v-else />
+                        <app-uploader @success="f_afterRead" />
                     </template>
                 </Field>
-                <Field name="idCardPhotoBackURL" label="证件反面照片" :rules="formRules.idCardPhotoBackURL">
+                <Field name="cardbackphotourl" label="证件反面照片" :rules="PFormRules.idCardPhotoBackURL">
                     <template #input>
-                        <Image fit="contain" :src="getFileUrl(formData.idCardPhotoBackURL)" width="100" height="100"
-                            v-if="isReadonly" />
-                        <app-uploader @success="b_afterRead" v-else />
+                        <app-uploader @success="b_afterRead" />
+                    </template>
+                </Field>
+            </CellGroup>
+            <!-- 企业 -->
+            <CellGroup inset v-else>
+                <Field v-model="company.realName" :readonly="isReadonly && realName != ''" name="realName" label="法人姓名" placeholder="请输入法人姓名" :rules="CFormRules.realName" />
+                <Field v-model="company.mobile" name="mobile" readonly label="法人手机号" />
+                <Field v-model="company.companyName" name="companyName" label="企业名称" placeholder="请输入企业名称" :rules="CFormRules.companyName" />
+                <Field v-model="company.creditCode" name="creditCode" label="信用代码" placeholder="请输入社会统一信用代码" :rules="CFormRules.creditCode" />
+                <Field v-model="company.bankCard" :readonly="isReadonly && bankaccount != ''" name="bankCard" label="法人银行卡号" placeholder="请输入法人银行卡号" :rules="CFormRules.bankCard" />
+                <Field v-model="company.idCardNo" :readonly="isReadonly && cardnum != ''" name="idCardNo" label="法人身份证号" placeholder="请输入法人身份证号" :rules="CFormRules.idCardNo" />
+                    <Field name="idCardPhoto" label="营业执照" :rules="CFormRules.idCardPhoto">
+                    <template #input>
+                        <app-uploader @success="f_afterRead" />
                     </template>
                 </Field>
             </CellGroup>
@@ -35,59 +46,79 @@
         <img src="../../../assets/images/certification.png" />
         <template #footer>
             <div class="g-form__footer inset">
-                <Button type="danger" :loading="buttonLoading" @click="onSubmit" round block>提交实名认证</Button>
+                <Button type="danger" :loading="buttonLoading" @click="formRef?.submit" round block>提交实名认证验证</Button>
             </div>
         </template>
-        <component ref="componentRef" v-bind="{ formData }" :is="componentMap.get(componentId)" @closed="closeComponent"
+        <component ref="componentRef" v-bind="{ serialNo }" :is="componentMap.get(componentId)" @closed="closeComponent"
             v-if="componentId" />
     </app-view>
 </template>
 
 <script lang="ts" setup>
-import { shallowRef, defineAsyncComponent, onMounted, computed } from 'vue'
-import { CellGroup, Button, Field, Form, FormInstance, showFailToast, FieldRule, Image } from 'vant'
+import { shallowRef, onMounted, ref, defineAsyncComponent } from 'vue'
+import { useComponent } from '@/hooks/component'
+import { CellGroup, Button, Field, Form, FormInstance, showFailToast, FieldRule } from 'vant'
 import { fullloading, dialog } from '@/utils/vant'
-import { getFileUrl } from '@/filters'
-import { getAQCertificateTypeList } from "@/constants/account"
+import { getIdCardAge } from '@/filters'
 import { useRequest } from '@/hooks/request'
-import { queryUserESignRecord, requestCheckCardNum } from '@/services/api/account'
-import { adddUserReq } from '@/business/user/account'
+import { queryTencentUsereSignRecords, requestCheckCardNum } from '@/services/api/account'
+import { useRequestBankCard4 } from '@/business/user/account'
 import { validateRules } from '@/constants/regex'
-import { useComponent } from '@/hooks/component'
 import { useUserStore } from '@/stores'
-import { getIdCardAge } from '@/filters'
 import { useNavigation } from '@mobile/router/navigation'
-import AppSelect from '@mobile/components/base/select/index.vue'
+import { getUserId, getMemberUserId, getUserInfoType } from '@/services/methods/user'
+import { decryptAES } from '@/services/websocket/package/crypto'
+import { queryBankAccountSign } from '@/services/api/bank'
+import { useQueryCusBankSignBank } from '@/business/bank'
 import AppUploader from '@mobile/components/base/uploader/index.vue'
+import AppSelect from '@mobile/components/base/select/index.vue'
 
 const componentMap = new Map<string, unknown>([
-    ['certification-next', defineAsyncComponent(() => import('./components/certification-next/Index.vue'))], // 爱签-实名认证第二步
+    ['captcha', defineAsyncComponent(() => import('./components/captcha/Index.vue'))],
 ])
-
+const { banklist } = useQueryCusBankSignBank()
 const { router } = useNavigation()
 const userStore = useUserStore()
 const formRef = shallowRef<FormInstance>()
-const { formData, formSubmit, loading } = adddUserReq()
-
+/// 个人信息
+const person = ref<Model.PersonBankCard4>({})
+/// 公司信息
+const company = ref<Model.CompanyBankCard4>({})
+/// 四要素校验
+const { formData, onBankCard4, loading } = useRequestBankCard4()
+const { customername, mobile2, cardnum, legalpersonname } = userStore.userInfo
 const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => {
     router.back()
 })
+/// 流水号
+const serialNo = ref<string>('')
+/// 是否只读
+const isReadonly = ref(false)
+const bankaccount = ref('')
+const realName = ref('')
 
-const isReadonly = computed(() => userESignRecords.value.some((e) => e.templatetype === 1 && e.recordstatus === 3))
+/// 查询托管银行信息
+useRequest(queryBankAccountSign, {
+    onSuccess: (res) => {
+        /// 签约状态
+        if (res.data.length != 0) {
+            /// 如果已经签约了 不允许修改信息
+            const { bankaccountname, bankaccountno2, bankid } = res.data[0]
+            isReadonly.value = res.data.some(e => [4].includes(e.signstatus))
+            person.value.bankCard = decryptAES(bankaccountno2)
+            person.value.bankId = bankid
+            bankaccount.value = bankaccountno2
+            realName.value = bankaccountname
+            person.value.realName = realName.value
+        } 
+    }
+})
 
 /// 查询记录
-const { loading: buttonLoading, dataList: userESignRecords, run: getUserESignRecord } = useRequest(queryUserESignRecord, {
-    onSuccess: (res) => {
-        const record = res.data.find((e) => e.templatetype === 1 && e.recordstatus === 3)
-        if (record) {
-            const { name, idCard, idCardPhoto, idCardPhotoBackURL, mobile, idCardType } = JSON.parse(record.authinfo || '{}')
-            formData.name = name
-            formData.idCard = idCard
-            formData.idCardPhoto = idCardPhoto
-            formData.idCardPhotoBackURL = idCardPhotoBackURL
-            formData.idCardType = idCardType
-            formData.mobile = mobile
-        }
+const { loading: buttonLoading } = useRequest(queryTencentUsereSignRecords, {
+    params: {
+        userId: getUserId(),
+        memberUserId: getMemberUserId()
     },
     onError: (err) => {
         showFailToast(err)
@@ -95,18 +126,27 @@ const { loading: buttonLoading, dataList: userESignRecords, run: getUserESignRec
 })
 
 const b_afterRead = (filePath: string) => {
-    formData.idCardPhotoBackURL = filePath
+    if (getUserInfoType() === 1) {
+        person.value.idCardPhotoBackURL = filePath
+    }
 }
 
 const f_afterRead = (filePath: string) => {
-    formData.idCardPhoto = filePath
+    if (getUserInfoType() === 1) {
+        person.value.idCardPhoto = filePath
+    } else {
+        company.value.idCardPhoto = filePath
+    }
 }
 
-// 表单验证规则
-const formRules: { [key in keyof Model.AddUserReq]?: FieldRule[] } = {
-    name: [{
+// 个人信息表单验证规则
+const PFormRules: { [key in keyof Model.PersonBankCard4]?: FieldRule[] } = {
+    realName: [{
         required: true,
         message: '请输入用户姓名',
+        validator: () => {
+            return !!person.value.realName
+        }
     }],
     mobile: [{
         required: true,
@@ -118,7 +158,23 @@ const formRules: { [key in keyof Model.AddUserReq]?: FieldRule[] } = {
             return validateRules.phone.message
         }
     }],
-    idCard: [{
+    bankId: [{
+        message: '请选择开户银行',
+        validator: () => {
+            return !!person.value.bankId
+        }
+    }],
+    bankCard: [{
+        required: true,
+        message: '请输入银行卡号',
+        validator: (val) => {
+            if (validateRules. bankcardno.validate(val)) {
+                return true
+            }
+            return validateRules.bankcardno.message
+        }
+    }],
+    idCardNo: [{
         required: true,
         message: '请输入证件号码',
         validator: (val) => {
@@ -131,47 +187,97 @@ const formRules: { [key in keyof Model.AddUserReq]?: FieldRule[] } = {
             return validateRules.cardno.message
         }
     }],
-    idCardPhotoBackURL: [{
+    idCardPhoto: [{
         required: true,
         message: '请上传证件背面照片',
     }],
-    idCardPhoto: [{
+    idCardPhotoBackURL: [{
         required: true,
         message: '请上传证件正面照片',
     }],
 }
 
+// 企业信息表单验证规则
+const CFormRules: { [key in keyof Model.CompanyBankCard4]?: FieldRule[] } = {
+    realName: [{
+        required: true,
+        message: '请输入法人姓名',
+    }],
+    companyName: [{
+        required: true,
+        message: '请输入企业名称',
+    }],
+    creditCode: [{
+        required: true,
+        message: '请输入社会统一信用代码',
+    }],
+    mobile: [{
+        required: true,
+        message: '请输入手机号码',
+        validator: (val) => {
+            if (validateRules.phone.validate(val)) {
+                return true
+            }
+            return validateRules.phone.message
+        }
+    }],
+    bankCard: [{
+        required: true,
+        message: '请输入法人银行卡号(仅限印有“银联”字样的银行卡)',
+        validator: (val) => {
+            if (validateRules. bankcardno.validate(val)) {
+                return true
+            }
+            return validateRules.bankcardno.message
+        }
+    }],
+    idCardNo: [{
+        required: true,
+        message: '请输入法人身份证号',
+        validator: (val) => {
+            if (validateRules.cardno.validate(val)) {
+                if (getIdCardAge(val)) {
+                    return '开户失败,您的年龄不符合开户要求'
+                } 
+                return true
+            }
+            return validateRules.cardno.message
+        }
+    }],
+    idCardPhoto: [{
+        required: true,
+        message: '请上传营业执照',
+    }],
+}
+
 const onCheckCardNum = () => {
     fullloading((hideLoading) => {
         requestCheckCardNum({
             data: {
-                cardnum: formData.idCard
+                cardnum: getUserInfoType() === 1 ? person.value.idCardNo : company.value.idCardNo
             }
         }).then(() => {
-            formSubmit().then(() => {
+            // 实体类型 1:个人 2:企业
+            formData.type = getUserInfoType()
+            // 个人
+            if ( getUserInfoType() === 1) {
+                formData.person = person.value
+            } else {
+                // 企业
+                formData.company = company.value
+            }
+            // 发送验证
+            onBankCard4().then((res) => {
                 hideLoading()
-                getUserESignRecord()
-                dialog('提交请求成功').then(() => {
-                    /// 进行下一步
-                    openComponent('certification-next')
+                /// 流水号
+                serialNo.value = res.data.serialNo
+                /// 已发送验证码
+                dialog('验证码已发送!').then(() => {
+                    /// 发送验证码
+                    openComponent('captcha')
                 })
             }).catch((err) => {
-                switch (err) {
-                    case '100020':
-                        hideLoading('个人三要素信息验证失败', 'fail')
-                        break
-                    case '100021':
-                        hideLoading('用户已存在', 'fail')
-                        break
-                    case '100726':
-                        hideLoading('该条实名记录为核验记录,无法用于添加用户', 'fail')
-                        break
-                    case '100727':
-                        hideLoading('实名认证类型和添加用户类型不匹配', 'fail')
-                        break
-                    default:
-                        hideLoading(err, 'fail')
-                }
+                hideLoading(err, 'fail')
             })
         }).catch((err) => {
             hideLoading(err, 'fail')
@@ -179,16 +285,20 @@ const onCheckCardNum = () => {
     })
 }
 
-const onSubmit = () => {
-    const isInclude = userESignRecords.value.some((e) => e.templatetype === 1 && e.recordstatus === 1)
-    if (!userESignRecords.value.length || isInclude) {
-        formRef.value?.submit()
+onMounted(() => {
+    // 个人
+    if (getUserInfoType() === 1) {
+        person.value.idCardNo = decryptAES(cardnum)
+        person.value.mobile = mobile2
+        realName.value = customername
+        person.value.realName = realName.value
     } else {
-        openComponent('certification-next')
+        // 企业
+        company.value.mobile = mobile2
+        company.value.creditCode = decryptAES(cardnum)
+        realName.value = legalpersonname
+        company.value.realName = realName.value
+        company.value.companyName = customername
     }
-}
-
-onMounted(() => {
-    formData.mobile = userStore.userInfo?.mobile2 ?? ''
 })
-</script>
+</script>

+ 118 - 0
src/packages/qxst/views/account/certification/components/captcha/Index.vue

@@ -0,0 +1,118 @@
+<template>
+    <app-modal direction="right-top" height="100%" width="100%" v-model:show="showModal" :loading="loading" :refresh="refresh">
+        <app-view class="g-form">
+            <template #header>
+                <app-navbar title="验证码" @back="closed" />
+            </template>
+            <Form ref="formRef" class="g-form__container" @submit="formSubmit">
+                <CellGroup inset>
+                    <Field v-model="CaptchaVerifyFormData.captcha" type="digit" name="captcha" label="短信验证码" placeholder="必填" :rules="formRules.captcha" autocomplete="off">
+                        <template #button>
+                            <Button size="small" type="danger" :disabled="isCountdown" @click="sendVerifyCode">
+                                <span v-if="isCountdown">重新发送({{ currentTime.seconds }})</span>
+                                <span v-else>获取验证码</span>
+                            </Button>
+                        </template>
+                    </Field>
+                </CellGroup> 
+            </Form>
+            <template #footer>
+                <div class="g-form__footer inset">
+                    <Button type="danger" round block @click="formRef?.submit()">提交验证</Button>
+                </div>
+            </template>
+        </app-view>
+    </app-modal>
+</template>
+
+<script lang="ts" setup>
+
+import { shallowRef, ref, computed, onMounted } from 'vue'
+import { CellGroup, Button, Field, Form, FormInstance, FieldRule, showFailToast } from 'vant'
+import { fullloading, dialog } from '@/utils/vant'
+import { useCountDown } from '@vant/use'
+import { useRequestCaptchaVerify, useRequestCaptcaResend } from '@/business/user/account';
+import { getUserInfoType } from '@/services/methods/user'
+const { loading, CaptchaVerifyFormData, onCaptchaVerify } = useRequestCaptchaVerify()
+const { formData, onCaptcaResend } = useRequestCaptcaResend()
+import AppModal from '@/components/base/modal/index.vue'
+
+const showModal = shallowRef(true)
+const refresh = shallowRef(false) // 是否刷新父组件数据
+const formRef = shallowRef<FormInstance>()
+
+// 倒计时函数
+const countdown = useCountDown({
+    time: 60 * 1000,
+    onFinish: () => {
+        countdown.reset()
+        isCountdown.value = false
+    }
+})
+const isCountdown = ref(false) // 是否正在倒计时
+// 倒计时剩余时间
+const currentTime = computed(() => countdown.current.value)
+
+const props = defineProps({
+    serialNo: {
+        type: String,
+        required: true,
+    }
+})
+
+// 发送手机验证码
+const sendVerifyCode = () => {
+    formData.serialNo = props.serialNo
+    /// 重新发送验证码
+    onCaptcaResend().then(() => {
+        isCountdown.value = true
+        countdown.start()
+    }).catch(() => {
+        showFailToast('发送失败')
+    })
+}
+
+// // 表单验证规则
+const formRules: { [key in keyof Model.CaptchaVerifyReq]?: FieldRule[] } = {
+    captcha: [{
+        message: '请输入短信验证码',
+        validator: () => {
+            return !!CaptchaVerifyFormData.captcha
+        }
+    }],
+}
+
+const formSubmit = () => {
+    fullloading((hideLoading) => {
+        /// 参数信息
+        CaptchaVerifyFormData.serialNo = props.serialNo
+        CaptchaVerifyFormData.type = getUserInfoType()
+        /// 短信校验
+        onCaptchaVerify().then(() => {
+            hideLoading()
+            dialog('实名认证提交成功!').then(() => {
+                closed(true)
+            })
+        }).catch((err) => {
+            hideLoading(err, 'fail')
+        })
+    })
+}
+
+// 关闭弹窗
+const closed = (isRefresh = false) => {
+    refresh.value = isRefresh
+    showModal.value = false
+}
+
+// 暴露组件属性给父组件调用
+defineExpose({
+    closed,
+})
+
+onMounted(() => {
+    isCountdown.value = true
+    /// 默认倒计时
+    countdown.start()
+})
+</script>

+ 0 - 207
src/packages/qxst/views/account/certification/components/certification-next/Index.vue

@@ -1,207 +0,0 @@
-<template>
-    <app-modal direction="right-top" height="100%" width="100%" v-model:show="showModal" :refresh="refresh">
-        <app-view class="g-form">
-            <template #header>
-                <app-navbar title="实名认证" @back="closed" />
-            </template>
-            <div class="g-form__container">
-                <CellGroup inset>
-                    <Cell title="姓名" :value="formData.name" />
-                    <Cell title="手机号码" :value="formData.mobile" />
-                    <Cell title="证件类型" :value="getAQCertificateTypeListName(formData.idCardType ?? 1)" />
-                    <Cell title="证件号码" :value="formData.idCard" />
-                    <Cell title="证件正面照片">
-                        <template #value>
-                            <Image fit="contain" :src="idCardPhoto" width="100" height="100" />
-                        </template>
-                    </Cell>
-                    <Cell title="证件反面照片">
-                        <template #value>
-                            <Image fit="contain" :src="idCardPhotoBackURL" width="100" height="100" />
-                        </template>
-                    </Cell>
-                </CellGroup>
-                <CellGroup inset>
-                    <template v-for="(item, index) in secondStepList" :key="index">
-                        <Cell :title="item.templatename" :icon="iconName(item.recordstatus)" @click="signer(item)"
-                            is-link />
-                    </template>
-                    <template v-if="secondStepList.every(e => e.recordstatus === 3)">
-                        <template v-for="(item, index) in thirdStepList" :key="index">
-                            <Cell title="视频认证" :icon="iconName(item.recordstatus)" @click="faceAuth(item.recordstatus)"
-                                :is-link="item.recordstatus !== 3" />
-                        </template>
-                    </template>
-                </CellGroup>
-            </div>
-            <template #footer>
-                <div class="g-form__footer inset">
-                    <Button type="danger" :disabled="dataList.some((e) => e.recordstatus !== 3)" @click="onSubmit()" round
-                        block>提交认证</Button>
-                </div>
-            </template>
-        </app-view>
-    </app-modal>
-</template>
-
-<script lang="ts" setup>
-import { shallowRef, computed, PropType } from 'vue'
-import { CellGroup, Button, Cell, showFailToast, Image, showToast } from 'vant'
-import { fullloading, dialog } from '@/utils/vant';
-import { getAQCertificateTypeListName } from "@/constants/account";
-import { useRequest } from '@/hooks/request'
-import { queryUserESignRecord } from '@/services/api/account';
-import { useRequestCreateContractAndAddSigner, useRequestSignCompleted, useRequestWillFace } from '@/business/user/account';
-import plus from '@/utils/h5plus'
-import eventBus from '@/services/bus'
-import { getFileUrl } from '@/filters';
-import AppModal from '@/components/base/modal/index.vue'
-
-const showModal = shallowRef(true)
-// 是否刷新父组件数据
-const refresh = shallowRef(false)
-const { createSigner, templateNoFormData } = useRequestCreateContractAndAddSigner()
-/// 意愿视频认证
-const { willFace, willFaceFormData } = useRequestWillFace()
-const { signCompleted } = useRequestSignCompleted()
-
-/// 查询
-const { run, dataList } = useRequest(queryUserESignRecord)
-
-// 步骤2列表
-const secondStepList = computed(() => dataList.value.filter(obj => obj.templatetype === 2))
-// 步骤3列表
-const thirdStepList = computed(() => dataList.value.filter(obj => obj.templatetype === 3))
-
-const iconName = (type: number) => {
-    switch (type) {
-        case 2: return 'info-o'
-        case 4: return 'close'
-        case 3: return 'passed'
-        default: return 'circle'
-    }
-}
-
-// 正面照
-const idCardPhoto = computed(() => {
-    const idCardPhoto = props.formData.idCardPhoto ?? ''
-    const image = idCardPhoto.split(',')[0]
-    return getFileUrl(image)
-})
-
-// 背面照
-const idCardPhotoBackURL = computed(() => {
-    const idCardPhotoBackURL = props.formData.idCardPhotoBackURL ?? ''
-    const image = idCardPhotoBackURL.split(',')[0]
-    return getFileUrl(image)
-})
-
-const openWebview = (url: string) => {
-    const ua = window.navigator.userAgent.toLowerCase()
-    if (ua.indexOf('micromessenger') !== -1) {
-        showToast({
-            type: 'fail',
-            message: '请使用浏览器打开此页面'
-        })
-    } else {
-        plus.openWebview({
-            url,
-            titleText: '实名认证',
-            onClose: () => run()
-        })
-    }
-}
-
-const props = defineProps({
-    formData: {
-        type: Object as PropType<Model.AddUserReq>,
-        required: true,
-    }
-})
-
-const signer = (item: Model.UserESignRecordRsq) => {
-    ///  如果是已签署
-    if (item.recordstatus === 2) {
-        item.signurl ? openWebview(item.signurl) : showFailToast('合同地址错误')
-    } else if (item.recordstatus === 3) {
-        const fileUrl = getFileUrl(item.contractfileaddr)
-        item.contractfileaddr ? plus.openURL(fileUrl) : showFailToast('合同地址错误')
-    } else {
-        fullloading((hideLoading) => {
-            templateNoFormData.templateNo = item.templateno
-            /// 创建合同
-            createSigner().then((res) => {
-                hideLoading()
-                openWebview(res.data.signUrl)
-            }).catch((err) => {
-                hideLoading(err, 'fail')
-            })
-        })
-    }
-}
-
-// 视频认证
-const faceAuth = (status: number) => {
-    if (status !== 3) {
-        plus.requestPermissionCamera({
-            onSuccess: () => {
-                plus.requestPermissionRecordAudio({
-                    onSuccess: () => {
-                        /// 进行视频认证
-                        willFaceFormData.idCardNo = props.formData.idCard
-                        willFaceFormData.realName = props.formData.name
-                        /// loading
-                        fullloading((hideLoading) => {
-                            willFace().then((res) => {
-                                hideLoading()
-                                openWebview(res.data.faceUrl)
-                            }).catch((err) => {
-                                hideLoading(err, 'fail')
-                            })
-                        })
-                    },
-                    onError: (err) => {
-                        showFailToast(err)
-                    }
-                })
-            },
-            onError: (err) => {
-                showFailToast(err)
-            }
-        })
-    }
-}
-
-/// 最终提交
-const onSubmit = () => {
-    fullloading((hideLoading) => {
-        signCompleted().then(() => {
-            hideLoading()
-            dialog('实名认证提交请求成功').then(() => {
-                closed(true)
-            })
-        }).catch((err) => {
-            hideLoading(err, 'fail')
-        })
-    })
-}
-
-// 接收窗口页面状态通知
-const documentVisibilityStateNotify = eventBus.$on('DocumentVisibilityStateNotify', (state) => {
-    if (state === 'visible') {
-        run()
-    }
-})
-
-// 关闭弹窗
-const closed = (isRefresh = false) => {
-    refresh.value = isRefresh
-    showModal.value = false
-    documentVisibilityStateNotify.cancel()
-}
-
-// 暴露组件属性给父组件调用
-defineExpose({
-    closed,
-})
-</script>

+ 168 - 0
src/packages/qxst/views/account/protocol/Index.vue

@@ -0,0 +1,168 @@
+<template>
+    <app-view class="g-form">
+        <template #header>
+            <app-navbar title="入市协议" />
+        </template>
+        <div class="g-form__container">
+            <CellGroup inset>
+                <Cell title="名称" :value="customername" />
+                <Cell title="手机号码" :value="mobile2" />
+                <Cell title="证件号码" :value="decryptAES(cardnum)" />
+                <Cell title="开户银行" :value="bankbankname" />
+                <Cell title="银行卡号" :value="decryptAES(bankaccount)" />
+                <Cell title="签署机构" :value="memberUserId" />
+            </CellGroup>
+            <CellGroup inset>
+                <template v-for="(item, index) in dataList" :key="index">
+                    <Cell :title="item.templatename" :icon="iconName(item.recordstatus)" @click="signer(item)" is-link />
+                </template>
+                <template v-if="thirdStepList.length != 0">
+                    <template v-for="(item, index) in thirdStepList" :key="index">
+                        <Cell title="视频认证" :icon="iconName(item.recordstatus)" @click="faceAuth(item.recordstatus)"
+                            :is-link="item.recordstatus !== 3" />
+                    </template>
+                </template>
+            </CellGroup>
+        </div>
+    </app-view>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, onMounted } from 'vue'
+import { CellGroup, Cell, showFailToast, showToast } from 'vant'
+import { fullloading, dialog } from '@/utils/vant';
+import { useNavigation } from '@mobile/router/navigation'
+import { useRequest } from '@/hooks/request'
+import { queryUsereSignRecords } from '@/services/api/account';
+import { useRequestCreateContract, useRequestAsignWillFace } from '@/business/user/account';
+import plus from '@/utils/h5plus'
+import { getUserId } from '@/services/methods/user'
+import { useUserStore } from '@/stores'
+import { decryptAES } from '@/services/websocket/package/crypto'
+import eventBus from '@/services/bus'
+
+const { getQueryStringToNumber } = useNavigation()
+/// 所属机构
+const memberUserId = getQueryStringToNumber('memberUserId')
+/// userStore
+const userStore = useUserStore()
+/// 创建电子签合同
+const { createTemplate, createFormData } = useRequestCreateContract()
+/// 意愿视频认证
+const { asignWillFace, asignWillFaceFormData } = useRequestAsignWillFace()
+/// 电子签合同信息
+const dataList = shallowRef<Model.UsereSignRecordsRsq[]>([])
+// 步骤3列表
+const thirdStepList = shallowRef<Model.UsereSignRecordsRsq[]>([])
+/// 用户信息
+const { customername, cardnum, bankaccount, bankbankname, mobile2 } = userStore.userInfo
+/// 查询
+const { run } = useRequest(queryUsereSignRecords, {
+    params: {
+        userId: getUserId(),
+        memberUserId: memberUserId
+    },
+    onSuccess: (res) => {
+        if (res.data != null && res.data.length != 0) {
+            dataList.value = res.data.filter(e => e.templatetype === 2 )
+            thirdStepList.value = res.data.filter(e => e.templatetype === 3 )
+        }
+    }
+})
+
+const iconName = (type: number) => {
+    switch (type) {
+        case 2: return 'info-o'
+        case 4: return 'close'
+        case 3: return 'passed'
+        default: return 'circle'
+    }
+}
+
+const openWebview = (url: string) => {
+    const ua = window.navigator.userAgent.toLowerCase()
+    if (ua.indexOf('micromessenger') !== -1) {
+        showToast({
+            type: 'fail',
+            message: '请使用浏览器打开此页面'
+        })
+    } else {
+        plus.openWebview({
+            url,
+            titleText: '实名认证',
+            onClose: () => run()
+        })
+    }
+}
+
+// 视频认证
+const faceAuth = (status: number) => {
+    if (status !== 3) {
+        plus.requestPermissionCamera({
+            onSuccess: () => {
+                plus.requestPermissionRecordAudio({
+                    onSuccess: () => {
+                        /// 进行视频认证
+                        asignWillFaceFormData.idCardNo = decryptAES(cardnum)
+                        asignWillFaceFormData.realName = customername
+                        /// loading
+                        fullloading((hideLoading) => {
+                            asignWillFace().then((res) => {
+                                hideLoading()
+                                openWebview(res.data.faceUrl)
+                            }).catch((err) => {
+                                hideLoading(err, 'fail')
+                            })
+                        })
+                    },
+                    onError: (err) => {
+                        showFailToast(err)
+                    }
+                })
+            },
+            onError: (err) => {
+                showFailToast(err)
+            }
+        })
+    }
+}
+
+const signer = (item: Model.UsereSignRecordsRsq) => {
+    ///  如果是已签署
+    if ([2, 3].includes(item.recordstatus)) {
+        item.signurl ? openWebview(item.signurl) : showFailToast('合同地址错误')
+    } else {
+        fullloading((hideLoading) => {
+            createFormData.recordId = item.recordid
+            /// 创建合同
+            createTemplate().then((res) => {
+                hideLoading()
+                /// 更新数据
+                run()
+                dialog({
+                    message: '是否立即签署协议?',
+                    showCancelButton: true,
+                    cancelButtonText: '暂不签署',
+                    confirmButtonText: '立即签署'
+                }).then(() => {
+                    openWebview(res.data.signUrl)
+                })
+            }).catch((err) => {
+                hideLoading(err, 'fail')
+            })
+        })
+    }
+}
+
+// 接收窗口页面状态通知
+const documentVisibilityStateNotify = eventBus.$on('DocumentVisibilityStateNotify', (state) => {
+    if (state === 'visible') {
+        run()
+    }
+})
+
+onMounted(() => {
+    documentVisibilityStateNotify.cancel()
+})
+
+</script>

+ 70 - 11
src/packages/qxst/views/mine/Index.vue

@@ -92,9 +92,15 @@
                         <Iconfont icon="g-icon-certification">实名认证</Iconfont>
                     </template>
                 </Cell>
-                <Cell is-link :to="{ name: 'bank-sign' }" v-if="authStatus === AuthStatus.Certified">
+                <Cell is-link :to="{ name: 'bank-sign' }" v-if="authStatus === AuthStatus.Certified && hasSignContract">
                     <template #title>
-                        <Iconfont icon="g-icon-sign">签约账户</Iconfont>
+                        <Iconfont icon="g-icon-sign">银行签约</Iconfont>
+                    </template>
+                </Cell>
+                <Cell is-link :to="{ name: 'account-protocol', query: { memberUserId: getMemberUserId() } }"
+                    v-if="userStore.userType != 2 && authStatus === AuthStatus.Certified">
+                    <template #title>
+                        <Iconfont icon="g-icon-order--line">入市协议</Iconfont>
                     </template>
                 </Cell>
                 <Cell is-link :to="{ name: 'mine-profile' }">
@@ -144,6 +150,8 @@ import { useNavigation } from '@mobile/router/navigation'
 import { AuthStatus } from '@/constants/account'
 import { queryBankAccountSign } from '@/services/api/bank'
 import { useLoginStore, useAccountStore, useUserStore } from '@/stores'
+import { getMemberUserId } from '@/services/methods/user'
+import { queryUsereSignRecords } from '@/services/api/account'
 import eventBus from '@/services/bus'
 import Iconfont from '@/components/base/iconfont/index.vue'
 
@@ -154,6 +162,9 @@ const accountStore = useAccountStore()
 const { currentAccount } = accountStore.$toRefs()
 
 const headerRef = shallowRef<HTMLDivElement>()
+/// 判断是否能签约
+const hasSignContract = shallowRef(false)
+// 实名认证状态
 const authStatus = computed(() => userStore.userAccount.hasauth) // 实名认证状态
 
 const onReady = (el: HTMLDivElement) => {
@@ -164,21 +175,51 @@ const onReady = (el: HTMLDivElement) => {
 /// 进行出入金操作判断
 const doInOutMoney = (tab: string) => {
     if (authStatus.value === AuthStatus.Certified) {
+        // 判断是否已签约
         fullloading((hideLoading) => {
             queryBankAccountSign().then((res) => {
                 hideLoading()
                 const { signstatus } = res.data[0] ?? {}
-                /// 只有已签约的情况下才可以进行出入金
+                /// 判断当前有签约
                 if (signstatus && signstatus === 4) {
-                    router.push({ name: 'bank-wallet', query: { tab } })
+                    /// 已实名未签合同
+                    if (!hasSignContract.value) {
+                        dialog({
+                            message: '根据平台规定,请先通过《入市协议》签署协议,谢谢!',
+                            showCancelButton: true,
+                            cancelButtonText: '暂不签署',
+                            confirmButtonText: '立即签署'
+                        })
+                        .then(() => {
+                            router.push({ name: 'account-protocol', query: { memberUserId: getMemberUserId()} })
+                        })
+                        .catch(() => {
+                            router.push({ name: 'bank-wallet', query: { tab } })
+                        })
+                    } else {
+                        router.push({ name: 'bank-wallet', query: { tab } })
+                    }
                 } else {
-                    dialog({
-                        message: '请先添加签约账户信息!',
-                        showCancelButton: true,
-                        confirmButtonText: '去签约'
-                    }).then(() => {
-                        router.push({ name: 'bank-sign' })
-                    })
+                    /// 未签约
+                    if (!hasSignContract.value) {
+                        dialog({
+                            message: '根据平台规定,请先通过《入市协议》签署协议,谢谢!',
+                            showCancelButton: true,
+                            cancelButtonText: '关闭',
+                            confirmButtonText: '立即签署'
+                        }).then(() => {
+                            router.push({ name: 'account-protocol', query: { memberUserId: getMemberUserId()} })
+                        })
+                    } else {
+                        dialog({
+                            message: '根据平台规定,请先通过《银行签约》签约银行,谢谢!',
+                            showCancelButton: true,
+                            cancelButtonText: '关闭',
+                            confirmButtonText: '立即签约'
+                        }).then(() => {
+                            router.push({ name: 'bank-sign' })
+                        })
+                    }
                 }
             }).catch(() => {
                 hideLoading('加载失败', 'fail')
@@ -210,6 +251,24 @@ onActivated(() => {
         userStore.getUserData()
     }
     accountStore.getAccountList()
+
+    /// 机构不需要签合同
+    if (userStore.userInfo.usertype === 2) {
+        hasSignContract.value = true
+        return
+    }
+
+    /// 查询数据
+    queryUsereSignRecords({
+        data: {
+            userId: loginStore.userId,
+            memberUserId: getMemberUserId(),
+            templatetype: 2
+        }
+    }).then((res) => {
+        /// 判断是否能签约
+        hasSignContract.value = !res.data.some(e => e.recordstatus != 3)
+    })
 })
 </script>
 

+ 46 - 4
src/packages/zrwyt/views/account/protocol/Index.vue

@@ -16,6 +16,12 @@
                 <template v-for="(item, index) in dataList" :key="index">
                     <Cell title-class="cell__title" :title="item.templatename" :value="item.recordstatus != 3 ? '去签署' : '去查看'" value-class="cell__value" :icon="iconName(item.recordstatus)" @click="signer(item)" is-link />
                 </template>
+                <template v-if="thirdStepList.length != 0">
+                    <template v-for="(item, index) in thirdStepList" :key="index">
+                        <Cell title="视频认证" :icon="iconName(item.recordstatus)" @click="faceAuth(item.recordstatus)"
+                            :is-link="item.recordstatus !== 3" />
+                    </template>
+                </template>
             </CellGroup>
         </div>
     </app-view>
@@ -28,7 +34,7 @@ import { fullloading, dialog } from '@/utils/vant';
 import { useNavigation } from '@mobile/router/navigation'
 import { useRequest } from '@/hooks/request'
 import { queryUsereSignRecords } from '@/services/api/account';
-import { useRequestCreateContract } from '@/business/user/account';
+import { useRequestCreateContract, useRequestAsignWillFace } from '@/business/user/account';
 import plus from '@/utils/h5plus'
 import { getUserId } from '@/services/methods/user'
 import { useUserStore } from '@/stores'
@@ -42,20 +48,24 @@ const memberUserId = getQueryStringToNumber('memberUserId')
 const userStore = useUserStore()
 /// 创建电子签合同
 const { createTemplate, createFormData } = useRequestCreateContract()
+/// 意愿视频认证
+const { asignWillFace, asignWillFaceFormData } = useRequestAsignWillFace()
 /// 电子签合同信息
 const dataList = shallowRef<Model.UsereSignRecordsRsq[]>([])
+// 步骤3列表
+const thirdStepList = shallowRef<Model.UsereSignRecordsRsq[]>([])
 /// 用户信息
 const { customername, cardnum, bankaccount, bankbankname, mobile2 } = userStore.userInfo
 /// 查询
 const { run } = useRequest(queryUsereSignRecords, {
     params: {
         userId: getUserId(),
-        memberUserId: memberUserId,
-        templatetype: 2
+        memberUserId: memberUserId
     },
     onSuccess: (res) => {
         if (res.data != null && res.data.length != 0) {
-            dataList.value = res.data
+            dataList.value = res.data.filter(e => e.templatetype === 2 )
+            thirdStepList.value = res.data.filter(e => e.templatetype === 3 )
         }
     }
 })
@@ -85,6 +95,38 @@ const openWebview = (url: string) => {
     }
 }
 
+// 视频认证
+const faceAuth = (status: number) => {
+    if (status !== 3) {
+        plus.requestPermissionCamera({
+            onSuccess: () => {
+                plus.requestPermissionRecordAudio({
+                    onSuccess: () => {
+                        /// 进行视频认证
+                        asignWillFaceFormData.idCardNo = decryptAES(cardnum)
+                        asignWillFaceFormData.realName = customername
+                        /// loading
+                        fullloading((hideLoading) => {
+                            asignWillFace().then((res) => {
+                                hideLoading()
+                                openWebview(res.data.faceUrl)
+                            }).catch((err) => {
+                                hideLoading(err, 'fail')
+                            })
+                        })
+                    },
+                    onError: (err) => {
+                        showFailToast(err)
+                    }
+                })
+            },
+            onError: (err) => {
+                showFailToast(err)
+            }
+        })
+    }
+}
+
 const signer = (item: Model.UsereSignRecordsRsq) => {
     ///  如果是已签署
     if ([2, 3].includes(item.recordstatus)) {

+ 46 - 4
src/packages/zrwyt2/views/account/protocol/Index.vue

@@ -16,6 +16,12 @@
                 <template v-for="(item, index) in dataList" :key="index">
                     <Cell title-class="cell__title" :title="item.templatename" :value="item.recordstatus != 3 ? '去签署' : '去查看'" value-class="cell__value" :icon="iconName(item.recordstatus)" @click="signer(item)" is-link />
                 </template>
+                <template v-if="thirdStepList.length != 0">
+                    <template v-for="(item, index) in thirdStepList" :key="index">
+                        <Cell title="视频认证" :icon="iconName(item.recordstatus)" @click="faceAuth(item.recordstatus)"
+                            :is-link="item.recordstatus !== 3" />
+                    </template>
+                </template>
             </CellGroup>
         </div>
     </app-view>
@@ -28,7 +34,7 @@ import { fullloading, dialog } from '@/utils/vant';
 import { useNavigation } from '@mobile/router/navigation'
 import { useRequest } from '@/hooks/request'
 import { queryUsereSignRecords } from '@/services/api/account';
-import { useRequestCreateContract } from '@/business/user/account';
+import { useRequestCreateContract, useRequestAsignWillFace } from '@/business/user/account';
 import plus from '@/utils/h5plus'
 import { getUserId } from '@/services/methods/user'
 import { useUserStore } from '@/stores'
@@ -42,20 +48,24 @@ const memberUserId = getQueryStringToNumber('memberUserId')
 const userStore = useUserStore()
 /// 创建电子签合同
 const { createTemplate, createFormData } = useRequestCreateContract()
+/// 意愿视频认证
+const { asignWillFace, asignWillFaceFormData } = useRequestAsignWillFace()
 /// 电子签合同信息
 const dataList = shallowRef<Model.UsereSignRecordsRsq[]>([])
+// 步骤3列表
+const thirdStepList = shallowRef<Model.UsereSignRecordsRsq[]>([])
 /// 用户信息
 const { customername, cardnum, bankaccount, bankbankname, mobile2 } = userStore.userInfo
 /// 查询
 const { run } = useRequest(queryUsereSignRecords, {
     params: {
         userId: getUserId(),
-        memberUserId: memberUserId,
-        templatetype: 2
+        memberUserId: memberUserId
     },
     onSuccess: (res) => {
         if (res.data != null && res.data.length != 0) {
-            dataList.value = res.data
+            dataList.value = res.data.filter(e => e.templatetype === 2 )
+            thirdStepList.value = res.data.filter(e => e.templatetype === 3 )
         }
     }
 })
@@ -85,6 +95,38 @@ const openWebview = (url: string) => {
     }
 }
 
+// 视频认证
+const faceAuth = (status: number) => {
+    if (status !== 3) {
+        plus.requestPermissionCamera({
+            onSuccess: () => {
+                plus.requestPermissionRecordAudio({
+                    onSuccess: () => {
+                        /// 进行视频认证
+                        asignWillFaceFormData.idCardNo = decryptAES(cardnum)
+                        asignWillFaceFormData.realName = customername
+                        /// loading
+                        fullloading((hideLoading) => {
+                            asignWillFace().then((res) => {
+                                hideLoading()
+                                openWebview(res.data.faceUrl)
+                            }).catch((err) => {
+                                hideLoading(err, 'fail')
+                            })
+                        })
+                    },
+                    onError: (err) => {
+                        showFailToast(err)
+                    }
+                })
+            },
+            onError: (err) => {
+                showFailToast(err)
+            }
+        })
+    }
+}
+
 const signer = (item: Model.UsereSignRecordsRsq) => {
     ///  如果是已签署
     if ([2, 3].includes(item.recordstatus)) {

+ 14 - 0
src/services/api/account/index.ts

@@ -235,6 +235,20 @@ export function requestWillFace(config: RequestConfig<Model.WillFaceReq> = {}) {
 }
 
 /**
+ * 个人意愿核身认证-爱签长连
+ */
+export function requestAsignWillFace(config: RequestConfig<Model.WillFaceReq> = {}) {
+    return http.commonRequest<Model.WillFaceRsq>({
+        method: 'post',
+        url: '/Asign/WillFace',
+        params: {
+            userId: getUserId(), 
+            data: config.data,
+        }
+    })
+}
+
+/**
  * 证件号重复校验
  */
 export function requestCheckCardNum(config: RequestConfig<Model.CheckCardNumReq> = {}) {

+ 13 - 4
src/types/model/bank.d.ts

@@ -274,14 +274,22 @@ declare namespace Model {
 
     // 查询用户电子签记录表请求
     interface UserESignRecordReq {
-        /// 页数
-        page?: number
-        /// 条数
-        size?: number
+        /// 用户ID
+        userId: number
+        /// 所属会员ID
+        memberUserId: number
+        /// 记录ID
+        recordId?: number
+        /// 模板配置ID
+        templateConfigId?: number
+        /// 模板类型 - 1:实名认证 2:开户协议 3:日结算单 4:交易协议
+        templatetype?: number
     }
 
     // 查询用户电子签记录表回应
     interface UserESignRecordRsq {
+        /// 服务商ID - 麦顿
+        areauserid: number
         // 认证信息
         authinfo: string
         // 合同签署文件地址
@@ -354,6 +362,7 @@ declare namespace Model {
 
     /// 个人意愿核身认证-请求
     interface WillFaceReq {
+        userId?: number; // 用户ID
         /// 身份证号
         idCardNo?: string
         /// 真实姓名