推送项目重构代码

This commit is contained in:
2026-05-31 14:19:15 +08:00
parent a28ea44cab
commit dcc66aa4a9
30 changed files with 1112 additions and 1021 deletions

View File

@@ -34,18 +34,32 @@
<div class="msg-list" ref="msgListRef">
<div v-for="m in messages" :key="m.clientMsgID"
class="msg-row" :class="{ mine: m.sendID === myUserId }">
<div class="msg-bubble">
<div class="msg-bubble" :class="{ 'bubble-image': isImage(m) }">
<div class="msg-meta">{{ m.senderNickname || m.sendID }} · {{ formatTime(m.sendTime) }}</div>
<div class="msg-text">{{ renderText(m) }}</div>
<img v-if="isImage(m)" :src="imageUrl(m)" class="msg-image"
@click="previewImage(imageUrl(m))" :alt="m.clientMsgID" />
<div v-else class="msg-text">{{ renderText(m) }}</div>
</div>
</div>
<div v-if="!messages.length" class="empty">无消息</div>
</div>
<div class="msg-input">
<input ref="imgInput" type="file" accept="image/*" style="display:none"
@change="onPickImage" />
<el-tooltip content="发送图片" placement="top">
<el-button size="mini" icon="el-icon-picture-outline" circle
@click="$refs.imgInput.click()" />
</el-tooltip>
<el-input v-model="draft" size="mini" placeholder="输入消息,回车发送"
@keyup.enter.native="send" />
<el-button type="primary" size="mini" :disabled="!draft.trim()" @click="send">发送</el-button>
</div>
<!-- 图片预览大图 -->
<el-dialog :visible.sync="previewVisible" :modal-append-to-body="true"
append-to-body width="auto" custom-class="image-preview-dialog">
<img :src="previewSrc" style="max-width: 80vw; max-height: 80vh;" />
</el-dialog>
</template>
<div v-else class="empty pick-conv">从左侧选择一个会话</div>
</div>
@@ -68,18 +82,27 @@ export default {
current: null,
messages: [],
draft: '',
myUserId: ''
myUserId: '',
previewVisible: false,
previewSrc: ''
}
},
created () {
this.init()
imBus.$on('new-message', this.onNewMessage)
imBus.$on('conv-changed', this.refreshConversations)
imBus.$on('sync-done', this.refreshConversations)
imBus.$on('disconnected', () => { this.errorMsg = 'IM 断线,尝试重连…'; this.canRetry = true })
// 兜底定时刷新(防止 SDK 事件未触发)
this.pollTimer = setInterval(() => {
if (this.myUserId) this.refreshConversations()
}, 15000)
},
beforeDestroy () {
imBus.$off('new-message', this.onNewMessage)
imBus.$off('conv-changed', this.refreshConversations)
imBus.$off('sync-done', this.refreshConversations)
if (this.pollTimer) clearInterval(this.pollTimer)
},
methods: {
async init () {
@@ -132,6 +155,42 @@ export default {
this.$message.error('发送失败:' + (e.message || e))
}
},
async onPickImage (event) {
const file = event.target.files && event.target.files[0]
event.target.value = ''
if (!file || !this.current) return
if (!/^image\//.test(file.type)) {
this.$message.warning('请选择图片文件')
return
}
try {
this.$message.info('正在上传图片…')
await im.sendImage(this.current, file)
const newMessages = await im.getMessages(this.current.conversationID, 30)
this.messages = newMessages
this.$nextTick(() => this.scrollBottom())
} catch (e) {
this.$message.error('发送图片失败:' + (e && (e.errMsg || e.message) || e))
}
},
isImage (m) {
if (!m) return false
if (m.contentType === 102) return true
return !!(m.pictureElem && (m.pictureElem.sourcePicture || m.pictureElem.bigPicture))
},
imageUrl (m) {
const p = m && m.pictureElem
if (!p) return ''
const big = p.bigPicture || {}
const src = p.sourcePicture || {}
const snap = p.snapshotPicture || {}
return big.url || src.url || snap.url || ''
},
previewImage (url) {
if (!url) return
this.previewSrc = url
this.previewVisible = true
},
onNewMessage (msg) {
// 当前会话的消息追加
if (this.current && msg && this.current.conversationID === msg.conversationID) {
@@ -265,6 +324,24 @@ export default {
.msg-input {
display: flex; gap: 4px; padding: 6px 4px;
border-top: 1px solid #ebeef5;
align-items: center;
}
.msg-input .el-input { flex: 1; }
.bubble-image { padding: 4px !important; background: transparent !important; }
.msg-image {
display: block;
max-width: 220px;
max-height: 220px;
border-radius: 4px;
cursor: zoom-in;
background: #fff;
border: 1px solid #ebeef5;
object-fit: contain;
}
::v-deep .image-preview-dialog {
background: transparent;
box-shadow: none;
.el-dialog__body { padding: 0; text-align: center; }
.el-dialog__header { padding: 0; height: 32px; }
}
</style>