|
|
@@ -1,71 +1,258 @@
|
|
|
<template>
|
|
|
- <div class="apply-card-content">
|
|
|
- <div class="apply-card-steps">
|
|
|
- <div class="steps-top">
|
|
|
- <div class="step">
|
|
|
- <div class="step-circle">1</div>
|
|
|
- <div class="step-label">选择卡片类型</div>
|
|
|
- </div>
|
|
|
- <div class="step-dash"></div>
|
|
|
- <div class="step">
|
|
|
- <div class="step-circle">2</div>
|
|
|
- <div class="step-label">提交KYC认证</div>
|
|
|
+ <div class="page">
|
|
|
+ <div class="card-swiper">
|
|
|
+ <div class="swiper-container" @touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd">
|
|
|
+ <div
|
|
|
+ class="card-wrapper"
|
|
|
+ v-for="(card, index) in cardList"
|
|
|
+ :key="card.id"
|
|
|
+ :style="{ transform: `translateX(${(index - currentIndex) * 100 + offsetX}%)` }"
|
|
|
+ >
|
|
|
+ <div class="card-info" @click.stop="(e: MouseEvent) => toggleCardNo(card.id, e)" :class="{ flipping: isFlipping[card.id] }">
|
|
|
+ <img src="@/assets/images/logo.png" alt="" srcset="" />
|
|
|
+ <div class="number" v-if="showCardNo[card.id]">
|
|
|
+ {{ card?.cardNo}}
|
|
|
+ </div>
|
|
|
+ <div class="number" v-else>
|
|
|
+ {{ card?.cardNo?.replace(/(\d{4})\d+(\d{4})/, '$1 **** **** $2') }}
|
|
|
+ </div>
|
|
|
+ <div class="card-b">
|
|
|
+ <div class="valid">
|
|
|
+ <span class="lable">{{ t('cards.p13') }}</span>
|
|
|
+ <span>{{ card.firstName }} {{ card.lastName }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="valid">
|
|
|
+ <span class="lable">{{ t('cards.p14') }}</span>
|
|
|
+ <span>{{ card.expire }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <img
|
|
|
+ v-if="card.status != 1"
|
|
|
+ src="https://upload.wikimedia.org/wikipedia/commons/a/a4/Flag_of_the_United_States.svg"
|
|
|
+ class="flags"
|
|
|
+ @click.stop="ucardActivate(card.id)"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="step-dash"></div>
|
|
|
- <div class="step">
|
|
|
- <div class="step-circle">3</div>
|
|
|
- <div class="step-label">申请开卡</div>
|
|
|
+ </div>
|
|
|
+ <div class="swiper-indicators" v-if="cardList.length > 1">
|
|
|
+ <div
|
|
|
+ v-for="(card, index) in cardList"
|
|
|
+ :key="card.id"
|
|
|
+ class="indicator-dot"
|
|
|
+ :class="{ active: index === currentIndex }"
|
|
|
+ @click="currentIndex = index"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="actions" v-if="currentCard.isOk">
|
|
|
+ <div class="action-btn" @click="goToCardRecharge">
|
|
|
+ <icon name="icon_recharge" :size="28" color="#EA002A" />
|
|
|
+ <span>{{ t('cards.recharge') }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="action-btn" @click="goToFindPassword">
|
|
|
+ <icon name="icon_card password reset" :size="28" color="#EA002A" />
|
|
|
+ <span>{{ t('cards.findPassword') }}</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="action-btn" @click="goToFreezeCard" v-if="currentCard.freezeStatus === 2">
|
|
|
+ <icon name="icon_unfreeze" :size="28" color="#EA002A" />
|
|
|
+ <span>{{ t('cards.unfreezeCard') }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="action-btn" @click="goToFreezeCard" v-else>
|
|
|
+ <icon name="icon_freeze" :size="28" color="#EA002A" />
|
|
|
+ <span>{{ t('cards.freezeCard') }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <template v-if="!currentCard.isOk">
|
|
|
+ <div class="balance-content">{{ t('apply-record-detail.p1') }}</div>
|
|
|
+ <div class="g">
|
|
|
+ <img src="../assets/images/apply-record-1.png" alt="" />
|
|
|
+ <div class="g-l">
|
|
|
+ <div class="g-item">
|
|
|
+ <div class="label a1">{{ currentCard.cardName }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="g-item">
|
|
|
+ <div class="label">{{ t('apply-record-detail.p2') }}</div>
|
|
|
+ <div :class="statusClass(currentCard.kycStatus)">{{ statusMap[currentCard.kycStatus] }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="g-item" v-if="currentCard.applyStatus === 0 || currentCard.applyStatus === 1">
|
|
|
+ <div class="label">{{ t('apply-record-detail.p7') }}</div>
|
|
|
+ <div :class="statusClass1(currentCard.applyStatus)">{{ applyStatusMap[currentCard.applyStatus] }}</div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <div class="cwg-button cwg-btn">
|
|
|
+ <van-button type="primary" block v-if="currentCard.kycStatus === 2 && currentCard.applyStatus === null" @click="handleApply(2, currentCard)">{{
|
|
|
+ t('cards.p6')
|
|
|
+ }}</van-button>
|
|
|
+ <van-button type="primary" block v-if="[null, -1, 1, 3].includes(currentCard.kycStatus)" @click="handleApply(3, currentCard)">{{
|
|
|
+ t('cards.p7')
|
|
|
+ }}</van-button>
|
|
|
</div>
|
|
|
- <button class="apply-btn" @click="handleApply(1, {})" v-if="isShowBtn">申请银行卡</button>
|
|
|
- <div class="apply-card-footer">
|
|
|
- <div class="kyc-list-title">KYC认证</div>
|
|
|
- <div v-if="kycList.length > 0">
|
|
|
- <div class="kyc-list">
|
|
|
- <div class="kyc-item" v-for="item in kycList" :key="item.id">
|
|
|
- <div class="kyc-icon">
|
|
|
- <div class="kyc-icon-inner">
|
|
|
- <i class="i-mdi-card-account-details-outline"></i>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="kyc-left">
|
|
|
- <div class="kyc-type">{{ item.cardName }}</div>
|
|
|
- <div class="kyc-desc">{{ statusMap[item.kycStatus] }}</div>
|
|
|
- </div>
|
|
|
- <div class="kyc-right">
|
|
|
- <button class="apply-btn" v-if="item.kycStatus === 2 && item.applyStatus === null" @click="handleApply(2, item)">
|
|
|
- 开卡
|
|
|
- </button>
|
|
|
- <button class="apply-btn" v-if="[null,-1, 1, 3].includes(item.kycStatus)" @click="handleApply(3, item)">重新认证</button>
|
|
|
- <div v-if="item.applyStatus === 0" class="processing-status">处理中</div>
|
|
|
- <div v-if="item.applyStatus === 1" class="success-status">开卡成功</div>
|
|
|
+ </template>
|
|
|
+ <template v-if="currentCard.isOk">
|
|
|
+ <div class="balance-wrap" v-if="balance.length > 0">
|
|
|
+ <div class="balance-content">{{ t('cards.currency') }}</div>
|
|
|
+ <div class="balance-title">{{ t('cards.balance') }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="balance-wrap" v-for="item in balance" :key="item.currency">
|
|
|
+ <div class="currency">
|
|
|
+ <img :src="imageSrc(item.currency)" class="flag" />
|
|
|
+ <span>{{ item.currency }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="balance">{{ item.amount }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="trans-header">
|
|
|
+ <div class="trans-title">{{ t('cards.transactions') }}</div>
|
|
|
+ <i class="i-mdi-calendar-month-outline" @click="showDatePicker = true" />
|
|
|
+ <van-calendar
|
|
|
+ v-model:show="showDatePicker"
|
|
|
+ :min-date="minDate"
|
|
|
+ :title="t('cards.selectDateRange')"
|
|
|
+ :subtitle="t('cards.selectDate')"
|
|
|
+ :max-date="maxDate"
|
|
|
+ :show-mark="false"
|
|
|
+ color="var(--main-yellow)"
|
|
|
+ type="range"
|
|
|
+ :show-confirm="false"
|
|
|
+ @cancel="showDatePicker = false"
|
|
|
+ :formatter="formatter"
|
|
|
+ @confirm="onConfirmStart"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="transactions" v-if="transactions.length > 0">
|
|
|
+ <div v-for="t in transactions" :key="t.id" class="transaction" @click="goToTransactionDetail(t)">
|
|
|
+ <div class="trans-icon">
|
|
|
+ <div class="trans-icon-inner">
|
|
|
+ <i class="i-mdi-cart-outline"></i>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <div class="trans-left">
|
|
|
+ <div class="trans-type">{{ t.tradeTypeStr }}</div>
|
|
|
+ <div class="trans-desc">{{ t.remark }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="trans-right">
|
|
|
+ <div class="trans-amount">{{ t.amount }} {{ t.currencyTxn }}</div>
|
|
|
+ <div class="trans-date">{{ t.businessDate }}</div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <EmptyState v-if="kycList.length === 0" icon="i-mdi-receipt-text-outline" text="暂无卡片" />
|
|
|
- </div>
|
|
|
+ <EmptyState :title="t('empty-state.t1')" :text="t('empty-state.c1')"
|
|
|
+ /></template>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { useRouter } from 'vue-router'
|
|
|
import EmptyState from '@/components/EmptyState.vue'
|
|
|
-import { ucardApi } from '@/api/ucard'
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
+import { ucardApi } from '@/api/ucard'
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
+import type { CardInfo, TransactionInfo } from '@/api/ucard'
|
|
|
+import { showToast } from 'vant'
|
|
|
+import dayjs from 'dayjs'
|
|
|
+const router = useRouter()
|
|
|
import useUserStore from '@/stores/use-user-store'
|
|
|
const userStore = useUserStore()
|
|
|
-const userInfo = computed(() => userStore.userInfo)
|
|
|
-const router = useRouter()
|
|
|
+const cardList = computed(() => userStore.userCard)
|
|
|
const { t } = useI18n()
|
|
|
-interface KycItem {
|
|
|
- id: number
|
|
|
- cardName: string
|
|
|
- kycStatus: number | null
|
|
|
- applyStatus: number | null
|
|
|
- cardTypeId: number
|
|
|
- uniqueId: string
|
|
|
+const currentCard = ref<CardInfo | null>({})
|
|
|
+const balance = ref<{ balance: number; currency: string }[]>([])
|
|
|
+const globalStore = useGlobalStore()
|
|
|
+const transactions = ref<TransactionInfo[]>([])
|
|
|
+const showCardNo = ref<{ [key: string]: boolean }>({})
|
|
|
+const isFlipping = ref<{ [key: string]: boolean }>({})
|
|
|
+const currentIndex = ref(0)
|
|
|
+const startX = ref(0)
|
|
|
+const offsetX = ref(0)
|
|
|
+const isDragging = ref(false)
|
|
|
+const images = import.meta.glob('@/assets/images/currency/*.png', { eager: true })
|
|
|
+
|
|
|
+const imageSrc = (currency:string) => {
|
|
|
+ return images[`/src/assets/images/currency/${currency}.png`]?.default || fallbackImg
|
|
|
+}
|
|
|
+const showDatePicker = ref(false)
|
|
|
+const dateRange = ref<[string, string] | undefined>(undefined)
|
|
|
+dateRange.value = ['', '']
|
|
|
+const minDate = new Date(new Date().getFullYear() - 10, 0, 1)
|
|
|
+const maxDate = new Date(new Date().getFullYear() + 10, 0, 1)
|
|
|
+const onConfirmStart = (value: [string, string]) => {
|
|
|
+ dateRange.value = ['', '']
|
|
|
+ if (value && value.length === 2) {
|
|
|
+ dateRange.value = value
|
|
|
+ }
|
|
|
+ showDatePicker.value = false
|
|
|
+ handleDateRangeChange()
|
|
|
+}
|
|
|
+
|
|
|
+const formatter = (day: any) => {
|
|
|
+ if (day.type === 'start') {
|
|
|
+ day.bottomInfo = t('cards.start')
|
|
|
+ } else if (day.type === 'end') {
|
|
|
+ day.bottomInfo = t('cards.end')
|
|
|
+ } else {
|
|
|
+ day.bottomInfo = ''
|
|
|
+ }
|
|
|
+ return day
|
|
|
+}
|
|
|
+
|
|
|
+const ucardActivate = async (id: string) => {
|
|
|
+ router.push({
|
|
|
+ path: '/card/activation',
|
|
|
+ query: {
|
|
|
+ id,
|
|
|
+ },
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 获取交易记录
|
|
|
+const getTransactions = async () => {
|
|
|
+ try {
|
|
|
+ transactions.value = []
|
|
|
+ if (!currentCard.value?.cardNo) return
|
|
|
+ const res = await ucardApi.transactionsList({
|
|
|
+ beginDate: dateRange.value?.[0] ? dayjs(dateRange.value?.[0]).format('YYYY-MM-DD') : undefined,
|
|
|
+ endDate: dateRange.value?.[1] ? dayjs(dateRange.value?.[1]).format('YYYY-MM-DD') : undefined,
|
|
|
+ cardNo: currentCard.value?.cardNo,
|
|
|
+ page: {
|
|
|
+ current: 1,
|
|
|
+ row: 10,
|
|
|
+ },
|
|
|
+ })
|
|
|
+ transactions.value = res.data && Array.isArray(res.data) ? res.data : []
|
|
|
+ } catch (error: any) {
|
|
|
+ showToast(error?.message || String(error))
|
|
|
+ transactions.value = []
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const getBalance = async () => {
|
|
|
+ globalStore.setFullScreenLoading(true)
|
|
|
+ try {
|
|
|
+ balance.value = []
|
|
|
+ if (!currentCard.value?.cardNo) return
|
|
|
+ const res = await ucardApi.ucardBalance({
|
|
|
+ cardNo: currentCard.value?.cardNo,
|
|
|
+ uniqueId: currentCard.value?.uniqueId,
|
|
|
+ })
|
|
|
+ if (res.code == 200) {
|
|
|
+ balance.value = res.data
|
|
|
+ } else {
|
|
|
+ balance.value = []
|
|
|
+ }
|
|
|
+ } catch (error: any) {
|
|
|
+ showToast(error?.message || String(error))
|
|
|
+ balance.value = []
|
|
|
+ } finally {
|
|
|
+ globalStore.setFullScreenLoading(false)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleDateRangeChange = () => {
|
|
|
+ if (currentCard.value) {
|
|
|
+ getTransactions()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
const statusMap: Record<string, string> = {
|
|
|
@@ -75,41 +262,36 @@ const statusMap: Record<string, string> = {
|
|
|
'2': t('kyc.statusDesc3'),
|
|
|
'3': t('kyc.statusDesc4'),
|
|
|
}
|
|
|
-const kycList = ref<KycItem[]>([])
|
|
|
-const isShowBtn = ref(false)
|
|
|
-async function getKycList() {
|
|
|
- const res = await ucardApi.cardKycTypesList({ page: { current: 1, row: 10 } })
|
|
|
- if (res.code === 200) {
|
|
|
- let a = res.data.filter((i)=> {
|
|
|
- return i.kycStatus !== null
|
|
|
- })
|
|
|
- let b = res.data.filter((i)=> {
|
|
|
- return i.kycStatus == null
|
|
|
- })
|
|
|
- if(b.length != 0){
|
|
|
- isShowBtn.value = true
|
|
|
- }else {
|
|
|
- isShowBtn.value = false
|
|
|
- }
|
|
|
- kycList.value = a
|
|
|
+const applyStatusMap: Record<string, string> = {
|
|
|
+ '0': t('apply-record-detail.p8'),
|
|
|
+ '1': t('apply-record-detail.p6'),
|
|
|
+}
|
|
|
+function statusClass(status: number) {
|
|
|
+ switch (status) {
|
|
|
+ case 2:
|
|
|
+ return 'status-default status-success'
|
|
|
+ case 3:
|
|
|
+ return 'status-default status-error'
|
|
|
+ default:
|
|
|
+ return 'status-default'
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-const ucardApply = async (item: any) => {
|
|
|
- console.log(1111,item);
|
|
|
-
|
|
|
- const res = await ucardApi.ucardApply({
|
|
|
- cardTypeId: item.cardTypeId,
|
|
|
- uniqueId: userInfo.value.customInfo.uniqueId,
|
|
|
- })
|
|
|
- if (res.code === 200) {
|
|
|
- showToast(t('improve-info.kycSuccess'))
|
|
|
- // router.push('/select/card')
|
|
|
+function statusClass1(status: number) {
|
|
|
+ switch (status) {
|
|
|
+ case 1:
|
|
|
+ return 'status-default status-success'
|
|
|
+ default:
|
|
|
+ return 'status-default'
|
|
|
}
|
|
|
}
|
|
|
const handleApply = (type: number, item: any) => {
|
|
|
if (type == 1) {
|
|
|
- router.push('/select/card')
|
|
|
+ router.push({
|
|
|
+ path: '/apply/record/detail',
|
|
|
+ query: {
|
|
|
+ cardTypeId: item.cardTypeId,
|
|
|
+ },
|
|
|
+ })
|
|
|
} else if (type == 2) {
|
|
|
ucardApply(item)
|
|
|
} else if (type == 3) {
|
|
|
@@ -123,148 +305,317 @@ const handleApply = (type: number, item: any) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+watch(
|
|
|
+ currentIndex,
|
|
|
+ (newIndex) => {
|
|
|
+ if (cardList.value[newIndex]) {
|
|
|
+ currentCard.value = cardList.value[newIndex]
|
|
|
+ if (cardList.value[newIndex].isOk) {
|
|
|
+ getTransactions()
|
|
|
+ getBalance()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ { immediate: true },
|
|
|
+)
|
|
|
+
|
|
|
+const toggleCardNo = (cardId: string, event: MouseEvent) => {
|
|
|
+ event.stopPropagation()
|
|
|
+ isFlipping.value[cardId] = !isFlipping.value[cardId]
|
|
|
+ showCardNo.value[cardId] = !showCardNo.value[cardId]
|
|
|
+}
|
|
|
+
|
|
|
+const handleTouchStart = (e: TouchEvent) => {
|
|
|
+ startX.value = e.touches[0].clientX
|
|
|
+ isDragging.value = true
|
|
|
+}
|
|
|
|
|
|
-onMounted(() => {
|
|
|
- getKycList()
|
|
|
+const handleTouchMove = (e: TouchEvent) => {
|
|
|
+ if (!isDragging.value) return
|
|
|
+ const currentX = e.touches[0].clientX
|
|
|
+ const diff = currentX - startX.value
|
|
|
+ const containerWidth = document.querySelector('.swiper-container')?.clientWidth || 0
|
|
|
+ if (currentIndex.value === 0 && diff > 0) {
|
|
|
+ offsetX.value = (diff / containerWidth) * 30
|
|
|
+ } else if (currentIndex.value === cardList.value.length - 1 && diff < 0) {
|
|
|
+ offsetX.value = (diff / containerWidth) * 30
|
|
|
+ } else {
|
|
|
+ offsetX.value = (diff / containerWidth) * 100
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleTouchEnd = () => {
|
|
|
+ isDragging.value = false
|
|
|
+ if (Math.abs(offsetX.value) > 0.2) {
|
|
|
+ if (offsetX.value > 0 && currentIndex.value > 0) {
|
|
|
+ currentIndex.value--
|
|
|
+ } else if (offsetX.value < 0 && currentIndex.value < cardList.value.length - 1) {
|
|
|
+ currentIndex.value++
|
|
|
+ }
|
|
|
+ cardList.value.forEach((card) => {
|
|
|
+ showCardNo.value[card.id] = false
|
|
|
+ })
|
|
|
+ }
|
|
|
+ offsetX.value = 0
|
|
|
+}
|
|
|
+
|
|
|
+function goToCardRecharge() {
|
|
|
+ if (currentCard.value?.status != 1) {
|
|
|
+ showToast('请先激活银行卡')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ router.push({
|
|
|
+ path: '/card/recharge',
|
|
|
+ query: {
|
|
|
+ id: currentCard.value?.id,
|
|
|
+ },
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function goToFindPassword() {
|
|
|
+ if (currentCard.value?.status != 1) {
|
|
|
+ showToast('请先激活银行卡')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ router.push({
|
|
|
+ path: '/find/password',
|
|
|
+ query: {
|
|
|
+ cardNo: currentCard.value?.cardNo,
|
|
|
+ cardTypeId: currentCard.value?.cardTypeId,
|
|
|
+ },
|
|
|
+ })
|
|
|
+}
|
|
|
+// 冻结卡片/解冻卡片
|
|
|
+async function goToFreezeCard() {
|
|
|
+ if (currentCard.value?.status != 1) {
|
|
|
+ showToast('请先激活银行卡')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ switch (currentCard.value?.freezeStatus) {
|
|
|
+ case 1:
|
|
|
+ let res = await ucardApi.ucardFreeze({ cardNo: currentCard.value?.cardNo, uniqueId: currentCard.value?.uniqueId })
|
|
|
+ if (res.code === 200) {
|
|
|
+ showToast('冻结成功')
|
|
|
+ getCardList()
|
|
|
+ } else {
|
|
|
+ showToast(res.msg)
|
|
|
+ }
|
|
|
+ break
|
|
|
+ case 2:
|
|
|
+ let res1 = await ucardApi.ucardUnfreeze({ cardNo: currentCard.value?.cardNo, uniqueId: currentCard.value?.uniqueId })
|
|
|
+ if (res1.code === 200) {
|
|
|
+ showToast('解冻成功')
|
|
|
+ getCardList()
|
|
|
+ } else {
|
|
|
+ showToast(res1.msg)
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ showToast(error || String(error))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function goToTransactionDetail(transaction: TransactionInfo) {
|
|
|
+ router.push({
|
|
|
+ path: '/card/transaction/detail',
|
|
|
+ query: {
|
|
|
+ id: transaction.id,
|
|
|
+ },
|
|
|
+ })
|
|
|
+}
|
|
|
+onMounted(async () => {
|
|
|
+ cardList.value.forEach((card) => {
|
|
|
+ showCardNo.value[card.id] = false
|
|
|
+ isFlipping.value[card.id] = false
|
|
|
+ })
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
+.page {
|
|
|
+ padding: 0 16px;
|
|
|
+}
|
|
|
+.card-wrapper {
|
|
|
+ position: absolute;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
+ will-change: transform;
|
|
|
+}
|
|
|
|
|
|
-.apply-card-steps {
|
|
|
+.card-info {
|
|
|
+ background: url(/src/assets/images/visa.png) no-repeat center center;
|
|
|
+ background-size: cover;
|
|
|
+ border-radius: 0.426667rem;
|
|
|
+ padding: 0.64rem 0.533333rem 0 0.533333rem;
|
|
|
+ color: var(--main-yellow);
|
|
|
+ box-shadow: 0 0.106667rem 0.533333rem rgba(214, 255, 0, 0.1);
|
|
|
+ border: 1px solid rgba(214, 255, 0, 0.2);
|
|
|
width: 100%;
|
|
|
- margin-bottom: 52px;
|
|
|
+ height: 100%;
|
|
|
display: flex;
|
|
|
+ justify-content: flex-start;
|
|
|
+ align-items: baseline;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ transform-style: preserve-3d;
|
|
|
+ transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
flex-direction: column;
|
|
|
- align-items: center;
|
|
|
+
|
|
|
+ &.flipping {
|
|
|
+ transform: rotateX(360deg);
|
|
|
+ }
|
|
|
+
|
|
|
+ .card-b {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ span {
|
|
|
+ display: block;
|
|
|
+ }
|
|
|
+ .valid {
|
|
|
+ font-size: var(--font-size-14);
|
|
|
+ font-weight: 500;
|
|
|
+ color: var(--black);
|
|
|
+ text-shadow: 0 0 8px rgba(214, 255, 0, 0.2);
|
|
|
+ gap: 8px;
|
|
|
+ padding-right: 30px;
|
|
|
+ line-height: 20px;
|
|
|
+ }
|
|
|
+ .lable {
|
|
|
+ font-size: var(--font-size-10);
|
|
|
+ font-weight: 400;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
-.steps-top {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
+
|
|
|
+.card-front,
|
|
|
+.card-back {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
width: 100%;
|
|
|
- position: relative;
|
|
|
- max-width: 600px;
|
|
|
- margin: 0 auto;
|
|
|
+ height: 100%;
|
|
|
+ padding: 24px 20px 16px 20px;
|
|
|
+ backface-visibility: hidden;
|
|
|
+ -webkit-backface-visibility: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.card-back {
|
|
|
+ transform: rotateY(180deg);
|
|
|
}
|
|
|
-.step {
|
|
|
+
|
|
|
+.owner {
|
|
|
+ font-size: var(--font-size-14);
|
|
|
+ line-height: 2;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ text-shadow: 0 0 10px rgba(214, 255, 0, 0.3);
|
|
|
display: flex;
|
|
|
- flex-direction: column;
|
|
|
align-items: center;
|
|
|
- position: relative;
|
|
|
- z-index: 2;
|
|
|
- flex: 0 0 auto;
|
|
|
+ gap: 8px;
|
|
|
+
|
|
|
+ i {
|
|
|
+ font-size: var(--font-size-18);
|
|
|
+ color: var(--main-yellow);
|
|
|
+ }
|
|
|
}
|
|
|
-.step-circle {
|
|
|
- width: 28px;
|
|
|
- height: 28px;
|
|
|
- color: var(--main-yellow);
|
|
|
- border-radius: 50%;
|
|
|
- font-size: 14px;
|
|
|
- font-weight: bold;
|
|
|
+
|
|
|
+.number {
|
|
|
+ color: var(--black);
|
|
|
+ font-size: var(--font-size-18);
|
|
|
+ font-weight: 500;
|
|
|
+ line-height: 3;
|
|
|
+ letter-spacing: 2px;
|
|
|
+ margin: 24px 0;
|
|
|
+ text-shadow: 0 0 15px rgba(214, 255, 0, 0.4);
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- justify-content: center;
|
|
|
- border: 2px solid var(--main-yellow);
|
|
|
- margin-bottom: 8px;
|
|
|
- transition: all 0.3s ease;
|
|
|
-}
|
|
|
-.step-circle:hover {
|
|
|
- transform: scale(1.1);
|
|
|
- box-shadow: 0 0 10px rgba(73, 247, 166, 0.3);
|
|
|
+ gap: 8px;
|
|
|
}
|
|
|
-.step-dash {
|
|
|
- flex: 1;
|
|
|
- height: 2px;
|
|
|
- background: repeating-linear-gradient(to right, var(--main-yellow), var(--main-yellow) 6px, transparent 6px, transparent 12px);
|
|
|
- min-width: 18px;
|
|
|
- position: relative;
|
|
|
- top: -16px;
|
|
|
+
|
|
|
+.actions {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin: 20px 0 16px 0;
|
|
|
}
|
|
|
-.step-label {
|
|
|
+
|
|
|
+.action-btn {
|
|
|
color: var(--white);
|
|
|
- font-size: 14px;
|
|
|
- font-weight: 500;
|
|
|
- text-align: center;
|
|
|
- line-height: 2;
|
|
|
-}
|
|
|
-.apply-btn {
|
|
|
- width: 90%;
|
|
|
- max-width: 320px;
|
|
|
- background: var(--main-yellow);
|
|
|
- color: var(--black);
|
|
|
border: none;
|
|
|
- border-radius: 24px;
|
|
|
- font-size: 17px;
|
|
|
- font-weight: 600;
|
|
|
- padding: 12px 0;
|
|
|
- margin: 0 auto 14px;
|
|
|
- display: block;
|
|
|
- box-shadow: 0 2px 8px rgba(214, 255, 0, 0.15);
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 0 12px;
|
|
|
+ font-size: var(--font-size-14);
|
|
|
cursor: pointer;
|
|
|
- transition: background 0.2s;
|
|
|
-}
|
|
|
-.apply-btn:hover {
|
|
|
- background: var(--main-yellow-dark);
|
|
|
-}
|
|
|
-.activate-btn {
|
|
|
- width: 90%;
|
|
|
- max-width: 320px;
|
|
|
- background: transparent;
|
|
|
- color: var(--main-yellow);
|
|
|
- border: 1.5px solid var(--main-yellow);
|
|
|
- border-radius: 24px;
|
|
|
- font-size: 16px;
|
|
|
- font-weight: 500;
|
|
|
- padding: 10px 0;
|
|
|
- margin: 0 auto 18px;
|
|
|
- display: block;
|
|
|
- cursor: pointer;
|
|
|
- transition: background 0.2s, color 0.2s;
|
|
|
-}
|
|
|
-.activate-btn:hover {
|
|
|
- background: var(--main-yellow);
|
|
|
- color: #232323;
|
|
|
-}
|
|
|
-.apply-card-footer {
|
|
|
- width: 100%;
|
|
|
- margin-top: 34px;
|
|
|
-}
|
|
|
-.apply-card-empty {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
align-items: center;
|
|
|
- gap: 6px;
|
|
|
+ gap: 4px;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ text-align: center;
|
|
|
+ color: #1a1a1a;
|
|
|
+ width: 100px;
|
|
|
+ height: 100px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ border-radius: 15px;
|
|
|
+ box-shadow: 0px 5px 30px 0px rgba(5, 0, 1, 0.05);
|
|
|
+ .icon {
|
|
|
+ margin: 16px 0 4px 0;
|
|
|
+ }
|
|
|
+ span {
|
|
|
+ line-height: 20px;
|
|
|
+ }
|
|
|
}
|
|
|
-.empty-text {
|
|
|
- color: var(--main-yellow);
|
|
|
- font-size: 15px;
|
|
|
- margin-top: 2px;
|
|
|
+
|
|
|
+.balance-wrap {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin: 24px 0;
|
|
|
+ font-size: var(--font-size-14);
|
|
|
}
|
|
|
-.kyc-list {
|
|
|
- background: var(--action-bg);
|
|
|
- border-radius: 16px;
|
|
|
- margin-bottom: 16px;
|
|
|
- padding: 16px 12px;
|
|
|
- width: 100%;
|
|
|
- max-width: 340px;
|
|
|
+
|
|
|
+.balance-content {
|
|
|
+ font-size: var(--font-size-20);
|
|
|
+ color: var(--white);
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+
|
|
|
+.balance-title {
|
|
|
+ font-size: var(--font-size-12);
|
|
|
+ color: var(--white);
|
|
|
}
|
|
|
|
|
|
-.kyc-item {
|
|
|
+.currency {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- padding: 12px 0;
|
|
|
- border-bottom: 1px solid var(--border, #333);
|
|
|
- font-size: 16px;
|
|
|
+ font-size: var(--font-size-14);
|
|
|
+ margin-right: 12px;
|
|
|
+ color: var(--white);
|
|
|
}
|
|
|
|
|
|
-.kyc-item:last-child {
|
|
|
- border-bottom: none;
|
|
|
+.flag {
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
+ border-radius: 50%;
|
|
|
+ margin-right: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.balance {
|
|
|
+ font-size: var(--font-size-14);
|
|
|
+ font-weight: bold;
|
|
|
+ color: var(--white);
|
|
|
}
|
|
|
|
|
|
-.kyc-icon {
|
|
|
- width: 40px;
|
|
|
- height: 40px;
|
|
|
+.transactions {
|
|
|
+ border-radius: 16px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ padding: 16px 0;
|
|
|
+}
|
|
|
+.trans-icon {
|
|
|
+ width: 48px;
|
|
|
+ height: 48px;
|
|
|
display: flex;
|
|
|
background: var(--main-bg);
|
|
|
box-shadow: 0 4px 12px rgba(214, 255, 0, 0.1);
|
|
|
@@ -272,92 +623,258 @@ onMounted(() => {
|
|
|
margin-right: 12px;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
+ .trans-icon-inner {
|
|
|
+ width: 48px;
|
|
|
+ height: 48px;
|
|
|
+ background: rgba(212, 206, 206, 0.24);
|
|
|
+ border-radius: 50%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+ i {
|
|
|
+ color: #000;
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
+ border-radius: 50%;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-.kyc-icon-inner {
|
|
|
- width: 22px;
|
|
|
- height: 22px;
|
|
|
- background: var(--white);
|
|
|
- border-radius: 50%;
|
|
|
+.trans-header {
|
|
|
display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
align-items: center;
|
|
|
- justify-content: center;
|
|
|
+ margin: 32px 0;
|
|
|
+ color: var(--white);
|
|
|
+ i {
|
|
|
+ font-size: var(--font-size-20);
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
-.kyc-icon-inner i {
|
|
|
- color: #000;
|
|
|
- width: 18px;
|
|
|
- height: 18px;
|
|
|
- font-size: var(--font-size-16);
|
|
|
- border-radius: 50%;
|
|
|
+::v-deep .van-calendar {
|
|
|
+ background: var(--action-bg);
|
|
|
+}
|
|
|
+::v-deep .van-calendar__month-mark {
|
|
|
+ display: none;
|
|
|
+}
|
|
|
+::v-deep .van-calendar__header-subtitle {
|
|
|
+ color: var(--white);
|
|
|
+}
|
|
|
+::v-deep .van-calendar__header-title {
|
|
|
+ color: var(--white);
|
|
|
+}
|
|
|
+::v-deep .van-calendar__month-title {
|
|
|
+ color: var(--main-yellow);
|
|
|
}
|
|
|
|
|
|
-.kyc-left {
|
|
|
- flex: 1;
|
|
|
- min-width: 0;
|
|
|
+.trans-title {
|
|
|
+ font-size: var(--font-size-20);
|
|
|
+ color: var(--white);
|
|
|
+ font-weight: bold;
|
|
|
}
|
|
|
|
|
|
-.kyc-right {
|
|
|
- width: 80px;
|
|
|
- text-align: right;
|
|
|
- margin-left: 6px;
|
|
|
- .apply-btn {
|
|
|
- padding: 10px 0;
|
|
|
- font-size: var(--font-size-14);
|
|
|
- font-weight: 500;
|
|
|
+.date-field {
|
|
|
+ width: 200px;
|
|
|
+
|
|
|
+ :deep(.van-field__control) {
|
|
|
+ color: var(--white);
|
|
|
}
|
|
|
+
|
|
|
+ :deep(.van-field__placeholder) {
|
|
|
+ color: var(--gray);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.van-popup) {
|
|
|
+ background: var(--action-bg);
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.van-picker__toolbar) {
|
|
|
+ background: var(--action-bg);
|
|
|
+ border-bottom: 1px solid var(--border);
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.van-picker__title) {
|
|
|
+ color: var(--white);
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.van-picker__confirm) {
|
|
|
+ color: var(--main-yellow);
|
|
|
}
|
|
|
|
|
|
-.kyc-type {
|
|
|
+:deep(.van-picker__cancel) {
|
|
|
+ color: var(--gray);
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.van-picker-column) {
|
|
|
color: var(--white);
|
|
|
- font-size: 15px;
|
|
|
- margin-bottom: 4px;
|
|
|
}
|
|
|
|
|
|
-.kyc-desc {
|
|
|
- color: #bdbdbd;
|
|
|
- font-size: 13px;
|
|
|
+:deep(.van-picker-column__item) {
|
|
|
+ color: var(--white);
|
|
|
}
|
|
|
|
|
|
-.kyc-amount {
|
|
|
- font-size: 15px;
|
|
|
+:deep(.van-picker-column__item--selected) {
|
|
|
color: var(--main-yellow);
|
|
|
- margin-bottom: 4px;
|
|
|
}
|
|
|
|
|
|
-.kyc-date {
|
|
|
- color: #bdbdbd;
|
|
|
- font-size: 13px;
|
|
|
+.transaction {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 10px 0;
|
|
|
+ /* border-bottom: 1px solid var(--border); */
|
|
|
+ font-size: var(--font-size-16);
|
|
|
+}
|
|
|
+
|
|
|
+.transaction:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+}
|
|
|
+
|
|
|
+.trans-left {
|
|
|
+ width: 200px;
|
|
|
}
|
|
|
|
|
|
-.kyc-list-title {
|
|
|
+.trans-right {
|
|
|
+ width: 100px;
|
|
|
+
|
|
|
+ div {
|
|
|
+ text-align: right;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.trans-type {
|
|
|
+ color: var(--white);
|
|
|
font-size: var(--font-size-16);
|
|
|
- color: var(--main-yellow);
|
|
|
- margin-bottom: 12px;
|
|
|
- font-weight: 500;
|
|
|
+ font-weight: 600;
|
|
|
+ line-height: 2;
|
|
|
+}
|
|
|
+
|
|
|
+.trans-desc {
|
|
|
+ font-size: var(--font-size-12);
|
|
|
+ color: var(--gray);
|
|
|
+}
|
|
|
+
|
|
|
+.trans-amount {
|
|
|
+ font-size: var(--font-size-16);
|
|
|
+ font-weight: 600;
|
|
|
+ color: var(--white);
|
|
|
+ line-height: 2;
|
|
|
+}
|
|
|
+
|
|
|
+.trans-date {
|
|
|
+ color: var(--gray);
|
|
|
+ font-size: var(--font-size-12);
|
|
|
+}
|
|
|
+
|
|
|
+.card-swiper {
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ padding-bottom: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.swiper-container {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ height: 209px;
|
|
|
+ touch-action: pan-y pinch-zoom;
|
|
|
+ user-select: none;
|
|
|
+}
|
|
|
+
|
|
|
+.card-info {
|
|
|
+ position: absolute;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ transition: transform 0.3s ease;
|
|
|
+ will-change: transform;
|
|
|
+}
|
|
|
+
|
|
|
+.swiper-controls {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ margin-top: 16px;
|
|
|
+ gap: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.swiper-btn {
|
|
|
+ background: var(--action-bg);
|
|
|
+ border: none;
|
|
|
+ border-radius: 50%;
|
|
|
+ width: 32px;
|
|
|
+ height: 32px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ cursor: pointer;
|
|
|
+ color: var(--main-yellow);
|
|
|
+
|
|
|
+ &:disabled {
|
|
|
+ opacity: 0.5;
|
|
|
+ cursor: not-allowed;
|
|
|
+ }
|
|
|
+
|
|
|
+ i {
|
|
|
+ font-size: var(--font-size-20);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.swiper-dots {
|
|
|
+ display: flex;
|
|
|
gap: 8px;
|
|
|
+}
|
|
|
|
|
|
- &::before {
|
|
|
- content: '';
|
|
|
- display: inline-block;
|
|
|
- width: 4px;
|
|
|
- height: 16px;
|
|
|
+.dot {
|
|
|
+ width: 8px;
|
|
|
+ height: 8px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: var(--action-bg);
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &.active {
|
|
|
background: var(--main-yellow);
|
|
|
- border-radius: 2px;
|
|
|
+ transform: scale(1.2);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-.processing-status {
|
|
|
- color: var(--main-yellow);
|
|
|
- font-size: 14px;
|
|
|
- text-align: center;
|
|
|
+.swiper-indicators {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ margin-top: 16px;
|
|
|
+ gap: 8px;
|
|
|
}
|
|
|
|
|
|
-.success-status {
|
|
|
- color: #4caf50;
|
|
|
- font-size: 14px;
|
|
|
- text-align: center;
|
|
|
+.indicator-dot {
|
|
|
+ width: 8px;
|
|
|
+ height: 8px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: var(--action-bg);
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background: var(--main-yellow);
|
|
|
+ transform: scale(1.2);
|
|
|
+ }
|
|
|
+}
|
|
|
+.flags {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ cursor: pointer;
|
|
|
+ position: absolute;
|
|
|
+ top: 10px;
|
|
|
+ right: 10px;
|
|
|
}
|
|
|
+
|
|
|
+.balance-content {
|
|
|
+ font-size: var(--font-size-20);
|
|
|
+ color: var(--white);
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+.cwg-btn {
|
|
|
+ margin-top: 36px;
|
|
|
+}
|
|
|
+
|
|
|
</style>
|