mahjong_group/components/com/index/ReservationPopup.vue
2025-09-29 19:01:18 +08:00

470 lines
14 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>
<uni-popup ref="popup" type="center">
<view class="column center" style="width: 680rpx; background: #F7F7F7; border-radius: 10rpx; padding: 20rpx;">
<text style="">预约信息</text>
<view class="column card" style="width: 100%; height: 180rpx;">
<view class="row lable" style="margin-top: 20rpx; align-items: center; margin-left: 20rpx;">
<text>发起者</text>
<image :src="initiatorInfo.avatarImage || ''"
style="width: 50rpx; height: 50rpx; background-color: antiquewhite; border-radius: 50%; margin-left: 20rpx;"
mode=""></image>
<text style="margin-left: 15rpx;">{{ initiatorInfo.userName || '' }}</text>
</view>
<view class="row lable" style="margin-top: 30rpx; align-items: center; margin-left: 20rpx;">
<text>参与者</text>
<view class="row" v-for="(item, index) in participantList" :key="index"
style="align-items: center;">
<view class="" style="position: relative; width: 50rpx; height: 50rpx; display: flex;">
<image :src="item.avatarImage || ''" @click="openUserPop(item)"
style="width: 50rpx; height: 50rpx; background-color: antiquewhite; border-radius: 50%; margin-left: 20rpx; position: absolute;"
mode=""></image>
<view v-if="item.userBlackStatus === 1" class="center"
style="width: 50rpx; height: 20rpx; background-color: #FFB7B7; position: absolute; left: 40%; top: -10rpx;">
<text style="font-size: 10rpx;">黑名单</text>
</view>
</view>
</view>
</view>
</view>
<view class="column card lable" style="width: 100%;padding-bottom:20rpx;">
<view class="row lable" style="justify-content: space-between; margin: 20rpx;">
<text>开始时间</text>
<text>{{ formatTime(reservationData.start_time) }}</text>
</view>
<view class="row lable" style="justify-content: space-between; margin: 0 20rpx;">
<text>结束时间</text>
<text>{{ formatTime(reservationData.end_time) }}</text>
</view>
<text style="margin:20rpx 20rpx 0;">合计:{{
formatDuration(reservationData.duration_minutes) }}</text>
</view>
<view class="column card lable" style="width: 100%;">
<text style="margin: 20rpx 20rpx 10rpx;">房间号:{{ reservationData.room_name || ''
}}</text>
<text style="margin: 10rpx 20rpx;">人数:{{ reservationData.player_count || 0 }}人</text>
<text style="margin: 10rpx 20rpx;">玩法类型:{{ reservationData.game_type || '' }}</text>
<text style="margin: 10rpx 20rpx;">具体规则:{{ reservationData.game_rule || '' }}</text>
<text style="margin: 10rpx 20rpx 20rpx;">补充信息:{{ reservationData.extra_info || '无' }}</text>
</view>
<view class="column card lable" style="width: 100%; ">
<text style="margin: 20rpx 20rpx 10rpx;">是否禁烟:{{
getSmokingText(reservationData.is_smoking) }}</text>
<text style="margin: 10rpx 20rpx;">性别:{{ getGenderText(reservationData.gender_limit) }}</text>
<text style="margin: 10rpx 20rpx;">年龄范围:{{
getAgeRangeText(reservationData.min_age, reservationData.max_age) }}</text>
<text style="margin: 10rpx 20rpx 20rpx;">信誉:≧{{ reservationData.credit_limit || 0 }}</text>
</view>
<view class="column card lable" style="width: 100%; ">
<text style="margin: 20rpx 20rpx 10rpx; ">鸽子费:{{ reservationData.deposit_fee || 0
}}元</text>
<text
style="margin: 10rpx 20rpx 20rpx; color: #9F9F9F;">组局成功后若有牌友未赴约,其鸽子费平均分给其他牌友。组局成功或失败后鸽子费将全额返还。</text>
</view>
<view class="row" v-if="isAddhandleJoin == 0" style="width: 100%; margin-top: 30rpx;">
<view class="center" @click="close"
style="height: 80rpx; flex: 1; background-color: #9F9F9F; border-radius: 10rpx;">
<text style="font-size: 24rpx; font-weight: 600; color: white;">关闭</text>
</view>
<view class="center" @click="handleJoin"
style="height: 80rpx; flex: 3; background-color: #1989FA; border-radius: 10rpx; margin-left: 20rpx;">
<text style="font-size: 24rpx; font-weight: 600; color: white;">参与组局</text>
</view>
</view>
<view class="row" v-if="isAddhandleJoin == 1" style="width: 100%; margin-top: 30rpx;">
<view class="center" @click="close"
style="height: 80rpx; flex: 1; background-color: #9F9F9F; border-radius: 10rpx;">
<text style="font-size: 24rpx; font-weight: 600; color: white;">关闭</text>
</view>
<view class="center" @click="exitJoin"
style="height: 80rpx; flex: 3; background-color: #1989FA; border-radius: 10rpx; margin-left: 20rpx;">
<text style="font-size: 24rpx; font-weight: 600; color: white;">退出组局</text>
</view>
</view>
<view class="row" v-if="isAddhandleJoin == 2" style="width: 100%; margin-top: 30rpx;">
<view class="center" @click="close"
style="height: 80rpx; flex: 1; background-color: #9F9F9F; border-radius: 10rpx;">
<text style="font-size: 24rpx; font-weight: 600; color: white;">关闭</text>
</view>
<view class="center" @click="cancelJoin"
style="height: 80rpx; flex: 3; background-color: #1989FA; border-radius: 10rpx; margin-left: 20rpx;">
<text style="font-size: 24rpx; font-weight: 600; color: white;">取消组局</text>
</view>
<button @click.stop open-type="share" style=" background-color:#00AC4E;color: #fff; height: 80rpx; flex: 1; background-color:#00AC4E; border-radius: 10rpx;margin-left: 20rpx;" class="center evaluate-btn"
:data-item="reservation">
<text class="evaluate-btn-text">分享</text>
</button>
</view>
<view class="row" v-if="isAddhandleJoin == 3" style="width: 100%; margin-top: 30rpx;">
<view class="center" @click="close"
style="height: 80rpx; flex: 1; background-color: #9F9F9F; border-radius: 10rpx;">
<text style="font-size: 24rpx; font-weight: 600; color: white;">关闭</text>
</view>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { ref, computed } from 'vue'
import { userInfo, isLogin } from '@/common/server/user'
import { showModalConfirm, requestSubscribeMessage, requestPayment } from '@/common/utils'
import { getSubscribeMessage } from '@/common/server/config'
import { usePay } from '@/common/server/interface/user'
import { joinReservation, cancelReservation } from '@/common/server/interface/sq'
// 组件引用
const popup = ref(null)
// 预约数据
const reservationData = ref({})
// 计算属性:发起者信息
const initiatorInfo = computed(() => {
if (reservationData.value.participants && reservationData.value.participants.length > 0) {
return reservationData.value.participants.find(p => p.role === 1) || {}
}
return {}
})
// 计算属性:参与者列表
const participantList = computed(() => {
if (reservationData.value.participants && reservationData.value.participants.length > 0) {
return reservationData.value.participants.filter(p => p.role === 0) || []
}
return []
});
var isAddhandleJoin = ref(0);
// 显示弹窗
const show = (item) => {
console.log("itemitemitemitem", item)
reservationData.value = item || {}
isAddhandleJoin.value = 0;
if (userInfo.value != null) {
var _initiatorInfo = item.participants.find(p => p.role === 1);
if (_initiatorInfo != null) {
if (userInfo.value.id == _initiatorInfo.user_id) {
isAddhandleJoin.value = 2;
}
}
var _joinInfo = item.participants.find(p => p.role === 0);
if (_joinInfo != null) {
if (userInfo.value.id == _joinInfo.user_id) {
isAddhandleJoin.value = 1;
}
}
}
popup.value.open()
}
// 关闭弹窗
const close = () => {
popup.value.close()
}
// 格式化时间
const formatTime = (timeStr) => {
if (!timeStr) return ''
// 将 "2025/09/13 21:09:43" 格式转换为 "2025/09/13 2109"
const date = new Date(timeStr)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}`
}
// 格式化时长
const formatDuration = (minutes) => {
if (!minutes) return '0分钟'
const hours = Math.floor(minutes / 60)
const mins = minutes % 60
if (hours > 0) {
return mins > 0 ? `${hours}小时${mins}分钟` : `${hours}小时`
}
return `${mins}分钟`
}
// 获取禁烟文本
const getSmokingText = (isSmoking) => {
return isSmoking === 1 ? '禁烟' : '不禁烟'
}
// 获取性别限制文本
const getGenderText = (genderLimit) => {
switch (genderLimit) {
case 0:
return '不限'
case 1:
return '男'
case 2:
return '女'
default:
return '不限'
}
}
const getAgeRangeText = (minAge, maxAge) => {
if (minAge == 0 && maxAge == 0) {
return "不限";
}
let s = "";
if (minAge == 0) {
s = "不限";
} else {
s = minAge + "岁";
}
s += " ~ ";
if (maxAge == 0) {
s += "不限";
} else {
s += maxAge + "岁";
}
return s;
}
// 处理加入组局
const handleJoin = async () => {
// 检查登录状态
var isLoginSuccess = await isLogin();
if (!isLoginSuccess) {
close();
uni.navigateTo({
url: '/pages/me/login'
});
return;
}
try {
// 获取订阅消息
var messageId = await getSubscribeMessage(reservationData.value.deposit_fee);
console.log("messageId", messageId);
var subscribeMessage = await requestSubscribeMessage(messageId);
console.log("message", subscribeMessage);
// 显示加载状态
uni.showLoading({
title: '加入中...'
});
// 准备加入数据
const joinData = {
ReservationsId: reservationData.value.id,
important_data: {}
};
var resPay = null;
// 如果有鸽子费,需要先支付
if (reservationData.value.deposit_fee > 0) {
resPay = await usePay(reservationData.value.deposit_fee);
if (resPay == null) {
uni.hideLoading();
uni.showToast({
title: '鸽子费订单创建失败,请重新尝试!',
icon: 'none'
});
return;
}
subscribeMessage.result.paymentId = resPay.paymentId;
}
// 准备重要数据
var important_data = "";
if (subscribeMessage.result != null) {
important_data = JSON.stringify(subscribeMessage.result);
}
joinData.important_data = important_data;
// 调用加入预约接口
const result = await joinReservation(joinData);
uni.hideLoading();
console.log("join result", result);
if (!result.success) {
uni.showToast({
title: result.message,
icon: 'none'
});
return;
}
// 如果有鸽子费,进行支付
if (reservationData.value.deposit_fee > 0 && resPay != null) {
var payRes = await requestPayment(resPay);
if (payRes.success) {
uni.showToast({
title: '鸽子费支付成功,加入组局成功!',
icon: 'success'
});
} else {
uni.showToast({
title: '鸽子费支付失败,请重新加入!',
icon: 'none'
});
await cancelReservation(result.data, "鸽子费支付失败,请重新加入!");
return;
}
} else {
uni.showToast({
title: '加入组局成功!',
icon: 'success'
});
}
// 关闭弹窗
close();
// 触发刷新列表事件
emit('refreshList');
} catch (error) {
uni.hideLoading();
console.error('加入组局失败:', error);
uni.showToast({
title: '加入失败,请重试',
icon: 'none'
});
}
}
//取消组局
const cancelJoin = async () => {
var res = await showModalConfirm('提示', '确定要取消组局吗?')
if (res) {
try {
// 显示加载状态
uni.showLoading({
title: '取消中...'
});
// 调用取消预约接口
const result = await cancelReservation(reservationData.value.id, "发起者取消组局");
uni.hideLoading();
console.log("cancel result", result);
if (result.success) {
uni.showToast({
title: '取消组局成功!',
icon: 'success'
});
// 关闭弹窗
close();
// 触发刷新列表事件
emit('refreshList');
} else {
uni.showToast({
title: result.message,
icon: 'none'
});
}
} catch (error) {
uni.hideLoading();
console.error('取消组局失败:', error);
uni.showToast({
title: '取消失败,请重试',
icon: 'none'
});
}
}
}
//退出组局
const exitJoin = async () => {
var res = await showModalConfirm('提示', '确定要退出组局吗?')
if (res) {
try {
// 显示加载状态
uni.showLoading({
title: '退出中...'
});
// 调用取消预约接口(参与者退出)
const result = await cancelReservation(reservationData.value.id, "参与者退出组局");
uni.hideLoading();
console.log("exit result", result);
if (result.success) {
uni.showToast({
title: '退出组局成功!',
icon: 'success'
});
// 关闭弹窗
close();
// 触发刷新列表事件
emit('refreshList');
} else {
uni.showToast({
title: result.message,
icon: 'none'
});
}
} catch (error) {
uni.hideLoading();
console.error('退出组局失败:', error);
uni.showToast({
title: '退出失败,请重试',
icon: 'none'
});
}
}
}
// 打开用户信息弹窗
const openUserPop = (user) => {
// 触发父组件的用户信息弹窗事件
emit('openUserPop', user)
}
// 定义事件
const emit = defineEmits(['openUserPop', 'refreshList'])
// 暴露方法给父组件
defineExpose({
show,
close
})
</script>
<style lang="scss" scoped>
.column {
display: flex;
flex-direction: column;
}
.row {
display: flex;
flex-direction: row;
}
.center {
display: flex;
justify-content: center;
align-items: center;
}
.card {
background: #FFFFFF;
box-shadow: 0rpx 0rpx 10rpx 4rpx rgba(0, 0, 0, 0.25);
border-radius: 30rpx 30rpx 30rpx 30rpx;
margin-top: 20rpx;
}
.lable {
font-family: PingFang SC, PingFang SC;
font-size: 22rpx;
}
</style>