1092 lines
35 KiB
Vue
1092 lines
35 KiB
Vue
<template>
|
||
<view class="page">
|
||
<u-navbar title="拼桌群聊" :border-bottom="false" :background="bgc" back-icon-color="#fff" title-color='#fff'
|
||
title-size='36' height='44' id="navbar">
|
||
</u-navbar>
|
||
<view class="tablexx">
|
||
<view class="top">
|
||
<view class="zhuti">
|
||
拼桌主题:
|
||
</view>
|
||
<!-- <button open-type="share"><image src="https://api.ccttiot.com/smartmeter/img/static/uOBU07kcJQjEVSNLyxPC" mode=""></image></button> -->
|
||
</view>
|
||
<view class="jieshao">
|
||
{{pinzhuoobj.topic}}
|
||
</view>
|
||
<view class="dangqian">
|
||
当前桌号:{{pinzhuoobj.boothName}}
|
||
</view>
|
||
<view class="pianhao">
|
||
<view class="">
|
||
<image style="height:26rpx;" src="https://api.ccttiot.com/smartmeter/img/static/uSFf09L8mtvqhjMOu0RV" mode=""></image>
|
||
{{pinzhuoobj.prefer == 1 ? '意向偏好男性' : pinzhuoobj.prefer == 2 ? '意向偏好女性' : '男女不限'}}
|
||
</view>
|
||
<view class="">
|
||
<image src="https://api.ccttiot.com/smartmeter/img/static/uL0SnLLV5kxVrM1dGGsQ" mode=""></image> 已加入{{pinzhuoobj.currentNum}}人 | 还可以加入{{Number(pinzhuoobj.limitNum == null ? 0 : pinzhuoobj.limitNum) - Number(pinzhuoobj.currentNum == null ? 0 : pinzhuoobj.currentNum)}}人
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="chat-container" :style="{ transform: `translateY(-${keyboardHeight}px)` }">
|
||
<scroll-view
|
||
scroll-y="true"
|
||
class="message-list"
|
||
:scroll-top="scrollTop"
|
||
:scroll-with-animation="true"
|
||
:scroll-anchoring="false"
|
||
:enhanced="true"
|
||
:bounces="false"
|
||
@scrolltoupper="loadMoreMessages"
|
||
refresher-enabled @refresherrefresh="onRefresh" :refresher-triggered="isRefreshing" refresher-default-style="white"
|
||
id="messageList">
|
||
<view style="width: 300rpx;color: #fff;text-align: center;background-color: #999;margin: auto;border-radius: 10rpx;opacity: .7;padding: 20rpx;box-sizing: border-box;">
|
||
倡导聊天过程文明善意~ 谨慎判断切勿私下交易,谨防网络诈骗,切勿违法乱纪!!!</view>
|
||
<view v-for="(messages, index) in messages" :key="index" class="message-item">
|
||
<view v-if="messages.sendId == userId" class="message-self">
|
||
<view class="message-content" v-if="messages.type == 1">
|
||
<view class="message-header">
|
||
<view class="message-status" v-if="messages.status">
|
||
<image v-if="messages.status === 'sending'" src="https://api.ccttiot.com/smartmeter/img/static/uO9UFjzbzS9YlabzL4HV" class="status-icon sending"></image>
|
||
<image v-else-if="messages.status === 'failed'" src="https://api.ccttiot.com/smartmeter/img/static/ualrWMTB5pPYR8UvkCv6" class="status-icon failed" @click="resendMessage(messages)"></image>
|
||
<text v-else-if="messages.status === 'sent'" class="status-text">已发送</text>
|
||
</view>
|
||
<text class="message-time">{{ messages.createTime }}</text>
|
||
</view>
|
||
<text class="message-text">{{ messages.content }}</text>
|
||
</view>
|
||
<view class="message-content" v-if="messages.type == 2">
|
||
<view class="message-header">
|
||
<view class="message-status" v-if="messages.status">
|
||
<image v-if="messages.status === 'sending'" src="https://api.ccttiot.com/smartmeter/img/static/uO9UFjzbzS9YlabzL4HV" class="status-icon sending"></image>
|
||
<image v-else-if="messages.status === 'failed'" src="https://api.ccttiot.com/smartmeter/img/static/ualrWMTB5pPYR8UvkCv6" class="status-icon failed" @click="resendMessage(messages)"></image>
|
||
<text v-else-if="messages.status === 'sent'" class="status-text">已发送</text>
|
||
</view>
|
||
<text class="message-time">{{ messages.createTime }}</text>
|
||
</view>
|
||
<image class="message-image" :src="messages.content" mode="widthFix" @click="previewImage(messages.content)"></image>
|
||
</view>
|
||
<view class="message-content" v-if="messages.type == 3">
|
||
<view class="message-header">
|
||
<view class="message-status" v-if="messages.status">
|
||
<image v-if="messages.status === 'sending'" src="https://api.ccttiot.com/smartmeter/img/static/uO9UFjzbzS9YlabzL4HV" class="status-icon sending"></image>
|
||
<image v-else-if="messages.status === 'failed'" src="https://api.ccttiot.com/smartmeter/img/static/ualrWMTB5pPYR8UvkCv6" class="status-icon failed" @click="resendMessage(messages)"></image>
|
||
<text v-else-if="messages.status === 'sent'" class="status-text">已发送</text>
|
||
</view>
|
||
<text class="message-time">{{ messages.createTime }}</text>
|
||
</view>
|
||
<video class="message-video" :src="messages.content" object-fit="contain" :id="'video-' + index"></video>
|
||
</view>
|
||
<image class="avatar" :src="messages.senderAvatar" v-if="messages.type != 4" mode=""></image>
|
||
</view>
|
||
<view v-else class="message-other">
|
||
<image class="avatar" :src="messages.senderAvatar" v-if="messages.type != 4" mode=""></image>
|
||
<view v-if="messages.type == 4" style="width: 100%;color: #666;text-align: center;">{{ messages.content }}</view>
|
||
<view class="message-content" v-if="messages.type == 1">
|
||
<view class="message-header">
|
||
<text class="message-time">{{ messages.createTime }}</text>
|
||
</view>
|
||
<text class="message-text">{{ messages.content }}</text>
|
||
</view>
|
||
<image class="message-image" v-if="messages.type == 2" :src="messages.content" mode="widthFix" @click="previewImage(messages.content)"></image>
|
||
<video class="message-video" v-if="messages.type == 3" :src="messages.content" object-fit="contain" :id="'video-' + index"></video>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
<view class="bottom-area">
|
||
<view class="input-container">
|
||
<input
|
||
type="text"
|
||
v-model="inputContent"
|
||
placeholder="说点什么..."
|
||
class="input-box"
|
||
:adjust-position="false"
|
||
:cursor-spacing="20"
|
||
:hold-keyboard="true"
|
||
@confirm="sendMessage(1)"
|
||
@focus="onInputFocus"
|
||
@blur="onInputBlur"
|
||
/>
|
||
<button @click="sendMessage(1)" class="send-button">发送</button>
|
||
</view>
|
||
<view class="function-icons">
|
||
<view class="icon-item" @click="showMediaActionSheet">
|
||
<image src="https://api.ccttiot.com/smartmeter/img/static/uGcE4EknnaOMxnqivQgL" mode="aspectFit"></image>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
data() {
|
||
return {
|
||
bgc: {
|
||
backgroundColor: "#010000",
|
||
},
|
||
messages: [
|
||
{ content: '倡导聊天过程文明善意~ 谨慎判断,切勿私下交易,谨防网络诈骗', type: 4 },
|
||
],
|
||
inputContent: '',
|
||
scrollTop: 0,
|
||
keyboardHeight: 0,
|
||
isKeyboardShow: false,
|
||
pinzhuoobj:{},
|
||
teamId:'',
|
||
total:'',
|
||
pageNum:1,
|
||
isRefreshing: false,
|
||
userId:'',
|
||
oldScrollTop: 0,
|
||
socketTask: null,
|
||
isConnected: false,
|
||
sendingMessages: new Set(),
|
||
reconnectTimer: null,
|
||
reconnectCount: 0,
|
||
maxReconnectAttempts: 50,
|
||
token:''
|
||
}
|
||
},
|
||
onLoad(option) {
|
||
this.userId = uni.getStorageSync('user').userId //获取登录用户id
|
||
if(option.teamId){
|
||
this.teamId = option.teamId
|
||
this.getxq()
|
||
}
|
||
this.getlist()
|
||
console.log('didididididdididi');
|
||
this.connectWebSocket()
|
||
this.gettoken()
|
||
|
||
// 监听键盘高度变化
|
||
uni.onKeyboardHeightChange(res => {
|
||
this.keyboardHeight = res.height;
|
||
this.isKeyboardShow = res.height > 0;
|
||
if (this.isKeyboardShow) {
|
||
this.$nextTick(() => {
|
||
this.scrollToBottom();
|
||
});
|
||
}
|
||
});
|
||
},
|
||
onUnload() {
|
||
// 页面卸载时断开WebSocket连接
|
||
this.disconnectWebSocket();
|
||
uni.offKeyboardHeightChange();
|
||
},
|
||
methods: {
|
||
// 下拉刷新更多历史消息
|
||
onRefresh() {
|
||
if(this.messages.length < this.total){
|
||
this.isRefreshing = true
|
||
// 保存当前滚动位置
|
||
const query = uni.createSelectorQuery().in(this);
|
||
query.select('#messageList').boundingClientRect(data => {
|
||
this.oldScrollTop = data.top;
|
||
}).exec();
|
||
|
||
this.pageNum++
|
||
this.getlists()
|
||
setTimeout(() => {
|
||
this.isRefreshing = false
|
||
}, 1000)
|
||
}else{
|
||
this.isRefreshing = true
|
||
setTimeout(() => {
|
||
this.isRefreshing = false
|
||
}, 1000)
|
||
}
|
||
},
|
||
getlists(){
|
||
this.$u.get(`/app/chat/receiveList?pageNum=${this.pageNum}&pageSize=20&teamId=${this.teamId}`).then(res =>{
|
||
if(res.code == 200){
|
||
this.total = res.total
|
||
this.messages = [...res.rows.reverse(), ...this.messages] //将获取的信息进行倒转,最新的信息显示最下面
|
||
}
|
||
})
|
||
},
|
||
// 查询队伍聊天信息
|
||
getlist(){
|
||
this.$u.get(`/app/chat/receiveList?pageNum=${this.pageNum}&pageSize=20&teamId=${this.teamId}`).then(res =>{
|
||
if(res.code == 200){
|
||
this.total = res.total
|
||
if(this.pageNum == 1){
|
||
this.messages = res.rows.reverse()
|
||
this.$nextTick(() => {
|
||
this.scrollToBottom()
|
||
})
|
||
}else{
|
||
const oldLength = this.messages.length
|
||
this.messages = [...res.rows.reverse(), ...this.messages] //将获取的信息进行倒转,最新的信息显示最下面
|
||
this.$nextTick(() => {
|
||
// 计算新增内容的高度并设置滚动位置
|
||
const query = uni.createSelectorQuery().in(this)
|
||
query.selectAll('.message-item').boundingClientRect(items => {
|
||
if (items && items.length > oldLength) {
|
||
let newHeight = 0
|
||
for (let i = 0; i < items.length - oldLength; i++) {
|
||
newHeight += items[i].height
|
||
}
|
||
this.scrollTop = newHeight
|
||
}
|
||
}).exec()
|
||
})
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
// 查询拼桌信息
|
||
getxq(){
|
||
this.$u.get(`/app/team/getTeamInfo?id=${this.teamId}`).then(res =>{
|
||
if(res.code == 200){
|
||
this.pinzhuoobj = res.data
|
||
}
|
||
})
|
||
},
|
||
// 进行WebSocket连接
|
||
connectWebSocket() {
|
||
if (this.socketTask) {
|
||
this.disconnectWebSocket()
|
||
}
|
||
const token = uni.getStorageSync('token')
|
||
const wsUrl = `${this.$store.state.wsUrl}/ws/user?token=${token}`
|
||
this.socketTask = uni.connectSocket({
|
||
url: wsUrl,
|
||
success: () => {
|
||
console.log('WebSocket连接成功')
|
||
}
|
||
})
|
||
this.socketTask.onOpen(() => {
|
||
console.log('WebSocket连接已打开')
|
||
this.isConnected = true
|
||
this.reconnectCount = 0 // 重置重连次数
|
||
if (this.reconnectTimer) {
|
||
clearTimeout(this.reconnectTimer)
|
||
this.reconnectTimer = null
|
||
}
|
||
})
|
||
this.socketTask.onMessage((res) => {
|
||
const message = JSON.parse(res.data)
|
||
console.log('收到WebSocket消息:', message)
|
||
console.log('当前用户ID:', this.userId)
|
||
console.log('消息发送者ID:', message.sendId)
|
||
|
||
// 检查是否是自己发送的消息,如果是则不重复添加
|
||
const isOwnMessage = this.messages.some(msg =>
|
||
msg.tempId &&
|
||
msg.sendId == this.userId &&
|
||
message.sendId == this.userId &&
|
||
msg.content === message.content &&
|
||
msg.type === message.type
|
||
)
|
||
|
||
console.log('是否为自己的消息:', isOwnMessage)
|
||
|
||
if (!isOwnMessage) {
|
||
this.$nextTick(() => {
|
||
this.messages.push(message)
|
||
console.log('添加新消息:', message)
|
||
// this.scrollToBottom()
|
||
})
|
||
} else {
|
||
// 如果是自己的消息,更新状态为已发送
|
||
const index = this.messages.findIndex(msg =>
|
||
msg.tempId &&
|
||
msg.sendId == this.userId &&
|
||
msg.content === message.content &&
|
||
msg.type === message.type
|
||
)
|
||
console.log('找到匹配的消息索引:', index)
|
||
if (index !== -1) {
|
||
console.log('更新消息状态为已发送')
|
||
this.messages[index].status = 'sent'
|
||
this.sendingMessages.delete(this.messages[index].tempId)
|
||
}
|
||
}
|
||
})
|
||
this.socketTask.onClose(() => {
|
||
console.log('WebSocket连接已关闭')
|
||
this.isConnected = false
|
||
this.handleReconnect()
|
||
})
|
||
this.socketTask.onError((err) => {
|
||
console.error('WebSocket错误:', err)
|
||
this.isConnected = false
|
||
this.handleReconnect()
|
||
})
|
||
},
|
||
disconnectWebSocket() {
|
||
if (this.socketTask) {
|
||
this.socketTask.close()
|
||
this.socketTask = null
|
||
this.isConnected = false
|
||
}
|
||
},
|
||
handleReconnect() {
|
||
if (this.reconnectCount >= this.maxReconnectAttempts) {
|
||
uni.showToast({
|
||
title: '网络连接失败,请检查网络后重试',
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
return
|
||
}
|
||
if (!this.reconnectTimer) {
|
||
this.reconnectTimer = setTimeout(() => {
|
||
this.reconnectCount++
|
||
console.log(`尝试第${this.reconnectCount}次重连`);
|
||
this.connectWebSocket()
|
||
}, 3000) // 3秒后重试
|
||
}
|
||
},
|
||
// 点击发送信息
|
||
sendMessage(type) {
|
||
if(this.isConnected == false){
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '当前聊天未连接,请退出重试',
|
||
showCancel: false,
|
||
success: function(res) {
|
||
if (res.confirm) {
|
||
|
||
} else if (res.cancel) {
|
||
|
||
}
|
||
}
|
||
})
|
||
return
|
||
}
|
||
if (this.inputContent.trim() !== '') {
|
||
const tempId = Date.now().toString()
|
||
const tempMessage = {
|
||
sendId: this.userId,
|
||
content: this.inputContent,
|
||
type: type,
|
||
status: 'sending',
|
||
tempId: tempId,
|
||
senderAvatar: uni.getStorageSync('user').avatar || 'https://api.ccttiot.com/smartmeter/img/static/default-avatar',
|
||
createTime: this.$u.timeFormat(new Date(), 'yyyy-mm-dd hh:MM:ss')
|
||
}
|
||
|
||
// 立即将消息添加到聊天列表中
|
||
this.messages.push(tempMessage)
|
||
this.sendingMessages.add(tempId)
|
||
|
||
this.$nextTick(() => {
|
||
this.scrollToBottom()
|
||
})
|
||
|
||
let data = {
|
||
teamId: this.teamId,
|
||
content: this.inputContent,
|
||
type: type
|
||
}
|
||
this.$u.post(`/app/chat/sendMessage`, data).then(res => {
|
||
if (res.code == 200) {
|
||
// 状态更新由WebSocket处理,这里不需要重复更新
|
||
console.log('消息发送成功')
|
||
// 备用机制:如果3秒后WebSocket没有更新状态,则手动更新
|
||
setTimeout(() => {
|
||
const index = this.messages.findIndex(msg => msg.tempId === tempId)
|
||
if (index !== -1 && this.messages[index].status === 'sending') {
|
||
console.log('WebSocket未响应,手动更新状态为已发送')
|
||
this.messages[index].status = 'sent'
|
||
this.sendingMessages.delete(tempId)
|
||
}
|
||
}, 3000)
|
||
} else {
|
||
const index = this.messages.findIndex(msg => msg.tempId === tempId)
|
||
if (index !== -1) {
|
||
this.messages[index].status = 'failed'
|
||
}
|
||
this.sendingMessages.delete(tempId)
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
}
|
||
}).catch(err => {
|
||
const index = this.messages.findIndex(msg => msg.tempId === tempId)
|
||
if (index !== -1) {
|
||
this.messages[index].status = 'failed'
|
||
}
|
||
this.sendingMessages.delete(tempId)
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '发送失败,请重试',
|
||
showCancel: false,
|
||
success: function(res) {
|
||
if (res.confirm) {
|
||
|
||
} else if (res.cancel) {
|
||
|
||
}
|
||
}
|
||
})
|
||
})
|
||
this.inputContent = ''
|
||
}
|
||
},
|
||
scrollToBottom() {
|
||
const query = uni.createSelectorQuery().in(this)
|
||
query.select('#messageList').boundingClientRect(data => {
|
||
if (data) {
|
||
query.selectAll('.message-item').boundingClientRect(items => {
|
||
let totalHeight = 0
|
||
items.forEach(item => {
|
||
totalHeight += item.height
|
||
})
|
||
this.scrollTop = totalHeight
|
||
}).exec()
|
||
}
|
||
}).exec()
|
||
},
|
||
loadMoreMessages() {
|
||
|
||
},
|
||
onInputFocus() {
|
||
this.scrollToBottom()
|
||
},
|
||
onInputBlur() {
|
||
this.isKeyboardShow = false
|
||
this.keyboardHeight = 0
|
||
},
|
||
// 预览图片
|
||
previewImage(url) {
|
||
uni.previewImage({
|
||
urls: [url],
|
||
current: url,
|
||
indicator: 'number',
|
||
loop: false
|
||
})
|
||
},
|
||
// 播放视频
|
||
playVideo(index) {
|
||
const videoContext = uni.createVideoContext('video-' + index, this)
|
||
videoContext.requestFullScreen()
|
||
videoContext.play()
|
||
},
|
||
// 重发消息
|
||
resendMessage(message) {
|
||
const index = this.messages.findIndex(msg => msg.tempId === message.tempId)
|
||
if (index !== -1) {
|
||
this.messages[index].status = 'sending'
|
||
this.sendingMessages.add(message.tempId)
|
||
|
||
// 根据消息类型处理重发
|
||
if (message.type === 2 || message.type === 3) {
|
||
// 图片或视频消息,需要重新上传
|
||
this.reuploadMedia(message, index)
|
||
} else {
|
||
// 文本消息,直接发送
|
||
let data = {
|
||
teamId: this.teamId,
|
||
content: message.content,
|
||
type: message.type
|
||
}
|
||
this.$u.post(`/app/chat/sendMessage`, data).then(res => {
|
||
if (res.code == 200) {
|
||
this.messages[index].status = 'sent'
|
||
this.sendingMessages.delete(message.tempId)
|
||
} else {
|
||
this.messages[index].status = 'failed'
|
||
this.sendingMessages.delete(message.tempId)
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
}
|
||
}).catch(err => {
|
||
this.messages[index].status = 'failed'
|
||
this.sendingMessages.delete(message.tempId)
|
||
uni.showToast({
|
||
title: '发送失败,请重试',
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
})
|
||
}
|
||
}
|
||
},
|
||
// 重新上传媒体文件
|
||
reuploadMedia(message, index) {
|
||
// 这里需要根据实际情况处理,可能需要用户重新选择文件
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '媒体文件重发需要重新选择文件,是否重新选择?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
if (message.type === 2) {
|
||
this.chooseImage()
|
||
} else if (message.type === 3) {
|
||
this.takeVideo()
|
||
}
|
||
// 移除失败的消息
|
||
this.messages.splice(index, 1)
|
||
this.sendingMessages.delete(message.tempId)
|
||
} else {
|
||
// 取消重发,恢复失败状态
|
||
this.messages[index].status = 'failed'
|
||
this.sendingMessages.delete(message.tempId)
|
||
}
|
||
}
|
||
})
|
||
},
|
||
// 获取七牛云token
|
||
gettoken(){
|
||
this.$u.get(`/common/qiniuToken`).then(res => {
|
||
if (res.code == 200) {
|
||
this.token = res.data
|
||
}
|
||
})
|
||
},
|
||
// 显示媒体选择菜单
|
||
showMediaActionSheet() {
|
||
uni.showActionSheet({
|
||
itemList: ['从相册选择', '拍摄视频'],
|
||
success: (res) => {
|
||
switch (res.tapIndex) {
|
||
case 0: // 从相册选择
|
||
this.chooseImage()
|
||
break;
|
||
case 1: // 拍摄视频
|
||
this.takeVideo()
|
||
break;
|
||
}
|
||
}
|
||
})
|
||
},
|
||
// 选择图片
|
||
chooseImage() {
|
||
let _this = this
|
||
let math = 'static/' + _this.$u.guid(20)
|
||
uni.chooseImage({
|
||
count: 9,
|
||
type: 'all',
|
||
success(res) {
|
||
const tempFilePaths = res.tempFiles
|
||
// 立即添加一个发送中的消息
|
||
const tempId = Date.now().toString()
|
||
const tempMessage = {
|
||
sendId: _this.userId,
|
||
content: tempFilePaths[0].path, // 临时路径
|
||
type: 2,
|
||
status: 'sending',
|
||
tempId: tempId,
|
||
senderAvatar: uni.getStorageSync('user').avatar || 'https://api.ccttiot.com/smartmeter/img/static/default-avatar',
|
||
createTime: _this.$u.timeFormat(new Date(), 'yyyy-mm-dd hh:MM:ss')
|
||
}
|
||
_this.messages.push(tempMessage)
|
||
_this.sendingMessages.add(tempId)
|
||
|
||
_this.$nextTick(() => {
|
||
_this.scrollToBottom()
|
||
})
|
||
|
||
wx.uploadFile({
|
||
url: 'https://up-z2.qiniup.com',
|
||
name: 'file',
|
||
filePath: tempFilePaths[0].path,
|
||
formData: {
|
||
token: _this.token, //后端返回的token
|
||
key: 'smartmeter/img/' + math
|
||
},
|
||
success: function(res) {
|
||
console.log(res, 'resres')
|
||
let str = JSON.parse(res.data)
|
||
const imageUrl = 'https://api.ccttiot.com/' + str.key
|
||
|
||
// 更新消息内容为真实URL
|
||
const index = _this.messages.findIndex(msg => msg.tempId === tempId)
|
||
if (index !== -1) {
|
||
_this.messages[index].content = imageUrl
|
||
}
|
||
|
||
// 发送消息到服务器
|
||
let data = {
|
||
teamId: _this.teamId,
|
||
content: imageUrl,
|
||
type: 2
|
||
}
|
||
_this.$u.post(`/app/chat/sendMessage`, data).then(res => {
|
||
if (res.code == 200) {
|
||
console.log('图片消息发送成功')
|
||
// 备用机制:如果3秒后WebSocket没有更新状态,则手动更新
|
||
setTimeout(() => {
|
||
const index = _this.messages.findIndex(msg => msg.tempId === tempId)
|
||
if (index !== -1 && _this.messages[index].status === 'sending') {
|
||
console.log('WebSocket未响应,手动更新图片状态为已发送')
|
||
_this.messages[index].status = 'sent'
|
||
_this.sendingMessages.delete(tempId)
|
||
}
|
||
}, 3000)
|
||
} else {
|
||
const index = _this.messages.findIndex(msg => msg.tempId === tempId)
|
||
if (index !== -1) {
|
||
_this.messages[index].status = 'failed'
|
||
}
|
||
_this.sendingMessages.delete(tempId)
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
}
|
||
}).catch(err => {
|
||
const index = _this.messages.findIndex(msg => msg.tempId === tempId)
|
||
if (index !== -1) {
|
||
_this.messages[index].status = 'failed'
|
||
}
|
||
_this.sendingMessages.delete(tempId)
|
||
uni.showToast({
|
||
title: '发送失败,请重试',
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
})
|
||
},
|
||
fail(err) {
|
||
const index = _this.messages.findIndex(msg => msg.tempId === tempId)
|
||
if (index !== -1) {
|
||
_this.messages[index].status = 'failed'
|
||
}
|
||
_this.sendingMessages.delete(tempId)
|
||
uni.showToast({
|
||
title: '上传失败,请重试',
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
}
|
||
})
|
||
}
|
||
})
|
||
},
|
||
|
||
// 拍摄视频
|
||
takeVideo() {
|
||
let _this = this;
|
||
let fileKey = 'static/video/' + _this.$u.guid(20) + '.mp4' // 视频文件后缀
|
||
uni.chooseVideo({
|
||
sourceType: ['camera'], // 只允许拍摄
|
||
maxDuration: 60, // 最长60秒
|
||
camera: 'back', // 默认后置摄像头
|
||
compressed: true, // 压缩视频
|
||
success(res) {
|
||
const tempFilePath = res.tempFilePath
|
||
|
||
// 立即添加一个发送中的消息
|
||
const tempId = Date.now().toString()
|
||
const tempMessage = {
|
||
sendId: _this.userId,
|
||
content: tempFilePath, // 临时路径
|
||
type: 3,
|
||
status: 'sending',
|
||
tempId: tempId,
|
||
senderAvatar: uni.getStorageSync('user').avatar || 'https://api.ccttiot.com/smartmeter/img/static/default-avatar',
|
||
createTime: _this.$u.timeFormat(new Date(), 'yyyy-mm-dd hh:MM:ss')
|
||
}
|
||
_this.messages.push(tempMessage)
|
||
_this.sendingMessages.add(tempId)
|
||
|
||
_this.$nextTick(() => {
|
||
_this.scrollToBottom()
|
||
})
|
||
|
||
// 显示上传loading
|
||
uni.showLoading({
|
||
title: '视频上传中...',
|
||
mask: true
|
||
})
|
||
wx.uploadFile({
|
||
url: 'https://up-z2.qiniup.com',
|
||
name: 'file',
|
||
filePath: tempFilePath,
|
||
formData: {
|
||
token: _this.token, // 后端返回的token
|
||
key: 'smartmeter/video/' + fileKey // 修改存储路径为video目录
|
||
},
|
||
success: function(uploadRes) {
|
||
uni.hideLoading()
|
||
let responseData = JSON.parse(uploadRes.data);
|
||
const videoUrl = 'https://api.ccttiot.com/' + responseData.key
|
||
|
||
// 更新消息内容为真实URL
|
||
const index = _this.messages.findIndex(msg => msg.tempId === tempId)
|
||
if (index !== -1) {
|
||
_this.messages[index].content = videoUrl
|
||
}
|
||
|
||
// 视频特殊处理:保存缩略图
|
||
if(res.thumbTempFilePath){
|
||
_this.uploadThumb(res.thumbTempFilePath)
|
||
}
|
||
|
||
// 发送消息到服务器
|
||
let data = {
|
||
teamId: _this.teamId,
|
||
content: videoUrl,
|
||
type: 3
|
||
}
|
||
_this.$u.post(`/app/chat/sendMessage`, data).then(res => {
|
||
if (res.code == 200) {
|
||
console.log('视频消息发送成功')
|
||
// 备用机制:如果3秒后WebSocket没有更新状态,则手动更新
|
||
setTimeout(() => {
|
||
const index = _this.messages.findIndex(msg => msg.tempId === tempId)
|
||
if (index !== -1 && _this.messages[index].status === 'sending') {
|
||
console.log('WebSocket未响应,手动更新视频状态为已发送')
|
||
_this.messages[index].status = 'sent'
|
||
_this.sendingMessages.delete(tempId)
|
||
}
|
||
}, 3000)
|
||
} else {
|
||
const index = _this.messages.findIndex(msg => msg.tempId === tempId)
|
||
if (index !== -1) {
|
||
_this.messages[index].status = 'failed'
|
||
}
|
||
_this.sendingMessages.delete(tempId)
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
}
|
||
}).catch(err => {
|
||
const index = _this.messages.findIndex(msg => msg.tempId === tempId)
|
||
if (index !== -1) {
|
||
_this.messages[index].status = 'failed'
|
||
}
|
||
_this.sendingMessages.delete(tempId)
|
||
uni.showToast({
|
||
title: '发送失败,请重试',
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
})
|
||
},
|
||
fail(err) {
|
||
uni.hideLoading()
|
||
const index = _this.messages.findIndex(msg => msg.tempId === tempId)
|
||
if (index !== -1) {
|
||
_this.messages[index].status = 'failed'
|
||
}
|
||
_this.sendingMessages.delete(tempId)
|
||
uni.showToast({
|
||
title: '上传失败',
|
||
icon: 'none'
|
||
})
|
||
console.error('视频上传失败:', err)
|
||
}
|
||
})
|
||
},
|
||
fail(err) {
|
||
console.log('视频选择失败:', err)
|
||
}
|
||
})
|
||
},
|
||
// 上传视频缩略图
|
||
uploadThumb(thumbPath) {
|
||
let _this = this
|
||
let thumbKey = 'static/thumb/' + _this.$u.guid(20) + '.jpg'
|
||
wx.uploadFile({
|
||
url: 'https://up-z2.qiniup.com',
|
||
name: 'file',
|
||
filePath: thumbPath,
|
||
formData: {
|
||
token: _this.token,
|
||
key: thumbKey
|
||
},
|
||
success(res) {
|
||
console.log('缩略图上传成功')
|
||
}
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
page {
|
||
background: #010000;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
.page {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
.chat-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: calc(100vh - 350rpx - 70rpx);
|
||
background-color: #010000;
|
||
width: 750rpx;
|
||
position: relative;
|
||
top: 240rpx;
|
||
transition: transform 0.3s;
|
||
|
||
.message-list {
|
||
flex: 1;
|
||
padding: 20rpx;
|
||
width: 100%;
|
||
height: calc(100% - 120rpx);
|
||
overflow: hidden;
|
||
box-sizing: border-box;
|
||
.message-item {
|
||
margin-bottom: 20rpx;
|
||
.avatar {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
background-color: #ccc;
|
||
border-radius: 50%;
|
||
margin-left: 15rpx;
|
||
}
|
||
.message-self {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
|
||
.message-content {
|
||
background-color: #4D3F53;
|
||
color: white;
|
||
padding: 15rpx 20rpx;
|
||
border-radius: 10rpx;
|
||
max-width: 70%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8rpx;
|
||
|
||
.message-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10rpx;
|
||
flex-shrink: 0;
|
||
|
||
.message-status {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.status-icon {
|
||
width: 24rpx;
|
||
height: 24rpx;
|
||
|
||
&.sending {
|
||
animation: rotate 1s linear infinite;
|
||
}
|
||
|
||
&.failed {
|
||
opacity: 0.7;
|
||
}
|
||
}
|
||
|
||
.status-text {
|
||
font-size: 18rpx;
|
||
color: #999;
|
||
white-space: nowrap;
|
||
}
|
||
}
|
||
|
||
.message-time {
|
||
font-size: 18rpx;
|
||
color: #999;
|
||
white-space: nowrap;
|
||
}
|
||
}
|
||
|
||
.message-text {
|
||
word-break: break-all;
|
||
line-height: 1.4;
|
||
margin-top: 4rpx;
|
||
}
|
||
}
|
||
.message-image {
|
||
max-width: 400rpx;
|
||
border-radius: 10rpx;
|
||
}
|
||
.message-video {
|
||
max-width: 400rpx;
|
||
border-radius: 10rpx;
|
||
}
|
||
}
|
||
|
||
.message-other {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
|
||
.avatar {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
background-color: #ccc;
|
||
border-radius: 50%;
|
||
margin-right: 15rpx;
|
||
}
|
||
|
||
.message-content {
|
||
background-color: #333;
|
||
color: white;
|
||
padding: 15rpx 20rpx;
|
||
border-radius: 10rpx;
|
||
max-width: 70%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8rpx;
|
||
|
||
.message-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10rpx;
|
||
flex-shrink: 0;
|
||
|
||
.message-time {
|
||
font-size: 18rpx;
|
||
color: #999;
|
||
white-space: nowrap;
|
||
}
|
||
}
|
||
|
||
.message-text {
|
||
word-break: break-all;
|
||
line-height: 1.4;
|
||
margin-top: 4rpx;
|
||
}
|
||
}
|
||
.message-image {
|
||
max-width: 400rpx;
|
||
border-radius: 10rpx;
|
||
}
|
||
.message-video {
|
||
max-width: 400rpx;
|
||
border-radius: 10rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.bottom-area {
|
||
background-color: #010000;
|
||
border-top: 1rpx solid #333;
|
||
|
||
.input-container {
|
||
display: flex;
|
||
padding: 20rpx;
|
||
|
||
.input-box {
|
||
flex: 1;
|
||
height: 80rpx;
|
||
background-color: #333;
|
||
color: white;
|
||
padding: 0 20rpx;
|
||
border-radius: 40rpx;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.send-button {
|
||
margin-left: 20rpx;
|
||
background-color: #FF8998;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 40rpx;
|
||
padding: 0 30rpx;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
font-size: 28rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.tablexx{
|
||
width: 730rpx;
|
||
height: 266rpx;
|
||
background: #010000;
|
||
border-radius: 20rpx 20rpx 20rpx 20rpx;
|
||
border-image: linear-gradient(226deg, rgba(255, 137.00000703334808, 152.0000061392784, 0.1899999976158142), rgba(255, 137.00000703334808, 152.0000061392784, 0)) 2 2;
|
||
padding: 32rpx 36rpx;
|
||
box-sizing: border-box;
|
||
position: fixed;
|
||
top: 150rpx;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
z-index: 99;
|
||
.jieshao{
|
||
font-size: 24rpx;
|
||
color: #FFFFFF;
|
||
margin-top: 16rpx;
|
||
}
|
||
.dangqian{
|
||
font-weight: 600;
|
||
font-size: 28rpx;
|
||
color: #FFFFFF;
|
||
margin-top: 26rpx;
|
||
}
|
||
.pianhao{
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
view{
|
||
margin-top: 20rpx;
|
||
font-size: 24rpx;
|
||
color: #6E7191;
|
||
display: flex;
|
||
align-items: center;
|
||
image{
|
||
height: 32rpx;
|
||
width: 32rpx;
|
||
margin-right: 16rpx;
|
||
}
|
||
}
|
||
}
|
||
.top{
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
font-weight: 600;
|
||
font-size: 32rpx;
|
||
color: #FFFFFF;
|
||
button{
|
||
background-color: transparent;
|
||
margin: 0 !important;
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
line-height: 32rpx;
|
||
position: relative;
|
||
image{
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
position: absolute;
|
||
right: 10rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@keyframes rotate {
|
||
from {
|
||
transform: rotate(0deg);
|
||
}
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
.function-icons {
|
||
display: flex;
|
||
padding: 20rpx;
|
||
border-top: 1rpx solid #333;
|
||
|
||
.icon-item {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
|
||
image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
}
|
||
</style> |