aslan-h5/src/views/ApplyView.vue
2025-09-05 22:08:25 +08:00

563 lines
12 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">
<MobileHeader title="Apply to join the team" :showBack="false" :showHelp="true" @help="showHelp" />
<div class="content">
<!-- 申请说明 -->
<div class="reminder-section">
<h3>Reminder before application:</h3>
<p>Enter the ID of your agent, and you will become the host after the host apply is approved. After becoming an host, your income will be entrusted to your agent</p>
</div>
<!-- 申请表单 -->
<div class="application-form">
<div class="input-group">
<label>Apply ID:</label>
<input
v-model="agentId"
type="text"
placeholder="Please enter the agent ID"
class="agent-input"
/>
</div>
<!-- 错误提示 -->
<div v-if="errorMessage" class="error-message">
{{ errorMessage }}
</div>
<!-- 申请按钮 -->
<button
v-if="applyRecords.length === 0"
class="apply-btn"
@click="submitApplication"
:disabled="!agentId.trim() || isLoading"
>
{{ isLoading ? 'Applying...' : 'Apply' }}
</button>
<!-- 已有申请记录时的提示 -->
<div v-else class="existing-application">
<p>You already have an application in process, please wait for the review result.</p>
</div>
</div>
</div>
<!-- 申请记录 -->
<div v-if="applyRecords.length > 0" class="records-section">
<h3>Application Records</h3>
<div class="record-list">
<div v-for="record in applyRecords" :key="record.messageId" class="record-item">
<div class="record-info">
<div class="team-info">
<p class="team-label">Apply to join team:</p>
<p class="team-id">Team ID: {{ record.teamProfile?.id }}</p>
<p class="team-country">{{ record.teamProfile?.country?.countryName }} ({{ record.teamProfile?.country?.countryCode }})</p>
</div>
<div class="owner-info">
<p class="owner-label">Team owner:</p>
<div class="owner-profile">
<img v-if="record.ownUserProfile?.userAvatar" :src="record.ownUserProfile.userAvatar" class="owner-avatar" />
<div>
<p class="owner-name">{{ record.ownUserProfile?.userNickname }}</p>
<p class="owner-account">ID: {{ record.ownUserProfile?.account }}</p>
</div>
</div>
</div>
</div>
<div class="record-actions">
<span class="record-status status-pending">
Pending
</span>
<button
class="cancel-btn"
@click="handleCancelApply(record)"
>
Cancel
</button>
</div>
</div>
</div>
</div>
<!-- 帮助弹窗 -->
<div v-if="showHelpModal" class="help-modal" @click="closeHelp">
<div class="help-content" @click.stop>
<h3>Application Help</h3>
<p>Enter your agent's ID and click Apply to submit your application.</p>
<button class="close-btn" @click="closeHelp">Got it</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import MobileHeader from '../components/MobileHeader.vue'
import { sendTeamApplyJoin, getWaitApplyRecord, cancelApply, searchTeamByAccount } from '../api/wallet.js'
import {showError, showWarning, showInfo, showSuccess} from "@/utils/toast.js";
import { ApiError } from '@/utils/http.js';
import {usePageInitializationWithConfig} from "@/utils/pageConfig.js";
const router = useRouter()
const agentId = ref('')
const errorMessage = ref('')
const isLoading = ref(false)
const showHelpModal = ref(false)
const applyRecords = ref([])
const searchedTeamInfo = ref(null) // 存储搜索到的团队信息
// 获取申请记录
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('Successfully canceled')
// 刷新申请记录
applyRecords.value = [] // 清空记录
await fetchApplyRecords()
} else {
showError(response.message || 'Cancellation failed, please try again')
}
} catch (error) {
console.error('撤销申请失败:', error)
if (error instanceof ApiError) {
if (error.type === 'business') {
switch (error.errorCode) {
case 6404: // 假设的记录不存在错误码
showError('Application record not found', 'Cancellation failed')
break
case 6003: // 假设的已处理错误码
showError('Application has been processed and cannot be canceled', 'Cancellation failed')
break
default:
showError(error.errorMsg || error.message || 'Cancellation failed, please try again', 'Cancellation failed')
}
} else {
showError('Network error, please check your connection and try again', 'Cancellation failed')
}
} else {
showError('Cancellation failed, please 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 agent 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 agent'
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('Application submitted successfully!')
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', {
needsBankBalance: false,
needsWorkStatistics: false,
needsTeamInfo: false,
needsRouteGuard: true,
onDataLoaded: () => {
fetchApplyRecords()
}
})
</script>
<style scoped>
.apply-view {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
min-height: 100vh;
background-color: #f5f5f5;
}
.content {
padding: 20px 16px;
}
.reminder-section {
background-color: white;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.reminder-section h3 {
margin: 0 0 12px 0;
font-size: 16px;
font-weight: 600;
color: #333;
}
.reminder-section p {
margin: 0;
font-size: 14px;
line-height: 1.5;
color: #666;
}
.application-form {
background-color: white;
padding: 20px;
border-radius: 8px;
}
.input-group {
margin-bottom: 20px;
}
.input-group label {
display: block;
font-size: 16px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.agent-input {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 16px;
box-sizing: border-box;
}
.agent-input:focus {
outline: none;
border-color: #8B5CF6;
}
.error-message {
margin-bottom: 16px;
padding: 12px;
background-color: #FEE2E2;
border: 1px solid #FECACA;
border-radius: 6px;
color: #DC2626;
font-size: 14px;
}
.apply-btn {
width: 100%;
padding: 14px;
background-color: #8B5CF6;
color: white;
border: none;
border-radius: 6px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
}
.apply-btn:disabled {
background-color: #D1D5DB;
cursor: not-allowed;
}
.help-modal {
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;
}
.help-content {
background-color: white;
padding: 20px;
border-radius: 8px;
max-width: 300px;
width: 100%;
}
.help-content h3 {
margin: 0 0 12px 0;
font-size: 18px;
font-weight: 600;
color: #333;
text-align: center;
}
.help-content p {
margin: 0 0 16px 0;
color: #666;
line-height: 1.5;
}
.close-btn {
width: 100%;
padding: 10px;
background-color: #8B5CF6;
color: white;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
}
.records-section {
background-color: white;
padding: 20px;
border-radius: 8px;
margin-top: 20px;
}
.records-section h3 {
margin: 0 0 16px 0;
font-size: 16px;
font-weight: 600;
color: #333;
}
.record-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.record-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
border: 1px solid #e0e0e0;
border-radius: 6px;
}
.record-info {
flex: 1;
}
.record-actions {
display: flex;
align-items: center;
gap: 8px;
}
.cancel-btn {
padding: 4px 8px;
background-color: #EF4444;
color: white;
border: none;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
}
.record-reason {
margin: 0 0 4px 0;
font-size: 14px;
color: #333;
}
.record-time {
margin: 0;
font-size: 12px;
color: #666;
}
.team-info, .owner-info {
margin-bottom: 12px;
}
.team-label, .owner-label {
margin: 0 0 4px 0;
font-size: 14px;
font-weight: 600;
color: #333;
}
.team-id, .team-country {
margin: 0 0 2px 0;
font-size: 12px;
color: #666;
}
.owner-profile {
display: flex;
align-items: center;
gap: 8px;
}
.owner-avatar {
width: 32px;
height: 32px;
border-radius: 16px;
object-fit: cover;
}
.owner-name {
margin: 0 0 2px 0;
font-size: 14px;
font-weight: 500;
color: #333;
}
.owner-account {
margin: 0;
font-size: 12px;
color: #666;
}
.record-status {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 600;
}
.status-pending {
background-color: #FEF3C7;
color: #D97706;
}
.status-approved {
background-color: #D1FAE5;
color: #059669;
}
.status-rejected {
background-color: #FEE2E2;
color: #DC2626;
}
.existing-application {
padding: 12px;
background-color: #FEF3C7;
border: 1px solid #F59E0B;
border-radius: 6px;
text-align: center;
}
.existing-application p {
margin: 0;
font-size: 14px;
color: #D97706;
}
</style>