aslan-h5/src/views/ApplyHost/ApplyView.vue

566 lines
14 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="apply-view">
<GeneralHeader
:isHomePage="true"
:showBack="false"
:showLanguageList="true"
:title="t('apply_join_team')"
color="black"
:style="{ borderBottom: '1px solid #E5E5E5' }"
/>
<div class="content" style="padding: 16px 10px">
<!-- 申请说明 -->
<div class="reminder-section" style="margin-bottom: 20px">
<div
class="reminder-title"
style="display: flex; align-items: center; justify-content: space-between"
>
<h3 style="margin-bottom: 4px; font-weight: 600; color: rgba(0, 0, 0, 0.4)">
{{ t('apply_reminder') }}
</h3>
<img
src="../../assets/icon/help.png"
alt=""
style="width: 16px; aspect-ratio: 1/1; cursor: pointer"
@click="showHelp"
/>
</div>
<p style="font-size: 0.9em; font-weight: 510">
{{ t('apply_reminder_detail') }}
</p>
</div>
<!-- 申请表单 -->
<div class="application-form" style="display: flex; gap: 8px">
<!-- 输入框 -->
<div
class="input-group"
style="
width: 100%;
border-radius: 8px;
background: #fff;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
white-space: nowrap;
display: flex;
align-items: center;
padding-inline: 8px;
padding-block: 8px;
"
>
<label style="display: block; font-weight: 600">
{{ t('apply_id') }}
</label>
<input
v-model="agentId"
type="text"
:placeholder="t('enter_agent_id')"
class="agency-input"
style="
width: 100%;
padding: 4px;
border: none;
outline: none;
border-radius: 6px;
box-sizing: border-box;
color: rgba(0, 0, 0, 0.4);
"
/>
</div>
<!-- 申请按钮 -->
<button
v-if="applyRecords.length === 0"
class="apply-btn"
@click="submitApplication"
:disabled="!agentId.trim() || isLoading"
style="
padding: 4px 8px;
color: white;
border: none;
border-radius: 8px;
background: linear-gradient(135deg, #bb92ff 2.82%, #8b45ff 99.15%);
box-shadow: 0 0 4px 0 rgba(198, 112, 255, 0.25);
font-weight: 600;
cursor: pointer;
"
>
{{ t('apply') }}
</button>
</div>
</div>
<!-- 申请记录 -->
<div
v-if="applyRecords.length > 0"
class="records-section"
style="background-color: white; padding: 20px; border-radius: 8px; margin-top: 20px"
>
<h3 style="margin: 0 0 16px 0; font-weight: 600; color: #333">
{{ t('application_records') }}
</h3>
<div class="record-list" style="display: flex; flex-direction: column; gap: 12px">
<div
v-for="record in applyRecords"
:key="record.messageId"
class="record-item"
style="
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
border: 1px solid #e0e0e0;
border-radius: 6px;
"
>
<div class="record-info" style="flex: 1; min-width: 0">
<div class="owner-info" style="margin-bottom: 12px">
<p
class="owner-label"
style="margin: 0 0 4px 0; font-size: 0.9em; font-weight: 600; color: #333"
>
{{ t('my_agent') }}:
</p>
<div
class="owner-profile"
style="display: flex; align-items: center; gap: 8px; text-align: start"
>
<img
:src="record.ownUserProfile.userAvatar || ''"
style="
display: block;
object-fit: cover;
width: 20%;
aspect-ratio: 1/1;
border-radius: 50%;
"
@error="handleAvatarImageError"
/>
<div style="width: 70%">
<p
class="owner-name"
style="
margin: 0 0 2px 0;
font-size: 0.9em;
font-weight: 500;
color: #333;
width: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
"
>
{{ record.ownUserProfile?.userNickname }}
</p>
<p
class="owner-account"
style="margin: 0; font-size: 0.7em; color: #666; text-align: start"
>
ID: {{ record.ownUserProfile?.account }}
</p>
</div>
</div>
</div>
</div>
<div class="record-actions" style="display: flex; align-items: center; gap: 8px">
<span
class="record-status status-pending"
style="
padding: 4px 8px;
border-radius: 4px;
font-size: 0.7em;
font-weight: 600;
background-color: #fef3c7;
color: #d97706;
"
>
{{ t('pending') }}
</span>
<button
class="cancel-btn"
@click="handleCancelApply(record)"
style="
padding: 4px 8px;
background-color: #ef4444;
color: white;
border: none;
border-radius: 4px;
font-size: 0.7em;
cursor: pointer;
"
>
{{ t('cancel') }}
</button>
</div>
</div>
</div>
</div>
<!-- 帮助弹窗 -->
<div
v-if="showHelpModal"
class="help-modal"
@click="closeHelp"
style="
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: 20px;
"
>
<div
class="help-content"
@click.stop
style="
background-color: white;
padding: 20px;
border-radius: 8px;
max-width: 300px;
width: 100%;
"
>
<h3 style="margin: 0 0 12px 0; font-weight: 600; color: #333; text-align: center">
{{ t('application_help') }}
</h3>
<p style="margin: 0 0 16px 0; color: #666; line-height: 1.5">
{{ t('application_help_detail') }}
</p>
<button
class="close-btn"
@click="closeHelp"
style="
width: 100%;
padding: 10px;
background-color: #8b5cf6;
color: white;
border: none;
border-radius: 6px;
font-size: 0.9em;
font-weight: 600;
cursor: pointer;
"
>
{{ t('got_it') }}
</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { showError, showWarning, showInfo, showSuccess } from '@/utils/toast.js'
import { ApiError } from '@/utils/http.js'
import { usePageInitializationWithConfig } from '@/utils/pageConfig.js'
import { setDocumentDirection } from '@/locales/i18n'
import { handleAvatarImageError } from '@/utils/imageHandler.js'
import {
sendTeamApplyJoin,
getWaitApplyRecord,
cancelApply,
searchTeamByAccount,
} from '@/api/wallet.js'
import GeneralHeader from '@/components/GeneralHeader.vue'
const { t, locale } = useI18n()
const router = useRouter()
const agentId = ref('')
const errorMessage = ref('')
const isLoading = ref(false)
const showHelpModal = ref(false)
const applyRecords = ref([])
const searchedTeamInfo = ref(null) // 存储搜索到的团队信息
// 监听语言变化并设置文档方向
watch(locale, (newLocale) => {
setDocumentDirection(newLocale)
})
// 获取申请记录
const fetchApplyRecords = async () => {
try {
const response = await getWaitApplyRecord()
if (response.status && response.body) {
applyRecords.value = [response.body] // 单个记录包装成数组
}
} catch (error) {
console.error('获取申请记录失败:', error)
applyRecords.value = []
showInfo(error.errorMsg)
}
}
// 撤销申请
const handleCancelApply = async (record) => {
try {
const response = await cancelApply(record.messageId)
if (response.status) {
showSuccess(t('application_cancelled'))
// 刷新申请记录
applyRecords.value = [] // 清空记录
await fetchApplyRecords()
} else {
showError(response.message || t('cancellation_failed'))
}
} catch (error) {
console.error('撤销申请失败:', error)
if (error instanceof ApiError) {
if (error.type === 'business') {
switch (error.errorCode) {
case 6404: // 假设的记录不存在错误码
showError(t('application_record_not_found'), t('cancellation_failed'))
break
case 6003: // 假设的已处理错误码
showError(t('application_processed_cannot_cancel'), t('cancellation_failed'))
break
default:
showError(
error.errorMsg || error.message || 'Cancellation failed, please try again',
'Cancellation failed'
)
}
} else {
showError(t('network_error'), t('cancellation_failed'))
}
} else {
showError(t('cancellation_failed_try_again'))
}
}
}
// 搜索团队账户
const searchTeamAccount = async (account) => {
try {
console.debug('🔍 Searching team account:', account)
const response = await searchTeamByAccount(account)
if (response.status && response.body && response.body.teamProfile) {
searchedTeamInfo.value = response.body
console.debug('✅ Team found:', response.body.teamProfile)
return response.body.teamProfile.id // 返回团队ID
} else {
searchedTeamInfo.value = null
showError(response.errorMsg)
}
} catch (error) {
searchedTeamInfo.value = null
showError(error.errorMsg)
}
}
const submitApplication = async () => {
if (!agentId.value.trim()) {
errorMessage.value = 'Please enter agency ID'
return
}
isLoading.value = true
errorMessage.value = ''
try {
const teamId = await searchTeamAccount(agentId.value.trim())
if (!teamId) {
errorMessage.value = 'Account not found or not an agency'
return
}
console.debug('✅ Team ID found:', teamId)
// 第二步使用获取到的teamId提交申请
console.debug('📝 Step 2: Submitting application...')
const applicationData = {
teamId: teamId, // 使用搜索获取的teamId
reason: 'JOIN',
}
const response = await sendTeamApplyJoin(applicationData)
if (response.status) {
showSuccess(t('application_submitted'))
await fetchApplyRecords() // 刷新申请记录
agentId.value = '' // 清空输入
searchedTeamInfo.value = null // 清空搜索结果
} else {
errorMessage.value = response.message || 'Application failed. Please try again.'
}
} catch (error) {
// 根据错误类型和错误码做不同处理
if (error instanceof ApiError) {
if (error.type === 'business') {
showError('business error!' + error.errorMsg)
} else if (error.type === 'http') {
// HTTP错误
if (error.status === 406) {
showWarning(error.errorMsg)
} else {
showError(error.message || 'Unknown error, please try again')
}
} else {
// 非 ApiError 类型的错误
showError('Unknown error, please try again')
}
}
} finally {
isLoading.value = false
}
}
const showHelp = () => {
showHelpModal.value = true
}
const closeHelp = () => {
showHelpModal.value = false
}
// 获取状态文本
const getStatusText = (status) => {
const statusMap = {
WAIT: 'Pending',
AGREE: 'Approved',
REJECT: 'Rejected',
}
return statusMap[status] || status
}
// 获取状态样式
const getStatusClass = (status) => {
const classMap = {
WAIT: 'status-pending',
AGREE: 'status-approved',
REJECT: 'status-rejected',
}
return classMap[status] || ''
}
// 页面加载时获取申请记录
usePageInitializationWithConfig('APPLY', {
forceRefresh: true, // 强制刷新
onDataLoaded: () => {
fetchApplyRecords()
},
})
</script>
<style scoped>
* {
color: rgba(0, 0, 0, 0.8);
font-family: 'SF Pro Text';
}
.apply-view {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
width: 100vw;
min-height: 100vh;
background-color: #ffffff;
background-image: url(../../assets/images/secondBg.png);
}
/* RTL布局支持 */
[dir='rtl'] .application-form {
/* flex-direction: row-reverse; */
}
[dir='rtl'] .record-item {
/* flex-direction: row-reverse; */
}
[dir='rtl'] .owner-profile {
/* flex-direction: row-reverse; */
text-align: right;
}
[dir='rtl'] .reminder-title {
/* flex-direction: row-reverse; */
}
[dir='rtl'] .record-info {
text-align: right;
}
[dir='rtl'] .team-info,
[dir='rtl'] .owner-info {
text-align: right;
}
[dir='rtl'] .cancel-btn {
margin-left: 0;
margin-right: 8px;
}
[dir='rtl'] .record-status {
margin-left: 0;
margin-right: 8px;
}
[dir='rtl'] .input-group label {
margin-right: 0;
margin-left: 8px;
}
[dir='rtl'] .help-content {
text-align: right;
}
[dir='rtl'] .records-section {
text-align: right;
}
[dir='rtl'] .apply-btn {
margin-left: 0;
margin-right: 8px;
}
[dir='rtl'] .team-label,
[dir='rtl'] .owner-label {
text-align: right;
}
[dir='rtl'] .team-id,
[dir='rtl'] .team-country {
text-align: right;
}
[dir='rtl'] .owner-name,
[dir='rtl'] .owner-account {
text-align: right;
}
@media screen and (max-width: 360px) {
* {
font-size: 10px;
}
}
@media screen and (min-width: 360px) {
* {
font-size: 14px;
}
}
@media screen and (min-width: 768px) {
* {
font-size: 24px;
}
}
@media screen and (min-width: 1024px) {
* {
font-size: 32px;
}
}
</style>