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>