功能:基础功能群聊 文件上传 图片上传 表情发送 超链接跳转
注:里面涉及到诸多函数依赖,请忽略,只看主要功能即可
html
<template> <div class="container" :style="{'top':'calc('+marginTop+' + 0.16rem)'}"> <div class="chart-message display-flex" :style="{'height':'calc(100vh - '+marginTop+' - '+(KEYBOARD_H+0.16)+'rem)'}"> <div class="chart-header"> <h2>{{groupChatInfo.groupName}}({{chatGroupList.length}})</h2> <div @click="handleGroupUser" class="more-user display-flex hvc"> <i class="icon icon-more"></i> </div> </div> <div class="chart-message-cont flex-1" @touchstart="handleMessageInputBlur"> <div class="chart-message-cont-wrap"> <div class="load-more" v-if="hasMore"> <span @click="getHistoryMessage({type:'loadMore'})">加载更多</span> </div> <template v-for="(item,index) in historyMessageList"> <!--加入者信息--> <div v-if="item.messageType == 'RC:InfoNtf'" class="chart-message-item message-time"> <p>{{item.content.message}}</p> </div> <!--接收者--> <div v-if="item.messageDirection == '2'&&item.messageType != 'RC:InfoNtf'" class="chart-message-item message-receive display-flex"> <div class="user-avatar" @click="handleAvatar({type:'2',data:item.content.extra})"> <img v-if="item.content.extra" :src="item.content.extra.avatar" alt=""> <img v-else src="./../../assets/images/industry/icon-avatar.png" alt=""> </div> <div> <p v-if="item.content.extra" style="color: #555;font-size: 0.2rem;line-height: 1.5;">{{item.content.extra.name}}</p> <div @click="handleDownloadFile({type:item.messageType,data:item})" class="user-msg " :class="{'display-flex':item.messageType == 'RC:FileMsg'}"> <template v-if="item.messageType == 'RC:TxtMsg'"> <p @contextmenu.stop.prevent="handleCopyText({data:item.content.content,eType:'contextmenu'})" @click="handleCopyText({data:item.content.content,eType:'click'})" v-html="chatMessageFormat({data:item.content.content,type:2})"></p> </template> <template v-else-if="item.messageType=='RC:ImgMsg'"> <img @click="handleImagePreview({url:item.content.imageUri})" :src="item.content.imageUri" alt=""> </template> <template v-else-if="item.messageType == 'RC:FileMsg'"> <div class="file-info"> <p class="file-name">{{item.content.name}}</p> <p class="file-size">{{item.content.size>1024*1024?(item.content.size/1024/1024).toFixed(2)+'MB':(item.content.size/1024).toFixed(2)+'KB'}}</p> </div> <div class="file-icon"> <img src="./../../assets/images/chat/icon-file.png" style="height: 0.68rem; 0.68rem;" alt=""> </div> </template> </div> </div> </div> <!--发送者--> <div v-if="item.messageDirection == '1'&&item.messageType != 'RC:InfoNtf'" class="chart-message-item message-send display-flex"> <div> <p v-if="item.content.extra" style="text-align: right;color: #555;font-size: 0.2rem;line-height: 1.5;">{{item.content.extra.name}}</p> <div @click="handleDownloadFile({type:item.messageType,data:item})" class="user-msg" :class="{'user-msg--file display-flex':item.messageType == 'RC:FileMsg'}"> <template v-if="item.messageType == 'RC:TxtMsg'"> <p @contextmenu.stop.prevent="handleCopyText({data:item.content.content,eType:'contextmenu'})" @click="handleCopyText({data:item.content.content,eType:'click'})" v-html="chatMessageFormat({data:item.content.content,type:1})"></p> </template> <template v-else-if="item.messageType=='RC:ImgMsg'"> <img @click="handleImagePreview({url:item.content.imageUri})" :src="item.content.imageUri" alt=""> </template> <template v-else-if="item.messageType == 'RC:FileMsg'"> <div class="file-icon"> <img src="./../../assets/images/chat/icon-file.png" style="height: 0.68rem; 0.68rem;" alt=""> </div> <div class="file-info"> <p class="file-name">{{item.content.name}}</p> <p class="file-size">{{item.content.size>1024*1024?(item.content.size/1024/1024).toFixed(2)+'MB':(item.content.size/1024).toFixed(2)+'KB'}}</p> </div> </template> </div> </div> <div @click="handleAvatar({type:'1',data:item.content.extra})" class="user-avatar"> <img v-if="item.content.extra" :src="item.content.extra.avatar" alt=""> <img v-else src="./../../assets/images/industry/icon-avatar.png" alt=""> </div> </div> <!--时间类型--> <div v-if="index < historyMessageList.length-1 &&historyMessageList[index+1].sentTime-historyMessageList[index].sentTime>10*60*1000" class="chart-message-item message-time"> <p v-html="chatMessageTimeFormat({data:historyMessageList[index+1]})"></p> </div> </template> </div> </div> <div class="chart-message-footer" v-if="sendMsgState"> <div class="btn-group display-flex"> <div class="btn-industry-map display-flex hvc"> <i v-if="browser.mobile" @touchstart="handleIndustryMap" class="icon icon-industryMap"></i> <i v-else @click="handleIndustryMap" class="icon icon-industryMap"></i> </div> <textarea @blur="messageBlur" ref="messageContent" @focus="messageFocus" @keydown.enter="handleSendMessage({event:$event,sendType:'text'})" v-model.trim="messageContent" placeholder="请输入内容" :class="messageContent?'message-val':'message-placeholder'" class="message-input flex-1"></textarea> <div class="btn-smile display-flex hvc"> <i class="icon icon-smile" v-if="browser.mobile" @touchstart="handleTogglePanelEmtion({event:$event})"></i> <i class="icon icon-smile" v-else @click="handleTogglePanelEmtion({event:$event})"></i> </div> <template v-if="messageContent"> <div class="btn-send display-flex hvc"> <span v-if="browser.mobile" @touchstart="handleSendMessage({event:$event,sendType:'text'})">发送</span> <span v-else @click="handleSendMessage({event:$event,sendType:'text'})">发送</span> </div> </template> <template v-else> <div class="btn-add display-flex hvc"> <i class="icon icon-add" v-if="browser.mobile" @touchstart="handleTogglePanelImage({event:$event})"></i> <i class="icon icon-add" v-else @click="handleTogglePanelImage({event:$event})"></i> </div> </template> </div> <div class="multi-fun-panel"> <div v-show="panelImageState" class="panel-image"> <ul> <li> <label for="uploadImage"> <input @change.prevent="handleUploadImg({event: $event})" style="display: none" accept="image/*" type="file" ref="uploadImage" id="uploadImage"> <img style=" 1.16rem;" src="./../../assets/images/chat/icon-upload-images.png" alt=""> <p>照片</p> </label> </li> <li> <label for="uploadFile"> <input @change.prevent="handleUploadFile({event: $event})" style="display: none" type="file" ref="uploadFile" id="uploadFile"> <img style=" 1.16rem;" src="./../../assets/images/chat/icon-upload-file.png" alt=""> <p>文件</p> </label> </li> </ul> </div> <div v-show="panelEmtionState" class="panel-emtion display-flex"> <template v-if="browser.mobile"> <Emotion class="emotion-item" v-for="(item, i) in emoticon" :key="i" @touchend.native="handleEmotion({data:item})">{{item}}</Emotion> </template> <template v-else> <Emotion class="emotion-item" v-for="(item, i) in emoticon" :key="i" @click.native="handleEmotion({event:$event,data:item})">{{item}}</Emotion> </template> </div> </div> </div> </div> </div> </template>
js
<script> import {ImagePreview} from 'vant' import {formatDate,browser,getOpenId,emotionParams ,addEvent,regularExpression,copyText,globalStatistics} from "../../../static/js/help"; import {getByToken,groupBoardChat,setMessageRead,groupChatInfo,getWechatInfoByUnionId ,attachUpload,chatAutoQuote,groupChatMembers} from "../../../static/js/api"; import {mapGetters} from 'vuex'; import Emotion from './../../components/chat/emotion' const avatar = require('./../../assets/images/industry/icon-avatar.png'); import {Dialog} from 'vant' window.vueThis = null; export default { name: "chatMessage", components:{ Emotion }, data() { return { userInfo:{ name:'', icon:'', appUserId:'' },//自己的用户信息 receiveTargetId:'',//接送消息的userId messageContent:'',//消息内容 historyMessageList:[],//历史消息 scrollDirection:'down',//down 向下,up向上 timestrap:new Date().getTime(),//历史消息limit hasMore:false,//是否展示加载更多 beforeScrollHeight:0,//滚动之前高度 sentTime:localStorage.sentTime,//记录已读消息时间 groupChatInfo:{},//群聊信息 wxUserInfo:{},//微信用户信息 emoticon:emotionParams.list, panelImageState:false,//图片上传面板 panelEmtionState:false,//表情面板 sendImage:'',//图片发送 chatGroupList:[], chatFilter:process.env.CHAT_FILTER,//聊天用户过滤列表 browser:browser.versions, KEYBOARD_H:0,//软盘高度 } }, watch:{ 'historyMessageList'(){ this.$nextTick(()=>{ this.scrollView(); }) }, 'im'(){ this.creatRongIMConnect(); }, }, computed:{ ...mapGetters ([ 'im', 'sysUserInfo' ]), deviceInfo() { return this.$store.getters.deviceInfo; }, marginTop() { if (this.deviceInfo.grade == '1.3' || this.deviceInfo.grade == '1.4') {//App return '0rem' } else if (this.deviceInfo.grade == '2') {//PC return '1.78rem' } else {//手机微信 return '0.9rem' } }, //发送框状态 sendMsgState(){ let state = true; for(let i = 0;i<this.chatFilter.length;i++){ if(this.chatFilter[i].id == this.receiveTargetId){ return false continue } } return state } }, methods: { //让input失去焦点 handleMessageInputBlur(){ this.$refs.messageContent.blur(); }, //点击头像 handleAvatar({type,data} = {}){ if(type == '1'){ this.$router.push({ name:'industrySetting' }) }else if(type == '2'){ this.$router.push({ name:'industryContacts', query:{ clientId:data.clientId } }) } }, //图片面板切换 handleTogglePanelImage({event} = {}){ this.panelImageState = !this.panelImageState; this.panelEmtionState = false; if(this.panelImageState){ this.$refs.messageContent.blur(); }else { this.$refs.messageContent.focus(); } event.preventDefault(); }, //表情面板切换 handleTogglePanelEmtion({event} = {}){ this.panelEmtionState = !this.panelEmtionState; this.panelImageState = false; if(this.panelEmtionState){ this.$refs.messageContent.blur(); }else { this.$refs.messageContent.focus(); } event.preventDefault(); }, //图片预览 handleImagePreview({url} = {}){ ImagePreview({ images: [url], closeable: true, }); }, //图片上传 handleUploadImg({event} = {}){ let file = event.target.files[0]; if(file.size>10*1024*1024){ this.$_toast('图片大小需小于1OMB') this.$refs.uploadImage.value = ''; return } attachUpload({ multipartFile:file }).then((res)=>{ if(res.data.state == 'SUCCESS'){ let url = res.data.url; this.sendImage = url; this.handleSendMessage({event:event,sendType:'image'}); this.$refs.uploadImage.value = ''; } }) event.preventDefault(); }, //文件上传 handleUploadFile({event} = {}){ let file = event.target.files[0]; let fileInfo = { name: file.name, size: file.size, type: file.type, } if(file.size>10*1024*1024){ this.$_toast('文件大小需小于1OMB') this.$refs.uploadFile.value = ''; return } attachUpload({ multipartFile:file }).then((res)=>{ if(res.data.state == 'SUCCESS'){ let url = res.data.url; this.sendImage = url; fileInfo.fileUrl = url; this.handleSendMessage({event:event,sendType:'file',fileInfo}); this.$refs.uploadFile.value = ''; } }) event.preventDefault(); }, //文件下载 handleDownloadFile({type,data} = {}){ if(type == 'RC:FileMsg'){ let {fileUrl,name,size} = data.content; this.$router.push({ name:'downloadFile', query:{ params:JSON.stringify({ fileUrl, name, size, }) } }) } }, //聊天消息格式化 chatMessageFormat({data,type} = {}){ if(type == 1){ var style = "color:#232323;text-decoration: underline" }else if(type == 2){ var style = "color:#333;text-decoration: underline" } let _html = data.replace(regularExpression.http, (res)=> { let url = res; if(res.indexOf('http')=='-1'){ url ='http://'+url; } return `<a style="${style}" onclick="handleCopyTextByMiniProgarm({event:event,url:'${url}'})" href="javascript:;">${res}</a>`; return this.deviceInfo.terminalType == 'MINIPROGRAM'?`<a style="${style}" onclick="handleCopyTextByMiniProgarm(event,'${res}')" href="javascript:;">${res}</a>`:`<a onclick="handleCopyTextByMiniProgarm(event,'${res}')" style="${style}" target="_blank" href="${url}">${res}</a>` }) _html = _html.replace(/#[u4E00-u9FA5]{1,3};/gi, this.emotionFormat) return _html }, //表情格式化 emotionFormat (res) { let word = res.replace(/#|;/gi,'') const list = emotionParams.list let index = list.indexOf(word) return `<img style=" 0.343rem;height: 0.343rem;display: inline-block;vertical-align: -4px" src="${emotionParams.url}${index}.gif" align="middle">` }, //选择表情 handleEmotion({event,data} ={}){ let emotion = `#${data};`; this.messageContent +=emotion; let h = this.$refs.messageContent.scrollHeight; let fontSize = 100/parseFloat($('html').css('font-size'));//px换算rem this.$refs.messageContent.scrollTop =h*fontSize*2; //event.preventDefault(); }, //切换到地图 handleIndustryMap(){ this.handleMessageInputBlur(); this.$parent.type = 1; localStorage.industryType = 1; globalStatistics({ eventType:'E_033', clickType:'C_032_0002' }) }, //聊天用户 getGroupChatMembers(){ groupChatMembers({ groupId:this.$route.query.groupId }).then((res)=>{ if(res.data.code == '0000'){ let data = res.data.data; this.chatGroupList = data; } }) }, //获取微信用户信息 getWechatInfoByUnionId(){ getWechatInfoByUnionId({ unionId:getOpenId() }).then((res)=>{ if(res.data.code == '0000'){ let data = res.data.data; this.wxUserInfo = data; } }) }, //input失去焦点滚动到可视区域 https://cdn.ronghub.com/RongIMLib-3.0.6-dev.min.js handleScrolltoView() { document.querySelector(".chart-message").scrollIntoView(true); }, //与建立融云连接 creatRongIMConnect(){ //只有获取到了targetId才能做其他操作 this.getGroupChatInfo().then(()=>{ this.watchRongIMLib();//监测 this.getHistoryMessage();//历史消息 this.clearnUnReadConvert() //清除未读 }) }, //设置监听 watchRongIMLib(){ this.im.watch({ conversation: ()=>{ console.log('消息列表更新'); }, message: (event)=>{ if(this.$route.name !='industry') return var message = event.message; let {messageUId,sentTime,type} = message; if(message.messageType !='RC:ReadNtf'){ if(message.type == 3){ message.receivedStatus = 0; this.historyMessageList.push(message); this.setMessageRead({ messageUId, lastMessageSendTime:sentTime, type}); this.clearnUnReadConvert() //清除未读 } console.log('我已接收到新消息'); }else { this.sentTime = localStorage.sentTime = message.sentTime; this.SetServerMessageRead();//服务器已读 console.log('我已接收到已读通知') } } }); }, //message失去焦点 messageBlur(){ this.handleScrolltoView(); }, //message获取焦点 messageFocus(){ this.panelImageState = false; this.panelEmtionState = false; }, //消息发送 handleSendMessage({event,sendType,fileInfo} = {}){ //手机回车不发消息 var conversation = this.im.Conversation.get({ targetId: this.receiveTargetId,//接收方USERID type: RongIMLib.CONVERSATION_TYPE.GROUP }); //发送文本 if(sendType == 'text'){ if(this.browser.mobile&&event.keyCode == '13'){ return } if(!this.messageContent) { this.$_toast('请输入内容'); return } //发送文本 var sendParams = { messageType: RongIMLib.MESSAGE_TYPE.TEXT, // 填写开发者定义的 messageType content: { // 填写开发者定义的消息内容 content:this.messageContent, extra:{ avatar:this.wxUserInfo.headImgUrl, name:this.sysUserInfo.name, clientId:this.sysUserInfo.client_id }, }, isPersited: true,// 是否存储在服务端,默认为 true isCounted: true, // 是否计数. 计数消息接收端接收后未读数加 1,默认为 true pushContent:'user 发送了一条消息', // Push 显示内容 pushData: 'Push 通知时附加信息', // Push 通知时附加信息, 可不填 isStatusMessage: false, // 设置为 true 后 isPersited 和 isCounted 属性失效 disableNotification: false, // 设置为 true 后移动端不会收到 Push 信息和本地通知提醒 } }else if(sendType == 'image'){ //发送图片 var sendParams = { messageType: RongIMLib.MESSAGE_TYPE.IMAGE, // 填写开发者定义的 messageType content: { // 填写开发者定义的消息内容 content:'',//base64图片地址 imageUri:this.sendImage,//图片url extra:{ avatar:this.wxUserInfo.headImgUrl, name:this.sysUserInfo.name, clientId:this.sysUserInfo.client_id }, }, isPersited: true,// 是否存储在服务端,默认为 true isCounted: true, // 是否计数. 计数消息接收端接收后未读数加 1,默认为 true pushContent:'user 发送了一条消息', // Push 显示内容 pushData: 'Push 通知时附加信息', // Push 通知时附加信息, 可不填 isStatusMessage: false, // 设置为 true 后 isPersited 和 isCounted 属性失效 disableNotification: false, // 设置为 true 后移动端不会收到 Push 信息和本地通知提醒 } }else if(sendType == 'file'){ //发文件 var sendParams = { messageType: RongIMLib.MESSAGE_TYPE.FILE, // 填写开发者定义的 messageType content: { ...fileInfo, extra:{ avatar:this.wxUserInfo.headImgUrl, name:this.sysUserInfo.name, clientId:this.sysUserInfo.client_id } }, isPersited: true,// 是否存储在服务端,默认为 true isCounted: true, // 是否计数. 计数消息接收端接收后未读数加 1,默认为 true pushContent:'user 发送了一条消息', // Push 显示内容 pushData: 'Push 通知时附加信息', // Push 通知时附加信息, 可不填 isStatusMessage: false, // 设置为 true 后 isPersited 和 isCounted 属性失效 disableNotification: false, // 设置为 true 后移动端不会收到 Push 信息和本地通知提醒 } } conversation.send(sendParams).then((message)=>{ this.scrollDirection = 'down'; this.historyMessageList.push(message); if(sendType == 'text'){ //自动报单 chatAutoQuote({ wechatNickName:this.wxUserInfo.nickname, content:this.messageContent }) } this.messageContent = ''; }); groupBoardChat({ targetToken:this.receiveTargetId, content:this.messageContent }) event.preventDefault(); }, //告诉对方,该消息已读过了 setMessageRead({messageUId,lastMessageSendTime,type}){ var conversation = this.im.Conversation.get({ targetId:this.receiveTargetId, type: RongIMLib.CONVERSATION_TYPE.GROUP }); // 以上 3 个属性在会话的最后一条消息中可以获得 conversation.send({ messageType: 'RC:ReadNtf', content: { messageUId: messageUId, lastMessageSendTime: lastMessageSendTime, type: type } }).then((message)=>{ console.log('消息我已经读了,发了通知给你'); }); }, //获取历史消息 getHistoryMessage({type}={}){ if(type == 'loadMore'){ this.scrollDirection = 'up' }else { this.scrollDirection = 'down' } var conversation = this.im.Conversation.get({ targetId: this.receiveTargetId,//接收方的 userId type: RongIMLib.CONVERSATION_TYPE.GROUP }); conversation.getMessages({ timestrap:this.timestrap, count: 20 }).then((result)=>{ console.log(result.list) this.scrollDirection = 'up'; this.beforeScrollHeight = $('.chart-message-cont-wrap').height(); this.historyMessageList = [...result.list,...this.historyMessageList]; // 历史消息列表 this.hasMore = result.hasMore; if(result.list.length){ this.timestrap = result.list[0].sentTime; } }); }, //滚动到可视区域 scrollView({animate}={animate:true}){ let scrollHeight = $('.chart-message-cont-wrap').height(); if(this.scrollDirection == 'down'){ if(animate){ $('.chart-message-cont').animate({scrollTop: scrollHeight+'px'}, 500); }else { $('.chart-message-cont').scrollTop(scrollHeight) } }else if(this.scrollDirection == 'up'){ $('.chart-message-cont').animate({scrollTop: (scrollHeight- this.beforeScrollHeight)+'px'}, 0); } }, //时间格式化 chatMessageTimeFormat({data}={}){ let currentDate = formatDate({date:data.sentTime}); let nowDate = formatDate(); if(currentDate == nowDate){ return formatDate({ date:data.sentTime, fmt:'hh:mm', }) }else { return formatDate({ date:data.sentTime, fmt:'yyyy-MM-dd hh:mm' }) } }, //清除未读数 clearnUnReadConvert(){ this.im.Conversation.get({ targetId:this.receiveTargetId, type: RongIMLib.CONVERSATION_TYPE.GROUP }).read().then(()=>{ console.log('清除未读数成功'); // im.watch conversation 将被触发 }); }, getUserInfo(){ getByToken({ targetId:this.receiveTargetId }).then((res)=>{ if(res.data.code == '0000'){ let data = res.data.data; if(data){ this.userInfo = data; } } }) }, //服务器已读 SetServerMessageRead(){ setMessageRead({ targetToken:this.receiveTargetId }) }, //获取群聊信息 getGroupChatInfo(){ return new Promise((resolve,reject)=>{ groupChatInfo({}).then((res)=>{ if(res.data.code == '0000'){ let data = res.data.data; this.groupChatInfo = data; this.receiveTargetId = data.groupId; document.title = data.groupName resolve(); } }) }) }, //查看群组用户 handleGroupUser(){ this.$router.push({ name:'chatGroupUser', query:{ groupId:this.receiveTargetId } }) }, //复制 handleCopyText({data,eType} ={}){ if(!this.browser.mobile&&eType =='click') return if(this.deviceInfo.grade == '2.1') { this.$_toast('请使用ctrl+c复制') return; } copyText({text:data}) }, stopEvent(event) { //阻止冒泡事件 //取消事件冒泡 var e = event; //若省略此句,下面的e改为event,IE运行可以,但是其他浏览器就不兼容 if (e && e.stopPropagation) { // this code is for Mozilla and Opera e.stopPropagation(); } else if (window.event) { // this code is for IE window.event.cancelBubble = true; } } }, created(){ this.getGroupChatMembers(); window.vueThis = this; }, activated(){ this.scrollView(); this.getGroupChatMembers(); if(this.im){ this.clearnUnReadConvert() //清除未读 } }, mounted() { this.SetServerMessageRead(); this.getWechatInfoByUnionId(); if(this.im){ this.creatRongIMConnect(); } addEvent({ele:'.panel-emtion'}) } } window.handleCopyTextByMiniProgarm = function(data){ if(window.vueThis.deviceInfo.terminalType == 'MINIPROGRAM'){ copyText({text:data.url,isTip:false}) Dialog.alert({ message: '小程序不支持打开链接,链接已复制请在浏览器中打开', }) }else { window.open(data.url,'_blank') } window.vueThis.stopEvent(event); } </script>
css
<style lang="scss" scoped> .container{ 7.5rem;margin: auto;position: relative; -moz-user-select:text; -webkit-user-select:text; } .chart-header{height: 0.93rem; 7.5rem;background: #fff; h2{font-size: 0.3rem;color: #2C2C2C;text-align: center;line-height: 0.93rem;font-weight: 700} .more-user{ 0.93rem; height: 0.93rem; position: absolute;right: 0;top: 0; .icon-more{ 0.36rem;height: 0.09rem;background: url("././../../assets/images/chat/icon-more.png") no-repeat;background-size: contain;} } } .chart-message{flex-direction: column;height: 100vh; .load-more{text-align: center;padding: 0.3rem 0;cursor: pointer; span{color: #555;} } .chart-message{ &-item{margin: 0.7rem 0; .user-avatar{ 0.88rem; img{ 0.88rem;height: 0.88rem;background: #fff;border-radius: 0.88rem;overflow: hidden;border: 0.01rem solid #eee;} } } &-cont{padding: 0 0.18rem;overflow: scroll;background: #f5f5f5; .message-time{color: #888888;font-size: 0.26rem;text-align: center;margin: 0.2rem 0; p{display: inline-block; background: rgba(0,0,0,0.2); padding: 2px 5px; color: #fff; border-radius: 2px;} } .message-receive{margin-right: 1.2rem; .user-avatar{margin-right: 0.2rem;} .user-msg {background: #fff;padding: 0.25rem 0.3rem;border: 0.01rem solid #DDDDDD;border-radius: 0.04rem 0.2rem 0.2rem 0.2rem; img{ auto;height: auto;max- 1.8rem;} .href{color: #333;text-decoration: underline} } .file-info{margin-right: 0.6rem;} .file-size{color: #999;font-size: 0.24rem;} p{font-size:0.28rem;color: #333;line-height: 0.45rem;word-break: break-all;} } .message-send{margin-left: 1.2rem;justify-content: flex-end; .user-avatar{margin-left: 0.2rem;} .user-msg {background: #9eea6a;padding: 0.25rem 0.3rem;border: 0.01rem solid #9eea6a;border-radius: 0.2rem 0.04rem 0.2rem 0.2rem;position: relative; img{ auto;height: auto;max- 1.8rem;} } .user-msg.user-msg--file{background: #fff;border: 0.01rem solid #DDDDDD;} .file-info{margin-left: 0.6rem;} .file-size{color: #232323;font-size: 0.24rem;} .href{color: #232323;text-decoration: underline;border: 1px solid red;} .read-state{position: absolute;font-size: 0.24rem; 0.6rem;text-align: center;right: 0;bottom: -0.4rem;background: rgba(0,0,0,0.1);border-radius: 0.05rem;color: #555;} p{font-size:0.28rem;color: #232323;line-height: 0.45rem;word-break: break-all;} } } &-footer{ .btn-group{ background: #fff;padding: 0.15rem 0.25rem;box-sizing: border-box;padding-bottom: 0.4rem; } .btn-industry-map{ 0.68rem;margin-right: 0.34rem; .icon-industryMap{ 0.68rem;height: 0.68rem;display: inline-block;background: url("./../../assets/images/industry/icon-industryMap.png") no-repeat;background-size: contain;cursor: pointer} } .btn-smile{ 0.56rem;margin-right: 0.24rem;margin-left: 0.24rem; .icon-smile{ 0.56rem;height: 0.56rem;display: inline-block;background: url("./../../assets/images/industry/icon-smile.png") no-repeat;background-size: contain;cursor: pointer} } .btn-add{ 0.56rem; .icon-add{ 0.56rem;height: 0.56rem;display: inline-block;background: url("./../../assets/images/industry/icon-add.png") no-repeat;background-size: contain;;cursor: pointer} } .btn-send{ 0.56rem; span{color: #5C86F7;font-size: 0.3rem;background: transparent;border: none;text-align: center;font-weight: bold;min- 1rem;white-space: nowrap;cursor: pointer} } .message-input{padding: 0;margin: 0;border: none;height: 0.8rem;border-radius: 0.12rem;background:#F1F3FA;box-sizing: border-box;font-size: 0.28rem;color: #555; &::-webkit-scrollbar { 0.05rem; height: 0.1rem } &::-webkit-scrollbar-thumb { min-height: 0.05rem; background: #c4c6cc; border-radius: 0.02rem; } &::-webkit-scrollbar-track-piece { background: #fff } &::placeholder{color: #999;} } .message-placeholder{ padding: 0.2rem 0.1rem; line-height: 0.4rem; } .message-val{ padding: 0.1rem 0.1rem; line-height: 0.35rem; } } } .multi-fun-panel{background: #F7F7F7; .panel-image{ ul{ li{text-align: center;display: inline-block;margin:0.3rem 0.33rem; p{text-align: center;color: #4C4C4C;font-size: 0.24rem;margin-top: 0.1rem;} } } } } .panel-emtion{flex-direction: row;flex-wrap: wrap;max-height: 3rem;overflow: scroll;padding: 0.1rem ;box-sizing: border-box;} } </style>