|
|
<template> <view class="activity-list"> <!-- tab栏 --> <view class="tab-box"> <view class="tab-item" v-for="(tab,idx) in tabList" :key="idx" :class="{active:activeTab === idx}" @click="switchTab(idx)" > {{tab.label}} </view> </view>
<scroll-view scroll-y @scrolltolower="onScrollLower" lower-threshold="150" class="scroll-view" > <view v-if="loading" class="loading-box"> <text class="loading-text">加载中...</text> </view>
<!-- 左滑组件 uni-swipe-action --> <!-- :disabled="!(item.status === '未开始' && !item.isCancel)" --> <uni-swipe-action> <uni-swipe-action-item v-for="item in activityList" :key="item.signId" > <!-- 左侧主体内容 --> <view class="activity-item" @click="toActivityDetail(item)"> <view class="activity-info"> <view class="title-row"> <text class="title">{{ item.title }}</text> <text class="status-tag" :class="getStatusClass(item.status)"> {{ item.status }} </text> </view> <view class="item-info"> <text class="label">开始时间</text> <text class="value">{{ formatTime(item.start_time) }}</text> </view> <view class="item-info"> <text class="label">结束时间</text> <text class="value">{{ formatTime(item.end_time) }}</text> </view> <view class="item-info"> <text class="label">预约人</text> <text class="value">{{ item.signName }}</text> </view> <view class="item-info"> <text class="label">参加人数</text> <text class="value">{{ item.signNum }}</text> </view> <!-- 未开始 && 未取消预约 才显示取消预约按钮 --> <view class="btn-box" v-if="item.status === '未开始' && !item.isCancel"> <button class="activity-btn" type="primary" @click.stop="cancelAppoint(item)"> 取消预约 </button> </view> </view> </view>
<!-- 右滑出来的按钮插槽 --> <!-- v-if="!(item.status === '未开始' && !item.isCancel)" --> <!-- v-if="item.status !== '进行中'" --> <template #right > <view class="swipe-right-wrap"> <view class="swipe-btn swipe-btn-delete" @click.stop="deleteSignActivity(item)"> <uni-icons type="trash" size="24"></uni-icons> </view> </view> </template> </uni-swipe-action-item> </uni-swipe-action>
<!-- 非loading,有数据,是最后一页,哪怕仅1条也展示没有更多 --> <view v-if="!loading && noMore && activityList.length > 0" class="nomore-box"> <text class="nomore-text">没有更多数据了</text> </view>
<view class="empty-box" v-if="!loading && activityList.length === 0"> <uni-icons type="empty" size="80" color="#ccc"></uni-icons> <text class="empty-text">暂无预约活动~</text> </view> </scroll-view> </view></template>
<script>import config from '@/utils/config';import { FetchSessionKey } from '@/api/user.js';import { FetchMySignActivityInfo, FetchCancelSignActivity,FetchDeleteSignActivity } from '@/api/activity.js'export default { data() { return { tabList:[ {label:'全部',value:null}, {label:'预约成功',value:false}, {label:'已取消',value:true} ], activeTab:0, activityList: [], baseUrl: config.baseUrl, refreshing: false, loading: false, noMore: false, page: 0, pageSize: 20, sessionKey: '', openid: '' }; }, async onLoad() { await this.getSessionKey(); this.getActivityList(true);
uni.$on('refreshMySignList',()=>{ this.getActivityList(true) }) }, onUnload(){ uni.$off('refreshMySignList') }, onPullDownRefresh() { if(this.loading){ uni.stopPullDownRefresh() return } this.getActivityList(true).finally(()=>{ uni.stopPullDownRefresh() }) }, methods: { switchTab(index){ this.activeTab = index this.page = 0 this.noMore = false this.activityList = [] this.getActivityList(true) },
formatTime(timestamp) { if (!timestamp) return ''; const date = new Date(timestamp); const y = date.getFullYear(); const m = String(date.getMonth() + 1).padStart(2, '0'); const d = String(date.getDate()).padStart(2, '0'); const h = String(date.getHours()).padStart(2, '0'); const min = String(date.getMinutes()).padStart(2, '0'); return `${y}-${m}-${d} ${h}:${min}`; }, getStatusClass(status) { if (status === '未开始') return 'status-0'; if (status === '进行中') return 'status-1'; if (status === '已结束') return 'status-2'; return ''; }, async getSessionKey() { return new Promise((resolve) => { uni.login({ success: async (loginRes) => { console.log('loginRes', loginRes) if (loginRes.code) { const res = await FetchSessionKey({ libcode: config.LIB_CODE, code: loginRes.code }); if (res.code === 200) { this.sessionKey = res.data.session_key; this.openid = res.data.openid; } } resolve() }, fail: () => { resolve() } }); }) }, async getActivityList(isRefresh = false) { if (!this.openid) { uni.showToast({ title: '获取用户信息失败', icon: 'none' }) return } if (this.loading) return;
if (isRefresh) { this.page = 0; this.noMore = false; this.refreshing = true; } else { if (this.noMore) return; }
this.loading = true; const currentTabItem = this.tabList[this.activeTab] const params = { openId: this.openid, page: this.page, pageSize: this.pageSize } if(currentTabItem.value !== null){ params.isCancel = currentTabItem.value }
try { const res = await FetchMySignActivityInfo(params); console.log('FetchMySignActivityInfo返回结果', res) if (res.code === 200) { const pageData = res.data; const list = pageData.content || [];
// ==========新增:统一处理图片地址,和第一个接口逻辑保持一致==========
list.forEach(item => { if(item.cover_image){ const imgId = encodeURIComponent(item.cover_image); item.cover_image = `${this.baseUrl}/files/${imgId}`; }else{ item.cover_image = '' } })
const currentPage = pageData.number ?? 0; const totalPage = pageData.totalPages ?? 0; this.noMore = currentPage >= totalPage - 1;
if (isRefresh) { this.activityList = list; } else { this.activityList = [...this.activityList, ...list]; } } else { if (isRefresh) this.activityList = []; } } catch (err) { console.error('请求活动接口异常', err) uni.showToast({ title: '网络请求失败', icon: 'none' }) } finally { this.refreshing = false; this.loading = false; } },
onRefresh() { if(this.loading) return this.getActivityList(true); },
onScrollLower() { if(this.loading || this.noMore) return; this.page += 1; this.getActivityList(false); },
cancelAppoint(item) { uni.showModal({ title: '提示', content: `确定要取消活动【${item.title}】的预约吗?`, success: async (res) => { if (res.confirm) { try { const apiRes = await FetchCancelSignActivity({ id: item.signId }) if(apiRes.code === 200){ uni.showToast({ title: '取消成功', icon: 'success' }) this.getActivityList(true) }else{ uni.showToast({ title: apiRes.message || '取消失败', icon: 'none' }) } }catch(err){ console.error('取消预约失败', err) uni.showToast({ title: '网络异常,取消失败', icon: 'none' }) } } } }) }, deleteSignActivity(item){ uni.showModal({ title: '提示', content: `确定要删除活动【${item.title}】的预约吗?`, success: async (res) => { if (res.confirm) { try { const apiRes = await FetchDeleteSignActivity({ id: item.signId }) if(apiRes.code === 200){ uni.showToast({ title: '删除成功', icon: 'success' }) this.getActivityList(true) }else{ uni.showToast({ title: apiRes.message || '删除失败', icon: 'none' }) } }catch(err){ console.error('删除预约失败', err) uni.showToast({ title: '网络异常,删除失败', icon: 'none' }) } } } }) }, toActivityDetail(item) { console.log('toActivityDetail',item); uni.navigateTo({ url: "/subpkg/pages/activity-detail/activity-detail?item=" + encodeURIComponent(JSON.stringify(item)) }); } },};</script>
<style lang="scss" scoped>.activity-list { padding: 0; height: 100vh; box-sizing: border-box; background-color: #f5f5f5; display:flex; flex-direction:column;}
.tab-box { display: flex; background-color: #fff; margin-bottom: 10px; position: sticky; top: 0; z-index: 99;}.tab-item{ flex:1; text-align:center; padding:12px 0; font-size:14px; color:#666; position:relative;}.tab-item.active{ color:#01a4fe; font-weight:bold;}.tab-item.active::after{ content:""; position:absolute; bottom:0; left:50%; transform:translateX(-50%); width:28px; height:2px; background:#01a4fe;}
.scroll-view { flex:1; height: 0; padding: 0 12px; box-sizing: border-box;}
.loading-box{ padding:20px 0; text-align:center;}.loading-text{ font-size:14px; color:#999;}
.nomore-box{ padding:15px 0; text-align:center;}.nomore-text{ font-size:13px; color:#999;}
.empty-box { height: calc(100vh - 180px); display: flex; flex-direction: column; align-items: center; justify-content: center;}.empty-text { margin-top: 16px; font-size: 14px; color: #999;}
.activity-item { background: #fff; border-radius: 12px; overflow: hidden; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06); .activity-info { display: flex; flex-direction: column; align-items: flex-start; padding: 16px;
.title-row{ width:100%; display:flex; justify-content: space-between; align-items:flex-start; margin-bottom:12px; } .title { flex: 1; font-size: 16px; font-weight: bold; color: #222; } .item-info { display: flex; justify-content: space-between; padding: 4px 0; font-size: 14px; width:100%; .label { color: #666; width: 72px; } .value { color: #333; flex: 1; text-align: right; } } }}.btn-box { width: 100%; display: flex; justify-content: flex-end; margin-top: 10px; padding-top: 10px; border-top: 1px solid #e5e5e5; }
.status-tag { padding: 2px 8px; border-radius: 4px; font-size: 12px;}.status-tag.status-0 { color: #01a4fe; background: #e6f7ff;}.status-tag.status-1 { color: #00b42a; background: #e6ffed;}.status-tag.status-2 { color: #999; background: #f5f5f5;}
/* 👉 插槽外层容器:占满高度,垂直水平居中 */.swipe-right-wrap { width: 80px; height: calc(100% - 20px); display: flex; align-items: center; /* 垂直居中 */ justify-content: center; /*水平居中*/ // background-color: #f5222d;
}
/* 圆形按钮,不再写height:100%,固定大小做圆圈 */.swipe-btn { width: 48px; height: 48px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #ffffff !important;}
.swipe-btn-delete { background-color: #f5222d;}
/* 深度修改uni-icons图标颜色,scoped必须加 ::v‑deep */::v-deep .swipe-btn .uni-icons { color: #ffffff !important;}
</style>
|