You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1092 lines
33 KiB

3 weeks ago
<template>
<view class="content">
<!-- 聊天内容 -->
<scroll-view class="chat" scroll-y="true" scroll-with-animation="true" :scroll-into-view="scrollToView"
refresher-enabled="true" :refresher-triggered="triggered" :refresher-threshold="100"
refresher-background="#f0f0f0" @refresherpulling="onPulling"
@refresherrefresh="onRefresh" @refresherrestore="onRestore" @refresherabort="onAbort">
<view class="chat-main" :style="{paddingBottom:inputh+'px'}">
<!-- <view class="chat-ls" v-if="unshiftmsg.length==0">
<view class="chat-time">现在可以开始聊天了.</view>
</view> -->
<view class="chat-ls" v-for="(item,index) in unshiftmsg" :key="index" :id="'msg'+ index">
<view class="chat-time" v-if="item.fromtime != ''">{{changeTime(item.fromtime)}}</view>
<view class="msg-m msg-left" v-if="item.fromuser !== friendcode">
<view class="user-img" >
<image class="uimg" :src="item.headimg" @error="handleImageError($event,index)"></image>
<text class="uname">{{item.fromname}}</text>
</view>
<view class="message" v-if="item.type == 'txt'">
<!-- 文字 -->
<view class="msg-text">
<text selectable>{{item.content}}</text>
</view>
</view>
<view class="message" v-if="item.type == 'image'" @tap="previewImg(item.content)">
<!-- 图像 -->
<image :src="item.content" class="msg-img" mode="widthFix"></image>
</view>
<view class="messagevideo" v-if="item.type == 'video'">
<!-- 视频 -->
<myVideo :videoUrl="item.content"></myVideo>
</view>
<view class="message" v-if="item.type == 'audio'" @click="playVoice(item.content,item.ifaudio,index)">
<!-- 音频 -->
<view class="msg-text voice" :style="{width:item.time*40+'rpx'}">
<view class="voicel" v-if="item.ifaudio" >
<div class="bg voicePlay"></div>
</view>
<image v-else src="../../static/icon/horn.png" class="voice-img"></image>
{{item.time}}
</view>
</view>
</view>
<view class="msg-m msg-right" v-if="item.fromuser == friendcode">
<view class="user-img" >
<image class="uimg" :src="item.headimg" @error="handleImageError($event,index)" ></image>
<!-- <text class="uname"></text> -->
<text class="uname">{{item.fromname}}</text>
</view>
<view class="message" v-if="item.type == 'txt'">
<view class="msg-text">
<text selectable>{{item.content}}</text>
</view>
</view>
<view class="message" v-if="item.type == 'image'" @tap="previewImg(item.content)">
<image :src="item.content" class="msg-img" mode="widthFix"></image>
</view>
<view class="messagevideo" v-if="item.type == 'video'" >
<!-- 视频 -->
<myVideo :videoUrl="item.content"></myVideo>
</view>
<view class="message" v-if="item.type == 'audio'" @click="playVoice(item.content,item.ifaudio,index)">
<!-- 音频 -->
<view class="msg-text voice" :style="{width: item.time>2? item.time*40+'rpx' :'auto'}">
{{item.time}}
<view class="voicel" v-if="item.ifaudio" >
<div class="bg voicePlay"></div>
</view>
<image v-else src="../../static/icon/hornrw.png" class="voice-img"></image>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
<submit @inputs="inputs" @heights="heights"></submit>
<u-toast ref="uToast" />
</view>
</template>
<script>
import { myCache } from '../../utils/utils.js';
import dateTime from '@/common/dateTime.js';
import submit from '@/components/chat/submit.vue';
import myVideo from "@/components/myVideo.vue";
//音频播放
const innerAudioContext = uni.createInnerAudioContext();
export default {
components: {
submit,
myVideo
},
data() {
return {
text:"",
triggered: false,
friendName: "",
friendcode: "",
friendheadimg: "",
info:{},// 会话基本信息
imgurl:"",// uni.$http.baseUrl,
iftop:true,
msg: [
// {
// fromuser: "11",
// fromname: "Madeline 老师 (女)",
// fromtime:'2025-06-20 10:00:00',
// headimg:require("@/static/image/i1.png"),
// content:'Hixxx我是普拉提教练小a周日上午10点的课您可以准时参加吧',
// type:'txt'
// },
], // 排序用,暂不使用
// 反转数据接收
unshiftmsg: [
// {
// fromuser: "11",
// fromname: "Madeline 老师 (女)",
// fromtime:'2025-06-20 10:00:00',
// headimg:require("@/static/image/i1.png"),
// content:'Hixxx我是普拉提教练小a周日上午10点的课您可以准时参加吧',
// type:'txt'
// },
// {
// fromuser: "1",
// fromname: "测试人",
// fromtime:'2025-06-20 10:35:40',
// headimg:require("@/static/image/girl.jpg"),
// content:'没问题',
// type:'txt'
// },
], // chatrurn
imgMsg: [],
scrollToView: '',
oldTime: new Date(),
inputh: '90',
loadStatus:true,
ifaudio:false, // 是否音频
socket: null, // WebSocket实例
isConnected: false, // WebSocket连接状态
}
},
onLoad(options) {
var user = myCache('user');
// 本人聊天信息
this.friendName=user.nickName?user.nickName:"";
this.friendcode=user.userid?user.userid:"";
this.friendheadimg=user.avatar?user.avatar:require("@/static/image/girl.jpg");
if(options.data){
// 会话信息
this.info=JSON.parse(decodeURIComponent(options.data));
// 会话标题
var title =this.info.chatType=="teacher"?'教练'+this.info.chatName:this.info.chatName;
uni.setNavigationBarTitle({
title: title,
});
// 先获取缓存
this.getchatstore();
// socket接收信息
this.socketinit();
}
},
onShow() {
// 跳转到最后一条数据 与前面的:id进行对照
this.screendo(this.unshiftmsg.length - 1);
},
onBackPress(options) {
if (options.from === 'backbutton') {
// 来自顶部菜单的返回按钮
// 在这里处理你的逻辑
console.log('返回按钮被点击');
this.closeWebSocket();
uni.onSocketClose(function (res) {
console.log('WebSocket 已关闭!');
});
return false;
}
},
beforeDestroy() {
console.log('界面关闭socketbeforeDestroy');
// 在组件销毁前,确保关闭 WebSocket 连接
this.closeWebSocket();
},
//点击导航栏 buttons 时触发 添加任务
onNavigationBarButtonTap: async function(e) {
const _that = this;
const index = e.index;
if (index === 0) {
var data=encodeURIComponent(JSON.stringify(_that.info));
uni.navigateTo({
url: `/pages/chat/chatset?data=${data}`
});
}
},
methods: {
gotoset(){
var data=encodeURIComponent(JSON.stringify(this.info));
uni.navigateTo({
url: `/pages/chat/chatset?data=${data}`
});
},
tuclose(){
this.iftop=false;
},
handleImageError(e,index){
console.log(e,index);
this.unshiftmsg[index]["headimg"]= require("@/static/image/girl.jpg");
this.$forceUpdate();
},
// 获取聊天记录
getchatstore(){
var that=this;
try{
// 先获取会话缓存
console.log(this.info.chatId);
var data= myCache(this.info.chatId)?myCache(this.info.chatId):[];
data.forEach((cell,i)=>{
cell["ifaudio"]=false;
//时间间隔处理
if (i < this.unshiftmsg.length - 1) {
//这里表示头部时间还是显示一下
let t = dateTime.spaceTime(this.oldTime, cell.fromtime);
if (t) {
this.oldTime = t;
}
cell.fromtime = t;
}
// 获取图片,为下面的预览做准备
if (cell.type == 'image') {
this.imgMsg.unshift(cell.content)
}
else if(cell.type=='txt'){
cell.content=decodeURIComponent(cell.content);
}
this.unshiftmsg.push(cell);
// 跳转到最后一条数据 与前面的:id进行对照
setTimeout(() => {
that.screendo(that.unshiftmsg.length-2);
}, 300);
});
if(this.unshiftmsg.length<=0)
{
// 首次教练会话自动信息
var cont = 'Hi'+this.friendName+',欢迎您!请问有什么可以帮助您的呢?';
if(this.info.chatType=="teacher"){
cont = 'Hi'+this.friendName+',欢迎您!我是'+this.info.chatName+'教练,请问有什么可以帮助您的呢?';
}
this.unshiftmsg = [
{
"fromname": this.info.chatName,
"fromuser": this.info.friendId,
"headimg": this.info.chatAvatar,
"toname": this.friendName, // 接收人
"touser": this.friendcode, // 接收人姓名
"content": cont,
"time": 0,
"ifaudio":false,
"fromtime": new Date(),
"type": 'txt'
}
];
this.$forceUpdate();
}
// 请求服务器加载加载拉取离线聊天记录
if(this.info.minId>0){
this.getList(1);
}
}
catch(err){
console.log(err);
}
},
// ws接收消息
socketinit(){
var that = this;
if (!this.isConnected) {
this.socket = uni.connectSocket({
url: "wss://www.sanduolantoyoga.com/yoga-imserver/",
});
uni.onSocketOpen(res => {
console.log('WebSocket连接已打开');
that.isConnected = true;
// 登录参数
let loginInfo = {
cmd: 0,//登录
data: {
accessToken: uni.getStorageSync("token")
}
};
// this.socket.send(JSON.stringify(loginInfo));
});
uni.onSocketMessage(res => {
console.log('收到WebSocket服务器消息', res.data);
if(res.data){
let data=JSON.parse(res.data);
console.log(data);
if(data){
// 加入聊天记录
// 消息类型 0:文字 1:图片 2:文件 3:语音 4:视频 10, "撤回" 11, "已读 "12, "消息已读回执 " 30,"加载中标记"
if(data.type=='0'){
data.content=decodeURIComponent(data.content);
}
let mdata = {
"fromname": this.friendName,
"fromuser": this.friendcode,
"headimg": this.friendheadimg,
"toname": this.info.chatName, // 接收人
"touser": this.info.friendId, // 接收人姓名
"content": data.content,
"time": data.type==3?5:0,
"ifaudio":data.type==3? true:false,
"fromtime": data.sendTime,
"type": data.type==0?'txt':(data.type==1?'image':data.type==3?'audio':(data.type==4?'video':'')),
"id": data.id,
"recvId": data.recvId,
"sendId": data.sendId,
};
if(data.type==0||data.type==1||data.type==3||data.type==4){
that.unshiftmsg.push(data);
that.$forceUpdate();
// 跳转到最后一条数据 与前面的:id进行对照
that.$nextTick(function() {
that.scrollToView = 'msg' + (that.unshiftmsg.length);
});
}
else{
// 10, "撤回" 11, "已读 "12, "消息已读回执 " 30,"加载中标记"
}
}
}
});
uni.onSocketClose(res => {
console.log('WebSocket连接已关闭');
that.isConnected = false;
});
uni.onSocketError(err => {
console.error('WebSocket连接打开失败请检查', err);
});
}
},
// 发送WebSocket消息
sendWebSocketMessage() {
if (this.socket && this.isConnected) {
uni.sendSocketMessage({
data: '这是一条测试消息' // 发送的消息内容
});
} else {
console.error('WebSocket未连接或未打开请先连接WebSocket');
}
},
// 关闭WebSocket连接
closeWebSocket() {
if (this.socket) {
uni.closeSocket();
this.socket = null;
this.isConnected = false;
}
},
// 加载对话
async getList(ii) {
var that=this;
if(this.loadStatus)
{
const {data: res} = await uni.$http.get('/api/message/private/pullOfflineMessage',{minId:this.info.minId});
this.triggered = false;
if(res.success){
if(res.data&&res.data.length>0){
const gotonum=res.data.length;
res.data.forEach((cell,i)=>{
cell["ifaudio"]=false;
//时间间隔处理
if (i < this.unshiftmsg.length - 1) {
//这里表示头部时间还是显示一下
let t = dateTime.spaceTime(this.oldTime, cell.fromtime);
if (t) {
this.oldTime = t;
}
cell.fromtime = t;
}
// 获取图片,为下面的预览做准备
if (cell.type == 'image') {
// cell.content=this.imgurl+'/images/'+cell.content;
this.imgMsg.unshift(cell.content)
}
else if(cell.type=='txt'){
cell.content=decodeURIComponent(cell.content);
}
this.unshiftmsg.unshift(cell);
});
if(ii==2){
// 跳转到加载数据的第一条 与前面的:id进行对照
setTimeout(() => {
that.screendo(gotonum -2);
}, 200);
}
else{
// 跳转到最后一条数据 与前面的:id进行对照
setTimeout(() => {
that.screendo(that.unshiftmsg.length-1);
}, 300);
}
// 缓存聊天
myCache(this.info.chatId);
}
else{
this.$refs.uToast.show({
title: "信息已加载完毕...",
type: "success",
duration: 2000,
});
this.loadStatus=false;
}
}
else{
this.$refs.uToast.show({
title: "信息加载失败...",
type: "error",
duration: 2000,
});
}
}
else{
this.triggered = false;
this.$refs.uToast.show({
title: "信息已加载完毕...",
type: "success",
duration: 2000,
});
}
},
screendo(scrindex){
this.scrollToView = '';
// 跳转到最后一条数据 与前面的:id进行对照
this.$nextTick(function() {
this.scrollToView = 'msg' + scrindex
});
},
// 自定义下拉刷新控件被下拉
onPulling(e) {
var that=this;
// console.log("onpulling", e);
if (!this.triggered) {
//下拉刷新先变true再变false才能关闭
this.triggered = true;
//关掉圈圈,需要先执行完刷新操作
setTimeout(() => {
that.triggered = false;
}, 2000);
}
},
// 自定义下拉刷新被触发
onRefresh(event){
console.log("下拉加载"+event);
// 加载聊天记录
this.getList(2);
},
// 自定义下拉刷新被复位
onRestore() {
this.triggered = 'restore'; // 需要重置
console.log("onRestore");
},
// 自定义下拉刷新被中止
onAbort() {
this.triggered = false;
console.log("onAbort");
},
changeTime(date) {
return dateTime.dateTime1(date);
},
// 进行图片的预览
previewImg(e) {
let index = 0;
console.log(this.imgMsg)
for (let i = 0; i < this.imgMsg.length; i++) {
if (this.imgMsg[i] == e) {
index = i;
}
}
console.log("index", index)
// 预览图片
uni.previewImage({
current: index,
urls: this.imgMsg,
longPressActions: {
itemList: ['发送给朋友', '保存图片', '收藏'],
success: function(data) {
console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');
},
fail: function(err) {
console.log(err.errMsg);
}
}
});
},
//音频播放
playVoice(e,ifplay,index) {
var that=this;
// 先关闭停止所有
innerAudioContext.src = '';
innerAudioContext.stop();
that.unshiftmsg.forEach((cell,i)=>{
cell.ifaudio=false;
});
ifplay=!ifplay;
this.unshiftmsg[index].ifaudio=ifplay;
this.$forceUpdate();
if(ifplay){
innerAudioContext.src = e;
innerAudioContext.play(() => {
console.log('开始播放');
});
// 监听音频播放结束事件
innerAudioContext.onEnded((res) => {
console.log('音频播放结束');
that.unshiftmsg[index].ifaudio=!ifplay;
that.$forceUpdate();
// 在这里执行你想要的操作
});
// innerAudioContext.onPlay(() => {
// console.log('开始播放');
// });
}
else{
innerAudioContext.src = '';
innerAudioContext.stop();
}
},
//地图定位
covers(e) {
let map = [{
latitude: e.latitude,
longitude: e.longitude,
iconPath: '../../static/image/head.png'
}]
return (map);
},
//跳转地图信息
openLocation(e) {
uni.openLocation({
latitude: e.latitude,
longitude: e.longitude,
name: e.name,
address: e.address,
success: function() {
console.log('success');
}
});
},
fullscreenchange(e){
if(!e.detail.fullScreen){ // 退出全屏,锁定竖屏
plus.screen.lockOrientation('portrait-primary');
}
},
//接受输入内容
async inputs(e) {
console.log(e);
var rindex=this.unshiftmsg.length-1;
let data = {
"fromname": this.friendName,
"fromuser": this.friendcode,
"headimg": this.friendheadimg,
"toname": this.info.chatName, // 接收人
"touser": this.info.friendId, // 接收人姓名
"content": e.type=='audio'?e.message.voice:e.message,
"time": e.type=='audio'?e.message.time:0,
"ifaudio":false,
"fromtime": new Date(),
"type": e.type
};
if(e.type!=='video'){
this.unshiftmsg.push(data);
rindex=this.unshiftmsg.length-1;
// 跳转到最后一条数据 与前面的:id进行对照
this.screendo(this.unshiftmsg.length - 1);
}
// 发送给服务器消息
var that = this;
if(e.type=='txt'){
// 文本
// 消息发送
this.onSendWS(data,rindex);
}
else if(e.type=='image'){
// 图片
// 先上传在传送消息
var token= uni.getStorageSync("token");
console.log('先上传在传送消息');
uni.showLoading({
title: '图片上传中...'
});
uni.uploadFile({
url: uni.$http.baseUrl+"/api/image/upload",
filePath: e.message,
name: 'file',
header: {
'Authorization': token
},
success: res => {
console.log(res);
uni.hideLoading();
var rdata=JSON.parse(res.data);
console.log(rdata);
if(rdata.data&&rdata.data.originUrl){
var url=rdata.data&&rdata.data.originUrl?rdata.data.originUrl:'';
data.content= url;
console.log(data);
// 图片放入集合
if (e.type == 'image'&&url) {
this.imgMsg.push(url);
}
// 消息发送
this.onSendWS(data,rindex);
}
},
fail: res => {
uni.hideLoading();
console.log(res)
}
});
}
else if(e.type=='audio'){
// 音频
// 先上传在传送消息
var token= uni.getStorageSync("token");
console.log('先上传在传送消息');
uni.showLoading({
title: '音频上传中...'
});
uni.uploadFile({
url: uni.$http.baseUrl+"/api/file/upload",
filePath: e.message.voice,
name: 'file',
header: {
'Authorization': token
},
success: res => {
console.log(res);
uni.hideLoading();
var rdata=JSON.parse(res.data);
console.log(rdata);
if(rdata.data&&rdata.data.originUrl){
var url=rdata.data&&rdata.data.originUrl?rdata.data.originUrl:'';
data.content= url;
console.log(data);
// 消息发送
this.onSendWS(data,rindex);
}
},
fail: res => {
uni.hideLoading();
console.log(res)
}
});
}
else if(e.type=='video'){
// 视频
// 先上传在传送消息
var token= uni.getStorageSync("token");
console.log('先上传在传送消息');
uni.showLoading({
title: '视频上传中...'
});
uni.uploadFile({
url: uni.$http.baseUrl+"/api/file/upload",
filePath: e.message,
name: 'file',
header: {
'Authorization': token
},
success: res => {
uni.hideLoading();
console.log(res);
var rdata=JSON.parse(res.data);
console.log(rdata);
if(rdata.data&&rdata.data.originUrl){
var url=rdata.data&&rdata.data.originUrl?rdata.data.originUrl:'';
data.content= url;
console.log(data);
// 信息发送聊天框
this.unshiftmsg.push(data);
rindex=this.unshiftmsg.length-1;
// 跳转到最后一条数据 与前面的:id进行对照
this.screendo(this.unshiftmsg.length - 1);
// 消息发送
this.onSendWS(data,rindex);
}
},
fail: res => {
uni.hideLoading();
console.log(res)
}
});
}
},
// 消息发送
async onSendWS(datamsg,rindex){
var message=JSON.stringify(datamsg);
var msssagebf=JSON.parse(message);
if(msssagebf.type=='txt'){
msssagebf.content=encodeURIComponent(msssagebf.content);
}
var mtype= '0'; // 消息类型 0:文字 1:图片 2:文件 3:语音 4:视频
if(msssagebf.type=='txt'){
mtype='0';
}
else if(msssagebf.type=='image'){
mtype='1';
}
else if(msssagebf.type=='audio'){
mtype='3';
}
else if(msssagebf.type=='video'){
mtype='4';
}
var param={
"recvId": this.info.friendId,
"content": msssagebf.content,
"type": mtype
}
// 发送消息
const {data: res} = await uni.$http.post('/api/message/private/send',param);
if(res.data){
var rrdata=res.data;
if(rrdata.type==0){
rrdata.content=decodeURIComponent(rrdata.content);
}
this.unshiftmsg[this.unshiftmsg.length-1]["content"]=rrdata.content;
this.unshiftmsg[this.unshiftmsg.length-1]["id"]=rrdata.id;
this.unshiftmsg[this.unshiftmsg.length-1]["recvId"]=rrdata.recvId;
this.unshiftmsg[this.unshiftmsg.length-1]["sendId"]=rrdata.sendId;
this.unshiftmsg[this.unshiftmsg.length-1]["fromtime"]=rrdata.sendTime;
this.$forceUpdate();
// 缓存聊天
myCache(this.info.chatId,this.unshiftmsg);
console.log('this.unshiftmsg',this.unshiftmsg);
// 更新聊天记录列表
var chatlastinfo={
id:this.info.chatId,
content:rrdata.content,
minId:rrdata.id,
datetime:rrdata.sendTime,
chatType: "teacher",
name: this.info.chatName,
fromuser: this.info.friendId,
img: this.info.chatAvatar,
};
var chatlist = myCache("chatlist");
if(chatlist&&chatlist.length>0){
var ifexist=0;
chatlist.forEach((cell,i)=>{
if(cell.id==this.info.chatId){
ifexist++;
chatlist[i]=chatlastinfo;
}
});
if(ifexist==0){
// 新增的添加
chatlist.unshift(chatlastinfo);
}
// 重新保存聊天记录
myCache("chatlist",chatlist);
}
else{
chatlist=[];
chatlist.push(chatlastinfo);
myCache("chatlist",chatlist);
}
}
},
//输入框高度
heights(e) {
this.inputh = e;
this.goBottom();
},
// 滚动到底部
goBottom() {
this.scrollToView = '';
this.screendo(this.unshiftmsg.length - 1);
}
}
}
</script>
<style lang="scss">
page {
height: 100%;
}
.content {
height: 100%;
background-color: rgba(244, 244, 244, 1);
position: relative;
}
::v-deep .uni-noticebar{
margin-bottom: 0 !important;
padding: 10rpx 12rpx !important;
border-radius: 10rpx;
}
.chat {
height: 100%;
.chat-main {
padding-left: 20rpx;
padding-right: 20rpx;
padding-top: 20rpx;
// padding-bottom: 120rpx; //获取动态高度
display: flex;
flex-direction: column;
}
.chat-ls {
.chat-time {
font-size: 24rpx;
color: rgba(39, 40, 50, 0.3);
line-height: 34rpx;
padding: 10rpx 0rpx;
text-align: center;
}
.msg-m {
display: flex;
padding: 20rpx 0;
.user-img {
flex: none;
width: 92rpx;
height: 90rpx;
border-radius: 20rpx;
text-align: center;
.uimg{
width: 70rpx;
height: 70rpx;
margin: 0 auto;
border-radius: 35rpx;
}
.uname{
height: 30rpx;
display: block;
margin: 0 auto;
line-height: 30rpx;
color: rgba(39, 40, 50, 0.5);
font-size: 24rpx;
}
}
.message {
flex: none;
max-width: 480rpx;
}
.messagevideo {
max-width: 480rpx;
height: 300rpx;
}
.msg-text {
font-size: 32rpx;
color: rgba(39, 40, 50, 1);
line-height: 44rpx;
padding: 18rpx 24rpx;
}
.msg-img {
max-width: 400rpx;
border-radius: 20rpx;
}
.msg-video{
width: 400rpx;
height: 300rpx;
border-radius: 20rpx;
}
.msg-map {
background: #fff;
width: 464rpx;
height: 284rpx;
overflow: hidden;
margin: 0 20rpx;
.map-name {
font-size: 32rpx;
color: rgba(39, 40, 50, 1);
line-height: 44rpx;
padding: 18rpx 24rpx 0 24rpx;
//下面四行是单行文字的样式
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
}
.map-address {
font-size: 24rpx;
color: rgba(39, 40, 50, 0.4);
padding: 0 24rpx;
//下面四行是单行文字的样式
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
}
.map {
padding-top: 8rpx;
width: 464rpx;
height: 190rpx;
}
}
.voice {
// width: 200rpx;
min-width: 100rpx;
max-width: 400rpx;
}
.voice-img {
width: 36rpx;
height: 36rpx;
}
}
.msg-left {
flex-direction: row;
.feed-imgy{
display: flex;
flex-direction: column;
justify-content: center;
align-self: center;
margin-right: 0rpx;
width: 95rpx;
height: 75rpx;
}
.feed-img{
display: flex;
flex-direction: column;
justify-content: center;
align-self: center;
margin-right: 0rpx;
width: 75rpx;
height: 75rpx;
}
.msg-text {
margin-left: 16rpx;
background-color: #fff;
border-radius: 0rpx 20rpx 20rpx 20rpx;
}
.ms-img {
margin-left: 16rpx;
}
.msg-video{
margin-left: 16rpx;
}
.msh-map {
margin-left: 16rpx;
border-radius: 0rpx 20rpx 20rpx 20rpx;
}
.msg-img {
margin: 0 0 0 20rpx;
}
.voice {
text-align: left;
display: flex;
}
.voice-img {
transform: rotate(180deg);
width: 36rpx;
height: 36rpx;
padding-bottom: 4rpx;
margin-right: 10rpx;
}
.voicel{
height: 32rpx;
width: 32rpx;
}
.bg {
background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAAAYCAYAAAAF6fiUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MEM0NzgwODc1MDY0MTFFRjk1QjhFRUUxQjYyOUQ5NDQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MEM0NzgwODg1MDY0MTFFRjk1QjhFRUUxQjYyOUQ5NDQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowQzQ3ODA4NTUwNjQxMUVGOTVCOEVFRTFCNjI5RDk0NCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowQzQ3ODA4NjUwNjQxMUVGOTVCOEVFRTFCNjI5RDk0NCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqxjhsEAAANzSURBVHjazJlLaNRQFIaTVIcRQUELoggVKYIgQvGFr40giLMRBAtjXblwUSmIK1vBTQUREUFB8NGdGwUXgoIVFwq6EGSkOAguXKiUQQq6kKK0Gv+LJ+HkTpLJfYUc+DnJ3M436Tn3eeKHYeiZ2NDQkGfZfCj03Nka6DzUgqZMYa1WS45HzIf15C/xqmeuE7ATOs3up1zxRefslYSgggn465j/GLpJ13ehfS75SEIu36/AFCQ6wQHoBbTgIOBZfBGow9AvaIXBb8d89PYFFpcEn7e5GAG+wXfPQc+g91DNQQKy+A3oB1SnkWDMR9BrbG0oxLeVgNAgCeLhF6FN0CuLiS3CP07+BDTgko/kbHC9BugG6w20ja63QxNSYk0tj/8EatP1uOYuKMFHoCdYW09+kDKnT0Kz0KjGKChik9AsxPkz0FnWvtIgsar8y2wUBDp8BDrBR+zS+CP4PCgyApZDa6Eb+ELDQQJiPs3DkV2FOnQ9atDjVfkPyS+DduvykQQtfpAypM7A3aPbB0iC7bNCgi+dRW6RP2owDanyf0Lv6HqPKl+KTxcf8czlBxnz2gjcPGXtiIOdSRb/Ofmt1KY7DanyZ8gP2uIjMYX4eXPeNPm9jg5Eafwv7IS+rkT+N/Kry+bnJeA3+T5HCUjjhxa3oCr8UOM3rfDzEnCQ/Fu2Q7K5be3i0+IWWadEfj/572Xzg4zywhW4VWwhs128y+LvJ9+mxbEs/mbyn2zxafHtyU87B4yzPe1JgOZZ8x8LwU/waTGL7BT5RwZTkSq/Rge06NCmxJfi08Wn8kQmP20ErKc56xIvpQJUx72NBMR8L1kKbkIb6fq6FKDQIb9BI1v8by91+YiPFj+1GgpYP4I9x+593IcZ05VOEsScOMfuxUuMr/Sg4uHHWNtSjUqlCv81HZDuQ8OqfPFChl7CxHx8NsbiE/Px+XChBKiYhXL0IPUMsYB9lopiddpthI74O9i0sIXVbbT5CPIAi02Cj7a2y2KcrjXp4UXpdpfUtmihIJfHjxbQp5rBL8xPC77NXY2JXaOeOS1t3fooAa74t9loaNrgI8gd1vsL8f0KvpSPdj4u3wuLncxFuj4mbSWVTH4pz3aSMR9/k8mv4jthz3HwD7HgXDAJfkaHTPDzgl+VKahs+wjdgT54/0vUzvhUos61fwIMANiWariLn8/jAAAAAElFTkSuQmCC);
width: 32rpx;
height: 32rpx;
background-size: 400%;
}
}
.msg-right {
flex-direction: row-reverse;
.feed-imgy{
display: flex;
flex-direction: column;
justify-content: center;
align-self: center;
margin-right: 0rpx;
width: 95rpx;
height: 75rpx;
}
.feed-img{
display: flex;
flex-direction: column;
justify-content: center;
align-self: center;
margin-right: 0rpx;
width: 75rpx;
height: 75rpx;
}
.msg-text {
margin-right: 16rpx;
background-color: #89965f;
font-family: PingFang SC, PingFang SC;
font-weight: 400;
font-size: 32rpx;
color: #FFFFFF;
line-height: 48rpx;
border-radius: 20rpx 0rpx 20rpx 20rpx;
}
.ms-img {
margin-right: 16rpx;
}
.msg-img {
margin: 0 20rpx 0 0;
}
.msg-video{
margin: 0 20rpx 0 0;
}
.msh-map {
margin-left: 16rpx;
border-radius: 20rpx 0rpx 20rpx 20rpx;
}
.voice {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
}
.voice-img {
display: block;
width: 36rpx;
height: 36rpx;
margin-left: 10rpx;
}
.voicel{
height: 32rpx;
width: 32rpx;
}
.bg {
background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAAAYCAYAAAAF6fiUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6REJBQzQ1RjQ1MDYzMTFFRjk5QjZEQkI5MDlENzE3RTQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6REJBQzQ1RjU1MDYzMTFFRjk5QjZEQkI5MDlENzE3RTQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpEQkFDNDVGMjUwNjMxMUVGOTlCNkRCQjkwOUQ3MTdFNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpEQkFDNDVGMzUwNjMxMUVGOTlCNkRCQjkwOUQ3MTdFNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pm5p+OoAAAKmSURBVHja1Jk/SBxBFMbdy6mHSEARNYIkhU0ICCEkEEynEYmkUEiTxmBtZZHCgGVsAgEhQozYaAo7G0GwjgemC1ZBgzbaKKnEIsb1HbwjH8PM7szdfEQffMzyzexvhn2zM/snSdO0wSOmRfdFc6JfDfGDzb++UUlAjmbTfzHi0T5UbP61Vl6DV3BxZggDYPNRyU1LQDtcnC9GXY/ojeh2HZ2z+ahbZD4lAZt6cQ4Mv1v0W+tm6+icza+qSOZTEvAAZudjo+5Q/SNRX40ds/m47JSIfFoC1nSQ24Y/r/4fUZdR1xHQMZtfVSOZH7LndPgmoEl0oQMdA/8OzNrXxjlzokvRgsfA2HxUM5nvUsmXbzt5CGZJE/jv1d832k8GPsmw+TgDEyLfd8PP5NsAM9q4bPi76r8FrwXgHzwHyOa7loDY/Kw73JtvAyzpCcvgtQLoKfgT6p0GDJDNtyWAwXepEMIvWF6O27Q8Aa8bjo/h+JGWWwEv32y+Ldh8jMsQvi0B1Y9DicUz/b9aNod8/SDzs/pk8V2Ry7cl4FTLTvCORBd63Av+Ny2HAwbF5tuCzXdFLt+WgD0t+8E7F/3Q40Hw17WuRbTqOSg23zbrGXyfyOdbNoYB2LBaLU8vx0b7UWj/0WOTYvNdGzGD76NMvu0OKGvWKjEO/ifY0KbB3xBN6eZ25jEr2HxXxOQnAf1m8x1ZW9SM7Rr+O8hmfx2z4jOZ71IsfoH9LegeDPSFUfcd3mSf1NjxXTI/64UsBj/av4WC47Y5EK3o8VejbkD0U1QUPa9xOTgk812bcSx+GrgM1fRLsvIl8Vxny4albqjOW5HNz+ubyY/2S/IZLBULhAGz+Tf+n7D5Ne8lYRBsPmXzjKWixyq1rOVD0Q7hZYXNz9oP/ntcCTAAHScbp0OSlTgAAAAASUVORK5CYII=);
width: 32rpx;
height: 32rpx;
background-size: 400%;
}
}
}
}
.voicePlay {
animation-name: voicePlay;
animation-duration: 1s;
animation-direction: normal;
animation-iteration-count: infinite;
animation-timing-function: steps(3);
}
@keyframes voicePlay {
0% {
background-position: 0;
}
100% {
background-position: 100%;
}
}
</style>