Initial commit
This commit is contained in:
1
miniprogram/pages/chat/chat.json
Normal file
1
miniprogram/pages/chat/chat.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "navigationStyle": "custom", "usingComponents": {} }
|
||||
92
miniprogram/pages/chat/chat.ts
Normal file
92
miniprogram/pages/chat/chat.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { api } from '../../utils/api'
|
||||
|
||||
const app = getApp<{ userInfo: any }>()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 0,
|
||||
peerId: '',
|
||||
peerName: '',
|
||||
messages: [] as any[],
|
||||
inputText: '',
|
||||
scrollToMsg: '',
|
||||
},
|
||||
|
||||
_watcher: null as any,
|
||||
|
||||
onLoad(query: { userId?: string }) {
|
||||
const info = wx.getSystemInfoSync()
|
||||
this.setData({ statusBarHeight: info.statusBarHeight, peerId: query.userId || '' })
|
||||
this.loadMessages()
|
||||
this.subscribeRealtime()
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
this._watcher?.close()
|
||||
},
|
||||
|
||||
async loadMessages() {
|
||||
try {
|
||||
const res = await api.getMessages()
|
||||
const msgs = res.list.map((m: any) => ({
|
||||
...m,
|
||||
isMe: m.fromId === app.globalData?.userInfo?._id,
|
||||
}))
|
||||
this.setData({ messages: msgs })
|
||||
this.scrollToBottom()
|
||||
} catch {}
|
||||
},
|
||||
|
||||
subscribeRealtime() {
|
||||
const db = wx.cloud.database()
|
||||
this._watcher = db.collection('messages')
|
||||
.where({ toId: app.globalData?.userInfo?._id })
|
||||
.watch({
|
||||
onChange: snapshot => {
|
||||
if (snapshot.type === 'add') {
|
||||
const newMsgs = snapshot.docs.map((d: any) => ({ ...d, isMe: false }))
|
||||
this.setData({ messages: [...this.data.messages, ...newMsgs] })
|
||||
this.scrollToBottom()
|
||||
}
|
||||
},
|
||||
onError: () => {},
|
||||
})
|
||||
},
|
||||
|
||||
scrollToBottom() {
|
||||
const msgs = this.data.messages
|
||||
if (msgs.length > 0) {
|
||||
this.setData({ scrollToMsg: `msg-${msgs[msgs.length - 1]._id}` })
|
||||
}
|
||||
},
|
||||
|
||||
onInput(e: WechatMiniprogram.CustomEvent) {
|
||||
this.setData({ inputText: e.detail.value })
|
||||
},
|
||||
|
||||
async onSend() {
|
||||
const text = this.data.inputText.trim()
|
||||
if (!text) return
|
||||
this.setData({ inputText: '' })
|
||||
|
||||
const tempMsg = {
|
||||
_id: `temp_${Date.now()}`,
|
||||
content: text,
|
||||
isMe: true,
|
||||
fromId: app.globalData?.userInfo?._id,
|
||||
toId: this.data.peerId,
|
||||
}
|
||||
this.setData({ messages: [...this.data.messages, tempMsg] })
|
||||
this.scrollToBottom()
|
||||
|
||||
try {
|
||||
await api.sendMessage(this.data.peerId, text)
|
||||
} catch {
|
||||
wx.showToast({ title: '发送失败', icon: 'none' })
|
||||
}
|
||||
},
|
||||
|
||||
onBack() {
|
||||
wx.navigateBack()
|
||||
},
|
||||
})
|
||||
21
miniprogram/pages/chat/chat.wxml
Normal file
21
miniprogram/pages/chat/chat.wxml
Normal file
@@ -0,0 +1,21 @@
|
||||
<view class="chat-page">
|
||||
<view class="safe-top" style="height: {{statusBarHeight}}px"></view>
|
||||
<view class="chat-nav">
|
||||
<view class="nav-back" bindtap="onBack">‹</view>
|
||||
<view class="nav-title">{{peerName}}</view>
|
||||
<view class="nav-more">⋯</view>
|
||||
</view>
|
||||
<scroll-view scroll-y="true" class="msg-scroll" scroll-into-view="{{scrollToMsg}}" enhanced="true" show-scrollbar="false">
|
||||
<view wx:for="{{messages}}" wx:key="_id" id="msg-{{item._id}}"
|
||||
class="msg-row {{item.isMe ? 'msg-right' : 'msg-left'}}">
|
||||
<view class="msg-bubble {{item.isMe ? 'bubble-me' : 'bubble-peer'}}">
|
||||
<text>{{item.content}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view style="height: 160rpx;"></view>
|
||||
</scroll-view>
|
||||
<view class="input-bar">
|
||||
<input class="msg-input" value="{{inputText}}" bindinput="onInput" placeholder="发消息..." adjust-position="true" cursor-spacing="20" />
|
||||
<view class="send-btn {{inputText ? '' : 'send-disabled'}}" bindtap="onSend">发送</view>
|
||||
</view>
|
||||
</view>
|
||||
16
miniprogram/pages/chat/chat.wxss
Normal file
16
miniprogram/pages/chat/chat.wxss
Normal file
@@ -0,0 +1,16 @@
|
||||
.chat-page { display: flex; flex-direction: column; height: 100vh; background: #f5f0ff; }
|
||||
.chat-nav { display: flex; align-items: center; padding: 16rpx 28rpx; background: rgba(255,255,255,0.90); border-bottom: 1rpx solid rgba(43,37,61,0.08); }
|
||||
.nav-back { font-size: 60rpx; color: #272235; font-weight: 300; padding: 0 10rpx; line-height: 1; }
|
||||
.nav-title { flex: 1; font-size: 32rpx; font-weight: 900; color: #272235; text-align: center; }
|
||||
.nav-more { font-size: 36rpx; color: #9b8fa8; padding: 0 10rpx; }
|
||||
.msg-scroll { flex: 1; padding: 20rpx 28rpx; }
|
||||
.msg-row { display: flex; margin-bottom: 20rpx; }
|
||||
.msg-right { justify-content: flex-end; }
|
||||
.msg-left { justify-content: flex-start; }
|
||||
.msg-bubble { max-width: 75%; padding: 18rpx 24rpx; border-radius: 28rpx; font-size: 28rpx; line-height: 1.5; }
|
||||
.bubble-me { background: linear-gradient(135deg, #ff4f91, #ff9f1c); color: #fff; border-bottom-right-radius: 8rpx; }
|
||||
.bubble-peer { background: rgba(255,255,255,0.90); color: #272235; border-bottom-left-radius: 8rpx; box-shadow: 0 6rpx 14rpx rgba(78,56,96,0.09); }
|
||||
.input-bar { display: flex; align-items: center; gap: 14rpx; padding: 16rpx 28rpx; padding-bottom: calc(16rpx + env(safe-area-inset-bottom)); background: rgba(255,255,255,0.92); border-top: 1rpx solid rgba(43,37,61,0.08); }
|
||||
.msg-input { flex: 1; height: 80rpx; background: rgba(255,255,255,0.80); border: 1rpx solid rgba(43,37,61,0.10); border-radius: 999rpx; padding: 0 24rpx; font-size: 28rpx; color: #272235; }
|
||||
.send-btn { background: linear-gradient(135deg, #ff4f91, #ff9f1c); color: #fff; border-radius: 999rpx; padding: 18rpx 30rpx; font-size: 28rpx; font-weight: 800; flex-shrink: 0; }
|
||||
.send-disabled { background: #e8e4f0; color: #9b8fa8; }
|
||||
Reference in New Issue
Block a user