32
This commit is contained in:
parent
d91eac13d1
commit
e0ba4f5d5b
|
|
@ -6,11 +6,11 @@
|
|||
// 开发环境配置
|
||||
const development = {
|
||||
// API基础URL
|
||||
// baseUrl: 'https://ydsapi.zpc-xy.com',
|
||||
baseUrl: 'https://sqqp.zpc-xy.com',
|
||||
// baseUrl: 'http://1.15.21.245:2401',
|
||||
// host: ['http://1.15.21.245:2401'],
|
||||
baseUrl: 'http://localhost:2015',
|
||||
host: ['http://localhost:2015'],
|
||||
host: ['https://sqqp.zpc-xy.com'],
|
||||
// baseUrl: 'http://192.168.1.21:2016',
|
||||
// host: ['http://192.168.1.21:2016'],
|
||||
imageUrl: 'https://guyu-1308826010.cos.ap-shanghai.myqcloud.com',
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export const getConfig = async () => {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 接口测试反馈
|
||||
* @returns {Promise<any>}
|
||||
|
|
@ -36,6 +36,18 @@ export const interFaceTest = async () => {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务与说明数据
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
export const getServiceDescription = async () => {
|
||||
const res = await request.post("Common/GetServiceDescription");
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传附件通用接口
|
||||
* @param {FormData} formData 上传的表单数据
|
||||
|
|
|
|||
|
|
@ -1,5 +1,21 @@
|
|||
import request from '@/common/system/request';
|
||||
|
||||
/**
|
||||
* 用户是否可以创建预约(仅校验,不创建)
|
||||
* @param {Object} reservationData 创建预约所需参数
|
||||
* @returns {Promise<{canCreate:boolean,message?:string,code?:number}>}
|
||||
*/
|
||||
export const canCreateSQReservation = async (reservationData) => {
|
||||
const res = await request.post("sq/CanCreateSQReservation", reservationData);
|
||||
if (res && typeof res.code !== 'undefined') {
|
||||
if (res.code === 0) {
|
||||
return { canCreate: true, code: 0 };
|
||||
}
|
||||
return { canCreate: false, message: res.msg || '不可创建', code: res.code };
|
||||
}
|
||||
return { canCreate: false, message: '网络异常', code: 500 };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取预约记录
|
||||
* @param {number} index
|
||||
|
|
@ -95,6 +111,23 @@ export const getReputationByUser = async (pageIndex = 1, pageSize = 20) => {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付记录
|
||||
* @param {number} pageIndex 起始页
|
||||
* @param {number} pageSize 页大小
|
||||
* @returns {Promise<any>} 返回支付记录数据
|
||||
*/
|
||||
export const getPaymentRecords = async (pageIndex = 1, pageSize = 20) => {
|
||||
const res = await request.get("sq/GetPaymentRecords", {
|
||||
pageIndex: pageIndex,
|
||||
pageSize: pageSize
|
||||
});
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可预约的房间列表
|
||||
* @param {number} startTime 开始时间 时间戳(秒)
|
||||
|
|
@ -181,18 +214,45 @@ export const cancelReservation = async (reservation_id, cancel_reason) => {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 预约签到接口(仅发起者可操作,且只能签到一次)
|
||||
* @param {Object} checkInData 签到数据
|
||||
* @param {number} checkInData.reservation_id 预约ID
|
||||
* @param {Array} checkInData.attendeds 参会名单(不包含发起者)
|
||||
* @param {number} checkInData.attendeds[].user_id 用户ID
|
||||
* @param {boolean} checkInData.attendeds[].isAttended 是否到场
|
||||
* @returns {Promise<any>} 返回签到结果
|
||||
*/
|
||||
export const checkInReservation = async (checkInData) => {
|
||||
console.log("checkInReservation", checkInData);
|
||||
const res = await request.post("sq/CheckInReservation", checkInData);
|
||||
if (res.code == 0) {
|
||||
return {
|
||||
success: true,
|
||||
message: res.msg || '签到成功'
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
message: res.msg || '签到失败'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export const sqInterface = {
|
||||
canCreateSQReservation,
|
||||
getReservationList,
|
||||
getMyReservation,
|
||||
getMyUseReservation,
|
||||
getEvaluateServices,
|
||||
addEvaluateServices,
|
||||
getReputationByUser,
|
||||
getPaymentRecords,
|
||||
getReservationRoomList,
|
||||
addSQReservation,
|
||||
joinReservation,
|
||||
cancelReservation
|
||||
cancelReservation,
|
||||
checkInReservation
|
||||
}
|
||||
|
|
@ -31,4 +31,5 @@ export const navigateToAccountLogin = (page = "") => {
|
|||
export const navigateToAgreement = (type) => {
|
||||
|
||||
navigateTo(`/pages/other/agreement?type=${type}`);
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
1
components.d.ts
vendored
1
components.d.ts
vendored
|
|
@ -17,6 +17,7 @@ declare module 'vue' {
|
|||
NoData: typeof import('./components/com/page/no-data.vue')['default']
|
||||
NoEmpty: typeof import('./components/com/index/NoEmpty.vue')['default']
|
||||
PickerData: typeof import('./components/com/appointment/picker-data.vue')['default']
|
||||
QiandaoPopup: typeof import('./components/com/page/qiandao-popup.vue')['default']
|
||||
RadioSelect: typeof import('./components/com/appointment/radio-select.vue')['default']
|
||||
ReservationEvaluate: typeof import('./components/com/page/reservation-evaluate.vue')['default']
|
||||
ReservationItem: typeof import('./components/com/page/reservation-item.vue')['default']
|
||||
|
|
|
|||
|
|
@ -1,21 +1,19 @@
|
|||
<template>
|
||||
<uni-popup ref="popup" type="center">
|
||||
<view class="column center"
|
||||
style="width: 680rpx; background-color: white; border-radius: 10rpx; padding: 20rpx;">
|
||||
<view class="column center" style="width: 680rpx; background: #F7F7F7; border-radius: 10rpx; padding: 20rpx;">
|
||||
|
||||
<text style="">预约信息</text>
|
||||
|
||||
<view class="column"
|
||||
style="width: 100%; height: 180rpx; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx;">
|
||||
<view class="row" style="margin-top: 20rpx; align-items: center; margin-left: 20rpx;">
|
||||
<text style="font-size: 22rpx;">发起者</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="font-size: 20rpx; margin-left: 15rpx;">{{ initiatorInfo.userName || '' }}</text>
|
||||
<text style="margin-left: 15rpx;">{{ initiatorInfo.userName || '' }}</text>
|
||||
</view>
|
||||
<view class="row" style="margin-top: 30rpx; align-items: center; margin-left: 20rpx;">
|
||||
<text style="font-size: 22rpx;">参与者</text>
|
||||
<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;">
|
||||
|
|
@ -30,10 +28,6 @@
|
|||
<text style="font-size: 10rpx;">黑名单</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<text
|
||||
style="font-size: 20rpx; margin-left: 30rpx; width: 100rpx; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">{{
|
||||
item.userName || '' }}</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
|
@ -41,25 +35,23 @@
|
|||
</view>
|
||||
|
||||
|
||||
<view class="column"
|
||||
style="width: 100%; height: 180rpx; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx;font-size: 22rpx;">
|
||||
<view class="column card lable" style="width: 100%;padding-bottom:20rpx;">
|
||||
|
||||
<view class="row" style="justify-content: space-between; margin: 20rpx;">
|
||||
<view class="row lable" style="justify-content: space-between; margin: 20rpx;">
|
||||
<text>开始时间</text>
|
||||
<text>{{ formatTime(reservationData.start_time) }}</text>
|
||||
</view>
|
||||
<view class="row" style="justify-content: space-between; margin: 0 20rpx;">
|
||||
<view class="row lable" style="justify-content: space-between; margin: 0 20rpx;">
|
||||
<text>结束时间</text>
|
||||
<text>{{ formatTime(reservationData.end_time) }}</text>
|
||||
</view>
|
||||
|
||||
<text style="margin: 30rpx 20rpx 0;">合计:{{ formatDuration(reservationData.duration_minutes) }}</text>
|
||||
<text style="margin:20rpx 20rpx 0;">合计:{{
|
||||
formatDuration(reservationData.duration_minutes) }}</text>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="column"
|
||||
style="width: 100%; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx; font-size: 22rpx;">
|
||||
<text style="margin: 20rpx 20rpx 10rpx; font-size: 24rpx;">房间号:{{ reservationData.room_name || ''
|
||||
<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>
|
||||
|
|
@ -67,17 +59,17 @@
|
|||
<text style="margin: 10rpx 20rpx 20rpx;">补充信息:{{ reservationData.extra_info || '无' }}</text>
|
||||
</view>
|
||||
|
||||
<view class="column"
|
||||
style="width: 100%; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx; font-size: 22rpx;">
|
||||
<text style="margin: 20rpx 20rpx 10rpx; font-size: 24rpx;">是否禁烟:{{
|
||||
<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"
|
||||
style="width: 100%; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx; font-size: 22rpx;">
|
||||
<text style="margin: 20rpx 20rpx 10rpx; font-size: 24rpx;">鸽子费:{{ reservationData.deposit_fee || 0
|
||||
<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>
|
||||
|
|
@ -113,6 +105,13 @@
|
|||
<text style="font-size: 24rpx; font-weight: 600; color: white;">取消组局</text>
|
||||
</view>
|
||||
</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>
|
||||
|
|
@ -150,7 +149,9 @@ const participantList = computed(() => {
|
|||
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) {
|
||||
|
|
@ -158,6 +159,12 @@ const show = (item) => {
|
|||
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()
|
||||
}
|
||||
|
|
@ -209,7 +216,24 @@ const getGenderText = (genderLimit) => {
|
|||
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 () => {
|
||||
// 检查登录状态
|
||||
|
|
@ -424,4 +448,17 @@ defineExpose({
|
|||
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>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
<view>
|
||||
<slot></slot>
|
||||
<reservation-evaluate ref="_baseEvaluatePop" />
|
||||
<qiandao-popup ref="_qianDaoPopup" />
|
||||
<!-- 预约信息弹窗组件 -->
|
||||
<ReservationPopup ref="_reservationPopup" />
|
||||
<up-datetime-picker :show="_upDatesTimePicker.show" :filter="_upDatesTimePicker.filter"
|
||||
|
|
@ -16,9 +17,11 @@
|
|||
import { ref, onMounted, provide } from 'vue'
|
||||
import ReservationEvaluate from '@/components/com/page/reservation-evaluate.vue'
|
||||
import ReservationPopup from '@/components/com/index/ReservationPopup.vue'
|
||||
import QiandaoPopup from '@/components/com/page/qiandao-popup.vue'
|
||||
import dayjs from 'dayjs';
|
||||
const _baseEvaluatePop = ref(null)
|
||||
const _reservationPopup = ref(null)
|
||||
const _qianDaoPopup = ref(null);
|
||||
|
||||
const openEvaluatePop = async (reservation) => {
|
||||
_baseEvaluatePop.value && _baseEvaluatePop.value.show(reservation)
|
||||
|
|
@ -27,6 +30,10 @@ const openReservationPopup = async (reservation) => {
|
|||
console.log("openReservationPopup", reservation)
|
||||
_reservationPopup.value && _reservationPopup.value.show(reservation)
|
||||
}
|
||||
const openQianDaoPop = async (reservation) => {
|
||||
console.log("openQianDaoPop", reservation)
|
||||
_qianDaoPopup.value && _qianDaoPopup.value.show(reservation)
|
||||
}
|
||||
const _upDatesTimePicker = ref({
|
||||
refId: null,
|
||||
show: false,
|
||||
|
|
@ -105,7 +112,7 @@ const openUpDatesTimePicker = (value, minDate, title) => {
|
|||
})
|
||||
}
|
||||
|
||||
defineExpose({ openEvaluatePop, openReservationPopup, openUpDatesTimePicker })
|
||||
defineExpose({ openEvaluatePop, openReservationPopup, openUpDatesTimePicker,openQianDaoPop })
|
||||
|
||||
|
||||
// onMounted(() => {
|
||||
|
|
|
|||
329
components/com/page/qiandao-popup.vue
Normal file
329
components/com/page/qiandao-popup.vue
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
<template>
|
||||
<uni-popup ref="_qiandaoPopupRef" type="center">
|
||||
|
||||
<view class="popup-container">
|
||||
<view class="column popup-inner">
|
||||
<view class="popup-title">到场确认</view>
|
||||
<view style="height:40rpx;"></view>
|
||||
<view class="popup-h1">请确认预约参与者是否已到场</view>
|
||||
<view v-if="currentEvaluate.length > 0" class="participants-list">
|
||||
<view v-for="(item, index) in currentEvaluate" :key="index" class="participant-item">
|
||||
<view class="participant-info">
|
||||
<image :src="item.avatarImage || '/static/default-avatar.png'" class="participant-avatar"
|
||||
mode="aspectFill" />
|
||||
<text class="participant-name">{{ item.userName }}</text>
|
||||
</view>
|
||||
<view class="attendance-btn" :class="{ 'attended': item.isAttended }"
|
||||
@click="toggleAttendance(item, index)">
|
||||
<text class="attendance-text">
|
||||
{{ item.isAttended ? '已到场' : '未到场' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view style="height: 5rpx;"></view>
|
||||
<view style="display: flex;justify-content: center;align-items: center;">
|
||||
<view
|
||||
style="background: #00AC4E;box-shadow: 0rpx 0rpx 10rpx 2rpx rgba(0,0,0,0.25);border-radius: 16rpx 16rpx 16rpx 16rpx;width: 478rpx;height: 70rpx;display: flex;justify-content: center;align-items: center;"
|
||||
@click="submit">
|
||||
<text
|
||||
style="font-family: PingFang SC, PingFang SC;font-weight: 500;font-size: 28rpx;color: #FFFFFF;font-style: normal;text-transform: none;">提交</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
computed
|
||||
} from 'vue'
|
||||
import {
|
||||
sqInterface
|
||||
} from '@/common/server/interface/sq.js'
|
||||
import {
|
||||
showModalConfirm
|
||||
} from '@/common/utils'
|
||||
import {
|
||||
userInfo
|
||||
} from '@/common/server/user.js'
|
||||
const _qiandaoPopupRef = ref(null)
|
||||
const evaluateList = ref([])
|
||||
const currentReservation = ref(null)
|
||||
|
||||
const currentEvaluate = ref([])
|
||||
|
||||
// 计算是否全部到场
|
||||
const isAllAttended = computed(() => {
|
||||
if (currentEvaluate.value.length === 0) return false
|
||||
return currentEvaluate.value.every(item => item.isAttended)
|
||||
})
|
||||
|
||||
const show = async (reservation) => {
|
||||
currentReservation.value = reservation
|
||||
|
||||
// 初始化参与者列表,过滤掉当前用户
|
||||
if (reservation && reservation.participants) {
|
||||
currentEvaluate.value = reservation.participants
|
||||
.filter(item => item.user_id != userInfo.value.id)
|
||||
.map(item => ({
|
||||
...item,
|
||||
isAttended: item.isAttended || true // 初始化签到状态
|
||||
}))
|
||||
} else {
|
||||
currentEvaluate.value = []
|
||||
}
|
||||
evaluateList.value = []
|
||||
_qiandaoPopupRef.value.open()
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
_qiandaoPopupRef.value.close()
|
||||
}
|
||||
|
||||
// 切换参与者签到状态
|
||||
const toggleAttendance = (participant, index) => {
|
||||
participant.isAttended = !participant.isAttended
|
||||
console.log('切换签到状态:', participant.userName, participant.isAttended)
|
||||
}
|
||||
|
||||
// 设置全部到场
|
||||
const setAllAttended = () => {
|
||||
currentEvaluate.value.forEach(item => {
|
||||
item.isAttended = true
|
||||
})
|
||||
console.log('设置全部到场')
|
||||
}
|
||||
|
||||
const submit = async () => {
|
||||
var isSuccess = await showModalConfirm("签到提示", "确定提交签到数据吗?签到后将无法更改数据!");
|
||||
if (!isSuccess) {
|
||||
return;
|
||||
}
|
||||
uni.showLoading({
|
||||
title: '签到提交中...'
|
||||
})
|
||||
const payload = {
|
||||
reservation_id: currentReservation.value.id,
|
||||
attendeds: currentEvaluate.value.map(item => {
|
||||
return {
|
||||
user_id: item.user_id,
|
||||
isAttended: item.isAttended
|
||||
}
|
||||
})
|
||||
}
|
||||
console.log('签到数据:', payload)
|
||||
|
||||
const result = await sqInterface.checkInReservation(payload)
|
||||
uni.hideLoading()
|
||||
|
||||
if (result.success) {
|
||||
uni.showToast({
|
||||
title: result.message || '签到提交成功',
|
||||
icon: 'success'
|
||||
})
|
||||
close()
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: result.message || '签到提交失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
show,
|
||||
close
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.popup-container {
|
||||
width: 700rpx;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 20rpx;
|
||||
height: 830rpx;
|
||||
}
|
||||
|
||||
.popup-inner {
|
||||
width: 90%;
|
||||
height: 700rpx;
|
||||
margin: 0 auto 0;
|
||||
}
|
||||
|
||||
.popup-title {
|
||||
font-size: 26rpx;
|
||||
margin-top: 30rpx;
|
||||
text-align: center;
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 500;
|
||||
font-size: 40rpx;
|
||||
color: #000000;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.popup-h1 {
|
||||
width: 476rpx;
|
||||
height: 44rpx;
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #000000;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.row-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mt-20 {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.mt-10 {
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.ml-20 {
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
/* 参与者列表样式 */
|
||||
.participants-list {
|
||||
margin-top: 20rpx;
|
||||
max-height: 300rpx;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.participant-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.participant-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.participant-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.participant-avatar {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.participant-name {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.attendance-btn {
|
||||
width: 120rpx;
|
||||
height: 50rpx;
|
||||
border-radius: 25rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #f0f0f0;
|
||||
border: 2rpx solid #ddd;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.attendance-btn.attended {
|
||||
background-color: #00AC4E;
|
||||
border-color: #00AC4E;
|
||||
}
|
||||
|
||||
.attendance-text {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.attendance-btn.attended .attendance-text {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/* 全部到场按钮样式 */
|
||||
.all-attended-btn {
|
||||
background: #6BB08A;
|
||||
box-shadow: 0rpx 0rpx 10rpx 2rpx rgba(0, 0, 0, 0.25);
|
||||
border-radius: 16rpx;
|
||||
width: 180rpx;
|
||||
height: 70rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.all-attended-btn.all-attended {
|
||||
background: #00AC4E;
|
||||
}
|
||||
|
||||
.all-attended-text {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
color: #FFFFFF;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
<template>
|
||||
<uni-popup ref="popupRef" type="center">
|
||||
|
||||
|
||||
<view class="popup-container">
|
||||
<view class="column popup-inner">
|
||||
<text class="popup-title">牌友评价</text>
|
||||
<text class="popup-desc">请对每位牌友进行评价</text>
|
||||
|
||||
<view class="row panel-box" v-if="currentEvaluate" @click="index = 0, showPicker = true">
|
||||
<view class="row panel-box" style="padding-left: 10rpx;" v-if="currentEvaluate"
|
||||
@click="index = 0, showPicker = true">
|
||||
{{ currentEvaluate.userName }}
|
||||
<image src="/static/down.png" class="down-icon" mode=""></image>
|
||||
</view>
|
||||
|
|
@ -180,6 +181,7 @@ defineExpose({ show, close })
|
|||
.mt-20 {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.mt-10 {
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,12 @@
|
|||
<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 class="center evaluate-btn" v-if="reservation.status == 2" @click.stop="onEvaluate()">
|
||||
<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>
|
||||
</view>
|
||||
<view class="mt-20" style="height: 10rpx;"></view>
|
||||
</view>
|
||||
|
|
@ -33,7 +36,7 @@
|
|||
|
||||
<script setup>
|
||||
// 仅负责展示与触发评价事件
|
||||
import { inject } from 'vue'
|
||||
import { inject, computed } from 'vue'
|
||||
const props = defineProps({
|
||||
reservation: {
|
||||
type: Object,
|
||||
|
|
@ -41,11 +44,59 @@ const props = defineProps({
|
|||
}
|
||||
})
|
||||
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 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 '时间未设置'
|
||||
|
|
@ -57,11 +108,11 @@ const formatTimeRange = (startTime, endTime) => {
|
|||
}
|
||||
|
||||
const getStatusText = (status) => {
|
||||
const map = { 0: '待开始', 1: '进行中', 2: '已结束', 3: "已取消" }
|
||||
const map = { 0: '组局中', 1: '待开始', 2: '进行中', 3: "已结束", 4: "已取消" }
|
||||
return map[status] || '未知状态'
|
||||
}
|
||||
const getStatusStyle = (status) => {
|
||||
const map = { 0: 'color: #1989FA', 1: 'color: #999', 2: 'color: #999', 3: 'color: #999' }
|
||||
const map = { 0: 'color: #999', 1: 'color: #1989FA', 2: 'color: #1989FA', 3: 'color: #999', 4: 'color: #999' }
|
||||
return map[status] || 'color: #999'
|
||||
}
|
||||
const openReservationPopup = () => {
|
||||
|
|
|
|||
|
|
@ -137,13 +137,13 @@ const statusName = computed(() => {
|
|||
const count = joinPerson.length
|
||||
|
||||
if (status === 0) {
|
||||
return personCount === count ? "待开始" : "组局中..."
|
||||
return "组局中..."
|
||||
} else if (status === 1) {
|
||||
return "进行中"
|
||||
return "待开始"
|
||||
} else if (status === 2) {
|
||||
return "已结束"
|
||||
return "进行中"
|
||||
} else if (status === 3) {
|
||||
return "取消"
|
||||
return "已结束"
|
||||
}
|
||||
return "其它"
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name" : "mahjong_group",
|
||||
"appid" : "__UNI__60D9924",
|
||||
"appid" : "__UNI__6A23109",
|
||||
"description" : "",
|
||||
"versionName" : "1.0.0",
|
||||
"versionCode" : "100",
|
||||
|
|
|
|||
12
pages.json
12
pages.json
|
|
@ -85,6 +85,18 @@
|
|||
"navigationBarTitleText": "",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/other/payment-records",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/other/faq",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ import TagSelect from '@/components/com/appointment/tag-select.vue'
|
|||
import ComAppointmentPickerData from '@/components/com/appointment/picker-data.vue'
|
||||
import ComAppointmentRadioSelect from '@/components/com/appointment/radio-select.vue'
|
||||
import {
|
||||
getReservationRoomList, addSQReservation, cancelReservation
|
||||
getReservationRoomList, addSQReservation, cancelReservation, canCreateSQReservation
|
||||
} from '@/common/server/interface/sq'
|
||||
const _containerBase = ref(null)
|
||||
// 年龄选择器状态
|
||||
|
|
@ -497,6 +497,23 @@ const submitReservation = async () => {
|
|||
// 将玩法类型和具体规则从数字转换为字符串
|
||||
submitData.game_type = getGameTypeText(submitData.game_type);
|
||||
submitData.game_rule = getGameRuleText(submitData.game_rule);
|
||||
var important_data = "";
|
||||
if (subscribeMessage.result != null) {
|
||||
important_data = JSON.stringify(subscribeMessage.result);
|
||||
}
|
||||
submitData.important_data = important_data;
|
||||
// 检测是否能创建预约 ,如果不能创建则提示并中断
|
||||
const canCreateRes = await canCreateSQReservation(submitData)
|
||||
if (!canCreateRes || canCreateRes.canCreate !== true) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: (canCreateRes && canCreateRes.message) ? canCreateRes.message : '当前条件不可创建预约',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
var resPay = null;
|
||||
if (submitData.deposit_fee > 0) {
|
||||
|
|
@ -509,13 +526,12 @@ const submitReservation = async () => {
|
|||
return;
|
||||
}
|
||||
subscribeMessage.result.paymentId = resPay.paymentId;
|
||||
if (subscribeMessage.result != null) {
|
||||
important_data = JSON.stringify(subscribeMessage.result);
|
||||
}
|
||||
submitData.important_data = important_data;
|
||||
}
|
||||
console.log('提交预约数据:', submitData)
|
||||
var important_data = "";
|
||||
if (subscribeMessage.result != null) {
|
||||
important_data = JSON.stringify(subscribeMessage.result);
|
||||
}
|
||||
submitData.important_data = important_data;
|
||||
|
||||
// TODO: 调用后端API提交预约
|
||||
// 调用后端API提交预约
|
||||
const result = await addSQReservation(submitData)
|
||||
|
|
|
|||
|
|
@ -183,7 +183,9 @@ const queryList = (pageNo, pageSize) => {
|
|||
|
||||
// 手动刷新
|
||||
const refreshData = () => {
|
||||
pagePaging.value.reload()
|
||||
if (pagePaging.value != null) {
|
||||
pagePaging.value.reload()
|
||||
}
|
||||
}
|
||||
|
||||
// 清空数据
|
||||
|
|
@ -226,7 +228,9 @@ onMounted(() => {
|
|||
// 初始化模拟数据
|
||||
|
||||
})
|
||||
|
||||
onShow(async () => {
|
||||
refreshData();
|
||||
});
|
||||
onLoad(async () => {
|
||||
if (!homeData.value) preloadHomeData();
|
||||
if (!configData.value) preloadConfigData();
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
<view class="column " style="width: 90%;margin: 0 auto;" v-for="(item, index) in myUseReservation"
|
||||
:key="index">
|
||||
<reservation-item :reservation="item" @evaluate="_containerBase.openEvaluatePop"
|
||||
@click="_containerBase.openReservationPopup" />
|
||||
@click="_containerBase.openReservationPopup" @qianDao="_containerBase.openQianDaoPop" />
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="userInfo != null" class="column"
|
||||
|
|
@ -99,128 +99,19 @@
|
|||
|
||||
<text style="font-size: 30rpx; margin-top: 26rpx; margin-left: 36rpx;">常用功能</text>
|
||||
|
||||
<view class="row" style=" justify-content: space-between; margin: 40rpx;">
|
||||
|
||||
<view class="column" @click="toAppointment()" style="align-items: center;">
|
||||
<image src="@@:app/static/appoin_record.png" style="width: 50rpx; height: 50rpx;" mode="">
|
||||
</image>
|
||||
<text style="font-size: 24rpx; margin-top: 20rpx;">预约记录</text>
|
||||
<view class="row" style="flex-wrap: wrap; justify-content: flex-start; margin: 40rpx;">
|
||||
<view class="column" v-for="item in commonActions" :key="item.label"
|
||||
@click="handleCommonAction(item)"
|
||||
style="width: 25%; align-items: center; margin-bottom: 30rpx;">
|
||||
<image :src="item.icon" style="width: 50rpx; height: 50rpx;" mode=""></image>
|
||||
<text style="font-size: 24rpx; margin-top: 20rpx;">{{ item.label }}</text>
|
||||
</view>
|
||||
|
||||
<view class="column" style="align-items: center;">
|
||||
<image src="@@:app/static/problem.png" style="width: 50rpx; height: 50rpx;" mode=""></image>
|
||||
<text style="font-size: 24rpx; margin-top: 20rpx;">常见问题</text>
|
||||
</view>
|
||||
|
||||
<view class="column" style="align-items: center;">
|
||||
<image src="@@:app/static/customer_s.png" style="width: 50rpx; height: 50rpx;" mode=""></image>
|
||||
<text style="font-size: 24rpx; margin-top: 20rpx;">联系我们</text>
|
||||
</view>
|
||||
|
||||
<view class="column" @click="toBlacklist()" style="align-items: center;">
|
||||
<image src="@@:app/static/blacklist.png" style="width: 50rpx; height: 50rpx;" mode=""></image>
|
||||
<text style="font-size: 24rpx; margin-top: 20rpx;">黑名单</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</container-base>
|
||||
|
||||
<uni-popup ref="infoPop" type="center">
|
||||
|
||||
<view class="column center"
|
||||
style="width: 680rpx; background-color: white; border-radius: 10rpx; padding: 20rpx;">
|
||||
|
||||
<text style="">预约信息</text>
|
||||
|
||||
<view class="column"
|
||||
style="width: 100%; height: 180rpx; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx;">
|
||||
<view class="row" style="margin-top: 20rpx; align-items: center; margin-left: 20rpx;">
|
||||
<text style="font-size: 22rpx;">发起者</text>
|
||||
<image :src="appointmentDetail.organizer.avatar"
|
||||
style="width: 50rpx; height: 50rpx; background-color: antiquewhite; border-radius: 50%; margin-left: 20rpx;"
|
||||
mode=""></image>
|
||||
<text style="font-size: 20rpx; margin-left: 15rpx;">{{ appointmentDetail.organizer.name }}</text>
|
||||
</view>
|
||||
<view class="row" style="margin-top: 30rpx; align-items: center; margin-left: 20rpx;">
|
||||
<text style="font-size: 22rpx;">参与者</text>
|
||||
|
||||
<view class="row" v-for="(participant, index) in appointmentDetail.participants" :key="index"
|
||||
style="align-items: center;">
|
||||
|
||||
<view class="" style="position: relative; width: 50rpx; height: 50rpx; display: flex;">
|
||||
<image :src="participant.avatar" @click="openUserPop()"
|
||||
style="width: 50rpx; height: 50rpx; background-color: antiquewhite; border-radius: 50%; margin-left: 20rpx; position: absolute;"
|
||||
mode=""></image>
|
||||
|
||||
<view v-if="participant.isBlacklisted" class="center"
|
||||
style="width: 50rpx; height: 20rpx; background-color: #FFB7B7; position: absolute; left: 40%; top: -10rpx;">
|
||||
<text style="font-size: 10rpx;">黑名单</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<text
|
||||
style="font-size: 20rpx; margin-left: 30rpx; width: 100rpx; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">{{
|
||||
participant.name }}</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
|
||||
<view class="column"
|
||||
style="width: 100%; height: 180rpx; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx;font-size: 22rpx;">
|
||||
|
||||
<view class="row" style="justify-content: space-between; margin: 20rpx;">
|
||||
<text>开始时间</text>
|
||||
<text>{{ appointmentDetail.timeInfo.startTime }}</text>
|
||||
</view>
|
||||
<view class="row" style="justify-content: space-between; margin: 0 20rpx;">
|
||||
<text>结束时间</text>
|
||||
<text>{{ appointmentDetail.timeInfo.endTime }}</text>
|
||||
</view>
|
||||
|
||||
<text style="margin: 30rpx 20rpx 0;">合计:{{ appointmentDetail.timeInfo.duration }}</text>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="column"
|
||||
style="width: 100%; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx; font-size: 22rpx;">
|
||||
<text style="margin: 20rpx 20rpx 10rpx; font-size: 24rpx;">房间号:{{ appointmentDetail.roomInfo.roomNumber
|
||||
}},{{ appointmentDetail.roomInfo.price }}</text>
|
||||
<text style="margin: 10rpx 20rpx;">人数:{{ appointmentDetail.roomInfo.playerCount }}人</text>
|
||||
<text style="margin: 10rpx 20rpx;">玩法类型:{{ appointmentDetail.roomInfo.gameType }}</text>
|
||||
<text style="margin: 10rpx 20rpx;">具体规则:{{ appointmentDetail.roomInfo.rules }}</text>
|
||||
<text style="margin: 10rpx 20rpx 20rpx;">补充信息:{{ appointmentDetail.roomInfo.additionalInfo }}</text>
|
||||
</view>
|
||||
|
||||
<view class="column"
|
||||
style="width: 100%; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx; font-size: 22rpx;">
|
||||
<text style="margin: 20rpx 20rpx 10rpx; font-size: 24rpx;">是否禁烟:{{
|
||||
appointmentDetail.requirements.smoking
|
||||
}}</text>
|
||||
<text style="margin: 10rpx 20rpx;">性别:{{ appointmentDetail.requirements.gender }}</text>
|
||||
<text style="margin: 10rpx 20rpx 20rpx;">信誉:{{ appointmentDetail.requirements.reputation }}</text>
|
||||
</view>
|
||||
|
||||
<view class="column"
|
||||
style="width: 100%; background-color: #E3E2E2; margin-top: 20rpx; border-radius: 10rpx; font-size: 22rpx;">
|
||||
<text style="margin: 20rpx 20rpx 10rpx; font-size: 24rpx;">鸽子费:{{ appointmentDetail.pigeonFee }}</text>
|
||||
<text
|
||||
style="margin: 10rpx 20rpx 20rpx; color: #9F9F9F;">组局成功后若有牌友未赴约,其鸽子费平均分给其他牌友。组局成功或失败后鸽子费将全额返还。</text>
|
||||
</view>
|
||||
|
||||
<view class="row" style="width: 100%; margin-top: 30rpx;">
|
||||
<view class="center" @click="clasePop()"
|
||||
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>
|
||||
|
|
@ -231,6 +122,7 @@ import {
|
|||
loadUserInfo,
|
||||
isLogin
|
||||
} from '@/common/server/user'
|
||||
import { navigateToAgreement } from '@/common/system/router'
|
||||
import { sqInterface } from '@/common/server/interface/sq'
|
||||
import ReservationEvaluate from '@/components/com/page/reservation-evaluate.vue'
|
||||
import ContainerBase from '@/components/com/page/container-base.vue'
|
||||
|
|
@ -242,7 +134,6 @@ import {
|
|||
import ReservationItem from '@/components/com/page/reservation-item.vue'
|
||||
// 响应式数据
|
||||
const rateValue = ref(4.5)
|
||||
const infoPop = ref(null)
|
||||
const loading = ref(false)
|
||||
const _containerBase = ref(null)
|
||||
|
||||
|
|
@ -250,61 +141,58 @@ const _containerBase = ref(null)
|
|||
// 当前预约信息
|
||||
const currentAppointment = ref(null)
|
||||
|
||||
// 预约详情数据
|
||||
const appointmentDetail = reactive({
|
||||
organizer: {
|
||||
name: '苏家辉',
|
||||
avatar: '',
|
||||
isBlacklisted: false
|
||||
},
|
||||
participants: [{
|
||||
name: '树下的胖子',
|
||||
avatar: '',
|
||||
isBlacklisted: true
|
||||
},
|
||||
{
|
||||
name: '张三',
|
||||
avatar: '',
|
||||
isBlacklisted: false
|
||||
},
|
||||
{
|
||||
name: '李四',
|
||||
avatar: '',
|
||||
isBlacklisted: false
|
||||
|
||||
|
||||
|
||||
// 常用功能 - 动态项
|
||||
const commonActions = [
|
||||
{ label: '预约记录', icon: '@@:app/static/me/wodd.png', action: 'appointment' },
|
||||
{ label: '订单记录', icon: '@@:app/static/me/wodd.png', action: 'payment' },
|
||||
{ label: '常见问题', icon: '@@:app/static/me/cjwt.png', action: 'faq' },
|
||||
{ label: '黑名单', icon: '@@:app/static/me/hmd.png', action: 'blacklist' },
|
||||
{ label: '联系我们', icon: '@@:app/static/me/lxwm.png', action: 'contact' },
|
||||
|
||||
]
|
||||
|
||||
const handleCommonAction = (item) => {
|
||||
switch (item.action) {
|
||||
case 'appointment':
|
||||
toAppointment();
|
||||
break;
|
||||
case 'blacklist':
|
||||
toBlacklist();
|
||||
break;
|
||||
case 'faq':
|
||||
if (userInfo.value == null) {
|
||||
uni.navigateTo({
|
||||
url: '/pages/me/login'
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/pages/other/faq'
|
||||
});
|
||||
break;
|
||||
case 'payment':
|
||||
if (userInfo.value == null) {
|
||||
uni.navigateTo({
|
||||
url: '/pages/me/login'
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/pages/other/payment-records'
|
||||
});
|
||||
break;
|
||||
case 'contact':
|
||||
uni.navigateTo({
|
||||
url: '/pages/other/agreement?type=about'
|
||||
});
|
||||
break;
|
||||
default:
|
||||
uni.showToast({ title: '敬请期待', icon: 'none' });
|
||||
break;
|
||||
}
|
||||
],
|
||||
timeInfo: {
|
||||
startTime: '2025/08/27 15:30',
|
||||
endTime: '2025/08/27 17:30',
|
||||
duration: '2小时'
|
||||
},
|
||||
roomInfo: {
|
||||
roomNumber: '304号-大包',
|
||||
price: '30元/小时',
|
||||
playerCount: 3,
|
||||
gameType: '扑克',
|
||||
rules: '斗地主',
|
||||
additionalInfo: '无'
|
||||
},
|
||||
requirements: {
|
||||
smoking: '禁烟',
|
||||
gender: '不限',
|
||||
reputation: '≧4.0'
|
||||
},
|
||||
pigeonFee: '0元'
|
||||
})
|
||||
|
||||
// 方法
|
||||
const openPop = () => {
|
||||
infoPop.value.open();
|
||||
}
|
||||
|
||||
const clasePop = () => {
|
||||
infoPop.value.close();
|
||||
}
|
||||
const openReservationPopup = (item) => {
|
||||
console.log("openReservationPopup", item)
|
||||
// _containerBase.value.openReservationPopup(item)
|
||||
}
|
||||
|
||||
const toAppointment = () => {
|
||||
|
|
@ -380,8 +268,10 @@ const loadCurrentAppointment = async () => {
|
|||
var res = await sqInterface.getMyUseReservation();
|
||||
console.log("getMyUseReservation", res);
|
||||
if (res != null) {
|
||||
myUseReservation.value = myUseReservation.value.splice(0, myUseReservation.value.length);
|
||||
myUseReservation.value.push(...res);
|
||||
// console.log("myUseReservation.value.length", myUseReservation.value.length);
|
||||
// myUseReservation.value = myUseReservation.value.splice(0, myUseReservation.value.length);
|
||||
// console.log("myUseReservation.value.length", myUseReservation.value.length);
|
||||
myUseReservation.value = res;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -390,19 +280,16 @@ const loadCurrentAppointment = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
// 组件挂载时加载数据
|
||||
onMounted(() => {
|
||||
loadCurrentAppointment()
|
||||
})
|
||||
|
||||
// 页面显示时重新加载数据(用于登录后刷新)
|
||||
onShow(() => {
|
||||
onShow(async () => {
|
||||
// 检查是否有登录状态
|
||||
// getUserInfoData();
|
||||
loadCurrentAppointment();
|
||||
await loadUserInfo();
|
||||
})
|
||||
onLoad(async () => {
|
||||
await loadUserInfo();
|
||||
|
||||
//loadCurrentAppointment();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<view class="content column">
|
||||
|
||||
<z-paging ref="pagePaging" v-model="repList" @query="queryList" :refresher-enabled="true"
|
||||
:loading-more-enabled="true" :auto="true" :empty-view-text="'暂无记录'"
|
||||
:empty-view-img="'@@:app/static/index/no.png'" style="flex: 1; width: 100%; margin-top: 30rpx;">
|
||||
|
|
@ -25,7 +24,7 @@
|
|||
:key="index">
|
||||
<view class="column reservation-inner">
|
||||
<view class="row title title-row">
|
||||
<view class="title">{{ item.title || '记录' }}</view>reservation-box
|
||||
<view class="title">{{ item.title || '记录' }}</view>
|
||||
<view
|
||||
:class="['value-text', Number(item.reputation_value) >= 0 ? 'value-pos' : 'value-neg']">
|
||||
{{ formatValue(item.reputation_value) }}
|
||||
|
|
|
|||
|
|
@ -2,19 +2,29 @@
|
|||
<view>
|
||||
<com-page-container :title="title" showBack>
|
||||
<view class="agreement-container">
|
||||
<rich-text v-if="article != null" :nodes="article.contentBody" class="agreement-content"></rich-text>
|
||||
<rich-text v-if="article != null" :nodes="safeHtml" class="agreement-content"></rich-text>
|
||||
</view>
|
||||
</com-page-container>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { getArticleDetail } from '@/common/server/interface/article'
|
||||
import { getConfigData } from '@/common/server/config'
|
||||
let title = ref('')
|
||||
const article = ref(null)
|
||||
let configData = null;
|
||||
|
||||
// 将不被 rich-text 支持的标签进行替换,并为缺失样式的图片补充样式
|
||||
const safeHtml = computed(() => {
|
||||
const html = article.value?.contentBody || ''
|
||||
return html
|
||||
.replace(/<figure\b[^>]*>/gi, '<div>')
|
||||
.replace(/<\/figure>/gi, '</div>')
|
||||
// 为没有 style 的 img 补充样式,避免超出
|
||||
.replace(/<img(?![^>]*\bstyle=)([^>]*?)>/gi, '<img$1 style="max-width:100%;height:auto;display:block;">')
|
||||
})
|
||||
onLoad(async (option) => {
|
||||
console.log(option);
|
||||
if (configData == null) {
|
||||
|
|
@ -24,11 +34,14 @@ onLoad(async (option) => {
|
|||
|
||||
if (option.type != null) {
|
||||
if (option.type == 'userAgreement') {
|
||||
title = "用户协议";
|
||||
title.value = "用户协议";
|
||||
article.value = await getArticleDetail(configData.config.userAgreementId)
|
||||
} else if (option.type == 'privacyPolicy') {
|
||||
title = "隐私协议";
|
||||
title.value = "隐私协议";
|
||||
article.value = await getArticleDetail(configData.config.privacyPolicyId)
|
||||
} else if (option.type == 'about') {
|
||||
title.value = "关于我们";
|
||||
article.value = await getArticleDetail(configData.config.aboutArticleId)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
232
pages/other/faq.vue
Normal file
232
pages/other/faq.vue
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
<template>
|
||||
<view class="content column">
|
||||
<view class="row header-row" :style="{ marginTop: statusBarHeight + 'px' }">
|
||||
<image src="/static/back.png" class="back-icon" @click="goBack()" mode=""></image>
|
||||
<text class="page-title">常见问题</text>
|
||||
<view class="spacer-40"></view>
|
||||
</view>
|
||||
|
||||
<view class="list-wrapper">
|
||||
<view v-if="loading" class="tip-text">加载中...</view>
|
||||
<view v-else>
|
||||
<view v-if="errorMsg" class="tip-text">{{ errorMsg }}</view>
|
||||
<template v-else>
|
||||
<view v-if="commonList.length" class="section">
|
||||
<view class="section-title">常见问题</view>
|
||||
<view class="column list-inner">
|
||||
<view class="reservation-item reservation-box" v-for="(item, index) in commonList" :key="item.id || index">
|
||||
<view class="column reservation-inner">
|
||||
<view class="row title title-row">
|
||||
<view class="title">{{ item.title }}</view>
|
||||
</view>
|
||||
<view class="row row-text row-center mt-20">
|
||||
<text class="ml-20">{{ item.description }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="serviceList.length" class="section">
|
||||
<view class="section-title">服务问题</view>
|
||||
<view class="column list-inner">
|
||||
<view class="reservation-item reservation-box" v-for="(item, index) in serviceList" :key="item.id || index">
|
||||
<view class="column reservation-inner">
|
||||
<view class="row title title-row">
|
||||
<view class="title">{{ item.title }}</view>
|
||||
</view>
|
||||
<view class="row row-text row-center mt-20">
|
||||
<text class="ml-20">{{ item.description }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="deliveryList.length" class="section">
|
||||
<view class="section-title">订单问题</view>
|
||||
<view class="column list-inner">
|
||||
<view class="reservation-item reservation-box" v-for="(item, index) in deliveryList" :key="item.id || index">
|
||||
<view class="column reservation-inner">
|
||||
<view class="row title title-row">
|
||||
<view class="title">{{ item.title }}</view>
|
||||
</view>
|
||||
<view class="row row-text row-center mt-20">
|
||||
<text class="ml-20">{{ item.description }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="!commonList.length && !serviceList.length && !deliveryList.length" class="tip-text">暂无内容</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getServiceDescription } from '@/common/server/interface/common.js'
|
||||
|
||||
const statusBarHeight = ref(uni.getSystemInfoSync().statusBarHeight);
|
||||
|
||||
const loading = ref(true)
|
||||
const errorMsg = ref('')
|
||||
const commonList = ref([])
|
||||
const serviceList = ref([])
|
||||
const deliveryList = ref([])
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
errorMsg.value = ''
|
||||
const data = await getServiceDescription()
|
||||
if (data) {
|
||||
const filterSort = (arr = []) => arr
|
||||
.filter(i => i && i.isShow)
|
||||
.sort((a, b) => Number(a.sortId || 0) - Number(b.sortId || 0))
|
||||
commonList.value = filterSort(data.commonQuestion || [])
|
||||
serviceList.value = filterSort(data.service || [])
|
||||
deliveryList.value = filterSort(data.delivery || [])
|
||||
} else {
|
||||
errorMsg.value = '获取失败'
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('获取服务与说明失败:', e)
|
||||
errorMsg.value = '获取失败'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(fetchData)
|
||||
|
||||
const goBack = () => {
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background: #F7F7F7;
|
||||
}
|
||||
|
||||
.header-row {
|
||||
width: 90%;
|
||||
margin: 100rpx auto 0;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.spacer-40 {
|
||||
width: 40rpx;
|
||||
}
|
||||
|
||||
.list-wrapper {
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
|
||||
.list-inner {
|
||||
width: 90%;
|
||||
margin: 0 auto 0;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.section {
|
||||
width: 100%;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
width: 90%;
|
||||
margin: 0 auto 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #322A2A;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.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;
|
||||
word-break: break-all;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.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: 10rpx;
|
||||
background-color: #F2F3F5;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.reservation-inner {
|
||||
width: 95%;
|
||||
margin: 20rpx auto 20rpx;
|
||||
}
|
||||
|
||||
.row-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mt-20 {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.ml-20 {
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 26rpx;
|
||||
margin-top: 60rpx;
|
||||
}
|
||||
</style>
|
||||
194
pages/other/payment-records.vue
Normal file
194
pages/other/payment-records.vue
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
<template>
|
||||
<view class="content column">
|
||||
<z-paging
|
||||
ref="pagePaging"
|
||||
v-model="orderList"
|
||||
@query="queryList"
|
||||
:refresher-enabled="true"
|
||||
:loading-more-enabled="true"
|
||||
:auto="true"
|
||||
:empty-view-text="'暂无订单记录'"
|
||||
:empty-view-img="'@@:app/static/index/no.png'"
|
||||
style="flex: 1; width: 100%; margin-top: 30rpx;"
|
||||
>
|
||||
<template #top>
|
||||
<view class="row header-row" :style="{ marginTop: statusBarHeight + 'px' }">
|
||||
<image src="/static/back.png" class="back-icon" @click="goBack()" mode=""></image>
|
||||
<text class="page-title">订单记录</text>
|
||||
<view class="spacer-40"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<view class="list-wrapper">
|
||||
<view class="column list-inner">
|
||||
<view class="reservation-item reservation-box" v-for="(item, index) in orderList" :key="index">
|
||||
<view class="column reservation-inner">
|
||||
<view class="row title title-row">
|
||||
<view class="title">{{ item.title || '订单' }}</view>
|
||||
<view :class="['status-text', statusClass(item)]">{{ item.is_refund_text || '' }}</view>
|
||||
</view>
|
||||
|
||||
<view class="row row-text row-center mt-20">
|
||||
<text class="label">订单号:</text>
|
||||
<text class="value">{{ item.paymentId || '' }}</text>
|
||||
</view>
|
||||
|
||||
<view class="row row-text row-center mt-10">
|
||||
<text class="label">加入时间:</text>
|
||||
<text class="value">{{ item.join_time || '' }}</text>
|
||||
</view>
|
||||
|
||||
<view class="row row-text row-center mt-10" v-if="item.status === 1">
|
||||
<text class="label">预约状态:</text>
|
||||
<text class="value status-quit">已退出</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import zPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import { sqInterface } from '@/common/server/interface/sq.js'
|
||||
|
||||
const statusBarHeight = ref(uni.getSystemInfoSync().statusBarHeight)
|
||||
const orderList = ref([])
|
||||
const pagePaging = ref(null)
|
||||
|
||||
const queryList = async (pageNo, pageSize) => {
|
||||
try {
|
||||
const data = await sqInterface.getPaymentRecords(pageNo, pageSize) || []
|
||||
pagePaging.value.complete(data)
|
||||
} catch (error) {
|
||||
console.error('获取订单记录失败:', error)
|
||||
pagePaging.value.complete(false)
|
||||
uni.showToast({
|
||||
title: '获取订单记录失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const goBack = () => {
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
})
|
||||
}
|
||||
|
||||
const statusClass = (item) => {
|
||||
switch (Number(item?.is_refund)) {
|
||||
case 1: return 'status-wait-pay'
|
||||
case 2: return 'status-paid'
|
||||
case 3: return 'status-wait-refund'
|
||||
case 4: return 'status-refunded'
|
||||
default: return ''
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background: #F7F7F7;
|
||||
}
|
||||
|
||||
.header-row {
|
||||
width: 90%;
|
||||
margin: 100rpx auto 0;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.spacer-40 {
|
||||
width: 40rpx;
|
||||
}
|
||||
|
||||
.list-wrapper {
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
|
||||
.list-inner {
|
||||
width: 90%;
|
||||
margin: 0 auto 0;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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: 10rpx;
|
||||
background-color: #F2F3F5;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.reservation-inner {
|
||||
width: 95%;
|
||||
margin: 20rpx auto 20rpx;
|
||||
}
|
||||
|
||||
.row-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mt-10 { margin-top: 10rpx; }
|
||||
.mt-20 { margin-top: 20rpx; }
|
||||
|
||||
.label { color: #888; }
|
||||
.value { color: #333; }
|
||||
|
||||
.status-text { font-size: 26rpx; }
|
||||
.status-wait-pay { color: #e67e22; }
|
||||
.status-paid { color: #2ecc71; }
|
||||
.status-wait-refund { color: #3498db; }
|
||||
.status-refunded { color: #7f8c8d; }
|
||||
.status-quit { color: #e74c3c; }
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user