502 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="fullPage">
<GeneralHeader
style="width: 100%"
:title="t('kyc')"
:showLanguageList="true"
color="black"
:backQuery="backQuery"
/>
<div style="padding: 16px; display: flex; flex-direction: column; gap: 8px">
<!-- 银行卡号 -->
<div style="font-weight: 600">{{ t('contact_number') }}</div>
<div
style="
border-radius: 8px;
background: #fff;
padding: 12px;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
"
>
<input
type="text"
v-model="applyInfo.contactNumber"
:placeholder="t('enter_contact_number')"
style="width: 100%; border: none; outline: none"
/>
</div>
<!-- 申请信息 -->
<div style="font-weight: 600">{{ t('other_description') }}</div>
<div
style="
padding: 12px 12px 20px;
background: #fff;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
border-radius: 8px;
position: relative;
"
>
<textarea
v-model="applyInfo.otherDescription"
rows="5"
maxlength="100"
:placeholder="t('enter_other_description')"
style="width: 100%; border: none; outline: none; resize: none"
></textarea>
<span
style="
position: absolute;
bottom: 4px;
right: 4px;
color: rgba(0, 0, 0, 0.4);
font-size: 0.8em;
"
class="word-count"
>{{ count }}/100</span
>
</div>
<!-- 上传图片 -->
<div style="font-weight: 600">{{ t('passport_id_card_title') }}:</div>
<div style="display: flex; align-items: center">
<!-- 预览图片 -->
<div v-for="(imgUrl, index) in resImgUrls" :key="index" style="position: relative">
<img
:src="imgUrl"
alt=""
style="display: block; width: 15vw; aspect-ratio: 1/1; object-fit: cover"
/>
<img
src="/src/assets/icon/cancelGray.png"
alt=""
style="
position: absolute;
top: 6%;
right: 6%;
display: block;
width: 3vw;
aspect-ratio: 1/1;
object-fit: cover;
"
@click="handleCancel(index)"
/>
</div>
<!-- 添加按钮 -->
<div v-if="resImgUrls.length < 3" style="position: relative">
<img
src="/src/assets/icon/addImg.png"
alt=""
style="display: block; width: 15vw; aspect-ratio: 1/1; object-fit: cover"
/>
<button
@click="onClickImagePicker"
style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
border: none;
"
></button>
<!-- <input
v-else
type="file"
accept="image/*"
@click="onFileInputClick"
@change="handleFileChange"
style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
"
/> -->
</div>
</div>
<!-- 当前状态 -->
<div v-if="updateStatus" style="display: flex; align-items: flex-end; gap: 8px">
<div style="font-weight: 600">{{ t('current_status') }}</div>
<div
v-if="applyInfo.status === 'PASS'"
style="
font-weight: 510;
font-size: 0.9em;
background: linear-gradient(248deg, #75ff98 5.66%, #3dff54 42.49%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
"
>
{{ t('review_passed') }}
</div>
<div
v-else-if="applyInfo.status === 'NOT_PASS'"
style="
font-weight: 510;
font-size: 0.9em;
background: linear-gradient(248deg, #ff7578 5.66%, #ff3d40 42.49%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
"
>
{{ t('review_failed') }}
</div>
<div
v-else-if="applyInfo.status === 'PENDING'"
style="
font-weight: 510;
font-size: 0.9em;
background: linear-gradient(248deg, #ffe675 5.66%, #ffc53d 42.49%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
"
>
{{ t('awaiting_review') }}
</div>
</div>
<!-- 提现按钮 -->
<div style="display: flex; justify-content: center; align-items: center">
<div
style="
margin-top: 10px;
width: 80%;
border-radius: 32px;
padding: 12px;
color: #fff;
font-weight: 590;
text-align: center;
"
:style="{
background: checkStatus
? 'linear-gradient(135deg, #BB92FF 2.82%, #8B45FF 99.15%), linear-gradient(135deg, #D1CED6 2.82%, #7C7882 99.15%)'
: 'linear-gradient(135deg, #D1CED6 2.82%, #7C7882 99.15%)',
}"
@click="Submit"
>
{{ t('submit') }}
</div>
</div>
</div>
</div>
</template>
<script setup>
import { isInApp } from '@/utils/appBridge.js'
import { useI18n } from 'vue-i18n'
import GeneralHeader from '@/components/GeneralHeader.vue'
import { ref, onMounted, computed, nextTick } from 'vue'
import { showError, showWarning, showInfo, showSuccess } from '@/utils/toast.js'
import { useRoute, useRouter } from 'vue-router'
import { uploadImg } from '@/api/upload.js'
import { setDocumentDirection } from '@/locales/i18n'
import { getWithdrawInfoList, addWithdrawInfo, updateWithdrawInfo } from '@/api/wallet'
import { uploadImgFile, parseAccessOrigin } from '@/utils/uploadFiles.js'
import { useLastImgStore } from '@/stores/lastImg'
import { storeToRefs } from 'pinia'
const { t, locale } = useI18n()
const router = useRouter()
const route = useRoute()
const backQuery = ref(route.query || null)
const lastImgStore = useLastImgStore()
// 初始化时从 localStorage 恢复数据
lastImgStore.initFromStorage()
const { imgUrl: lastImgUrl } = storeToRefs(lastImgStore) //获取图片缓存
// 监听语言变化并设置文档方向
locale.value && setDocumentDirection(locale.value)
const count = computed(() => applyInfo.value.otherDescription.length) //字数
const tempFiles = ref([]) //用于提交的临时图片文件列表
// const previewUrls = ref([]) //预览图片数组
const resImgUrls = ref([]) //用于提交申请的图片数组
const updateStatus = ref(false) //是否为修改
const applyInfo = ref({
id: '', //提现信息ID
contactNumber: '', //银行卡号
category: 'CARD', //类别
otherDescription: '', //其他描述
passportFrontUrl: '', //图片
passportBackUrl: 'meaningless',
})
// 是否全填
const checkStatus = computed(() => {
return applyInfo.value.contactNumber != '' && resImgUrls.value.length > 0
})
// 点击图片选择按钮
const onClickImagePicker = async () => {
try {
await callNativeImagePicker()
} catch (error) {
console.error('图片选择失败:', error)
// 可以在这里添加额外的错误处理逻辑
}
}
// 调用原生图片选择器
const callNativeImagePicker = async () => {
console.log('调用原生图片选择器')
// 最多3张图片
if (resImgUrls.value.length >= 3) {
throw new Error('Maximum number of images reached')
}
// 定义过滤函数
const filterDuplicateImages = (access) => {
try {
// 处理空数据情况
if (!access || access === '""' || access === '') {
return true // 空数据不过滤,让上层处理
}
const result = parseAccessOrigin(access)
if (result.success && result.data) {
const file = result.data
console.log('检查图片数据:', file.path)
// 如果是已存在的图片返回false表示需要继续检查
if (lastImgUrl.value && file.path === lastImgUrl.value) {
console.log('检测到重复图片,继续等待新图片...')
return false
}
// 新图片,可以接受
return true
}
// 其他情况接受数据
return true
} catch (error) {
console.error('过滤图片数据时出错:', error)
return true // 出错时不过滤,让上层处理
}
}
return new Promise((resolve, reject) => {
try {
uploadImgFile(
async (access) => {
try {
// 处理空数据情况
if (!access || access === '""' || access === '') {
console.warn('接收到空的图片数据')
reject(new Error('No image selected'))
return
}
const result = parseAccessOrigin(access)
if (result.success && result.data) {
const file = result.data
console.log('接收到原生图片数据:', file.path)
lastImgStore.setLastImgUrl(file.path) // 更新上一次实际选择的图片路径
// 生成预览图
resImgUrls.value.push(file.path)
resolve()
} else {
const errorMsg = result.error
console.error('图片选择失败:', errorMsg)
reject(new Error(errorMsg))
}
} catch (error) {
console.error('处理原生图片选择结果失败:', error)
reject(error)
}
},
filterDuplicateImages // 传递过滤函数
)
} catch (error) {
console.error('调用原生图片选择器失败:', error)
reject(error)
}
})
}
// 取消图片选择
const handleCancel = (index) => {
resImgUrls.value.splice(index, 1)
}
// 上传图片
const uploadImgUrl = async () => {
for (let index = 0; index < tempFiles.value.length; index++) {
const file = tempFiles.value[index]
try {
const res = await uploadImg(file)
if (res.status) {
resImgUrls.value.push(res.body)
}
} catch (error) {
showError(error.response?.errorMsg || t('upload_failed'))
}
}
}
// 提交申请
const Submit = async () => {
// 检查是否漏填
console.log('checkStatus.value:', checkStatus.value)
if (checkStatus.value) {
applyInfo.value.passportFrontUrl = JSON.stringify(resImgUrls.value)
if (updateStatus.value) {
console.log('更新信息:', applyInfo.value)
await updateWithdrawInfoData()
} else {
console.log('添加信息:', applyInfo.value)
await addWithdrawInfoData()
}
// 先返回到 availableIncome (原始状态)
router.go(-1)
// 检查是否来自 availableIncome 页面
if (route.query) {
console.log('route.query:', route.query)
// 使用 nextTick 确保上一步完成
nextTick(() => {
// 然后使用 replace 替换为带参数的版本
router.replace({
path: route.query.from,
query: {
activeAction: route.query.activeAction,
},
})
})
}
}
}
// 获取提现信息列表
const getWithdrawInfoListData = async () => {
try {
const resWithdrawInfoList = await getWithdrawInfoList()
console.log('提现信息列表:', resWithdrawInfoList)
if (resWithdrawInfoList.status && resWithdrawInfoList.body?.length > 0) {
updateStatus.value = true
applyInfo.value = resWithdrawInfoList.body[0]
let imgList = JSON.parse(resWithdrawInfoList.body[0].passportFrontUrl)
resImgUrls.value = imgList.map((item) => item)
}
} catch (error) {
console.error('获取提现信息列表失败:', error)
showError(error.response?.errorMsg)
}
}
// 添加提现信息
const addWithdrawInfoData = async () => {
try {
const resAddWithdrawInfo = await addWithdrawInfo(applyInfo.value)
if (resAddWithdrawInfo.status) {
showSuccess(t('application_submitted'))
// getWithdrawInfoListData() // 重新获取提现信息列表
}
} catch (error) {
showError(error.response?.errorMsg)
throw error
}
}
// 更新提现信息
const updateWithdrawInfoData = async () => {
try {
const resUpdateWithdrawInfo = await updateWithdrawInfo(applyInfo.value)
if (resUpdateWithdrawInfo.status) {
showSuccess(t('application_submitted'))
// getWithdrawInfoListData() // 重新获取提现信息列表
}
} catch (error) {
showError(error.response?.errorMsg)
throw error
}
}
onMounted(() => {
getWithdrawInfoListData() // 获取个人申请信息
})
</script>
<style lang="scss" scoped>
* {
color: rgba(0, 0, 0, 0.8);
font-family: 'SF Pro Text';
}
.fullPage {
width: 100vw;
min-height: 100vh;
background-color: #fff;
background-image: url(/src/assets/images/secondBg.png);
background-size: 100% auto;
background-repeat: no-repeat;
position: relative;
}
input::placeholder {
font-weight: 510;
color: rgba(0, 0, 0, 0.4);
}
textarea::placeholder {
font-weight: 510;
color: rgba(0, 0, 0, 0.4);
}
textarea::-webkit-scrollbar {
width: 0;
height: 0;
}
@media screen and (max-width: 360px) {
* {
font-size: 10px;
}
}
@media screen and (min-width: 360px) {
* {
font-size: 16px;
}
}
@media screen and (min-width: 768px) {
* {
font-size: 24px;
}
}
@media screen and (min-width: 1024px) {
* {
font-size: 32px;
}
}
/* RTL支持 */
[dir='rtl'] .word-count {
right: auto;
left: 4px;
}
</style>