feat(event): 添加酒局报名审核功能并优化前端组件
- 新增 ApproveEvent API 接口用于审核报名用户
- 在 EventCard 组件中显示待审核状态(⏳ 待审核)
- 实现酒局发起人审核报名用户的完整流程
- 添加用户协议和隐私政策页面
- 优化 FeedCard 组件中的图片展示逻辑
- 修复连续打卡天数计算问题
- 更新 HaveADrink SDK 至 1.0.15 版本
This commit is contained in:
@@ -80,11 +80,32 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 参与者 -->
|
||||
<view class="evt-participants" v-if="event.participants && event.participants.length">
|
||||
<text class="evt-section-label">已报名 ({{ event.participants.length }})</text>
|
||||
<!-- 待审核报名(发起人可见,可同意/拒绝) -->
|
||||
<view class="evt-pending" v-if="event.isOrganizer && pendingParticipants.length">
|
||||
<text class="evt-section-label">待审核报名 ({{ pendingParticipants.length }})</text>
|
||||
<view class="pending-item" v-for="p in pendingParticipants" :key="p.id">
|
||||
<view class="pending-user">
|
||||
<view class="evt-person-avatar">
|
||||
<text class="evt-person-text">{{ p.nickname[0] }}</text>
|
||||
</view>
|
||||
<text class="pending-name">{{ p.nickname }}</text>
|
||||
</view>
|
||||
<view class="pending-actions">
|
||||
<view class="pending-btn pending-approve" @click="handleApprove(p, 2)">
|
||||
<text class="pending-btn-text">✓ 同意</text>
|
||||
</view>
|
||||
<view class="pending-btn pending-reject" @click="handleApprove(p, 3)">
|
||||
<text class="pending-btn-text">✕ 拒绝</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 参与者(已通过审核) -->
|
||||
<view class="evt-participants" v-if="approvedParticipants.length">
|
||||
<text class="evt-section-label">已报名 ({{ approvedParticipants.length }})</text>
|
||||
<view class="evt-people-grid">
|
||||
<view class="evt-person" v-for="p in event.participants" :key="p.id">
|
||||
<view class="evt-person" v-for="p in approvedParticipants" :key="p.id">
|
||||
<view class="evt-person-avatar">
|
||||
<text class="evt-person-text">{{ p.nickname[0] }}</text>
|
||||
</view>
|
||||
@@ -97,21 +118,33 @@
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="evt-action-bar" v-if="event">
|
||||
<button
|
||||
v-if="canJoin && !event.isJoined && !event.isOrganizer"
|
||||
v-if="canJoin && !event.isOrganizer && (!event.isJoined || myApprove === 3)"
|
||||
class="evt-btn evt-btn-primary"
|
||||
@click="handleJoin"
|
||||
>
|
||||
报名参加
|
||||
{{ myApprove === 3 ? '重新报名' : '报名参加' }}
|
||||
</button>
|
||||
<view
|
||||
v-if="event.isJoined && myApprove === 1 && !event.isOrganizer"
|
||||
class="evt-ended-tip"
|
||||
>
|
||||
<text class="evt-ended-text">⏳ 已提交报名,等待发起人审核</text>
|
||||
</view>
|
||||
<view
|
||||
v-if="event.isJoined && myApprove === 3 && !event.isOrganizer"
|
||||
class="evt-ended-tip"
|
||||
>
|
||||
<text class="evt-ended-text">报名未通过,可重新申请</text>
|
||||
</view>
|
||||
<button
|
||||
v-if="canJoin && event.isJoined && !event.isOrganizer"
|
||||
v-if="canJoin && event.isJoined && myApprove !== 3 && !event.isOrganizer"
|
||||
class="evt-btn evt-btn-ghost"
|
||||
@click="handleQuit"
|
||||
>
|
||||
取消报名
|
||||
</button>
|
||||
<button
|
||||
v-if="statusKey === 'ongoing' && event.isJoined"
|
||||
v-if="statusKey === 'ongoing' && event.isJoined && myApprove === 2"
|
||||
class="evt-btn evt-btn-primary"
|
||||
@click="handleCheckIn"
|
||||
>
|
||||
@@ -134,7 +167,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { GetEventDetail, JoinEvent, QuitEvent, CheckInEvent, DeleteEvent } from '../../common/api'
|
||||
import { GetEventDetail, JoinEvent, QuitEvent, CheckInEvent, DeleteEvent, ApproveEvent } from '../../common/api'
|
||||
import wsManager from '../../common/websocket'
|
||||
import themeMixin from '../../common/theme-mixin'
|
||||
|
||||
export default {
|
||||
@@ -159,6 +193,23 @@ export default {
|
||||
canJoin() {
|
||||
return this.statusKey === 'open' || this.statusKey === 'upcoming'
|
||||
},
|
||||
/** 我的报名审核状态:1=待审核 2=通过 3=拒绝(兼容后端未返回字段的旧数据) */
|
||||
myApprove() {
|
||||
if (!this.event) return 0
|
||||
const raw = this.event.approveStatus
|
||||
if (raw !== undefined && raw !== null && raw !== '') return Number(raw)
|
||||
return this.event.isJoined ? 2 : 0
|
||||
},
|
||||
/** 待审核报名列表(发起人审核用) */
|
||||
pendingParticipants() {
|
||||
if (!this.event || !this.event.participants) return []
|
||||
return this.event.participants.filter(p => Number(p.approveStatus) === 1)
|
||||
},
|
||||
/** 已通过审核的参与者(无字段的旧数据默认视为已通过) */
|
||||
approvedParticipants() {
|
||||
if (!this.event || !this.event.participants) return []
|
||||
return this.event.participants.filter(p => Number(p.approveStatus) !== 1)
|
||||
},
|
||||
statusLabel() {
|
||||
const map = { open: '报名中', upcoming: '待开始', ongoing: '进行中', ended: '已结束' }
|
||||
return map[this.statusKey] || '报名中'
|
||||
@@ -224,11 +275,46 @@ export default {
|
||||
async handleJoin() {
|
||||
try {
|
||||
await JoinEvent({ eventId: this.eventId })
|
||||
this.event.isJoined = true
|
||||
this.event.joined++
|
||||
uni.showToast({ title: '报名成功 🎉', icon: 'none' })
|
||||
uni.showToast({ title: '申请已提交,等待发起人审核', icon: 'none', duration: 2500 })
|
||||
// 后端不会自动推送通知,前端主动给发起人发一条私信,
|
||||
// 使其在消息列表(chat-list)看到未读提醒
|
||||
this.notifyOrganizer()
|
||||
this.loadDetail()
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '报名失败', icon: 'none' })
|
||||
// httpRequest 已全局弹错误提示
|
||||
}
|
||||
},
|
||||
/** 通过 WebSocket 给发起人发送报名提醒私信 */
|
||||
notifyOrganizer() {
|
||||
const organizer = this.event && this.event.organizer
|
||||
if (!organizer || organizer.id === undefined || organizer.id === null) return
|
||||
wsManager.send('chat', {
|
||||
receiverId: organizer.id,
|
||||
content: `我报名了你的酒局「${this.event.title}」,请审核 🍻`,
|
||||
msgType: 'text',
|
||||
clientMesgId: 'msg_' + Date.now()
|
||||
})
|
||||
},
|
||||
/** 发起人审核报名:approve 2=同意 3=拒绝 */
|
||||
async handleApprove(user, approve) {
|
||||
try {
|
||||
await ApproveEvent({ eventId: this.eventId, userId: user.id, approve })
|
||||
uni.showToast({ title: approve === 2 ? '已同意报名' : '已拒绝报名', icon: 'none' })
|
||||
// 审核结果私信通知报名用户
|
||||
if (user && user.id !== undefined && user.id !== null) {
|
||||
const resultText = approve === 2
|
||||
? `你报名的酒局「${this.event.title}」已通过审核 🎉`
|
||||
: `很遗憾,你报名的酒局「${this.event.title}」未通过审核`
|
||||
wsManager.send('chat', {
|
||||
receiverId: user.id,
|
||||
content: resultText,
|
||||
msgType: 'text',
|
||||
clientMesgId: 'msg_' + Date.now()
|
||||
})
|
||||
}
|
||||
this.loadDetail()
|
||||
} catch (e) {
|
||||
// httpRequest 已全局弹错误提示
|
||||
}
|
||||
},
|
||||
async handleQuit() {
|
||||
@@ -241,11 +327,10 @@ export default {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
await QuitEvent({ eventId: this.eventId })
|
||||
this.event.isJoined = false
|
||||
this.event.joined--
|
||||
uni.showToast({ title: '已取消报名', icon: 'none' })
|
||||
this.loadDetail()
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '操作失败', icon: 'none' })
|
||||
// httpRequest 已全局弹错误提示
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -584,4 +669,70 @@ export default {
|
||||
color: $coral;
|
||||
border: 1rpx solid rgba(255,107,107,0.25);
|
||||
}
|
||||
|
||||
/* 待审核报名 */
|
||||
.evt-pending {
|
||||
margin-bottom: $sp-xl;
|
||||
}
|
||||
|
||||
.pending-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: $sp-md $sp-lg;
|
||||
background: $bg-card;
|
||||
border: 1rpx solid rgba(232,168,56,0.25);
|
||||
border-radius: $radius-lg;
|
||||
margin-bottom: $sp-sm;
|
||||
}
|
||||
|
||||
.pending-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $sp-md;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.pending-name {
|
||||
font-size: $fs-base;
|
||||
font-weight: $fw-medium;
|
||||
color: $text-primary;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pending-actions {
|
||||
display: flex;
|
||||
gap: $sp-sm;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.pending-btn {
|
||||
padding: 10rpx 24rpx;
|
||||
border-radius: $radius-full;
|
||||
|
||||
&:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.pending-approve {
|
||||
background: linear-gradient(135deg, $amber, $amber-deep);
|
||||
}
|
||||
|
||||
.pending-reject {
|
||||
background: rgba(255,107,107,0.1);
|
||||
border: 1rpx solid rgba(255,107,107,0.25);
|
||||
}
|
||||
|
||||
.pending-btn-text {
|
||||
font-size: $fs-sm;
|
||||
font-weight: $fw-bold;
|
||||
color: $text-on-amber;
|
||||
|
||||
.pending-reject & {
|
||||
color: $coral;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user