feat(chat): 添加私信功能和WebSocket实时通信

- 集成WebSocket管理器,支持心跳、自动重连、消息队列
- 实现私信相关API接口,包括会话列表、历史消息、已读标记等
- 添加聊天页面和会话列表页面,支持实时消息收发
- 配置地图权限和腾讯地图SDK用于酒局定位功能
- 修改应用名称为"喝酒了么"并更新应用描述
- 在App.vue中初始化WebSocket连接和私信功能
- 添加详细的私信模块接口文档和Mock数据
- 优化单图片动态高度计算和预加载逻辑
This commit is contained in:
cg
2026-07-21 21:27:01 +08:00
parent 0332aabba0
commit eaa0d7101f
14 changed files with 2112 additions and 38 deletions
+135 -7
View File
@@ -45,13 +45,31 @@
<!-- 地点 -->
<view class="form-group">
<text class="form-label">地点</text>
<input
class="form-input"
v-model="form.location"
placeholder="输入聚会地点"
placeholder-class="form-placeholder"
:maxlength="50"
/>
<!-- 已选地点显示地图预览 -->
<view v-if="form.latitude" class="location-preview">
<map
class="location-map"
:latitude="form.latitude"
:longitude="form.longitude"
:markers="locationMarkers"
:scale="15"
:show-location="false"
@click="chooseLocation"
></map>
<view class="location-info">
<text class="location-name">{{ form.location }}</text>
<text class="location-address">{{ form.address || '' }}</text>
</view>
<view class="location-change" @click="chooseLocation">
<text class="location-change-text">重新选择</text>
</view>
</view>
<!-- 未选地点点击选择 -->
<view v-else class="location-picker" @click="chooseLocation">
<text class="location-picker-icon">📍</text>
<text class="location-picker-text">点击选择地点</text>
<text class="location-picker-arrow"></text>
</view>
</view>
<!-- 人数 -->
@@ -104,6 +122,9 @@ export default {
date: '',
time: '',
location: '',
address: '',
latitude: null,
longitude: null,
maxPeople: 6,
note: ''
},
@@ -113,6 +134,17 @@ export default {
computed: {
canSubmit() {
return this.form.title.trim() && this.form.date && this.form.time && this.form.location.trim() && !this.submitting
},
locationMarkers() {
if (!this.form.latitude) return []
return [{
id: 1,
latitude: this.form.latitude,
longitude: this.form.longitude,
title: this.form.location,
width: 28,
height: 38
}]
}
},
methods: {
@@ -125,6 +157,19 @@ export default {
onTimeChange(e) {
this.form.time = e.detail.value
},
chooseLocation() {
uni.chooseLocation({
success: (res) => {
this.form.location = res.name || res.address || ''
this.form.address = res.address || ''
this.form.latitude = res.latitude
this.form.longitude = res.longitude
},
fail: () => {
// 用户取消或无权限,不做处理
}
})
},
changePeople(delta) {
const val = this.form.maxPeople + delta
if (val >= 2 && val <= 50) {
@@ -139,6 +184,9 @@ export default {
title: this.form.title,
time: `${this.form.date} ${this.form.time}`,
location: this.form.location,
address: this.form.address,
latitude: this.form.latitude,
longitude: this.form.longitude,
maxPeople: this.form.maxPeople,
note: this.form.note
})
@@ -279,6 +327,86 @@ export default {
box-sizing: border-box;
}
/* 地点选择器 */
.location-picker {
display: flex;
align-items: center;
gap: $sp-md;
height: 96rpx;
padding: 0 $sp-lg;
background: $bg-card;
border-radius: $radius-lg;
border: 2rpx dashed var(--border-dashed, rgba(255,255,255,0.12));
&:active {
background: $bg-card-alt;
}
}
.location-picker-icon {
font-size: $fs-lg;
}
.location-picker-text {
flex: 1;
font-size: $fs-base;
color: $text-tertiary;
}
.location-picker-arrow {
font-size: $fs-xl;
color: $text-tertiary;
}
/* 地图预览 */
.location-preview {
border-radius: $radius-lg;
overflow: hidden;
border: 1rpx solid var(--border-faint, rgba(255,255,255,0.08));
}
.location-map {
width: 100%;
height: 280rpx;
}
.location-info {
display: flex;
flex-direction: column;
gap: 6rpx;
padding: $sp-md $sp-lg;
background: $bg-card;
}
.location-name {
font-size: $fs-base;
font-weight: $fw-bold;
color: $text-primary;
}
.location-address {
font-size: $fs-xs;
color: $text-tertiary;
}
.location-change {
display: flex;
align-items: center;
justify-content: center;
padding: $sp-sm;
background: $bg-card-alt;
&:active {
background: $bg-elevated;
}
}
.location-change-text {
font-size: $fs-sm;
color: $amber;
font-weight: $fw-medium;
}
/* 人数步进器 */
.form-stepper {
display: flex;