<template> <div v-if="typeof contactInfoData === 'object' && Object.keys(contactInfoData).length" :class="['tui-contact-info', !isPC && 'tui-contact-info-h5']" > <div v-if="!isPC" :class="[ 'tui-contact-info-header', !isPC && 'tui-contact-info-h5-header', ]" > <div :class="[ 'tui-contact-info-header-icon', !isPC && 'tui-contact-info-h5-header-icon', ]" @click="resetContactSearchingUIData" > <Icon :file="backSVG" /> </div> <div :class="[ 'tui-contact-info-header-title', !isPC && 'tui-contact-info-h5-header-title', ]" > {{ TUITranslateService.t("TUIContact.添加好友/群聊") }} </div> </div> <div :class="['tui-contact-info-basic', !isPC && 'tui-contact-info-h5-basic']"> <div :class="[ 'tui-contact-info-basic-text', !isPC && 'tui-contact-info-h5-basic-text', ]" > <div :class="[ 'tui-contact-info-basic-text-name', !isPC && 'tui-contact-info-h5-basic-text-name', ]" > {{ generateContactInfoName(contactInfoData) }} </div> <div v-for="item in contactInfoBasicList" :key="item.label" :class="[ 'tui-contact-info-basic-text-other', !isPC && 'tui-contact-info-h5-basic-text-other', ]" > {{ `${TUITranslateService.t(`TUIContact.${item.label}`)}: ${item.data}` }} </div> </div> <img :class="[ 'tui-contact-info-basic-avatar', !isPC && 'tui-contact-info-h5-basic-avatar', ]" :src="generateAvatar(contactInfoData)" > </div> <div v-if="contactInfoMoreList[0]" :class="['tui-contact-info-more', !isPC && 'tui-contact-info-h5-more']" > <div v-for="item in contactInfoMoreList" :key="item.key" :class="[ 'tui-contact-info-more-item', !isPC && 'tui-contact-info-h5-more-item', item.labelPosition === CONTACT_INFO_LABEL_POSITION.TOP ? 'tui-contact-info-more-item-top' : 'tui-contact-info-more-item-left', ]" > <div :class="[ 'tui-contact-info-more-item-label', !isPC && 'tui-contact-info-h5-more-item-label', ]" > {{ `${TUITranslateService.t(`TUIContact.${item.label}`)}` }} </div> <div :class="[ 'tui-contact-info-more-item-content', !isPC && 'tui-contact-info-h5-more-item-content', ]" > <div v-if="!item.editing" :class="[ 'tui-contact-info-more-item-content-text', !isPC && 'tui-contact-info-h5-more-item-content-text', ]" > <div :class="[ 'tui-contact-info-more-item-content-text-data', !isPC && 'tui-contact-info-h5-more-item-content-text-data', ]" > {{ item.data }} </div> <div v-if="item.editable" :class="[ 'tui-contact-info-more-item-content-text-icon', !isPC && 'tui-contact-info-h5-more-item-content-text-icon', ]" @click="setEditing(item)" > <Icon :file="editSVG" width="14px" height="14px" /> </div> </div> <input v-else-if="item.editType === CONTACT_INFO_MORE_EDIT_TYPE.INPUT" v-model="item.data" :class="[ 'tui-contact-info-more-item-content-input', !isPC && 'tui-contact-info-h5-more-item-content-input', ]" type="text" @confirm="onContactInfoEmitSubmit(item)" @keyup.enter="onContactInfoEmitSubmit(item)" > <textarea v-else-if="item.editType === CONTACT_INFO_MORE_EDIT_TYPE.TEXTAREA" v-model="item.data" :class="[ 'tui-contact-info-more-item-content-textarea', !isPC && 'tui-contact-info-h5-more-item-content-textarea', ]" confirm-type="done" /> <div v-else-if="item.editType === CONTACT_INFO_MORE_EDIT_TYPE.SWITCH" @click="onContactInfoEmitSubmit(item)" > <SwitchBar :value="item.data" /> </div> </div> </div> </div> <div :class="[ 'tui-contact-info-button', !isPC && 'tui-contact-info-h5-button', ]" > <button v-for="item in contactInfoButtonList" :key="item.key" :class="[ 'tui-contact-info-button-item', !isPC && 'tui-contact-info-h5-button-item', item.type === CONTACT_INFO_BUTTON_TYPE.CANCEL ? `tui-contact-info-button-item-cancel` : `tui-contact-info-button-item-submit`, ]" @click="onContactInfoButtonClicked(item)" > {{ TUITranslateService.t(`TUIContact.${item.label}`) }} </button> </div> </div> </template> <script setup lang="ts"> import TUIChatEngine, { TUIStore, StoreName, TUITranslateService, IGroupModel, Friend, FriendApplication, } from '@tencentcloud/chat-uikit-engine'; import { TUIGlobal } from '@tencentcloud/universal-api'; import { ref, computed, onMounted, onUnmounted } from '../../../adapter-vue'; import { isPC } from '../../../utils/env'; import { generateAvatar, generateContactInfoName, generateContactInfoBasic, isFriend, isApplicationType, } from '../utils/index'; import { contactMoreInfoConfig, contactButtonConfig, } from './contact-info-config'; import Icon from '../../common/Icon.vue'; import editSVG from '../../../assets/icon/edit.svg'; import backSVG from '../../../assets/icon/back.svg'; import SwitchBar from '../../common/SwitchBar/index.vue'; import { IBlackListUserItem, IContactInfoMoreItem, IContactInfoButton, } from '../../../interface'; import { CONTACT_INFO_LABEL_POSITION, CONTACT_INFO_MORE_EDIT_TYPE, CONTACT_INFO_BUTTON_TYPE, } from '../../../constant'; import { deepCopy } from '../../TUIChat/utils/utils'; type IContactInfoType = IGroupModel | Friend | FriendApplication | IBlackListUserItem; const emits = defineEmits(['switchConversation']); const contactInfoData = ref<IContactInfoType>({} as IContactInfoType); const contactInfoBasicList = ref<Array<{ label: string; data: string }>>([]); const contactInfoMoreList = ref<IContactInfoMoreItem[]>([]); const contactInfoButtonList = ref<IContactInfoButton[]>([]); const setEditing = (item: any) => { item.editing = true; }; const isGroup = computed((): boolean => (contactInfoData.value as IGroupModel)?.groupID ? true : false, ); const isApplication = computed((): boolean => { return isApplicationType(contactInfoData?.value); }); // is both friend, if is group type always false const isBothFriend = ref<boolean>(false); // is group member, including ordinary member, admin, group owner const isGroupMember = computed((): boolean => { return (contactInfoData.value as IGroupModel)?.selfInfo?.userID ? true : false; }); // is in black list, if is group type always false const isInBlackList = computed((): boolean => { return ( !isGroup.value && blackList.value?.findIndex( (item: IBlackListUserItem) => item?.userID === (contactInfoData.value as IBlackListUserItem)?.userID, ) >= 0 ); }); const blackList = ref<IBlackListUserItem[]>([]); onMounted(() => { TUIStore.watch(StoreName.CUSTOM, { currentContactInfo: onCurrentContactInfoUpdated, }); TUIStore.watch(StoreName.USER, { userBlacklist: onUserBlacklistUpdated, }); }); onUnmounted(() => { TUIStore.unwatch(StoreName.CUSTOM, { currentContactInfo: onCurrentContactInfoUpdated, }); TUIStore.unwatch(StoreName.USER, { userBlacklist: onUserBlacklistUpdated, }); }); const resetContactInfoUIData = () => { contactInfoData.value = {} as IContactInfoType; contactInfoBasicList.value = []; contactInfoMoreList.value = []; contactInfoButtonList.value = []; }; const resetContactSearchingUIData = () => { TUIStore.update(StoreName.CUSTOM, 'currentContactInfo', {}); TUIStore.update(StoreName.CUSTOM, 'currentContactSearchingStatus', false); TUIGlobal?.closeSearching && TUIGlobal?.closeSearching(); }; const onContactInfoEmitSubmit = (item: any) => { item.editSubmitHandler && item.editSubmitHandler({ item, contactInfoData: contactInfoData.value, isBothFriend: isBothFriend.value, isInBlackList: isInBlackList.value, }); }; const onContactInfoButtonClicked = (item: any) => { item.onClick && item.onClick({ contactInfoData: contactInfoData.value, contactInfoMoreList: contactInfoMoreList.value, }); if ( item.key === 'enterGroupConversation' || item.key === 'enterC2CConversation' ) { emits('switchConversation', contactInfoData.value); resetContactSearchingUIData(); } }; const generateMoreInfo = async () => { if (!isApplication.value) { if ( (!isGroup.value && !isBothFriend.value && !isInBlackList.value) || (isGroup.value && !isGroupMember.value && (contactInfoData.value as IGroupModel)?.type !== TUIChatEngine?.TYPES?.GRP_AVCHATROOM) ) { contactMoreInfoConfig.setWords.data = ''; contactInfoMoreList.value.push(contactMoreInfoConfig.setWords); } if (!isGroup.value && !isInBlackList.value) { contactMoreInfoConfig.setRemark.data = (contactInfoData.value as Friend)?.remark || ''; contactMoreInfoConfig.setRemark.editing = false; contactInfoMoreList.value.push(contactMoreInfoConfig.setRemark); } if (!isGroup.value && (isBothFriend.value || isInBlackList.value)) { contactMoreInfoConfig.blackList.data = isInBlackList.value || false; contactInfoMoreList.value.push(contactMoreInfoConfig.blackList); } } else { contactMoreInfoConfig.displayWords.data = (contactInfoData.value as FriendApplication)?.wording || ''; contactInfoMoreList.value.push(contactMoreInfoConfig.displayWords); } }; const generateButton = () => { if (isInBlackList.value) { return; } if (isApplication.value) { if ( (contactInfoData.value as FriendApplication)?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_TO_ME ) { contactInfoButtonList?.value?.push( contactButtonConfig.refuseFriendApplication, ); contactInfoButtonList?.value?.push( contactButtonConfig.acceptFriendApplication, ); } } else { if (isGroup.value && isGroupMember.value) { switch ((contactInfoData.value as IGroupModel)?.selfInfo?.role) { case 'Owner': contactInfoButtonList?.value?.push(contactButtonConfig.dismissGroup); break; default: contactInfoButtonList?.value?.push(contactButtonConfig.quitGroup); break; } contactInfoButtonList?.value?.push( contactButtonConfig.enterGroupConversation, ); } else if (!isGroup.value && isBothFriend.value) { contactInfoButtonList?.value?.push(contactButtonConfig.deleteFriend); contactInfoButtonList?.value?.push( contactButtonConfig.enterC2CConversation, ); } else { if (isGroup.value) { contactInfoButtonList?.value?.push( (contactInfoData.value as IGroupModel)?.type === TUIChatEngine?.TYPES?.GRP_AVCHATROOM ? contactButtonConfig.joinAVChatGroup : contactButtonConfig.joinGroup, ); } else { contactInfoButtonList?.value?.push(contactButtonConfig.addFriend); } } } }; function onUserBlacklistUpdated(userBlacklist: IBlackListUserItem[]) { blackList.value = userBlacklist; } async function onCurrentContactInfoUpdated(contactInfo: IContactInfoType) { if ( contactInfoData.value && contactInfo && JSON.stringify(contactInfoData.value) === JSON.stringify(contactInfo) ) { return; } resetContactInfoUIData(); // deep clone contactInfoData.value = deepCopy(contactInfo) || {}; if (!contactInfoData.value || Object.keys(contactInfoData.value)?.length === 0) { return; } contactInfoBasicList.value = generateContactInfoBasic( contactInfoData.value, ); isBothFriend.value = await isFriend(contactInfoData.value); generateMoreInfo(); generateButton(); if (contactInfo.infoKeyList) { contactInfoMoreList.value = contactInfo.infoKeyList.map((key: string) => { return (contactMoreInfoConfig as any)[key]; }); } if (contactInfo.btnKeyList) { contactInfoButtonList.value = contactInfo.btnKeyList.map((key: string) => { return (contactButtonConfig as any)[key]; }); } } </script> <style lang="scss" scoped src="./style/index.scss"></style>