Handy_Cao 1 år sedan
förälder
incheckning
0bb0a65f4a

+ 1 - 1
oem/zrwyt/config/appconfig.json

@@ -3,7 +3,7 @@
   "appName": "中融文遗通",
   "version": "1.0.16",
   "versionCode": "100016",
-  "apiUrl": "http://192.168.31.204:8080/cfg?key=test_204",
+  "apiUrl": "http://192.168.31.134:8080/cfg?key=test_134",
   "tradeChannel": "ws",
   "modules": [
     "register",

+ 3 - 3
src/business/user/account.ts

@@ -297,7 +297,7 @@ export function useRequestCaptchaVerify() {
     const loading = shallowRef(false)
 
     // 表单信息
-    const formData = reactive<Model.CaptchaVerifyReq>({
+    const CaptchaVerifyFormData = reactive<Model.CaptchaVerifyReq>({
         userId: loginStore.userId
     })
 
@@ -306,7 +306,7 @@ export function useRequestCaptchaVerify() {
             loading.value = true
             return await requestCaptchaVerify({
                 data: {
-                    ...formData,
+                    ...CaptchaVerifyFormData,
                 }
             })
         } finally {
@@ -317,6 +317,6 @@ export function useRequestCaptchaVerify() {
     return {
         loading,
         onCaptchaVerify,
-        formData
+        CaptchaVerifyFormData
     }
 }

+ 6 - 1
src/packages/zrwyt/router/index.ts

@@ -132,8 +132,13 @@ const routes: Array<RouteRecordRaw> = [
       {
         path: 'certification',
         name: 'account-certification',
-        component: () => import('@mobile/views/account/certification/Index.vue'),
+        component: () => import('../views/account/certification/Index.vue'),
       },
+      {
+        path: 'protocol',
+        name: 'account-protocol',
+        component: () => import('../views/account/protocol/Index.vue'),
+      }
     ],
   },
   {

+ 264 - 0
src/packages/zrwyt/views/account/certification/Index.vue

@@ -0,0 +1,264 @@
+<template>
+    <app-view class="g-form account-certification">
+        <template #header>
+            <app-navbar title="实名认证" />
+        </template>
+        <Form ref="formRef" class="g-form__container" @submit="onCheckCardNum" :loading="loading">
+            <!-- 个人 -->
+            <CellGroup v-if="getUserInfoType() === 1" inset>
+                <Field v-model="person.realName" name="realName" label="姓名" placeholder="请输入用户姓名" :rules="PFormRules.realName" />
+                <Field v-model="person.mobile" name="mobile" readonly label="手机号码" :rules="PFormRules.mobile"/>
+                <Field v-model="person.bankCard" name="bankCard" label="银行卡号" placeholder="请输入银行卡号" :rules="PFormRules.bankCard" />
+                <Field v-model="person.idCardNo" name="idCardNo" label="证件号码" placeholder="请输入证件号码" :rules="PFormRules.idCardNo" />
+                <Field name="idCardPhoto" label="证件正面照片" :rules="PFormRules.idCardPhoto">
+                    <template #input>
+                        <Image fit="contain" :src="getFileUrl(person.idCardPhoto)" width="100" height="100"
+                            v-if="isReadonly" />
+                        <app-uploader @success="f_afterRead" v-else />
+                    </template>
+                </Field>
+                <Field name="cardbackphotourl" label="证件反面照片" :rules="PFormRules.idCardPhotoBackURL">
+                    <template #input>
+                        <Image fit="contain" :src="getFileUrl(person.idCardPhotoBackURL)" width="100" height="100"
+                            v-if="isReadonly" />
+                        <app-uploader @success="b_afterRead" v-else />
+                    </template>
+                </Field>
+            </CellGroup>
+            <!-- 企业 -->
+            <CellGroup inset v-else>
+                <Field v-model="company.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" name="bankCard" label="法人银行卡号" placeholder="请输入法人银行卡号" :rules="CFormRules.bankCard" />
+                <Field v-model="company.idCardNo" name="idCardNo" label="法人身份证号" placeholder="请输入法人身份证号" :rules="CFormRules.idCardNo" />
+                    <Field name="idCardPhoto" label="营业执照" :rules="CFormRules.idCardPhoto">
+                    <template #input>
+                        <Image fit="contain" :src="getFileUrl(company.idCardPhoto)" width="100" height="100"
+                            v-if="isReadonly" />
+                        <app-uploader @success="f_afterRead" v-else />
+                    </template>
+                </Field>
+            </CellGroup>
+        </Form>
+        <img src="../../../assets/images/certification.png" />
+        <template #footer>
+            <div class="g-form__footer inset">
+                <Button type="danger" :loading="buttonLoading" @click="formRef?.submit" round block>提交实名认证</Button>
+            </div>
+        </template>
+        <component ref="componentRef" v-bind="{ serialNo }" :is="componentMap.get(componentId)" @closed="closeComponent"
+            v-if="componentId" />
+    </app-view>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, onMounted, ref, defineAsyncComponent } from 'vue'
+import { useComponent } from '@/hooks/component'
+import { CellGroup, Button, Field, Form, FormInstance, showFailToast, FieldRule, Image } from 'vant'
+import { fullloading, dialog } from '@/utils/vant';
+import { getFileUrl, getIdCardAge } from '@/filters';
+import { useRequest } from '@/hooks/request'
+import { queryTencentUsereSignRecords, requestCheckCardNum } from '@/services/api/account';
+import { useRequestBankCard4 } from '@/business/user/account';
+import { validateRules } from '@/constants/regex';
+import { useUserStore } from '@/stores'
+import { useNavigation } from '@mobile/router/navigation'
+import { getUserId, getMemberUserId, getUserInfoType } from '@/services/methods/user'
+import AppUploader from '@mobile/components/base/uploader/index.vue'
+
+const componentMap = new Map<string, unknown>([
+    ['captcha', defineAsyncComponent(() => import('./components/captcha/Index.vue'))],
+])
+
+const { router } = useNavigation()
+const userStore = useUserStore()
+const formRef = shallowRef<FormInstance>()
+/// 个人信息
+const person = ref<Model.PersonBankCard4>({})
+/// 公司信息
+const company = ref<Model.CompanyBankCard4>({})
+/// 四要素校验
+const { formData, onBankCard4, loading } = useRequestBankCard4()
+
+const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => {
+    router.back()
+})
+/// 流水号
+const serialNo = ref<string>('')
+/// 是否只读
+const isReadonly = false
+
+/// 查询记录
+const { loading: buttonLoading } = useRequest(queryTencentUsereSignRecords, {
+    params: {
+        userId: getUserId(),
+        memberUserId: getMemberUserId()
+    },
+    onError: (err) => {
+        showFailToast(err)
+    }
+})
+
+const b_afterRead = (filePath: string) => {
+    if (getUserInfoType() === 1) {
+        person.value.idCardPhotoBackURL = filePath
+    }
+}
+
+const f_afterRead = (filePath: string) => {
+    if (getUserInfoType() === 1) {
+        person.value.idCardPhoto = filePath
+    } else {
+        company.value.idCardPhoto = filePath
+    }
+}
+
+// 个人信息表单验证规则
+const PFormRules: { [key in keyof Model.PersonBankCard4]?: FieldRule[] } = {
+    realName: [{
+        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) < 18) || (getIdCardAge(val) > 65)) {
+                    return '开户失败,您的年龄不符合开户要求'
+                } 
+                return true
+            }
+            return validateRules.cardno.message
+        }
+    }],
+    idCardPhoto: [{
+        required: true,
+        message: '请上传证件背面照片',
+    }],
+    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) < 18) || (getIdCardAge(val) > 65)) {
+                    return '开户失败,您的年龄不符合开户要求'
+                } 
+                return true
+            }
+            return validateRules.cardno.message
+        }
+    }],
+    idCardPhoto: [{
+        required: true,
+        message: '请上传营业执照',
+    }],
+}
+
+const onCheckCardNum = () => {
+    fullloading((hideLoading) => {
+        requestCheckCardNum({
+            data: {
+                cardnum: getUserInfoType() === 1 ? person.value.idCardNo : company.value.idCardNo
+            }
+        }).then(() => {
+            // 实体类型 1:个人 2:企业
+            formData.type = getUserInfoType()
+            // 个人
+            if ( getUserInfoType() === 1) {
+                formData.person = person.value
+            } else {
+                // 企业
+                formData.company = company.value
+            }
+            // 发送验证
+            onBankCard4().then((res) => {
+                hideLoading()
+                /// 流水号
+                serialNo.value = res.data.serialNo
+                /// 已发送验证码
+                dialog('验证码已发送!').then(() => {
+                    /// 发送验证码
+                    openComponent('captcha')
+                })
+            }).catch((err) => {
+                hideLoading(err, 'fail')
+            })
+        }).catch((err) => {
+            hideLoading(err, 'fail')
+        })
+    })
+}
+
+onMounted(() => {
+    // 个人
+    if (getUserInfoType() === 1) {
+        person.value.mobile = userStore.userInfo?.mobile2 ?? ''
+    } else {
+        // 企业
+        company.value.mobile = userStore.userInfo?.mobile2 ?? ''
+    }
+})
+</script>

+ 120 - 0
src/packages/zrwyt/views/account/certification/components/captcha/Index.vue

@@ -0,0 +1,120 @@
+
+import { useRequestCaptchaVerify } from '@/business/user/account';
+<template>
+    <app-modal direction="right-top" height="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 } 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'
+import { onActivated } from '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,
+})
+
+onActivated(() => {
+    /// 默认倒计时
+    countdown.start()
+})
+</script>

+ 135 - 0
src/packages/zrwyt/views/account/protocol/Index.vue

@@ -0,0 +1,135 @@
+<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="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>
+            </CellGroup>
+        </div>
+    </app-view>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef } from 'vue'
+import { CellGroup, Cell, showFailToast, showToast } from 'vant'
+import { fullloading } from '@/utils/vant';
+import { useNavigation } from '@mobile/router/navigation'
+import { useRequest } from '@/hooks/request'
+import { queryTencentUsereSignRecords, requestInitTencentESS } from '@/services/api/account';
+import { useRequestCreateFlowByTemplateDirectly } 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'
+
+const { getQueryStringToNumber } = useNavigation()
+/// 所属机构
+const memberUserId = getQueryStringToNumber('memberUserId')
+/// userStore
+const userStore = useUserStore()
+/// 创建电子签合同
+const { createTemplate, templateFormData } = useRequestCreateFlowByTemplateDirectly()
+/// 电子签合同信息
+const dataList = shallowRef<Model.TencentUsereSignRecordsRsq[]>([])
+/// 用户信息
+const { customername, cardnum, mobile2 } =  userStore.userInfo
+/// 查询
+const { run } = useRequest(queryTencentUsereSignRecords, {
+    params: {
+        userId: getUserId(),
+        memberUserId: memberUserId
+    },
+    onSuccess: (res) => {
+        if (res.data != null && res.data.length != 0) {
+            dataList.value = res.data
+        }  else {
+            /// 创建电子签合同
+            initTencentESS()
+        }
+    }
+})
+
+/// 创建电子签合同
+const { run: initTencentESS } = useRequest(requestInitTencentESS, {
+    manual: true,
+    params: {
+        userId: getUserId(),
+        memberUserId: memberUserId
+    },
+    onSuccess: () => {
+        /// 重新请求
+        run()
+    }
+})
+
+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 signer = (item: Model.TencentUsereSignRecordsRsq) => {
+    ///  如果是已签署
+    if (item.recordstatus === 2) {
+        item.signurl ? openWebview(item.signurl) : showFailToast('合同地址错误')
+    } else if (item.recordstatus === 3) {
+        showFailToast('合同已签署,请前往腾讯电子签小程序查看!')
+    } else {
+        fullloading((hideLoading) => {
+            const userinfotype = useUserStore().userInfo.userinfotype
+            templateFormData.userESignRecordID = item.recordid
+            templateFormData.userType = userinfotype
+            /// 个人信息
+            if (userinfotype === 1) {
+                templateFormData.personInfo = {
+                    idCardNumber: decryptAES(cardnum),
+                    mobile: mobile2,
+                    name: customername
+                }
+            } else {
+                templateFormData.organizationInfo = {
+                    name: customername
+                }
+            }
+            /// 创建合同
+            createTemplate().then((res) => {
+                hideLoading()
+                openWebview(res.data.signUrl)
+            }).catch((err) => {
+                hideLoading(err, 'fail')
+            })
+        })
+    }
+}
+
+</script>

+ 37 - 2
src/packages/zrwyt/views/mine/Index.vue

@@ -96,11 +96,16 @@
                         <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 && canBankSign">
                     <template #title>
                         <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' }">
                     <template #title>
                         <Iconfont icon="g-icon-profile">个人信息</Iconfont>
@@ -150,6 +155,8 @@ import { queryBankAccountSign } from '@/services/api/bank'
 import { useLoginStore, useAccountStore, useUserStore, usePositionStore } from '@/stores'
 import eventBus from '@/services/bus'
 import Iconfont from '@/components/base/iconfont/index.vue'
+import { getMemberUserId } from '@/services/methods/user'
+import { queryMdUserSwapProtocol } from '@/services/api/swap'
 
 const { router, routerTo } = useNavigation()
 const loginStore = useLoginStore()
@@ -157,7 +164,8 @@ const userStore = useUserStore()
 const positionStore = usePositionStore()
 const accountStore = useAccountStore()
 const { currentAccount } = accountStore.$toRefs()
-
+/// 判断是否能签约
+const canBankSign = shallowRef(false) 
 const headerRef = shallowRef<HTMLDivElement>()
 const authStatus = computed(() => userStore.userAccount.hasauth) // 实名认证状态
 
@@ -168,6 +176,17 @@ const onReady = (el: HTMLDivElement) => {
 
 /// 进行出入金操作判断
 const doInOutMoney = (tab: string) => {
+    /// 当前未签署合同
+    if (!canBankSign.value) {
+        dialog({
+            message: '请先去签署合同条例!',
+            showCancelButton: true,
+            confirmButtonText: '去签署合同'
+        }).then(() => {
+            router.push({ name: 'account-protocol', query: { memberUserId: getMemberUserId()} })
+        })
+        return
+    }
     if (authStatus.value === AuthStatus.Certified) {
         fullloading((hideLoading) => {
             queryBankAccountSign().then((res) => {
@@ -215,6 +234,22 @@ onActivated(() => {
         userStore.getUserData()
     }
     accountStore.getAccountList()
+
+    /// 机构不需要签合同
+    if (userStore.userInfo.usertype === 2) {
+        canBankSign.value = true
+        return 
+    }
+    
+    /// 查询数据
+    queryMdUserSwapProtocol({
+        data: {
+            userId: loginStore.userId
+        }
+    }).then((res) => {
+        /// 判断是否能签约
+        canBankSign.value = res.data.some(e => e.protocolstatus === 4 )
+    })
 })
 
 onMounted(() => {

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

@@ -290,7 +290,6 @@ export function requestBankCard4(config: RequestConfig<Model.BankCard4Req> = {})
     })
 }
 
-
 /**
  * 重新发送认证验证码
  * 在实名认证的过程中,因用户手机原因未收到短信验证码或原验证码超过15分钟有效期而过期时,可以调用此接口重新发送短信验证码。若1分钟内多次调用此接口,则仅会发送一次短信验证码,15分钟内重新发送短信验证码不会变化。

+ 1 - 1
src/services/http/index.ts

@@ -132,7 +132,7 @@ export default new (class {
      * @returns 
      */
     async commonRequest<T>(config: AxiosRequestConfig, errMsg?: string) {
-        const baseUrl = service.getConfig('goCommonSearchUrl')
+        const baseUrl = 'http://192.168.30.172:8082/api'//service.getConfig('goCommonSearchUrl')
         config.url = baseUrl + config.url
         const res = await this.request<CommonResult<T>>(config, errMsg)
         switch (res.code) {

+ 2 - 2
src/types/model/user.d.ts

@@ -238,9 +238,9 @@ declare namespace Model {
     /** 银行卡四要素认证 请求 */
     interface BankCard4Req {
         // 企业信息
-        company?: CompanyBankCard4
+        company?: PersonBankCard4
         // 个人信息
-        person?: PersonBankCard4
+        person?: CompanyBankCard4
         // 实体类型 1:个人 2:企业
         type?: number
         // 用户ID