feat(主播转账页、选择转账对象页): 使用新的头部组件,对接语言切换功能并调整布局

This commit is contained in:
hzj 2025-10-30 11:46:35 +08:00
parent 3e5fd32bea
commit ba657dcbbc
5 changed files with 167 additions and 39 deletions

View File

@ -148,6 +148,22 @@
"exchange_failed": "فشل التبادل، يرجى المحاولة لاحقًا.",
"exchange_error_occurred": "حدث خطأ أثناء عملية التبادل. يرجى المحاولة لاحقًا.",
"please_select_amount": "يرجى اختيار المبلغ أولاً",
"please_select_payee": "يرجى اختيار المستلم أولاً",
"enter_payment_password": "يرجى إدخال كلمة مرور الدفع الخاصة بك:",
"payment_password_required": "كلمة مرور الدفع مطلوبة",
"transfer_success": "تم تحويل ${price} دولارًا إلى ${name} بنجاح",
"transfer_failed": "فشل تحويل ${price} دولارًا",
"payment_password_error": "خطأ في كلمة مرور الدفع",
"transfer_error": "فشل التحويل، يرجى المحاولة لاحقًا",
"search_payee": "البحث عن المستلم",
"checking_permissions": "جارٍ التحقق من الأذونات",
"searching": "جارٍ البحث",
"confirm_transfer_to_user": "هل أنت متأكد من أنك تريد تحويل العملات إلى هذا المستخدم؟",
"user_not_found_or_search_failed": "لم يتم العثور على المستخدم أو فشل البحث",
"add_recharge_agent": "إضافة وكيل إعادة الشحن",
"application_record_not_found": "Application record not found",
"application_processed_cannot_cancel": "Application has been processed and cannot be canceled",
"cancellation_failed_try_again": "Cancellation failed, please try again",

View File

@ -148,6 +148,22 @@
"exchange_failed": "Exchange failed, please try again later.",
"exchange_error_occurred": "An error occurred during the exchange process. Please try again later.",
"please_select_amount": "Please select an amount first",
"please_select_payee": "Please select a payee first",
"enter_payment_password": "Please enter your payment password:",
"payment_password_required": "Payment password is required",
"transfer_success": "Successfully transferred $${price} to ${name}",
"transfer_failed": "Failed to transfer $${price}",
"payment_password_error": "Payment password error",
"transfer_error": "Transfer failed, please try again later",
"search_payee": "Search Payee",
"checking_permissions": "Checking permissions",
"searching": "Searching",
"confirm_transfer_to_user": "Are you sure you want to transfer coins to this user?",
"user_not_found_or_search_failed": "User not found or search failed",
"add_recharge_agent": "Add recharge agent",
"application_record_not_found": "Application record not found",
"application_processed_cannot_cancel": "Application has been processed and cannot be canceled",
"cancellation_failed_try_again": "Cancellation failed, please try again",

View File

@ -148,6 +148,22 @@
"exchange_failed": "兑换失败,请稍后重试。",
"exchange_error_occurred": "兑换过程中发生错误,请稍后重试。",
"please_select_amount": "请先选择金额",
"please_select_payee": "请选择收款人",
"enter_payment_password": "请输入支付密码:",
"payment_password_required": "需要支付密码",
"transfer_success": "成功转账${price}美元给${name}",
"transfer_failed": "转账${price}美元失败",
"payment_password_error": "支付密码错误",
"transfer_error": "转账失败,请稍后重试",
"search_payee": "搜索收款人",
"checking_permissions": "正在检查权限",
"searching": "搜索中",
"confirm_transfer_to_user": "您确定要转账给这个用户吗?",
"user_not_found_or_search_failed": "未找到用户或搜索失败",
"add_recharge_agent": "添加充值代理",
"application_record_not_found": "申请记录未找到",
"application_processed_cannot_cancel": "申请已被处理,无法取消",
"cancellation_failed_try_again": "取消失败,请重试",

View File

@ -1,6 +1,13 @@
<template>
<div class="search-payee gradient-background-circles">
<MobileHeader :title="pageTitle" />
<!-- 使用 GeneralHeader 替换 MobileHeader -->
<GeneralHeader
:title="pageTitle"
:showBack="true"
:showLanguageList="true"
color="black"
style="box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); z-index: 999"
/>
<div class="content">
<!-- 搜索框 -->
@ -23,7 +30,7 @@
<input
v-model="searchQuery"
type="text"
placeholder="Search by ID or name"
:placeholder="t('search_user')"
class="search-input"
style="
flex: 1;
@ -48,7 +55,7 @@
"
@click="performSearch"
>
Search
{{ t('search') }}
</button>
<button
v-else
@ -56,14 +63,14 @@
style="border: none; font-weight: 600; background-color: transparent"
@click="clearSearch"
>
Cancel
{{ t('cancel') }}
</button>
</div>
<!-- 角色检查加载状态 -->
<div v-if="isLoadingRole" class="loading-state">
<div class="loading-spinner"></div>
<p>Checking permissions...</p>
<p>{{ t('checking_permissions') }}...</p>
</div>
<!-- 搜索结果 -->
@ -104,10 +111,14 @@
</div>
<div class="user-details">
<h4>{{ user.name }}</h4>
<p>ID: {{ user.account || user.id }}</p>
<div style="display: flex; align-items: center">
<p>ID</p>
<p>:</p>
<p>{{ user.account || user.id }}</p>
</div>
</div>
</div>
<button class="add-btn" @click="selectUser(user)">Add</button>
<button class="add-btn" @click="selectUser(user)">{{ t('add') }}</button>
</div>
<div style="display: flex; gap: 2px; margin-top: 8px; margin-left: 50px; height: 20px">
<img
@ -138,7 +149,7 @@
<!-- 加载状态 -->
<div v-if="isSearching" class="loading-state">
<div class="loading-spinner"></div>
<p>Searching...</p>
<p>{{ t('searching') }}...</p>
</div>
</div>
@ -166,9 +177,9 @@
gap: 8px;
"
>
<div style="color: rgba(0, 0, 0, 0.8); font-weight: 800">Tips</div>
<div style="color: rgba(0, 0, 0, 0.8); font-weight: 800">{{ t('tips') }}</div>
<div style="color: rgba(0, 0, 0, 0.5); font-weight: 600; text-align: center">
Are you sure you want to transfer coins to this user?
{{ t('confirm_transfer_to_user') }}
</div>
<div
style="width: 100%; display: flex; justify-content: space-around; align-items: center"
@ -183,7 +194,7 @@
"
@click="Cancel"
>
Cancel
{{ t('cancel') }}
</div>
<div
style="
@ -199,7 +210,7 @@
"
@click="Confirm"
>
Confirm
{{ t('confirm') }}
</div>
</div>
</div>
@ -211,17 +222,25 @@
<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import MobileHeader from '../components/MobileHeader.vue'
import { useI18n } from 'vue-i18n'
import GeneralHeader from '@/components/GeneralHeader.vue'
import { userBankCheckTransfer, userBankSearchUserProfile } from '../api/wallet.js'
import { setSelectedPayee } from '../utils/payeeStore.js'
import { showError } from '@/utils/toast.js'
import maskLayer from '@/components/MaskLayer.vue'
import { setDocumentDirection } from '@/locales/i18n'
const { t, locale } = useI18n()
const router = useRouter()
const route = useRoute()
//
locale.value && setDocumentDirection(locale.value)
const fromLottery = computed(() => route.query?.from == 'lottery')
const pageTitle = ref(route.query?.from == 'lottery' ? 'Add recharge agent' : 'Search Payee')
const pageTitle = computed(() =>
route.query?.from == 'lottery' ? t('add_recharge_agent') : t('search_payee')
)
const searchQuery = ref('')
const searchResults = ref([])
@ -399,7 +418,7 @@ const performSearch = async () => {
} catch (error) {
console.error('Search failed:', error)
searchResults.value = []
showError('User not found or search failed')
showError(t('user_not_found_or_search_failed'))
} finally {
isSearching.value = false
hasSearched.value = true
@ -556,6 +575,7 @@ onMounted(() => {
font-size: 14px;
font-weight: 500;
padding: 2px 8px;
cursor: pointer;
}
/* 空状态和提示 */
@ -644,4 +664,14 @@ onMounted(() => {
font-size: 13px;
}
}
/* RTL支持 */
[dir='rtl'] .user-details {
text-align: right;
}
[dir='rtl'] .add-btn {
margin-left: 0;
margin-right: 12px;
}
</style>

View File

@ -1,6 +1,13 @@
<template>
<div class="transfer gradient-background-circles">
<MobileHeader title="Transfer" />
<!-- 使用 GeneralHeader 替换 MobileHeader -->
<GeneralHeader
:title="t('transfer')"
:showBack="true"
:showLanguageList="true"
color="black"
style="box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); z-index: 999"
/>
<div class="content">
<!-- 信息部分 -->
@ -13,10 +20,16 @@
margin-bottom: 10px;
"
>
<div style="font-weight: 600">Information</div>
<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)">Details</div>
<img src="../assets/icon/arrow.png" alt="" width="16px" />
<div style="color: rgba(0, 0, 0, 0.4)">{{ t('details') }}</div>
<img
src="../assets/icon/arrow.png"
alt=""
width="16px"
style="margin-left: 4px"
class="rtl-flip"
/>
</div>
</div>
<div
@ -28,8 +41,8 @@
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
"
>
<span style="font-weight: 510; font-size: 1em">Available salary: </span>
<span v-if="loading" class="loading-text">Loading...</span>
<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>
@ -38,7 +51,7 @@
<!-- 转账对象 -->
<div>
<div style="font-weight: 600; margin-bottom: 10px">Transfer to others</div>
<div style="font-weight: 600; margin-bottom: 10px">{{ t('transfer_to_others') }}</div>
<div
style="
background-color: white;
@ -91,8 +104,12 @@
<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 style="margin: 0; display: flex; align-items: center">
<p style="font-size: 14px; color: #666">ID</p>
<p style="font-size: 14px; color: #666">:</p>
<p style="font-size: 14px; color: #666">
{{ selectedPayee.account || selectedPayee.id }}
</p>
</div>
</div>
<!-- 换人按钮 -->
@ -107,7 +124,7 @@
"
@click="searchPayee"
>
Change
{{ t('change') }}
</button>
</div>
<!-- 身份 -->
@ -140,7 +157,7 @@
"
@click="searchPayee"
>
Search
{{ t('search') }}
</div>
</div>
</div>
@ -178,7 +195,7 @@
@click="transfer"
:disabled="!selectedCoin || !selectedPayee.id"
>
Transfer
{{ t('transfer') }}
</button>
</div>
</div>
@ -188,14 +205,20 @@
<script setup>
import { ref, reactive, onMounted, onUnmounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import MobileHeader from '../components/MobileHeader.vue'
import GeneralHeader from '@/components/GeneralHeader.vue'
import { getBankBalance, userBankTransfer } from '../api/wallet.js'
import { getSelectedPayee, clearSelectedPayee } from '../utils/payeeStore.js'
import { showError, showSuccess } 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)
@ -248,7 +271,7 @@ const fetchBankBalance = async () => {
const response = await getBankBalance()
if (response.status && typeof response.body === 'number') {
// 2
// 2
availableSalary.value = response.body.toFixed(2)
} else {
availableSalary.value = '0.00'
@ -257,7 +280,7 @@ const fetchBankBalance = async () => {
console.error('Failed to fetch bank balance:', error)
availableSalary.value = '0.00'
//
showError('Failed to load balance. Please try again.')
showError(t('failed_to_load_balance'))
} finally {
loading.value = false
}
@ -298,21 +321,21 @@ const selectCoin = (coin) => {
const transfer = async () => {
if (!selectedCoin.value) {
showError('Please select an amount first')
showError(t('please_select_amount'))
return
}
if (!selectedPayee.id) {
showError('Please select a payee first')
showError(t('please_select_payee'))
return
}
const selectedCoinData = coinOptions.find((coin) => coin.id === selectedCoin.value)
//
// const password = prompt('Please enter your payment password:')
// const password = prompt(t('enter_payment_password'))
// if (!password) {
// showError('Payment password is required')
// showError(t('payment_password_required'))
// return
// }
@ -328,9 +351,14 @@ const transfer = async () => {
//
const response = await userBankTransfer(transferData)
if (response.status) {
showSuccess(`Successfully transferred $${selectedCoinData.price} to ${selectedPayee.name}`)
showSuccess(
t('transfer_success', {
price: selectedCoinData.price,
name: selectedPayee.name,
})
)
} else {
showError(`Failed to transfer $${selectedCoinData.price}`)
showError(t('transfer_failed', { price: selectedCoinData.price }))
}
//
@ -343,11 +371,11 @@ const transfer = async () => {
console.error('Transfer failed:', error)
if (error.errorCode === 5000) {
showError('Insufficient balance')
showError(t('insufficient_balance'))
} else if (error.errorCode === 5010 || error.errorCode === 5009) {
showError('Payment password error')
showError(t('payment_password_error'))
} else {
showError('Transfer failed, please try again later')
showError(t('transfer_error'))
}
}
}
@ -596,16 +624,27 @@ onUnmounted(() => {
border: none;
border-radius: 12px;
font-weight: 600;
cursor: pointer;
}
.transfer-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
.transfer-btn:not(:disabled):active {
background: linear-gradient(135deg, #bb92ff 2.82%, #8b45ff 99.15%);
}
.rtl-flip {
width: 16px;
height: 16px;
}
[dir='rtl'] .rtl-flip {
transform: scaleX(-1);
}
@media screen and (max-width: 360px) {
* {
font-size: 10px;
@ -623,4 +662,15 @@ onUnmounted(() => {
font-size: 24px;
}
}
/* RTL支持 */
[dir='rtl'] .payee-avatar {
margin-right: 0;
margin-left: 12px;
}
[dir='rtl'] .change-btn {
margin-left: 0;
margin-right: 12px;
}
</style>