解决手机端解码报错的问题

This commit is contained in:
赵毅 2025-09-24 17:55:52 +08:00
parent 088891bdbb
commit 9b6a3c9bce
5 changed files with 109 additions and 73 deletions

33
package-lock.json generated
View File

@ -9,6 +9,7 @@
"version": "1.0.1", "version": "1.0.1",
"dependencies": { "dependencies": {
"abort-controller": "^3.0.0", "abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"mqtt": "^3.0.0", "mqtt": "^3.0.0",
"vue": "^3.5.21" "vue": "^3.5.21"
} }
@ -220,6 +221,30 @@
"readable-stream": "^3.4.0" "readable-stream": "^3.4.0"
} }
}, },
"node_modules/bl/node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/bl/node_modules/readable-stream": { "node_modules/bl/node_modules/readable-stream": {
"version": "3.6.2", "version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
@ -245,9 +270,9 @@
} }
}, },
"node_modules/buffer": { "node_modules/buffer": {
"version": "5.7.1", "version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -265,7 +290,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"base64-js": "^1.3.1", "base64-js": "^1.3.1",
"ieee754": "^1.1.13" "ieee754": "^1.2.1"
} }
}, },
"node_modules/buffer-from": { "node_modules/buffer-from": {

View File

@ -18,6 +18,7 @@
}, },
"dependencies": { "dependencies": {
"abort-controller": "^3.0.0", "abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"mqtt": "^3.0.0", "mqtt": "^3.0.0",
"vue": "^3.5.21" "vue": "^3.5.21"
} }

View File

@ -1,4 +1,4 @@
<template> packages/customerService/index/index<template>
<view class="chat-container"> <view class="chat-container">
<!-- 聊天头部 --> <!-- 聊天头部 -->
<view :style="{ paddingTop: top + 'px', height: localHeight + 'px' }" class="chat-header"> <view :style="{ paddingTop: top + 'px', height: localHeight + 'px' }" class="chat-header">
@ -15,8 +15,8 @@
<view v-if="connectingStatus" class="connecting-status">{{ connectingStatus }}</view> <view v-if="connectingStatus" class="connecting-status">{{ connectingStatus }}</view>
<!-- 聊天消息区域 --> <!-- 聊天消息区域 -->
<scroll-view :scroll-into-view="scrollToView" class="chat-messages" scroll-y="true" <scroll-view :scroll-into-view="scrollToView" class="chat-messages" scroll-y="true" @scrolltoupper="loadMoreHistory"
@scrolltoupper="loadMoreHistory" @scrolltolower="loadMoreHistory" lower-threshold="100" upper-threshold="100"> @scrolltolower="loadMoreHistory" lower-threshold="100" upper-threshold="100">
<!-- 加载历史消息提示 --> <!-- 加载历史消息提示 -->
<view v-if="isLoadingHistory" class="message-time">加载历史消息...</view> <view v-if="isLoadingHistory" class="message-time">加载历史消息...</view>
@ -32,7 +32,7 @@
'loading': message.isLoading 'loading': message.isLoading
}" class="message-item"> }" class="message-item">
<image :src="message.isSelf ? userAvatar : (chatTarget.employee_image)" class="message-avatar" <image :src="message.isSelf ? userAvatar : (chatTarget.employee_image)" class="message-avatar"
mode="aspectFill"></image> mode="aspectFill"></image>
<view class="message-content"> <view class="message-content">
{{ message.content }} {{ message.content }}
</view> </view>
@ -44,9 +44,9 @@
<view class="chat-input-area"> <view class="chat-input-area">
<view class="input-container"> <view class="input-container">
<textarea v-model="inputMessage" :adjust-position="true" class="message-input" placeholder="请输入消息..." <textarea v-model="inputMessage" :adjust-position="true" class="message-input" placeholder="请输入消息..."
@confirm="sendMessage" @input="handleInput" auto-height hold-keyboard="true" @confirm="sendMessage" @input="handleInput" auto-height hold-keyboard="true"
enable-keyboard-accessory-view="true" cursor-spacing="10" maxlength="500" enable-keyboard-accessory-view="true" cursor-spacing="10" maxlength="500" @focus="onInputFocus"
@focus="onInputFocus" @blur="onInputBlur"></textarea> @blur="onInputBlur"></textarea>
<button :disabled="!canSend || !client || !isConnected" class="send-btn" @tap="sendMessage"> <button :disabled="!canSend || !client || !isConnected" class="send-btn" @tap="sendMessage">
发送 发送
</button> </button>
@ -61,7 +61,7 @@ import { apiArr } from '@/api/customerService'
import mqttTool from '@/utils/mqtt' import mqttTool from '@/utils/mqtt'
export default { export default {
data(){ data() {
return { return {
localHeight: '', localHeight: '',
top: '', top: '',
@ -105,14 +105,14 @@ export default {
scrollToBottomFlag: false scrollToBottomFlag: false
} }
}, },
onLoad(options){ onLoad(options) {
const meun = menuButtonInfo() const meun = menuButtonInfo()
this.top = meun.top this.top = meun.top
this.localHeight = meun.height this.localHeight = meun.height
// //
if (options.item) { if (options.item) {
this.chatTarget = JSON.parse(options.item) this.chatTarget = JSON.parse(options.item)
this.chatTarget.title = `客服${ this.chatTarget.employee_name }` this.chatTarget.title = `客服${this.chatTarget.employee_name}`
} }
// MQTT // MQTT
this.initChat() this.initChat()
@ -120,10 +120,10 @@ export default {
// //
this.userAvatar = picUrl + uni.getStorageSync('headPhoto') this.userAvatar = picUrl + uni.getStorageSync('headPhoto')
}, },
onShow(){ onShow() {
}, },
methods: { methods: {
async connect(){ async connect() {
this.client = null this.client = null
const options = { const options = {
clientId: this.selfClientId clientId: this.selfClientId
@ -154,9 +154,10 @@ export default {
await this.subscribe() await this.subscribe()
this.client.on('message', (topic, message) => { this.client.on('message', (topic, message) => {
let de = new TextDecoder('utf-8') const msgStr = Buffer.from(message).toString('utf8'); // UTF-8
let msg = de.decode(message) const msg = JSON.parse(msgStr); //
let jsMsg = JSON.parse(msg)
let jsMsg = msg // 使
console.log('收到消息', topic, msg) console.log('收到消息', topic, msg)
if (jsMsg.send_client === this.selfClientId || jsMsg.receive_client === this.selfClientId) { if (jsMsg.send_client === this.selfClientId || jsMsg.receive_client === this.selfClientId) {
console.log('接收或发送人是我') console.log('接收或发送人是我')
@ -174,7 +175,7 @@ export default {
} }
}) })
}, },
async subscribe(){ async subscribe() {
if (this.isConnected && this.client) { if (this.isConnected && this.client) {
this.client.subscribe('contact/message/receive_msg', { qos: 0 }, (err) => { this.client.subscribe('contact/message/receive_msg', { qos: 0 }, (err) => {
if (!err) { if (!err) {
@ -191,7 +192,7 @@ export default {
} }
}, },
// //
async initChat(){ async initChat() {
try { try {
// //
this.connectingStatus = '正在连接客服...' this.connectingStatus = '正在连接客服...'
@ -212,7 +213,7 @@ export default {
}, },
// MQTT // MQTT
async getMqttConfig(){ async getMqttConfig() {
try { try {
// clientIdAPI // clientIdAPI
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -244,7 +245,7 @@ export default {
}, },
// MQTT // MQTT
onDisconnect(packet){ onDisconnect(packet) {
console.log('MQTT连接断开', packet) console.log('MQTT连接断开', packet)
this.isConnected = false this.isConnected = false
this.client = null this.client = null
@ -263,7 +264,7 @@ export default {
}, },
// //
async loadHistoryMessages(){ async loadHistoryMessages() {
if (!this.hasMoreHistory || this.isLoadingHistory) { if (!this.hasMoreHistory || this.isLoadingHistory) {
return return
} }
@ -319,14 +320,14 @@ export default {
}, },
// //
loadMoreHistory(){ loadMoreHistory() {
if (!this.isLoadingHistory && this.hasMoreHistory) { if (!this.isLoadingHistory && this.hasMoreHistory) {
this.loadHistoryMessages() this.loadHistoryMessages()
} }
}, },
// //
sendMessage(){ sendMessage() {
const content = this.inputMessage.trim() const content = this.inputMessage.trim()
console.log('发送消息', content) console.log('发送消息', content)
if (!content || !this.client || !this.isConnected) return if (!content || !this.client || !this.isConnected) return
@ -344,17 +345,17 @@ export default {
} }
console.log('发送消息', msgData) console.log('发送消息', msgData)
this.client.publish( this.client.publish(
'contact/message/send_msg', // 使 'contact/message/send_msg', // 使
JSON.stringify(msgData), JSON.stringify(msgData),
{ Qos: 0 }, { Qos: 0 },
(err) => { (err) => {
if (err) { if (err) {
console.error('发送消息失败', err) console.error('发送消息失败', err)
// //
} else { } else {
console.log('发送消息成功') console.log('发送消息成功')
}
} }
}
) )
// //
@ -362,7 +363,7 @@ export default {
}, },
// //
handleInput(){ handleInput() {
this.canSend = this.inputMessage.trim().length > 0 this.canSend = this.inputMessage.trim().length > 0
}, },
@ -382,7 +383,7 @@ export default {
}, },
// 线 // 线
needShowTime(index){ needShowTime(index) {
if (index === 0) return true if (index === 0) return true
const currentMsg = this.messages[index] const currentMsg = this.messages[index]
@ -393,28 +394,28 @@ export default {
}, },
// //
formatTime(time){ formatTime(time) {
const date = new Date(time) const date = new Date(time)
const hours = date.getHours().toString().padStart(2, '0') const hours = date.getHours().toString().padStart(2, '0')
const minutes = date.getMinutes().toString().padStart(2, '0') const minutes = date.getMinutes().toString().padStart(2, '0')
return `${ hours }:${ minutes }` return `${hours}:${minutes}`
}, },
// //
scrollToBottom(){ scrollToBottom() {
setTimeout(() => { setTimeout(() => {
this.scrollToView = 'msg-' + (this.messages.length - 1) this.scrollToView = 'msg-' + (this.messages.length - 1)
}, 100) }, 100)
}, },
// //
goBack(){ goBack() {
uni.navigateBack() uni.navigateBack()
}, },
// - // -
startKeepalive(){ startKeepalive() {
// //
this.stopKeepalive() this.stopKeepalive()
@ -426,20 +427,20 @@ export default {
} }
this.client.publish( this.client.publish(
'contact/message/keep_time', 'contact/message/keep_time',
JSON.stringify(keepaliveData), JSON.stringify(keepaliveData),
{}, {},
(err) => { (err) => {
if (err) { if (err) {
console.error('发送心跳包失败', err) console.error('发送心跳包失败', err)
// //
if (!this.isConnected) { if (!this.isConnected) {
return return
}
console.log('心跳包发送失败,尝试检查连接状态')
//
} }
console.log('心跳包发送失败,尝试检查连接状态')
//
} }
}
) )
} else { } else {
console.warn('MQTT未连接停止心跳包') console.warn('MQTT未连接停止心跳包')
@ -449,7 +450,7 @@ export default {
}, },
// //
stopKeepalive(){ stopKeepalive() {
if (this.keepaliveTimer) { if (this.keepaliveTimer) {
clearInterval(this.keepaliveTimer) clearInterval(this.keepaliveTimer)
this.keepaliveTimer = null this.keepaliveTimer = null
@ -457,7 +458,7 @@ export default {
}, },
// //
goToChangeService(){ goToChangeService() {
uni.navigateTo({ uni.navigateTo({
url: '/packages/customerService/changeService/index?currentMchId=' + this.chatTarget.mchId url: '/packages/customerService/changeService/index?currentMchId=' + this.chatTarget.mchId
}) })
@ -465,7 +466,7 @@ export default {
}, },
// //
onUnload(){ onUnload() {
// MQTT // MQTT
if (this.client) { if (this.client) {
this.client.disconnect() this.client.disconnect()

View File

@ -83,6 +83,7 @@ page {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
box-shadow: 3rpx -3rpx 15rpx 0rpx rgba(255,27,27,0.05); box-shadow: 3rpx -3rpx 15rpx 0rpx rgba(255,27,27,0.05);
padding-bottom: 30rpx;
} }
.left { .left {

View File

@ -164,6 +164,14 @@ buffer@^5.5.0:
base64-js "^1.3.1" base64-js "^1.3.1"
ieee754 "^1.1.13" ieee754 "^1.1.13"
buffer@^6.0.3:
version "6.0.3"
resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz"
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
dependencies:
base64-js "^1.3.1"
ieee754 "^1.2.1"
callback-stream@^1.0.2: callback-stream@^1.0.2:
version "1.1.0" version "1.1.0"
resolved "https://registry.npmjs.org/callback-stream/-/callback-stream-1.1.0.tgz" resolved "https://registry.npmjs.org/callback-stream/-/callback-stream-1.1.0.tgz"
@ -384,7 +392,7 @@ help-me@^1.0.1:
through2 "^2.0.1" through2 "^2.0.1"
xtend "^4.0.0" xtend "^4.0.0"
ieee754@^1.1.13: ieee754@^1.1.13, ieee754@^1.2.1:
version "1.2.1" version "1.2.1"
resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==