Files
property-resident-side/property-uniapp-project/pageSubPack/activity-list/activityList.vue
2026-04-21 18:25:00 +08:00

724 lines
16 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="container">
<view class="status_bar"></view>
<!-- 顶部标题栏 固定 -->
<view class="top-bar">
<uni-nav-bar left-icon="left" title="活动列表" backgroundColor="none" :border="false"
@clickLeft="narBack" />
</view>
<!-- 滚动区域从banner开始滚动 -->
<scroll-view class="scroll-container" scroll-y="true" @scrolltolower="loadMoreData"
:lower-threshold="100">
<!-- 顶部图片 -->
<view class="topImg">
<image src="http://47.104.199.163:9090/images/home/banner.png"
style="width: 100%;height:100%;"></image>
</view>
<!-- 选项卡 -->
<view class="tabs-container">
<view class="tab-item" :class="{ 'active': activeTab === 'list' }" @tap="switchTab('list')">
活动列表
<view v-if="activeTab === 'list'" class="tab-indicator"></view>
</view>
<view class="tab-item" :class="{ 'active': activeTab === 'my' }" @tap="switchTab('my')">
我的活动
<view v-if="activeTab === 'my'" class="tab-indicator"></view>
</view>
</view>
<!-- 我的活动状态筛选 -->
<view v-if="activeTab === 'my'" class="filter-container">
<scroll-view class="filter-scroll" scroll-x>
<view class="filter-buttons">
<view v-for="filter in filters" :key="filter.value" class="filter-btn"
:class="{ 'active': myFilter === filter.value }" @tap="switchFilter(filter.value)">
{{ filter.label }}
</view>
</view>
</scroll-view>
</view>
<!-- 活动列表 -->
<view class="activity-list">
<!-- 空状态 -->
<view v-if="displayedActivities.length === 0" class="empty-state">
<uni-icons type="info" size="60" color="#ccc"></uni-icons>
<text class="empty-text">
{{ activeTab === 'list' ? '暂无活动' : '暂无参与的活动' }}
</text>
</view>
<!-- 活动卡片 -->
<view v-for="activity in displayedActivities" :key="activity.id" class="activity-card"
@tap="viewActivityDetail(activity)">
<view class="status-badge">
进行中
</view>
<view v-if="activity.hasSigned && activeTab === 'list'" class="signed-badge">
<uni-icons type="checkbox-filled" size="14" color="#fff"></uni-icons>
<text>已报名</text>
</view>
<view class="activity-info">
<view class="u-demo-block__content ">
<up-row justify="space-between" customStyle="margin-bottom: 10px">
<up-col span="4">
<view class="info-image-left">
<image src="http://47.104.199.163:9090/images/home/activity-img.png"
style="width: 100%;height: 100%;"></image>
</view>
</up-col>
<up-col span="8">
<view class="demo-layout bg-purple">
<view class="activity-title">
{{ activity.title }}
</view>
<view class="activity-detail">
<text class="detail-text">时间{{ formatDate(activity.startTime) }}
{{ formatDate(activity.endTime) }}</text>
</view>
<view class="activity-detail">
<text class="detail-text">地点{{ activity.location }}</text>
</view>
<view class="activity-detail">
<text class="detail-text-num">活动名额{{ activity.totalQuota }}已报名{{ activity.signedCount }}</text>
</view>
</view>
</up-col>
</up-row>
</view>
<view class="action-buttons">
<button v-if="activeTab === 'list' && !activity.hasSigned" class="action-btn signup-btn"
@tap.stop="signupActivity(activity)">
立即报名
</button>
</view>
</view>
</view>
<!-- 加载更多 -->
<view v-if="hasMoreData && displayedActivities.length > 0" class="load-more-container">
<view v-if="!isLoading" class="load-more-btn" @tap="loadMoreData">
<text>加载更多</text>
<uni-icons type="arrow-down" size="16" color="#007AFF"></uni-icons>
</view>
<view v-else class="loading-container">
<uni-icons type="spinner-cycle" size="20" color="#007AFF" class="loading-icon"></uni-icons>
<text>正在加载...</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup>
import {
ref,
computed,
onMounted,
watch
} from 'vue'
import moment from 'moment';
// 响应式数据
const activeTab = ref('list') // 'list' 或 'my'
const myFilter = ref('reviewing') // 我的活动筛选条件
const isLoading = ref(false)
const hasMoreData = ref(true)
const currentPage = ref(1)
const pageSize = 5
// 筛选条件
const filters = ref([{
label: '审核中',
value: 'reviewing'
},
{
label: '已通过',
value: 'passed'
},
{
label: '未通过',
value: 'rejected'
},
{
label: '已结束',
value: 'ended'
}
])
// 模拟活动数据
const allActivities = ref([{
id: 1,
title: '阳光海岸社区生活节活动',
startTime: '2023-06-19 08:00',
endTime: '2023-06-30 17:00',
location: '山东省日照市东港区莱顿二期花语墅西沿街',
totalQuota: 100,
signedCount: 68,
status: 'ongoing', // ongoing, upcoming, ended
hasSigned: true,
myStatus: 'passed', // reviewing, passed, rejected
coverImage: '/static/activity1.jpg',
description: '阳光海岸社区生活节是一个为期两周的社区文化活动,包括文艺表演、手工艺品展示、美食节等多种活动。'
},
{
id: 2,
title: '社区环保志愿者招募活动',
startTime: '2023-07-01 09:00',
endTime: '2023-07-31 18:00',
location: '社区公园及周边街道',
totalQuota: 50,
signedCount: 32,
status: 'upcoming',
hasSigned: false,
myStatus: 'reviewing',
coverImage: '/static/activity2.jpg',
description: '招募社区环保志愿者,参与垃圾分类宣传、社区清洁等环保活动。'
},
{
id: 3,
title: '端午节包粽子亲子活动',
startTime: '2023-06-10 14:00',
endTime: '2023-06-10 17:00',
location: '社区活动中心',
totalQuota: 30,
signedCount: 30,
status: 'ended',
hasSigned: true,
myStatus: 'passed',
coverImage: '/static/activity3.jpg',
description: '端午节亲子活动,教孩子们包粽子,了解传统文化。'
},
{
id: 4,
title: '老年人智能手机使用培训',
startTime: '2023-07-05 14:00',
endTime: '2023-07-12 16:00',
location: '社区图书馆二楼',
totalQuota: 20,
signedCount: 15,
status: 'upcoming',
hasSigned: false,
myStatus: null,
coverImage: '/static/activity4.jpg',
description: '为社区老年人提供智能手机使用培训包括微信、支付宝等常用APP的使用。'
},
{
id: 5,
title: '社区篮球友谊赛',
startTime: '2023-06-25 15:00',
endTime: '2023-06-25 18:00',
location: '社区体育馆',
totalQuota: 40,
signedCount: 25,
status: 'ongoing',
hasSigned: false,
myStatus: 'rejected',
coverImage: '/static/activity5.jpg',
description: '社区篮球友谊赛,欢迎篮球爱好者报名参加。'
},
{
id: 6,
title: '儿童暑期绘画班',
startTime: '2023-07-10 09:00',
endTime: '2023-08-20 11:00',
location: '社区艺术工作室',
totalQuota: 25,
signedCount: 18,
status: 'upcoming',
hasSigned: true,
myStatus: 'reviewing',
coverImage: '/static/activity6.jpg',
description: '暑期儿童绘画班,培养孩子的艺术兴趣和创造力。'
},
{
id: 7,
title: '社区消防安全演练',
startTime: '2023-05-15 10:00',
endTime: '2023-05-15 12:00',
location: '社区广场',
totalQuota: 100,
signedCount: 85,
status: 'ended',
hasSigned: true,
myStatus: 'passed',
coverImage: '/static/activity7.jpg',
description: '社区消防安全知识讲座和应急演练。'
},
{
id: 8,
title: '邻里美食分享会',
startTime: '2023-07-08 16:00',
endTime: '2023-07-08 19:00',
location: '社区花园',
totalQuota: 40,
signedCount: 22,
status: 'upcoming',
hasSigned: false,
myStatus: null,
coverImage: '/static/activity8.jpg',
description: '邻里美食分享会,每家带一道拿手菜,共享美食。'
}
])
// 当前显示的活动列表
const displayedActivities = ref([])
// 计算属性:根据当前标签和筛选条件过滤活动
const filteredActivities = computed(() => {
if (activeTab.value === 'list') {
// 活动列表页:显示所有活动
return allActivities.value
} else {
// 我的活动页:根据筛选条件过滤
if (myFilter.value === 'all') {
return allActivities.value.filter(activity => activity.hasSigned)
} else {
return allActivities.value.filter(activity =>
activity.hasSigned && activity.myStatus === myFilter.value
)
}
}
})
// 返回
const narBack = () => {
if (uni.getStorageSync('sourcePage') === "index") {
uni.switchTab({
url: "/pages/index/index"
})
} else if (uni.getStorageSync('sourcePage') === "serviceIndex") {
uni.switchTab({
url: "/pages/serviceIndex/serviceIndex"
})
}
}
// 切换选项卡
const switchTab = (tab) => {
activeTab.value = tab
currentPage.value = 1
displayedActivities.value = []
loadPageData(1)
}
// 切换筛选条件
const switchFilter = (filter) => {
myFilter.value = filter
currentPage.value = 1
displayedActivities.value = []
loadPageData(1)
}
// 加载分页数据
const loadPageData = (pageNum) => {
isLoading.value = true
// 模拟网络请求延迟
setTimeout(() => {
const startIndex = (pageNum - 1) * pageSize
const endIndex = startIndex + pageSize
const pageData = filteredActivities.value.slice(startIndex, endIndex)
if (pageNum === 1) {
displayedActivities.value = pageData
} else {
displayedActivities.value = [...displayedActivities.value, ...pageData]
}
currentPage.value = pageNum
hasMoreData.value = displayedActivities.value.length < filteredActivities.value.length
isLoading.value = false
}, 500)
}
// 加载更多数据
const loadMoreData = () => {
if (isLoading.value || !hasMoreData.value) return
loadPageData(currentPage.value + 1)
}
// 获取活动状态类名
const getStatusClass = (status) => {
const classMap = {
'ongoing': 'status-ongoing',
}
return classMap[status] || ''
}
// 获取我的活动状态文本
const getMyStatusText = (status) => {
const statusMap = {
'reviewing': '审核中',
'passed': '已通过',
'rejected': '未通过',
'ended': '已结束'
}
return statusMap[status] || '未知'
}
// 获取我的活动状态类名
const getMyStatusClass = (status) => {
const classMap = {
'reviewing': 'my-status-reviewing',
'passed': 'my-status-passed',
'rejected': 'my-status-rejected',
'ended': 'my-status-ended'
}
return classMap[status] || ''
}
// 格式化日期
function formatDate(date) {
return moment(date).format('YYYY-MM-DD HH:mm:ss')
}
// 报名活动
const signupActivity = (activity) => {
uni.navigateTo({
url: `/pages/activity-list/activityListDetails?id=${activity.id}&title=${activity.title}`
})
}
// 查看活动详情
const viewActivityDetail = (activity) => {
uni.navigateTo({
url: `/pages/activityDetail/activityDetail?id=${activity.id}`,
fail: () => {
// 如果页面不存在,显示活动信息
uni.showModal({
title: activity.title,
content: `${activity.description}\n\n时间${formatDate(activity.startTime)}${formatDate(activity.endTime)}\n地点${activity.location}\n名额${activity.totalQuota}人,已报名:${activity.signedCount}`,
showCancel: false,
confirmText: '知道了'
})
}
})
}
// 联系管理员
const contactAdmin = (activity) => {
uni.showModal({
title: '联系管理员',
content: `您报名"${activity.title}"未通过审核,是否联系管理员了解原因?`,
success: (res) => {
if (res.confirm) {
uni.makePhoneCall({
phoneNumber: '400-123-4567'
})
}
}
})
}
// 监听筛选条件变化
watch(() => myFilter.value, () => {
if (activeTab.value === 'my') {
currentPage.value = 1
displayedActivities.value = []
loadPageData(1)
}
})
// 初始化
onMounted(() => {
loadPageData(1)
})
</script>
<style>
/* 页面容器 */
.container {
width: 100%;
height: 100vh;
background-color: #f9f9f9;
display: flex;
flex-direction: column;
padding: 0;
}
.status_bar {
height: 46px;
width: 100%;
}
/* 顶部标题 固定 */
.top-bar {
width: 100%;
flex-shrink: 0;
background: #f9f9f9;
}
/* 滚动区域 */
.scroll-container {
flex: 1;
width: 100%;
overflow-y: auto;
padding: 0 20rpx;
box-sizing: border-box;
}
.topImg {
width: 100%;
height: 280rpx;
margin: 20rpx 0;
}
/* 顶部选项卡 */
.tabs-container {
display: flex;
top: 0;
z-index: 10;
margin-bottom: 20rpx
}
.tab-item {
flex: 1;
text-align: center;
padding: 20rpx 0;
font-size: 32rpx;
color: #666;
position: relative;
}
.tab-item.active {
color: #007AFF;
font-weight: 500;
}
.tab-indicator {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 120rpx;
height: 6rpx;
background-color: #007AFF;
border-radius: 4rpx;
}
/* 筛选容器 */
.filter-container {
background-color: #fff;
padding: 20rpx 0;
border-bottom: 1px solid #eee;
}
.filter-scroll {
width: 100%;
white-space: nowrap;
}
.filter-buttons {
display: inline-flex;
padding: 0 30rpx;
}
.filter-btn {
display: inline-block;
padding: 12rpx 30rpx;
margin-right: 20rpx;
font-size: 28rpx;
color: #666;
background-color: #f8f8f8;
border-radius: 25rpx;
border: 1px solid #eee;
}
.filter-btn.active {
color: #fff;
background-color: #2F77FD;
}
/* 活动列表 */
.activity-list {
width: 100%;
padding-bottom: 40rpx;
}
/* 活动卡片 */
.activity-card {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
position: relative;
}
/* 活动状态标签 */
.status-badge {
position: absolute;
top: 10rpx;
left: 20rpx;
padding: 6rpx 20rpx;
font-size: 22rpx;
border-radius: 15rpx 0rpx 15rpx 0rpx;
color: #fff;
z-index: 1;
background: repeating-linear-gradient(to right, #FD7373, #FB3F3F);
}
/* 已报名标识 */
.signed-badge {
position: absolute;
top: 20rpx;
right: 8rpx;
display: flex;
align-items: center;
padding: 6rpx 14rpx;
background-color: #007AFF;
color: #fff;
font-size: 22rpx;
border-radius: 20rpx;
z-index: 1;
}
.signed-badge text {
margin-left: 8rpx;
}
/* 活动信息 */
.activity-info {
margin-top: 10rpx;
}
.info-image-left {
width: 170rpx;
height: 220rpx;
}
.activity-title {
font-size: 30rpx;
font-weight: 500;
color: #222;
margin-bottom: 30rpx;
line-height: 1.4;
padding-right: 100rpx;
}
.activity-detail {
display: flex;
align-items: flex-start;
margin-bottom: 20rpx;
color: #999;
font-size: 24rpx;
line-height: 1.4;
}
.activity-detail .uni-icons {
margin-right: 15rpx;
margin-top: 4rpx;
flex-shrink: 0;
}
.detail-text-num {
font-size: 28rpx;
color: #666666;
}
.detail-text {
flex: 1;
}
/* 操作按钮 */
.action-buttons {
margin-top: 30rpx;
display: flex;
justify-content: flex-end;
}
.action-btn {
padding: 0 40rpx;
height: 60rpx;
line-height: 60rpx;
font-size: 24rpx;
border-radius: 35rpx;
margin: 0;
}
.signup-btn {
background-color: #007AFF;
color: #fff;
}
.signup-btn:active {
background-color: #0056cc;
}
/* 加载更多 */
.load-more-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx 0;
color: #999;
}
.load-more-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 40rpx;
background-color: #f8f8f8;
border-radius: 50rpx;
color: #007AFF;
font-size: 28rpx;
margin-bottom: 20rpx;
}
.load-more-btn text {
margin-right: 10rpx;
}
.loading-container {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 40rpx;
background-color: #f8f8f8;
border-radius: 50rpx;
color: #007AFF;
font-size: 28rpx;
margin-bottom: 20rpx;
}
.loading-icon {
margin-right: 10rpx;
animation: rotate 1s linear infinite;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
color: #ccc;
}
.empty-text {
font-size: 32rpx;
color: #999;
margin-top: 20rpx;
margin-bottom: 10rpx;
}
</style>