|
@@ -0,0 +1,213 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="global-im">
|
|
|
|
|
+ <!-- 悬浮按钮与角标 -->
|
|
|
|
|
+ <div class="im-trigger" :class="{ active: visible }" @click="toggleChat">
|
|
|
|
|
+ <el-icon><el-icon-chat-round /></el-icon>
|
|
|
|
|
+ <el-badge :value="unreadCount" :hidden="unreadCount === 0" :max="99">
|
|
|
|
|
+ <i class="el-icon-chat-dot-round" style="font-size: 24px"></i>
|
|
|
|
|
+ </el-badge>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- IM 聊天窗口 -->
|
|
|
|
|
+ <el-drawer
|
|
|
|
|
+ v-model="visible"
|
|
|
|
|
+ title="消息中心"
|
|
|
|
|
+ :append-to-body="true"
|
|
|
|
|
+ :destroy-on-close="false"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div class="chat-container">
|
|
|
|
|
+ <div class="message-list" ref="messageListRef">
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="msg in messages"
|
|
|
|
|
+ :key="msg.id"
|
|
|
|
|
+ class="message-item"
|
|
|
|
|
+ :class="msg.self ? 'self' : 'other'"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div class="avatar">
|
|
|
|
|
+ <el-avatar :size="32" :src="msg.avatar"></el-avatar>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="content">
|
|
|
|
|
+ <div class="name">{{ msg.name }}</div>
|
|
|
|
|
+ <div class="text">{{ msg.text }}</div>
|
|
|
|
|
+ <div class="time">{{ msg.time }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="input-area">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ :rows="3"
|
|
|
|
|
+ v-model="inputMessage"
|
|
|
|
|
+ placeholder="请输入消息..."
|
|
|
|
|
+ ></el-input>
|
|
|
|
|
+ <el-button type="primary" style="margin-top: 10px;" @click="sendMessage">发送</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-drawer>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script>
|
|
|
|
|
+
|
|
|
|
|
+// 注意:Element UI 的图标需要单独引入或使用字体图标
|
|
|
|
|
+// 如果使用 el-icon,需要安装 @element-plus/icons-vue(但这是 Vue 3 的)
|
|
|
|
|
+// Vue 2 可以使用字体图标或 SVG 图标,这里用 Element UI 自带的字体图标
|
|
|
|
|
+
|
|
|
|
|
+export default {
|
|
|
|
|
+ name: 'GlobalIM',
|
|
|
|
|
+ components: {
|
|
|
|
|
+ // Element UI 组件已全局注册的话,这里不需要重复注册
|
|
|
|
|
+ },
|
|
|
|
|
+ data() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ visible: false, // 聊天窗口可见性
|
|
|
|
|
+ unreadCount: 0, // 未读消息数量
|
|
|
|
|
+ inputMessage: '', // 输入的消息
|
|
|
|
|
+ timer: null, // 定时器
|
|
|
|
|
+ messages: [ // 消息列表
|
|
|
|
|
+ { id: 1, name: '系统助手', avatar: '', text: '欢迎使用 SCUI!', time: '10:00', self: false },
|
|
|
|
|
+ { id: 2, name: '我', avatar: '', text: '你好,这个全局IM功能不错', time: '10:01', self: true },
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ mounted() {
|
|
|
|
|
+ this.simulateNewMessage()
|
|
|
|
|
+ },
|
|
|
|
|
+ beforeDestroy() {
|
|
|
|
|
+ if (this.timer) {
|
|
|
|
|
+ clearInterval(this.timer)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ methods: {
|
|
|
|
|
+ // 模拟新消息(演示角标更新)
|
|
|
|
|
+ simulateNewMessage() {
|
|
|
|
|
+ this.timer = setInterval(() => {
|
|
|
|
|
+ if (!this.visible) { // 仅当聊天窗口关闭时增加未读数
|
|
|
|
|
+ const newMsg = {
|
|
|
|
|
+ id: this.messages.length + 1,
|
|
|
|
|
+ name: '访客',
|
|
|
|
|
+ avatar: '',
|
|
|
|
|
+ text: `自动消息 ${new Date().toLocaleTimeString()}`,
|
|
|
|
|
+ time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
|
|
|
|
|
+ self: false
|
|
|
|
|
+ }
|
|
|
|
|
+ this.messages.push(newMsg)
|
|
|
|
|
+ this.unreadCount++
|
|
|
|
|
+ }
|
|
|
|
|
+ }, 15000) // 每15秒模拟一条新消息
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 发送消息
|
|
|
|
|
+ sendMessage() {
|
|
|
|
|
+ if (!this.inputMessage.trim()) return
|
|
|
|
|
+ const newMsg = {
|
|
|
|
|
+ id: this.messages.length + 1,
|
|
|
|
|
+ name: '我',
|
|
|
|
|
+ avatar: '',
|
|
|
|
|
+ text: this.inputMessage,
|
|
|
|
|
+ time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
|
|
|
|
|
+ self: true
|
|
|
|
|
+ }
|
|
|
|
|
+ this.messages.push(newMsg)
|
|
|
|
|
+ this.inputMessage = ''
|
|
|
|
|
+ // 滚动到底部
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ const container = this.$refs.messageListRef
|
|
|
|
|
+ if (container) container.scrollTop = container.scrollHeight
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 切换聊天窗口
|
|
|
|
|
+ toggleChat() {
|
|
|
|
|
+ this.visible = !this.visible
|
|
|
|
|
+ if (this.visible) {
|
|
|
|
|
+ // 打开窗口时清除未读角标
|
|
|
|
|
+ this.unreadCount = 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
|
+.global-im {
|
|
|
|
|
+ .im-trigger {
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ right: 24px;
|
|
|
|
|
+ bottom: 24px;
|
|
|
|
|
+ width: 48px;
|
|
|
|
|
+ height: 48px;
|
|
|
|
|
+ background: #409eff;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
|
|
|
|
+ z-index: 2000;
|
|
|
|
|
+ transition: all 0.3s;
|
|
|
|
|
+ color: white;
|
|
|
|
|
+ &:hover {
|
|
|
|
|
+ transform: scale(1.05);
|
|
|
|
|
+ background: #66b1ff;
|
|
|
|
|
+ }
|
|
|
|
|
+ &.active {
|
|
|
|
|
+ background: #909399;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+.chat-container {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ .message-list {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ overflow-y: auto;
|
|
|
|
|
+ padding: 16px;
|
|
|
|
|
+ .message-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ margin-bottom: 16px;
|
|
|
|
|
+ &.self {
|
|
|
|
|
+ flex-direction: row-reverse;
|
|
|
|
|
+ .content {
|
|
|
|
|
+ margin-right: 12px;
|
|
|
|
|
+ align-items: flex-end;
|
|
|
|
|
+ .text {
|
|
|
|
|
+ background: #95ec69;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ &.other {
|
|
|
|
|
+ .content {
|
|
|
|
|
+ margin-left: 12px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .content {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ max-width: 70%;
|
|
|
|
|
+ .name {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+ margin-bottom: 4px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .text {
|
|
|
|
|
+ padding: 8px 12px;
|
|
|
|
|
+ background: #f0f0f0;
|
|
|
|
|
+ border-radius: 12px;
|
|
|
|
|
+ word-break: break-all;
|
|
|
|
|
+ }
|
|
|
|
|
+ .time {
|
|
|
|
|
+ font-size: 10px;
|
|
|
|
|
+ color: #ccc;
|
|
|
|
|
+ margin-top: 4px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .input-area {
|
|
|
|
|
+ padding: 16px;
|
|
|
|
|
+ border-top: 1px solid #eee;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|