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/GroupManage.vue |  921 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 921 insertions(+), 0 deletions(-)

diff --git a/src/components/group/GroupManage.vue b/src/components/group/GroupManage.vue
new file mode 100644
index 0000000..7c60852
--- /dev/null
+++ b/src/components/group/GroupManage.vue
@@ -0,0 +1,921 @@
+<template>
+  <div class="lum-dialog-mask">
+    <div class="lum-dialog-box">
+      <el-container class="container">
+        <el-header class="header no-select" height="60px">
+          <p>群管理 ({{ detail.group_name }})</p>
+          <p class="tools">
+            <i class="el-icon-close" @click="$emit('close')" />
+          </p>
+        </el-header>
+        <el-main class="main no-padding">
+          <el-container class="full-height">
+            <el-aside width="100px" class="aside-border no-select">
+              <div
+                v-for="(menu, index) in menus"
+                :key="menu.name"
+                class="menu-list"
+                :class="{ selectd: tabIndex == index }"
+                v-text="menu.name"
+                @click="triggerTab(index)"
+              />
+            </el-aside>
+
+            <!-- 群介绍模块 -->
+            <el-main v-if="tabIndex == 0">
+              <el-row>
+                <el-col :span="14">
+                  <el-form ref="groupForm" :model="form" :rules="rules">
+                    <el-form-item label="群名:" prop="group_name">
+                      <el-input
+                        v-model="form.group_name"
+                        size="medium"
+                        placeholder="请输入群名称"
+                        maxlength="30"
+                        show-word-limit
+                      />
+                    </el-form-item>
+                    <el-form-item label="群描述:">
+                      <el-input
+                        v-model="form.profile"
+                        type="textarea"
+                        rows="3"
+                        placeholder="请输入群描述"
+                      />
+                    </el-form-item>
+                    <el-form-item>
+                      <el-button
+                        type="primary"
+                        icon="el-icon-edit"
+                        size="small"
+                        :loading="loading"
+                        @click="editGroup"
+                        >修改信息
+                      </el-button>
+                    </el-form-item>
+                  </el-form>
+                </el-col>
+                <el-col :span="10" class="avatar-col">
+                  <div class="avatar-box">
+                    <img v-show="form.avatar" :src="form.avatar" />
+                    <div class="upload-icon">
+                      <i class="el-icon-upload" />
+                    </div>
+                    <div class="upload-mask" @click="isAvatarCropper = true">
+                      <i class="el-icon-plus" />
+                    </div>
+                  </div>
+                  <p style="margin-top: 20px">设置头像</p>
+                </el-col>
+              </el-row>
+            </el-main>
+
+            <!-- 群成员模块 -->
+            <el-main v-else-if="tabIndex == 1" class="no-padding">
+              <el-container class="full-height">
+                <el-header height="50px" class="notice-header">
+                  <el-input
+                    v-model="keywords"
+                    style="width: 200px"
+                    size="small"
+                    clearable
+                    prefix-icon="el-icon-search"
+                    :placeholder="`搜索群成员 ( 共${members.length}人 )`"
+                  />
+                  <p>
+                    <el-button
+                      plain
+                      size="small"
+                      icon="el-icon-plus"
+                      @click="inviteFriendBox = true"
+                      >邀请好友
+                    </el-button>
+                    <el-button
+                      v-if="batchDelMember"
+                      type="danger"
+                      size="small"
+                      icon="el-icon-delete"
+                      @click="deleteMembers"
+                      >确认删除
+                    </el-button>
+                    <el-button
+                      v-else
+                      plain
+                      size="small"
+                      icon="el-icon-finished"
+                      @click="batchDelMember = true"
+                      >批量操作
+                    </el-button>
+                  </p>
+                </el-header>
+                <el-main class="no-padding">
+                  <el-scrollbar
+                    class="full-height"
+                    tag="section"
+                    :native="false"
+                  >
+                    <div class="members">
+                      <div
+                        v-for="member in searchs"
+                        class="member no-select"
+                        :class="{
+                          selectd: member.is_delete && batchDelMember,
+                        }"
+                        :key="member.user_id"
+                      >
+                        <div class="item-header">
+                          <div class="avatar" @click="catUserDetail(member)">
+                            <el-avatar :size="30" :src="member.avatar">
+                              <img src="~@/assets/image/detault-avatar.jpg" />
+                            </el-avatar>
+                            <span class="nickname" v-text="member.nickname" />
+                            <span class="larkc-tag" v-show="member.leader == 2">
+                              群主
+                            </span>
+                          </div>
+                          <div
+                            v-show="batchDelMember && member.leader != 2"
+                            class="tools"
+                          >
+                            <i
+                              class="el-icon-success"
+                              :class="{ 'is-delete': member.is_delete }"
+                              @click.stop="triggerDelBtn(member)"
+                            />
+                          </div>
+                        </div>
+                        <div class="profile">
+                          签名 | {{ member.motto ? member.motto : '未设置' }}
+                        </div>
+                      </div>
+                    </div>
+                  </el-scrollbar>
+                </el-main>
+              </el-container>
+            </el-main>
+
+            <!-- 群公告模块 -->
+            <el-main v-else-if="tabIndex == 2" class="no-padding">
+              <el-container class="full-height">
+                <el-header
+                  class="notice-header"
+                  height="50px"
+                  style="padding-left: 14px"
+                >
+                  <span>群公告 ({{ notice.items.length }})</span>
+                  <el-button
+                    plain
+                    size="small"
+                    icon="el-icon-plus"
+                    @click="showNoticeBox(0, '', '')"
+                  >
+                    添加公告
+                  </el-button>
+                </el-header>
+
+                <el-main class="no-padding">
+                  <el-scrollbar
+                    class="full-height"
+                    tag="section"
+                    :native="false"
+                  >
+                    <div v-if="notice.items.length == 0" class="empty-notice">
+                      <SvgNotData style="width: 80px; margin-bottom: 10px" />
+                      <span>暂无群公告</span>
+                    </div>
+
+                    <div v-else class="notices">
+                      <div
+                        v-for="(item, index) in notice.items"
+                        :key="item.id"
+                        class="notice"
+                      >
+                        <div class="title">
+                          <span
+                            class="left-title"
+                            v-text="item.title"
+                            @click="
+                              showNoticeBox(item.id, item.title, item.content)
+                            "
+                          ></span>
+                          <span
+                            class="right-tools no-select"
+                            @click="catNoticeDetail(index)"
+                            >{{ item.isShow ? '收起' : '展开' }}</span
+                          >
+                        </div>
+                        <p class="datetime">
+                          <el-avatar :size="15" :src="item.avatar">
+                            <img src="~@/assets/image/detault-avatar.jpg" />
+                          </el-avatar>
+                          <span
+                            class="text nickname"
+                            v-text="item.nickname"
+                            @click="$user(item.user_id)"
+                          />
+                          <span class="text">
+                            发表于 {{ item.created_at.substr(0, 16) }}
+                          </span>
+                        </p>
+                        <p
+                          class="content"
+                          :class="{ retract: !item.isShow }"
+                          v-text="item.content"
+                        ></p>
+                      </div>
+                    </div>
+                  </el-scrollbar>
+                </el-main>
+              </el-container>
+            </el-main>
+
+            <el-main v-else-if="tabIndex == 3" class="no-padding"> </el-main>
+          </el-container>
+        </el-main>
+      </el-container>
+    </div>
+
+    <!-- 编辑公告信息 -->
+    <div class="lum-dialog-mask animated fadeIn" v-show="notice.isShowform">
+      <div class="notice-box">
+        <h4>编辑群公告</h4>
+        <el-form ref="noticeForm" :model="notice.form" :rules="notice.rules">
+          <el-form-item label="标题" prop="title">
+            <el-input
+              v-model="notice.form.title"
+              size="medium"
+              placeholder="请输入标题..."
+              maxlength="30"
+              show-word-limit
+            />
+          </el-form-item>
+          <el-form-item label="详情" prop="content">
+            <el-input
+              v-model="notice.form.content"
+              type="textarea"
+              rows="5"
+              placeholder="请输入公告详情..."
+              maxlength="500"
+            />
+          </el-form-item>
+          <el-form-item style="text-align: right">
+            <el-button plain size="small" @click="notice.isShowform = false">
+              取消
+            </el-button>
+            <el-button
+              type="primary"
+              size="small"
+              :loading="notice.loading"
+              @click="onSubmitNotice"
+              >提交
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    </div>
+
+    <transition name="el-fade-in-linear">
+      <AvatarCropper v-if="isAvatarCropper" @close="closeAvatarCropper" />
+    </transition>
+
+    <transition name="el-fade-in-linear">
+      <GroupLaunch
+        v-if="inviteFriendBox"
+        :group-id="groupId"
+        @close="inviteFriendBox = false"
+        @invite-success="inviteSuccess"
+      />
+    </transition>
+  </div>
+</template>
+<script>
+import AvatarCropper from '@/components/layout/AvatarCropper'
+import GroupLaunch from '@/components/group/GroupLaunch'
+import { SvgNotData } from '@/core/icons'
+import {
+  ServeGetGroupMembers,
+  ServeGetGroupNotices,
+  ServeEditGroupNotice,
+  ServeRemoveMembersGroup,
+  ServeGroupDetail,
+  ServeEditGroup,
+} from '@/api/group'
+
+export default {
+  name: 'GroupManage',
+  props: {
+    groupId: {
+      type: [String, Number],
+      default: null,
+    },
+  },
+  components: {
+    AvatarCropper,
+    GroupLaunch,
+    SvgNotData,
+  },
+  data() {
+    return {
+      // 当前选中菜单
+      tabIndex: 0,
+      menus: [
+        { name: '群信息' },
+        { name: '群成员' },
+        { name: '群公告' },
+        { name: '群设置' },
+      ],
+
+      loading: false,
+      form: {
+        group_name: '',
+        profile: '',
+        avatar: '',
+      },
+      rules: {
+        group_name: [
+          {
+            required: true,
+            message: '用户昵称不能为空!',
+            trigger: 'blur',
+          },
+        ],
+      },
+
+      detail: {
+        group_name: '',
+        profile: '',
+        avatar: '',
+      },
+
+      // 群成员列表
+      batchDelMember: false,
+      members: [],
+      keywords:"",
+
+      // 群公告相关数据
+      notice: {
+        isShowform: false,
+        loading: false,
+        form: {
+          id: 0,
+          title: '',
+          content: '',
+        },
+        rules: {
+          title: [
+            {
+              required: true,
+              message: '标题不能为空!',
+              trigger: 'blur',
+            },
+          ],
+          content: [
+            {
+              required: true,
+              message: '详情不能为空',
+              trigger: 'blur',
+            },
+          ],
+        },
+        items: [],
+      },
+
+      inviteFriendBox: false,
+      isAvatarCropper: false,
+    }
+  },
+  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() {
+    this.loadGroupDetail()
+    this.loadMembers()
+    this.loadNotices()
+  },
+  methods: {
+    // 加载群信息
+    loadGroupDetail() {
+      ServeGroupDetail({
+        group_id: this.groupId,
+      }).then(({ code, data }) => {
+        if (code == 200) {
+          this.form = this.detail = {
+            group_name: data.group_name,
+            profile: data.profile,
+            avatar: data.avatar,
+          }
+        }
+      })
+    },
+
+    // 加载群组成员列表
+    loadMembers() {
+      ServeGetGroupMembers({
+        group_id: this.groupId,
+      }).then(res => {
+        if (res.code == 200) {
+          this.members = res.data.map(item => {
+            item.is_delete = false
+            return item
+          })
+        }
+      })
+    },
+
+    // 加载群组公告列表
+    loadNotices() {
+      ServeGetGroupNotices({
+        group_id: this.groupId,
+      }).then(res => {
+        if (res.code == 200) {
+          this.notice.items = res.data.rows.map(item => {
+            item.isShow = false
+            return item
+          })
+        }
+      })
+    },
+
+    // 修改群信息
+    editGroup() {
+      this.$refs.groupForm.validate(valid => {
+        if (!valid) return false
+        this.loading = true
+        ServeEditGroup({
+          group_id: parseInt(this.groupId),
+          group_name: this.form.group_name,
+          profile: this.form.profile,
+          avatar: this.form.avatar,
+        })
+          .then(res => {
+            if (res.code == 200) {
+              this.detail.group_name = this.form.group_name
+              this.detail.profile = this.form.profile
+              this.detail.avatar = this.form.avatar
+
+              this.$message({
+                message: '信息修改成功...',
+                type: 'success',
+              })
+            } else {
+              this.$message('信息修改失败...')
+            }
+          })
+          .finally(() => {
+            this.loading = false
+          })
+      })
+    },
+
+    // 左侧菜单栏切换事件
+    triggerTab(i) {
+      this.tabIndex = i
+    },
+
+    // 关闭头像裁剪弹出层
+    closeAvatarCropper(type, avatar = '') {
+      this.isAvatarCropper = false
+      if (type == 1 && avatar != '') {
+        this.form.avatar = avatar
+      }
+    },
+
+    // 显示编辑公告窗口
+    showNoticeBox(id = 0, title = '', content = '') {
+      this.notice.isShowform = true
+      this.notice.form.id = id
+      this.notice.form.title = title
+      this.notice.form.content = content
+    },
+
+    // 编辑公告提交事件
+    onSubmitNotice() {
+      this.$refs.noticeForm.validate(valid => {
+        if (!valid) return false
+
+        this.notice.loading = true
+        ServeEditGroupNotice({
+          notice_id: parseInt(this.notice.form.id),
+          group_id: parseInt(this.groupId),
+          title: this.notice.form.title,
+          content: this.notice.form.content,
+          is_top: 0,
+          is_confirm: 0,
+        })
+          .then(res => {
+            if (res.code == 200) {
+              this.notice.isShowform = false
+              this.loadNotices()
+              this.$notify({
+                title: '消息提示',
+                message: this.notice.form.id
+                  ? '群公告修改成功...'
+                  : '群公告添加成功...',
+                type: 'success',
+              })
+            } else {
+              this.$notify({
+                title: '消息提示',
+                message: this.notice.form.id
+                  ? '群公告修改失败...'
+                  : '群公告添加失败...',
+                type: 'success',
+              })
+            }
+          })
+          .catch(() => {
+            this.$notify({
+              title: '消息提示',
+              message: '网络异常,请稍后再试...',
+              position: 'bottom-right',
+              type: 'warning',
+            })
+          })
+          .finally(() => {
+            this.notice.loading = false
+          })
+      })
+    },
+
+    // 展开/收起群公告详情
+    catNoticeDetail(index) {
+      this.notice.items[index].isShow = !this.notice.items[index].isShow
+    },
+
+    // 查看群成员信息事件
+    catUserDetail(item) {
+      this.$user(item.user_id)
+    },
+
+    // 选中删除成员事件
+    triggerDelBtn(member) {
+      let i = this.members.findIndex(item => {
+        return item.id === member.id
+      })
+
+      this.members[i].is_delete = !this.members[i].is_delete
+    },
+
+    // 批量删除群成员
+    deleteMembers() {
+      let ids = [],
+        names = []
+
+      this.members.forEach(item => {
+        if (item.is_delete) {
+          ids.push(item.user_id)
+          names.push(item.nickname)
+        }
+      })
+
+      if (ids.length == 0) {
+        this.batchDelMember = false
+        return
+      }
+
+      this.$confirm(`您确定要将【 ${names.join('、')}】移出群聊?`, '温馨提示', {
+        confirmButtonText: '确定删除',
+        cancelButtonText: '取消',
+        dangerouslyUseHTMLString: true,
+      })
+        .then(() => {
+          ServeRemoveMembersGroup({
+            group_id: parseInt(this.groupId),
+            members_ids: ids.join(','),
+          }).then(res => {
+            if (res.code == 200) {
+              this.loadMembers()
+              this.$notify({
+                title: '删除成功',
+                message: `已成功将群成员移除群组...`,
+                type: 'success',
+              })
+              this.batchDelMember = false
+            }
+          })
+        })
+        .catch(() => {
+          this.members.map(item => {
+            return (item.is_delete = false)
+          })
+          this.batchDelMember = false
+        })
+    },
+
+    // 好友邀请成功回调方法
+    inviteSuccess() {
+      this.inviteFriendBox = false
+      this.loadMembers()
+    },
+  },
+}
+</script>
+<style lang="less" scoped>
+.lum-dialog-box {
+  width: 80%;
+  height: 500px;
+  max-width: 800px;
+}
+
+.aside-border {
+  display: flex;
+  flex-direction: column;
+  padding: 8px;
+  border-right: 1px solid #f5f5f5;
+
+  .menu-list {
+    height: 25px;
+    line-height: 25px;
+    margin: 8px 2px;
+    font-weight: 400;
+    font-size: 13px;
+    background-color: white;
+    cursor: pointer;
+    border-left: 3px solid white;
+    padding-left: 10px;
+
+    &.selectd {
+      color: #2196f3;
+      border-color: #2196f3;
+    }
+  }
+}
+
+.avatar-col {
+  display: flex;
+  justify-content: center;
+  flex-direction: column;
+  align-items: center;
+}
+
+.avatar-box {
+  width: 100px;
+  height: 100px;
+  box-shadow: 0px 0px 7px 1px #e8e4e4;
+  border-radius: 50%;
+  position: relative;
+  cursor: pointer;
+  transition: ease 0.5s;
+
+  .upload-icon {
+    position: absolute;
+    right: 0;
+    top: 0;
+    width: 40px;
+    height: 40px;
+    border-radius: 50%;
+    background-color: rgba(184, 184, 197, 0.2);
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    i {
+      font-size: 30px;
+      color: #1bb0f3;
+    }
+  }
+
+  img {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    border-radius: 50%;
+    z-index: 0;
+  }
+
+  .upload-mask {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    border-radius: 50%;
+    background-color: rgba(0, 0, 0, 0.2);
+    z-index: 3;
+    display: none;
+    justify-content: center;
+    align-items: center;
+
+    i {
+      font-size: 30px;
+      color: white;
+    }
+  }
+
+  &:hover .upload-mask {
+    display: flex;
+  }
+}
+
+/* 群成员相关 start */
+.members {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  padding: 5px 20px;
+  justify-content: space-between;
+
+  .member {
+    width: 48%;
+    height: 70px;
+    border-radius: 3px;
+    cursor: pointer;
+    border: 1px dashed #e2dcdc;
+    margin: 5px 0;
+    padding: 3px;
+    transition: ease 0.5s;
+
+    .larkc-tag {
+      color: #3370ff;
+      background-color: #e1eaff;
+    }
+
+    .item-header {
+      display: flex;
+      flex-direction: row;
+      justify-content: space-between;
+      align-items: center;
+
+      .avatar {
+        flex: 1 1;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        padding: 3px 5px;
+
+        .nickname {
+          font-size: 13px;
+          margin: 0 5px 0 15px;
+        }
+      }
+
+      .tools {
+        flex-basis: 50px;
+        overflow: hidden;
+        text-align: right;
+        padding-right: 5px;
+
+        i {
+          color: #cccccc;
+        }
+
+        .is-delete {
+          color: #03a9f4;
+        }
+      }
+    }
+
+    .profile {
+      color: #8f8a8a;
+      font-size: 12px;
+      padding: 3px 5px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      margin: 3px 0;
+    }
+
+    &:hover,
+    &.selectd {
+      border-color: #2196f3;
+    }
+  }
+}
+
+/* 群成员相关 end */
+
+/* 公告相关 start */
+.notice-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.empty-notice {
+  width: 100%;
+  height: 100%;
+  min-height: 200px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-direction: column;
+
+  span {
+    color: #cccccc;
+    font-size: 13px;
+  }
+}
+
+.notices {
+  .notice {
+    cursor: pointer;
+    min-height: 76px;
+    overflow: hidden;
+    border-bottom: 1px dashed #e2dcdc;
+    margin-bottom: 15px;
+    margin-right: 15px;
+    padding-bottom: 5px;
+    margin: 2px 20px 15px 15px;
+
+    h6 {
+      font-size: 15px;
+      font-weight: 300;
+    }
+
+    .title {
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      justify-content: space-between;
+      height: 30px;
+
+      .left-title {
+        flex: 1 1;
+        height: 100%;
+        line-height: 30px;
+        font-size: 14px;
+      }
+
+      .right-tools {
+        flex-basis: 70px;
+        flex-shrink: 0;
+        height: 100%;
+        line-height: 30px;
+        text-align: right;
+        font-weight: 300;
+        font-size: 12px;
+        color: #2196f3;
+      }
+    }
+
+    .datetime {
+      font-size: 10px;
+      color: #a59696;
+      font-weight: 300;
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      margin: 10px 0;
+
+      .text {
+        margin: 0 5px;
+      }
+
+      .nickname {
+        color: #2196f3;
+        font-weight: 400;
+      }
+    }
+
+    .retract {
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+
+    .content {
+      font-size: 12px;
+      line-height: 28px;
+      font-weight: 500;
+      color: #7d7a7a;
+    }
+  }
+}
+
+.notice-box {
+  position: relative;
+  padding: 28px;
+  background: #fff;
+  box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.2);
+  border-radius: 10px;
+  overflow: hidden;
+  box-sizing: border-box;
+  height: 415px;
+  width: 420px;
+
+  h4 {
+    margin-bottom: 20px;
+    font-weight: 400;
+  }
+}
+
+/* 公告相关 end */
+
+/deep/.el-scrollbar__wrap {
+  overflow-x: hidden;
+}
+</style>

--
Gitblit v1.9.3