aslan-h5/src/views/TransferView.vue
2025-08-14 23:35:56 +08:00

525 lines
11 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="transfer gradient-background-circles">
<MobileHeader title="Transfer" />
<div class="content">
<!-- 信息部分 -->
<div class="info-section">
<div class="info-header">
<h3>Information</h3>
<button class="details-btn" @click="showDetails">
Details <span>></span>
</button>
</div>
<div class="available-salary">
<span>Available salary: </span>
<span v-if="loading" class="loading-text">Loading...</span>
<span v-else class="amount">{{ availableSalary }}</span>
</div>
</div>
<!-- 转账部分 -->
<div class="transfer-section">
<h3>Transfer to others</h3>
<!-- 收款人信息 -->
<div v-if="selectedPayee.id" class="payee-info" @click="searchPayee">
<div class="payee-details">
<div class="payee-avatar">
<img v-if="selectedPayee.avatar" :src="selectedPayee.avatar" :alt="selectedPayee.name" />
<span v-else>{{ selectedPayee.name.charAt(0) }}</span>
</div>
<div class="payee-text">
<h4>{{ selectedPayee.name }}</h4>
<p>ID: {{ selectedPayee.account || selectedPayee.id }}</p>
<div class="payee-tags">
<span v-if="selectedPayee.isHost" class="tag host-tag">👑 Host</span>
<span v-if="selectedPayee.isAgency" class="tag agency-tag">🏢 Agency</span>
<span v-if="selectedPayee.hasSalary" class="tag salary-tag">💰 Salary</span>
</div>
</div>
</div>
<button class="change-btn" @click.stop="clearPayee">Change</button>
</div>
<!-- 空状态 - 默认显示 -->
<div v-else class="empty-payee-state" @click="searchPayee">
<div class="search-placeholder">
<div class="search-icon">🔍</div>
<span>Search</span>
</div>
</div>
<!-- 转账金额选择 -->
<div class="coins-grid">
<div
v-for="coin in coinOptions"
:key="coin.id"
@click="selectCoin(coin)"
:class="['coin-item', { selected: selectedCoin === coin.id }]"
>
<div class="coin-icon">🪙</div>
<div class="coin-amount">{{ coin.amount }}</div>
<div class="coin-price">${{ coin.price }}</div>
</div>
</div>
<!-- 转账按钮 -->
<button
class="transfer-btn"
@click="transfer"
:disabled="!selectedCoin || !selectedPayee.id"
>
Transfer
</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import MobileHeader from '../components/MobileHeader.vue'
import {getBankBalance, userBankTransfer} from '../api/wallet.js'
import { getSelectedPayee, clearSelectedPayee } from '../utils/payeeStore.js'
const router = useRouter()
const availableSalary = ref('0')
const selectedCoin = ref(null)
const loading = ref(false)
// 选中的收款人
const selectedPayee = reactive({
id: null,
account: '',
name: '',
avatar: '',
type: '',
isHost: false,
isAgency: false,
hasSalary: false
})
// 初始化收款人信息
const initializePayee = () => {
const savedPayee = getSelectedPayee()
if (savedPayee) {
Object.assign(selectedPayee, savedPayee)
}
}
// 金币选项
const coinOptions = reactive([
{ id: 1, amount: 10000, price: 1 },
{ id: 2, amount: 50000, price: 5 },
{ id: 3, amount: 100000, price: 10 },
{ id: 4, amount: 500000, price: 50 },
{ id: 5, amount: 1000000, price: 100 },
{ id: 6, amount: 2000000, price: 200 }
])
// 获取银行余额
const fetchBankBalance = async () => {
loading.value = true
try {
const response = await getBankBalance()
if (response.status && typeof response.body === 'number') {
// 格式化余额显示保疙2位小数
availableSalary.value = response.body.toFixed(2)
} else {
availableSalary.value = '0.00'
}
} catch (error) {
console.error('Failed to fetch bank balance:', error)
availableSalary.value = '0.00'
// 可以显示错误提示
alert('Failed to load balance. Please try again.')
} finally {
loading.value = false
}
}
// 方法
const showDetails = () => {
router.push('/information-details')
}
const searchPayee = () => {
router.push('/search-payee')
}
// 清除当前选中的收款人
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 transfer = async () => {
if (!selectedCoin.value) {
alert('Please select an amount first')
return
}
if (!selectedPayee.id) {
alert('Please select a payee first')
return
}
const selectedCoinData = coinOptions.find(coin => coin.id === selectedCoin.value)
// 获取支付密码
// const password = prompt('Please enter your payment password:')
// if (!password) {
// alert('Payment password is required')
// return
// }
// 构造请求参数
const transferData = {
amount: selectedCoinData.price,
acceptUserId: selectedPayee.id,
acceptMethod: 'BANK_TRANSFER',
// password: password
}
try {
// 调用转账接口
const response = await userBankTransfer(transferData)
// 转账成功
alert(`Successfully transferred $${selectedCoinData.price} to ${selectedPayee.name}`)
// 重置选择
selectedCoin.value = null
// 刷新银行余额
await fetchBankBalance()
} catch (error) {
// 错误处理
console.error('Transfer failed:', error)
if (error.errorCode === 5000) {
alert('Insufficient balance')
} else if (error.errorCode === 5010 || error.errorCode === 5009) {
alert('Payment password error')
} else {
alert('Transfer failed, please try again later')
}
}
}
// 页面加载时获取银行余额和初始化收款人
onMounted(() => {
fetchBankBalance()
initializePayee()
})
</script>
<style scoped>
.transfer {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
}
.content {
padding: 16px;
position: relative;
z-index: 2;
}
/* 信息部分 */
.info-section {
background-color: white;
padding: 16px;
border-radius: 12px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.info-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.info-header h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: #333;
}
.details-btn {
background: none;
border: none;
color: #666;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
}
.available-salary {
font-size: 14px;
color: #666;
}
.available-salary .amount {
font-weight: 600;
color: #333;
}
.available-salary .loading-text {
font-weight: 500;
color: #8B5CF6;
font-style: italic;
}
/* 转账部分 */
.transfer-section {
background-color: white;
padding: 16px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.transfer-section h3 {
margin: 0 0 16px 0;
font-size: 16px;
font-weight: 600;
color: #333;
}
/* 收款人信息 */
.payee-info {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px;
border: 1px solid #e0e0e0;
border-radius: 12px;
margin-bottom: 20px;
cursor: pointer;
transition: background-color 0.2s;
}
.payee-info:active {
background-color: #f9f9f9;
}
.payee-details {
display: flex;
align-items: center;
flex: 1;
}
.payee-avatar {
width: 40px;
height: 40px;
border-radius: 20px;
background-color: #8B5CF6;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 16px;
font-weight: 600;
margin-right: 12px;
overflow: hidden;
}
.payee-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.payee-text h4 {
margin: 0 0 4px 0;
font-size: 14px;
font-weight: 600;
color: #333;
}
.payee-text p {
margin: 0 0 8px 0;
font-size: 12px;
color: #666;
}
.payee-tags {
display: flex;
gap: 4px;
}
.tag {
padding: 2px 8px;
border-radius: 12px;
font-size: 10px;
font-weight: 600;
}
.host-tag {
background-color: #E0E7FF;
color: #5B21B6;
}
.agency-tag {
background-color: #DBEAFE;
color: #1E40AF;
}
.salary-tag {
background-color: #FEF3C7;
color: #D97706;
}
.change-btn {
background-color: #8B5CF6;
color: white;
border: none;
padding: 6px 12px;
border-radius: 6px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
}
.change-btn:active {
background-color: #7C3AED;
}
/* 空状态样式 */
.empty-payee-state {
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
border: 2px dashed #ddd;
border-radius: 12px;
margin-bottom: 20px;
cursor: pointer;
transition: all 0.2s;
background-color: #fafafa;
}
.empty-payee-state:hover {
border-color: #8B5CF6;
background-color: #f8f5ff;
}
.empty-payee-state:active {
background-color: #f0e6ff;
}
.search-placeholder {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
color: #666;
}
.search-icon {
font-size: 24px;
}
.search-placeholder span {
font-size: 14px;
font-weight: 500;
}
/* 金币网格 */
.coins-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
margin-bottom: 24px;
}
.coin-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 16px 8px;
border: 2px solid #e0e0e0;
border-radius: 12px;
cursor: pointer;
transition: all 0.2s;
background-color: white;
}
.coin-item.selected {
border-color: #8B5CF6;
background-color: #F3F4F6;
}
.coin-item:active {
transform: scale(0.98);
}
.coin-icon {
font-size: 24px;
margin-bottom: 8px;
}
.coin-amount {
font-size: 14px;
font-weight: 600;
color: #333;
margin-bottom: 4px;
}
.coin-price {
font-size: 12px;
color: #666;
}
/* 转账按钮 */
.transfer-btn {
width: 100%;
padding: 14px;
background-color: #8B5CF6;
color: white;
border: none;
border-radius: 12px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: background-color 0.2s;
}
.transfer-btn:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.transfer-btn:not(:disabled):active {
background-color: #7C3AED;
}
</style>