| | |
| | | <template> |
| | | <view class="wrapper"> |
| | | <u-navbar :is-back="true" title="活动"> |
| | | </u-navbar> |
| | | |
| | | |
| | | <view style="height: 100rpx"></view> |
| | | <!-- 内容区域 --> |
| | | <scroll-view scroll-y class="content" :style="{ paddingBottom: safeAreaInsets.bottom + 'px' }"> |
| | | <scroll-view scroll-y class="content" style="height: 40vh;" @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 === 'image'" :src="item.url" 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 === 'video'" :src="item.url" class="media" controls :poster="item.poster" :data-item="item" |
| | | @play="handleVideoPlay" :style="{ height: item.height + 'px' }"></video> |
| | | |
| | | <!-- 文字类型 --> |
| | | <view v-if="item.type === '文字'" class="text-content"> |
| | | <view v-if="item.type === 'text'" 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 { |
| | | getActivityReportList, |
| | | } from '@/api/activity.js'; |
| | | import '@/components/uview-components/uview-ui'; |
| | | import {getActivityReportList} from '@/api/activity.js'; |
| | | export default { |
| | | data() { |
| | | return { |
| | |
| | | ], // 双列布局 |
| | | mockData: [], |
| | | colHeight: [0, 0], // 记录各列高度 |
| | | |
| | | baseImageHeight: 300, // 图片基础高度 |
| | | baseVideoHeight: 350, // 视频基础高度 |
| | | baseTextHeight: 120, // 文字基础高度 |
| | | query: { |
| | | pageNumber: 1, |
| | | pageSize: 10, |
| | | }, |
| | | loading: false, // 是否正在加载 |
| | | noMore: false, // 是否没有更多数据 |
| | | total: 0 // 总数据量 |
| | | }; |
| | | }, |
| | | onLoad() { |
| | | this.getActivityList(); |
| | | //获得userId |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 下拉刷新时 |
| | | */ |
| | | onPullDownRefresh() { |
| | | this.query.pageNumber = 1; // 重置页码 |
| | | this.noMore = false; |
| | | this.mockData = []; // 清空数据 |
| | | this.getActivityList(); |
| | | }, |
| | | getActivityList() { |
| | | uni.showLoading({ |
| | | title: '加载中' |
| | | }); |
| | | const mock = []; |
| | | getActivityReportList().then(res => { |
| | | uni.hideLoading(); |
| | | 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) { |
| | | 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: '', |
| | | }); |
| | | } |
| | | const newData = res.data.data.map(value => ({ |
| | | id: value.id, |
| | | type: value.coverType, |
| | | cover: value.cover, |
| | | height: value.coverType === 'image' ? this.baseImageHeight : value.coverType === |
| | | 'video' ? this.baseVideoHeight : this.baseTextHeight, |
| | | title: value.activityName, |
| | | content: value.activityContent, |
| | | poster: '', |
| | | url: value.url |
| | | })); |
| | | |
| | | // 更新总数据量 |
| | | 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(); |
| | | }); |
| | | } |
| | | this.mockData = mock; |
| | | 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() { |
| | |
| | | |
| | | 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) { |
| | |
| | | const ratio = height / width; |
| | | const item = e.currentTarget.dataset.item; |
| | | |
| | | if (!item) { |
| | | console.error('无法获取图片项数据', e); |
| | | return; |
| | | } |
| | | // 重新计算实际显示高度 |
| | | 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; |
| | | 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> |