aslan-h5/src/views/SearchPayeeView.vue
2025-08-26 10:20:42 +00:00

664 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="search-payee gradient-background-circles">
<MobileHeader title="Search Payee" />
<div class="content">
<!-- 搜索框 -->
<div class="search-section">
<!-- 用户类型选择 -->
<div v-if="!isLoadingRole && availableUserTypes.length > 1" class="user-type-selector">
<label>搜索类型:</label>
<div class="type-buttons">
<button
v-for="type in availableUserTypes"
:key="type.value"
@click="selectedUserType = type.value"
:class="['type-btn', { active: selectedUserType === type.value }]"
>
{{ type.label }}
</button>
</div>
</div>
<div class="search-box">
<input
v-model="searchQuery"
type="text"
placeholder="Search by ID or name"
class="search-input"
@keyup.enter="performSearch"
/>
<button
v-if="searchQuery"
@click="clearSearch"
class="clear-btn"
>
×
</button>
<button class="search-btn" @click="performSearch" :disabled="!selectedUserType">
Search
</button>
</div>
</div>
<!-- 角色检查加载状态 -->
<div v-if="isLoadingRole" class="loading-state">
<div class="loading-spinner"></div>
<p>Checking permissions...</p>
</div>
<!-- 搜索结果 -->
<div v-else-if="searchResults.length > 0" class="results-section">
<div
v-for="user in searchResults"
:key="user.id"
class="user-item"
>
<div class="user-info">
<div class="user-avatar">
<img v-if="user.avatar" :src="user.avatar" :alt="user.name" />
<span v-else>{{ user.name.charAt(0) }}</span>
</div>
<div class="user-details">
<h4>{{ user.name }}</h4>
<p>ID: {{ user.account || user.id }}</p>
<div class="user-tags">
<span v-if="user.isHost" class="tag host-tag">👑 Host</span>
<span v-if="user.isAgency" class="tag agency-tag">🏢 Agency</span>
<span v-if="user.hasSalary" class="tag salary-tag">💰 Salary</span>
</div>
</div>
</div>
<button class="add-btn" @click="selectUser(user)">
Add
</button>
</div>
</div>
<!-- 空状态 -->
<div v-else-if="searchQuery && !isSearching && !isLoadingRole" class="empty-state">
<div class="empty-icon">🔍</div>
<p>No users found</p>
<span>Try searching with a different keyword</span>
</div>
<!-- 搜索提示 -->
<div v-else-if="!searchQuery && !isLoadingRole" class="search-hint">
<div class="hint-icon">👥</div>
<p>Search for users</p>
<span v-if="userRole">You can search for: {{ getRoleDescription(userRole) }}</span>
<span v-else>Enter user ID or name to find payees</span>
</div>
<!-- 加载状态 -->
<div v-if="isSearching" class="loading-state">
<div class="loading-spinner"></div>
<p>Searching...</p>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import MobileHeader from '../components/MobileHeader.vue'
import { userBankCheckTransfer, userBankSearchUserProfile } from '../api/wallet.js'
import { setSelectedPayee } from '../utils/payeeStore.js'
import {showError} from "@/utils/toast.js";
const router = useRouter()
const searchQuery = ref('')
const searchResults = ref([])
const isSearching = ref(false)
const userRole = ref('')
const isLoadingRole = ref(false)
const selectedUserType = ref('')
const showTypeSelector = ref(false)
// 模拟用户数据 - 根据角色显示不同的用户
const allUsers = {
'TEAM_OWN': [
{
id: '1234567890',
name: 'Agent User',
type: 'AGENT',
isHost: false,
isAgency: true,
hasSalary: false
},
{
id: '0987654321',
name: 'BD Manager',
type: 'BD',
isHost: false,
isAgency: true,
hasSalary: true
},
{
id: '1122334455',
name: 'Host Anchor',
type: 'ANCHOR',
isHost: true,
isAgency: false,
hasSalary: true
}
],
'TEAM_MEMBER': [
{
id: '1234567890',
name: 'Team Agent',
type: 'AGENT',
isHost: false,
isAgency: true,
hasSalary: false
},
{
id: '0987654321',
name: 'BD Manager',
type: 'BD',
isHost: false,
isAgency: true,
hasSalary: true
}
],
'BD': [
{
id: '1234567890',
name: 'Agent User',
type: 'AGENT',
isHost: false,
isAgency: true,
hasSalary: false
}
],
'default': [
{
id: '1234567890',
name: 'Default User',
type: 'USER',
isHost: false,
isAgency: false,
hasSalary: false
}
]
}
// 根据角色获取可搜索的用户列表
const availableUsers = computed(() => {
return allUsers[userRole.value] || allUsers['default']
})
// 根据用户角色获取可选择的用户类型
const availableUserTypes = computed(() => {
switch (userRole.value) {
case 'TEAM_OWN':
return [
{ label: 'Agent', value: 'AGENT' },
{ label: 'BD Manager', value: 'BD' },
{ label: 'Anchor', value: 'ANCHOR' }
]
case 'TEAM_MEMBER':
return [
{ label: 'Agent', value: 'AGENT' },
{ label: 'BD Manager', value: 'BD' }
]
case 'BD':
return [
{ label: 'Agent', value: 'AGENT' }
]
default:
return [
{ label: 'User', value: 'USER' }
]
}
})
// 检查用户转账角色
const checkUserRole = async () => {
isLoadingRole.value = true
try {
const response = await userBankCheckTransfer()
if (response.status && response.body) {
// 根据接口返回的role设置用户角色
userRole.value = response.body.role || ''
// 设置默认的用户类型
if (userRole.value === 'TEAM_OWN') {
selectedUserType.value = 'ANCHOR' // TEAM_OWN 默认选择 ANCHOR
} else {
selectedUserType.value = 'AGENT' // 其他角色默认选择 AGENT
}
} else {
userRole.value = ''
selectedUserType.value = 'USER'
}
} catch (error) {
console.error('Failed to check user role:', error)
userRole.value = ''
selectedUserType.value = 'USER'
} finally {
isLoadingRole.value = false
}
}
// 执行搜索
const performSearch = async () => {
if (!searchQuery.value.trim()) {
searchResults.value = []
return
}
if (!selectedUserType.value) {
showError('Please select user type first')
return
}
isSearching.value = true
try {
const response = await userBankSearchUserProfile(selectedUserType.value, searchQuery.value.trim())
if (response.status && response.body) {
// 根据新的API返回数据结构进行映射
const user = {
id: response.body.id,
account: response.body.account,
name: response.body.userNickname,
avatar: response.body.userAvatar,
type: selectedUserType.value,
sex: response.body.userSex,
age: response.body.age,
countryName: response.body.countryName,
countryCode: response.body.countryCode,
accountStatus: response.body.accountStatus,
// 根据类型设置标签
isHost: selectedUserType.value === 'ANCHOR',
isAgency: ['AGENT', 'BD'].includes(selectedUserType.value),
hasSalary: ['ANCHOR', 'BD'].includes(selectedUserType.value)
}
searchResults.value = [user]
} else {
searchResults.value = []
}
} catch (error) {
console.error('Search failed:', error)
searchResults.value = []
showError('User not found or search failed')
} finally {
isSearching.value = false
}
}
// 清除搜索
const clearSearch = () => {
searchQuery.value = ''
searchResults.value = []
}
// 选择用户
const selectUser = (user) => {
// 将选中的用户信息保存到全局状态
setSelectedPayee({
id: user.id,
account: user.account,
name: user.name,
avatar: user.avatar,
type: user.type,
isHost: user.isHost,
isAgency: user.isAgency,
hasSalary: user.hasSalary
})
// 返回到 Transfer 页面
router.push('/transfer')
}
// 获取角色描述
const getRoleDescription = (role) => {
switch (role) {
case 'TEAM_OWN':
return 'Agents, BD Managers, Anchors'
case 'TEAM_MEMBER':
return 'Agents, BD Managers'
case 'BD':
return 'Agents'
default:
return 'Users'
}
}
// 页面加载时检查用户角色
onMounted(() => {
checkUserRole()
})
</script>
<style scoped>
.search-payee {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
}
.content {
padding: 16px;
position: relative;
z-index: 2;
}
/* 搜索部分 */
.search-section {
margin-bottom: 20px;
}
/* 用户类型选择器 */
.user-type-selector {
margin-bottom: 12px;
}
.user-type-selector label {
display: block;
font-size: 14px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.type-buttons {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.type-btn {
padding: 6px 12px;
border: 1px solid #ddd;
border-radius: 16px;
background-color: white;
color: #666;
font-size: 12px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.type-btn.active {
background-color: #8B5CF6;
color: white;
border-color: #8B5CF6;
}
.type-btn:active {
transform: scale(0.98);
}
.search-box {
display: flex;
align-items: center;
background-color: white;
border-radius: 12px;
padding: 4px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
width: 100%;
max-width: 100%;
box-sizing: border-box;
gap: 4px; /* 添加元素间距 */
}
.search-input {
flex: 1;
border: none;
padding: 12px 16px;
font-size: 16px;
background: transparent;
outline: none;
min-width: 0; /* 防止flex item超出容器 */
}
.search-input::placeholder {
color: #999;
}
.clear-btn {
background: none;
border: none;
padding: 8px;
color: #999;
font-size: 18px;
cursor: pointer;
flex-shrink: 0;
}
.search-btn {
background-color: #8B5CF6;
color: white;
border: none;
padding: 10px 16px;
border-radius: 8px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
flex-shrink: 0; /* 防止按钮被压缩 */
white-space: nowrap; /* 防止文本换行 */
}
.search-btn:active {
background-color: #7C3AED;
}
.search-btn:disabled {
background-color: #ccc;
cursor: not-allowed;
}
/* 搜索结果 */
.results-section {
margin-bottom: 20px;
}
.user-item {
display: flex;
align-items: center;
justify-content: space-between;
background-color: white;
padding: 16px;
border-radius: 12px;
margin-bottom: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
cursor: pointer;
transition: background-color 0.2s;
}
.user-item:active {
background-color: #f9f9f9;
}
.user-info {
display: flex;
align-items: center;
flex: 1;
}
.user-avatar {
width: 50px;
height: 50px;
border-radius: 25px;
background-color: #8B5CF6;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 18px;
font-weight: 600;
margin-right: 12px;
overflow: hidden;
}
.user-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.user-details h4 {
margin: 0 0 4px 0;
font-size: 16px;
font-weight: 600;
color: #333;
}
.user-details p {
margin: 0 0 8px 0;
font-size: 14px;
color: #666;
}
.user-tags {
display: flex;
gap: 4px;
flex-wrap: wrap;
}
.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;
}
.add-btn {
background-color: #10B981;
color: white;
border: none;
padding: 8px 16px;
border-radius: 8px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
}
.add-btn:active {
background-color: #059669;
}
/* 空状态和提示 */
.empty-state, .search-hint {
text-align: center;
padding: 40px 20px;
color: #666;
}
.empty-icon, .hint-icon {
font-size: 48px;
margin-bottom: 16px;
}
.empty-state p, .search-hint p {
font-size: 18px;
font-weight: 600;
margin: 0 0 8px 0;
color: #333;
}
.empty-state span, .search-hint span {
font-size: 14px;
color: #666;
}
/* 加载状态 */
.loading-state {
text-align: center;
padding: 40px 20px;
}
.loading-spinner {
width: 32px;
height: 32px;
border: 3px solid #f3f3f3;
border-top: 3px solid #8B5CF6;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 16px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-state p {
margin: 0;
color: #666;
font-size: 14px;
}
/* 响应式设计 */
@media (max-width: 480px) {
.search-box {
padding: 3px;
gap: 4px;
}
.search-input {
padding: 10px 12px;
font-size: 14px;
}
.search-btn {
padding: 10px 12px;
font-size: 13px;
min-width: 60px;
}
.clear-btn {
padding: 6px;
font-size: 16px;
}
.type-btn {
padding: 5px 10px;
font-size: 11px;
}
.user-item {
padding: 12px;
}
.user-avatar {
width: 44px;
height: 44px;
font-size: 16px;
}
.user-details h4 {
font-size: 15px;
}
.user-details p {
font-size: 13px;
}
.add-btn {
padding: 6px 12px;
font-size: 13px;
}
}
</style>