mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-08-06 10:57:26 +08:00
feat: 添加直播聊天功能与相关配置
- 在 LiveChatPanel.vue 中实现直播间聊天功能,支持发送和接收消息。 - 新增 useChat.js 组合式 API,管理聊天逻辑与状态。 - 更新 Live.vue 控制面板,集成聊天组件并优化直播状态显示。 - 更新 package.json 和 pnpm-lock.yaml,添加 @tencentcloud/chat 和 hls.js 依赖。
This commit is contained in:
251
manager/src/views/live/components/LiveChatPanel.vue
Normal file
251
manager/src/views/live/components/LiveChatPanel.vue
Normal 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>
|
||||
Reference in New Issue
Block a user