From 089bf5d2378b3c4a61d795b2a92bede2c193b771 Mon Sep 17 00:00:00 2001
From: admin <344137771@qq.com>
Date: Tue, 06 Jan 2026 11:22:58 +0800
Subject: [PATCH] 1

---
 src/components/group/GroupPanel.vue |  824 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 824 insertions(+), 0 deletions(-)

diff --git a/src/components/group/GroupPanel.vue b/src/components/group/GroupPanel.vue
new file mode 100644
index 0000000..00de977
--- /dev/null
+++ b/src/components/group/GroupPanel.vue
@@ -0,0 +1,824 @@
+<template>
+  <el-container class="container">
+    <el-header class="header">
+      <span>群信息</span>
+      <el-tooltip content="发送消息" placement="top">
+        <i class="icon-send el-icon-chat-line-square" @click="sendGroup" />
+      </el-tooltip>
+      <i class="el-icon-close" @click="$emit('close')" />
+    </el-header>
+
+    <el-main class="main lum-scrollbar">
+      <div class="list-item flex">
+        <p>
+          <span>群名称:</span>
+          <span class="group-setting-title">{{ detail.groupName }}</span>
+        </p>
+        <span
+          v-show="detail.is_manager"
+          class="more"
+          @click="isShowManager = true"
+          >管理
+        </span>
+      </div>
+
+      <div class="list-item">
+        <span>群主:</span>
+        <span class="group-boss-name">{{ detail.groupOwner }}</span>
+      </div>
+
+      <div class="list-item">
+        <span>我的群昵称:</span>
+        <span v-if="!isEditRemark" class="edit-visit-card">
+          <span>
+            {{ detail.visitCard }}
+            <span v-show="!detail.visitCard" style="font-size: 12px">
+              添加群名片
+            </span>
+          </span>
+          <i
+            class="el-icon-edit-outline edit-remark-icon"
+            @click="
+              isEditRemark = true
+              editRemarkText = detail.visitCard
+            "
+          />
+        </span>
+        <span v-else>
+          <input
+            v-model.trim="editRemarkText"
+            class="edit-input"
+            type="text"
+            v-focus
+            @keyup.enter="editRemark"
+          />
+          <span class="input-submit" @click="editRemark">确认</span>
+        </span>
+      </div>
+
+      <div class="list-item flex">
+        <span>消息免打扰:</span>
+        <el-switch
+          v-model="detail.is_disturb"
+          inactive-color="#e0d6d6"
+          :disabled="disturbDisabled"
+          @change="editDisturb"
+        />
+      </div>
+
+      <!-- 预留 -->
+      <div class="list-item flex">
+        <span>全员禁言:</span>
+        <el-switch v-model="detail.no_message" inactive-color="#e0d6d6" />
+      </div>
+
+      <div class="list-item">
+        <span>群成员:</span>
+        <span>{{ members.length }} 人</span>
+      </div>
+
+      <div class="list-item-tips">群主已开启“新成员入群可查看所有聊天记录”</div>
+
+      <div class="list-item">群简介</div>
+
+      <div class="list-item-tips">
+        {{ detail.profile ? detail.profile : '暂无群简介' }}
+      </div>
+
+      <div class="list-item flex">
+        <span>群公告</span>
+        <span
+          v-show="detail.group_notice"
+          class="more"
+          @click="isShowGroupNotice = true"
+          >更多
+        </span>
+      </div>
+
+      <div class="list-item-tips group-notice">
+        <span v-if="detail.group_notice.title">
+          <b>#{{ detail.group_notice.title }}#</b><br />
+          {{ detail.group_notice.content }}
+        </span>
+        <span v-else>暂无群公告</span>
+      </div>
+
+      <div class="list-item">
+        <p class="group-invite" @click="addGroupMembers">
+          <i class="el-icon-plus" />
+          <span>&nbsp;邀请好友</span>
+        </p>
+      </div>
+
+      <div class="list-item">
+        <div class="member-box">
+          <div class="view-box">
+            <i class="iconfont icon-sousuo i-sousuo" />
+            <input type="text" placeholder="搜索群成员" v-model="keywords" />
+          </div>
+
+          <el-row class="row-header">
+            <el-col :span="11">昵称</el-col>
+            <el-col :span="8">名片</el-col>
+            <el-col :span="5">性别</el-col>
+          </el-row>
+
+          <template v-if="searchs.length == 0">
+            <el-row class="row-items">
+              <el-col :span="24">
+                <p style="text-align:center;">无数据</p>
+              </el-col>
+            </el-row>
+          </template>
+          <template v-else>
+            <el-row
+              v-for="member in searchs"
+              :key="member.user_id"
+              class="row-items"
+              @click.native="openUserDetail(member.user_id)"
+            >
+              <el-col :span="11">
+                <img
+                  width="20px"
+                  :src="member.avatar"
+                  :onerror="$store.state.detaultAvatar"
+                />
+                <span class="nickname">{{ member.nickname }}</span>
+              </el-col>
+              <el-col :span="8">
+                <span>{{ member.user_card ? member.user_card : '-' }}</span>
+              </el-col>
+              <el-col :span="5">
+                <span v-if="member.gender == 1">男</span>
+                <span v-else-if="member.gender == 2">女</span>
+                <span v-else>未知</span>
+              </el-col>
+            </el-row>
+          </template>
+        </div>
+      </div>
+    </el-main>
+
+    <el-footer class="footer">
+      <button v-if="detail.is_manager" @click="dismiss">解散群聊</button>
+      <button v-else @click="isShowSignout = true">退出该群聊</button>
+    </el-footer>
+
+    <!-- 退群提示层 -->
+    <div class="signout-box no-select" v-show="isShowSignout">
+      <p v-show="signoutStatus == 0">您确认退出当前群聊吗?</p>
+      <p v-show="signoutStatus == 0">退群后群聊信息将不能查看</p>
+      <p v-show="signoutStatus == 0" class="signout-btn">
+        <button @click="signout">确认</button>
+        <button @click="isShowSignout = false">取消</button>
+      </p>
+
+      <p v-show="signoutStatus == 1" class="signout-btn mt38">
+        <span style="color: #ccc">
+          <i class="el-icon-loading" />
+          正在退出群聊...
+        </span>
+      </p>
+
+      <p v-show="signoutStatus == 2" class="signout-btn mt38">
+        <span style="color: #cccccc">退出群聊失败,请3(s)后再试...</span>
+      </p>
+
+      <p v-show="signoutStatus == 3" class="signout-btn mt38">
+        <span style="color: #339e19">
+          <i class="iconfont icon-success_no_circle" /> 已成功退出群聊...
+        </span>
+      </p>
+    </div>
+
+    <!-- 邀请好友组件 -->
+    <transition name="el-fade-in-linear">
+      <GroupLaunch
+        v-if="inviteFriendBox"
+        :group-id="groupId"
+        @close="inviteFriendBox = false"
+        @invite-success="inviteSuccess"
+      />
+    </transition>
+
+    <!-- 群管理组件 -->
+    <transition name="el-fade-in-linear">
+      <GroupManage
+        v-if="isShowManager"
+        :group-id="groupId"
+        @close="isShowManager = false"
+      />
+    </transition>
+
+    <!-- 群公告组件 -->
+    <transition name="el-fade-in-linear">
+      <GroupNotice
+        v-if="isShowGroupNotice"
+        :group-id="groupId"
+        @close="isShowGroupNotice = false"
+      />
+    </transition>
+  </el-container>
+</template>
+<script>
+import { ServeSetNotDisturb } from '@/api/chat'
+import {
+  ServeGroupDetail,
+  ServeUpdateGroupCard,
+  ServeSecedeGroup,
+  ServeGetGroupMembers,
+} from '@/api/group'
+
+//创建群聊组件
+import GroupLaunch from '@/components/group/GroupLaunch'
+import GroupManage from '@/components/group/GroupManage'
+import GroupNotice from '@/components/group/GroupNotice'
+
+export default {
+  name: 'GroupPanel',
+  components: {
+    GroupLaunch,
+    GroupManage,
+    GroupNotice,
+  },
+  props: {
+    groupId: {
+      type: [String, Number],
+      default: null,
+    },
+  },
+  data() {
+    return {
+      detail: {
+        groupAvatar: '',
+        groupId: 0,
+        groupName: '',
+        groupOwner: '',
+        profile: '',
+        is_disturb: 0,
+        no_message: false,
+        visitCard: '',
+        is_manager: false,
+        group_notice: [],
+      },
+
+      keywords: '',
+      members: [],
+
+      isEditRemark: false,
+      editRemarkText: '',
+
+      inviteFriendBox: false,
+      isShowSignout: false,
+
+      signoutStatus: 0,
+
+      disturbDisabled: false,
+
+      // 是否显示群管理窗口
+      isShowManager: false,
+
+      // 是否显示群公告窗口
+      isShowGroupNotice: false,
+    }
+  },
+  watch: {
+    groupId: function(value) {
+      if (value > 0) {
+        this.loadGroupDetail()
+        this.loadMembers()
+      }
+    },
+  },
+  computed: {
+    searchs() {
+      return this.keywords == ''
+        ? this.members
+        : this.members.filter(item => {
+            return (
+              item.nickname.match(this.keywords) != null ||
+              item.user_card.match(this.keywords) != null
+            )
+          })
+    },
+  },
+  created() {
+    if (parseInt(this.groupId) > 0) {
+      this.loadGroupDetail()
+      this.loadMembers()
+    }
+  },
+  methods: {
+    // 加载群组成员列表
+    loadMembers() {
+      ServeGetGroupMembers({
+        group_id: this.groupId,
+      }).then(res => {
+        if (res.code == 200) {
+          this.members = res.data
+          this.$emit('group-info', {
+            group_id: this.members.groupId,
+            members_num: this.members.length,
+          })
+        }
+      })
+    },
+
+    // 加载群信息
+    loadGroupDetail() {
+      this.isEditRemark = false
+      ServeGroupDetail({
+        group_id: this.groupId,
+      }).then(res => {
+        if (res.code == 200) {
+          let result = res.data
+          this.detail.groupAvatar = result.avatar
+          this.detail.groupId = result.group_id
+          this.detail.userId = res.data.user_id
+          this.detail.groupName = result.group_name
+          this.detail.groupOwner = result.manager_nickname
+          this.detail.profile = result.profile
+          this.detail.is_disturb = result.is_disturb == 1
+          this.detail.visitCard = result.visit_card
+          this.detail.is_manager = result.is_manager
+
+          if (result.notice) {
+            this.detail.group_notice = result.notice
+          }
+        }
+      })
+    },
+
+    // 设置群免打扰状态
+    editDisturb(value) {
+      this.disturbDisabled = true
+      ServeSetNotDisturb({
+        talk_type: 2,
+        receiver_id: parseInt(this.groupId),
+        is_disturb: value ? 1 : 0,
+      })
+        .then(res => {
+          if (res.code == 200) {
+            this.$emit('disturb-change', {
+              group_id: this.groupId,
+              status: value ? 1 : 0,
+            })
+          } else {
+            this.detail.is_disturb = value ? 0 : 1
+          }
+        })
+        .catch(() => {
+          this.detail.is_disturb = value ? 0 : 1
+        })
+        .finally(() => {
+          this.disturbDisabled = false
+        })
+    },
+
+    // 设置用户群名片
+    editRemark() {
+      if (this.editRemarkText == '') {
+        this.isEditRemark = false
+        return
+      }
+
+      if (this.detail.visitCard == this.editRemarkText) {
+        this.isEditRemark = false
+        return
+      }
+
+      ServeUpdateGroupCard({
+        group_id: parseInt(this.groupId),
+        visit_card: this.editRemarkText,
+      }).then(res => {
+        if (res.code == 200) {
+          this.isEditRemark = false
+          this.detail.visitCard = this.editRemarkText
+        }
+      })
+    },
+
+    // 查看用户信息
+    openUserDetail(user_id) {
+      this.$user(user_id)
+    },
+
+    // 邀请好友加入群聊
+    addGroupMembers() {
+      sessionStorage.setItem('invite_group_id', this.detail.groupId)
+      this.inviteFriendBox = true
+    },
+
+    // 邀请好友成功之后的回调事件
+    inviteSuccess() {
+      this.inviteFriendBox = false
+      this.loadMembers()
+
+      this.$notify({
+        title: '邀请成功',
+        message: `好友已成功加入群组...`,
+        type: 'success',
+      })
+    },
+
+    // 发送群聊
+    sendGroup() {
+      this.$emit('send-group', this.detail.groupId)
+    },
+
+    // 退出群操操作
+    signout() {
+      this.signoutStatus = 1
+      ServeSecedeGroup({
+        group_id: this.detail.groupId,
+      })
+        .then(res => {
+          if (res.code == 200) {
+            this.signoutStatus = 3
+            setTimeout(() => {
+              this.signoutStatus = 0
+              this.isShowSignout = false
+              this.$emit('quit-group')
+            }, 1500)
+          } else {
+            this.signoutStatus = 2
+            setTimeout(() => {
+              this.signoutStatus = 0
+            }, 3000)
+          }
+        })
+        .catch(() => {
+          this.signoutStatus = 2
+          setTimeout(() => {
+            this.signoutStatus = 0
+          }, 3000)
+        })
+    },
+
+    // 解散群组
+    dismiss() {
+      this.$confirm(`你确定要解散当前群组吗?此操作是不可恢复的!`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+        center: true,
+        dangerouslyUseHTMLString: true,
+      }).then(() => {})
+    },
+  },
+}
+</script>
+<style lang="less" scoped>
+.container {
+  height: 100%;
+
+  .header {
+    height: 60px;
+    line-height: 60px;
+    padding: 0;
+    text-align: center;
+    box-shadow: -1px 0px 5px 0px #cccccc45;
+    position: relative;
+
+    span {
+      font-size: 16px;
+      font-weight: 400;
+    }
+
+    .icon-send {
+      position: absolute;
+      left: 15px;
+      top: 22px;
+      font-size: 18px;
+      cursor: pointer;
+    }
+
+    .el-icon-close {
+      position: absolute;
+      right: 15px;
+      top: 22px;
+      font-size: 18px;
+      cursor: pointer;
+    }
+  }
+
+  .main {
+    padding: 0;
+  }
+}
+
+.list-item {
+  position: relative;
+  padding: 16px 16px 0;
+  min-height: 18px;
+  font-size: 14px;
+
+  &.flex {
+    display: flex;
+    justify-content: space-between;
+  }
+
+  .edit-visit-card {
+    position: initial;
+    color: #a29f9f;
+  }
+
+  .edit-remark-icon {
+    margin-left: 5px;
+    color: rgb(169, 184, 187);
+    position: absolute;
+    top: 20px;
+    cursor: pointer;
+  }
+
+  .edit-input {
+    width: 46%;
+    height: 25px;
+    line-height: 25px;
+    border: 1px solid #92cbff;
+    padding-left: 5px;
+    border-radius: 3px;
+  }
+
+  .input-submit {
+    width: 55px;
+    text-align: center;
+    height: 25px;
+    line-height: 25px;
+    background-color: #008cee;
+    border-radius: 2px;
+    display: inline-block;
+    color: #fff !important;
+    font-size: 12px;
+    margin-left: 10px;
+    cursor: pointer;
+  }
+
+  .group-setting-title {
+    max-width: 250px;
+    display: inline-block;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    vertical-align: bottom;
+  }
+
+  .group-boss-name {
+    display: inline-block;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    word-wrap: normal;
+    max-width: 180px;
+    position: relative;
+    top: 4px;
+  }
+
+  .group-invite {
+    height: 30px;
+    line-height: 30px;
+    text-align: center;
+    color: #877272;
+    cursor: pointer;
+    border-radius: 2px;
+    border: 1px dashed #d9bbbb;
+    font-size: 13px;
+    transition: all 0.5s ease-in-out;
+    &:hover {
+      color: #ff5722;
+      border-color: #ff5722;
+      transform: scale(1.01);
+    }
+  }
+
+  .more {
+    color: #409eff;
+    cursor: pointer;
+    font-size: 12px;
+  }
+}
+
+.list-item-tips {
+  font-size: 12px;
+  color: #b1b1b1;
+  margin-top: 5px;
+  padding-left: 16px;
+  padding-right: 16px;
+  font-weight: 300;
+  overflow: hidden;
+  word-break: break-word;
+}
+
+.group-notice {
+  line-height: 22px;
+}
+
+.member-box {
+  min-height: 180px;
+  padding: 10px;
+  margin-bottom: 20px;
+  border: 1px solid #ecebeb;
+  border-radius: 3px;
+
+  .view-box {
+    width: 100%;
+    height: 30px;
+    margin-top: 20px;
+    margin-bottom: 15px;
+    position: relative;
+
+    input {
+      width: calc(100% - 40px);
+      height: 30px;
+      line-height: 28px;
+      border-radius: 3px;
+      border: 1px solid #f1e9e9;
+      color: #b3b0b0;
+      font-size: 13px;
+      padding: 0 10px 0 30px;
+
+      &::-webkit-input-placeholder {
+        color: #ccc9c9;
+        font-size: 13px;
+      }
+    }
+
+    .i-sousuo {
+      color: rgb(179, 176, 176);
+      position: absolute;
+      left: 10px;
+      top: 9px;
+    }
+
+    span {
+      position: relative;
+
+      i {
+        font-size: 24px;
+        top: -3px;
+        left: 10px;
+        position: absolute;
+        color: #ccc;
+      }
+    }
+  }
+
+  .row-header {
+    width: 100%;
+    height: 30px;
+    margin-bottom: 10px;
+    border-bottom: 1px solid #e0dddd;
+
+    div {
+      height: 30px;
+      line-height: 30px;
+
+      &:nth-child(2) {
+        text-align: center;
+      }
+
+      &:nth-child(3) {
+        text-align: right;
+      }
+    }
+  }
+
+  .row-items {
+    width: 100%;
+    height: 30px;
+    margin-bottom: 3px;
+    font-size: 12px;
+    cursor: pointer;
+
+    &:hover {
+      background: #f6f6f6;
+    }
+
+    div {
+      height: 30px;
+      line-height: 30px;
+
+      &:nth-child(2) {
+        text-align: center;
+      }
+
+      &:nth-child(3) {
+        text-align: right;
+      }
+    }
+
+    img {
+      display: inline-block;
+      border-radius: 50%;
+      position: relative;
+      top: 4px;
+    }
+
+    .nickname {
+      margin-left: 5px;
+      &:hover {
+        color: #3685d6;
+      }
+    }
+  }
+}
+
+.signout-box {
+  width: 100%;
+  height: 100px;
+  background: #ffffff;
+  position: absolute;
+  z-index: 2;
+  bottom: 0;
+  box-shadow: -1px -3px 11px 0px #cccccc82;
+  -webkit-animation: showFooter 0.35s ease-in-out;
+  -moz-animation: showFooter 0.35s ease-in-out;
+  animation: showFooter 0.35s ease-in-out;
+
+  p {
+    &:first-child {
+      text-align: center;
+      height: 35px;
+      line-height: 35px;
+    }
+
+    &:nth-child(2) {
+      text-align: center;
+      font-size: 12px;
+      color: #cccccc;
+    }
+  }
+
+  .mt38 {
+    margin-top: 38px;
+  }
+}
+
+.signout-btn {
+  text-align: center;
+  margin-top: 10px;
+
+  button {
+    height: 30px;
+    width: 90px;
+    line-height: 30px;
+    background: #007fbb;
+    border-radius: 3px;
+    cursor: pointer;
+    font-size: 14px;
+
+    &:first-child {
+      background: #ff3333;
+      color: white;
+    }
+
+    &:last-child {
+      background: #f1eded;
+    }
+  }
+}
+
+.container .footer {
+  height: 60px;
+  padding: 0;
+  line-height: 60px;
+  text-align: center;
+  background-color: #f8f8f8;
+
+  button {
+    width: 180px;
+    height: 35px;
+    line-height: 35px;
+    background: #ed3c3b;
+    border-radius: 3px;
+    color: white;
+    cursor: pointer;
+    font-size: 12px;
+    margin: auto auto;
+
+    &:active {
+      background: #f5b8b8;
+    }
+  }
+}
+
+@keyframes showFooter {
+  0% {
+    transform: translateY(75px);
+  }
+
+  to {
+    transform: translateY(0);
+  }
+}
+
+@-webkit-keyframes showFooter {
+  0% {
+    -webkit-transform: translateY(75px);
+  }
+
+  to {
+    -webkit-transform: translateY(0);
+  }
+}
+</style>

--
Gitblit v1.9.3