Files
ckb-SuperAdmin/Backend/src/components/group/GroupManage.vue
2025-04-09 09:31:09 +08:00

927 lines
24 KiB
Vue

<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="searchMembers"
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 filterMembers"
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="$refs.userBusinessCard.open(item.user_id)"
></span>
<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>
<!-- 查看好友用户信息 -->
<UserBusinessCard ref="userBusinessCard" />
<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 UserBusinessCard from '@/components/user/UserBusinessCard'
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,
UserBusinessCard,
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: '',
},
// 群成员列表
searchMembers: '',
batchDelMember: false,
members: [],
// 群公告相关数据
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: {
filterMembers() {
return this.searchMembers == ''
? this.members
: this.members.filter(item => {
return (
item.nickname.match(this.searchMembers) != null ||
item.visit_card.match(this.searchMembers) != 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.map(item => {
item.isShow = false
return item
})
}
})
},
// 修改群信息
editGroup() {
this.$refs.groupForm.validate(valid => {
if (!valid) return false
this.loading = true
ServeEditGroup({
group_id: 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: this.notice.form.id,
group_id: 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.$refs.userBusinessCard.open(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,
customClass: 'border-radius0',
})
.then(() => {
ServeRemoveMembersGroup({
group_id: this.groupId,
members_ids: ids.join(','),
}).then(res => {
if (res.code == 200) {
this.loadMembers()
this.$notify({
title: '删除成功',
message: `已成功将群成员移除群组...`,
type: 'success',
})
}
})
})
.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: 4px;
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>