Files
martial-mini/pages/event-list/event-list.vue
2025-12-26 10:20:46 +08:00

472 lines
12 KiB
Vue
Raw Permalink 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="event-list-page">
<!-- 搜索栏 -->
<view class="search-bar">
<view class="search-input-wrapper">
<text class="search-icon">🔍</text>
<input
class="search-input"
v-model="searchText"
placeholder="请输入赛事关键字"
placeholder-class="placeholder"
/>
</view>
</view>
<!-- 筛选栏 -->
<view class="filter-bar">
<view class="filter-item" @click="showDatePicker = true">
<text>{{ selectedDate || '日期' }}</text>
<text class="dropdown-icon"></text>
</view>
<view class="filter-item" @click="showAreaPicker = true">
<text>{{ selectedArea || '地区' }}</text>
<text class="dropdown-icon"></text>
</view>
</view>
<!-- 赛事列表 -->
<view class="event-list" v-if="filteredEventList.length > 0">
<view
class="event-item"
v-for="(item, index) in filteredEventList"
:key="index"
@click="goToEventDetail(item)"
>
<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.registerTime }}</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.registerCount }}</text>
</view>
<view class="event-footer">
<view class="register-btn" @click.stop="goToRegister(item)">
{{ item.status === 'finished' ? '报名已结束' : '立即报名' }}
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-else>
<text class="empty-text">没有相关结果</text>
</view>
<!-- 日期选择器 -->
<view class="picker-mask" v-if="showDatePicker" @click="showDatePicker = false">
<view class="picker-content" @click.stop>
<view class="picker-header">
<text @click="showDatePicker = false">取消</text>
<text @click="confirmDatePicker">确定</text>
</view>
<picker-view :value="datePickerValue" @change="handleDateChange" class="picker-view">
<picker-view-column>
<view class="picker-item" v-for="(item, index) in dateOptions" :key="index">
{{ item }}
</view>
</picker-view-column>
</picker-view>
</view>
</view>
<!-- 地区选择器 -->
<view class="picker-mask" v-if="showAreaPicker" @click="showAreaPicker = false">
<view class="picker-content" @click.stop>
<view class="picker-header">
<text @click="showAreaPicker = false">取消</text>
<text @click="confirmAreaPicker">确定</text>
</view>
<picker-view :value="areaPickerValue" @change="handleAreaChange" class="picker-view">
<picker-view-column>
<view class="picker-item" v-for="(item, index) in areaOptions" :key="index">
{{ item }}
</view>
</picker-view-column>
</picker-view>
</view>
</view>
</view>
</template>
<script>
import competitionAPI from '@/api/competition.js'
export default {
data() {
return {
searchText: '',
selectedDate: '',
selectedArea: '',
showDatePicker: false,
showAreaPicker: false,
datePickerValue: [0],
areaPickerValue: [0],
dateOptions: ['2025-04-09', '2025-04-10', '2025-04-11'],
areaOptions: ['乌鲁木齐', '天津市', '北京市'],
eventList: [],
// 分页参数
pageParams: {
current: 1,
size: 20
},
hasMore: true
};
},
onLoad() {
this.loadEventList()
},
// 下拉刷新
onPullDownRefresh() {
this.pageParams.current = 1
this.loadEventList(true)
},
// 上拉加载更多
onReachBottom() {
if (this.hasMore) {
this.pageParams.current++
this.loadEventList(false, true)
}
},
computed: {
filteredEventList() {
// 前端筛选(作为后备方案)
let list = this.eventList
// 如果有搜索关键字,进行前端筛选
if (this.searchText) {
list = list.filter(item => item.title && item.title.includes(this.searchText))
}
return list
}
},
// 监听搜索关键字变化
watch: {
searchText(newVal, oldVal) {
// 防抖处理
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.pageParams.current = 1
this.loadEventList(true)
}, 500)
},
selectedDate() {
this.pageParams.current = 1
this.loadEventList(true)
},
selectedArea() {
this.pageParams.current = 1
this.loadEventList(true)
}
},
methods: {
/**
* 加载赛事列表
* @param {Boolean} refresh 是否刷新(重置列表)
* @param {Boolean} loadMore 是否加载更多(追加列表)
*/
async loadEventList(refresh = false, loadMore = false) {
try {
// 构建查询参数
const params = {
current: this.pageParams.current,
size: this.pageParams.size
}
// 添加搜索关键字
// 注意:后端接口参数名待确认,可能是 name/keyword/search
if (this.searchText) {
params.name = this.searchText
}
// 添加地区筛选
if (this.selectedArea) {
params.location = this.selectedArea
}
// 调用API
const res = await competitionAPI.getCompetitionList(params)
console.log('赛事列表API返回:', 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 mappedList = list.map(item => {
// 尝试多个可能的时间字段
const regStartTime = item.registrationStartTime || item.registerStartTime || item.signUpStartTime
const regEndTime = item.registrationEndTime || item.registerEndTime || item.signUpEndTime
const startTime = item.startTime || item.competitionStartTime || item.beginTime
const endTime = item.endTime || item.competitionEndTime || item.finishTime
return {
id: item.id,
title: item.name || item.title || item.competitionName || '未命名赛事',
location: item.location || item.address || item.venue || '待定',
registerTime: this.formatTimeRange(regStartTime, regEndTime) ||
item.registerTime || item.registrationPeriod || '待定',
matchTime: this.formatTimeRange(startTime, endTime) ||
item.matchTime || item.competitionTime || '待定',
registerCount: item.registrationCount || item.registerCount || item.signUpCount || item.totalParticipants || '0',
status: this.getStatus(item.status)
}
})
console.log('格式化后的赛事列表:', mappedList)
// 刷新或加载更多
if (refresh || !loadMore) {
this.eventList = mappedList
} else {
this.eventList = [...this.eventList, ...mappedList]
}
// 判断是否还有更多数据
this.hasMore = this.eventList.length < total
// 停止下拉刷新
if (refresh) {
uni.stopPullDownRefresh()
}
} catch (err) {
console.error('加载赛事列表失败:', err)
uni.stopPullDownRefresh()
}
},
/**
* 格式化时间范围
*/
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: 已结束
if (status === 3 || status === '3' || status === 'finished') {
return 'finished'
}
return 'open'
},
handleDateChange(e) {
this.datePickerValue = e.detail.value;
},
handleAreaChange(e) {
this.areaPickerValue = e.detail.value;
},
confirmDatePicker() {
this.selectedDate = this.dateOptions[this.datePickerValue[0]];
this.showDatePicker = false;
},
confirmAreaPicker() {
this.selectedArea = this.areaOptions[this.areaPickerValue[0]];
this.showAreaPicker = false;
},
goToEventDetail(item) {
uni.navigateTo({
url: '/pages/event-detail/event-detail?id=' + item.id
});
},
goToRegister(item) {
if (item.status === 'open') {
uni.navigateTo({
url: '/pages/register-type/register-type?id=' + item.id
});
}
}
}
};
</script>
<style lang="scss" scoped>
.event-list-page {
min-height: 100vh;
background-color: #f5f5f5;
}
.search-bar {
background-color: #fff;
padding: 20rpx 30rpx;
}
.search-input-wrapper {
display: flex;
align-items: center;
background-color: #f5f5f5;
border-radius: 50rpx;
padding: 20rpx 30rpx;
}
.search-icon {
font-size: 32rpx;
margin-right: 15rpx;
color: #999999;
}
.search-input {
flex: 1;
font-size: 28rpx;
}
.placeholder {
color: #cccccc;
}
.filter-bar {
display: flex;
gap: 20rpx;
padding: 20rpx 30rpx;
background-color: #fff;
margin-bottom: 20rpx;
}
.filter-item {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 30rpx;
background-color: #f5f5f5;
border-radius: 8rpx;
font-size: 28rpx;
color: #333333;
}
.dropdown-icon {
font-size: 24rpx;
color: #999999;
}
.event-list {
padding: 0 30rpx 30rpx;
}
.event-item {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
.event-title {
font-size: 32rpx;
font-weight: bold;
color: #333333;
margin-bottom: 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;
}
.event-footer {
display: flex;
justify-content: flex-end;
margin-top: 20rpx;
}
.register-btn {
background-color: #C93639;
color: #fff;
padding: 16rpx 50rpx;
border-radius: 50rpx;
font-size: 28rpx;
}
.empty-state {
padding: 200rpx 0;
text-align: center;
}
.empty-text {
font-size: 28rpx;
color: #999999;
}
.picker-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9999;
display: flex;
align-items: flex-end;
}
.picker-content {
width: 100%;
background-color: #fff;
border-radius: 24rpx 24rpx 0 0;
}
.picker-header {
display: flex;
justify-content: space-between;
padding: 30rpx;
border-bottom: 1rpx solid #f5f5f5;
font-size: 30rpx;
}
.picker-view {
height: 400rpx;
}
.picker-item {
height: 80rpx;
line-height: 80rpx;
text-align: center;
font-size: 30rpx;
}
</style>