aslan-h5/src/views/ExchangeGoldCoinsView.vue

398 lines
9.6 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="exchange-gold-coins gradient-background-circles">
<!-- 使用 GeneralHeader 替换 MobileHeader -->
<GeneralHeader
:title="t('exchange_gold_coins')"
:showBack="true"
: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('information') }}</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="" 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 style="font-weight: 510; font-size: 1em">{{ t('available_salaries') }} </span>
<span v-if="loading" class="loading-text">{{ t('loading') }}...</span>
<span v-else style="font-weight: 700; color: #333; font-size: 1.1em">{{
availableSalary
}}</span>
</div>
</div>
<!-- 兑换部分 -->
<div>
<div style="font-weight: 600; margin-bottom: 10px">{{ t('exchange_gold_coins') }}</div>
<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: 12px">
<div
v-for="coin in coinOptions"
:key="coin.id"
@click="selectCoin(coin)"
:class="['coin-item', { selected: selectedCoin === coin.id }]"
>
<img src="../assets/icon/dollar.png" alt="" style="width: 40%" />
<div style="font-weight: 500; color: #131111">${{ coin.price }}</div>
</div>
</div>
</div>
<!-- 兑换按钮 -->
<div style="display: flex; justify-content: center; margin-top: 10px">
<button class="exchange-btn" @click="exchangeCoins" :disabled="!selectedCoin">
{{ t('exchange') }}
</button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import GeneralHeader from '@/components/GeneralHeader.vue'
import { getBankBalance, userSalaryCheckExchange, userBankExchangeGold } from '../api/wallet.js'
import { showError, showSuccess, showWarning } from '@/utils/toast.js'
import { setDocumentDirection } from '@/locales/i18n'
const { t, locale } = useI18n()
const router = useRouter()
// 监听语言变化并设置文档方向
locale.value && setDocumentDirection(locale.value)
const availableSalary = ref('0')
const selectedCoin = ref(null)
const loading = ref(false)
const isLoadingRole = ref(false)
const userRole = ref('')
// 金币选项
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 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'
// 可以显示错误提示
showError(t('failed_to_load_balance'))
} finally {
loading.value = false
}
}
// 检查用户exchange角色
const checkUserRole = async () => {
isLoadingRole.value = true
try {
const response = await userSalaryCheckExchange()
if (response.status && response.body) {
// 根据接口返回的role设置用户角色
userRole.value = response.body.role || ''
console.debug('用户角色:', userRole.value)
} else {
// 接口返回成功但没有有效数据的情况
userRole.value = ''
router.back() // 对于其他严重错误,也建议退出页面
}
} catch (error) {
// 处理特定错误情况
if (error.errorCode === 1083) {
// NOT_OPEN_ERROR
showWarning(t('exchange_not_available'))
} else if (error.message && error.message.includes('HTTP Error: 406')) {
// 处理 406 Not Acceptable 状态码
showError(t('request_not_acceptable'))
} else {
// 其他错误情况
showError(t('failed_to_load_user_info'))
}
router.back() // 对于其他严重错误,也建议退出页面
// 清理状态
userRole.value = ''
} finally {
isLoadingRole.value = false
}
}
const exchangeCoins = async () => {
if (!selectedCoin.value) {
showError(t('please_select_coin_amount'))
return
}
const selectedCoinData = coinOptions.find((coin) => coin.id === selectedCoin.value)
// 检查余额是否足够
const coinPrice = selectedCoinData.price
const availableBalance = parseFloat(availableSalary.value)
if (isNaN(availableBalance) || availableBalance < coinPrice) {
showError(
t('insufficient_balance', {
balance: availableBalance.toFixed(2),
price: coinPrice,
})
)
return
}
// 显示确认对话框
const confirmExchange = confirm(t('confirm_exchange_coins', { price: selectedCoinData.price }))
if (confirmExchange) {
try {
// 调用接口进行兑换
const response = await userBankExchangeGold({
coinId: selectedCoinData.id,
amount: selectedCoinData.price,
price: selectedCoinData.price,
})
if (response.status === true || response.errorCode === 0) {
showSuccess(t('exchange_success', { price: selectedCoinData.price }))
// 刷新银行余额
await fetchBankBalance()
// 重置选择
selectedCoin.value = null
} else {
// 接口调用失败
const errorMessage = response.message || t('exchange_failed')
showError(errorMessage)
}
} catch (error) {
console.error('Exchange error:', error)
showError(t('exchange_error_occurred'))
}
}
// 如果用户取消,则什么都不做
}
// 方法
const showDetails = () => {
router.push('/information-details')
}
const selectCoin = (coin) => {
// 单选逻辑:如果已经选中相同的金币,则取消选择;否则选中新的金币
if (selectedCoin.value === coin.id) {
selectedCoin.value = null
} else {
selectedCoin.value = coin.id
}
}
// 页面加载时获取银行余额
onMounted(() => {
checkUserRole()
fetchBankBalance()
console.log(
"document.documentElement.getAttribute('dir') === 'rtl'",
document.documentElement.getAttribute('dir') === 'rtl'
)
})
</script>
<style scoped>
* {
color: rgba(0, 0, 0, 0.8);
font-family: 'SF Pro Text';
}
.exchange-gold-coins {
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: flex-end;
margin-bottom: 12px;
}
.details-btn {
background: none;
border: none;
color: #8b5cf6;
font-size: 14px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
border-radius: 4px;
transition: all 0.2s;
}
.details-btn:hover {
background-color: #f3f4f6;
}
.details-btn:active {
background-color: #e5e7eb;
color: #7c3aed;
}
.available-salary {
font-size: 16px;
color: #333;
}
.available-salary .amount {
font-weight: 600;
color: #333;
}
.available-salary .loading-text {
font-weight: 500;
color: #8b5cf6;
font-style: italic;
}
.coin-item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: 2px solid #e5e7eb;
border-radius: 12px;
cursor: pointer;
transition: all 0.2s;
background-color: white;
aspect-ratio: 1/1;
}
.coin-item.selected {
border-color: #8b5cf6;
background-color: #f3f4f6;
}
.coin-item:active {
transform: scale(0.98);
}
/* 兑换按钮 */
.exchange-btn {
width: 70%;
border-radius: 32px;
background: linear-gradient(135deg, #bb92ff 2.82%, #8b45ff 99.15%);
border: none;
padding: 12px;
color: white;
font-weight: 600;
cursor: pointer;
}
.exchange-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
.flipImg {
display: block;
width: 16px;
margin-left: 4px;
}
@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: 24px;
}
}
/* RTL支持 */
[dir='rtl'] .details-btn {
flex-direction: row-reverse;
}
[dir='rtl'] .flipImg {
margin-left: auto;
margin-right: 4px;
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}
</style>