feat: 添加直播聊天功能与相关配置

- 在 LiveChatPanel.vue 中实现直播间聊天功能,支持发送和接收消息。
- 新增 useChat.js 组合式 API,管理聊天逻辑与状态。
- 更新 Live.vue 控制面板,集成聊天组件并优化直播状态显示。
- 更新 package.json 和 pnpm-lock.yaml,添加 @tencentcloud/chat 和 hls.js 依赖。
This commit is contained in:
Ryan Ran
2026-07-07 16:40:09 +08:00
parent 548335b658
commit 709d114986
9 changed files with 1113 additions and 65 deletions

View File

@@ -0,0 +1,251 @@
<template>
<div class="live-chat-panel">
<div v-if="initError" class="chat-error">
<el-alert :title="initError" type="warning" :closable="false" show-icon />
</div>
<div ref="chatListRef" class="chat-list" @scroll="onScroll">
<div v-if="isLoadingHistory" class="chat-loading">加载历史消息...</div>
<div
v-for="msg in messageList"
:key="msg.messageId"
class="chat-item"
:class="{ 'is-system': msg.isSystem }"
>
<el-avatar v-if="!msg.isSystem" :src="msg.avatar" :size="28">
{{ (msg.username || "").charAt(0) }}
</el-avatar>
<div class="chat-bubble">
<div v-if="!msg.isSystem" class="chat-user">{{ msg.username }}</div>
<div class="chat-text">{{ msg.message }}</div>
</div>
</div>
<el-empty v-if="!messageList.length && !initError && !initializing" :image-size="64" description="暂无聊天消息" />
<div ref="bottomAnchorRef" class="chat-bottom-anchor" />
</div>
<div v-if="unreadCount > 0" class="chat-unread" @click="scrollToBottomAndClearUnread">
{{ unreadCount }} 条新消息
</div>
<div class="chat-input-bar">
<el-input
v-model="messageContent"
placeholder="发送消息到直播间..."
maxlength="200"
@keyup.enter="createTextMessageByInput"
/>
<el-button type="primary" :disabled="!messageContent.trim()" @click="handleSend">
发送
</el-button>
</div>
</div>
</template>
<script>
import Cookies from "js-cookie";
import { toRef } from "vue";
import { useChat } from "../composables/useChat";
export default {
name: "LiveChatPanel",
props: {
liveId: {
type: String,
required: true,
},
liveDetail: {
type: Object,
default: () => ({}),
},
active: {
type: Boolean,
default: true,
},
},
setup(props) {
let userInfo = {};
try {
userInfo = JSON.parse(Cookies.get("userInfoManager") || "{}");
} catch {
userInfo = {};
}
return useChat(toRef(props, "liveId"), userInfo);
},
data() {
return {
initializing: false,
chatInited: false,
};
},
computed: {
/** 腾讯云 IM 群组:优先后端字段,否则用直播间 ID与旧版中控台一致 */
imGroupId() {
return this.liveDetail?.imGroupId || this.liveDetail?.groupId || this.liveId || "";
},
},
watch: {
imGroupId: {
immediate: true,
handler(id) {
if (!id) return;
this.setGroupID(id);
if (this.active) {
this.bootstrapChat();
}
},
},
active(val) {
if (val && this.imGroupId && !this.chatInited) {
this.bootstrapChat();
}
},
liveId() {
this.chatInited = false;
this.cleanup();
if (this.active && this.imGroupId) {
this.bootstrapChat();
}
},
},
beforeUnmount() {
this.cleanup();
},
methods: {
handleSend() {
this.createTextMessageByInput();
},
async bootstrapChat() {
if (!this.liveId || this.chatInited || this.initializing) {
return;
}
const groupId = this.imGroupId;
if (!groupId) {
this.initError = "缺少直播间 ID无法初始化聊天";
return;
}
this.setGroupID(groupId);
this.initializing = true;
try {
await this.initTencentIm();
if (!this.initError) {
this.chatInited = true;
}
} catch (error) {
console.error("初始化直播聊天失败:", error);
this.initError = "聊天初始化失败,请刷新重试";
this.chatInited = false;
} finally {
this.initializing = false;
}
},
},
};
</script>
<style lang="scss" scoped>
.live-chat-panel {
display: flex;
flex-direction: column;
flex: 1;
height: 100%;
min-height: 0;
max-height: 100%;
overflow: hidden;
position: relative;
}
.chat-error {
flex-shrink: 0;
margin-bottom: 8px;
}
.chat-list {
flex: 1 1 0;
height: 0;
min-height: 0;
overflow-x: hidden;
overflow-y: auto;
padding: 4px 0;
overscroll-behavior: contain;
}
.chat-bottom-anchor {
height: 1px;
flex-shrink: 0;
}
.chat-loading {
text-align: center;
color: #909399;
font-size: 12px;
padding: 8px 0;
}
.chat-item {
display: flex;
align-items: flex-start;
gap: 8px;
margin-bottom: 12px;
&.is-system {
justify-content: center;
.chat-bubble {
background: #f4f4f5;
border-radius: 12px;
padding: 4px 12px;
}
.chat-text {
color: #909399;
font-size: 12px;
}
}
}
.chat-bubble {
min-width: 0;
flex: 1;
}
.chat-user {
font-size: 12px;
color: #909399;
margin-bottom: 4px;
}
.chat-text {
font-size: 14px;
color: #303133;
line-height: 1.5;
word-break: break-all;
}
.chat-unread {
position: absolute;
left: 50%;
bottom: 56px;
transform: translateX(-50%);
padding: 4px 12px;
background: $theme_color;
color: #fff;
font-size: 12px;
border-radius: 12px;
cursor: pointer;
z-index: 2;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}
.chat-input-bar {
flex-shrink: 0;
display: flex;
gap: 8px;
padding-top: 12px;
border-top: 1px solid #ebeef5;
background: #fff;
}
</style>