Files
martial-mini/pages/my-registration/my-registration.vue
2025-12-12 01:44:41 +08:00

430 lines
11 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>
<view class="my-registration-page">
<!-- Tab切换 -->
<custom-tabs :tabs="tabs" :current="currentTab" @change="handleTabChange"></custom-tabs>
<!-- 赛事列表 -->
<view class="event-list">
<view
class="event-item"
v-for="(item, index) in filteredList"
:key="index"
@click="goToEventDetail(item)"
>
<view class="status-tag" :class="getStatusClass(item.status)">
<text>{{ getStatusText(item.status) }}</text>
</view>
<view class="event-title">{{ item.title }}</view>
<view class="event-info">
<text class="label">地点</text>
<text class="value">{{ item.location }}</text>
</view>
<view class="event-info">
<text class="label">比赛时间</text>
<text class="value">{{ item.matchTime }}</text>
</view>
<view class="event-info">
<text class="label">报名项目</text>
<text class="value">{{ item.projects }}</text>
</view>
<view class="event-info">
<text class="label">联系人</text>
<text class="value">{{ item.contact }}</text>
</view>
<view class="event-info">
<text class="label">参赛选手</text>
<text class="value participants">{{ item.participants }}</text>
</view>
<view class="event-footer" v-if="item.status === 'ongoing'">
<view class="register-note">
<text>点击后进入赛事详情页面</text>
</view>
<view class="view-cert-btn" @click.stop="handleViewCert(item)">
<text>查看证件</text>
<text class="arrow"></text>
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="filteredList.length === 0">
<text class="empty-text">暂无报名记录</text>
</view>
</view>
</template>
<script>
import CustomTabs from '../../components/custom-tabs/custom-tabs.vue';
import registrationAPI from '@/api/registration.js'
import competitionAPI from '@/api/competition.js'
export default {
components: {
CustomTabs
},
data() {
return {
tabs: ['全部', '待开始', '进行中', '已结束'],
currentTab: 0,
eventList: [],
// 分页参数
pageParams: {
current: 1,
size: 20
},
hasMore: true
};
},
onLoad() {
this.loadRegistrationList()
},
// 下拉刷新
onPullDownRefresh() {
this.pageParams.current = 1
this.loadRegistrationList(true)
},
// 上拉加载更多
onReachBottom() {
if (this.hasMore) {
this.pageParams.current++
this.loadRegistrationList(false, true)
}
},
computed: {
filteredList() {
if (this.currentTab === 0) {
return this.eventList;
}
const statusMap = {
1: 'pending',
2: 'ongoing',
3: 'finished'
};
return this.eventList.filter(item => item.status === statusMap[this.currentTab]);
}
},
methods: {
/**
* 加载我的报名列表
* @param {Boolean} refresh 是否刷新(重置列表)
* @param {Boolean} loadMore 是否加载更多(追加列表)
*/
async loadRegistrationList(refresh = false, loadMore = false) {
try {
const params = {
current: this.pageParams.current,
size: this.pageParams.size
}
// 添加状态筛选
if (this.currentTab > 0) {
params.status = this.currentTab
}
const res = await registrationAPI.getRegistrationList(params)
console.log('=== 我的报名列表 - 后端返回的原始数据 ===')
console.log('完整响应:', res)
let list = []
let total = 0
// 处理分页数据
if (res.records) {
list = res.records
total = res.total || 0
} else if (Array.isArray(res)) {
list = res
total = res.length
}
// 为每条报名记录获取详情(包含关联数据)
const detailPromises = list.map(item => this.getRegistrationDetailData(item))
const mappedList = await Promise.all(detailPromises)
// 过滤掉获取失败的记录
const validList = mappedList.filter(item => item !== null)
// 刷新或加载更多
if (refresh || !loadMore) {
this.eventList = validList
} else {
this.eventList = [...this.eventList, ...validList]
}
// 判断是否还有更多数据
this.hasMore = this.eventList.length < total
// 停止下拉刷新
if (refresh) {
uni.stopPullDownRefresh()
}
} catch (err) {
console.error('加载报名列表失败:', err)
uni.stopPullDownRefresh()
}
},
/**
* 获取单条报名记录的详细信息
* @param {Object} orderItem 订单基本信息
* @returns {Promise<Object>} 包含完整信息的记录
*/
async getRegistrationDetailData(orderItem) {
try {
console.log('=== 获取报名详情 ===', orderItem.id)
// 获取报名详情
const detail = await registrationAPI.getRegistrationDetail(orderItem.id)
console.log('报名详情:', detail)
// 获取赛事详情
let competitionInfo = null
if (orderItem.competitionId || detail.competitionId) {
const competitionId = orderItem.competitionId || detail.competitionId
competitionInfo = await competitionAPI.getCompetitionDetail(competitionId)
console.log('赛事详情:', competitionInfo)
}
// 构建映射数据
const mapped = {
id: orderItem.id,
status: this.getStatus(orderItem.status),
title: competitionInfo?.name || detail.competitionName || '未知赛事',
location: competitionInfo?.location || competitionInfo?.address || detail.location || '',
matchTime: this.formatTimeRange(
competitionInfo?.startTime || detail.startTime,
competitionInfo?.endTime || detail.endTime
) || '',
projects: detail.projectNames || this.formatProjects(detail.projects || detail.projectList) || '',
contact: orderItem.contactPhone || detail.contactPhone || '',
participants: detail.athleteNames || this.formatParticipants(detail.athletes || detail.athleteList) || ''
}
console.log('映射后的数据:', mapped)
return mapped
} catch (err) {
console.error('获取报名详情失败:', err, orderItem.id)
// 返回基本信息,避免整个记录丢失
return {
id: orderItem.id,
status: this.getStatus(orderItem.status),
title: '获取详情失败',
location: '',
matchTime: '',
projects: '',
contact: orderItem.contactPhone || '',
participants: `${orderItem.totalParticipants || 0}`
}
}
},
/**
* 格式化时间范围
*/
formatTimeRange(startTime, endTime) {
if (!startTime || !endTime) return ''
const formatDate = (dateStr) => {
if (!dateStr) return ''
const date = new Date(dateStr)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}.${month}.${day}`
}
return `${formatDate(startTime)}-${formatDate(endTime)}`
},
/**
* 获取报名状态
*/
getStatus(status) {
// 1: 待开始, 2: 进行中, 3: 已结束
const statusMap = {
1: 'pending',
2: 'ongoing',
3: 'finished',
'pending': 'pending',
'ongoing': 'ongoing',
'finished': 'finished'
}
return statusMap[status] || 'pending'
},
/**
* 格式化报名项目
*/
formatProjects(projects) {
if (!projects) return ''
if (Array.isArray(projects)) {
return projects.map(p => p.name || p.projectName).join('、')
}
return projects
},
/**
* 格式化参赛选手
*/
formatParticipants(athletes) {
if (!athletes) return ''
if (Array.isArray(athletes)) {
return athletes.map(a => a.name || a.athleteName).join('、')
}
return athletes
},
handleTabChange(index) {
this.currentTab = index;
// 切换tab时重新加载
this.pageParams.current = 1
this.loadRegistrationList(true)
},
getStatusClass(status) {
return {
'status-ongoing': status === 'ongoing',
'status-pending': status === 'pending',
'status-finished': status === 'finished'
};
},
getStatusText(status) {
const statusMap = {
ongoing: '进行中',
pending: '待开始',
finished: '已结束'
};
return statusMap[status] || '';
},
goToEventDetail(item) {
uni.navigateTo({
url: '/pages/event-detail/event-detail?id=' + item.id
});
},
handleViewCert(item) {
uni.showToast({
title: '查看证件',
icon: 'none'
});
}
}
};
</script>
<style lang="scss" scoped>
.my-registration-page {
min-height: 100vh;
background-color: #f5f5f5;
}
.event-list {
padding: 30rpx;
}
.event-item {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
position: relative;
}
.status-tag {
position: absolute;
top: 30rpx;
left: 30rpx;
padding: 8rpx 20rpx;
border-radius: 8rpx;
font-size: 24rpx;
color: #fff;
}
.status-ongoing {
background-color: #4CAF50;
}
.status-pending {
background-color: #FF9800;
}
.status-finished {
background-color: #999999;
}
.event-title {
font-size: 32rpx;
font-weight: bold;
color: #333333;
margin: 50rpx 0 20rpx;
line-height: 1.5;
}
.event-info {
display: flex;
margin-bottom: 10rpx;
}
.label {
font-size: 26rpx;
color: #666666;
flex-shrink: 0;
}
.value {
font-size: 26rpx;
color: #666666;
flex: 1;
}
.participants {
word-break: break-all;
}
.event-footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20rpx;
padding-top: 20rpx;
border-top: 1rpx solid #f5f5f5;
}
.register-note {
font-size: 24rpx;
color: #C93639;
}
.view-cert-btn {
display: flex;
align-items: center;
padding: 10rpx 20rpx;
border: 2rpx solid #C93639;
border-radius: 8rpx;
font-size: 24rpx;
color: #C93639;
}
.arrow {
font-size: 28rpx;
margin-left: 5rpx;
}
.empty-state {
padding: 200rpx 0;
text-align: center;
}
.empty-text {
font-size: 28rpx;
color: #999999;
}
</style>