aslan-h5/src/views/BDCenter/availableIncome.vue

790 lines
23 KiB
Vue

<template>
<div class="transfer gradient-background-circles">
<GeneralHeader
:title="t('available_salaries')"
:showLanguageList="true"
color="black"
style="box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); z-index: 999"
/>
<div class="content">
<!-- 信息部分 -->
<div>
<div
style="
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
"
>
<div style="font-weight: 600">{{ t('available_salaries') }}</div>
<!-- <div style="display: flex; align-items: center" @click="showDetails">
<div style="color: rgba(0, 0, 0, 0.4)">{{ t('details') }}</div>
<img src="../../assets/icon/arrow.png" alt="" width="16px" class="flipImg" />
</div> -->
</div>
<div
style="
background-color: white;
padding: 16px;
border-radius: 12px;
margin-bottom: 12px;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
"
>
<span v-if="loading" class="loading-text">{{ t('loading') }}...</span>
<span v-else style="font-weight: 700; color: #333; font-size: 1.1em"
>${{ availableIncome }}</span
>
</div>
</div>
<!-- 操作按钮 -->
<div
style="
display: flex;
justify-content: space-around;
align-items: center;
margin-bottom: 12px;
"
>
<button
v-for="action in actions"
:key="action.name"
@click="handleAction(action.name)"
:style="{
padding: '8px 12px',
borderRadius: '8px',
background:
activeAction === action.name
? 'linear-gradient(135deg, #BB92FF 2.82%, #8B45FF 99.15%)'
: 'linear-gradient(135deg, #A2A2A2 2.82%, #646464 99.15%)',
border: 'none',
color: 'white',
fontWeight: '600',
cursor: 'pointer',
transition: 'all 0.2s',
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
}"
>
{{ t(action.name.toLowerCase()) }}
</button>
</div>
<!-- Exchange -->
<div v-if="activeAction === 'Exchange'">
<div
style="
background-color: white;
padding: 16px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
"
>
<!-- 兑换金额选择 -->
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px">
<div
v-for="option in exchangeOptions"
:key="option.id"
@click="selectExchangeOption(option)"
:style="{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
border:
selectedExchangeOption === option.id ? '2px solid #8b5cf6' : '2px solid #e5e7eb',
borderRadius: '12px',
cursor: 'pointer',
transition: 'all 0.2s',
backgroundColor: selectedExchangeOption === option.id ? '#f3f4f6' : 'white',
aspectRatio: '1/1',
}"
>
<img src="../../assets/icon/coin.png" alt="" style="width: 40%" />
<div style="font-weight: 500; color: #373232">
<!-- {{ option.coins }} -->
</div>
<div style="font-weight: 500; color: #131111">${{ option.cash }}</div>
</div>
</div>
</div>
<!-- 兑换按钮 -->
<div style="display: flex; justify-content: center; margin-top: 10px">
<button
style="
width: 70%;
padding: 12px;
background: linear-gradient(135deg, #bb92ff 2.82%, #8b45ff 99.15%);
color: white;
border: none;
border-radius: 32px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
"
@click="handleExchange"
:disabled="!selectedExchangeOption"
:style="{
background: !selectedExchangeOption
? '#ccc'
: 'linear-gradient(135deg, #bb92ff 2.82%, #8b45ff 99.15%)',
opacity: !selectedExchangeOption ? 0.6 : 1,
}"
>
{{ t('exchange') }}
</button>
</div>
</div>
<!-- Transfer转账对象 -->
<div v-if="activeAction === 'Transfer'">
<div style="font-weight: 600; margin-bottom: 10px">{{ t('transfer_to_others') }}</div>
<div
style="
background-color: white;
padding: 16px;
border-radius: 12px;
margin-bottom: 12px;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
"
>
<!-- 用户信息 -->
<div v-if="selectedPayee.id" style="width: 100%">
<!-- 头像信息 -->
<div style="display: flex; align-items: center; margin-bottom: 12px">
<!-- 头像 -->
<div
style="
position: relative;
width: 50px;
height: 50px;
border-radius: 25px;
background-color: #8b5cf6;
color: white;
margin-right: 12px;
overflow: hidden;
font-size: 20px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
"
>
<img
v-if="selectedPayee.avatar"
:src="selectedPayee.avatar"
:alt="selectedPayee.name"
style="width: 100%; height: 100%; object-fit: cover; border-radius: 50%"
/>
<span v-else>{{ selectedPayee.name.charAt(0) }}</span>
</div>
<!-- 个人信息 -->
<div style="flex: 1">
<div style="margin: 0 0 4px 0; font-size: 16px; font-weight: 600; color: #333">
{{ selectedPayee.name }}
</div>
<div style="margin: 0; font-size: 14px; color: #666">
ID: {{ selectedPayee.account || selectedPayee.id }}
</div>
</div>
<!-- 换人按钮 -->
<button
style="
border-radius: 12px;
border: 1px solid #e6e6e6;
color: rgba(0, 0, 0, 0.4);
font-weight: 500;
padding: 2px 8px;
background: none;
"
@click="searchPayee"
>
{{ t('change') }}
</button>
</div>
<!-- 身份 -->
<div style="margin-left: 62px; height: 20px; display: flex; gap: 5px">
<img
v-if="selectedPayee.isHost"
src="../../assets/icon/host.png"
alt=""
height="100%"
/>
<img
v-if="selectedPayee.isAgency"
src="../../assets/icon/agency.png"
alt=""
height="100%"
/>
<img
v-if="selectedPayee.hasSalary"
src="../../assets/icon/RA.png"
alt=""
height="100%"
/>
</div>
</div>
<!-- 空状态 - 默认显示 -->
<div v-else style="display: flex; justify-content: center; align-items: center">
<div
style="
font-weight: 590;
color: rgba(0, 0, 0, 0.4);
padding: 16px;
border-radius: 12px;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
"
@click="searchPayee"
>
{{ t('search') }}
</div>
</div>
</div>
</div>
<!-- Transfer -->
<div v-if="activeAction === 'Transfer'">
<div
style="
background-color: white;
padding: 16px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
"
>
<!-- 转账金额选择 -->
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px">
<div
v-for="coin in coinOptions"
:key="coin.id"
@click="selectCoin(coin)"
:style="{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
border: selectedCoin === coin.id ? '2px solid #8b5cf6' : '2px solid #e5e7eb',
borderRadius: '12px',
cursor: 'pointer',
transition: 'all 0.2s',
backgroundColor: selectedCoin === coin.id ? '#f3f4f6' : 'white',
aspectRatio: '1/1',
}"
>
<img src="../../assets/icon/coin.png" alt="" style="width: 40%" />
<div style="font-weight: 500; color: #373232">
<!-- {{ coin.amount }} -->
</div>
<div style="font-weight: 500; color: #131111">${{ coin.price }}</div>
</div>
</div>
</div>
<!-- 转账按钮 -->
<div style="display: flex; justify-content: center; margin-top: 10px">
<button
style="
width: 70%;
padding: 12px;
background: linear-gradient(135deg, #bb92ff 2.82%, #8b45ff 99.15%);
color: white;
border: none;
border-radius: 32px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
"
@click="transfer"
:disabled="!selectedCoin || !selectedPayee.id"
:style="{
background:
!selectedCoin || !selectedPayee.id
? '#ccc'
: 'linear-gradient(135deg, #bb92ff 2.82%, #8b45ff 99.15%)',
opacity: !selectedCoin || !selectedPayee.id ? 0.6 : 1,
}"
>
{{ t('transfer') }}
</button>
</div>
</div>
<!-- Cash out -->
<div v-if="activeAction === 'Cash out'">
<!-- 提现资料 -->
<!-- 银行帐号信息 -->
<div>
<!-- 标题 -->
<div style="display: flex; align-items: center; margin-bottom: 10px">
<div style="font-weight: 600">{{ t('kyc') }}</div>
</div>
<!-- 银行卡信息 -->
<div
style="
background-color: white;
padding: 16px;
border-radius: 12px;
margin-bottom: 12px;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
"
>
<!-- 未上传银行卡信息 -->
<div
v-if="!hasBankCard"
style="display: flex; justify-content: space-between; align-items: center"
@click="gotoKYC"
>
<span style="font-weight: 510; font-size: 1em">{{ t('complete_information') }}</span>
<img src="../../assets/icon/arrow.png" alt="" style="width: 6vw" class="flipImg" />
</div>
<!-- 获取到银行卡信息 -->
<div v-else style="width: 100%; display: flex; flex-direction: column; gap: 8px">
<!-- 图片 -->
<div style="display: flex; justify-content: space-between; align-items: center">
<span style="font-weight: 510; font-size: 1em">{{
t('passport_id_card', { current: bankCardInfo.cardImages.length })
}}</span>
<img src="../../assets/icon/arrow.png" alt="" style="width: 6vw" @click="gotoKYC" />
</div>
<div style="display: flex; align-items: center; gap: 4px">
<img
:src="imgUrl"
alt=""
v-for="imgUrl in bankCardInfo.cardImages"
style="width: 10vw; aspect-ratio: 1/1; border-radius: 4px"
/>
</div>
<!-- 卡号 -->
<div style="font-weight: 500">{{ t('contact_number') }}:</div>
<div style="color: rgba(0, 0, 0, 0.4); font-weight: 600; font-size: 0.8em">
{{ bankCardInfo.contactNumber }}
</div>
<!-- 备注信息 -->
<div style="font-weight: 500">{{ t('other_description') }}:</div>
<div style="color: rgba(0, 0, 0, 0.4); font-weight: 600; font-size: 0.8em">
{{ bankCardInfo.description }}
</div>
</div>
</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: hasBankCard
? '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="CashOut"
>
{{ t('cash_out') }}
</div>
</div>
</div>
</div>
<!-- 弹窗遮罩层 -->
<maskLayer :maskLayerShow="maskLayerShow" @click="maskLayerShow = false">
<!-- 输入邀请码模块 -->
<div
style="
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
"
@click="maskLayerShow = false"
>
<div
style="
width: 80%;
border-radius: 12px;
background: #fff;
padding: 12px;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
"
@click.stop
>
<div style="color: rgba(0, 0, 0, 0.8); font-weight: 800">{{ t('tips') }}</div>
<div style="color: rgba(0, 0, 0, 0.4); font-weight: 600; font-size: 0.7em">
{{ t('cash_withdrawal_tips') }}
</div>
<div
style="
display: flex;
justify-content: center;
align-items: center;
border-radius: 12px;
background: linear-gradient(
152deg,
rgba(198, 112, 255, 0.8) 7.01%,
rgba(119, 38, 255, 0.8) 92.99%
);
color: #fff;
font-weight: 800;
padding: 8px 12px;
width: 50%;
"
@click="Receive"
>
{{ t('receive') }}
</div>
</div>
</div>
</maskLayer>
</div>
</template>
<script setup>
import { useI18n } from 'vue-i18n'
import { ref, reactive, onMounted, onUnmounted, computed } from 'vue'
import { useRouter } from 'vue-router'
import GeneralHeader from '@/components/GeneralHeader.vue'
import {
getBankBalance,
userBankTransfer,
userSalaryCheckExchange,
userBankExchangeGold,
} from '@/api/wallet.js'
import { getSelectedPayee, clearSelectedPayee } from '@/utils/payeeStore.js'
import { showError, showSuccess } from '@/utils/toast.js'
import { setApplyInfo, getApplyInfo } from '@/utils/applyStore.js'
import maskLayer from '@/components/MaskLayer.vue'
import { withdrawApply } from '@/api/bdCenter.js'
import { getUserId } from '@/utils/userStore.js'
import { setDocumentDirection } from '@/locales/i18n'
const { t, locale } = useI18n()
const router = useRouter()
// 监听语言变化并设置文档方向
locale.value && setDocumentDirection(locale.value)
const availableIncome = ref('')
const selectedCoin = ref(null)
const selectedExchangeOption = ref(null)
const activeAction = ref('Exchange')
const loading = ref(false)
const maskLayerShow = ref(false)
// 操作按钮
const actions = ref([{ name: 'Exchange' }, { name: 'Transfer' }, { name: 'Cash out' }])
// 查看历史提现信息
const gotoKYC = () => {
router.push('/KYC')
}
// 提现按钮
const CashOut = () => {
if (hasBankCard.value) {
try {
maskLayerShow.value = true
} catch (error) {
showError(error.errorMsg)
}
}
}
// 选中的收款人
const selectedPayee = reactive({
id: null,
account: '',
name: '',
avatar: '',
type: '',
isHost: false,
isAgency: false,
hasSalary: false,
})
// 转账金币选项
const coinOptions = reactive([
{ id: 1, amount: 10500, price: 1 },
{ id: 2, amount: 52500, price: 5 },
{ id: 3, amount: 105000, price: 10 },
{ id: 4, amount: 525000, price: 50 },
{ id: 5, amount: 1050000, price: 100 },
{ id: 6, amount: 2100000, price: 200 },
])
// 兑换选项
const exchangeOptions = reactive([
{ id: 1, coins: 10500, cash: 1 },
{ id: 2, coins: 52500, cash: 5 },
{ id: 3, coins: 105000, cash: 10 },
{ id: 4, coins: 525000, cash: 50 },
{ id: 5, coins: 1050000, cash: 100 },
{ id: 6, coins: 2100000, cash: 200 },
])
const bankCardInfo = ref({})
const hasBankCard = computed(() => {
return Object.keys(bankCardInfo.value).length > 0
})
// 初始化收款人信息
const initializePayee = () => {
const savedPayee = getSelectedPayee()
if (savedPayee) {
Object.assign(selectedPayee, savedPayee)
}
}
// 获取银行余额
const fetchBankBalance = async () => {
loading.value = true
try {
const response = await getBankBalance()
if (response.status && typeof response.body === 'number') {
availableIncome.value = response.body.toFixed(2) || '0.00'
} else {
availableIncome.value = '-'
}
} catch (error) {
console.error('Failed to fetch bank balance:', error)
availableIncome.value = '-'
showError(error.response?.errorMsg || t('failed_to_load_balance'))
} finally {
loading.value = false
}
}
// 方法
const showDetails = () => {
router.push('/income-details')
}
const searchPayee = () => {
router.push('/search-payee')
}
// 处理操作按钮点击
const handleAction = (actionName) => {
activeAction.value = actionName
// 重置所有选择
selectedCoin.value = null
selectedExchangeOption.value = null
}
// 清除当前选中的收款人
const clearPayee = () => {
Object.assign(selectedPayee, {
id: null,
account: '',
name: '',
avatar: '',
type: '',
isHost: false,
isAgency: false,
hasSalary: false,
})
clearSelectedPayee()
}
const selectCoin = (coin) => {
if (selectedCoin.value === coin.id) {
selectedCoin.value = null
} else {
selectedCoin.value = coin.id
}
}
const selectExchangeOption = (option) => {
if (selectedExchangeOption.value === option.id) {
selectedExchangeOption.value = null
} else {
selectedExchangeOption.value = option.id
}
}
const transfer = async () => {
if (!selectedCoin.value) {
showError(t('please_select_amount'))
return
}
if (!selectedPayee.id) {
showError(t('please_select_payee'))
return
}
const selectedCoinData = coinOptions.find((coin) => coin.id === selectedCoin.value)
const transferData = {
amount: selectedCoinData.price,
acceptUserId: selectedPayee.id,
acceptMethod: 'BANK_TRANSFER',
}
try {
const response = await userBankTransfer(transferData)
if (response.status) {
showSuccess(
t('transfer_success', { price: selectedCoinData.price, name: selectedPayee.name })
)
selectedCoin.value = null
await fetchBankBalance()
} else {
showError(t('transfer_failed', { price: selectedCoinData.price }))
}
} catch (error) {
console.error('Transfer failed:', error)
showError(error.response?.errorMsg || t('transfer_error'))
}
}
const handleExchange = async () => {
if (!selectedExchangeOption.value) {
showError(t('please_select_coin_amount'))
return
}
const selectedOption = exchangeOptions.find(
(option) => option.id === selectedExchangeOption.value
)
try {
// 调用接口进行兑换
const response = await userBankExchangeGold({
coinId: selectedOption.id,
amount: selectedOption.cash,
price: selectedOption.cash,
})
if (response.status === true || response.errorCode === 0) {
showSuccess(t('exchange_success', { price: selectedOption.cash }))
// 刷新银行余额
await fetchBankBalance()
}
selectedExchangeOption.value = null
} catch (error) {
console.error('Exchange failed:', error)
showError(error.response?.errorMsg || t('exchange_error_occurred'))
}
}
// 确认提现
const Receive = async () => {
try {
const resApply = await withdrawApply({ ...getApplyInfo(), activityId: getUserId() })
if (resApply.status) {
setApplyInfo()
}
maskLayerShow.value = false
} catch (error) {
maskLayerShow.value = false
showError(error.response?.errorMsg || t('something_went_wrong'))
}
}
// 页面加载时获取银行余额和初始化收款人
onMounted(() => {
fetchBankBalance()
initializePayee()
if (getApplyInfo()) {
bankCardInfo.value =
{ ...getApplyInfo(), cardImages: JSON.parse(getApplyInfo().cardImages) } || {}
}
})
onUnmounted(() => {
clearPayee()
// setApplyInfo()
})
</script>
<style scoped>
* {
color: rgba(0, 0, 0, 0.8);
font-family: 'SF Pro Text';
}
.transfer {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
width: 100vw;
min-height: 100vh;
background-color: #ffffff;
background-image: url(../../assets/images/secondBg.png);
background-size: 100% auto;
background-repeat: no-repeat;
position: relative;
}
.content {
padding: 16px;
position: relative;
z-index: 2;
}
.loading-text {
font-weight: 500;
color: #8b5cf6;
font-style: italic;
}
@media screen and (max-width: 360px) {
* {
font-size: 10px;
}
}
@media screen and (min-width: 360px) {
* {
font-size: 12px;
}
}
@media screen and (min-width: 768px) {
* {
font-size: 16px;
}
}
/* RTL支持 */
[dir='rtl'] .avatar {
margin-right: 0;
margin-left: 12px;
}
[dir='rtl'] .user-info {
text-align: right;
}
[dir='rtl'] .flipImg {
transform: scaleX(-1);
}
</style>