baodeng_xcx/page_user/pingzhuo/qunliao.vue
2025-06-23 08:56:52 +08:00

1092 lines
35 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>