From eaa0d7101f47ba53c5d64ce70602d6da6b09cab2 Mon Sep 17 00:00:00 2001
From: cheng <545895878@qq.com>
Date: Tue, 21 Jul 2026 21:27:01 +0800
Subject: [PATCH] =?UTF-8?q?feat(chat):=20=E6=B7=BB=E5=8A=A0=E7=A7=81?=
=?UTF-8?q?=E4=BF=A1=E5=8A=9F=E8=83=BD=E5=92=8CWebSocket=E5=AE=9E=E6=97=B6?=
=?UTF-8?q?=E9=80=9A=E4=BF=A1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 集成WebSocket管理器,支持心跳、自动重连、消息队列
- 实现私信相关API接口,包括会话列表、历史消息、已读标记等
- 添加聊天页面和会话列表页面,支持实时消息收发
- 配置地图权限和腾讯地图SDK用于酒局定位功能
- 修改应用名称为"喝酒了么"并更新应用描述
- 在App.vue中初始化WebSocket连接和私信功能
- 添加详细的私信模块接口文档和Mock数据
- 优化单图片动态高度计算和预加载逻辑
---
App.vue | 10 +
common/api.js | 34 +-
common/mock-data.js | 52 +++
common/websocket.js | 253 ++++++++++++
docs/私信模块接口文档.md | 465 +++++++++++++++++++++
manifest.json | 22 +-
pages.json | 14 +
pages/card/card.vue | 18 +-
pages/chat-list/chat-list.vue | 294 ++++++++++++++
pages/chat/chat.vue | 601 ++++++++++++++++++++++++++++
pages/circle/circle.vue | 86 +++-
pages/event-create/event-create.vue | 142 ++++++-
pages/event-detail/event-detail.vue | 75 ++++
pages/games/games.vue | 84 +++-
14 files changed, 2112 insertions(+), 38 deletions(-)
create mode 100644 common/websocket.js
create mode 100644 docs/私信模块接口文档.md
create mode 100644 pages/chat-list/chat-list.vue
create mode 100644 pages/chat/chat.vue
diff --git a/App.vue b/App.vue
index e7af936..726d2ea 100644
--- a/App.vue
+++ b/App.vue
@@ -1,6 +1,7 @@
+
+
diff --git a/pages/chat/chat.vue b/pages/chat/chat.vue
new file mode 100644
index 0000000..3a449c1
--- /dev/null
+++ b/pages/chat/chat.vue
@@ -0,0 +1,601 @@
+
+
+
+
+
+
+ ‹
+
+
+ {{ nickname }}
+ {{ peerTyping ? '正在输入...' : (isOnline ? '在线' : '离线') }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ formatTime(msg.timestamp) }}
+
+
+
+
+
+
+
+ {{ nickname ? nickname[0] : '酒' }}
+
+
+
+
+
+
+ {{ msg.content }}
+
+
+
+
+
+ 我
+
+
+
+
+
+
+ 发送中...
+
+
+ 发送失败,点击重试
+
+
+
+
+
+
+
+ {{ nickname ? nickname[0] : '酒' }}
+
+
+
+ ·
+ ·
+ ·
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ➤
+
+
+
+
+
+
+
+
diff --git a/pages/circle/circle.vue b/pages/circle/circle.vue
index 597d6ab..684a5d4 100644
--- a/pages/circle/circle.vue
+++ b/pages/circle/circle.vue
@@ -2,7 +2,15 @@
+
+
+
+
+ 🧭
+ 导航到这里
+
+
+
发起人
@@ -131,6 +147,17 @@ export default {
if (!this.event) return ''
const map = { open: '报名中', ongoing: '进行中', ended: '已结束' }
return map[this.event.status] || '报名中'
+ },
+ eventMarkers() {
+ if (!this.event || !this.event.latitude) return []
+ return [{
+ id: 1,
+ latitude: this.event.latitude,
+ longitude: this.event.longitude,
+ title: this.event.location,
+ width: 28,
+ height: 38
+ }]
}
},
onLoad(options) {
@@ -186,6 +213,18 @@ export default {
} catch (e) {
uni.showToast({ title: '签到失败', icon: 'none' })
}
+ },
+ openNavigation() {
+ if (!this.event || !this.event.latitude) return
+ uni.openLocation({
+ latitude: this.event.latitude,
+ longitude: this.event.longitude,
+ name: this.event.location,
+ address: this.event.address || this.event.location,
+ fail: () => {
+ uni.showToast({ title: '无法打开地图', icon: 'none' })
+ }
+ })
}
}
}
@@ -324,6 +363,42 @@ export default {
font-weight: $fw-medium;
}
+/* 地图区域 */
+.evt-map-section {
+ margin-bottom: $sp-xl;
+ border-radius: $radius-xl;
+ overflow: hidden;
+ border: 1rpx solid var(--border-faint, rgba(255,255,255,0.08));
+}
+
+.evt-map {
+ width: 100%;
+ height: 320rpx;
+}
+
+.evt-map-nav-btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: $sp-sm;
+ padding: $sp-md;
+ background: $bg-card;
+
+ &:active {
+ background: $bg-card-alt;
+ }
+}
+
+.evt-map-nav-icon {
+ font-size: $fs-base;
+}
+
+.evt-map-nav-text {
+ font-size: $fs-base;
+ color: $amber;
+ font-weight: $fw-bold;
+}
+
/* 发起人 & 参与者 */
.evt-section-label {
display: block;
diff --git a/pages/games/games.vue b/pages/games/games.vue
index 1aaf537..b6d8eda 100644
--- a/pages/games/games.vue
+++ b/pages/games/games.vue
@@ -166,10 +166,18 @@
- {{ diceShaking ? '?' : diceFaces[diceA] }}
+
+
+
+
+
- {{ diceShaking ? '?' : diceFaces[diceB] }}
+
+
+
+
+
{{ diceA + diceB }}
@@ -213,7 +221,9 @@
:key="i"
:class="{ 'slot-item-active': !wheelSpinning && wheelResult && i === slotTarget }"
>
- {{ s.emoji }}
+
+ {{ s.emoji }}
+
{{ s.label }}
@@ -299,7 +309,6 @@ export default {
rpsLastPick: '',
rpsTimer: null,
// --- 骰子 ---
- diceFaces: ['', '⚀', '⚁', '⚂', '⚃', '⚄', '⚅'],
diceA: 1,
diceB: 1,
diceShaking: false,
@@ -452,6 +461,18 @@ export default {
},
/* ========== 骰子大话 ========== */
+ // 骰子点数对应的 pip 位置(3x3 九宫格,1~9 从左到右、从上到下)
+ dicePips(val) {
+ const map = {
+ 1: [5],
+ 2: [1, 9],
+ 3: [1, 5, 9],
+ 4: [1, 3, 7, 9],
+ 5: [1, 3, 5, 7, 9],
+ 6: [1, 3, 4, 6, 7, 9]
+ }
+ return map[val] || []
+ },
diceRoll() {
if (this.diceShaking) return
this.diceShaking = true
@@ -716,14 +737,14 @@ export default {
.rule-title {
display: block;
- font-size: $fs-sm;
+ font-size: 30rpx;
font-weight: $fw-bold;
color: $text-secondary;
margin-bottom: $sp-sm;
}
.rule-text {
- font-size: $fs-xs;
+ font-size: 30rpx;
color: $text-tertiary;
line-height: $lh-loose;
}
@@ -784,7 +805,7 @@ export default {
background: linear-gradient(135deg, var(--amber, #E8A838), var(--amber-deep, #C47F17));
box-shadow: var(--shadow-amber, 0 8rpx 32rpx rgba(232,168,56,0.25));
transition: transform $duration-fast $ease-out, opacity $duration-fast;
- text { color: var(--text-on-amber, #0B0B14); font-size: $fs-base; font-weight: $fw-bold; }
+ text { color: var(--text-on-amber, #0B0B14); font-size:40rpx; font-weight: $fw-bold; }
&:active { transform: scale(0.94); }
&.btn-disabled { opacity: 0.5; pointer-events: none; }
}
@@ -1069,11 +1090,12 @@ export default {
}
.die {
- width: 120rpx; height: 120rpx;
- display: flex; align-items: center; justify-content: center;
+ width: 132rpx; height: 132rpx;
+ display: flex;
+ align-items: center; justify-content: center;
border-radius: $radius-md;
- background: $bg-elevated;
- border: 2rpx solid var(--border-subtle, rgba(255,255,255,0.10));
+ background: linear-gradient(160deg, #FBF7EF, #EAE2D3);
+ box-shadow: inset 0 4rpx 8rpx rgba(255,255,255,0.7), inset 0 -6rpx 12rpx rgba(0,0,0,0.12), $shadow-sm;
transition: transform $duration-base $ease-bounce;
&.die-rolling { animation: die-spin 0.3s linear infinite; }
@@ -1085,7 +1107,28 @@ export default {
100% { transform: rotate(360deg) scale(1); }
}
-.die-face { font-size: 72rpx; color: $text-primary; }
+.die-grid {
+ display: flex;
+ flex-wrap: wrap;
+ width: 96rpx;
+ height: 96rpx;
+}
+
+.die-cell {
+ width: 32rpx;
+ height: 32rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.die-pip {
+ width: 18rpx;
+ height: 18rpx;
+ border-radius: 50%;
+ background: #241B10;
+ box-shadow: inset 0 2rpx 3rpx rgba(0,0,0,0.5);
+}
.dice-sum {
font-size: $fs-2xl;
@@ -1199,6 +1242,7 @@ export default {
position: absolute;
left: 0; right: 0;
top: 0;
+ width: 100%;
will-change: transform;
}
@@ -1206,7 +1250,7 @@ export default {
display: flex;
align-items: center;
justify-content: center;
- gap: $sp-md;
+ gap: $sp-sm;
height: 120rpx;
border-bottom: 1rpx solid var(--border-micro, rgba(255,255,255,0.05));
transition: background $duration-base;
@@ -1216,8 +1260,18 @@ export default {
}
}
-.slot-item-emoji { font-size: 44rpx; }
-.slot-item-label { font-size: $fs-base; font-weight: $fw-bold; color: $text-primary; }
+.slot-item-emoji-box {
+ width: 52rpx;
+ height: 52rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+ flex-shrink: 0;
+}
+
+.slot-item-emoji { font-size: 40rpx; line-height: 1; }
+.slot-item-label { font-size: $fs-base; font-weight: $fw-bold; color: $text-primary; letter-spacing: 2rpx; }
.slot-mask-top, .slot-mask-bottom {
position: absolute;