绿满眶商城微信小程序-uniapp
zhanghua
2025-06-11 1eefd113e28e802348a9cae69a41945c1dc48b0f
pages/mine/activity/myActivity.vue
New file
@@ -0,0 +1,465 @@
<template>
  <view class="activity-container">
    <!-- 顶部 Tab 导航 -->
    <view class="tab-nav">
      <view
        v-for="(tab, index) in tabs"
        :key="index"
        class="tab-item"
        :class="{active: currentTab === index}"
        @click="switchTab(index)"
      >
        {{tab}}
        <view class="tab-indicator" v-if="currentTab === index"></view>
      </view>
    </view>
    <!-- 活动列表 -->
    <view class="activity-list">
      <!-- 已报名活动 -->
      <view v-if="currentTab === 0">
        <view v-if="signedActivities.length > 0">
          <view
            v-for="(item, idx) in signedActivities"
            :key="idx"
            class="activity-item card"
          >
            <!-- 封面区域 -->
            <view class="cover-container">
              <block v-if="item.coverType === '图片' || item.coverType === '视频'">
                <image :src="getUrl(item.cover)" mode="aspectFill" class="activity-cover" />
              </block>
              <block v-if="item.coverType === '文字'">
                <view class="activity-cover text-cover">{{ item.cover }}</view>
              </block>
            </view>
            <!-- 活动信息 -->
            <view class="activity-info">
              <view class="info-header">
                <view class="activity-title">{{ item.activityName }}</view>
                <view class="activity-status signed">已报名</view>
              </view>
              <view class="activity-meta">
                <view class="meta-item">
                  <u-icon name="calendar" size="16" color="#999"></u-icon>
                  <text class="activity-time">{{ item.startTime }} - {{ item.endTime }}</text>
                </view>
                <view class="meta-item">
                  <u-icon name="map" size="16" color="#999"></u-icon>
                  <text class="activity-location">{{ item.activityLocation || '待定' }}</text>
                </view>
              </view>
              <view class="action-container">
                <button
                  class="cancel-btn"
                  @click="handleActivityCancel(item.id)"
                  hover-class="cancel-btn-hover"
                >
                  取消报名
                </button>
              </view>
            </view>
          </view>
        </view>
        <view v-else class="empty-state">
          <text class="empty-text">暂无已报名活动</text>
        </view>
      </view>
      <!-- 已结束活动 -->
      <view v-if="currentTab === 1">
        <view v-if="endedActivities.length > 0">
          <view
            v-for="(item, idx) in endedActivities"
            :key="idx"
            class="activity-item card"
          >
            <view class="cover-container">
              <block v-if="item.coverType === '图片' || item.coverType === '视频'">
                <image :src="getUrl(item.cover)" mode="aspectFill" class="activity-cover" />
              </block>
              <block v-if="item.coverType === '文字'">
                <view class="activity-cover text-cover">{{ item.cover }}</view>
              </block>
            </view>
            <!-- 活动信息 -->
            <view class="activity-info">
              <view class="info-header">
                <view class="activity-title">{{ item.activityName }}</view>
                <view class="activity-status ended">已结束</view>
              </view>
              <view class="activity-meta">
                <view class="meta-item">
                  <u-icon name="calendar" size="16" color="#999"></u-icon>
                  <text class="activity-time">{{ item.startTime }} - {{ item.endTime }}</text>
                </view>
                <view class="meta-item">
                  <u-icon name="map" size="16" color="#999"></u-icon>
                  <text class="activity-location">{{ item.activityLocation || '待定' }}</text>
                </view>
              </view>
            </view>
          </view>
        </view>
        <view v-else class="empty-state">
          <text class="empty-text">暂无已结束活动</text>
        </view>
      </view>
      <!-- 已取消活动 -->
      <view v-if="currentTab === 2">
        <view v-if="canceledActivities.length > 0">
          <view
            v-for="(item, idx) in canceledActivities"
            :key="idx"
            class="activity-item card"
          >
            <view class="cover-container">
              <block v-if="item.coverType === '图片' || item.coverType === '视频'">
                <image :src="getUrl(item.cover)" mode="aspectFill" class="activity-cover" />
              </block>
              <block v-if="item.coverType === '文字'">
                <view class="activity-cover text-cover">{{ item.cover }}</view>
              </block>
            </view>
            <!-- 活动信息 -->
            <view class="activity-info">
              <view class="info-header">
                <view class="activity-title">{{ item.activityName }}</view>
                <view class="activity-status canceled">已取消</view>
              </view>
              <view class="activity-meta">
                <view class="meta-item">
                  <u-icon name="calendar" size="16" color="#999"></u-icon>
                  <text class="activity-time">{{ item.startTime }} - {{ item.endTime }}</text>
                </view>
                <view class="meta-item">
                  <u-icon name="map" size="16" color="#999"></u-icon>
                  <text class="activity-location">{{ item.activityLocation || '待定' }}</text>
                </view>
              </view>
            </view>
          </view>
        </view>
        <view v-else class="empty-state">
          <text class="empty-text">暂无已取消活动</text>
        </view>
      </view>
    </view>
  </view>
</template>
<script>
import {getMyActivityList,collectCancel,activityCancel} from '@/api/activity.js'
import {getFilePreviewUrl} from '@/api/common.js'
export default {
  data() {
    return {
      currentTab: 0, // 当前选中的tab索引
      tabs: ['已报名', '已结束', '已取消'],
      signedActivities: [], // 已报名列表
      endedActivities: [],// 已结束列表
      canceledActivities: [],//已取消列表
      query:{
        status:'',
        cancel:false,
      },
    }
  },
  onLoad(){
    this.currentTab = 0;
    this.getMyActivityList(this.currentTab);
  },
  methods: {
    handleActivityCancel(activityId){
      activityCancel(activityId).then(res =>{
        if(res.statusCode == 200){
          uni.showToast({
            title: res.data.msg,
            icon: 'success',
            mask: true,
            duration: 2000,
            success: () => {
              setTimeout(() => {
                this.getMyActivityList(this.currentTab);
              }, 2000);
            }
          });
        }
      })
    },
    getUrl(params){
      getFilePreviewUrl(params).then(res =>{
         return res.data.data
      })
    },
    switchTab(index) {
      if (this.currentTab !== index) {
        this.currentTab = index;
        this.getMyActivityList(index)
      }
    },
    getMyActivityList(index){
      uni.showLoading({
        title: '加载中'
      });
      if(index === 0){
        this.query.cancel = false;
        getMyActivityList(this.query).then(res =>{
          uni.hideLoading();
          if(res.statusCode === 200){
            this.signedActivities = res.data.data
          }
        })
      }else if(index === 1){
        this.query.status = '已结束';
        this.query.cancel = false;
        getMyActivityList(this.query).then(res =>{
          uni.hideLoading();
          if(res.statusCode === 200){
            this.endedActivities = res.data.data
          }
        })
      }else if(index === 2){
        this.query.cancel = true;
        getMyActivityList(this.query).then(res =>{
          uni.hideLoading();
          if(res.statusCode === 200){
            this.canceledActivities = res.data.data
          }
        })
      }
      this.query.status = '';
      this.query.cancel=  false;
    },
  }
}
</script>
<style lang="scss">
.activity-container {
  padding: 0;
  background-color: #f7f8fa;
  min-height: 100vh;
}
/* Tab 导航样式 */
.tab-nav {
  display: flex;
  background-color: #fff;
  margin-bottom: 16rpx;
  position: sticky;
  top: 0;
  z-index: 10;
  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
}
.tab-item {
  flex: 1;
  text-align: center;
  padding: 28rpx 0;
  font-size: 30rpx;
  color: #666;
  position: relative;
  transition: all 0.3s ease;
  &.active {
    color: #2979ff;
    font-weight: 500;
  }
  .tab-indicator {
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 80rpx;
    height: 6rpx;
    background-color: #2979ff;
    border-radius: 3rpx;
    animation: scaleIn 0.3s ease;
  }
}
@keyframes scaleIn {
  from { transform: translateX(-50%) scaleX(0); }
  to { transform: translateX(-50%) scaleX(1); }
}
/* 活动列表样式 */
.activity-list {
  padding: 20rpx 24rpx;
}
.card {
  background: #fff;
  border-radius: 16rpx;
  margin-bottom: 24rpx;
  overflow: hidden;
  box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  &:active {
    transform: scale(0.98);
    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
  }
}
.activity-item {
  display: flex;
  padding: 24rpx;
}
.cover-container {
  position: relative;
  width: 200rpx;
  height: 200rpx;
  border-radius: 12rpx;
  overflow: hidden;
  margin-right: 24rpx;
  flex-shrink: 0;
}
.activity-cover {
  width: 100%;
  height: 100%;
  border-radius: 12rpx;
}
.text-cover {
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
  color: #fff;
  font-size: 28rpx;
  padding: 16rpx;
  line-height: 1.4;
}
.activity-info {
  flex: 1;
  display: flex;
  flex-direction: column;
}
.info-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 16rpx;
}
.activity-title {
  flex: 1;
  font-size: 32rpx;
  color: #333;
  font-weight: 500;
  margin-right: 16rpx;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  line-height: 1.4;
}
.activity-meta {
  font-size: 24rpx;
  color: #999;
  margin-bottom: 16rpx;
}
.meta-item {
  display: flex;
  align-items: center;
  margin-bottom: 12rpx;
  .u-icon {
    margin-right: 8rpx;
  }
}
.activity-status {
  font-size: 24rpx;
  padding: 6rpx 16rpx;
  border-radius: 20rpx;
  flex-shrink: 0;
  &.signed {
    color: #2979ff;
    background-color: rgba(41, 121, 255, 0.1);
  }
  &.ended {
    color: #909399;
    background-color: rgba(144, 147, 153, 0.1);
  }
  &.canceled {
    color: #fa3534;
    background-color: rgba(250, 53, 52, 0.1);
  }
}
/* 操作按钮 */
.action-container {
  display: flex;
  justify-content: flex-end;
  margin-top: auto;
  padding-top: 16rpx;
}
.cancel-btn {
  background: #fef0f0;
  color: #fa3534;
  border: none;
  border-radius: 40rpx;
  padding: 0 32rpx;
  height: 64rpx;
  line-height: 64rpx;
  font-size: 26rpx;
  transition: all 0.3s ease;
  &::after {
    border: none;
  }
  &.cancel-btn-hover {
    background: #fde2e2;
    transform: scale(0.98);
  }
}
/* 空状态 */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 100rpx 0;
  text-align: center;
  .empty-image {
    width: 300rpx;
    height: 300rpx;
    opacity: 0.6;
    margin-bottom: 40rpx;
  }
  .empty-text {
    font-size: 28rpx;
    color: #999;
    margin-top: 20rpx;
  }
}
</style>