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' }) } }) } })