Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 新增添加管理员功能 #106

Merged
merged 3 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ declare module '@vue/runtime-core' {
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElBadge: typeof import('element-plus/es')['ElBadge']
ElButton: typeof import('element-plus/es')['ElButton']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImageViewer: typeof import('element-plus/es')['ElImageViewer']
ElInput: typeof import('element-plus/es')['ElInput']
Expand Down
2 changes: 2 additions & 0 deletions src/constant/group.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// 最大管理员数量
export const MAX_ADMIN_COUNT = 3
6 changes: 6 additions & 0 deletions src/services/apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,10 @@ export default {
/** 会话详情(联系人列表发消息用) */
sessionDetailWithFriends: (params: { uid: number }) =>
getRequest<SessionItem>(urls.sessionDetailWithFriends, { params }),
/** 添加群管理 */
addAdmin: ({ roomId, uidList }: { roomId: number; uidList: number[] }) =>
putRequest<Boolean>(urls.addAdmin, {
roomId,
uidList,
}),
}
1 change: 1 addition & 0 deletions src/services/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export default {
createGroup: `${prefix}/capi/room/group`, // 新增群组
getGroupUserList: `${prefix}/capi/room/public/group/member/page`,
inviteGroupMember: `${prefix}/capi/room/group/member`, // 邀请群成员
addAdmin: `${prefix}/capi/room/group/admin`, // 添加管理员
groupDetail: `${prefix}/capi/room/public/group`, // 群组详情
sessionDetail: `${prefix}/capi/chat/public/contact/detail`, // 会话详情
sessionDetailWithFriends: `${prefix}/capi/chat/public/contact/detail/friend`, // 会话详情(联系人列表发消息用)
Expand Down
11 changes: 10 additions & 1 deletion src/stores/cached.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useGlobalStore } from '@/stores/global'
import type { CacheUserItem, CacheBadgeItem } from '@/services/types'
import { isDiffNow10Min } from '@/utils/computedTime'

type BaseUserItem = Pick<CacheUserItem, 'uid' | 'avatar' | 'name'>
export type BaseUserItem = Pick<CacheUserItem, 'uid' | 'avatar' | 'name'>

export const useCachedStore = defineStore(
'cached',
Expand Down Expand Up @@ -110,6 +110,14 @@ export const useCachedStore = defineStore(
return currentAtUsersList.value?.filter((item) => item.name?.startsWith(searchKey))
}

/**
* 通过用户ID列表获取用户基本信息
* @param uidList
*/
const filterUsersByUidList = (uidList: number[]) => {
return currentAtUsersList.value.filter((user) => uidList.includes(user.uid))
}

return {
userCachedList,
badgeCachedList,
Expand All @@ -119,6 +127,7 @@ export const useCachedStore = defineStore(
filterUsers,
getGroupAtUserBaseInfo,
currentAtUsersList,
filterUsersByUidList,
}
},
{ persist: true },
Expand Down
9 changes: 9 additions & 0 deletions src/stores/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ export const useChatStore = defineStore('chat', () => {
replyMapping.set(currentRoomId.value, val as Map<number, number[]>)
},
})
const isGroup = computed(() => currentRoomType.value === RoomTypeEnum.Group)
/**
* 获取当前会话信息
*/
const currentSessionInfo = computed(() =>
sessionList.find((session) => session.roomId === globalStore.currentSession.roomId),
)

const chatListToBottomAction = ref<() => void>() // 外部提供消息列表滚动到底部事件

Expand Down Expand Up @@ -445,5 +452,7 @@ export const useChatStore = defineStore('chat', () => {
updateSession,
updateSessionLastActiveTime,
markSessionRead,
isGroup,
currentSessionInfo,
}
})
71 changes: 67 additions & 4 deletions src/stores/group.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { ref, reactive, computed } from 'vue'
import { computed, reactive, ref } from 'vue'
import apis from '@/services/apis'
import { defineStore } from 'pinia'
import { useCachedStore } from '@/stores/cached'
import { useGlobalStore } from '@/stores/global'
import type { UserItem, GroupDetailReq } from '@/services/types'
import type { GroupDetailReq, UserItem } from '@/services/types'
import { pageSize } from './chat'
import cloneDeep from 'lodash/cloneDeep'
import { OnlineEnum } from '@/enums'
import { OnlineEnum, RoleEnum } from '@/enums'
import { uniqueUserList } from '@/utils/unique'
import { useCachedStore } from '@/stores/cached'

const sorAction = (pre: UserItem, next: UserItem) => {
if (pre.activeStatus === OnlineEnum.ONLINE && next.activeStatus === OnlineEnum.ONLINE) {
Expand All @@ -30,6 +30,50 @@ export const useGroupStore = defineStore('group', () => {
const userList = ref<UserItem[]>([])
const userListOptions = reactive({ isLast: false, loading: true, cursor: '' })
const currentRoomId = computed(() => globalStore.currentSession.roomId)
/**
* 获取当前群主ID
*/
const currentLordId = computed(() => {
const list = userList.value.filter((member) => member.roleId === RoleEnum.LORD)
if (list.length) {
return list[0]?.uid
}
return -99
})
/**
* 获取当前管理员ID列表
*/
const adminUidList = computed(() => {
return userList.value
.filter((member) => member.roleId === RoleEnum.ADMIN)
.map((member) => member.uid)
})
/**
* 获取管理员基本信息列表
*/
const adminList = computed(() => {
return cachedStore.filterUsersByUidList(adminUidList.value)
})
/**
* 获取管理员基本信息列表
*/
const memberList = computed(() => {
const memberInfoList = cachedStore.filterUsersByUidList(userList.value.map((item) => item.uid))
return memberInfoList.map((member) => {
if (adminUidList.value.includes(member.uid)) {
return {
...member,
roleId: RoleEnum.ADMIN,
}
} else if (member.uid === currentLordId.value) {
return {
...member,
roleId: RoleEnum.LORD,
}
}
return member
})
})
const countInfo = ref<GroupDetailReq>({
avatar: '',
groupName: '',
Expand Down Expand Up @@ -99,15 +143,34 @@ export const useGroupStore = defineStore('group', () => {
userList.value = userList.value.filter((item) => item.uid !== uid)
}

/**
* 添加管理员
* @param uidList
*/
const addAdmin = async (uidList: number[]) => {
await apis.addAdmin({ roomId: currentRoomId.value, uidList }).send()

// 更新群成员列表
userList.value.forEach((user) => {
if (uidList.includes(user.uid)) {
user.roleId = RoleEnum.ADMIN
}
})
}

return {
userList,
userListOptions,
loadMore,
getGroupUserList,
getCountStatistic,
currentLordId,
countInfo,
batchUpdateUserStatus,
showGroupList,
filterUser,
adminList,
memberList,
addAdmin,
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<script setup lang="ts">
import { MAX_ADMIN_COUNT } from '@/constant/group'
import { computed, ref } from 'vue'
import { useGroupStore } from '@/stores/group'
import type { TransferKey } from 'element-plus'
import { ElMessage } from 'element-plus'
import type { BaseUserItem } from '@/stores/cached'

const groupStore = useGroupStore()

const isShowSetAdmin = ref<boolean>(false)
const selectedUidList = ref<number[]>([])

const computedMemberList = computed(() => {
return (groupStore.memberList as (BaseUserItem & { roleId?: number })[]).map((member) => ({
...member,
disabled: !!member.roleId,
}))
})

const selectMember = (checkedUidList: TransferKey[]) => {
if (checkedUidList.length + groupStore.adminList.length > MAX_ADMIN_COUNT) {
ElMessage.error('管理员数量不能超过' + MAX_ADMIN_COUNT + '个')
}
}

/**
* 添加管理员
*/
const addAdmin = async () => {
await groupStore.addAdmin(selectedUidList.value)
isShowSetAdmin.value = false
ElMessage.success('添加管理员成功')
}
</script>

<template>
<div class="setting-box">
<el-divider content-position="left">高级</el-divider>
<div class="advanced">
<div class="set-admin">
<h5>
<span>设置管理员</span>
<span class="admin-count">
({{ groupStore.adminList.length }}/{{ MAX_ADMIN_COUNT }})
</span>
</h5>
<div class="flex-center">
<el-button
type="primary"
size="small"
@click="isShowSetAdmin = true"
:disabled="groupStore.adminList.length >= MAX_ADMIN_COUNT"
>
设置管理员
</el-button>
<el-avatar
v-for="admin in groupStore.adminList"
:key="admin.uid"
:src="admin.avatar"
size="small"
class="admin-avatar"
>
<el-icon :size="10">
<Avatar />
</el-icon>
</el-avatar>
<el-dialog v-model="isShowSetAdmin">
<el-transfer
v-model="selectedUidList"
:data="computedMemberList"
:props="{
key: 'uid',
label: 'name',
disabled: 'disabled',
}"
class="add-admin-transfer flex-center"
:titles="['未设置', '已设置']"
@left-check-change="selectMember"
/>
<div class="add-admin-btn flex-center">
<el-button type="primary" @click="addAdmin">添加</el-button>
</div>
</el-dialog>
</div>
</div>
</div>
</div>
</template>

<style scoped lang="scss" src="./styles.scss"></style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.setting-box {
.advanced {
width: 100%;
padding: 10px;
border: 1px solid #777;
border-radius: 5px;

.set-admin {
display: flex;
align-items: center;
justify-content: space-between;

.admin-avatar {
margin: 0 3px;
}

.add-admin-transfer {
.add-admin-btn {
margin-top: 5px;
}
}
}
}
}
35 changes: 35 additions & 0 deletions src/views/Home/Chat/components/ChatList/RoomName/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script setup lang="ts">
import { useChatStore } from '@/stores/chat'
import { ref } from 'vue'
import SettingBox from '@/views/Home/Chat/components/ChatList/RoomName/components/SettingBox/SettingBox.vue'

const chatStore = useChatStore()
const isShowSetting = ref<boolean>(false)
</script>

<template>
<div class="room-name">
<span class="name">{{ chatStore.currentSessionInfo?.name }}</span>
<span
class="setting"
@click="isShowSetting = true"
v-if="chatStore.isGroup && !chatStore.currentSessionInfo?.hot_Flag"
>
设置
</span>
</div>
<el-drawer v-model="isShowSetting" direction="rtl" append-to-body>
<template #header>
<h4 class="text-[#f5f5f5]">设置</h4>
</template>
<template #default>
<SettingBox />
</template>
</el-drawer>
</template>

<style lang="scss" src="./styles.scss">
.el-drawer__header {
margin-bottom: 0 !important;
}
</style>
18 changes: 18 additions & 0 deletions src/views/Home/Chat/components/ChatList/RoomName/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.room-name {
display: flex;
align-items: center;
justify-content: space-between;
height: 50px;
padding: 0 10px;
background: var(--background-mask);
border-radius: 8px 8px 0 0;

.setting {
font-size: 13px;
cursor: pointer;

&:hover {
color: var(--hover-primary);
}
}
}
7 changes: 5 additions & 2 deletions src/views/Home/Chat/components/ChatList/index.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<script setup lang="ts">
import { ref, onMounted, nextTick, provide, computed } from 'vue'
import { computed, nextTick, onMounted, provide, ref } from 'vue'
import throttle from 'lodash/throttle'
import { useChatStore } from '@/stores/chat'
import type { MessageType } from '@/services/types'
import VirtualList from '@/components/VirtualList'
import MsgItem from './MsgItem/index.vue'
import RoomName from './RoomName/index.vue'

const chatStore = useChatStore()
const virtualListRef = ref()
Expand Down Expand Up @@ -69,8 +70,10 @@ const getKey = (item: MessageType) => item.message.id

<template>
<div class="chat-msg-list" @contextmenu.prevent>
<RoomName />
<el-icon v-if="messageOptions?.isLoading" :size="14" class="loading">
<IEpLoading />消息加载中
<IEpLoading />
消息加载中
</el-icon>
<VirtualList
v-if="chatMessageList?.length"
Expand Down
1 change: 1 addition & 0 deletions src/views/Home/Chat/components/ChatList/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

.loading {
position: absolute;
top: 50px;
z-index: 20;
gap: 4px;
width: 100%;
Expand Down