1
jhzh
2026-05-29 bbf6d337c9641c0d1bf2c57f05310e59c104990b
src/views/authentication/index.vue
@@ -1,304 +1,341 @@
<template>
  <!-- 申请身份认证 -->
  <div style="padding-bottom: 30px" class="font-26 authentication">
    <fx-header @back="loginOut">
  <div class="authentication">
    <fx-header :back="false" @back="loginOut">
      <template #title>
        <!-- {{ $t('primaryCertification') }} -->
        {{ $t('realNameVertify') }}
        <span>{{ $t('Primary') }}</span>
      </template>
    </fx-header>
    <!-- <country-list /> -->
    <div v-if="show">
      <div class=" pt-58 pb-54 box-border border-b-color" v-if="disabled() || status === 3">
        <div class="flex justify-between items-center px-30 textColor">
          <div class="font-20">{{ $t('authVerify') }}</div>
          <div class="flex items-center" v-if="resultArr[status]">
            <!-- <img
              :src="require(`@/assets/image/assets-center/${resultArr[status] && resultArr[status].split('_')[0]}.png`)"
              alt="success img" class="w-36 h-36" /> -->
            <img src="@/assets/image/assets-center/identifing.png" v-if="status == 1" class="w-20 h-20" />
            <img src="@/assets/image/assets-center/small-success.png" v-if="status == 0 || status == 2"
              class="w-20 h-20" />
            <img src="@/assets/image/assets-center/icon-close.png" v-if="status == 3" class="w-20 h-20" />
            <div class="font-10 ml-18">{{ resultArr[status] && resultArr[status].split('_')[1] }}</div>
    <div class="auth-body">
      <!-- nationality -->
      <div class="auth-field">
        <label class="auth-label">{{ $t('nationality') }}</label>
        <div class="auth-input auth-input--select" @click="openNationality">
          <span class="auth-placeholder" :class="{ 'auth-placeholder--filled': countryName && countryName !== $t('selectNation') }">{{ countryName }}</span>
          <van-icon name="arrow" class="auth-chevron" />
        </div>
      </div>
      <!-- document type -->
      <div class="auth-field">
        <label class="auth-label">{{ $t('documentType') }}</label>
        <div class="auth-input auth-input--select" @click="showDocType = true">
          <span class="auth-placeholder" :class="{ 'auth-placeholder--filled': idname }">{{ idname || $t('pleaseSelectDocumentType') }}</span>
          <van-icon name="arrow" class="auth-chevron" />
        </div>
      </div>
      <!-- last name -->
      <div class="auth-field">
        <label class="auth-label">{{ $t('lastName') }}</label>
        <input v-model="name" type="text" class="auth-input auth-input--text" :placeholder="$t('pleaseEnterLastName')" />
      </div>
      <!-- first name -->
      <div class="auth-field">
        <label class="auth-label">{{ $t('firstName') }}</label>
        <input v-model="firstName" type="text" class="auth-input auth-input--text" :placeholder="$t('pleaseEnterFirstName')" />
      </div>
      <!-- ID number -->
      <div class="auth-field">
        <label class="auth-label">{{ $t('idNumber') }}</label>
        <input v-model="idnumber" type="text" class="auth-input auth-input--text" :placeholder="$t('pleaseEnterIdNumber')" />
      </div>
      <!-- birth -->
      <div class="auth-field">
        <label class="auth-label">{{ $t('birth') }}</label>
        <input v-model="birth" type="date" class="auth-input auth-input--text auth-input--date" :placeholder="$t('pleaseSelectBirth')" />
      </div>
      <!-- 证件上传(保留接口) -->
      <div class="auth-upload-section" v-if="!disabled()">
        <div class="auth-label">{{ $t('uploadCredentPassport') }}</div>
        <div class="auth-upload-row">
          <div class="auth-upload-item">
            <van-uploader v-model="frontFile" :max-count="1" :deletable="!disabled()" :after-read="afterRead" />
            <div class="auth-upload-tip">{{ $t('credentFront') }}</div>
          </div>
          <div class="auth-upload-item">
            <van-uploader v-model="reverseFile" :max-count="1" :deletable="!disabled()" :after-read="afterRead" />
            <div class="auth-upload-tip">{{ $t('credentObverse') }}</div>
          </div>
        </div>
      </div>
      <div class="pl-30 pr-30">
        <div class="">
          <div class=" mb-25 font-14 textColor">{{ $t('nationality') }}</div>
          <div class="pt-28 pb-28 pl-38 pr-38 flex justify-between items-center rounded inputBackground textColor box"
            @click="openBtn">
            <div class="flex items-center ml-2">
              <!-- <div class="iti-flag" :class="key" style="transform: scale(2.1)"></div> -->
              <div class="iti-flag mr-20 " :class="countryCode" style="transform: scale(2.1)"></div>
              <div>{{ countryName }}</div>
            </div>
            <img src="@/assets/image/down-arrow.png" class="w-20 h-10" alt="arrow" />
          </div>
        </div>
        <ExInput :label="$t('realName')" :disabled="disabled()" :clearBtn="!disabled()"
          :placeholderText="$t('entryRealName')" v-model="name" />
        <ExInput :label="$t('credentPassport')" :disabled="disabled()" :clearBtn="!disabled()"
          :placeholderText="$t('entryCredentPassport')" v-model="idnumber" />
        <div>
          <div v-if="resultArr.length > 0" class="mt-4 mb-25 textColor">{{ $t('uploadCredentPassport') }} </div>
          <div v-else class="mt-4 mb-25 textColor">{{ $t('uploadPicCredentPassport') }}</div>
          <div class="flex mb-80 justify-between">
            <div class="flex-1 flex flex-col text-center justify-center items-center">
              <div class="upload-wrap">
                <img src="../../assets/image/kyc/0.png" alt="" class="w-full"
                  v-if="[1, 2].includes(status) && frontFile.length === 0" />
                <van-uploader v-model="frontFile" multiple :max-count="1" :deletable="!disabled()"
                  :after-read="afterRead" @click-upload="onClickUpload('frontFile')" v-else />
              </div>
              <div class=" font-26 h-20 textColor1">{{ $t('credentFront') }}</div>
            </div>
            <div class="flex-1 flex flex-col text-center justify-center items-center">
              <div class="upload-wrap">
                <img src="../../assets/image/kyc/1.png" alt="" class="w-full"
                  v-if="[1, 2].includes(status) && reverseFile.length === 0" />
                <van-uploader v-model="reverseFile" multiple :max-count="1" :disabled="disabled()"
                  :deletable="!disabled()" :after-read="afterRead" @click-upload="onClickUpload('reverseFile')"
                  v-else />
              </div>
              <div class=" font-26 h-20 textColor1">{{ $t('credentObverse') }}</div>
            </div>
            <!-- <div class="flex-1 flex flex-col text-center justify-center items-center">
              <div class="upload-wrap">
                <img src="../../assets/image/kyc/2.png" alt="" class="w-full"
                  v-if="[1, 2].includes(status) && fileList.length === 0" />
                <van-uploader v-model="fileList" multiple :max-count="1" :disabled="disabled()" :deletable="!disabled()"
                  :after-read="afterRead" @click-upload="onClickUpload('fileList')" v-else />
              </div>
              <div class="font-26 h-20" style="color:#868D9A;">{{ $t('handCredent') }}</div>
            </div> -->
          </div>
        </div>
        <template v-if="!disabled()">
          <div class="font-35 mb-32 textColor">{{ $t('photoExample') }}</div>
          <!-- <img src="@/assets/image/kyc/kyc-demo.png" alt="" style="width:100%;height:auto;" class="w-756 h-220 mb-100"> -->
          <div class="mb-100 flex justify-center">
            <div class="flex flex-1 justify-center">
              <img src="../../assets/image/kyc/1.png" alt="" class="w-120 h-120" />
            </div>
            <div class="flex flex-1 justify-center">
              <img src="../../assets/image/kyc/0.png" alt="" class="w-120 h-120" />
            </div>
          </div>
        </template>
        <button class="apply-btn btnMain text-white font-35 h-100 rounded" @click="onSubmit" v-if="!disabled()">{{
          $t('Apply')
        }}</button>
        <div class="pt-35 pb-60 font-30" v-if="!disabled() || status === 3">
          <span class="text-grey">{{ $t('uploadTitle1') }} {{ $t('photoExample') }}</span>
          <span class="text-blue service-text" @click="tokefu"> {{ $t('ContactService') }}</span>
        </div>
        <nationality-list ref='controlChild' :title="$t('selectNation')" @getName="getName" v-if="!disabled()">
        </nationality-list>
      </div>
      <button type="button" class="auth-submit" @click="onSubmit">{{ $t('Submit') }}</button>
    </div>
    <nationality-list ref="controlChild" :title="$t('selectNation')" @getName="getName" />
    <van-action-sheet v-model:show="showDocType" :actions="docTypeActions" @select="onSelectDocType" />
  </div>
</template>
<script setup>
import { _getIdentify, _info, _applyIdentify } from "@/service/user.api.js";
import { _getIdentify, _applyIdentify } from "@/service/user.api.js";
import { _uploadImage } from "@/service/upload.api.js";
import { onMounted, ref } from 'vue';
import nationalityList from './components/nationalityList.vue'
import nationalityList from './components/nationalityList.vue';
import { useRouter } from "vue-router";
import { showToast, Uploader } from "vant"
// import ExInput from "@/components/ex-input";
import { showToast } from "vant";
import countries from "./components/countryList";
import { getCurrentInstance } from 'vue';
import { useI18n } from "vue-i18n";
import { customerServiceUrl } from '@/config'
const { t } = useI18n()
const router = useRouter()
const countryName = ref(t('selectNation'))
const countryCode = ref('af')
const idnumber = ref('')
const name = ref('')
const frontFile = ref([])
const reverseFile = ref([])
const fileList = ref([])
const curFile = ref('frontFile')
const status = ref('')
const imgs = ref([])
const idcard_path_front_path = ref('')
const idcard_path_back_path = ref('')
const idcard_path_hold_path = ref('')
const resultArr = ref(['small-success_' + t('applynoView'), 'identifing_' + t('reviewing'), 'small-success_' + t('passView'), 'icon-close_' + t('noPassView')])
const show = ref(false)
const language = ref('en')
const controlChild = ref(null)
const { proxy } = getCurrentInstance();
const { t } = useI18n();
const router = useRouter();
const countryName = ref(t('selectNation'));
const countryCode = ref('af');
const idname = ref('');
const idnumber = ref('');
const name = ref('');
const firstName = ref('');
const birth = ref('');
const frontFile = ref([]);
const reverseFile = ref([]);
const status = ref('');
const show = ref(false);
const showDocType = ref(false);
const controlChild = ref(null);
const docTypeActions = [
  { name: 'ID Card', value: 'id/passport' },
  { name: 'Passport', value: 'passport' }
];
const disabled = () => ![0, 3, ''].includes(status.value);
onMounted(() => {
  fetchInfo();
  language.value = (localStorage.getItem('lang')).substring(0, 2)
})
});
const loginOut = () => {
  router.push('/certificationCenter')
}
const fetchInfo = () => {   // 获取状态
  _getIdentify().then(data => {
    show.value = true
    status.value = data.status
    if (data.id != null) {
      countryName.value = countries[data.nationality.toLowerCase()].name
      countryCode.value = data.nationality.toLowerCase()
      idnumber.value = data.idnumber
      name.value = data.name
      frontFile.value = data.idimg_1 ? [{ url: data.idimg_1_path, resURL: data.idimg_1 }] : []
      reverseFile.value = data.idimg_2 ? [{ url: data.idimg_2_path, resURL: data.idimg_2 }] : []
      fileList.value = data.idimg_3 ? [{ url: data.idimg_3_path, resURL: data.idimg_3 }] : []
    }
  })
}
const onClickUpload = (type) => {
  console.log(type)
  curFile.value = type
}
const disabled = () => { // 是否禁用按钮
  return ![0, 3, ''].includes(status.value)
}
const afterRead = (file) => {
  file.status = 'uploading'
  file.message = t('uploading')
  _uploadImage(file).then(data => {
    file.status = 'success'
    file.message = t('uploadSuccess')
    file.resURL = data
  }).catch(err => {
    file.status = 'failed'
    file.message = t('uploadFailed')
  })
  router.push('/certificationCenter');
};
const fetchInfo = () => {
  _getIdentify().then(data => {
    show.value = true;
    status.value = data.status ?? '';
    if (data.id != null || data.nationality) {
      const code = (data.nationality || 'af').toLowerCase();
      countryName.value = countries[code] ? countries[code].name : t('selectNation');
      countryCode.value = code;
      idnumber.value = data.idnumber || data.idNumber || '';
      name.value = data.name || '';
      firstName.value = data.firstName || data.firstname || '';
      birth.value = data.birth || '';
      frontFile.value = data.idimg_1 || data.idFrontImg ? [{ url: data.idimg_1_path || data.idFrontImg, resURL: data.idimg_1 || data.idFrontImg }] : [];
      reverseFile.value = data.idimg_2 || data.idBackImg ? [{ url: data.idimg_2_path || data.idBackImg, resURL: data.idimg_2 || data.idBackImg }] : [];
    }
  }).catch(() => {});
};
const openNationality = () => {
  if (!disabled() && controlChild.value) controlChild.value.open();
};
//打开国家列表底部弹窗
const openBtn = () => {
  if (!disabled()) {
    proxy.$refs.controlChild.open()
  }
}
//获取到当前选中国家的code值
const getName = (params) => {
  countryName.value = params.name;
  countryCode.value = params.code;
}
};
const onSelectDocType = (item) => {
  idname.value = item.value || item.name;
  showDocType.value = false;
};
const afterRead = (file) => {
  file.status = 'uploading';
  file.message = t('uploading');
  _uploadImage(file).then(data => {
    file.status = 'success';
    file.message = t('uploadSuccess');
    file.resURL = data;
  }).catch(() => {
    file.status = 'failed';
    file.message = t('uploadFailed');
  });
};
const onSubmit = () => {
  if (!countryName.value) {
    showToast(t('selectNation'))
    return
  if (!countryName.value || countryName.value === t('selectNation')) {
    showToast(t('selectNation'));
    return;
  }
  if (!name.value) {
    showToast(t('entryName'))
    return
    showToast(t('pleaseEnterLastName'));
    return;
  }
  if (!firstName.value) {
    showToast(t('pleaseEnterFirstName'));
    return;
  }
  if (!idnumber.value) {
    showToast(t('entryCredent'))
    return
    showToast(t('pleaseEnterIdNumber'));
    return;
  }
  if (!birth.value) {
    showToast(t('pleaseSelectBirth'));
    return;
  }
  if (!frontFile.value.length || !frontFile.value[0]?.resURL) {
    showToast(frontFile.value[0]?.status === 'uploading' ? t('uploading') : t('uploadComplete'));
    return;
  }
  if (!reverseFile.value.length || !reverseFile.value[0]?.resURL) {
    showToast(reverseFile.value[0]?.status === 'uploading' ? t('uploading') : t('uploadComplete'));
    return;
  }
  // if (frontFile.value.length == 0 || reverseFile.value.length == 0 || fileList.value.length == 0) { // 需要手持身份证
  if (frontFile.value.length == 0 || reverseFile.value.length == 0) { // 不需要手持身份证
    showToast(t('uploadComplete'))
    return
  }
  _applyIdentify({
    countryName: countryCode.value,
    idname: idname.value || 'id/passport',
    name: name.value,
    firstName: firstName.value,
    idnumber: idnumber.value,
    birth: birth.value,
    frontFile: frontFile.value,
    reverseFile: reverseFile.value,
    // fileList: fileList.value,
    countryName: countryCode.value // this.countryName 存储的 code, 回来再遍历
  }).then(() => {
    showToast(t('submitSuccess'))
    router.push('/verified')
    // this.fetchInfo()
    showToast(t('submitSuccess'));
    router.push('/certificationCenter');
  }).catch(err => {
    if (err.code === 'ECONNABORTED') { showToast('网络超时!'); }
    else if (err.msg !== undefined) { showToast(err.msg); }
  })
}
const tokefu = () => {
  if (customerServiceUrl()) {
    window.location.href = customerServiceUrl();
  } else {
    router.push('/customerService')
  }
}
    if (err.code === 'ECONNABORTED') showToast(t('networkTimeout'));
    else if (err.msg) showToast(err.msg);
  });
};
</script>
<style lang="scss" scoped>
@import '../../assets/css/copy.scss';
@import "@/views/authentication/components/intl.css";
.box {
  padding: 1.5rem !important;
<style lang="scss" scoped>
.authentication {
  min-height: 100vh;
  background: #fff;
  padding-bottom: 2rem;
  font-size: 14px;
}
.authentication {
:deep(.van-nav-bar) {
  background: #fff !important;
}
:deep(.van-nav-bar__title) {
  color: #333;
  font-weight: 700;
  font-size: 16px;
}
:deep(.van-icon) {
  color: #333;
}
.auth-body {
  padding: 0 1.25rem;
}
.auth-field {
  margin-bottom: 1.25rem;
}
.auth-label {
  display: block;
  font-size: 14px;
  color: #333;
  margin-bottom: 0.5rem;
  font-weight: 500;
}
.auth-input {
  width: 100%;
  box-sizing: border-box;
  height: 48px;
  line-height: 48px;
  padding: 0 1rem;
  background: #f8f8f8;
  border-radius: 8px;
  border: none;
  font-size: 14px;
  color: #333;
}
.upload-wrap {
  // width: 220px;
  // height: 220px;
.auth-input--text {
  display: block;
}
.auth-input--text::placeholder {
  color: #999;
}
.auth-input--date {
  color-scheme: light;
}
.auth-input--date::-webkit-calendar-picker-indicator {
  opacity: 0.6;
  cursor: pointer;
}
.auth-input--select {
  display: flex;
  justify-content: center;
  align-items: center;
  justify-content: space-between;
  cursor: pointer;
}
input {
  font-size: 35px;
.auth-placeholder {
  color: #999;
  font-size: 14px;
}
.auth-placeholder--filled {
  color: #333;
}
input:disabled {
  background: $mainbgWhiteColor;
.auth-chevron {
  font-size: 14px;
  color: #999;
  flex-shrink: 0;
  margin-left: 8px;
}
.list-view {
  overflow-y: scroll;
  border-bottom: 1px solid $border_color;
.auth-upload-section {
  margin-top: 1.5rem;
  margin-bottom: 1.5rem;
}
.kyc-input {
.auth-upload-row {
  display: flex;
  gap: 1rem;
  margin-top: 0.5rem;
}
.auth-upload-item {
  flex: 1;
  text-align: center;
  background-color: #F7F8FA;
}
.auth-upload-item :deep(.van-uploader__upload),
.auth-upload-item :deep(.van-uploader__preview) {
  width: 100%;
  border: none;
  height: 100px;
  margin: 0;
}
.auth-upload-item :deep(.van-uploader__upload-icon) {
  font-size: 24px;
}
.auth-upload-tip {
  font-size: 12px;
  color: #999;
  margin-top: 6px;
}
.apply-btn {
  border: none;
  outline: none;
.auth-submit {
  width: 100%;
  height: 50px;
}
.service-text {
  text-decoration: underline;
}
.rounded {
  padding: 0.6rem;
}
.mb-32 {
  margin-bottom: 16px;
}
.pt-35 {
  padding-top: 16px;
  line-height: 50px;
  font-size: 14px;
  font-weight: 700;
  color: #fff;
  background: linear-gradient(90deg, #7c3aed, #5b21b6);
  border: none;
  border-radius: 12px;
  margin-top: 1rem;
  cursor: pointer;
}
</style>