绿满眶商城微信小程序-uniapp
pages/mine/activity/reportActivity.vue
@@ -1,47 +1,73 @@
<template>
   <view class="wrapper">
      <u-navbar :is-back="true" title="活动">
      </u-navbar>
      <!-- 内容区域 -->
      <scroll-view scroll-y class="content" :style="{ paddingBottom: safeAreaInsets.bottom + 'px' }">
      <scroll-view scroll-y class="content" style="height: 100vh;"   @scrolltolower="loadMore"
          :lower-threshold="100"
         >
         <view class="waterfall">
            <view class="column" v-for="(column, index) in columns" :key="index">
               <!-- 遍历每列内容 -->
               <view class="item" v-for="(item, idx) in column" :key="item.id" @click="handleItemClick(item)">
                  <!-- 图片类型 -->
                  <image v-if="item.type === '图片'" :src="item.content" mode="widthFix" class="media"
                     @load="imageLoad" :data-item="item" />
                  <image v-if="item.type === '图片'" :src="getUrl(item.cover)" mode="widthFix" class="media"
                     @load="imageLoad" :data-item="item" :style="{ height: item.height + 'px' }" />
                  <!-- 视频类型 -->
                  <video v-if="item.type === '视频'" :src="item.content" class="media" controls
                     :poster="item.poster" @play="handleVideoPlay"></video>
                  <video v-if="item.type === '视频'" :src="getUrl(item.cover)" class="media" controls
                     :poster="item.poster" @play="handleVideoPlay"
                     :style="{ height: item.height + 'px' }"></video>
                  <!-- 文字类型 -->
                  <view v-if="item.type === '文字'" class="text-content">
                     <text class="title">{{ item.cover }}</text>
                  </view>
                  <text class="title">{{ item.title }}</text>
               </view>
            </view>
         </view>
      <!--    <view style="height: 150rpx;"></view> -->
          <!-- 改进的加载更多提示 -->
               <view class="load-more">
                 <u-loadmore
                   v-if="mockData.length > 0"
                   :status="loading ? 'loading' : noMore ? 'nomore' : 'loadmore'"
                   :load-text="{
                     loadmore: '上拉加载更多',
                     loading: '正在加载',
                     nomore: '没有更多了'
                   }"
                 />
               </view>
              <view style="height:150rpx">
              </view>
      </scroll-view>
      <custom-tabbar bgColor="#ffffff" selected="activity"></custom-tabbar>
   </view>
</template>
<script>
   import {
      changeCollect
   } from '@/api/collect.js'
   import {
      getFilePreviewUrl
   } from '@/api/common.js'
   import UButton from '@/uview-components/uview-ui/components/u-button/u-button.vue';
   import UImage from '@/uview-components/uview-ui/components/u-image/u-image.vue';
   import ULoadmore from '@/uview-components/uview-ui/components/u-loadmore/u-loadmore.vue'
   import {
      getActivityReportList,
   } from '@/api/activity.js';
   export default {
      components: {
         UImage,
         UButton,
         ULoadmore
      },
      data() {
         return {
            columns: [
@@ -50,45 +76,94 @@
            ], // 双列布局
            mockData: [],
            colHeight: [0, 0], // 记录各列高度
            baseImageHeight: 300, // 图片基础高度
            baseVideoHeight: 350, // 视频基础高度
            baseTextHeight: 120, // 文字基础高度
            query: {
               pageNumber: 1,
               pageSize: 8,
            },
            loading: false, // 是否正在加载
            noMore: false, // 是否没有更多数据
            total: 0 // 总数据量
         };
      },
      onLoad() {
         this.getActivityList();
         //获得userId
      },
      methods: {
         getUrl(params) {
            getFilePreviewUrl(params).then(res => {
               return res.data.data
            })
         },
         /**
          * 下拉刷新时
          */
         onPullDownRefresh() {
            this.query.pageNumber = 1; // 重置页码
            this.noMore = false;
            this.mockData = []; // 清空数据
            this.getActivityList();
         },
         getActivityList() {
            uni.showLoading({
               title: '加载中'
            });
            const mock = [];
            getActivityReportList().then(res => {
               uni.hideLoading();
               if (res.statusCode === 200) {
                  for (const value of res.data.data) {
                     const type = value.coverType;
                     const baseHeight = type === '文字' ? 120 : 350;
                     mock.push({
                        id: value.id,
                        type: type,
                        cover: value.cover,
                        height: baseHeight,
                        title: value.activityName,
                        content: value.activityContent,
                        poster: '',
                     });
                  }
               }
               this.mockData = mock;
               this.layoutItems();
            })
         loadMore() {
           // 显示加载状态
           this.loading = true;
           // 延迟执行让UI有反应时间
           setTimeout(() => {
             this.query.pageNumber += 1;
             this.getActivityList();
           }, 300);
         },
         async getActivityList() {
           try {
             const res = await getActivityReportList(this.query);
             this.loading = false;
             if (res.statusCode === 200) {
               const newData = res.data.data.map(value => ({
                 id: value.id,
                 type: value.coverType,
                 cover: value.cover,
                 height: value.coverType === '图片' ? this.baseImageHeight :
                        value.coverType === '视频' ? this.baseVideoHeight : this.baseTextHeight,
                 title: value.activityName,
                 content: value.activityContent,
                 poster: '',
               }));
               // 更新总数据量
               this.total = res.data.total || 0;
               // 追加或替换数据
               this.mockData = this.query.pageNumber === 1
                 ? newData
                 : [...this.mockData, ...newData];
               // 判断是否还有更多数据
               this.noMore = newData.length < this.query.pageSize ||
                            this.mockData.length >= this.total;
               // 布局更新
               this.$nextTick(() => {
                 this.layoutItems();
               });
             }
           } catch (error) {
             console.error('加载失败:', error);
             // 失败时回退页码
             if (this.query.pageNumber > 1) {
               this.query.pageNumber -= 1;
             }
           } finally {
             this.loading = false;
             uni.hideLoading();
             uni.stopPullDownRefresh();
           }
         },
         // 图片加载完成回调
         layoutItems() {
@@ -100,19 +175,9 @@
            this.mockData.forEach(item => {
               const minIndex = this.colHeight.indexOf(Math.min(...this.colHeight));
               this.columns[minIndex].push(item);
               // 文字类型不需要计算图片高度
               if (item.type !== 'text') {
                  this.colHeight[minIndex] += item.height + 40; // 40为间距
               } else {
                  // 文字类型固定高度计算(根据字体大小和行数)
                  const lineHeight = 40; // 假设每行40rpx
                  const lines = Math.ceil(uni.getSystemInfoSync().windowWidth / 345 * 0.8); // 响应式行数
                  this.colHeight[minIndex] += lineHeight * lines + 40;
               }
               this.columns[minIndex].push(item); //获得高度更小的 放入元素
               this.colHeight[minIndex] += item.height + 40; // 40为间距
            });
            console.log(this.colHeight)
         },
         // 图片加载回调
         imageLoad(e) {
@@ -124,176 +189,200 @@
            const item = e.currentTarget.dataset.item;
            // 重新计算实际显示高度
            const viewWidth = 345;
            const viewWidth = uni.upx2px(345); // 将rpx转换为px
            const viewHeight = viewWidth * ratio;
            const index = this.columns[0].findIndex(i => i.id === item.id) ||
               this.columns[1].findIndex(i => i.id === item.id);
            if (index !== -1) {
               const colIndex = this.colHeight[0] < this.colHeight[1] ? 0 : 1;
               this.colHeight[colIndex] -= item.height;
               this.colHeight[colIndex] += viewHeight;
               item.height = viewHeight;
            }
            // 更新item高度
            item.height = viewHeight;
            // 重新计算列高度
            this.recalculateColumns();
         },
         // 重新计算列高度
         recalculateColumns() {
            this.colHeight = [0, 0];
            this.columns.forEach((column, colIndex) => {
               column.forEach(item => {
                  this.colHeight[colIndex] += item.height + 40; // 40为间距
               });
            });
         },
         handleItemClick(item) {
            console.log(item)
            uni.navigateTo({
               url: `/pages/mine/activity/detail?id=${item.id}` // 参数通过 URL 传递
            });
         }
      },
      }
   }
</script>
<style lang="scss">
   /* 新增加载更多样式 */
      .load-more {
         padding: 20rpx 0;
         text-align: center;
         color: #999;
         font-size: 26rpx;
         background-color: #f7f8fa;
      }
   .btn-container {
      display: flex;
      justify-content: center;
      align-items: center;
      margin-top: 8px;
      /* 与上方标题保持间距 */
   }
   /* 全局样式优化 */
   .wrapper {
     height: 100vh;
     display: flex;
     flex-direction: column;
     background-color: #f7f8fa;
      height: 100vh;
      display: flex;
      flex-direction: column;
      background-color: #f7f8fa;
   }
   /* 导航栏优化 */
   .u-navbar {
     box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
      box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
   }
   /* 内容区域优化 */
   .content {
     flex: 1;
     overflow: hidden;
     padding: 0 20rpx;
     box-sizing: border-box;
     /* 确保可以滚动 */
     -webkit-overflow-scrolling: touch;
   }
   /* 瀑布流布局优化 */
   .waterfall {
     display: flex;
     padding: 20rpx 0;
     gap: 20rpx;
      display: flex;
      padding: 20rpx 0;
      gap: 20rpx;
   }
   .column {
     flex: 1;
     display: flex;
     flex-direction: column;
     gap: 20rpx;
      flex: 1;
      display: flex;
      flex-direction: column;
      gap: 20rpx;
   }
   /* 卡片项优化 */
   .item {
     background: #fff;
     border-radius: 16rpx;
     overflow: hidden;
     box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
     transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
     &:active {
       transform: scale(0.98);
       box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.12);
     }
      background: #fff;
      border-radius: 16rpx;
      overflow: hidden;
      box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
      transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
      &:active {
         transform: scale(0.98);
         box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.12);
      }
   }
   /* 媒体内容样式 */
   .media {
     width: 100%;
     display: block;
     border-radius: 16rpx 16rpx 0 0;
     background-color: #f5f5f5;
     &[mode="widthFix"] {
       height: auto;
     }
      width: 100%;
      display: block;
      border-radius: 16rpx 16rpx 0 0;
      background-color: #f5f5f5;
      &[mode="widthFix"] {
         height: auto;
      }
   }
   /* 视频特殊样式 */
   video.media {
     object-fit: cover;
      object-fit: cover;
   }
   /* 文字内容样式 */
   .text-content {
     padding: 24rpx;
     background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
     min-height: 160rpx;
     display: flex;
     align-items: center;
     justify-content: center;
     .title {
       color: #fff;
       font-size: 32rpx;
       font-weight: 500;
       line-height: 1.4;
       text-align: center;
       display: -webkit-box;
       -webkit-box-orient: vertical;
       -webkit-line-clamp: 3;
       overflow: hidden;
     }
      padding: 24rpx;
      background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
      min-height: 160rpx;
      display: flex;
      align-items: center;
      justify-content: center;
      .title {
         color: #fff;
         font-size: 32rpx;
         font-weight: 500;
         line-height: 1.4;
         text-align: center;
         display: -webkit-box;
         -webkit-box-orient: vertical;
         -webkit-line-clamp: 3;
         overflow: hidden;
      }
   }
   /* 标题样式优化 */
   .title {
     padding: 20rpx 24rpx;
     font-size: 28rpx;
     color: #333;
     line-height: 1.5;
     display: -webkit-box;
     -webkit-box-orient: vertical;
     -webkit-line-clamp: 2;
     overflow: hidden;
     font-weight: 500;
     &:not(.text-content .title) {
       border-top: 1rpx solid #f0f0f0;
     }
      padding: 20rpx 24rpx;
      font-size: 28rpx;
      color: #333;
      line-height: 1.5;
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 2;
      overflow: hidden;
      font-weight: 500;
      &:not(.text-content .title) {
         border-top: 1rpx solid #f0f0f0;
      }
   }
   /* 加载动画 */
   @keyframes fadeInUp {
     from {
       opacity: 0;
       transform: translateY(20rpx);
     }
     to {
       opacity: 1;
       transform: translateY(0);
     }
      from {
         opacity: 0;
         transform: translateY(20rpx);
      }
      to {
         opacity: 1;
         transform: translateY(0);
      }
   }
   .item {
     animation: fadeInUp 0.4s ease forwards;
     opacity: 0;
     @for $i from 1 through 10 {
       &:nth-child(#{$i}) {
         animation-delay: $i * 0.05s;
       }
     }
      animation: fadeInUp 0.4s ease forwards;
      opacity: 0;
      @for $i from 1 through 10 {
         &:nth-child(#{$i}) {
            animation-delay: $i * 0.05s;
         }
      }
   }
   /* 空状态样式 */
   .empty-state {
     display: flex;
     flex-direction: column;
     align-items: center;
     justify-content: center;
     height: 60vh;
     text-align: center;
     image {
       width: 240rpx;
       opacity: 0.6;
       margin-bottom: 30rpx;
     }
     text {
       color: #c0c4cc;
       font-size: 28rpx;
     }
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      height: 60vh;
      text-align: center;
      image {
         width: 240rpx;
         opacity: 0.6;
         margin-bottom: 30rpx;
      }
      text {
         color: #c0c4cc;
         font-size: 28rpx;
      }
   }
</style>