611 lines
20 KiB
JavaScript
611 lines
20 KiB
JavaScript
const app = getApp()
|
||
const db = wx.cloud.database()
|
||
|
||
Page({
|
||
data: {
|
||
roomId: '',
|
||
roomName: '',
|
||
roomTime: '',
|
||
keywords: '',
|
||
keywordOptions: ['咖啡馆', '餐厅', '公园', '商场', '电影院', 'KTV', '酒吧', '火锅', '烧烤', '其他'],
|
||
keywordIndex: 0,
|
||
requirements: '',
|
||
members: [],
|
||
readyCount: 0,
|
||
hasResult: false,
|
||
currentUserOpenId: '',
|
||
isCreator: false,
|
||
calculating: false,
|
||
editingRequirements: false,
|
||
meetDate: '',
|
||
meetTimeOnly: '',
|
||
hasJoined: false,
|
||
userInfo: {
|
||
avatarUrl: '',
|
||
nickName: ''
|
||
},
|
||
showUserInfoModal: false,
|
||
previousStatus: '' // 新增:记录上一次的状态,用于判断是否刚完成计算
|
||
},
|
||
|
||
onLoad: function (options) {
|
||
const roomId = options.roomId
|
||
const isCreator = options.isCreator === 'true'
|
||
this.setData({
|
||
roomId,
|
||
isCreator,
|
||
// 如果是创建者,已经在创建时加入了房间
|
||
hasJoined: isCreator
|
||
})
|
||
|
||
// 直接开始监听房间,不需要先加入
|
||
this.startWatch()
|
||
},
|
||
|
||
onUnload: function () {
|
||
if (this.watcher) {
|
||
this.watcher.close()
|
||
}
|
||
},
|
||
|
||
async joinGroup() {
|
||
// 显示用户信息填写弹窗
|
||
this.setData({ showUserInfoModal: true })
|
||
},
|
||
|
||
onChooseAvatar(e) {
|
||
const { avatarUrl } = e.detail
|
||
this.setData({
|
||
'userInfo.avatarUrl': avatarUrl
|
||
})
|
||
},
|
||
|
||
onNicknameChange(e) {
|
||
const nickName = e.detail.value
|
||
this.setData({
|
||
'userInfo.nickName': nickName
|
||
})
|
||
},
|
||
|
||
onCancelUserInfo() {
|
||
this.setData({ showUserInfoModal: false })
|
||
},
|
||
|
||
async onConfirmUserInfo() {
|
||
const { userInfo } = this.data
|
||
|
||
// 检查是否填写了昵称
|
||
if (!userInfo.nickName || !userInfo.nickName.trim()) {
|
||
wx.showToast({
|
||
title: '请输入你的昵称',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
// 关闭弹窗
|
||
this.setData({ showUserInfoModal: false })
|
||
|
||
// 调用云函数加入
|
||
wx.showLoading({ title: '加入房间...' })
|
||
try {
|
||
const res = await wx.cloud.callFunction({
|
||
name: 'joinRoom',
|
||
data: {
|
||
roomId: this.data.roomId,
|
||
userInfo: userInfo
|
||
}
|
||
})
|
||
|
||
wx.hideLoading()
|
||
|
||
if (!res.result.success) {
|
||
wx.showModal({ title: '提示', content: res.result.msg, showCancel: false })
|
||
return false
|
||
}
|
||
|
||
this.setData({ hasJoined: true })
|
||
|
||
// 加入成功后,继续添加位置
|
||
if (this.pendingAddLocation) {
|
||
this.pendingAddLocation()
|
||
this.pendingAddLocation = null
|
||
}
|
||
|
||
return true
|
||
|
||
} catch (err) {
|
||
console.error(err)
|
||
wx.hideLoading()
|
||
wx.showToast({ title: '加入失败', icon: 'none' })
|
||
return false
|
||
}
|
||
},
|
||
|
||
startWatch() {
|
||
// 先获取当前用户的 openid
|
||
this.getCurrentUserOpenId()
|
||
|
||
this.watcher = db.collection('rooms').doc(this.data.roomId).watch({
|
||
onChange: snapshot => {
|
||
if (snapshot.docs && snapshot.docs.length > 0) {
|
||
const roomData = snapshot.docs[0]
|
||
const members = roomData.members || []
|
||
const readyCount = members.filter(m => m.location).length
|
||
const hasResult = roomData.result && roomData.result.success && roomData.status === 'calculated'
|
||
const isCalculating = roomData.status === 'calculating'
|
||
const currentStatus = roomData.status
|
||
|
||
const roomTime = roomData.meetTime || ''
|
||
const [date, time] = roomTime.includes(' ') ? roomTime.split(' ') : [roomTime, '']
|
||
const keyword = roomData.keywords || '咖啡馆'
|
||
const keywordIndex = this.data.keywordOptions.indexOf(keyword)
|
||
|
||
// 检查当前用户是否已经在成员列表中
|
||
const currentUserOpenId = this.data.currentUserOpenId
|
||
const hasJoined = currentUserOpenId && members.some(m => m.openid === currentUserOpenId)
|
||
|
||
// 判断是否刚完成计算(从 calculating 变为 calculated)
|
||
const justFinishedCalculating = this.data.previousStatus === 'calculating' && currentStatus === 'calculated'
|
||
|
||
this.setData({
|
||
members,
|
||
readyCount,
|
||
hasResult,
|
||
calculating: isCalculating, // 同步云端计算状态
|
||
roomName: roomData.name || '未命名聚会',
|
||
roomTime: roomTime,
|
||
meetDate: date,
|
||
meetTimeOnly: time,
|
||
keywords: keyword,
|
||
keywordIndex: keywordIndex >= 0 ? keywordIndex : 0,
|
||
requirements: roomData.requirements || '',
|
||
hasJoined: hasJoined || this.data.hasJoined,
|
||
previousStatus: currentStatus // 更新状态记录
|
||
})
|
||
|
||
// 只有刚完成计算时才自动跳转到结果页
|
||
if (justFinishedCalculating && hasResult) {
|
||
wx.navigateTo({
|
||
url: `/pages/result/result?roomId=${this.data.roomId}`
|
||
})
|
||
}
|
||
}
|
||
},
|
||
onError: err => {
|
||
console.error('Watch Error', err)
|
||
}
|
||
})
|
||
},
|
||
|
||
async getCurrentUserOpenId() {
|
||
try {
|
||
const res = await wx.cloud.callFunction({
|
||
name: 'getOpenId'
|
||
})
|
||
if (res.result && res.result.openid) {
|
||
this.setData({ currentUserOpenId: res.result.openid })
|
||
}
|
||
} catch (err) {
|
||
console.error('获取openid失败', err)
|
||
}
|
||
},
|
||
|
||
onAddLocation() {
|
||
if (!this.data.roomId) return
|
||
|
||
// 定义添加位置的函数
|
||
const doAddLocation = () => {
|
||
wx.chooseLocation({
|
||
success: (res) => {
|
||
const { latitude, longitude, address, name } = res
|
||
|
||
wx.showLoading({ title: '提交位置...' })
|
||
wx.cloud.callFunction({
|
||
name: 'updateLocation',
|
||
data: {
|
||
roomId: this.data.roomId,
|
||
location: {
|
||
lng: longitude,
|
||
lat: latitude,
|
||
address: address || '地图选点',
|
||
name: name || '我的位置'
|
||
}
|
||
},
|
||
success: res => {
|
||
wx.hideLoading()
|
||
if (res.result.success) {
|
||
wx.showToast({ title: '已更新', icon: 'success' })
|
||
} else {
|
||
wx.showToast({ title: res.result.msg || '更新失败', icon: 'none' })
|
||
}
|
||
},
|
||
fail: err => {
|
||
wx.hideLoading()
|
||
console.error(err)
|
||
wx.showToast({ title: '网络错误', icon: 'none' })
|
||
}
|
||
})
|
||
},
|
||
fail: (err) => {
|
||
if (err.errMsg.indexOf('cancel') === -1) {
|
||
wx.showToast({ title: '选择位置失败', icon: 'none' })
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 如果还没加入房间,先加入
|
||
if (!this.data.hasJoined) {
|
||
// 保存待执行的操作
|
||
this.pendingAddLocation = doAddLocation
|
||
this.joinGroup()
|
||
} else {
|
||
doAddLocation()
|
||
}
|
||
},
|
||
|
||
onShareAppMessage() {
|
||
return {
|
||
title: '点线得面 - 找到大家的聚会好去处!',
|
||
path: `/pages/room/room?roomId=${this.data.roomId}`
|
||
}
|
||
},
|
||
|
||
// 添加测试成员(仅用于开发测试)
|
||
onAddTestMember() {
|
||
wx.showModal({
|
||
title: '添加测试成员',
|
||
content: '请输入成员名称',
|
||
editable: true,
|
||
placeholderText: '测试成员',
|
||
success: (res) => {
|
||
if (res.confirm && res.content) {
|
||
const nickName = res.content.trim() || '测试成员'
|
||
wx.showLoading({ title: '添加中...' })
|
||
wx.cloud.callFunction({
|
||
name: 'addTestMember',
|
||
data: {
|
||
roomId: this.data.roomId,
|
||
nickName: nickName
|
||
},
|
||
success: res => {
|
||
wx.hideLoading()
|
||
if (res.result.success) {
|
||
wx.showToast({ title: '添加成功', icon: 'success' })
|
||
} else {
|
||
wx.showToast({ title: res.result.msg || '添加失败', icon: 'none' })
|
||
}
|
||
},
|
||
fail: err => {
|
||
wx.hideLoading()
|
||
console.error(err)
|
||
wx.showToast({ title: '网络错误', icon: 'none' })
|
||
}
|
||
})
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
// 为测试成员添加/修改位置
|
||
onAddMemberLocation(e) {
|
||
const { openid, nickname, istest } = e.currentTarget.dataset
|
||
|
||
// 检查是否是测试成员,如果不是则不处理
|
||
if (!istest) {
|
||
return
|
||
}
|
||
|
||
wx.chooseLocation({
|
||
success: (res) => {
|
||
const { latitude, longitude, address, name } = res
|
||
|
||
wx.showLoading({ title: '提交中...' })
|
||
wx.cloud.callFunction({
|
||
name: 'updateMemberLocation',
|
||
data: {
|
||
roomId: this.data.roomId,
|
||
memberOpenid: openid,
|
||
location: {
|
||
lng: longitude,
|
||
lat: latitude,
|
||
address: address || '地图选点',
|
||
name: name || '选定位置'
|
||
}
|
||
},
|
||
success: res => {
|
||
wx.hideLoading()
|
||
if (res.result.success) {
|
||
wx.showToast({ title: `${nickname}位置已更新`, icon: 'success' })
|
||
} else {
|
||
wx.showToast({ title: res.result.msg || '更新失败', icon: 'none' })
|
||
}
|
||
},
|
||
fail: err => {
|
||
wx.hideLoading()
|
||
console.error(err)
|
||
wx.showToast({ title: '网络错误', icon: 'none' })
|
||
}
|
||
})
|
||
},
|
||
fail: (err) => {
|
||
if (err.errMsg.indexOf('cancel') === -1) {
|
||
wx.showToast({ title: '选择位置失败', icon: 'none' })
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
onCalculate() {
|
||
// 前端防抖:检查是否正在计算
|
||
if (this.data.calculating) {
|
||
wx.showToast({
|
||
title: '正在计算中,请稍候...',
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
return
|
||
}
|
||
|
||
const membersWithLocation = this.data.members.filter(m => m.location).length
|
||
|
||
if (membersWithLocation < 2) {
|
||
wx.showToast({
|
||
title: '至少需要2人添加位置才能计算',
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
return
|
||
}
|
||
|
||
// 设置计算状态,禁用按钮
|
||
this.setData({ calculating: true })
|
||
wx.showLoading({ title: '计算最佳地点...' })
|
||
|
||
wx.cloud.callFunction({
|
||
name: 'calculateMeetSpot',
|
||
data: { roomId: this.data.roomId },
|
||
success: res => {
|
||
wx.hideLoading()
|
||
this.setData({ calculating: false })
|
||
|
||
// 处理后端返回的状态锁信息
|
||
if (res.result.isCalculating) {
|
||
wx.showToast({
|
||
title: res.result.msg || '正在计算中',
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
return
|
||
}
|
||
|
||
// 处理重复计算拦截
|
||
if (res.result.isDuplicate) {
|
||
wx.showModal({
|
||
title: '提示',
|
||
content: res.result.msg || '成员位置未变化,无需重复计算',
|
||
confirmText: '查看结果',
|
||
cancelText: '知道了',
|
||
success: (modalRes) => {
|
||
if (modalRes.confirm) {
|
||
// 跳转到结果页
|
||
wx.navigateTo({
|
||
url: `/pages/result/result?roomId=${this.data.roomId}`
|
||
})
|
||
}
|
||
}
|
||
})
|
||
return
|
||
}
|
||
|
||
if (!res.result.success) {
|
||
wx.showModal({
|
||
title: '计算失败',
|
||
content: res.result.msg,
|
||
showCancel: false
|
||
})
|
||
}
|
||
// 成功后依靠 watch 自动跳转,或者这里手动跳转也可以
|
||
},
|
||
fail: err => {
|
||
wx.hideLoading()
|
||
this.setData({ calculating: false })
|
||
console.error(err)
|
||
wx.showToast({ title: '调用失败', icon: 'none' })
|
||
}
|
||
})
|
||
},
|
||
|
||
onViewResult() {
|
||
if (!this.data.hasResult) {
|
||
wx.showToast({ title: '暂无计算结果', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
wx.navigateTo({
|
||
url: `/pages/result/result?roomId=${this.data.roomId}`
|
||
})
|
||
},
|
||
|
||
onEditRoomName() {
|
||
wx.showModal({
|
||
title: '修改聚会名称',
|
||
content: this.data.roomName,
|
||
editable: true,
|
||
placeholderText: '请输入聚会名称',
|
||
success: (res) => {
|
||
if (res.confirm && res.content.trim()) {
|
||
const newName = res.content.trim()
|
||
this.updateRoomInfo({ name: newName })
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
onEditRoomTime() {
|
||
// 不需要做任何事,交给 picker 组件处理
|
||
},
|
||
|
||
onDateChange(e) {
|
||
const date = e.detail.value
|
||
this.setData({ meetDate: date })
|
||
this.updateMeetTime()
|
||
},
|
||
|
||
onTimeChange(e) {
|
||
const time = e.detail.value
|
||
this.setData({ meetTimeOnly: time })
|
||
this.updateMeetTime()
|
||
},
|
||
|
||
updateMeetTime() {
|
||
const { meetDate, meetTimeOnly } = this.data
|
||
let newTime = ''
|
||
if (meetDate && meetTimeOnly) {
|
||
newTime = `${meetDate} ${meetTimeOnly}`
|
||
} else if (meetDate) {
|
||
newTime = meetDate
|
||
}
|
||
|
||
if (newTime) {
|
||
this.setData({ roomTime: newTime })
|
||
this.updateRoomInfo({ meetTime: newTime })
|
||
}
|
||
},
|
||
|
||
onKeywordChange(e) {
|
||
const index = e.detail.value
|
||
const keyword = this.data.keywordOptions[index]
|
||
this.setData({
|
||
keywordIndex: index,
|
||
keywords: keyword
|
||
})
|
||
this.updateRoomInfo({ keywords: keyword })
|
||
},
|
||
|
||
onEditKeywords() {
|
||
// 已改用 picker,此方法不再需要
|
||
},
|
||
|
||
onEditRequirements() {
|
||
this.setData({ editingRequirements: true })
|
||
},
|
||
|
||
onRequirementsInput(e) {
|
||
this.setData({ requirements: e.detail.value })
|
||
},
|
||
|
||
onRequirementsBlur() {
|
||
this.setData({ editingRequirements: false })
|
||
this.updateRoomInfo({ requirements: this.data.requirements })
|
||
},
|
||
|
||
onRequirementsConfirm() {
|
||
this.setData({ editingRequirements: false })
|
||
this.updateRoomInfo({ requirements: this.data.requirements })
|
||
},
|
||
|
||
updateRoomInfo(updateData) {
|
||
wx.showLoading({ title: '更新中...' })
|
||
wx.cloud.callFunction({
|
||
name: 'updateRoomInfo',
|
||
data: {
|
||
roomId: this.data.roomId,
|
||
...updateData
|
||
},
|
||
success: res => {
|
||
wx.hideLoading()
|
||
if (res.result && res.result.success) {
|
||
wx.showToast({ title: '更新成功', icon: 'success' })
|
||
} else {
|
||
wx.showToast({ title: res.result?.msg || '更新失败', icon: 'none' })
|
||
}
|
||
},
|
||
fail: err => {
|
||
wx.hideLoading()
|
||
console.error('更新失败', err)
|
||
wx.showToast({ title: '网络错误', icon: 'none' })
|
||
}
|
||
})
|
||
},
|
||
|
||
// 退出聚会(普通成员)
|
||
onLeaveRoom() {
|
||
wx.showModal({
|
||
title: '确认退出',
|
||
content: '确定要退出这个聚会吗?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
this.removeMember()
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
// 移除成员(创建者权限)
|
||
onRemoveMember(e) {
|
||
const { openid, nickname } = e.currentTarget.dataset
|
||
|
||
// 不能移除测试成员(测试成员应该用专门的删除逻辑)
|
||
const member = this.data.members.find(m => m.openid === openid)
|
||
if (member && member.isTestMember) {
|
||
wx.showToast({ title: '请使用删除功能移除测试成员', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
wx.showModal({
|
||
title: '移除成员',
|
||
content: `确定要移除 ${nickname} 吗?`,
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
this.removeMember(openid, nickname)
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
// 执行移除操作
|
||
removeMember(memberOpenid = null, nickName = null) {
|
||
wx.showLoading({ title: '处理中...' })
|
||
|
||
wx.cloud.callFunction({
|
||
name: 'removeMember',
|
||
data: {
|
||
roomId: this.data.roomId,
|
||
memberOpenid: memberOpenid // 不传则退出自己
|
||
},
|
||
success: res => {
|
||
wx.hideLoading()
|
||
|
||
if (res.result.success) {
|
||
const msg = memberOpenid ? `已移除 ${nickName}` : res.result.msg
|
||
wx.showToast({
|
||
title: msg,
|
||
icon: 'success',
|
||
duration: 2000
|
||
})
|
||
|
||
// 如果是退出自己,返回首页
|
||
if (!memberOpenid) {
|
||
setTimeout(() => {
|
||
wx.navigateBack({ delta: 1 })
|
||
}, 2000)
|
||
}
|
||
} else {
|
||
wx.showModal({
|
||
title: '操作失败',
|
||
content: res.result.msg,
|
||
showCancel: false
|
||
})
|
||
}
|
||
},
|
||
fail: err => {
|
||
wx.hideLoading()
|
||
console.error('移除成员失败', err)
|
||
wx.showToast({ title: '网络错误', icon: 'none' })
|
||
}
|
||
})
|
||
}
|
||
})
|