10.10综合交易所原始源码-管理后台
1
admin
2026-01-06 089bf5d2378b3c4a61d795b2a92bede2c193b771
src/components/group/GroupManage.vue
New file
@@ -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>