1
2026-05-23 30d1ee7be942d7a2201e1ab2ca7d96badff8bf50
src/views/register/index.vue
@@ -1,438 +1,410 @@
<template>
    <div class="register">
        <div class="top flex justify-between items-center">
            <img src="../../assets/image/icon-close.png" alt="" @click="router.go(-1)">
            <div @click="onRoute('/customerService')">{{ $t('onLineService') }}</div>
        </div>
        <!-- <Step :step="1"></Step> -->
        <div class="title textColor">{{ $t('register') }}</div>
        <div class="flex re-tab">
            <!-- <div class="textColor1" :class="activeIndex == 0 ? 'active' : ''" @click="changeIndex(0)">{{
                $t('account')
            }}</div> -->
            <div class="textColor1" :class="activeIndex == 1 ? 'active' : ''" @click="changeIndex(1)">
                {{ $t('电子邮箱') }}
        <div class="reg-inner">
            <div class="reg-back" @click="router.go(-1)">
                <van-icon name="arrow-left" size="22" />
            </div>
            <div class="textColor1" :class="activeIndex == 2 ? 'active' : ''" @click="changeIndex(2)">
                {{ $t('手机号码') }}
            <div class="reg-logo-wrap">
                <img :src="LOGO" alt="" class="reg-logo" />
            </div>
        </div>
        <ExInput :label="getRegType(activeIndex, true)" :placeholderText="getRegType(activeIndex, false)"
            v-model="username" :area="isArea" :dialCode="dialCode" @selectArea="onSelectArea" :icon="icon" />
            <h1 class="reg-title">{{ $t('register') }}</h1>
            <p class="reg-login">
                {{ $t('hasAccount') }}
                <span class="reg-login-link" @click="router.push('/login')">{{ $t('goLogin') }}</span>
            </p>
            <p class="reg-lang" @click="onRoute('/language')">{{ currentLocaleLabel }}</p>
        <ExInput :label="$t('设置登录密码')" :placeholderText="$t('请输入8-16位字符,必须包含大写字母、和特殊字符')" v-model="password"
            typeText="password" />
        <ExInput :label="$t('确认登录密码')" :placeholderText="$t('surePassword')" v-model="repassword" typeText="password" />
        <ExInput :label="$t('setSafeword')" :placeholderText="$t('请输入6位阿拉伯数字的资金密码')" v-model="safeword"
            typeText="password" v-if="activeIndex === 1 || activeIndex === 2" />
        <div class="inputCom" v-if="activeIndex === 1 || activeIndex === 2">
            <p class="label  textColor" v-if="activeIndex == 1">{{ $t('邮箱验证码') }}</p>
            <p class="label  textColor" v-else-if="activeIndex == 2">{{ $t('手机验证码') }}</p>
            <p class="label  textColor" v-else>{{ $t('验证码') }}</p>
            <div class="iptbox inputBackground">
                <input class="inputBackground textColor" type="text"
                    :placeholder="activeIndex == 1 ? $t('请输入邮箱验证码') : activeIndex == 2 ? $t('请输入手机验证码') : $t('entryVerifyCode')"
                    v-model="verifyCode">
                <span v-if="type !== 3" @click="senCode">
                    <span v-if="activeIndex == 1">{{ $t('发送邮箱验证码') }}</span>
                    <span v-else-if="activeIndex == 2">{{ $t('发送手机验证码') }}</span>
                    <span v-else>{{ $t('sendVerifyCode') }}</span>
                    <template v-if="time">({{ time }})s</template>
            <!-- Email -->
            <div class="reg-field">
                <input
                    v-model="username"
                    type="text"
                    class="reg-input"
                    :placeholder="$t('entryEmail')"
                    autocomplete="email"
                />
            </div>
            <!-- Verification Code -->
            <div class="reg-field reg-field-code">
                <input
                    v-model="verifyCode"
                    type="text"
                    class="reg-input"
                    :placeholder="$t('entryVerifyCode')"
                    maxlength="6"
                />
                <span class="reg-send-code" @click="senCode">
                    {{ time > 0 ? `(${time})s` : $t('sendVerifyCode') }}
                </span>
            </div>
        </div>
        <ExInput :label="$t('金融机构代码')" :placeholderText="$t('请输入金融机构识别码')" v-model="invitCode" :clearBtn="false" />
        <div class="protocol textColor">
            <i @click="agreeProt">
                <img v-show="agree" src="../../assets/image/login/prot2.png" alt="" />
                <img v-show="!agree" src="../../assets/image/login/prot1.png" alt="" />
            </i>
            <!-- {{ $t('readAgree') }}<span class="colorMain" @click="router.push('/TermsOfService')">{{
                $t('serviceConf')
                }}</span> -->
            {{ $t('readAgree') }}<span class="colorMain" @click="router.push('/aboutUs?serviceTerm=23')">{{
                $t('serviceConf')
            }}</span>
        </div>
        <van-button class="w-full" style="margin-top:10px;" type="primary" @click="register">{{ $t('register') }}
        </van-button>
        <div class="noTips textColor">{{ $t('hasAccount') }}<span class="colorMain" @click="router.push('/login')">
                {{ $t('goLogin') }}</span>
        </div>
        <nationality-list ref='controlChildRef' :title="$t('selectArea')" @getName="getName"></nationality-list>
            <!-- Password -->
            <div class="reg-field reg-field-pwd">
                <input
                    v-model="password"
                    :type="pwdVisible ? 'text' : 'password'"
                    class="reg-input"
                    :placeholder="$t('entryPassword')"
                    autocomplete="new-password"
                />
                <span class="reg-eye" @click="pwdVisible = !pwdVisible">
                    <van-icon :name="pwdVisible ? 'eye-o' : 'closed-eye'" size="20" />
                </span>
            </div>
            <!-- Confirm Password -->
            <div class="reg-field reg-field-pwd">
                <input
                    v-model="repassword"
                    :type="repwdVisible ? 'text' : 'password'"
                    class="reg-input"
                    :placeholder="$t('surePassword')"
                    autocomplete="new-password"
                />
                <span class="reg-eye" @click="repwdVisible = !repwdVisible">
                    <van-icon :name="repwdVisible ? 'eye-o' : 'closed-eye'" size="20" />
                </span>
            </div>
            <!-- Referral Code (optional) -->
            <div class="reg-field">
                <input
                    v-model="invitCode"
                    type="text"
                    class="reg-input"
                    :placeholder="`${$t('entryInvitCode')} (optional)`"
                />
            </div>
        <Vcode :imgs="[img1, img2]" :show="show" @success="onSuccess" :canvasHeight="200" @fail="onFail"
            @close="show = false;" sliderText='' :successText="$t('vertifyPass')" :failText="$t('vertifuFail')" />
            <div class="reg-protocol">
                <span class="reg-protocol-check" :class="{ checked: agree }" @click="agree = !agree"></span>
                <span>{{ $t('readAgree') }}</span>
                <span class="reg-protocol-link" @click.stop="router.push('/aboutUs?serviceTerm=23')">{{ $t('serviceConf') }}</span>
            </div>
        <div>{{ msg }}</div>
            <button class="reg-btn reg-btn-primary" @click="register">{{ $t('register') }}</button>
        </div>
        <nationality-list ref="controlChildRef" :title="$t('selectArea')" @getName="getName" />
    </div>
</template>
<script setup>
import ExInput from "@/components/ex-input/index.vue";
import Step from "./step.vue";
import { _registerUser, _sendVerifyCode } from "@/service/login.api";
import { _sendVerifyCode } from "@/service/login.api";
import { _bindEmailRegister } from "@/service/user.api.js";
import { useUserStore } from '@/store/user';
import { GET_USERINFO } from '@/store/types.store'
import nationalityList from '../authentication/components/nationalityList.vue'
import Vcode from "vue3-puzzle-vcode";
import img1 from "../../assets/image/slider/1.png";
import img2 from "../../assets/image/slider/2.png";
import { getStorage } from '@/utils/index'
import { useI18n } from 'vue-i18n'
import { customerServiceUrl } from "@/config";
import { GET_USERINFO } from '@/store/types.store';
import nationalityList from '../authentication/components/nationalityList.vue';
import { getStorage } from '@/utils/index';
import { useI18n } from 'vue-i18n';
import { LOGO } from "@/config";
import { useRouter } from 'vue-router';
import { ref, onMounted, reactive, onUnmounted } from 'vue';
import { ref, computed, onMounted, onUnmounted, reactive } from 'vue';
import { showToast } from "vant";
import store from '@/store/store'
const { t } = useI18n()
const router = useRouter()
const onRoute = (path) => {
    if (path == 'back') {
        router.go(-1)
    } else {
        if (path == '/customerService') {
            if (customerServiceUrl()) {
                window.location.href = customerServiceUrl();
            } else {
                router.push(path)
            }
        } else {
            router.push(path)
        }
    }
}
import store from '@/store/store';
const { t, locale } = useI18n();
const router = useRouter();
const userStore = useUserStore();
let show = ref(false)
const msg = ref('')
const type = ref(1)
const time = ref(0)
let agree = ref(false)
const username = ref('')
const password = ref('')
const repassword = ref('')
const verifyCode = ref('')
const safeword = ref('')
const fundPassword = ref('')
const refundPassword = ref('')
const activeIndex = ref(1)
const typeText = ref('password')
let isArea = ref(false)
let dialCode = ref(0)
let icon = ref('')
const state = reactive({
    timer: null
})
const localeLabels = { en: 'English', cn: '中文', Korean: '한국인', Japanese: 'やまと', de: 'Deutsch', French: 'Français', Italy: 'Italiano' };
const currentLocaleLabel = computed(() => localeLabels[locale.value] || locale.value || 'English');
let invitCode = ref('')
const username = ref('');
const password = ref('');
const agree = ref(false);
const repassword = ref('');
const verifyCode = ref('');
const invitCode = ref('');
const pwdVisible = ref(false);
const repwdVisible = ref(false);
const time = ref(0);
const dialCode = ref(0);
const icon = ref('');
const controlChildRef = ref(null);
const state = reactive({ timer: null });
onMounted(() => {
    console.log(store)
    let usercode = getStorage('usercode')
    if (usercode) {
        invitCode = usercode;
    }
    clearInterval(state.timer)
    state.timer = null
})
    const usercode = getStorage('usercode');
    if (usercode) invitCode.value = usercode;
});
onUnmounted(() => {
    clearInterval(state.timer)
    state.timer = null
})
    if (state.timer) clearInterval(state.timer);
});
const getRegType = (activeIndex, bFlag) => {
    switch (activeIndex) {
        case 0:
            return bFlag ? t('account') : t('entryAccount');
        case 1:
            return bFlag ? t('电子邮箱') : t('entryEmail');
        case 2:
            return bFlag ? t('手机号码') : t('请输入手机号码');
    }
}
const onRoute = (path) => {
    router.push(path);
};
const senCode = () => {
    if (time.value > 0) {
        return false
    if (time.value > 0) return;
    const email = username.value.trim();
    if (!email || !/@/.test(email)) {
        showToast(t('entryCorrectEmail'));
        return;
    }
    _sendVerifyCode({
        target: activeIndex.value === 1 ? username.value : dialCode.value + username.value,
    }).then((res) => {
    _sendVerifyCode({ target: email }).then(() => {
        time.value = 30;
        state.timer = setInterval(() => {
            if (time.value > 0) {
                time.value = time.value - 1
            } else {
                time.value = 0;
                clearInterval(state.timer)
                state.timer = null
            if (time.value > 0) time.value--;
            else {
                clearInterval(state.timer);
                state.timer = null;
            }
        }, 1000);
    }).catch((error) => {
        showToast(t(error.msg));
    }).catch((err) => {
        showToast(err?.msg || t('sendVerifyCode'));
    });
}
};
const onSuccess = () => {
    console.log('onSuccess')
const validatePassword = (pwd) => {
    return /^(?=.*[A-Z])(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]).{8,16}$/.test(pwd);
};
const register = () => {
    const email = username.value.trim();
    if (!email || !/@/.test(email)) {
        showToast(t('entryCorrectEmail'));
        return;
    }
    if (!verifyCode.value || verifyCode.value.length < 6) {
        showToast(t('entryVerifyTips'));
        return;
    }
    if (!password.value) {
        showToast(t('entryPassword'));
        return;
    }
    if (!validatePassword(password.value)) {
        showToast(t('passwordTips'));
        return;
    }
    if (repassword.value !== password.value) {
        showToast(t('noSamePassword'));
    if (!agree.value) {
        showToast(t('agreeServiceCond'));
        return;
    }
        return;
    }
    // 推荐码可选,无则传空
    registerApi();
    show.value = false;
}
const onFail = () => {
    msg.value = ''
}
const onRefresh = () => {
    msg.value = ''
}
};
const controlChildRef = ref(null)
const onSelectArea = () => {
    controlChildRef.value.open();
}
const registerApi = () => {
    _bindEmailRegister({
        username: username.value.trim(),
        password: password.value,
        type: '2',
        verifcode: verifyCode.value,
        usercode: invitCode.value.trim() || '',
        safeword: password.value
    }).then((res) => {
        userStore[GET_USERINFO](res);
        store.state.user.userInfo = res;
        router.push('/login');
    }).catch((err) => {
        showToast(err?.msg || t('register'));
    });
};
const onDownloadApp = () => {
    // 可配置 APP 下载链接
    const url = 'https://your-app-download-url.com';
    if (url && (url.startsWith('http://') || url.startsWith('https://'))) {
        window.open(url, '_blank');
    } else {
        showToast(t('downloadAPP'));
    }
};
const getName = (params) => {
    icon.value = params.code;
    dialCode.value = params.dialCode;
}
const agreeProt = () => {
    agree.value = !agree.value
}
// 密码验证
const validatePassword = (password) => {
    const passwordRegex = /^(?=.*[A-Z])(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]).{8,16}$/;
    return passwordRegex.test(password);
}
const register = () => {
    console.log(activeIndex.value, 'activeIndex.value')
    if (activeIndex.value == 0) {
        if (username.value == '') {
            showToast(t('entryAccount'));
            return
        }
        if (username.value.length < 6 || username.value.length > 30) {
            showToast(t('accountLength'));
            return
        }
    } else if (activeIndex.value == 1) {
        let match = username.value.search(/@/);
        if (username.value == '' || match.value == -1) {
            showToast(t('entryCorrectEmail'));
            return
        }
        if (safeword.value == '') {
            showToast(t('safewordTips'));
            return
        }
        if (verifyCode.value.length < 6) {
            showToast(t('entryVerifyTips'));
            return
        }
    } else if (activeIndex.value == 2) {
        if (!(/(^[1-9]\d*$)/.test(username.value))) {
            showToast(t('entryPhone'));
            return
        }
        if (username.value == '') {
            showToast(t('entryPhone'));
            return
        }
    }
    if (password.value == '') {
        showToast(t('entryPassword'));
        return
    }
    if (!validatePassword(password.value)) {
        showToast(t('passwordTips'));
        return
    }
    if (repassword.value !== password.value) {
        showToast(t('noSamePassword'));
        return
    }
    if (invitCode.value.length == '') {
        showToast(t('请输入金融机构代码'));
        return
    }
    if (!agree.value) {
        showToast(t('agreeServiceCond'));
        return
    }
    show.value = true
}
const changeIndex = (index) => {
    activeIndex.value = index;
    if (index == 0 || index == 1) {
        isArea.value = false
    } else {
        isArea.value = true
    }
}
const registerApi = () => {
    switch (activeIndex.value) {
        case 0:
            {
                type.value = 3;
                break;
            }
        case 1:
            {
                type.value = 2;
                break;
            }
        case 2:
            {
                type.value = 1;
                break;
            }
    }
    if (activeIndex.value === 1 || activeIndex.value === 2) {
        _bindEmailRegister({
            username: activeIndex.value === 1 ? username.value : dialCode.value + username.value,
            password: password.value,
            type: activeIndex.value === 1 ? '2' : '1',   // 2邮箱,1手机
            verifcode: verifyCode.value,
            usercode: invitCode.value,
            safeword: safeword.value
        }).then((res) => {
            userStore[GET_USERINFO](res)
            store.state.user.userInfo = res
            // 其他操作??
            // router.push('/identity')
            router.push('/login')
        })
    } else {
        _registerUser({
            userName: (activeIndex.value === 0 || activeIndex.value === 1) ? username.value : `${dialCode.value}${username.value}`,
            password: password.value,
            // re_password: repassword.value,
            type: type.value,
            userCode: invitCode.value,
        }).then((res) => {
            userStore[GET_USERINFO](res)
            store.state.user.userInfo = res
            if (activeIndex.value == 0) {
                router.push('/setFond')
            } else {
                router.push({ name: 'verify', query: { type: activeIndex.value, account: activeIndex.value == 1 ? username.value : `${dialCode.value}${username.value}` } })
            }
        });
    }
}
};
</script>
<style lang="scss" scoped>
.activeBKClick {
    &:active {
        background: $tab_background;
        opacity: 0.5;
    }
}
.activeClick {
    &:active {
        background: $mainbgWhiteColor;
        opacity: 0.5;
    }
}
.register {
    width: 100%;
    position: relative;
    min-height: 100vh;
    background: #fff;
    padding: 56px 24px 48px;
    box-sizing: border-box;
    padding: 16px;
    font-size: 13px;
    background-color: $mainbgWhiteColor;
}
.reg-inner {
    max-width: 400px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
}
.top {
    padding-left: 9px;
    padding-top: 9px;
    img {
        width: 18px;
        height: 18px;
    }
.reg-back {
    position: absolute;
    left: 24px;
    top: 24px;
    color: #000;
    cursor: pointer;
}
.title {
    font-weight: 700;
    font-size: 26px;
    margin-top: 27px;
    margin-bottom: 33px;
}
.re-tab {
    margin-bottom: 22px;
    div {
        padding: 0 18px;
        height: 34px;
        line-height: 34px;
        text-align: center;
        border-radius: 4px;
        margin-right: 10px;
    }
    .active {
        // background: $US_tabActice_background;
        background: $bg_yellow;
        // color: $color_main;
        color: $text_color4;
    }
}
.forget {
    color: $color_main;
    font-size: 12px;
    line-height: 14px;
}
.noTips {
    margin-top: 24px;
}
.protocol {
.reg-logo-wrap {
    width: 72px;
    height: 72px;
    border-radius: 18px;
    background: linear-gradient(135deg, #2c1a5c 0%, #5a37a5 100%);
    display: flex;
    align-items: center;
    height: 15px;
    i {
        width: 15px;
        height: 15px;
        margin-right: 9px;
        img {
            width: 100%;
            height: 100%;
        }
    }
    justify-content: center;
    margin-bottom: 24px;
}
.iptbox {
.reg-logo {
    width: 44px;
    height: 44px;
    margin-top: 8px;
    margin-bottom: 18px;
    padding: 0 20px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    object-fit: contain;
}
.reg-title {
    font-size: 26px;
    font-weight: 700;
    color: #000;
    margin: 0 0 8px;
}
.reg-login {
    font-size: 14px;
    color: #4a4a4a;
    margin: 0 0 6px;
}
.reg-login-link {
    color: #8a2be2;
    cursor: pointer;
}
.reg-lang {
    font-size: 13px;
    color: #9b9b9b;
    margin: 0 0 28px;
    cursor: pointer;
}
/* 输入框:与登录页一致 */
.reg-field {
    width: 100%;
    margin-bottom: 16px;
    position: relative;
}
.reg-field-code {
    .reg-input { padding-right: 90px; }
}
.reg-field-pwd {
    .reg-input { padding-right: 44px; }
}
.reg-input {
    width: 100%;
    height: 48px;
    padding: 0 16px;
    box-sizing: border-box;
    font-size: 15px;
    color: #333;
    background: #f6f5fa;
    border: none;
    border-radius: 6px;
    outline: none;
}
.reg-input::placeholder {
    color: #9b9b9b;
}
.reg-send-code {
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 14px;
    color: #8a2be2;
    cursor: pointer;
}
.reg-eye {
    position: absolute;
    right: 16px;
    top: 50%;
    transform: translateY(-50%);
    color: #6e6e6e;
.reg-protocol {
    width: 100%;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 24px;
    font-size: 13px;
    color: #4a4a4a;
}
.reg-protocol-check {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    border: 1px solid #ccc;
    border-radius: 4px;
    cursor: pointer;
    background: #fff;
    display: inline-block;
}
.reg-protocol-check.checked {
    background: #8a2be2;
    border-color: #8a2be2;
}
.reg-protocol-link {
    color: #8a2be2;
    cursor: pointer;
}
    input {
        flex: 1;
        height: 100%;
        border: none;
    }
    cursor: pointer;
}
    span {
        color: $color_main;
    }
.reg-protocol {
    width: 100%;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 24px;
    font-size: 13px;
    color: #4a4a4a;
}
.reg-protocol-check {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    border: 1px solid #ccc;
    border-radius: 4px;
    cursor: pointer;
    background: #fff;
    display: inline-block;
}
.reg-protocol-check.checked {
    background: #8a2be2;
    border-color: #8a2be2;
}
.reg-protocol-link {
    color: #8a2be2;
    cursor: pointer;
}
/* 主按钮:渐变,圆角 6px */
.reg-btn {
    width: 100%;
    height: 48px;
    border: none;
    border-radius: 6px;
    font-size: 16px;
    font-weight: 700;
    cursor: pointer;
}
.reg-btn-primary {
    background: linear-gradient(90deg, #a443cf, #5e2bc8);
    color: #fff;
    margin-bottom: 12px;
}
.reg-btn-outline {
    background: #fff;
    border: 1px solid #8a2be2;
    color: #8a2be2;
}
</style>