aslan-h5/src/views/TeamMemberView.vue
2025-08-21 16:30:38 +08:00

447 lines
9.0 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="team-member gradient-background-circles">
<MobileHeader :title="`Team Member (${members.length})`" />
<div class="content">
<!-- 加载状态 -->
<div v-if="loading" class="loading-container">
<div class="loading-spinner"></div>
<p>Loading members...</p>
</div>
<!-- 成员列表 -->
<div v-else-if="members.length > 0" class="member-list">
<div
v-for="member in members"
:key="member.id"
class="member-item"
>
<div class="member-info">
<div class="member-avatar">
<img
v-if="member.userProfile.userAvatar"
:src="member.userProfile.userAvatar"
:alt="member.userProfile.userNickname"
@error="handleImageError"
/>
<span v-else>{{ member.userProfile.userNickname.charAt(0) }}</span>
</div>
<div class="member-details">
<h4>{{ member.userProfile.userNickname }}</h4>
<p>ID: {{ formatAccount(member.userProfile) }}</p>
</div>
</div>
<!-- <button
class="delete-btn"
@click="handleDeleteMember(member)"
:disabled="deletingMemberId === member.id"
>
<span v-if="deletingMemberId === member.id">⏳</span>
<span v-else>🗑️</span>
</button>-->
</div>
</div>
<!-- 空状态 -->
<div v-else class="empty-state">
<div class="empty-icon">👥</div>
<p>No team members found</p>
</div>
</div>
<!-- 删除确认模态框 -->
<div v-if="showDeleteModal" class="modal-overlay" @click="closeDeleteModal">
<div class="delete-modal-content" @click.stop>
<div class="modal-header">
<h3>Confirm Delete</h3>
</div>
<div class="modal-body">
<p>Are you sure you want to remove <strong>{{ memberToDelete?.userProfile?.userNickname }}</strong> from the team?</p>
<p class="warning-text">This action cannot be undone.</p>
</div>
<div class="modal-actions">
<button class="cancel-btn" @click="closeDeleteModal">Cancel</button>
<button class="confirm-btn" @click="confirmDelete">Delete</button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import MobileHeader from '../components/MobileHeader.vue'
import { getTeamMembers, deleteMember as deleteMemberApi } from '../api/teamBill.js'
import {getTeamId} from "@/utils/userStore.js";
import {showError} from "@/utils/toast.js";
const router = useRouter()
const loading = ref(false)
const members = ref([])
const deletingMemberId = ref(null)
const showDeleteModal = ref(false)
const memberToDelete = ref(null)
// 获取团队成员列表
const fetchTeamMembers = async () => {
try {
loading.value = true
// 这里需要传入实际的 teamId可以从路由参数或者用户状态获取
const teamId = getTeamId()
const response = await getTeamMembers(teamId)
if (response.status && response.body) {
members.value = response.body || []
} else {
members.value = []
}
} catch (error) {
console.error('Failed to fetch team members:', error)
members.value = []
} finally {
loading.value = false
}
}
// 格式化账户显示
const formatAccount = (userProfile) => {
if (!userProfile) return ''
// 如果有特殊ID优先显示特殊ID
if (userProfile.ownSpecialId && userProfile.ownSpecialId.account) {
return userProfile.ownSpecialId.account
}
// 否则显示普通账户
return userProfile.account || ''
}
// 处理图片加载错误
const handleImageError = (event) => {
event.target.style.display = 'none'
event.target.nextElementSibling.style.display = 'flex'
}
// 删除成员(重命名避免冲突)
const handleDeleteMember = (member) => {
memberToDelete.value = member
showDeleteModal.value = true
}
// 关闭删除模态框
const closeDeleteModal = () => {
showDeleteModal.value = false
memberToDelete.value = null
}
// 确认删除
const confirmDelete = async () => {
if (!memberToDelete.value) return
try {
deletingMemberId.value = memberToDelete.value.id
// 调用删除成员的API接口
await deleteMemberApi([memberToDelete.value.id])
// 从列表中移除成员
const index = members.value.findIndex(m => m.id === memberToDelete.value.id)
if (index > -1) {
members.value.splice(index, 1)
}
closeDeleteModal()
} catch (error) {
console.error('Failed to delete member:', error)
showError('Failed to delete member. Please try again.')
} finally {
deletingMemberId.value = null
}
}
// 组件挂载时获取数据
onMounted(() => {
fetchTeamMembers()
})
</script>
<style scoped>
.team-member {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
min-height: 100vh;
background-color: #f1f2f3;
}
.content {
padding: 16px;
position: relative;
z-index: 2;
}
/* 加载状态 */
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 60px 20px;
color: #666;
}
.loading-spinner {
width: 32px;
height: 32px;
border: 3px solid #f3f3f3;
border-top: 3px solid #8B5CF6;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 12px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 成员列表 */
.member-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.member-item {
background-color: white;
padding: 16px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
display: flex;
align-items: center;
justify-content: space-between;
transition: all 0.2s;
}
.member-item:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.member-info {
display: flex;
align-items: center;
flex: 1;
}
.member-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;
position: relative;
}
.member-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.member-avatar span {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-transform: uppercase;
}
.member-details {
flex: 1;
}
.member-details h4 {
margin: 0 0 4px 0;
font-size: 16px;
font-weight: 600;
color: #333;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.member-details p {
margin: 0;
font-size: 14px;
color: #666;
}
/* 删除按钮 */
.delete-btn {
background-color: #FEE2E2;
border: 1px solid #FECACA;
color: #DC2626;
padding: 8px;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
}
.delete-btn:hover:not(:disabled) {
background-color: #FECACA;
border-color: #FCA5A5;
}
.delete-btn:disabled {
cursor: not-allowed;
opacity: 0.6;
}
.delete-btn:active:not(:disabled) {
transform: scale(0.95);
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
color: #666;
text-align: center;
}
.empty-icon {
font-size: 48px;
margin-bottom: 16px;
}
.empty-state p {
margin: 0;
font-size: 16px;
}
/* 删除确认模态框 */
.modal-overlay {
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: 16px;
}
.delete-modal-content {
background-color: white;
border-radius: 12px;
width: 100%;
max-width: 400px;
overflow: hidden;
}
.modal-header {
padding: 20px 20px 0 20px;
}
.modal-header h3 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #333;
text-align: center;
}
.modal-body {
padding: 16px 20px;
text-align: center;
}
.modal-body p {
margin: 0 0 8px 0;
font-size: 14px;
color: #666;
line-height: 1.5;
}
.warning-text {
color: #DC2626 !important;
font-weight: 500;
}
.modal-actions {
display: flex;
gap: 12px;
padding: 0 20px 20px 20px;
}
.cancel-btn,
.confirm-btn {
flex: 1;
padding: 12px;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.cancel-btn {
background-color: #F3F4F6;
color: #374151;
}
.cancel-btn:hover {
background-color: #E5E7EB;
}
.confirm-btn {
background-color: #DC2626;
color: white;
}
.confirm-btn:hover {
background-color: #B91C1C;
}
.cancel-btn:active,
.confirm-btn:active {
transform: scale(0.98);
}
/* 响应式设计 */
@media (max-width: 480px) {
.member-details h4 {
max-width: 150px;
}
.delete-btn {
width: 36px;
height: 36px;
font-size: 14px;
}
}
</style>