目标:
建成一个简易直播间,要求聊天自由,可以发送表情,只显示昵称。
实现:
- 引入Tcplayer 播放器,封装组件。
组件代码:
<template name="cview-player">
<div class="cview-player-main">
<div class="cview-video-container" id="cview-video-container"></div>
</div>
</template>
<script>
let tencentPlay = null
export default {
name: 'cview-player',
components: { },
props: {},
watch: {},
data() {
return {}
},
computed: {},
created() {},
mounted() {},
methods: {
/***初始化直播播放器
@path {String} m3u8地址,
@live {Boolean} true:直播 false:点播
*/
init(path, live = true) {
if (window.TcPlayer) {
this.loadJS(path, live)
} else {
this.loadTcScript(() => {
this.loadJS(path, live)
})
}
},
loadTcScript(callback) {
this.loadScript(callback, {
id: 'tcPlayerScriptId',
url: 'https://web.sdk.qcloud.com/player/tcplayerlite/release/v2.4.1/TcPlayer-2.4.1.js'
// 也可引入本地TcPlayer-2.3.3.js文件,防止外网资源跨域
// url: ''
})
},
loadScript(callback, params) {
if (document.getElementById(params.id)) {
// eslint-disable-next-line callback-return
callback()
} else {
const script = document.createElement('script')
script.async = true
script.src = params.url
script.id = params.id
script.onload = () => {
callback()
}
document.body.appendChild(script)
}
},
loadJS(path, live) {
tencentPlay = new TcPlayer('cview-video-container', {
m3u8: path,
autoplay: true,
preload: true,
poster: { style: 'cover', src: 'https://img.yzcdn.cn/vant/cat.jpeg'},
live: live,
width: '100%',
height: '100%',
x5_type: 'h5',
listener: (msg) => {
if(msg.type == 'load') { //加载完事件
tencentPlay.play()
}
if(msg.type == 'ended'){//播放完成事件
this.close()
}
}
})
},
// 播放
play() {
if(tencentPlay) {
tencentPlay.play()
}
},
// 暂停
pause() {
if(tencentPlay) {
tencentPlay.pause()
}
},
// 销毁当前播放器
destroy() {
if(tencentPlay) {
tencentPlay.destroy()
tencentPlay = null
}
},
// 关闭
close() {
this.$emit('on-close')
}
}
};
</script>
<style lang="scss" scoped>
// $primary-background-blue: #266593;
// $primary-background-gray: #F1F2F5;
// $primary-color-black: #515a6e;
// $primary-color-blue: #266593;
// $primary-border-blue: #266593;
// $primary-btn-background: #FFFFFF;
// $primary-btn-background-yellow: #C8DC00;
.cview-player-main{
height: 100%;
.cview-video-container{
height: 100%;
border-radius: 4px;
overflow: hidden;
}
}
</style>
引用代码:
<Cviewplayer ref="cviewPlayer" @on-close="destroy" />
......
// 开始直播
init(path, live) {
this.$nextTick(() => {
this.$refs.cviewPlayer.init(path, live)
})
},
// 播放器销毁
destroy() {
this.$refs.cviewPlayer.destroy()
},
2. IM 集成
参考:
即时通信 IM Web & 小程序 & uni-app-自实现 UI 集成方案-文档中心-腾讯云 (tencent.com)
// IM Web SDK
// 从v2.11.2起,SDK 支持了 WebSocket,推荐接入;v2.10.2及以下版本,使用 HTTP
npm install tim-js-sdk --save
// 发送图片、文件等消息需要腾讯云即时通信 IM 上传插件
npm install tim-upload-plugin --save
在main.js 中引入
import TIM from "tim-wx-sdk";
import TIMUploadPlugin from "tim-upload-plugin";
Vue.prototype.$TIM = TIM;
Vue.prototype.$TIMUploadPlugin = TIMUploadPlugin;
创建实例 tim.js 文件
//tim.js代码
import TIM from "tim-wx-sdk";
import TIMUploadPlugin from "tim-upload-plugin";
let tim = TIM.create({
SDKAppID: ***** // 应该改成自己的
});
tim.registerPlugin({
"cos-wx-sdk": TIMUploadPlugin
});
export default tim
最后引用页
<template>
<div id="chat">
<div class="chatBox">
<div class="chatBox-top">
<Cviewplayer ref="cviewPlayer" @on-close="destroy" />
</div>
<div class="shop-box">
<van-icon name="friends-o" size="1.2rem" color="rgb(234, 201, 36)" style="margin-right:0.3rem"/>
<span class="text"><b>{{currentNum}}</b></span>
</div>
<div class="chatBox-middle">
<div class="chatInfo" id="chatInfo">
<div class="chatUser-box" v-for="(item,index) in messageList" :key="index" :class="chatUser-box">
<div class="chatUser-info">
<span class="chatUser-info-text" :class="current==item.id">
<span class="chatUser-info-name">{{item.userName}}:</span><span>{{item.content}}</span>
</span>
</div>
</div>
</div>
</div>
<div class="chatBox-infoDesk">
<van-cell-group>
<van-field
v-model="messageContent"
center
placeholder="来说点什么吧"
use-button-slot
>
<van-button slot="button" icon="smile-o" style="border:none;font-size:1.2rem;padding:0.4rem;color:grey" @click="getEmojiList">
</van-button>
<van-button slot="button" size="small" type="info" @click="sendTextMessage">
发送
</van-button>
</van-field>
</van-cell-group>
<van-popup
v-model="showEmoji"
round
position="bottom"
custom-style="height: 20%"
>
<div class="browBox">
<ul>
<li
v-for="(item, index) in faceList"
:key="index"
@click="getBrow(index)"
>
{{ item }}
</li>
</ul>
</div>
</van-popup>
</div>
<van-dialog id="van-dialog" />
</div>
</div>
</template>
<script>
const appData = require("@/assets/images/emoji.json");
import Cviewplayer from './cview-player.vue'
import tim from '@/AynPlugin/utils/tim'
import TIM from 'tim-wx-sdk';
import { Dialog } from "vant";
export default {
name: "chat",
components: {
Cviewplayer
},
data() {
return {
value: "",
userName:'',
// messages: '',
messageContent:'',
messageList:[],
nowDate: "", // 当前日期
current: 2,
// infoList : infoData,
showEmoji: false,
faceList:[],
getBrowString:'',
loginMessages:{
userID: "00007",
userSig: "eJyrVgrxCdYrSy1SslIy0jNQ0gHzM1NS80oy0zLBwgZAYA6VKE7JTiwoyExRsjI0AYoamlmYmkNkUisKMotSgeKmpqZGQB0Q0ZLMXJCYmamliaWRiZEZ1JTMdKC5bgaRZYHaERkhpT6OLi7*3gbuITm*Jak*LtkZkaZFHh4VBf4pxeaZ7mnh2bZKtQCT9DBt"
},
currentNum:0 ,// 直播在线人数
groupId:"*******", // 需要更改为自己的群组id
time:0,
};
},
computed: {
},
created() {
this.userId = this.$store.getters.user.userId;
this.userCode = this.$store.getters.user.userCode;
this.init('***.m3u8', true)
this.getUserDetail();
this.scrollToBottom();
this.loadEmojis()
this.timLogin()
this.currentTime();
},
updated() {
this.scrollToBottom()
},
mounted() {
},
beforeRouteLeave (to, from, next) {
// 1 操作来自关闭按钮(按钮点击flag)
console.log("我退出了")
setTimeout(() => {
Dialog.confirm({
title: '确认',
message: '确认离开直播间?',
})
.then(() => {
console.log(from.path ,from)
if(from.path === '/startLive'){
next();
this.quitGroup()
console.log("确认")
}
})
.catch(() => {
// on cancel
console.log("取消")
next(false)
});
},200)
},
beforeDestroy() {
if (this.currentTime) {
clearInterval(this.currentTime); // 在Vue实例销毁前,清除我们的定时器
}
},
methods: {
// 获取用户详情
getUserDetail() {
const pr = this.$utils.httpMiddleware(
this.$http.getUserDetail({
userId: this.userId,
userCode: this.userCode
})
);
pr.then(res => {
if (res.code === 0) {
// this.$toast(res.msg)
this.userName = res.data.realName;
} else {
this.$toast(res.msg);
}
});
},
//登录聊天室
timLogin() {
tim.login(this.loginMessages).then(res => {
console.log("登陆成功",res)
if(res.code == 0){
this.timListener();
}
}).catch(imError => {
//登录失败的消息
console.log("登陆失败")
});
},
// IM监听
timListener() {
console.log('IM监听')
// let _this = this;
console.log(TIM.EVENT.SDK_READY + '登录成功后会触发 SDK_READY 事件,该事件触发后,可正常使用 SDK 接口')
// 登录成功后会触发 SDK_READY 事件,该事件触发后,可正常使用 SDK 接口
//onReadyStateUpdate 加群组事件
tim.on(TIM.EVENT.SDK_READY, this.onReadyStateUpdate);
// 收到新消息--onReceiveMessage事件
tim.on(TIM.EVENT.MESSAGE_RECEIVED, this.onReceiveMessage);
// 群组列表更新时触发
tim.on(TIM.EVENT.GROUP_LIST_UPDATED, this.onGroupListUpdated);
},
// 登录成功触发
onReadyStateUpdate(event){
console.log('登录成功触发')
let _this = this;
// 加入群聊
tim.joinGroup({
groupID: _this.groupId,
type: "AVChatRoom"
}).then(res => {
if (res.data.status === "JoinedSuccess") {
console.log('加入群聊成功')
}
}).catch(imError => {
//加入群聊失败
console.log('加入群聊失败',imError)
});
// 获取群详细资料
tim.getGroupProfile({
groupID: _this.groupId
}).then(res => {
this.currentNum = res.data.group.memberCount; //群人数(当前直播间的人数)
console.log(res.data.group.memberCount,"群人数")
});
},
// 收到消息触发
onReceiveMessage(data) {
let _this = this;
console.log("收到消息:", data);
// 根据消息类型判断,执行不同操作
if (data.data[0].type == "TIMTextElem") {
// 群成员发送消息
let eleData = JSON.parse(data.data[0].payload.text);
// 获取群聊消息,消息上屏
this.updateMessageList(eleData);
}
if (data.data[0].payload.operationType == 5) {
// 群聊解散
}
tim.getGroupProfile({
groupID: this.groupId
}).then(res => {
console.log("当前群人数:", res.data.group.memberCount);
_this.currentNum = res.data.group.memberCount;
});
_this.conversationID = data.data[0].conversationID;
},
//群组列表更新触发
onGroupListUpdated(event){
console.log(event,'群组列表更新')
},
// 更新数据push进消息列表
updateMessageList(data) {
this.messageList = [...this.messageList, data];
// console.log(this.messageList)
this.scrollToBottom()
},
//发送聊天
sendTextMessage() {
let _this = this;
if (
this.messageContent === '' ||
this.messageContent.trim().length === 0
) {
this.messageContent = '';
this.$toast('不能发送空消息哦');
return
}
//拼接消息,包括内容,昵称,头像
let data = {
'userName': _this.userName,
'content': _this.messageContent,
};
// 1. 创建消息实例,接口返回的实例可以上屏
let message = tim.createTextMessage({
to: _this.groupId, //接收人
conversationType: TIM.TYPES.CONV_GROUP,
// 消息优先级,用于群聊(v2.4.2起支持)。如果某个群的消息超过了频率限制,后台会优先下发高优先级的消息,详细请参考:https://cloud.tencent.com/document/product/269/3663#.E6.B6.88.E6.81.AF.E4.BC.98.E5.85.88.E7.BA.A7.E4.B8.8E.E9.A2.91.E7.8E.87.E6.8E.A7.E5.88.B6)
// 支持的枚举值:TIM.TYPES.MSG_PRIORITY_HIGH, TIM.TYPES.MSG_PRIORITY_NORMAL(默认), TIM.TYPES.MSG_PRIORITY_LOW, TIM.TYPES.MSG_PRIORITY_LOWEST
// priority: TIM.TYPES.MSG_PRIORITY_NORMAL,
payload: {
text: JSON.stringify(data) //消息内容
}
});
// 发送消息
let promise = tim.sendMessage(message);
promise.then(function(imResponse) {
// 发送成功
console.log('发送消息成功');
console.log(imResponse.data)
_this.messageContent = '';
// 把自己发的消息拼接上屏(因为onReceiveMessage是监听不到自己发的消息的)
let text = JSON.parse(imResponse.data.message.payload.text);
_this.updateMessageList(text);
}).catch(function(imError) {
// 发送失败
console.log('发送消息失败:', imError);
});
},
//离开直播间,退出群聊
quitGroup() {
tim.quitGroup(this.groupId).then(() => {
console.log('退群成功')
let promise = tim.logout();
promise.then(function(imResponse) {
console.log('登出成功'); //
this.destroy();
}).catch(function(imError) {
console.warn('登出失败');
});
}).catch(error => {
console.log('退群失败')
});
},
// 观看计时
currentTime(){
setInterval(this.timer,500)
},
timer(){
let _this = this
_this.time = _this.time + 1
},
// 开始直播
init(path, live) {
this.$nextTick(() => {
this.$refs.cviewPlayer.init(path, live)
})
},
// 播放器销毁
destroy() {
this.$refs.cviewPlayer.destroy()
},
onChange(event) {
// event.detail 为当前输入的值
console.log(event.detail);
},
//加载表情,存放到表情列表中
loadEmojis() {
for (let i in appData) {
this.faceList.push(appData[i].char);
}
},
// 表情列表
getEmojiList(){
this.showEmoji = true
},
// 获取用户点击之后的标签 ,存放到输入框内
getBrow(index) {
for (let i in this.faceList) {
if (index == i) {
this.getBrowString = this.faceList[index];
this.messageContent += this.getBrowString;
}
}
this.showEmoji = false;
},
scrollToBottom(){
this.$nextTick(()=>{
let container = this.$el.querySelector("#chatInfo");
container.scrollTop = container.scrollHeight;
})
},
}
}
</script>
<style scoped>
html,
body {
background-color: #E8E8E8;
height: 100%;
}
#chat .chatBox {
width: 100%;
height: auto;
margin: auto 0;
background-color: #fff;
overflow: hidden;
border-radius: 0.625rem;
}
#chat .chatBox-top {
width: 100%;
height: 18rem;
position: fixed;
top:0;
display: flex;
flex-wrap: nowrap;
align-items: center;
/* background-color: rgb(184, 183, 183); */
}
#chat .shop-box{
margin: 0.23rem;
padding: 0.3rem;
position: fixed;
top:3rem;
color: rgb(234, 201, 36);
font-size: 0.6rem;
border-radius: 2rem;
background-color: rgba(230, 230, 230, 0.236);
}
#chat .text{
vertical-align: top;
display: inline;
}
#chat .chatBox-middle {
width: 100%;
position: fixed;
top:18rem;
height: calc(100% - 24rem);
}
#chat .chatBox-infoDesk {
width: 100%;
position: fixed;
bottom: 0;
}
#chat .chatBox-textarea {
width: 100%;
height: 6.25rem;
}
#chat .chatBox-sendOut {
margin-top: 0.625rem;
width: 100%;
height: 3.125rem;
text-align: right;
}
#chat .sendOut {
padding: 0 1.25rem;
height: 2.1875rem;
margin: 0.3125rem 1.25rem 0 0;
}
#chat .chatInfo {
width: 100%;
height: 94%;
margin: 1.25rem auto;
overflow-y: scroll;
}
#chat .chatInfo::-webkit-scrollbar {
width: 0 !important
}
#chat .chatUser-box {
width: 100%;
margin-bottom: 6px;
display: flex;
flex-direction: row;
}
#chat .chatUser-box-img {
display: flex;
}
#chat .chatUser-info {
margin: 0 1.25rem;
}
#chat .chatUser-info-name {
font-size: 1.5rem;
color: rgb(234, 201, 36);
margin-right: 0.4rem;
font-weight: 600;
/* display: flex; */
/* flex-direction: row; */
}
#chat .chatUser-info-text {
margin-top: 0.3rem;
max-width: 18rem;
/* height: 0.8rem; */
padding: 0.23rem;
background-color: #E8E8E8;
border-radius: 1rem;
color: #575757;
float: left;
table-layout:fixed;
word-break: break-all;
overflow:hidden;
}
#chat .chatUser-info-text span{
font-size: 0.8375rem;
/* line-height: 0.1rem; */
}
#chat .chatUser-box1 {
flex-direction: row-reverse;
}
.browBox {
width: 100%;
height: 200px;
background: #ffffff;
position: fixed;
bottom: 0;
overflow-y: scroll;
overflow-x: none;
}
.browBox ul {
display: flex;
flex-wrap: wrap;
padding: 0.4rem;
}
.browBox li {
cursor: pointer;
width: 12%;
font-size: 26px;
list-style: none;
text-align: center;
}
</style>
<style>
#chat .chatBox-infoDesk .van-field__control{
background-color: rgb(247, 247, 247) !important;
border-radius:2rem !important;
height: 2rem;
}
#chat .chatBox-infoDesk .van-button{
border-radius:2rem !important;
}
</style>
前端模拟,后端接口未完成