UI优化,红包领取后缺少头像,名称字段

This commit is contained in:
bobobobo
2026-01-19 23:30:08 +08:00
parent d2a22b9419
commit 651d20b909
41 changed files with 2189 additions and 1827 deletions

View File

@@ -1,28 +1,34 @@
<template>
<div
v-if="typeof contactInfoData === 'object' && Object.keys(contactInfoData).length"
v-if="
typeof contactInfoData === 'object' &&
Object.keys(contactInfoData).length
"
:class="['tui-contact-info', !isPC && 'tui-contact-info-h5']"
>
<Navigation>
<template #left>
<div @click="resetContactSearchingUIData">
<Icon
:file="backSVG"
/>
<Icon :file="backSVG" />
</div>
</template>
</Navigation>
<div :class="['tui-contact-info-basic', !isPC && 'tui-contact-info-h5-basic']">
<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',
!isPC && 'tui-contact-info-h5-basic-text'
]"
>
<div
:class="[
'tui-contact-info-basic-text-name',
!isPC && 'tui-contact-info-h5-basic-text-name',
!isPC && 'tui-contact-info-h5-basic-text-name'
]"
>
{{ generateContactInfoName(contactInfoData) }}
@@ -32,7 +38,7 @@
:key="item.label"
:class="[
'tui-contact-info-basic-text-other',
!isPC && 'tui-contact-info-h5-basic-text-other',
!isPC && 'tui-contact-info-h5-basic-text-other'
]"
>
{{
@@ -44,14 +50,17 @@
<img
:class="[
'tui-contact-info-basic-avatar',
!isPC && 'tui-contact-info-h5-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']"
:class="[
'tui-contact-info-more',
!isPC && 'tui-contact-info-h5-more'
]"
>
<div
v-for="item in contactInfoMoreList"
@@ -61,13 +70,13 @@
!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',
: 'tui-contact-info-more-item-left'
]"
>
<div
:class="[
'tui-contact-info-more-item-label',
!isPC && 'tui-contact-info-h5-more-item-label',
!isPC && 'tui-contact-info-h5-more-item-label'
]"
>
{{ `${TUITranslateService.t(`TUIContact.${item.label}`)}` }}
@@ -75,20 +84,20 @@
<div
:class="[
'tui-contact-info-more-item-content',
!isPC && 'tui-contact-info-h5-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',
!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',
!isPC && 'tui-contact-info-h5-more-item-content-text-data'
]"
>
{{ item.data }}
@@ -97,39 +106,41 @@
v-if="item.editable"
:class="[
'tui-contact-info-more-item-content-text-icon',
!isPC && 'tui-contact-info-h5-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"
/>
<Icon :file="editSVG" width="14px" height="14px" />
</div>
</div>
<input
v-else-if="item.editType === CONTACT_INFO_MORE_EDIT_TYPE.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',
!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-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',
!isPC && 'tui-contact-info-h5-more-item-content-textarea'
]"
confirm-type="done"
/>
<div
v-else-if="item.editType === CONTACT_INFO_MORE_EDIT_TYPE.SWITCH"
v-else-if="
item.editType === CONTACT_INFO_MORE_EDIT_TYPE.SWITCH
"
@click="onContactInfoEmitSubmit(item)"
>
<SwitchBar :value="item.data" />
@@ -140,7 +151,7 @@
<div
:class="[
'tui-contact-info-button',
!isPC && 'tui-contact-info-h5-button',
!isPC && 'tui-contact-info-h5-button'
]"
>
<button
@@ -151,7 +162,7 @@
!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`,
: `tui-contact-info-button-item-submit`
]"
@click="onContactInfoButtonClicked(item)"
>
@@ -161,263 +172,339 @@
</div>
</template>
<script setup lang="ts">
import TUIChatEngine, {
TUIStore,
StoreName,
TUITranslateService,
IGroupModel,
Friend,
FriendApplication,
} from '@tencentcloud/chat-uikit-engine-lite';
import { TUIGlobal } from '@tencentcloud/universal-api';
import { ref, computed, onMounted, onUnmounted } from '../../../adapter-vue';
import { isPC } from '../../../utils/env';
import TUIChatEngine, {
TUIStore,
StoreName,
TUITranslateService,
IGroupModel,
Friend,
FriendApplication,
TUIUserService
} from '@tencentcloud/chat-uikit-engine-lite'
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 Navigation from '../../common/Navigation/index.vue';
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,
CONTACT_INFO_TITLE,
} from '../../../constant';
import { deepCopy } from '../../TUIChat/utils/utils';
import {
generateAvatar,
generateContactInfoName,
generateContactInfoBasic,
isFriend,
isApplicationType
} from '../utils/index'
import {
contactMoreInfoConfig,
contactButtonConfig
} from './contact-info-config'
import Navigation from '../../common/Navigation/index.vue'
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,
CONTACT_INFO_TITLE
} from '../../../constant'
import { deepCopy } from '../../TUIChat/utils/utils'
import { useUI } from '../../../../utils/use-ui'
type IContactInfoType = IGroupModel | Friend | FriendApplication | IBlackListUserItem;
type IContactInfoType =
| IGroupModel
| Friend
| FriendApplication
| IBlackListUserItem
const emits = defineEmits(['switchConversation']);
const { showLoading, hideLoading } = useUI()
const contactInfoData = ref<IContactInfoType>({} as IContactInfoType);
const contactInfoBasicList = ref<Array<{ label: string; data: string }>>([]);
const contactInfoMoreList = ref<IContactInfoMoreItem[]>([]);
const contactInfoButtonList = ref<IContactInfoButton[]>([]);
const contactInfoTitle = ref<string>('');
const emits = defineEmits(['switchConversation'])
const setEditing = (item: any) => {
item.editing = true;
};
const contactInfoData = ref<IContactInfoType>({} as IContactInfoType)
const contactInfoBasicList = ref<
Array<{ label: string; data: string }>
>([])
const contactInfoMoreList = ref<IContactInfoMoreItem[]>([])
const contactInfoButtonList = ref<IContactInfoButton[]>([])
const contactInfoTitle = ref<string>('')
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,
currentContactListKey: onCurrentContactListKeyUpdated,
});
TUIStore.watch(StoreName.USER, {
userBlacklist: onUserBlacklistUpdated,
});
});
onUnmounted(() => {
TUIStore.unwatch(StoreName.CUSTOM, {
currentContactInfo: onCurrentContactInfoUpdated,
currentContactListKey: onCurrentContactListKeyUpdated,
});
TUIStore.unwatch(StoreName.USER, {
userBlacklist: onUserBlacklistUpdated,
});
});
const onCurrentContactListKeyUpdated = (key: string) => {
if (CONTACT_INFO_TITLE[key]) {
contactInfoTitle.value = TUITranslateService.t(`TUIContact.${CONTACT_INFO_TITLE[key]}`);
const setEditing = (item: any) => {
item.editing = true
}
};
const resetContactInfoUIData = () => {
contactInfoData.value = {} as IContactInfoType;
contactInfoBasicList.value = [];
contactInfoMoreList.value = [];
contactInfoButtonList.value = [];
};
const isGroup = computed((): boolean =>
(contactInfoData.value as IGroupModel)?.groupID ? true : false
)
const resetContactSearchingUIData = () => {
TUIStore.update(StoreName.CUSTOM, 'currentContactInfo', {});
TUIStore.update(StoreName.CUSTOM, 'currentContactSearchingStatus', false);
TUIGlobal?.closeSearching && TUIGlobal?.closeSearching();
};
const isApplication = computed((): boolean => {
return isApplicationType(contactInfoData?.value)
})
const onContactInfoEmitSubmit = (item: any) => {
item.editSubmitHandler
&& item.editSubmitHandler({
item,
contactInfoData: contactInfoData.value,
isBothFriend: isBothFriend.value,
isInBlackList: isInBlackList.value,
});
};
// is both friend, if is group type always false
const isBothFriend = ref<boolean>(false)
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();
// 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,
currentContactListKey: onCurrentContactListKeyUpdated
})
TUIStore.watch(StoreName.USER, {
userBlacklist: onUserBlacklistUpdated
})
})
onUnmounted(() => {
TUIStore.unwatch(StoreName.CUSTOM, {
currentContactInfo: onCurrentContactInfoUpdated,
currentContactListKey: onCurrentContactListKeyUpdated
})
TUIStore.unwatch(StoreName.USER, {
userBlacklist: onUserBlacklistUpdated
})
})
const onCurrentContactListKeyUpdated = (key: string) => {
if (CONTACT_INFO_TITLE[key]) {
contactInfoTitle.value = TUITranslateService.t(
`TUIContact.${CONTACT_INFO_TITLE[key]}`
)
}
}
};
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 resetContactInfoUIData = () => {
contactInfoData.value = {} as IContactInfoType
contactInfoBasicList.value = []
contactInfoMoreList.value = []
contactInfoButtonList.value = []
}
};
const generateButton = () => {
if (isInBlackList.value) {
return;
const resetContactSearchingUIData = () => {
TUIStore.update(StoreName.CUSTOM, 'currentContactInfo', {})
TUIStore.update(
StoreName.CUSTOM,
'currentContactSearchingStatus',
false
)
TUIGlobal?.closeSearching && TUIGlobal?.closeSearching()
}
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,
);
const onContactInfoEmitSubmit = (item: any) => {
if (item.key === 'blackList') {
// item.data = true
// resetContactSearchingUIData()
const userID = (contactInfoData.value as { userID: string }).userID
if (item.data) {
showLoading()
TUIUserService.removeFromBlacklist({ userIDList: [userID] })
.then(() => {
item.data = false
})
.finally(() => {
hideLoading()
})
} else {
contactInfoButtonList?.value?.push(contactButtonConfig.addFriend);
showLoading()
TUIUserService.addToBlacklist({ userIDList: [userID] })
.then(() => {
item.data = true
})
.finally(() => {
hideLoading()
})
}
} else {
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;
}
function onUserBlacklistUpdated(userBlacklist: IBlackListUserItem[]) {
blackList.value = userBlacklist
}
async function onCurrentContactInfoUpdated(contactInfo: IContactInfoType) {
if (
contactInfoData.value
&& contactInfo
&& JSON.stringify(contactInfoData.value) === JSON.stringify(contactInfo)
async function onCurrentContactInfoUpdated(
contactInfo: IContactInfoType
) {
return;
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]
}
)
}
}
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>
<style lang="scss" scoped>
.tui-contact-info-basic-avatar {
border-radius: 100rpx;
}
</style>

View File

@@ -1,28 +1,34 @@
<template>
<div
v-if="Object.keys(friendListData.map).length > 0"
class="friend-list"
>
<ul
v-for="(groupData, groupKey) in friendListData.map"
:key="groupKey"
<view>
<view
v-if="Object.keys(friendListData.map).length > 0"
class="friend-list"
>
<div class="friend-group-title">
{{ groupKey }} ({{ groupData.length }})
</div>
<li
v-for="contactListItem in groupData"
:key="contactListItem.renderKey"
class="friend-item"
@click="enterConversation(contactListItem)"
<ul
v-for="(groupData, groupKey) in friendListData.map"
:key="groupKey"
>
<ContactListItem
<div class="friend-group-title">
{{ groupKey }} ({{ groupData.length }})
</div>
<li
v-for="contactListItem in groupData"
:key="contactListItem.renderKey"
:item="deepCopy(contactListItem)"
/>
</li>
</ul>
</div>
class="friend-item"
@click="enterConversation(contactListItem)"
>
<ContactListItem
:key="contactListItem.renderKey"
:item="deepCopy(contactListItem)"
/>
</li>
</ul>
</view>
<cb-empty
v-if="Object.keys(friendListData.map).length === 0"
name="您还没有好友"
></cb-empty>
</view>
</template>
<script setup lang="ts">
@@ -83,7 +89,7 @@
.friend-group-title {
padding: 8px 16px;
background-color: #f8f9fa;
background-color: #ffffff;
font-size: 14px;
font-weight: 500;
color: #666;

View File

@@ -3,7 +3,7 @@
<div class="tui-contact-list-card-left">
<Avatar
useSkeletonAnimation
size="30px"
size="62rpx"
:url="generateAvatar(props.item)"
/>
<div

View File

@@ -3,9 +3,9 @@
v-if="!contactSearchingStatus"
:class="['tui-contact-list', !isPC && 'tui-contact-list-h5']"
>
<div v-if="!currentContactListKey">
<ul>
<li
<view v-if="!currentContactListKey">
<view class="top-list_box">
<view
v-for="(contactListObj, key) in contactListMap"
:key="key"
class="tui-contact-list-item"
@@ -14,11 +14,11 @@
class="tui-contact-list-item-header"
@click="toggleCurrentContactList(key)"
>
<div class="tui-contact-list-item-header-left">
<view class="tui-contact-list-item-header-left">
<Icon
v-if="contactListObj.icon"
:file="contactListObj.icon"
size="30px"
size="96rpx"
/>
<span
v-if="contactListObj.unreadCount"
@@ -26,25 +26,21 @@
>
{{ contactListObj.unreadCount }}
</span>
</div>
<div class="tui-contact-list-item-header-right">
<div>
{{
TUITranslateService.t(
`TUIContact.${contactListObj.title}`
)
}}
</div>
<Icon
</view>
<view class="tui-contact-list-item-header-right">
<text>
{{ contactListObj.title }}
</text>
<!-- <Icon
:file="currentContactListKey === key ? downSVG : rightSVG"
size="20px"
/>
</div>
/> -->
</view>
</header>
</li>
</ul>
</view>
</view>
<FriendList @enterConversation="selectFriend" />
</div>
</view>
<template v-else>
<li
v-for="contactListItem in contactListMap[currentContactListKey]
@@ -119,9 +115,10 @@
import downSVG from '../../../assets/icon/down-icon.svg'
import rightSVG from '../../../assets/icon/right-icon.svg'
import newContactsSVG from '../../../assets/icon/new-contacts.svg'
import groupSVG from '../../../assets/icon/groups.svg'
import blackListSVG from '../../../assets/icon/black-list.svg'
import newContactsSVG from '../../../assets/icon/friend-request-icon.svg'
import groupSVG from '../../../assets/icon/my-group-chat.svg'
import blackListSVG from '../../../assets/icon/blacklist-icon.svg'
import addFrienIcon from '../../../assets/icon/add-frien-icon.svg'
import type {
IContactList,
@@ -143,10 +140,16 @@
friendApplicationList: {
icon: newContactsSVG,
key: 'friendApplicationList',
title: '新的联系人',
title: '好友请求',
list: [] as FriendApplication[],
unreadCount: 0
},
currentContactSearchingStatus: {
icon: addFrienIcon,
key: 'currentContactSearchingStatus',
title: '添加好友',
list: [] as IGroupModel[]
},
groupList: {
icon: groupSVG,
key: 'groupList',
@@ -233,6 +236,10 @@
})
function toggleCurrentContactList(key: keyof IContactList) {
if (key === 'currentContactSearchingStatus') {
TUIStore.update(StoreName.CUSTOM, key, true)
return
}
if (currentContactListKey.value === key) {
currentContactListKey.value = ''
currentContactInfo.value = {} as IContactInfoType
@@ -465,4 +472,31 @@
height: 100% !important;
overflow: hidden;
}
.tui-contact-search-list-title {
padding: 16rpx 0;
}
.tui-contact-list-item-header {
&::after {
display: none !important;
}
}
.top-list_box {
display: flex;
justify-content: space-between;
padding: 32rpx 50rpx;
.tui-contact-list-item {
.tui-contact-list-item-header {
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
&:active {
background: none !important;
}
}
}
}
</style>

View File

@@ -3,175 +3,179 @@
<div
:class="[
'tui-contact-search-main',
!isPC && 'tui-contact-search-h5-main',
!isPC && 'tui-contact-search-h5-main'
]"
>
<input
v-model="searchValue"
class="tui-contact-search-main-input"
type="text"
:placeholder="searchingPlaceholder"
placeholder="请输入用户 / 群组搜索"
enterkeyhint="search"
@keyup.enter="search"
@blur="search"
@confirm="search"
>
<div
class="tui-contact-search-main-cancel"
@click="cancel"
>
{{ TUITranslateService.t("取消") }}
/>
<div class="tui-contact-search-main-cancel" @click="cancel">
{{ TUITranslateService.t('取消') }}
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from '../../../adapter-vue';
import {
TUITranslateService,
TUIStore,
StoreName,
} from '@tencentcloud/chat-uikit-engine-lite';
import TUICore, { TUIConstants } from '@tencentcloud/tui-core-lite';
import { TUIGlobal } from '@tencentcloud/universal-api';
import { isPC } from '../../../utils/env';
import { IContactSearchResult } from '../../../interface';
import { onMounted, ref, watch } from '../../../adapter-vue'
import {
TUITranslateService,
TUIStore,
StoreName
} from '@tencentcloud/chat-uikit-engine-lite'
import TUICore, { TUIConstants } from '@tencentcloud/tui-core-lite'
import { TUIGlobal } from '@tencentcloud/universal-api'
import { isPC } from '../../../utils/env'
import { IContactSearchResult } from '../../../interface'
const searchingPlaceholder = TUITranslateService.t('TUIContact.输入ID');
const searchValue = ref<string>('');
const searchResult = ref<IContactSearchResult>({
user: {
label: '联系人',
list: [],
},
group: {
label: '群聊',
list: [],
},
});
const cancel = () => {
TUIStore.update(
StoreName.CUSTOM,
'currentContactSearchingStatus',
false,
);
};
const search = async () => {
if (!searchValue.value) {
return;
}
TUICore.callService({
serviceName: TUIConstants.TUISearch.SERVICE.NAME,
method: TUIConstants.TUISearch.SERVICE.METHOD.SEARCH_USER,
params: {
userID: searchValue.value,
const searchValue = ref<string>('')
const searchResult = ref<IContactSearchResult>({
user: {
label: '联系人',
list: []
},
group: {
label: '群聊',
list: []
}
})
.then((res: any) => {
searchResult.value.user.list = res.data;
})
.catch((error: any) => {
searchResult.value.user.list = [];
console.warn('search user error', error);
});
TUICore.callService({
serviceName: TUIConstants.TUISearch.SERVICE.NAME,
method: TUIConstants.TUISearch.SERVICE.METHOD.SEARCH_GROUP,
params: {
groupID: searchValue.value,
},
})
.then((res: any) => {
searchResult.value.group.list = [res.data.group];
})
.catch((error: any) => {
searchResult.value.group.list = [];
console.warn('search group error', error);
});
};
watch(
() => searchResult.value,
() => {
const cancel = () => {
TUIStore.update(
StoreName.CUSTOM,
'currentContactSearchResult',
searchResult.value,
);
},
{
deep: true,
immediate: true,
},
);
'currentContactSearchingStatus',
false
)
}
onMounted(() => {
searchValue.value = '';
searchResult.value.user.list = [];
searchResult.value.group.list = [];
});
const search = async () => {
if (!searchValue.value) {
return
}
TUICore.callService({
serviceName: TUIConstants.TUISearch.SERVICE.NAME,
method: TUIConstants.TUISearch.SERVICE.METHOD.SEARCH_USER,
params: {
userID: searchValue.value
}
})
.then((res: any) => {
searchResult.value.user.list = res.data
})
.catch((error: any) => {
searchResult.value.user.list = []
console.warn('search user error', error)
})
TUICore.callService({
serviceName: TUIConstants.TUISearch.SERVICE.NAME,
method: TUIConstants.TUISearch.SERVICE.METHOD.SEARCH_GROUP,
params: {
groupID: searchValue.value
}
})
.then((res: any) => {
searchResult.value.group.list = [res.data.group]
})
.catch((error: any) => {
searchResult.value.group.list = []
console.warn('search group error', error)
})
}
watch(
() => searchResult.value,
() => {
TUIStore.update(
StoreName.CUSTOM,
'currentContactSearchResult',
searchResult.value
)
},
{
deep: true,
immediate: true
}
)
TUIGlobal.updateContactSearch = search;
TUIGlobal.closeSearching = () => {
searchValue.value = '';
searchResult.value.user.list = [];
searchResult.value.group.list = [];
};
onMounted(() => {
searchValue.value = ''
searchResult.value.user.list = []
searchResult.value.group.list = []
})
TUIGlobal.updateContactSearch = search
TUIGlobal.closeSearching = () => {
searchValue.value = ''
searchResult.value.user.list = []
searchResult.value.group.list = []
}
</script>
<style lang="scss" scoped>
.tui-contact-search {
position: sticky;
top: 0;
z-index: 1;
padding: 12px;
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
border-bottom: 1px solid #f4f5f9;
flex-direction: column;
&-main {
width: 100%;
height: 30px;
.tui-contact-search {
position: sticky;
top: 0;
z-index: 1;
padding: 12px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
background: #fff;
border-bottom: 1px solid #f4f5f9;
flex-direction: column;
&-main {
display: flex;
flex-direction: row;
flex: 1;
justify-content: center;
align-items: center;
width: 100%;
&-input {
flex: 1;
font-size: 14px;
border-radius: 5px;
padding: 7px;
border: 1px solid #ddd;
}
&-input:focus {
outline: none;
border: 1px solid #006eff;
}
&-cancel {
padding-left: 10px;
user-select: none;
cursor: pointer;
}
}
&-h5 {
&-header {
&-main {
width: 100%;
height: 30px;
display: flex;
flex-direction: row;
align-items: center;
}
&-main {
display: flex;
flex-direction: row;
flex: 1;
justify-content: center;
align-items: center;
width: 100%;
&-input {
flex: 1;
font-size: 14px;
border-radius: 5px;
padding: 7px;
border: 1px solid #ddd;
}
&-input:focus {
outline: none;
border: 1px solid #006eff;
}
&-cancel {
padding-left: 10px;
user-select: none;
cursor: pointer;
}
}
&-h5 {
&-header {
width: 100%;
}
}
}
}
.tui-contact-search-main-input {
border-radius: 64rpx;
height: 64rpx;
padding: 0 32rpx;
background: #f4f4f4;
}
</style>

View File

@@ -4,13 +4,7 @@
v-else-if="isShowContactList"
:class="['tui-contact', !isPC && 'tui-contact-h5']"
>
<Navigation
:title="
currentContactKey
? contactInfoTitle
: '通讯录'
"
>
<Navigation :title="currentContactKey ? contactInfoTitle : '通讯录'">
<template #left>
<div v-show="currentContactKey" @click="resetContactType">
<Icon :file="backSVG" />
@@ -67,7 +61,7 @@
import ContactList from './contact-list/index.vue'
import ContactInfo from './contact-info/index.vue'
import addCircle from '../../assets/icon/add-circle.svg'
import addCircle from '../../assets/icon/add-friend.svg'
import backSVG from '../../assets/icon/back.svg'
import { CONTACT_INFO_TITLE } from '../../constant'