li.shaoyi преди 3 седмици
родител
ревизия
1b2567e0e3

+ 2 - 2
public/locales/zh-CN.json

@@ -1462,7 +1462,7 @@
             "ruleszcxy": "《用户注册协议》",
             "rulesyhkhfxgzs": "《用户开户风险告知书》",
             "checked": "我已阅读并同意",
-            "Pleaseenterausername": "请输入用户名",
+            "Pleaseenterausername": "登录账号/邮箱/手机号",
             "Pleaseenterthepassword": "请输入密码",
             "startfailure": "初始化失败",
             "loading": "加载中...",
@@ -1498,7 +1498,7 @@
             "tips2": "请输入登录密码",
             "tips3": "请输入确认密码",
             "tips4": "登录密码和确认密码不一致",
-            "tips5": "请输入短信验证码",
+            "tips5": "请输入验证码",
             "tips6": "请输入注册编码",
             "tips7": "发送失败",
             "tips8": "您的账号已成功注册。",

+ 11 - 0
src/constants/common.ts

@@ -0,0 +1,11 @@
+import { useEnumStore } from '@/stores'
+
+const { getEnumTypeList } = useEnumStore()
+
+/**
+ * 获取开户方式列表
+ * @returns 
+ */
+export function getOpenMethodList() {
+    return getEnumTypeList('openmethod')
+}

+ 11 - 7
src/packages/digital/assets/themes/global/global.less

@@ -4,13 +4,17 @@
     }
 
     &-block {
-        &--inset {
-            padding: var(--van-padding-md);
+        padding-top: var(--van-padding-md);
+        padding-bottom: var(--van-padding-md);
+
+        &+& {
+            padding-top: 0;
         }
-    }
 
-    &-block+&-block {
-        padding-top: 0;
+        &--inset {
+            padding-left: var(--van-padding-md);
+            padding-right: var(--van-padding-md);
+        }
     }
 }
 
@@ -35,7 +39,7 @@
 
         /* 相邻兄弟元素 */
         .van-cell-group--inset+.van-cell-group--inset {
-            margin-top: var(--van-padding-md);
+            margin-top: 12px;
         }
 
         .van-field {
@@ -56,7 +60,7 @@
     }
 
     &__footer {
-        padding: 32px var(--van-padding-md);
+        padding: var(--van-padding-md);
     }
 }
 

+ 1 - 5
src/packages/digital/router/index.ts

@@ -214,11 +214,7 @@ const routes: Array<RouteRecordRaw> = [
       {
         path: 'certification',
         name: 'account-certification',
-        component: () => import('@mobile/views/account/certification/Index.vue'),
-        props: {
-          showExample: false,
-          ageCheck: false
-        }
+        component: () => import('../views/account/certification/index.vue'),
       },
       {
         path: 'authresult',

+ 20 - 0
src/packages/digital/views/account/certification/index.less

@@ -0,0 +1,20 @@
+.account-certification {
+    &__example {
+        padding: 0 16px;
+
+        fieldset {
+            border: 1px solid #dfdfdf;
+            border-radius: 8px;
+            padding: 10px;
+
+            legend {
+                color: #999;
+                margin-left: 10px;
+            }
+
+            img {
+                width: 100%;
+            }
+        }
+    }
+}

+ 156 - 0
src/packages/digital/views/account/certification/index.vue

@@ -0,0 +1,156 @@
+<!-- 实名认证 -->
+<template>
+    <app-view class="g-form account-certification">
+        <template #header>
+            <app-navbar :title="$t('user.authentication.title')" />
+        </template>
+        <Form ref="formRef" class="g-form__container" @submit="onSubmit">
+            <CellGroup title="信息填写" inset>
+                <Field v-model="formData.username" name="username" :label="$t('user.authentication.customername')"
+                    :placeholder="$t('user.authentication.pleaseentertheusername')" :rules="formRules.username" />
+                <Field name="cardtype" :label="$t('user.authentication.cardtype')" :rules="formRules.cardtype" is-link>
+                    <template #input>
+                        <app-select v-model="formData.cardtype"
+                            :placeholder="$t('user.authentication.pleaseselectthecardtype')"
+                            :options="cerTypePersonList" @confirm="formRef?.validate('cardtype')" />
+                    </template>
+                </Field>
+                <Field v-model="formData.cardnum" name="cardnum" :label="$t('user.authentication.cardnum')"
+                    :placeholder="$t('user.authentication.pleaseenterthecardnum')" :rules="formRules.cardnum"
+                    v-if="formData.cardtype !== undefined" />
+                <Field name="cardfrontphotourl" :label="$t('user.authentication.cardfrontphoto')"
+                    :rules="formRules.cardfrontphotourl">
+                    <template #input>
+                        <app-uploader @success="b_afterRead" />
+                    </template>
+                </Field>
+                <Field name="cardbackphotourl" v-if="showCardBackPhoto === '1'"
+                    :label="$t('user.authentication.cardbackphoto')" :rules="formRules.cardbackphotourl">
+                    <template #input>
+                        <app-uploader @success="f_afterRead" />
+                    </template>
+                </Field>
+                <Field name="halfbodyphotourl" v-if="showHalfBodyPhoto === '1'" :label="halfBodyPhotoTitle"
+                    :rules="formRules.halfbodyphotourl">
+                    <template #input>
+                        <app-uploader @success="h_afterRead" />
+                    </template>
+                </Field>
+                <Field v-if="modifyremark != ''" v-model="modifyremark" readonly name="modifyremark"
+                    :label="$t('user.authentication.modifyremark')" />
+            </CellGroup>
+        </Form>
+        <div class="g-form__footer inset">
+            <Button type="primary" @click="formRef?.submit" round block>
+                {{ $t('user.authentication.submit') }}
+            </Button>
+        </div>
+    </app-view>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef } from 'vue'
+import { CellGroup, Button, Field, Form, FormInstance, showFailToast, FieldRule } from 'vant'
+import { addAuthReq } from '@/business/user'
+import { fullloading, dialog } from '@/utils/vant'
+import { getCerTypePersonList } from "@/constants/account"
+import { useNavigation } from '@mobile/router/navigation'
+import { useRequest } from '@/hooks/request'
+import { getWskhOpenAccountConfigs } from '@/services/api/account'
+import { i18n } from '@/stores'
+import AppUploader from '@mobile/components/modules/uploader/index.vue'
+import AppSelect from '@mobile/components/base/select/index.vue'
+
+const formRef = shallowRef<FormInstance>()
+const { formData, formSubmit, modifyremark } = addAuthReq()
+const { router } = useNavigation()
+const { global: { t } } = i18n
+
+const cerTypePersonList = getCerTypePersonList() // 证件类型列表
+
+const showHalfBodyPhoto = shallowRef('0')
+const showCardBackPhoto = shallowRef('0')
+const halfBodyPhotoTitle = shallowRef(t('user.authentication.halfbodyphoto'))
+
+// 获取网上开户配置
+useRequest(getWskhOpenAccountConfigs, {
+    defaultParams: {
+        configs: '53,54,78'
+    },
+    onSuccess: (res) => {
+        /// 是否显示半身照和 证件背面照
+        showCardBackPhoto.value = res.data.filter(e => e.configid === 53)[0].configvalue ?? '0'
+        showHalfBodyPhoto.value = res.data.filter(e => e.configid === 54)[0].configvalue ?? '0'
+        halfBodyPhotoTitle.value = res.data.filter(e => e.configid === 78)[0].configvalue ?? t('user.authentication.halfbodyphoto')
+    }
+})
+
+const b_afterRead = (filePath: string) => {
+    formData.cardfrontphotourl = filePath
+    formRef.value?.validate('cardfrontphotourl')
+}
+
+const f_afterRead = (filePath: string) => {
+    formData.cardbackphotourl = filePath
+    formRef.value?.validate('cardbackphotourl')
+}
+
+const h_afterRead = (filePath: string) => {
+    formData.halfbodyphotourl = filePath
+    formRef.value?.validate('halfbodyphotourl')
+}
+
+// 表单验证规则
+const formRules: { [key: string]: FieldRule[] } = {
+    username: [{
+        required: true,
+        message: t("user.authentication.pleaseentertheusername"),
+    }],
+    cardtype: [{
+        message: t('user.authentication.pleaseselectthecardtype'),
+        validator: () => formData.cardtype !== undefined
+    }],
+    cardnum: [{
+        message: t("user.authentication.pleaseenterthecardnum"),
+        validator: (val) => {
+            // 任务 #7009
+            const enumItem = cerTypePersonList.find((e) => e.enumitemname === formData.cardtype)
+            if (enumItem && new RegExp(enumItem.param1).test(val)) {
+                return true
+            }
+            return t('regex.cardno')
+        }
+    }],
+    cardbackphotourl: [{
+        message: t("user.authentication.pleaseuploadthecardbackphoto"),
+        validator: () => !!formData.cardbackphotourl
+    }],
+    cardfrontphotourl: [{
+        message: t("user.authentication.pleaseuploadthecardfrontphoto"),
+        validator: () => !!formData.cardfrontphotourl
+    }]
+}
+
+const onSubmit = () => {
+    fullloading((hideLoading) => {
+        formSubmit().then((res) => {
+            /// 失败
+            if (res.code.toString() != '0') {
+                showFailToast(res.message)
+            } else {
+                hideLoading()
+                dialog(t("user.authentication.opensuccess")).then(() => {
+                    router.back()
+                })
+            }
+        }).catch((err) => {
+            formData.cardnum = ''
+            showFailToast(err)
+        })
+    })
+}
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 3 - 3
src/packages/digital/views/contract/detail/index.vue

@@ -45,15 +45,15 @@
             </table>
         </div>
         <Tabs>
+            <Tab title="持仓">
+                <contract-position :params="{ accountid }" />
+            </Tab>
             <Tab title="委托">
                 <contract-order v-bind="{ accountid }" />
             </Tab>
             <Tab title="成交">
                 <contract-trade v-bind="{ accountid }" />
             </Tab>
-            <Tab title="持仓">
-                <contract-position :params="{ accountid }" />
-            </Tab>
             <Tab title="资金明细">
                 <contract-statement v-bind="{ accountid }" />
             </Tab>

+ 5 - 5
src/packages/digital/views/home/index.vue

@@ -45,16 +45,16 @@ const tabbarItems = [
     icon: 'wap-home',
   },
   {
-    name: 'home-spot',
-    label: '现货',
-    icon: 'graphic',
-  },
-  {
     name: 'home-contract',
     label: '合约',
     icon: 'records',
   },
   {
+    name: 'home-spot',
+    label: '现货',
+    icon: 'graphic',
+  },
+  {
     name: 'home-wallet',
     label: '钱包',
     icon: 'cash-back-record',

+ 8 - 7
src/packages/digital/views/setting/index.vue

@@ -27,13 +27,14 @@
             </Cell>
         </CellGroup>
         <CellGroup title="系统" inset>
-            <app-luanguage #default="{ value }">
-                <Cell :value="value" is-link v-if="globalStore.getSystemInfo('i18nEnabled')">
-                    <template #title>
-                        <app-iconfont icon="g-icon-lang">语言设置</app-iconfont>
-                    </template>
-                </Cell>
-            </app-luanguage>
+            <Cell is-link v-if="globalStore.getSystemInfo('i18nEnabled')">
+                <template #title>
+                    <app-iconfont icon="g-icon-lang">语言设置</app-iconfont>
+                </template>
+                <template #value>
+                    <app-luanguage />
+                </template>
+            </Cell>
             <Cell is-link @click="userLogout">
                 <template #title>
                     <app-iconfont icon="g-icon-cancel">退出登录</app-iconfont>

+ 3 - 3
src/packages/digital/views/wallet/index.vue

@@ -11,12 +11,12 @@
             <GridItem icon="setting-o" text="设置" :to="{ name: 'setting' }" />
         </Grid>
         <Tabs v-model:active="currentTabIndex">
-            <Tab title="现货">
-                <spot-account @click="navigateToSpotDetail" />
-            </Tab>
             <Tab title="合约">
                 <contract-account @click="navigateToContractDetail" />
             </Tab>
+            <Tab title="现货">
+                <spot-account @click="navigateToSpotDetail" />
+            </Tab>
         </Tabs>
     </app-view>
 </template>

+ 6 - 0
src/packages/mobile/components/base/notify/index.less

@@ -36,6 +36,12 @@
         border-radius: 16px;
         padding: 24px 32px;
 
+        [class="van-theme-dark"] & {
+            background-color: #222;
+            border: 1px solid #333;
+            box-shadow: 0 0 12px rgba(0, 0, 0, .72);
+        }
+
         h4 {
             font-weight: bold;
             margin-bottom: 8px;

+ 25 - 0
src/packages/mobile/components/base/switch-tab/index.less

@@ -0,0 +1,25 @@
+.app-switch {
+    text-align: center;
+
+    &-tabs {
+        display: inline-flex;
+        height: 36px;
+        background-color: var(--van-button-default-background);
+        border-radius: 18px;
+        overflow: hidden;
+
+        &__item {
+            flex: 1;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+
+            &.active {
+                font-weight: bold;
+                color: #222;
+                background-color: #fff;
+                border-radius: inherit;
+            }
+        }
+    }
+}

+ 62 - 0
src/packages/mobile/components/base/switch-tab/index.vue

@@ -0,0 +1,62 @@
+<template>
+    <div class="app-switch">
+        <div class="app-switch-tabs" :style="styles">
+            <template v-for="(item, index) in options" :key="index">
+                <div :class="{ 'app-switch-tabs__item': true, 'active': item[customFieldName.value] === selectedValue }"
+                    @click="onChange(item[customFieldName.value])">
+                    <slot :option="options[index]" :index="index">
+                        <span>{{ item[customFieldName.label] }}</span>
+                    </slot>
+                </div>
+            </template>
+        </div>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, PropType, computed, watch, CSSProperties } from 'vue'
+
+const props = defineProps({
+    modelValue: {
+        type: null
+    },
+    options: {
+        type: Array as PropType<{ [key: string]: unknown; }[]>,
+        default: () => ([])
+    },
+    optionProps: {
+        type: Object as PropType<{ label?: string; value?: string; }>,
+        default: () => ({})
+    },
+    width: [Number, String]
+})
+
+const emit = defineEmits(['update:modelValue', 'change'])
+
+const selectedValue = shallowRef()
+
+const customFieldName = {
+    label: 'label',
+    value: 'value',
+    ...props.optionProps
+}
+
+const styles = computed<CSSProperties>(() => ({
+    width: props.width ? (typeof props.width === 'string' ? props.width : props.width + 'px') : '60%'
+}))
+
+const onChange = (value: unknown) => {
+    emit('update:modelValue', value)
+    emit('change', value)
+}
+
+watch(() => props.modelValue, (val) => {
+    selectedValue.value = val
+}, {
+    immediate: true
+})
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

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

@@ -9,7 +9,7 @@
                 <Cell :title="$t('user.authentication.cardtype')"
                     :value="getCertificateTypeCodeName(userInfo.cardtypeid)" />
                 <Cell :title="$t('user.authentication.cardnum')" :value="userInfo.cardnum" />
-                <Cell v-if="userInfo.mobile2 != ''" :title="$t('banksign.mobilephone')" :value="userInfo.mobile2" />
+                <Cell :title="$t('banksign.mobilephone')" :value="userInfo.mobile2" v-if="userInfo.mobile2" />
                 <Cell :title="$t('banksign.bankname1')" :value="userInfo.bankbankname" v-if="userInfo.bankbankname" />
                 <Cell :title="$t('banksign.bankno')" :value="userInfo.bankaccount" v-if="userInfo.bankaccount" />
                 <Cell v-if="userInfo.userinfotype === 1" :title="$t('user.authentication.cardfrontphoto')">

+ 4 - 0
src/packages/mobile/views/news/detail/index.less

@@ -3,6 +3,10 @@
 .news-details {
     background-color: #fff;
 
+    [class="van-theme-dark"] & {
+        background-color: #000;
+    }
+
     .app-view__body {
         padding-bottom: 60px;
     }

+ 6 - 0
src/packages/mobile/views/notice/list/components/detail/index.less

@@ -1,4 +1,10 @@
 .notice-detail {
+    background-color: #fff;
+
+    [class="van-theme-dark"] & {
+        background-color: #000;
+    }
+
     .app-view__body {
         padding-bottom: 60px;
     }

+ 2 - 2
src/packages/mobile/views/user/avatar/Index.vue

@@ -1,9 +1,9 @@
 <template>
-    <app-view class="g-form">
+    <app-view class="g-form g-layout">
         <template #header>
             <app-navbar :title="$t('user.avater.title')" />
         </template>
-        <Form ref="formRef" class="g-form__container" @submit="onSubmit">
+        <Form ref="formRef" class="g-form__container g-layout-block" @submit="onSubmit">
             <CellGroup inset>
                 <Field :label="$t('user.avater.cardbackphotourl')" :rules="formRules.headurl">
                     <template #input>

+ 57 - 12
src/packages/mobile/views/user/forget/Index.vue

@@ -1,10 +1,12 @@
 <template>
-    <app-view class="g-form">
+    <app-view class="forget g-form g-layout">
         <template #header>
             <app-navbar :title="t('user.forget.title')" />
         </template>
-        <Form ref="formRef" class="g-form__container" style="margin-top: var(--van-padding-md);" @submit="formSubmit">
-            <CellGroup :inset="insetStyle">
+        <app-switch-tab class="g-layout-block" v-model="selectedMethod" :options="openMethodList" @change="changeMethod"
+            v-if="openMethodList.length > 1" />
+        <Form ref="formRef" class="g-form__container g-layout-block" @submit="formSubmit">
+            <CellGroup :inset="insetStyle" v-if="selectedMethod === 1">
                 <Field :label="t('user.register.area')" right-icon="arrow" v-if="codeList.length">
                     <template #input>
                         <app-select v-model="selectedCode" :options="codeList">
@@ -16,9 +18,9 @@
                     </template>
                 </Field>
                 <Field v-model="formData.mobile" type="digit" name="mobile" :label="t('user.register.mobile')"
-                    :placeholder="t('common.pleaseenter')" autocomplete="off" :rules="formRules.mobile" />
+                    :placeholder="t('common.required')" autocomplete="off" :rules="formRules.mobile" />
                 <Field v-model="formData.vcode" type="digit" name="vcode" :label="t('user.forget.vcode')"
-                    :placeholder="t('common.pleaseenter')" autocomplete="off" :rules="formRules.vcode">
+                    :placeholder="t('common.required')" autocomplete="off" :rules="formRules.vcode">
                     <template #button>
                         <Button size="small" :disabled="loading" @click="sendVerifyCode">
                             <span v-if="isCountdown">{{ t('user.forget.sendagain') }}({{ currentTime }})</span>
@@ -27,11 +29,24 @@
                     </template>
                 </Field>
             </CellGroup>
+            <CellGroup :inset="insetStyle" v-if="selectedMethod === 2">
+                <Field name="email" label="邮箱地址" v-model="formData.mobile" :placeholder="t('common.required')"
+                    :rules="formRules.email" />
+                <Field v-model="formData.vcode" type="digit" name="vcode" label="验证码"
+                    :placeholder="t('common.required')" :rules="formRules.vcode">
+                    <template #button>
+                        <Button size="small" :disabled="loading" @click="sendVerifyCode">
+                            <span v-if="isCountdown">{{ t('user.register.sendagain') }}({{ currentTime }})</span>
+                            <span v-else>{{ t('user.register.getsmscode') }}</span>
+                        </Button>
+                    </template>
+                </Field>
+            </CellGroup>
             <CellGroup :inset="insetStyle">
                 <Field v-model="formData.password" name="password" type="password" :label="t('user.forget.newpwd')"
-                    :placeholder="t('common.pleaseenter')" autocomplete="off" :rules="formRules.password" />
+                    :placeholder="t('common.required')" autocomplete="off" :rules="formRules.password" />
                 <Field v-model="formData.confirmpassword" name="confirmpassword" type="password"
-                    :label="t('user.forget.confirmpwd')" :placeholder="t('common.pleaseenter')" autocomplete="off"
+                    :label="t('user.forget.confirmpwd')" :placeholder="t('common.required')" autocomplete="off"
                     :rules="formRules.confirmpassword" />
             </CellGroup>
         </Form>
@@ -50,12 +65,14 @@ import { useCountDown } from '@vant/use'
 import { fullloading, dialog } from '@/utils/vant'
 import { getEncryptMobile } from '@/filters'
 import { validateRules } from '@/constants/regex'
+import { getOpenMethodList } from '@/constants/common'
 import { useNavigation } from '@mobile/router/navigation'
 import { queryLoginId } from '@/services/api/account'
 import { resetPassword, sendResetVerifyCode } from '@/services/api/common'
 import cryptojs from 'crypto-js'
 import { i18n, useUserStore, useGlobalStore } from '@/stores'
 import { getCountryCodeList } from '@/constants/unit'
+import AppSwitchTab from '@mobile/components/base/switch-tab/index.vue'
 import AppSelect from '@mobile/components/base/select/index.vue'
 
 defineProps({
@@ -72,13 +89,21 @@ const loading = ref(false)
 const isCountdown = ref(false) // 是否正在倒计时
 const globalStore = useGlobalStore()
 
+const openMethodList = getOpenMethodList() // 开户方式
+const selectedMethod = shallowRef(1) // 选中的开户方式
+
 const { getSystemParamValue } = useUserStore()
 const param1010 = getSystemParamValue('1010') ?? '1'
 const param1013 = getSystemParamValue('1013') ?? '30'
 
 const codeList = getCountryCodeList()
 const selectedCode = shallowRef(codeList[0]?.value) // 选中的区号
-const phoneNumber = computed(() => (selectedCode.value ?? '') + formData.mobile) // 手机号码
+
+// 用户名
+const accountName = computed(() => {
+    const prefix = selectedMethod.value === 1 ? (selectedCode.value ?? '') : ''
+    return prefix + formData.mobile
+})
 
 // 倒计时函数
 const countdown = useCountDown({
@@ -125,6 +150,16 @@ const formRules: { [key: string]: FieldRule[] } = {
             }
         }
     }],
+    email: [{
+        required: true,
+        message: '请输入邮箱地址',
+        validator: (val) => {
+            if (validateRules.email.validate(val)) {
+                return true
+            }
+            return validateRules.email.message
+        }
+    }],
     password: [{
         required: true,
         message: t('user.forget.tips3'),
@@ -154,13 +189,22 @@ const formRules: { [key: string]: FieldRule[] } = {
     }],
 }
 
+// 选择忘记方式
+const changeMethod = (value: number) => {
+    formData.mobile = ''
+    formData.vcode = ''
+    selectedMethod.value = value
+}
+
 // 发送手机验证码
 const sendVerifyCode = () => {
-    formRef.value?.validate('mobile').then(() => {
+    const field = selectedMethod.value === 1 ? 'mobile' : 'email'
+
+    formRef.value?.validate(field).then(() => {
         loading.value = true
         sendResetVerifyCode({
             data: {
-                mobile: getEncryptMobile(phoneNumber.value),
+                mobile: getEncryptMobile(accountName.value),
                 businessType: 1
             }
         }).then(() => {
@@ -177,7 +221,7 @@ const formSubmit = () => {
     fullloading((hideLoading) => {
         queryLoginId({
             data: {
-                username: phoneNumber.value
+                username: accountName.value
             }
         }).then((res) => {
             const { password, vcode } = formData
@@ -188,7 +232,7 @@ const formSubmit = () => {
             resetPassword({
                 data: {
                     logincode,
-                    mobile: getEncryptMobile(phoneNumber.value),
+                    mobile: getEncryptMobile(accountName.value),
                     password: encryptedHex,
                     vcode,
                 }
@@ -199,6 +243,7 @@ const formSubmit = () => {
                         router.back()
                     })
                 } else {
+                    formData.vcode = ''
                     showFailToast(res.message)
                 }
             }).catch((err) => {

+ 2 - 2
src/packages/mobile/views/user/password/Index.vue

@@ -1,9 +1,9 @@
 <template>
-    <app-view class="g-form user-password" :loading="false">
+    <app-view class="g-form g-layout" :loading="false">
         <template #header>
             <app-navbar :title="$t('user.password.title')" />
         </template>
-        <Form ref="formRef" class="g-form__container" style="margin-top: var(--van-padding-md);" @submit="onSubmit">
+        <Form ref="formRef" class="g-form__container g-layout-block" @submit="onSubmit">
             <CellGroup inset>
                 <Field v-model="formData.OldPwd" type="password" name="OldPwd" :label="$t('user.password.oldpwd')"
                     :placeholder="$t('common.required')" autocomplete="off" :rules="formRules.OldPwd" />

+ 54 - 10
src/packages/mobile/views/user/register/Index.vue

@@ -1,10 +1,12 @@
 <template>
-  <app-view class="g-form">
+  <app-view class="register g-form g-layout">
     <template #header>
       <app-navbar :title="t('user.register.title')" :show-back-button="showBackButton" />
     </template>
-    <Form ref="formRef" class="g-form__container" style="margin-top: var(--van-padding-md);" @submit="formSubmit">
-      <CellGroup :inset="insetStyle">
+    <app-switch-tab class="g-layout-block" v-model="selectedMethod" :options="openMethodList" @change="changeMethod"
+      v-if="openMethodList.length > 1" />
+    <Form ref="formRef" class="g-form__container g-layout-block" @submit="formSubmit">
+      <CellGroup :inset="insetStyle" v-if="selectedMethod === 1">
         <Field :label="t('user.register.area')" right-icon="arrow" v-if="codeList.length">
           <template #input>
             <app-select v-model="selectedCode" :options="codeList">
@@ -27,6 +29,19 @@
           </template>
         </Field>
       </CellGroup>
+      <CellGroup :inset="insetStyle" v-if="selectedMethod === 2">
+        <Field name="email" label="邮箱地址" v-model="formData.mobilephone" :placeholder="t('common.required')"
+          :rules="formRules.email" />
+        <Field v-model="formData.vcode" type="digit" name="vcode" label="验证码" :placeholder="t('common.required')"
+          :rules="formRules.vcode">
+          <template #button>
+            <Button size="small" :disabled="loading" @click="sendVerifyCode">
+              <span v-if="isCountdown">{{ t('user.register.sendagain') }}({{ currentTime }})</span>
+              <span v-else>{{ t('user.register.getsmscode') }}</span>
+            </Button>
+          </template>
+        </Field>
+      </CellGroup>
       <CellGroup :inset="insetStyle">
         <Field v-model="formData.loginpwd" name="loginpwd" type="password" :label="t('user.register.logipwd')"
           :placeholder="t('common.required')" :rules="formRules.loginpwd" />
@@ -79,11 +94,13 @@ import { validateRules } from '@/constants/regex'
 import { useFacebook } from '@/hooks/facebook'
 import { useAppsFlyer } from '@/hooks/appsflyer'
 import { useNavigation } from '@mobile/router/navigation'
-import { userRegister, sendRegisterVerifyCode, queryMyRegisterMoney } from '@/services/api/common'
+import { userRegister, sendRegisterVerifyCode2, queryMyRegisterMoney } from '@/services/api/common'
 import { i18n, useGlobalStore, useUserStore, useErrorInfoStore } from '@/stores'
 import { getCountryCodeList } from '@/constants/unit'
+import { getOpenMethodList } from '@/constants/common'
 import cryptojs from 'crypto-js'
 import plus from '@/utils/h5plus'
+import AppSwitchTab from '@mobile/components/base/switch-tab/index.vue'
 import AppReward from '@mobile/components/modules/reward/index.vue'
 import AppSelect from '@mobile/components/base/select/index.vue'
 // import AppQrcodeScan from '@mobile/components/base/qrcode-scan/index.vue'
@@ -122,13 +139,21 @@ const isCountdown = ref(false) // 是否正在倒计时
 const showReward = ref(false) // 显示红包
 const redEnvelope = ref(0) // 红包金额
 
+const openMethodList = getOpenMethodList() // 开户方式
+const selectedMethod = shallowRef(1) // 选中的开户方式
+
 const { getSystemParamValue } = useUserStore()
 const param1010 = getSystemParamValue('1010') ?? '1'
 const param1013 = getSystemParamValue('1013') ?? '30'
 
 const codeList = getCountryCodeList()
 const selectedCode = shallowRef(codeList[0]?.value) // 选中的区号
-const phoneNumber = computed(() => (selectedCode.value ?? '') + formData.mobilephone) // 手机号码
+
+// 用户名
+const accountName = computed(() => {
+  const prefix = selectedMethod.value === 1 ? (selectedCode.value ?? '') : ''
+  return prefix + formData.mobilephone
+})
 
 const confirmpassword = ref('') // 确认密码
 
@@ -181,6 +206,16 @@ const formRules: { [key: string]: FieldRule[] } = {
       }
     }
   }],
+  email: [{
+    required: true,
+    message: '请输入邮箱地址',
+    validator: (val) => {
+      if (validateRules.email.validate(val)) {
+        return true
+      }
+      return validateRules.email.message
+    }
+  }],
   loginpwd: [{
     required: true,
     message: t('user.register.tips2'),
@@ -223,6 +258,13 @@ const formRules: { [key: string]: FieldRule[] } = {
 //   formData.refernum = text
 // }
 
+// 选择开户方式
+const changeMethod = (value: number) => {
+  formData.mobilephone = ''
+  formData.vcode = ''
+  selectedMethod.value = value
+}
+
 // 路由跳转
 const routerAction = () => {
   // 追踪注册结果
@@ -249,13 +291,15 @@ const resetData = () => {
   checked.value = false
 }
 
-// 发送手机验证码
+// 发送验证码
 const sendVerifyCode = () => {
-  formRef.value?.validate('mobilephone').then(() => {
+  const field = selectedMethod.value === 1 ? 'mobilephone' : 'email'
+
+  formRef.value?.validate(field).then(() => {
     loading.value = true
-    sendRegisterVerifyCode({
+    sendRegisterVerifyCode2({
       data: {
-        phonenumber: phoneNumber.value
+        param: accountName.value
       }
     }).then(() => {
       isCountdown.value = true
@@ -307,7 +351,7 @@ const formSubmit = () => {
       userRegister({
         data: {
           ...formData,
-          mobilephone: phoneNumber.value,
+          mobilephone: accountName.value,
           loginpwd
         }
       }).then((res) => {

+ 10 - 0
src/services/api/common/index.ts

@@ -53,6 +53,16 @@ export function sendRegisterVerifyCode(config: RequestConfig<{ phonenumber: stri
 }
 
 /**
+ * 发送注册验证码
+ */
+export function sendRegisterVerifyCode2(config: RequestConfig<{ param: string }>) {
+    return http.request({
+        url: service.getConfig('openApiUrl') + '/verifycode/identifyCode2',
+        params: config.data,
+    })
+}
+
+/**
  * 重置密码
  */
 export function resetPassword(config: RequestConfig<Model.ResetPasswordReq> = {}) {