需要使用到的扩展 https://github.com/tangbc/vue-virtual-scroll-list
最终效果

主页面 Main.vue
<template>
<div>
<!---chat message-->
<el-dialog id="chat" top="8vh" :width="dialogWidth" center :show-close="false" :close-on-press-escape="false" :close-on-click-modal="false" :visible.sync="visible">
<div slot="title" style="display: flex;align-items: center;position: relative;">
<el-avatar :src="chat.img" :size="50" fit="fill" :alt="chat.title"/>
<div>
<div v-html="chat.title" style="margin-left: 10px;text-align: left;color: #fff"/>
<div slot="title" v-html="chat.desc" style="margin-left: 10px;color: #fff"/>
</div>
</div>
<el-row :gutter="24">
<el-col :xl="{'span':18}" :lg="{'span':16}" :md="{'span':16}" :sm="{'span':16}" :xs="{'span':14}" id="leftBox">
<el-card>
<div class="main">
<div class="list-container">
<virtual-list v-show="!!chat.messageLists.length" class="stream scroll-touch" :class="{ overflow: overflow }" ref="vsl"
:data-key="'uid'"
:data-sources="chat.messageLists"
:data-component="messageComponent"
:estimate-size="70"
:item-class="'stream-item'"
:item-class-add="addItemClass"
@resized="onItemRendered"
@totop="onTotop">
<div slot="header" v-show="overflow" class="header">
<div class="spinner" v-show="!finished"></div>
<div class="finished" v-show="finished">No more data</div>
</div>
</virtual-list>
<div class="empty" v-show="!chat.messageLists.length">
<div class="wrapper">
<div class="icon"></div>
<div class="tips">No chats</div>
</div>
</div>
</div>
</div>
<div class="input-msg">
<emotion @clickEmotion="getEmotion" v-show="showEmotion" :height="300"/>
<div>
<el-tooltip effect="dark" content="房间名称" placement="top-start">
<el-menu :default-active="chat.room_id" background-color="#409EFF"
text-color="#fff"
active-text-color="#ffd04b" mode="horizontal" style="margin-bottom:10px;">
<el-menu-item @click="setRoomID(room)" v-for="(room,index) in oauthConfig" :key="index" :index="room.id.toString()">
{
{room.name}}
</el-menu-item>
</el-menu>
</el-tooltip>
<el-tooltip effect="dark" content="发送表情" placement="top-start">
<i @click="showEmotion = !showEmotion" class="el-icon-picture-outline-round icon"/>
</el-tooltip>
<el-upload :action="cgi.uploadUrl"
:data="fileData"
:headers="headers"
:show-file-list="false"
:on-success="uploadSuccess"
:before-upload="beforeUpload" style="float: left">
<el-tooltip effect="dark" content="发送文件和图片" placement="top-start">
<i @click="showEmotion = false" class="el-icon-picture-outline icon"/>
</el-tooltip>
</el-upload>
</div>
<div contentEditable="true" ref="message" id="content" @focus="showEmotion = false" @keydown="setMsg">
</div>
</div>
<div class="input-button" style="text-align: right">
<el-tooltip effect="dark" content="Shift + Enter 快捷发送" placement="top-start">
<el-button type="primary" round plain size="medium" @click="sendMsg">发 送</el-button>
</el-tooltip>
</div>
</el-card>
</el-col>
<el-col :xl="{'span':6}" :lg="{'span':8}" :md="{'span':8}" :sm="{'span':8}" :xs="{'span':10}" id="rightBox">
<el-card>
<div style="background: #fff;min-height: 60px">
群公告:
<el-carousel tyle="cursor: pointer" :interval="4000" arrow="never" direction="vertical" indicator-position="none" height="100px">
<el-carousel-item v-for="(item,index) in groupAnnouncementConfig" :key="index">
<div style="cursor: pointer;margin-top: 20px" v-html="item.name"/>
</el-carousel-item>
</el-carousel>
</div>
<el-divider/>
<div style="margin-bottom: 10px">
在线人数({
{chat.userTotal}}/{
{chat.userOnline}})
</div>
<el-autocomplete placeholder="搜索" v-model="chat.users" clearable :fetch-suggestions="querySearch" @clear="clearSearch" style="width: 100%"/>
<div class="user-list">
<el-menu style="width: 100%;">
<el-menu-item :users="user" v-contextmenu:contextmenu @click="sendUser(user,index)" v-for="(user,index) in chat.client_list_part" :key="index" :index="index.toString()">
<el-avatar :size="30" :src="user.client_img" style="cursor: pointer"/>
<span slot="title" style="font-size: 14px" v-html="user.client_name.replace(chat.users,'<b style=color:#0e82fc;font-weight:300>'+chat.users+'</b>')"/>
<!--未读消息数-->
<el-badge v-if="user.total" type="danger" :value="user.total" style="top: 10px;right: 15px"/>
<!--在线-->
<el-badge v-else-if="user.online" type="success" is-dot style="top: 12px;right: 10px"/>
<!--离线-->
<el-badge v-else-if="!user.online" type="info" is-dot style="top: 12px;right: 10px"/>
</el-menu-item>
</el-menu>
</div>
<!--右键菜单-->
<v-contextmenu ref="contextmenu" @contextmenu="menuRightChange">
<v-contextmenu-item @click="userInfoVisible = true"><i class="el-icon-postcard"/> 查看资料</v-contextmenu-item>
<v-contextmenu-item divider></v-contextmenu-item>
<v-contextmenu-item @click="sendToMessage"><i class="el-icon-chat-line-square"/> 发送消息</v-contextmenu-item>
</v-contextmenu>
</el-card>
</el-col>
</el-row>
</el-dialog>
<!---chat message-->
<!--userInfo Start-->
<UserInfo :user-info="targetUsers" :dialog-width="dialogWidth" :user-info-visible="userInfoVisible" :closeDialog="closeDialog"/>
<!--userInfo End-->
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
import func from '../../api/func'
import emotion from '../emotion/Index'
import Push from 'push.js'
import Item from './Item'
import VirtualList from 'vue-virtual-scroll-list'
import UserInfo from './UserInfo'
export default {
props: {
chatVisible: {
type: Boolean,
default: () => true
},
dialogWidth: {
type: String,
default: () => '50%'
},
currentPage: {
type: Number,
default: () => 1
},
sizeLimit: {
type: Number,
default: () => 19
}
},
name: 'chatRoom',
data () {
return {
cgi: {uploadUrl: process.env.API_ROOT + 'v1/common/upload'},
fileData: {},
headers: {},
inputMsg: '',
emotionLists: [],