mahjong_group/components/com/page/reservation-item.vue
2025-09-29 17:26:17 +08:00

288 lines
6.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="reservation-item reservation-box">
<view class="column reservation-inner" @click="openReservationPopup()">
<view class="row title title-row mt-10">
<view class="title">{{ reservation.title || '麻将预约' }}</view>
<view class="status-text" :style="getStatusStyle(reservation.status)">
{{ getStatusText(reservation.status) }}
</view>
</view>
<view class="row row-text row-center mt-10">
<text class="ml-20">{{ formatTimeRange(reservation.start_time, reservation.end_time) }}</text>
</view>
<view class="row row-center mt-10">
<text class="row-text ml-20">{{ reservation.room_name || '' }}</text>
</view>
<view class="row row-center mt-10" style="display: flex;">
<view style="width: 75%;">
<text class="row-text ml-20">已约牌友</text>
<image v-for="(participant, pIndex) in reservation.participants" :key="pIndex"
:src="participant.avatarImage || '/static/default-avatar.png'" class="avatar"
mode="aspectFill" />
</view>
<view>
<view class="center evaluate-btn" v-if="reservation.status == 3" @click.stop="onEvaluate()">
<text class="evaluate-btn-text">牌友评价</text>
</view>
<view class="center evaluate-btn" v-else-if="isQianDaoVisible" @click.stop="onQianDao()">
<text class="evaluate-btn-text">签到</text>
</view>
<button v-else-if="reservation.status == 0" @click.stop open-type="share"
style=" background-color:#00AC4E;" class="center evaluate-btn" :data-item="reservation">
<text class="evaluate-btn-text">分享</text>
</button>
</view>
</view>
<view class="mt-20" style="height: 10rpx;"></view>
</view>
<!-- 评价弹窗由父页面控制 -->
</view>
</template>
<script setup>
// 仅负责展示与触发评价事件
import { inject, computed } from 'vue'
const props = defineProps({
reservation: {
type: Object,
required: true
}
})
const emit = defineEmits(['evaluate', 'click'])
const onEvaluate = () => {
emit('evaluate', props.reservation)
// openEvaluatePop?.(props.reservation)
}
const onQianDao = () => {
console.log(" props.reservation", props.reservation);
emit('qianDao', props.reservation)
// TODO: 实现签到逻辑
}
const onfengxiao = () => {
console.log(" props.onfengxiao", props.reservation);
return false;
// TODO: 实现签到逻辑
}
// 计算属性:判断是否显示签到按钮
const isQianDaoVisible = computed(() => {
const item = props.reservation;
console.log("计算签到按钮显示状态", item);
// 检查预约状态1-待开始2-进行中
if (item.status !== 1) {
return false;
}
// 检查用户权限role为1表示有权限
if (item.role !== 1) {
return false;
}
// 检查开始时间是否存在
if (!item.start_time) {
return false;
}
// 检查结束时间是否存在
if (!item.end_time) {
return false;
}
const now = new Date();
const startTime = new Date(item.start_time);
const endTime = new Date(item.end_time);
const tenMinutes = 10 * 60 * 1000; // 10分钟的毫秒数
// 计算时间差
const timeToStart = startTime.getTime() - now.getTime();
const timeToEnd = endTime.getTime() - now.getTime();
// 签到条件:
// 1. 开始时间前10分钟内可以签到
// 2. 在结束时间内也可以签到
// 3. 结束时间外无法签到
const canSignBeforeStart = timeToStart <= tenMinutes && timeToStart > 0; // 开始前10分钟内
const canSignDuringEvent = timeToStart <= 0 && timeToEnd > 0; // 活动进行中(开始后,结束前)
return canSignBeforeStart || canSignDuringEvent;
})
const formatTimeRange = (startTime, endTime) => {
if (!startTime || !endTime) return '时间未设置'
const start = new Date(startTime)
const end = new Date(endTime)
const startStr = `${start.getFullYear()}-${String(start.getMonth() + 1).padStart(2, '0')}-${String(start.getDate()).padStart(2, '0')} ${String(start.getHours()).padStart(2, '0')}:${String(start.getMinutes()).padStart(2, '0')}`
const endStr = `${end.getFullYear()}-${String(end.getMonth() + 1).padStart(2, '0')}-${String(end.getDate()).padStart(2, '0')} ${String(end.getHours()).padStart(2, '0')}:${String(end.getMinutes()).padStart(2, '0')}`
return `${startStr} ~ ${endStr}`
}
const getStatusText = (status) => {
const map = { 0: '组局中', 1: '待开始', 2: '进行中', 3: "已结束", 4: "已取消" }
return map[status] || '未知状态'
}
const getStatusStyle = (status) => {
const map = { 0: 'color: #999', 1: 'color: #1989FA', 2: 'color: #1989FA', 3: 'color: #999', 4: 'color: #999' }
return map[status] || 'color: #999'
}
const openReservationPopup = () => {
emit('click', props.reservation)
// openReservationPopupf?.(props.reservation)
}
</script>
<style lang="scss" scoped>
.reservation-item {
background: linear-gradient(180deg, #D7F0DD 0%, #FFFFFF 100%);
box-shadow: 0rpx 0rpx 10rpx 0rpx rgba(0, 0, 0, 0.25);
border-radius: 46rpx 46rpx 46rpx 46rpx;
transition: transform 0.2s;
&:active {
transform: scale(0.98);
}
}
.reservation-box {
width: 100%;
border-radius: 25rpx;
background-color: #F2F3F5;
}
.reservation-inner {
width: 95%;
margin: 20rpx auto 20rpx;
}
.title {
font-family: PingFang SC, PingFang SC;
font-weight: 500;
font-size: 35rpx;
color: #322A2A;
text-align: left;
font-style: normal;
text-transform: none;
}
.title-row {
justify-content: space-between;
}
.row-text {
font-family: PingFang SC, PingFang SC;
font-weight: 400;
font-size: 30rpx;
color: #575757;
text-align: left;
font-style: normal;
text-transform: none;
}
.row-center {
align-items: center;
}
.mt-20 {
margin-top: 20rpx;
}
.mt-10 {
margin-top: 10rpx;
}
.ml-20 {
margin-left: 20rpx;
}
.avatar {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
margin-right: 20rpx;
}
.status-text {
font-size: 24rpx;
}
.evaluate-btn {
width: 120rpx;
height: 30px;
background-color: #1989FA;
border-radius: 10rpx;
margin-left: auto;
}
.evaluate-btn-text {
font-size: 24rpx;
color: #FFFFFF;
}
.popup-container {
width: 650rpx;
background-color: #FFFFFF;
border-radius: 10rpx;
}
.popup-inner {
width: 90%;
margin: 0 auto 0;
}
.popup-title {
font-size: 26rpx;
margin-top: 30rpx;
text-align: center;
}
.popup-desc {
font-size: 24rpx;
margin-top: 20rpx;
}
.panel-box {
width: 100%;
background-color: #F2F3F5;
border-radius: 10rpx;
padding-top: 20rpx;
padding-bottom: 20rpx;
margin-top: 20rpx;
}
.down-icon {
width: 40rpx;
height: 40rpx;
margin-left: auto;
margin-right: 20rpx;
}
.font-24 {
font-size: 24rpx;
}
.action-row {
margin-top: 50rpx;
height: 80rpx;
color: #FFFFFF;
font-size: 28rpx;
}
.action-btn {
flex: 1;
background-color: #1989FA;
border-radius: 10rpx;
}
.info-tip {
font-size: 24rpx;
text-align: center;
margin-top: 20rpx;
margin-bottom: 20rpx;
}
</style>