This commit is contained in:
pengxiaolong
2025-05-13 19:39:53 +08:00
parent 37da6765b8
commit c006a8e63d
1232 changed files with 96963 additions and 883 deletions

View File

@@ -0,0 +1,3 @@
import ManageGroup from './index.vue';
export default ManageGroup;

View File

@@ -0,0 +1,825 @@
<template>
<div
ref="manageRef"
class="manage"
>
<header
v-if="!isUniFrameWork || currentTab ==='admin'"
class="manage-header"
>
<Icon
:file="backSVG"
@onClick="back"
/>
<div class="manage-header-content">
{{ TUITranslateService.t(`TUIGroup.${TabName}`) }}
</div>
<div />
</header>
<main
v-if="!currentTab || (isUniFrameWork && currentTab != 'admin')"
class="main"
>
<ManageName
class="space-top"
:isAuthor="isOwner || isAdmin || isWorkGroup"
:data="currentGroup"
@update="updateProfile"
/>
<div class="user-info space-top">
<header
class="user-info-header"
@click="setCurrentTab('member')"
>
<label class="user-info-header-left">
{{ TUITranslateService.t(`TUIGroup.群成员`) }}
</label>
<div class="user-info-header-right">
<span class="span">
{{ currentGroup.memberCount || groupMemberList.length }}
{{ TUITranslateService.t(`TUIGroup.人`) }}
</span>
<Icon :file="rightIcon" />
</div>
</header>
<ol class="user-info-list">
<dl
v-for="(item, index) in groupMemberList.slice(0, showUserNum)"
:key="index"
class="user-info-list-item"
>
<dt
class="user-info-list-item-main"
@click="handleMemberProfileShow(item)"
>
<img
class="avatar"
:src="
item.avatar ||
'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'
"
onerror="this.onerror=null;this.src='https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'"
>
</dt>
<dd class="user-info-list-item-info">
{{ item.nick || item.userID }}
</dd>
</dl>
<dl
v-if="isShowAddMember"
class="user-info-list-item"
>
<dt
class="avatar"
@click="toggleMask('add')"
>
+
</dt>
</dl>
<dl
v-if="currentSelfRole === 'Owner'"
class="user-info-list-item"
>
<dt
class="avatar"
@click="toggleMask('remove')"
>
-
</dt>
</dl>
</ol>
</div>
<ul
class="content list space-top"
@click="editLableName = ''"
>
<li
class="list-item"
@click="setCurrentTab('notification')"
>
<aside class="aside">
<label class="label">{{
TUITranslateService.t(`TUIGroup.群公告`)
}}</label>
<article class="article">
{{ currentGroup.notification }}
</article>
</aside>
<Icon
:file="rightIcon"
class="end"
/>
</li>
<li
v-if="(isAdmin || isOwner) && isSetMuteTime"
class="list-item"
@click="setCurrentTab('admin')"
>
<label class="label">{{
TUITranslateService.t(`TUIGroup.群管理`)
}}</label>
<Icon :file="rightIcon" />
</li>
<li class="list-item">
<label class="label">{{
TUITranslateService.t(`TUIGroup.群ID`)
}}</label>
<div class="groupID">
<span class="span">{{ currentGroupID }}</span>
</div>
</li>
<li class="list-item">
<label class="label">{{
TUITranslateService.t(`TUIGroup.群头像`)
}}</label>
<img
class="avatar"
:src="
currentGroup.avatar ||
'https://web.sdk.qcloud.com/im/demo/TUIkit/web/img/constomer.svg'
"
onerror="this.onerror=null;this.src='https://web.sdk.qcloud.com/im/demo/TUIkit/web/img/constomer.svg'"
>
</li>
<li class="list-item">
<label class="label">{{
TUITranslateService.t(`TUIGroup.群类型`)
}}</label>
<span class="span">{{
TUITranslateService.t(`TUIGroup.${typeName[currentGroup.type]}`)
}}</span>
</li>
<li class="list-item">
<label class="label">{{
TUITranslateService.t(`TUIGroup.加群方式`)
}}</label>
<span class="span">{{
TUITranslateService.t(
`TUIGroup.${typeName[currentGroup.joinOption]}`
)
}}</span>
</li>
</ul>
<ul class="footer list space-top">
<li
v-if="currentSelfRole === 'Owner' && groupMemberList.length > 1"
class="list-item"
@click.stop="toggleMask('changeOwner')"
>
{{ TUITranslateService.t(`TUIGroup.转让群组`) }}
</li>
<li
v-if="canIDissmissGroup"
class="list-item"
@click.stop="dismissGroup(currentGroup)"
>
{{ TUITranslateService.t(`TUIGroup.解散群聊`) }}
</li>
<li
v-else
class="list-item"
@click.stop="quitGroup(currentGroup)"
>
{{ TUITranslateService.t(`TUIGroup.退出群组`) }}
</li>
</ul>
</main>
<ManageMember
v-if="currentTab === 'member'"
:self="currentGroup.selfInfo"
:list="groupMemberList"
:total="~~currentGroup.memberCount"
:isShowDel="currentSelfRole === 'Owner' && canDelMember"
@more="getMember('more')"
@del="submit"
@handleMemberProfileShow="handleMemberProfileShow"
@close="setCurrentTab('')"
/>
<ManageProfile
v-if="currentTab === 'profile'"
:userInfo="currentMember"
@close="setCurrentTab('')"
/>
<ManageNotification
v-if="currentTab === 'notification'"
:isAuthor="isOwner || isAdmin || isWorkGroup"
:data="currentGroup"
@update="updateProfile"
@close="setCurrentTab('')"
/>
<ManageAdmin
v-if="currentTab === 'admin'"
v-show="isAdmin"
:isSetMuteTime="isSetMuteTime"
:member="member"
:currentGroup="currentGroup"
@addAdmin="toggleMask('addAdmin')"
@removeAdmin="toggleMask('removeAdmin')"
@setAllMuteTime="setAllMuteTime"
@addMute="toggleMask('addMute')"
@removeMute="toggleMask('removeMute')"
@close="setCurrentTab('')"
/>
<MaskLayer
:show="mask"
@update:show="(e) => (mask = e)"
>
<Transfer
:title="TUITranslateService.t(`TUIGroup.${transferTitle}`)"
:list="transferList"
:isSearch="isSearch"
:isRadio="isRadio"
:selectedList="selectedList"
:isH5="!isPC"
@submit="submit"
@cancel="cancel"
@search="handleSearchMember"
/>
</MaskLayer>
<Dialog
class="deleted-dialog"
:title="TUITranslateService.t(`TUIGroup.删除成员`)"
:show="delDialogShow"
:isH5="!isPC"
:center="true"
:isHeaderShow="isPC"
@submit="handleManage(deletedUserList, 'remove')"
@update:show="(e) => (delDialogShow = e)"
>
<p
v-if="deletedUserList.length === 1"
class="del-dialog-title"
>
{{ TUITranslateService.t(`TUIGroup.确定从群聊中删除该成员?`) }}
</p>
<p
v-if="deletedUserList.length > 1"
class="del-dialog-title"
>
{{ TUITranslateService.t(`TUIGroup.确定从群聊中删除所选成员?`) }}
</p>
</Dialog>
</div>
</template>
<script lang="ts" setup>
import {
ref,
computed,
watchEffect,
onMounted,
nextTick,
} from '../../../adapter-vue';
import TUIChatEngine, {
TUITranslateService,
TUIGroupService,
TUIFriendService,
TUIStore,
StoreName,
IGroupModel,
TUIConversationService,
IConversationModel,
} from '@tencentcloud/chat-uikit-engine';
import { TUIGlobal, outsideClick } from '@tencentcloud/universal-api';
import MaskLayer from '../../common/MaskLayer/index.vue';
import Dialog from '../../common/Dialog/index.vue';
import Transfer from '../../common/Transfer/index.vue';
import ManageName from './manage-name.vue';
import ManageNotification from './manage-notification.vue';
import ManageMember from './manage-member.vue';
import ManageProfile from './manage-profile.vue';
import ManageAdmin from './manage-admin.vue';
import Icon from '../../common/Icon.vue';
import backSVG from '../../../assets/icon/back.svg';
import rightIcon from '../../../assets/icon/right-icon.svg';
import { Toast, TOAST_TYPE } from '../../common/Toast/index';
import { isPC, isUniFrameWork } from '../../../utils/env';
import Server from '../server';
import { enableSampleTaskStatus } from '../../../utils/enableSampleTaskStatus';
import { IFriendProfile, IGroupMember, IUserProfile } from '../../../interface';
const TUIGroupServer = Server.getInstance();
const TUIConstants = TUIGroupServer.constants;
const props = defineProps({
groupID: {
type: String,
default: '',
},
groupCurrentTab: {
type: String,
default: '',
},
});
const manageRef = ref<any>(undefined);
// type 'profile' | 'member' | 'notification' | 'admin' | ''
const currentTab = ref('');
const editLableName = ref('');
const transferType = ref('');
const mask = ref(false);
const currentGroupID = ref('');
const userInfo = ref({
list: [] as IGroupMember[],
});
const currentMember = ref<IGroupMember>({});
const typeName = ref({
[TUIChatEngine.TYPES.GRP_WORK]: '好友工作群',
[TUIChatEngine.TYPES.GRP_PUBLIC]: '陌生人社交群',
[TUIChatEngine.TYPES.GRP_MEETING]: '临时会议群',
[TUIChatEngine.TYPES.GRP_AVCHATROOM]: '直播群',
[TUIChatEngine.TYPES.GRP_COMMUNITY]: '社群',
[TUIChatEngine.TYPES.JOIN_OPTIONS_FREE_ACCESS]: '自由加入',
[TUIChatEngine.TYPES.JOIN_OPTIONS_NEED_PERMISSION]: '需要验证',
[TUIChatEngine.TYPES.JOIN_OPTIONS_DISABLE_APPLY]: '禁止加群',
});
const member = ref({
admin: [] as IGroupMember[],
member: [] as IGroupMember[],
muteMember: [] as IGroupMember[],
});
const transferList = ref<IGroupMember[]>([]);
const transferTitle = ref('');
const isSearch = ref(false);
const isRadio = ref(false);
const selectedList = ref([]);
const delDialogShow = ref(false);
const groupMemberList = ref<IGroupMember[]>([]);
const deletedUserList = ref([]);
const currentGroup = ref<IGroupModel>();
const currentSelfRole = ref('');
const groupIDValue = ref<string>('');
onMounted(() => {
nextTick(() => {
if (manageRef.value && !isUniFrameWork) {
outsideClick.listen({
domRefs: manageRef.value,
handler: handleCompleteManage,
});
}
});
});
TUIStore.watch(StoreName.GRP, {
currentGroup: (group: IGroupModel) => {
if (group) {
currentGroup.value = group;
currentSelfRole.value = currentGroup.value?.selfInfo?.role;
}
},
currentGroupMemberList: (memberList: IGroupMember[]) => {
groupMemberList.value = memberList;
member.value = {
admin: [],
member: [],
muteMember: [],
};
Array.from(memberList).map((item: any) => {
switch (item?.role) {
case TUIChatEngine.TYPES.GRP_MBR_ROLE_ADMIN:
member.value.admin.push(item);
break;
case TUIChatEngine.TYPES.GRP_MBR_ROLE_MEMBER:
member.value.member.push(item);
break;
default:
break;
}
return item;
});
const time: number = new Date().getTime();
member.value.muteMember = Array.from(memberList).filter(
(item: any) => item?.muteUntil * 1000 - time > 0,
);
},
});
TUIStore.watch(StoreName.CONV, {
currentConversation: (conversation: IConversationModel) => {
groupIDValue.value = conversation?.groupProfile?.groupID;
},
});
watchEffect(() => {
const params = TUIGroupServer.getOnCallParams(TUIConstants.TUIGroup.SERVICE.METHOD.OPEN_GROUP_MANAGEMENT);
currentGroupID.value = params?.groupID || groupIDValue.value;
currentTab.value = props.groupCurrentTab;
});
const TabName = computed(() => {
let name = '';
switch (currentTab.value) {
case 'notification':
name = '群公告';
break;
case 'member':
name = '群成员';
break;
case 'profile':
name = '群成员';
break;
default:
name = '群管理';
break;
}
return name;
});
const isOwner = computed(() => {
// Determine whether the person is the group owner/administrator
const userRole = currentGroup.value?.selfInfo?.role;
return userRole === TUIChatEngine.TYPES.GRP_MBR_ROLE_OWNER;
});
const isAdmin = computed(() => {
const userRole = currentGroup.value?.selfInfo?.role;
return userRole === TUIChatEngine.TYPES.GRP_MBR_ROLE_OWNER;
});
const isWorkGroup = computed(() => {
return currentGroup.value?.type === TUIChatEngine.TYPES.GRP_WORK;
});
const isSetMuteTime = computed(() => {
if (isWorkGroup.value || !(isOwner.value || isAdmin.value)) {
return false;
}
return true;
});
const canDelMember = computed(() => {
const groupType = currentGroup?.value?.type;
const isAVChatRoom = groupType === TUIChatEngine.TYPES.GRP_AVCHATROOM;
if (isAVChatRoom) {
return false;
}
return true;
});
const updateProfile = async (newGroupProfile: any) => {
const { key, value } = newGroupProfile;
const options: any = {
groupID: currentGroup.value.groupID,
[key]: value,
};
TUIGroupService.updateGroupProfile(options)
.then((res: any) => {
currentGroup.value = res.data.group;
editLableName.value = '';
})
.catch((error: any) => {
Toast({
message: error?.message,
type: TOAST_TYPE.ERROR,
});
});
};
const setCurrentTab = (tabName: string) => {
currentTab.value = tabName;
editLableName.value = '';
if (currentTab.value === 'member') {
transferType.value = 'remove';
}
if (!currentTab.value) {
transferType.value = '';
}
};
const cancel = () => {
toggleMask();
};
const toggleMask = async (type?: string) => {
selectedList.value = [];
let memberUserIDList: string[] = [];
switch (type) {
case 'add':
isRadio.value = false;
memberUserIDList = [...member.value.admin, ...member.value.member].map((item: IUserProfile) => item.userID);
transferList.value = (await friendList()).filter((item: IFriendProfile) => {
return item.userID && memberUserIDList.indexOf(item.userID) < 0;
});
transferTitle.value = '添加成员';
break;
case 'remove':
isRadio.value = false;
transferList.value = groupMemberList.value.filter(
(item: any) => item.userID !== currentGroup?.value?.selfInfo?.userID,
);
transferTitle.value = '删除成员';
break;
case 'addAdmin':
isRadio.value = true;
transferList.value = member.value.member;
transferTitle.value = '新增管理员';
break;
case 'removeAdmin':
isRadio.value = true;
transferList.value = member.value.admin;
transferTitle.value = '移除管理员';
break;
case 'changeOwner':
isRadio.value = true;
transferList.value = [...member.value.admin, ...member.value.member];
transferTitle.value = '转让群组';
break;
case 'addMute':
isRadio.value = true;
transferList.value = member.value.member;
if (currentGroup.value.selfInfo.role === 'Owner') {
transferList.value = [...member.value.admin, ...member.value.member];
}
transferList.value = transferList?.value?.filter((item: any) => {
return member?.value?.muteMember?.indexOf(item) < 0;
});
transferTitle.value = '新增禁言用户';
break;
case 'removeMute':
isRadio.value = true;
transferList.value = member.value.muteMember;
transferTitle.value = '移除禁言用户';
break;
default:
break;
}
type && (transferType.value = type);
mask.value = !mask.value;
};
const friendList = async () => {
const imResponse = await TUIFriendService.getFriendList();
const friendList = imResponse.data.map((item: any) => item?.profile);
return friendList.filter(
(item: any) =>
!userInfo.value.list.some(
(infoItem: any) => infoItem.userID === item.userID,
),
);
};
const canIDissmissGroup = computed(() => {
const userRole = currentGroup?.value?.selfInfo?.role;
const groupType = currentGroup?.value?.type;
return (
userRole === TUIChatEngine.TYPES.GRP_MBR_ROLE_OWNER
&& groupType !== TUIChatEngine.TYPES.GRP_WORK
);
});
const isShowAddMember = computed(() => {
const groupType = currentGroup?.value?.type;
return groupType === TUIChatEngine.TYPES.GRP_WORK;
});
const showUserNum = computed(() => {
let num = 3;
if (!isShowAddMember.value) {
num += 1;
}
if (currentGroup?.value?.selfInfo?.role !== 'Owner') {
num += 1;
}
return num;
});
const getMember = async (type?: string) => {
const groupID = currentGroupID.value;
const options = {
groupID,
count: 100,
offset: type && type === 'more' ? userInfo.value.list.length : 0,
};
await TUIGroupService.getGroupMemberList(options).then((res: any) => {
if (type && type === 'more') {
userInfo.value.list = [...userInfo.value.list, ...res.data.memberList];
} else {
userInfo.value.list = res.data.memberList;
}
});
};
const handleMemberProfileShow = (user: any) => {
currentMember.value = user;
setCurrentTab('profile');
};
const submit = (userList: any) => {
if (transferType.value === 'remove') {
deletedUserList.value = userList;
delDialogShow.value = !delDialogShow.value;
} else {
handleManage(userList, transferType.value);
}
mask.value = false;
};
const dismissGroup = async (group: any) => {
await TUIGroupService.dismissGroup(group.groupID);
enableSampleTaskStatus('dismissGroup');
Toast({
message: TUITranslateService.t('TUIGroup.群组解散成功'),
type: TOAST_TYPE.SUCCESS,
});
clearGroupInfo();
};
const clearGroupInfo = () => {
if (isUniFrameWork) {
TUIGlobal?.switchTab({
url: '/TUIKit/components/TUIConversation/index',
});
} else {
handleCompleteManage();
TUIConversationService.switchConversation();
}
};
const setAllMuteTime = (value: boolean) => {
// Set up a muting time for all members
updateProfile({ key: 'muteAllMembers', value });
if (value) {
enableSampleTaskStatus('muteGroup');
Toast({
message: TUITranslateService.t('TUIGroup.禁言设置成功'),
type: TOAST_TYPE.SUCCESS,
});
} else {
Toast({
message: TUITranslateService.t('TUIGroup.取消禁言成功'),
type: TOAST_TYPE.SUCCESS,
});
}
};
const handleSearchMember = async (value: string) => {
let imResponse: any = {};
let imMemberResponse: any = {};
const options: any = {
groupID: currentGroupID.value,
userIDList: [value],
};
switch (transferType.value) {
case 'add':
try {
imMemberResponse = await TUIGroupService.getGroupMemberProfile(options);
transferList.value = transferList.value.filter(
(item: any) => item.userID !== imResponse.data[0]?.userID,
);
transferList.value = [...transferList.value, ...imResponse.data];
if (imMemberResponse?.data?.memberList.length > 0) {
transferList.value = transferList.value.map((item: any) => {
if (item.userID === imMemberResponse?.data?.memberList[0].userID) {
item.isDisabled = true;
}
return item;
});
}
} catch (error: any) {
const message = TUITranslateService.t('TUIGroup.该用户不存在');
Toast({
message,
type: TOAST_TYPE.ERROR,
});
}
break;
case 'remove':
try {
imResponse = await TUIGroupService.getGroupMemberProfile(options);
if (imResponse.data.memberList.length === 0) {
const message = TUITranslateService.t('TUIGroup.该用户不在群组内');
Toast({
message,
type: TOAST_TYPE.ERROR,
});
}
transferList.value = transferList.value.filter(
(item: any) => item.userID !== imResponse?.data?.memberList[0]?.userID,
);
if (imResponse?.data?.memberList.length) {
transferList.value = [
...transferList.value,
...imResponse.data.memberList,
];
}
} catch (error: any) {
const message = TUITranslateService.t('TUIGroup.该用户不存在');
Toast({
message,
type: TOAST_TYPE.ERROR,
});
}
break;
default:
break;
}
};
const handleManage = (userList: any, type: any) => {
const userIDList: any = [];
userList.map((item: any) => {
userIDList.push(item.userID);
return item;
});
switch (type) {
case 'add':
addMember(userIDList);
break;
case 'remove':
deleteGroupMember(userIDList);
break;
case 'addAdmin':
handleAdmin(userList[0]);
break;
case 'removeAdmin':
handleAdmin(userList[0]);
break;
case 'changeOwner':
changeOwner(userIDList[0]);
break;
case 'addMute':
setMemberMuteTime(userIDList[0], 'add');
break;
case 'removeMute':
setMemberMuteTime(userIDList[0], 'remove');
break;
default:
break;
}
};
const addMember = async (userIDList: any) => {
const options: any = {
groupID: currentGroupID.value,
userIDList,
};
await TUIGroupService.addGroupMember(options);
};
const changeOwner = async (userID: any) => {
const options: any = {
groupID: currentGroupID.value,
newOwnerID: userID,
};
const imResponse = await TUIGroupService.changeGroupOwner(options);
currentGroup.value = {};
currentGroup.value = imResponse.data.group;
};
const setMemberMuteTime = async (userID: string, type?: string) => {
const options: any = {
groupID: currentGroupID.value,
userID,
muteTime: type === 'add' ? 60 * 60 * 24 * 30 : 0,
};
await TUIGroupService.setGroupMemberMuteTime(options);
};
const handleAdmin = async (user: any) => {
let role = '';
switch (user.role) {
case TUIChatEngine.TYPES.GRP_MBR_ROLE_ADMIN:
role = TUIChatEngine.TYPES.GRP_MBR_ROLE_MEMBER;
break;
case TUIChatEngine.TYPES.GRP_MBR_ROLE_MEMBER:
role = TUIChatEngine.TYPES.GRP_MBR_ROLE_ADMIN;
break;
}
const options: any = {
groupID: currentGroupID.value,
userID: user.userID,
role,
};
await TUIGroupService.setGroupMemberRole(options);
};
const deleteGroupMember = async (userIDList: any) => {
const options: any = {
groupID: currentGroupID.value,
userIDList,
reason: '',
};
await TUIGroupService.deleteGroupMember(options);
};
const quitGroup = async (group: any) => {
await TUIGroupService.quitGroup(group.groupID);
clearGroupInfo();
};
const back = () => {
if (currentTab.value) {
setCurrentTab('');
} else {
handleCompleteManage();
}
};
const handleCompleteManage = () => {
TUIStore.update(StoreName.GRP, 'isShowManageComponent', false);
const callback = TUIGroupServer.getOnCallCallback(TUIConstants.TUIGroup.SERVICE.METHOD.OPEN_GROUP_MANAGEMENT);
callback && callback();
};
</script>
<style lang="scss" scoped src="./style/index.scss"></style>

View File

@@ -0,0 +1,338 @@
<template>
<div class="admin-main">
<div class="admin-manage">
<div class="admin-manage-header">
{{ TUITranslateService.t(`TUIGroup.群管理员`) }}
</div>
<ul class="admin-manage-list">
<li
v-for="(item, index) in memberAdmin.admin"
:key="index"
class="admin-manage-list-item"
>
<div class="item-main">
<img
class="item-main-avatar"
:src="
item.avatar ||
'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'
"
onerror="this.onerror=null;this.src='https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'"
>
</div>
<div class="item-name">
{{ item.nick || item.userID }}
</div>
</li>
<li class="admin-manage-list-item">
<div
class="item-main"
@click="addAdmin"
>
<Icon
:file="plusSVG"
width="16px"
height="16px"
/>
</div>
</li>
<li class="admin-manage-list-item">
<div
v-if="memberAdmin.admin.length > 0"
class="item-main"
@click="removeAdmin"
>
<Icon
:file="minusSVG"
width="16px"
height="16px"
/>
</div>
</li>
</ul>
</div>
<div
v-if="isAdminSetMuteTime"
class="admin-mute-all"
>
<div>
<div class="admin-mute-all-title">
{{ TUITranslateService.t(`TUIGroup.全员禁言`) }}
</div>
<div class="admin-mute-all-content">
{{
TUITranslateService.t(
`TUIGroup.全员禁言开启后,只允许群主和管理员发言。`
)
}}
</div>
</div>
<Slider
:open="currentGroupAdmin.muteAllMembers"
@change="setAllMuteTime"
/>
</div>
<div
v-if="isAdminSetMuteTime"
class="admin-mute"
>
<div class="admin-mute-header">
{{ TUITranslateService.t(`TUIGroup.单独禁言人员`) }}
</div>
<ul class="admin-mute-list">
<li
v-for="(item, index) in memberAdmin.muteMember"
:key="index"
class="admin-mute-list-item"
>
<div class="item-main">
<img
class="item-main-avatar"
:src="
item.avatar ||
'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'
"
onerror="this.onerror=null;this.src='https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'"
>
</div>
<div class="item-name">
{{ item.nick || item.userID }}
</div>
</li>
<li class="admin-mute-list-item">
<div
class="item-main"
@click="addMute"
>
<Icon
:file="plusSVG"
width="16px"
height="16px"
/>
</div>
</li>
<li class="admin-mute-list-item">
<div
v-if="memberAdmin.muteMember.length > 0"
class="item-main"
@click="removeMute"
>
<Icon
:file="minusSVG"
width="16px"
height="16px"
/>
</div>
</li>
</ul>
</div>
</div>
</template>
<script lang="ts" setup>
import {
TUITranslateService,
IGroupModel,
} from '@tencentcloud/chat-uikit-engine';
import { watchEffect, ref } from '../../../adapter-vue';
import Slider from '../../common/Slider/index.vue';
import Icon from '../../common/Icon.vue';
import plusSVG from '../../../assets/icon/plus.svg';
import minusSVG from '../../../assets/icon/minus.svg';
import { IGroupMember } from '../../../interface';
const props = defineProps({
member: {
type: Object,
default: () => {},
},
isSetMuteTime: {
type: Boolean,
default: () => false,
},
currentGroup: {
type: Object,
default: () => {},
},
});
const isAdminSetMuteTime = ref(false);
const memberAdmin = ref({
admin: [] as Array<IGroupMember>,
member: [] as Array<IGroupMember>,
muteMember: [] as Array<IGroupMember>,
});
const currentGroupAdmin = ref<IGroupModel>();
watchEffect(() => {
memberAdmin.value = props.member as {
admin: Array<IGroupMember>;
member: Array<IGroupMember>;
muteMember: Array<IGroupMember>;
};
isAdminSetMuteTime.value = props.isSetMuteTime;
currentGroupAdmin.value = props.currentGroup;
});
const emits = defineEmits([
'addAdmin',
'removeAdmin',
'setAllMuteTime',
'addMute',
'removeMute',
'close',
]);
const addAdmin = () => {
emits('addAdmin');
};
const removeAdmin = () => {
emits('removeAdmin');
};
const setAllMuteTime = (value: boolean) => {
emits('setAllMuteTime', value);
};
const addMute = () => {
emits('addMute');
};
const removeMute = () => {
emits('removeMute');
};
</script>
<style lang="scss" scoped>
@import "../../../assets/styles/common";
.admin {
width: 100%;
overflow: hidden;
&-header {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 10px;
&-left {
font-family: "PingFang SC", sans-serif;
font-size: 18px;
font-weight: 500;
line-height: 50px;
letter-spacing: 0;
text-align: left;
}
&-close {
font-family: "PingFang SC", sans-serif;
font-size: 16px;
font-weight: 400;
line-height: 48px;
letter-spacing: 0;
text-align: left;
color: #3370ff;
}
}
&-main {
width: 100%;
overflow: hidden;
.admin-manage {
border-bottom: 10px solid #f4f5f9;
}
.admin-manage,
.admin-mute {
padding: 10px;
width: calc(100% - 20px);
overflow: hidden;
&-header {
padding-left: 10px;
font-family: "PingFang SC", sans-serif;
font-size: 14px;
font-weight: 400;
line-height: 20px;
letter-spacing: 0;
text-align: left;
}
&-list {
display: flex;
width: 100%;
overflow: hidden;
flex-wrap: wrap;
&-item {
flex: 0 0 36px;
display: flex;
flex-direction: column;
padding: 10px;
.item-main {
width: 36px;
height: 36px;
border-radius: 4px;
font-size: 12px;
display: flex;
justify-content: center;
align-items: center;
background: #f4f5f9;
color: #000;
&-avatar {
width: 36px;
height: 36px;
overflow: hidden;
border-radius: 4px;
}
}
.item-name {
text-align: center;
max-width: 36px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
.admin-mute-all {
margin: 0 10px;
padding: 20px 0;
border-bottom: 1px solid #e8e8e9;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
&-title {
padding-left: 10px;
font-family: "PingFang SC", sans-serif;
font-size: 14px;
font-weight: 400;
line-height: 20px;
letter-spacing: 0;
text-align: left;
}
&-content {
color: #999;
padding-left: 10px;
font-family: "PingFang SC", sans-serif;
font-size: 12px;
font-weight: 400;
line-height: 17px;
letter-spacing: 0;
text-align: left;
}
}
}
}
</style>

View File

@@ -0,0 +1,300 @@
<template>
<main
v-if="!isUniFrameWork"
class="member"
>
<ul class="list">
<li
v-for="(item, index) in memberList"
:key="index"
class="list-item"
>
<aside
class="aside"
@click="handleMemberProfileShow(item)"
>
<img
class="avatar"
:src="
item.avatar ||
'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'
"
onerror="this.onerror=null;this.src='https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'"
>
<span class="name">{{ item.nick || item.userID }}</span>
<span>{{ handleRoleName(item) }}</span>
</aside>
<div @click="submit(item)">
<Icon
v-if="item.role !== 'Owner' && isShowDeleteBtn"
:file="delIcon"
:width="'16px'"
:height="'16px'"
/>
</div>
</li>
<li
v-if="memberList.length < totalMember"
class="list-item"
@click="getMore"
>
{{ TUITranslateService.t(`TUIGroup.查看更多`) }}
</li>
</ul>
</main>
<div
v-else
class="edit-h5"
>
<main class="main">
<header class="edit-h5-header">
<aside class="left">
<h1>{{ TUITranslateService.t(`TUIGroup.群成员`) }}</h1>
</aside>
<span
class="close"
@click="close('member')"
>{{
TUITranslateService.t(`关闭`)
}}</span>
</header>
<div class="member">
<ul class="list list-uniapp">
<li
v-for="(item, index) in memberList"
:key="index"
class="list-item"
>
<aside
class="aside"
@click="handleMemberProfileShow(item)"
>
<img
class="avatar"
:src="
item.avatar ||
'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'
"
onerror="this.onerror=null;this.src='https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'"
>
<span class="name">{{ item.nick || item.userID }}</span>
<span>{{ handleRoleName(item) }}</span>
</aside>
<div @click="submit(item)">
<Icon
v-if="item.role !== 'Owner' && isShowDeleteBtn"
:file="delIcon"
:width="'16px'"
:height="'16px'"
/>
</div>
</li>
<li
v-if="memberList.length < totalMember"
class="list-item"
@click="getMore"
>
{{ TUITranslateService.t(`TUIGroup.查看更多`) }}
</li>
</ul>
</div>
</main>
</div>
</template>
<script lang="ts" setup>
import TUIChatEngine, {
TUITranslateService,
} from '@tencentcloud/chat-uikit-engine';
import { watchEffect, ref } from '../../../adapter-vue';
import Icon from '../../common/Icon.vue';
import delIcon from '../../../assets/icon/del-icon.svg';
import { IGroupSelfInfo, IGroupMember } from '../../../interface';
import { isUniFrameWork } from '../../../utils/env';
const props = defineProps({
list: {
type: Array,
default: () => [],
},
total: {
type: Number,
default: () => 0,
},
isShowDel: {
type: Boolean,
default: () => false,
},
self: {
type: Object,
default: () => ({}),
},
});
const totalMember = ref(0);
const memberList = ref<Array<IGroupMember>>([]);
const isShowDeleteBtn = ref(false);
const selfValue = ref<IGroupSelfInfo>({});
watchEffect(() => {
totalMember.value = props.total;
isShowDeleteBtn.value = props.isShowDel;
memberList.value = props.list as Array<IGroupMember>;
selfValue.value = props.self;
});
const emits = defineEmits(['more', 'del', 'handleMemberProfileShow', 'close']);
const handleRoleName = (item: any) => {
let name = '';
switch (item?.role) {
case TUIChatEngine.TYPES.GRP_MBR_ROLE_ADMIN:
name = TUITranslateService.t('TUIGroup.管理员');
break;
case TUIChatEngine.TYPES.GRP_MBR_ROLE_OWNER:
name = TUITranslateService.t('TUIGroup.群主');
break;
}
if (name) {
name = `(${name})`;
}
if (item.userID === selfValue.value.userID) {
name += ` (${TUITranslateService.t('TUIGroup.我')})`;
}
return name;
};
const getMore = () => {
emits('more');
};
const submit = (item: any) => {
emits('del', [item]);
};
const handleMemberProfileShow = (user: any) => {
emits('handleMemberProfileShow', user);
};
const close = (tabName: string) => {
emits('close', tabName);
};
</script>
<style lang="scss" scoped>
@import "../../../assets/styles/common";
.member {
flex: 1;
background: #fff;
.list {
display: flex;
flex-direction: column;
background: #f4f5f9;
padding-top: 22px;
&-uniapp {
background: none;
}
&-item {
padding: 13px;
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
font-size: 14px;
overflow: hidden;
cursor: pointer;
&:hover {
background: #f1f2f6;
}
.aside {
display: flex;
align-items: center;
width: 100%;
overflow: hidden;
.name {
margin-left: 8px;
font-weight: 400;
font-size: 14px;
color: #000;
flex: 1;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
}
}
.avatar {
width: 36px;
height: 36px;
border-radius: 4px;
}
.edit-h5 {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: flex-end;
z-index: 1;
.main {
background: #fff;
flex: 1;
padding: 18px;
border-radius: 12px 12px 0 0;
overflow: scroll;
height: 50%;
width: 80vw;
}
&-header {
display: flex;
align-items: center;
justify-content: space-between;
.close {
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 18px;
color: #3370ff;
letter-spacing: 0;
line-height: 27px;
}
}
&-footer {
display: flex;
.btn {
flex: 1;
border: none;
background: #147aff;
border-radius: 5px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 16px;
color: #fff;
letter-spacing: 0;
line-height: 27px;
padding: 8px 0;
&:disabled {
opacity: 0.3;
}
}
}
}
</style>

View File

@@ -0,0 +1,264 @@
<template>
<div class="group-name">
<label>{{ TUITranslateService.t(`TUIGroup.群名称`) }}</label>
<div
v-if="isEdit"
:class="{
'edit-h5': isMobile,
}"
>
<main class="edit-h5-main">
<header
v-if="!isPC"
class="edit-h5-header"
>
<aside class="left">
<h1>{{ TUITranslateService.t(`TUIGroup.修改群聊名称`) }}</h1>
<span>{{
TUITranslateService.t(
`TUIGroup.修改群聊名称后,将在群内通知其他成员`
)
}}</span>
</aside>
<span
class="close"
@click="toggleEditStatus"
>{{
TUITranslateService.t(`关闭`)
}}</span>
</header>
<div class="input-box">
<input
v-if="isEdit"
ref="nameInputRef"
v-model="inputGroupName"
class="input"
type="text"
@blur="updateProfile"
>
<span
v-if="!isPC"
class="tip"
>{{
TUITranslateService.t(
`TUIGroup.仅限中文、字母、数字和下划线2-20个字`
)
}}</span>
</div>
<footer
v-if="!isPC"
class="edit-h5-footer"
>
<button
class="btn"
@click="updateProfile"
>
{{ TUITranslateService.t(`确认`) }}
</button>
</footer>
</main>
</div>
<p
v-if="!isEdit || !isPC"
class="name"
@click="toggleEditStatus"
>
<span>{{ groupProfile.name }}</span>
<Icon
v-if="isAuthor"
class="icon"
:file="editIcon"
width="14px"
height="14px"
/>
</p>
</div>
</template>
<script lang="ts" setup>
import { watchEffect, ref, nextTick, watch } from '../../../adapter-vue';
import {
TUITranslateService,
IGroupModel,
} from '@tencentcloud/chat-uikit-engine';
import Icon from '../../common/Icon.vue';
import editIcon from '../../../assets/icon/edit.svg';
import { Toast, TOAST_TYPE } from '../../common/Toast/index';
import { isMobile, isPC } from '../../../utils/env';
const props = defineProps({
data: {
type: Object,
default: () => ({}),
},
isAuthor: {
type: Boolean,
default: false,
},
});
const groupProfile = ref<IGroupModel>({});
const inputGroupName = ref('');
const isEdit = ref(false);
const nameInputRef = ref<HTMLInputElement>(null);
watchEffect(() => {
groupProfile.value = props.data;
});
const emit = defineEmits(['update']);
const updateProfile = () => {
if (!inputGroupName.value) {
Toast({
message: TUITranslateService.t('TUIGroup.群名称不能为空'),
type: TOAST_TYPE.ERROR,
});
} else {
if (inputGroupName.value !== groupProfile.value.name) {
emit('update', { key: 'name', value: inputGroupName.value });
groupProfile.value.name = inputGroupName.value;
inputGroupName.value = '';
Toast({
message: TUITranslateService.t('TUIGroup.群名称修改成功'),
type: TOAST_TYPE.SUCCESS,
});
}
toggleEditStatus();
}
};
const toggleEditStatus = () => {
if (props.isAuthor) {
isEdit.value = !isEdit.value;
}
if (isEdit.value) {
inputGroupName.value = groupProfile.value.name;
}
};
watch(
() => isEdit.value,
(newVal: boolean) => {
if (newVal) {
nextTick(() => {
nameInputRef.value?.focus();
});
}
},
);
</script>
<style lang="scss" scoped>
@import "../../../assets/styles/common";
.group-name {
padding: 14px 20px;
font-weight: 400;
font-size: 14px;
color: #000;
display: flex;
flex-direction: column;
.name {
color: #999;
display: flex;
align-items: center;
.icon {
margin-left: 4px;
}
}
}
.input-box {
display: flex;
.input {
flex: 1;
border: 1px solid #e8e8e9;
border-radius: 4px;
padding: 4px 16px;
font-weight: 400;
font-size: 14px;
color: #000;
opacity: 0.6;
}
}
.space-top {
border-top: 10px solid #f4f5f9;
}
.edit-h5 {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: flex-end;
z-index: 1;
.edit-h5-main {
background: #fff;
flex: 1;
padding: 18px;
border-radius: 12px 12px 0 0;
width: 80vw;
.input-box {
flex-direction: column;
padding: 18px 0;
.input {
background: #f8f8f8;
padding: 10px 12px;
}
.tip {
font-size: 12px;
color: #888;
padding-top: 8px;
}
}
}
&-header {
display: flex;
align-items: center;
justify-content: space-between;
.close {
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 18px;
color: #3370ff;
letter-spacing: 0;
line-height: 27px;
}
}
&-footer {
display: flex;
.btn {
flex: 1;
border: none;
background: #147aff;
border-radius: 5px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 16px;
color: #fff;
letter-spacing: 0;
line-height: 27px;
padding: 8px 0;
&:disabled {
opacity: 0.3;
}
}
}
}
</style>

View File

@@ -0,0 +1,268 @@
<template>
<main
v-if="!isUniFrameWork"
class="notification"
>
<textarea
v-if="isEdit"
v-model="input"
class="textarea"
@keyup.enter="updateProfile"
/>
<section v-else>
<p v-if="!groupProfile.notification">
{{ TUITranslateService.t(`TUIGroup.暂无公告`) }}
</p>
<article v-else>
{{ groupProfile.notification }}
</article>
</section>
<footer v-if="isAuthorNotification">
<button
v-if="isEdit"
class="btn"
@click="updateProfile"
>
{{ TUITranslateService.t(`TUIGroup.发布`) }}
</button>
<button
v-else
class="btn"
@click="isEdit = !isEdit"
>
{{ TUITranslateService.t(`TUIGroup.编辑`) }}
</button>
</footer>
</main>
<div
v-else
class="edit-h5"
>
<main class="edit-h5-main">
<header class="edit-h5-header">
<aside class="left">
<h1>{{ TUITranslateService.t(`TUIGroup.群公告`) }}</h1>
</aside>
<span
class="close"
@click="close('notification')"
>{{
TUITranslateService.t(`关闭`)
}}</span>
</header>
<div class="notification">
<textarea
v-if="isEdit"
v-model="input"
:class="[isUniFrameWork ? 'uni-height' : '', 'textarea']"
@keyup.enter="updateProfile"
/>
<section
v-else
class="row"
>
<p
v-if="!groupProfile.notification"
class="row-p"
>
{{ TUITranslateService.t(`TUIGroup.暂无公告`) }}
</p>
<article v-else>
{{ groupProfile.notification }}
</article>
</section>
<footer
v-if="isAuthorNotification"
class="footer"
>
<button
v-if="isEdit"
class="btn"
@click="updateProfile"
>
{{ TUITranslateService.t(`TUIGroup.发布`) }}
</button>
<button
v-else
class="btn"
@click="isEdit = !isEdit"
>
{{ TUITranslateService.t(`TUIGroup.编辑`) }}
</button>
</footer>
</div>
</main>
</div>
</template>
<script lang="ts" setup>
import { nextTick } from '../../../adapter-vue';
import {
TUITranslateService,
IGroupModel,
} from '@tencentcloud/chat-uikit-engine';
import { watchEffect, ref } from '../../../adapter-vue';
import { Toast, TOAST_TYPE } from '../../common/Toast/index';
import { isUniFrameWork } from '../../../utils/env';
const props = defineProps({
data: {
type: Object,
default: () => ({}),
},
isAuthor: {
type: Boolean,
default: false,
},
});
const groupProfile = ref<IGroupModel>({});
const input = ref('');
const isAuthorNotification = ref(false);
const isEdit = ref(false);
watchEffect(() => {
groupProfile.value = props.data;
input.value = groupProfile.value.notification;
isAuthorNotification.value = props.isAuthor;
});
const emits = defineEmits(['update', 'close']);
const updateProfile = () => {
if (input.value.length > 150) {
Toast({
message: TUITranslateService.t('TUIGroup.群公告字数超出限制最大长度为150'),
type: TOAST_TYPE.ERROR,
});
return;
}
if (input.value && input.value !== groupProfile.value.notification) {
emits('update', { key: 'notification', value: input.value });
nextTick(() => {
input.value = '';
});
}
isEdit.value = !isEdit.value;
};
const close = (tabName: string) => {
emits('close', tabName);
};
</script>
<style lang="scss" scoped>
@import "../../../assets/styles/common";
.notification {
flex: 1;
padding: 20px;
display: flex;
flex-direction: column;
word-break: break-all;
.row {
flex: 1;
font-size: 14px;
.row-p {
text-align: center;
padding-bottom: 20px;
}
}
.textarea {
margin-bottom: 20px;
box-sizing: border-box;
padding: 10px;
border: 1px solid #e8e8e9;
resize: none;
font-size: 14px;
height: 100%;
}
.uni-height {
height: 20vh;
}
.footer {
display: flex;
justify-content: flex-end;
padding: 20px 10px;
}
}
.btn {
background: #3370ff;
border: 0 solid #2f80ed;
padding: 4px 28px;
font-weight: 400;
font-size: 12px;
color: #fff;
line-height: 24px;
border-radius: 4px;
&-cancel {
background: #fff;
border: 1px solid #ddd;
color: #828282;
}
}
.edit-h5 {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: flex-end;
z-index: 1;
.edit-h5-main {
background: #fff;
flex: 1;
padding: 18px;
border-radius: 12px 12px 0 0;
width: 80vw;
}
&-header {
display: flex;
align-items: center;
justify-content: space-between;
.close {
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 18px;
color: #3370ff;
letter-spacing: 0;
line-height: 27px;
}
}
&-footer {
display: flex;
.btn {
flex: 1;
border: none;
background: #147aff;
border-radius: 5px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 16px;
color: #fff;
letter-spacing: 0;
line-height: 27px;
padding: 8px 0;
&:disabled {
opacity: 0.3;
}
}
}
}
</style>

View File

@@ -0,0 +1,275 @@
<template>
<div
v-if="!isUniFrameWork"
class="memeber-profile"
>
<div class="memeber-profile-main">
<img
class="avatar"
:src="
userInfoManager.avatar ||
'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'
"
onerror="this.onerror=null;this.src='https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'"
>
<ul class="list">
<h2>{{ userInfoManager.nick || userInfoManager.userID }}</h2>
<li>
<label>ID</label>
<span>{{ userInfoManager.userID }}</span>
</li>
<li>
<label>{{ TUITranslateService.t("TUIContact.个性签名") }}</label>
<span>{{ userInfoManager.selfSignature }}</span>
</li>
</ul>
</div>
<div class="memeber-profile-footer">
<div
v-if="showEnter()"
class="button"
@click="enter(userInfoManager.userID, 'C2C')"
>
{{ TUITranslateService.t("TUIContact.发送消息") }}
</div>
</div>
</div>
<div
v-else
class="edit-h5"
>
<main class="main">
<header class="edit-h5-header">
<aside class="left">
<h1>{{ TUITranslateService.t(`TUIGroup.群成员`) }}</h1>
</aside>
<span
class="close"
@click="close('profile')"
>{{
TUITranslateService.t(`关闭`)
}}</span>
</header>
<div class="edit-h5-profile">
<div class="memeber-profile-main">
<Avatar
class="avatar"
:url="userInfoManager.avatar"
size="60px"
/>
<ul class="list">
<h1>{{ userInfoManager.nick || userInfoManager.userID }}</h1>
<li>
<label>ID</label>
<span>{{ userInfoManager.userID }}</span>
</li>
<li>
<label>{{ TUITranslateService.t("TUIContact.个性签名") }}</label>
<span>{{ userInfoManager.selfSignature }}</span>
</li>
</ul>
</div>
<div class="memeber-profile-footer">
<div
v-if="showEnter()"
class="button"
@click="enter(userInfoManager.userID, 'C2C')"
>
{{ TUITranslateService.t("TUIContact.发送消息") }}
</div>
</div>
</div>
</main>
</div>
</template>
<script lang="ts" setup>
import { ref, watch, watchEffect } from '../../../adapter-vue';
import TUIChatEngine, {
TUITranslateService,
TUIUserService,
TUIConversationService,
TUIFriendService,
TUIStore,
StoreName,
} from '@tencentcloud/chat-uikit-engine';
import { TUIGlobal } from '@tencentcloud/universal-api';
import Avatar from '../../common/Avatar/index.vue';
import { IUserProfile } from '../../../interface';
import { isUniFrameWork } from '../../../utils/env';
const props = defineProps({
userInfo: {
type: Object,
default: () => ({}),
},
});
const isFriendShip = ref(false);
const userInfoManager = ref<IUserProfile>({});
watchEffect(() => {
userInfoManager.value = props.userInfo;
});
const emits = defineEmits([
'handleSwitchConversation',
'close',
'openConversation',
]);
watch(
() => props.userInfo,
async (newVal: any, oldVal: any) => {
if (newVal === oldVal) return;
const res = await TUIUserService.getUserProfile({
userIDList: [props.userInfo.userID],
});
userInfoManager.value = res?.data[0];
checkFriend();
},
{
deep: true,
immediate: true,
},
);
const enter = async (ID: any, type: string) => {
const name = `${type}${ID}`;
TUIConversationService.getConversationProfile(name)
.then((res: any) => {
TUIConversationService.switchConversation(res.data.conversation.conversationID).then(() => {
TUIStore.update(StoreName.GRP, 'isShowManageComponent', false);
if (isUniFrameWork) {
TUIGlobal?.navigateBack();
}
});
})
.catch((err: any) => {
console.warn('获取会话资料失败', err.code, err.msg);
});
};
const checkFriend = async () => {
if (!(userInfoManager.value as any).userID) return;
TUIFriendService.checkFriend({
userIDList: [userInfoManager.value.userID],
type: TUIChatEngine.TYPES.SNS_CHECK_TYPE_BOTH,
}).then((res: any) => {
const relation = res?.data?.successUserIDList?.[0]?.relation;
isFriendShip.value = (relation === TUIChatEngine.TYPES.SNS_TYPE_BOTH_WAY);
});
};
const showEnter = () => {
return isFriendShip.value || !TUIStore.getData(StoreName.APP, 'isOfficial');
};
const close = (tabName: string) => {
emits('close', tabName);
};
</script>
<style lang="scss" scoped>
@import "../../../assets/styles/common";
.memeber-profile {
flex: 1;
display: flex;
flex-direction: column;
&-main {
display: flex;
flex-direction: row;
width: 100%;
overflow: hidden;
.avatar {
width: 60px;
height: 60px;
border-radius: 8px;
margin: 20px 10px 20px 20px;
}
.list {
flex: 1;
overflow: hidden;
margin: 20px 10px;
font-weight: 400;
li {
color: #999;
}
h1,
li {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
&-footer {
border-top: 1px solid #f4f5f9;
padding: 14px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.button {
width: 100px;
cursor: pointer;
background-color: #006eff;
color: #fff;
padding: 8px 20px;
border-radius: 4px;
border: none;
font-size: 14px;
text-align: center;
line-height: 20px;
}
}
}
.edit-h5 {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: flex-end;
z-index: 1;
.main {
background: #fff;
flex: 1;
padding: 18px;
border-radius: 12px 12px 0 0;
width: 80vw;
.edit-h5-header {
display: flex;
align-items: center;
justify-content: space-between;
.close {
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 18px;
color: #3370ff;
letter-spacing: 0;
line-height: 27px;
}
}
.edit-h5-profile {
.memeber-profile-main {
.avatar {
margin: 20px;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,64 @@
.manage {
background: #FFF;
box-shadow: 0 1px 10px 0 rgba(2, 16, 43, 0.15);
&-header {
border-bottom: 1px solid #E8E8E9;
.manage-header-content {
font-family: PingFangSC-Medium;
font-weight: 500;
color: #000;
}
}
.main {
.footer {
.list-item {
font-weight: 400;
color: #dc2113;
border-bottom: 1px solid #E8E8E9;
}
}
}
}
.input {
border: 1px solid #E8E8E9;
font-weight: 400;
color: #000;
opacity: 0.6;
}
.avatar {
background: #F4F5F9;
color: #000;
}
.space-top {
border-top: 10px solid #F4F5F9;
}
.btn {
background: #3370FF;
border: 0 solid #2F80ED;
color: #FFF;
&-cancel {
background: #FFF;
border: 1px solid #DDD;
color: #828282;
}
}
.slider {
&-box {
background: #E1E1E3;
}
&-block {
background: #FFF;
border: 0 solid rgba(0, 0, 0, 0.85);
box-shadow: 0 2px 4px 0 #D1D1D1;
}
}

View File

@@ -0,0 +1,3 @@
.deleted-dialog {
padding: 20%;
}

View File

@@ -0,0 +1,39 @@
@import '../../../../assets/styles/common';
@import './color';
@import './web';
@import './h5';
.icon-close {
display: inline-block;
width: 24px;
height: 24px;
position: relative;
border-radius: 50%;
}
.icon-close::before,
.icon-close::after {
content: "";
position: absolute;
background-color: #8F959E;
height: 16px;
width: 2px;
top: 50%;
left: 50%;
margin-top: -8px;
margin-left: -1px;
}
.icon-close::before {
transform: rotate(45deg);
}
.icon-close::after {
transform: rotate(-45deg);
}
.tab-icon {
position: absolute;
left: 20px;
}

View File

@@ -0,0 +1,243 @@
.btn {
padding: 4px 28px;
font-size: 12px;
line-height: 24px;
border-radius: 4px;
}
.list {
padding: 0 20px;
display: flex;
flex-direction: column;
&-item {
padding: 14px 0;
display: flex;
align-items: center;
font-size: 14px;
}
&-between {
justify-content: space-between;
}
}
.manage {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
overflow: auto;
&-header {
padding: 10px;
display: flex;
justify-content: space-between;
align-items: center;
&-content {
margin-left: -20px;
text-align: center;
font-size: 16px;
line-height: 30px;
font-weight: 500;
}
&-left {
display: flex;
}
}
.main {
.user-info {
padding: 0 20px;
display: flex;
flex-direction: column;
font-size: 14px;
&-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 14px 0;
&-right {
display: flex;
align-items: center;
}
}
&-list {
flex: 1;
display: flex;
flex-wrap: wrap;
padding-bottom: 20px;
&-item {
position: relative;
flex: 0 0 36px;
display: flex;
flex-direction: column;
padding-right: 20px;
&:last-child {
padding-right: 0;
}
.more {
padding-top: 10px;
}
&-info {
text-align: center;
max-width: 36px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
.content {
padding: 0 20px;
.list-item {
justify-content: space-between;
.btn {
flex: 1;
}
.article {
opacity: 0.6;
width: 246px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.end {
align-self: flex-end;
margin-bottom: 4px;
}
}
}
.footer {
padding: 0 20px;
.list-item {
cursor: pointer;
width: 100%;
font-size: 14px;
padding: 14px 0;
justify-content: center;
&:last-child {
border: none;
}
}
}
}
.admin {
padding: 20px 0;
&-content {
padding: 20px 20px 12px;
display: flex;
align-items: center;
.aside {
flex: 1;
font-size: 14px;
.p {
font-size: 12px;
}
}
}
&-list {
padding: 0 20px;
.label {
display: inline-block;
font-size: 14px;
padding-bottom: 8px;
}
}
.last {
padding-top: 13px;
position: relative;
&::before {
position: absolute;
content: "";
width: calc(100% - 40px);
height: 1px;
top: 0;
left: 0;
right: 0;
margin: 0 auto;
}
}
}
}
.input {
border-radius: 4px;
padding: 4px 16px;
font-size: 14px;
}
.group-id {
display: flex;
flex-direction: row;
align-items: center;
.icon {
width: 15px;
height: 15px;
cursor: pointer;
}
}
.avatar {
width: 36px;
height: 36px;
border-radius: 4px;
font-size: 12px;
display: flex;
justify-content: center;
align-items: center;
}
.slider {
&-box {
display: flex;
align-items: center;
width: 34px;
height: 20px;
border-radius: 10px;
}
&-block {
display: inline-block;
width: 16px;
height: 16px;
border-radius: 8px;
margin: 0 2px;
}
}
.space-between {
justify-content: space-between;
}
.del-dialog-title {
text-align: center;
padding: 20px 0;
}