This commit is contained in:
zpc 2025-09-16 03:35:03 +08:00
parent 2fae2b2bcf
commit 2c7a7940a0
11 changed files with 481 additions and 65 deletions

View File

@ -7,10 +7,10 @@
const development = {
// API基础URL
// baseUrl: 'https://ydsapi.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'],
// baseUrl: 'http://1.15.21.245:2401',
// host: ['http://1.15.21.245:2401'],
baseUrl: 'http://localhost:2015',
host: ['http://localhost:2015'],
imageUrl: 'https://guyu-1308826010.cos.ap-shanghai.myqcloud.com',
};

View File

@ -21,4 +21,38 @@ export const getConfigData = async () => {
await preloadConfigData();
}
return configData.value;
}
/**
* 获取订阅消息模板ID列表
* @param {Number} depositFee 押金费用
* @returns {Array|null} 返回模板ID数组失败时返回null
*/
export const getSubscribeMessage = async (depositFee) => {
try {
debugger
const config = await getConfigData();
if (!config || !config.config || !config.config.subscribeMessage) {
return null;
}
const templateIds = config.config.subscribeMessage
.filter(element => {
// 如果没有押金,过滤掉扣费通知和退款通知
if (element.type == "reservation_success" || element.type == "reservation_change" || element.type == "reservation_reminder") {
return true;
}
return false;
// if (depositFee === 0) {
// return element.type !== "deduction_notice" && element.type !== "refund";
// }
// return true;
})
.map(element => element.templateId);
return templateIds;
} catch (error) {
console.error('获取订阅消息配置失败:', error);
return null;
}
}

View File

@ -112,7 +112,75 @@ export const getReservationRoomList = async (startTime, endTime) => {
return null;
}
/**
* 用户创建预约接口
* @param {Object} reservationData 预约数据
* @param {number} reservationData.room_id 房间ID
* @param {number} reservationData.start_time 开始时间 时间戳()
* @param {number} reservationData.end_time 结束时间 时间戳()
* @param {string} reservationData.game_type 游戏类型
* @param {number} reservationData.deposit_fee 押金费用
* @param {string} reservationData.important_data 重要数据
* @returns {Promise<any>} 返回预约结果
*/
export const addSQReservation = async (reservationData) => {
const res = await request.post("sq/AddSQReservation", reservationData);
if (res.code == 0) {
return {
success: true,
data: res.data.reservation_id
};
}
return {
success: false,
message: res.msg || '预约失败'
};
}
/**
* 用户加入预约接口
* @param {Object} joinData 加入预约数据
* @param {number} joinData.ReservationsId 预约ID
* @param {string} joinData.important_data 重要数据
* @returns {Promise<any>} 返回加入结果
*/
export const joinReservation = async (joinData) => {
const res = await request.post("sq/JoinReservation", joinData);
if (res.code == 0) {
return {
success: true,
message: res.msg || '加入预约成功'
};
}
return {
success: false,
message: res.msg || '加入预约失败'
};
}
/**
* 取消预约接口
* @param {Object} cancelData 取消预约数据
* @param {number} cancelData.reservation_id 预约ID
* @param {string} cancelData.cancel_reason 取消原因可选
* @returns {Promise<any>} 返回取消结果
*/
export const cancelReservation = async (reservation_id, cancel_reason) => {
console.log("cancelReservation", reservation_id, cancel_reason);
const res = await request.post("sq/CancelReservation", { reservation_id: reservation_id, cancel_reason: cancel_reason });
if (res.code == 0) {
return {
success: true,
message: res.msg || '取消预约成功'
};
}
return {
success: false,
message: res.msg || '取消预约失败'
};
}
export const sqInterface = {
@ -122,5 +190,8 @@ export const sqInterface = {
getEvaluateServices,
addEvaluateServices,
getReputationByUser,
getReservationRoomList
getReservationRoomList,
addSQReservation,
joinReservation,
cancelReservation
}

View File

@ -124,8 +124,20 @@ export const cancelUserBlack = async (userId) => {
}
return false;
}
/**
* 添加黑名单
* @param {*} userId 要拉黑的用户ID
* @returns {Promise<boolean>}
*/
export const usePay = async (money) => {
const res = await request.post("user/UsePay", { money: money });
if (res.code == 0) {
return res.data;
}
return null;
}
export const userInterface = {
getAnonymousLogin,
ueWxPhoneNumberLogin,
anonymousLogin,
@ -134,7 +146,8 @@ export const userInterface = {
editUserInfo,
getMyBlackList,
addUserBlack,
cancelUserBlack
cancelUserBlack,
usePay
}

View File

@ -12,7 +12,7 @@ const throttledLoadUserInfo = throttle(_loadUserInfo, 2000, { leading: true, tra
/**
* 清除用户相关存储
*/
const clearUserStorage = () => {
export const clearUserStorage = () => {
userInfo.value = null;
uni.removeStorageSync('tokenInfo');
uni.removeStorageSync('userInfo');

View File

@ -7,6 +7,7 @@ import config from '@/common/env.js';
import md5 from 'js-md5';
import { getLocalStorage, setLocalStorage } from './cacheService';
import qs from 'qs';
import { clearUserStorage } from '@/common/server/user'
class request {
/**
* 生成唯一的nonce值
@ -139,6 +140,10 @@ class request {
success: res => {
const endDate = Date.now();
console.log(requestUrl, "请求消耗时间", endDate - startDate);
if (res.data.code == 14007) {
//登录失效
clearUserStorage();
}
resolve(res.data);
},
fail: e => {

View File

@ -1,3 +1,4 @@
import { forEach } from "lodash";
/**
* 延迟执行
@ -139,6 +140,81 @@ export function hideLoading() {
}
/**
* 请求订阅消息
* @param {Array} tmplIds 模板ID数组
* @returns {Promise} 返回Promise对象resolve中返回订阅结果对象
*/
export function requestSubscribeMessage(tmplIds) {
return new Promise((resolve) => {
uni.requestSubscribeMessage({
tmplIds: tmplIds || [],
success(res) {
console.log('订阅消息授权结果:', res);
// if(res[''])
var data = {};
for (let i = 0; i < tmplIds.length; i++) {
if (res[tmplIds[i]] != null && res[tmplIds[i]] == "accept") {
data[tmplIds[i]] = true;
} else {
data[tmplIds[i]] = false;
}
}
console.log("订阅消息授权结果:", data);
resolve({
success: true,
result: data
});
},
fail(err) {
console.error('订阅消息授权失败:', err);
resolve({
success: false,
result: [],
error: err
});
}
});
});
}
/**
* 微信支付
* @param {Object} orderInfo 支付订单信息对象
* @param {String} orderInfo.appid 微信开放平台应用AppId
* @param {String} orderInfo.noncestr 随机字符串
* @param {String} orderInfo.package 固定值 "Sign=WXPay"
* @param {String} orderInfo.partnerid 微信支付商户号
* @param {String} orderInfo.prepayid 统一下单订单号
* @param {Number} orderInfo.timestamp 时间戳单位
* @param {String} orderInfo.sign 签名
* @returns {Promise} 返回Promise对象resolve中返回支付结果对象
*/
export function requestPayment(orderInfo) {
return new Promise((resolve) => {
uni.requestPayment({
provider: "weixin",
...orderInfo,
success(res) {
console.log('微信支付成功:', res);
resolve({
success: true,
result: res
});
},
fail(err) {
console.error('微信支付失败:', err);
resolve({
success: false,
result: null,
error: err
});
}
});
});
}
let os = '';
/**
*

View File

@ -209,19 +209,20 @@ const getGenderText = (genderLimit) => {
//
const handleJoin = () => {
//
emit('join', reservationData.value)
// reservationData.value
console.log("reservationData.value", reservationData.value);
}
const cancelJoin = async () => {
var res = await showModalConfirm('提示', '确定要取消组局吗?')
if (res) {
emit('cancelJoin', reservationData.value)
console.log("reservationData.value", reservationData.value);
}
}
const exitJoin = async () => {
var res = await showModalConfirm('提示', '确定要退出组局吗?')
if (res) {
emit('exitJoin', reservationData.value)
console.log("reservationData.value", reservationData.value);
}
}
//
@ -231,7 +232,7 @@ const openUserPop = (user) => {
}
//
const emit = defineEmits(['join', 'openUserPop', 'cancelJoin', 'exitJoin'])
const emit = defineEmits(['openUserPop'])
//
defineExpose({

View File

@ -7,7 +7,7 @@
<view class="status-tag">
{{ statusName }}
</view>
<!-- 九宫格麻将桌 -->
<view class="nine-grid-container">
<view></view>
@ -17,21 +17,21 @@
</view>
</view>
<view></view>
<view class="item">
<view v-if="getJoinPlayerAtPosition(2)" class="item-avatar">
<image :src="getPlayerAtPosition(1)" class="avatar-img" mode="aspectFit"></image>
</view>
</view>
<view></view>
<view class="item">
<view v-if="getJoinPlayerAtPosition(3)" class="item-avatar">
<image :src="getPlayerAtPosition(2)" class="avatar-img" mode="aspectFit"></image>
</view>
</view>
<view></view>
<view class="item">
<view v-if="getJoinPlayerAtPosition(4)" class="item-avatar">
<image :src="getPlayerAtPosition(3)" class="avatar-img" mode="aspectFit"></image>
@ -40,7 +40,7 @@
<view></view>
</view>
</view>
<!-- 信息区域 -->
<view class="info-section">
<view class="info-content">
@ -68,7 +68,7 @@ const props = defineProps({
id: '',
status: '',
description: '',
dateStr:'',
dateStr: '',
time: '',
room: '',
requirements: '',
@ -135,7 +135,7 @@ const playerPositions = computed(() => {
const statusName = computed(() => {
const { status, personCount, joinPerson } = props.item
const count = joinPerson.length
if (status === 0) {
return personCount === count ? "待开始" : "组局中..."
} else if (status === 1) {
@ -168,7 +168,7 @@ const getPlayerAtPosition = (position) => {
*/
const getJoinPlayerAtPosition = (index) => {
const personCount = props.item.personCount
if (personCount === 2) {
// 2
return [2, 3].includes(index)
@ -255,7 +255,7 @@ const handleJoin = () => {
.status-tag {
position: absolute;
left: 16rpx;
top: 0;
top: 5px;
font-family: PingFang SC, PingFang SC;
font-weight: 500;
font-size: 26rpx;

View File

@ -10,11 +10,11 @@
</label-field>
<view :style="{ height: lineHeight }"></view>
<time-select-cell label="开始时间" icon="@@:app/static/time_start.png" @select="openUpDatesTimePicker">
{{ getDayDescription(startTimeStr) }}
{{ getDayDescription(reservationInfo.start_time * 1000) }}
</time-select-cell>
<view :style="{ height: lineHeight }"></view>
<time-select-cell label="结束时间" icon="@@:app/static/time_end.png" @select="openUpDatesTimePickerEnd">
{{ getDayDescription(endTimeStr) }}
{{ getDayDescription(reservationInfo.end_time * 1000) }}
</time-select-cell>
<view :style="{ height: lineHeight }"></view>
<time-select-cell label="房间" @select="openRoomPicker">
@ -97,7 +97,7 @@
<text class="note-text">鸽子费保证金参与人需缴纳鸽子费若有参与者在预约后没有赴约其鸽子费由在场的所有人平分组局成功或失败后鸽子费将全额返还</text>
</card-container>
<view class="center submit-button">
<view class="center submit-button" @click="submitReservation">
<text style="margin: 20rpx; color: white;">发起预约</text>
</view>
@ -121,12 +121,18 @@ import {
watch
} from 'vue';
import {
getConfigData
getConfigData, getSubscribeMessage
} from '@/common/server/config'
import { usePay } from '@/common/server/interface/user'
import {
requestSubscribeMessage,
requestPayment
} from '@/common/utils'
import {
getDayDescription,
ceilMinuteToNext5
} from '@/common/system/timeUtile';
import { isLogin } from '@/common/server/user'
import {
forEach,
union
@ -139,15 +145,13 @@ 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
getReservationRoomList, addSQReservation, cancelReservation
} from '@/common/server/interface/sq'
const _containerBase = ref(null)
//
const agePickerVisible = ref(false)
const agePickerColumns = ref([[], []])
const agePickerDefaultIndex = ref([0, 0])
const startTimeStr = ref(0)
const endTimeStr = ref(0)
const lineHeight = ref("15rpx")
const timeRange = ref(["2小时", "3小时", "4小时", "自定义"])
@ -157,17 +161,17 @@ const roomOptions = ref([])
const roomPickerRef = ref(null)
//
const reservationInfo = ref({
room_id: 0, //id
room_name: '请选择房间', //
start_time: 0, //
end_time: 0, //
max_age: 0, //
min_age: 0, //
title: '', //
extra_info: '', //
game_rule: '', //
game_type: '', //
gender_limit: 0, //
room_id: 0, //id
room_name: '请选择房间', //
start_time: 0, //
end_time: 0, //
max_age: 0, // 0
min_age: 0, //
title: '', //
extra_info: '', //
game_rule: '', //
game_type: '', //
gender_limit: 0, //
is_smoking: 2, //
credit_limit: 0, //
deposit_fee: 0, //
@ -179,14 +183,14 @@ const onTimeRangeChange = async (val) => {
console.log('timeRange change:', val)
if (val != "") {
await openUpDatesTimePicker(false);
if (startTimeStr.value > 0) {
if (reservationInfo.value.start_time > 0) {
var str = val;
if (str == "2小时") {
endTimeStr.value = startTimeStr.value + 1000 * 60 * 60 * 2;
reservationInfo.value.end_time = reservationInfo.value.start_time + 2 * 60 * 60;
} else if (str == "3小时") {
endTimeStr.value = startTimeStr.value + 1000 * 60 * 60 * 3;
reservationInfo.value.end_time = reservationInfo.value.start_time + 3 * 60 * 60;
} else if (str == "4小时") {
endTimeStr.value = startTimeStr.value + 1000 * 60 * 60 * 4;
reservationInfo.value.end_time = reservationInfo.value.start_time + 4 * 60 * 60;
} else {
await openUpDatesTimePickerEnd(false);
}
@ -198,43 +202,41 @@ const getRoomPickerName = () => {
}
const openUpDatesTimePicker = async (isManual = true) => {
var now = startTimeStr.value;
var now = reservationInfo.value.start_time * 1000;
var min = Date.now();
min += 1000 * 60 * 30;
min = ceilMinuteToNext5(min).valueOf();
if (startTimeStr.value == 0) {
if (reservationInfo.value.start_time == 0) {
now = Date.now();
now += 1000 * 60 * 30;
now = ceilMinuteToNext5(now).valueOf();
}
const startTime = await _containerBase.value.openUpDatesTimePicker(now, min, "预约开始时间")
startTimeStr.value = startTime
//
reservationInfo.value.start_time = Math.floor(startTimeStr.value / 1000)
//
//
reservationInfo.value.start_time = Math.floor(startTime / 1000)
// ""
if (isManual) {
timeRangeValue.value = "自定义"
}
}
const openUpDatesTimePickerEnd = async (isManual = true) => {
if (startTimeStr.value == 0) {
if (reservationInfo.value.start_time == 0) {
uni.showToast({
title: '请先选择开始时间',
icon: 'none'
})
return;
}
var now = endTimeStr.value;
var min = (startTimeStr.value + 1000 * 60 * 30);
var now = reservationInfo.value.end_time * 1000;
var min = (reservationInfo.value.start_time * 1000 + 1000 * 60 * 30);
if (now == 0) {
now = (startTimeStr.value + 1000 * 60 * 30);
now = (reservationInfo.value.start_time * 1000 + 1000 * 60 * 30);
}
//minDate+1000*60*30 30
const endTime = await _containerBase.value.openUpDatesTimePicker(now, min, "预约结束时间")
endTimeStr.value = endTime
//
reservationInfo.value.end_time = Math.floor(endTimeStr.value / 1000)
//
//
reservationInfo.value.end_time = Math.floor(endTime / 1000)
// ""
if (isManual) {
timeRangeValue.value = "自定义"
}
@ -339,6 +341,8 @@ const increment = () => {
if (currentValue.value > 5) {
currentValue.value = 5
}
//
reservationInfo.value.credit_limit = currentValue.value
}
}
@ -348,18 +352,227 @@ const decrement = () => {
if (currentValue.value < 0) {
currentValue.value = 0
}
//
reservationInfo.value.credit_limit = currentValue.value
}
}
const changeLog = (e) => {
console.log('change事件:', e)
}
//
watch([startTimeStr, endTimeStr], async ([s, e]) => {
reservationInfo.value.start_time = s ? Math.floor(s / 1000) : 0
reservationInfo.value.end_time = e ? Math.floor(e / 1000) : 0
//
const validateForm = () => {
const info = reservationInfo.value
//
if (!info.room_id || info.room_id === 0) {
uni.showToast({
title: '请选择房间',
icon: 'none'
})
return false
}
if (!info.start_time || info.start_time === 0) {
uni.showToast({
title: '请选择开始时间',
icon: 'none'
})
return false
}
if (!info.end_time || info.end_time === 0) {
uni.showToast({
title: '请选择结束时间',
icon: 'none'
})
return false
}
if (info.end_time <= info.start_time) {
uni.showToast({
title: '结束时间需晚于开始时间',
icon: 'none'
})
return false
}
if (!info.title || info.title.trim() === '') {
uni.showToast({
title: '请输入组局名称',
icon: 'none'
})
return false
}
if (!info.player_count || info.player_count === 0) {
uni.showToast({
title: '请选择游玩人数',
icon: 'none'
})
return false
}
if (!info.game_type || info.game_type === 0) {
uni.showToast({
title: '请选择玩法类型',
icon: 'none'
})
return false
}
if (!info.game_rule || info.game_rule === 0) {
uni.showToast({
title: '请选择具体规则',
icon: 'none'
})
return false
}
//
if (info.min_age > 0 && info.max_age > 0 && info.min_age > info.max_age) {
uni.showToast({
title: '最小年龄不能大于最大年龄',
icon: 'none'
})
return false
}
return true
}
//
const submitReservation = async () => {
var isLoginSucces = await isLogin();
if (!isLoginSucces) {
uni.navigateTo({
url: '/pages/me/login'
});
return;
}
//
if (!validateForm()) {
return
}
try {
var messageId = await getSubscribeMessage(reservationInfo.value.deposit_fee);
console.log("messageId", messageId);
var subscribeMessage = await requestSubscribeMessage(messageId);
console.log("message", subscribeMessage);
//
uni.showLoading({
title: '提交中...'
})
//
const submitData = {
...reservationInfo.value,
//
credit_limit: currentValue.value,
important_data: {}
}
var resPay = null;
if (submitData.deposit_fee > 0) {
resPay = await usePay(submitData.deposit_fee);
if (resPay == null) {
uni.showToast({
title: '鸽子费订单创建失败,请重新提交预约数据!',
icon: 'none'
})
return;
}
subscribeMessage.result.paymentId = resPay.paymentId;
}
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)
uni.hideLoading()
console.log("result", result);
if (!result.success) {
uni.showToast({
title: result.message,
icon: 'none'
})
return;
}
if (submitData.deposit_fee > 0 && resPay != null) {
var payRes = await requestPayment(resPay);
if (payRes.success) {
uni.showToast({
title: '鸽子费支付成功',
icon: 'success'
})
} else {
await cancelReservation(result.data, "鸽子费支付失败,请重新预约!");
uni.showToast({
title: '鸽子费支付失败,请重新预约!',
icon: 'none'
})
return;
}
console.log("payRes", payRes);
}
//
uni.showToast({
title: '预约提交成功',
icon: 'success'
})
//
// uni.navigateBack() //
//
resetForm()
} catch (error) {
uni.hideLoading()
console.error('提交预约失败:', error)
uni.showToast({
title: '提交失败,请重试',
icon: 'none'
})
}
}
//
const resetForm = () => {
reservationInfo.value = {
room_id: 0,
room_name: '请选择房间',
start_time: 0,
end_time: 0,
max_age: 0,
min_age: 0,
title: '',
extra_info: '',
game_rule: '',
game_type: '',
gender_limit: 0,
is_smoking: 2,
credit_limit: 0,
deposit_fee: 0,
player_count: 0,
}
currentValue.value = 0
timeRangeValue.value = ""
gameRuleRange.value = []
peopleRange.value = []
peopleText.value = "请先选择房间"
gameRuleText.value = "请先选择玩法类型"
}
//
watch([() => reservationInfo.value.start_time, () => reservationInfo.value.end_time], async ([s, e]) => {
await tryLoadRooms()
})
@ -412,6 +625,9 @@ onLoad(async () => {
gameTypeRange.value = [...config.config.playingMethodOptions];
}
})
onShow(async () => {
resetForm();
})
// /
const buildAgeColumns = () => {
const minList = [{ value: 0, text: '不限' }]

View File

@ -38,7 +38,7 @@
<!-- 预约信息弹窗组件 -->
<ReservationPopup ref="reservationPopup" @join="handleJoin" @openUserPop="openUserPop" />
<ReservationPopup ref="reservationPopup" @openUserPop="openUserPop" />