1、腾讯IM模块格式化
This commit is contained in:
parent
99dfe74b26
commit
d77452dc25
@ -3,10 +3,7 @@ import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
|
||||
let vueVersion: number;
|
||||
let framework = 'vue2';
|
||||
let createVNode = (
|
||||
arg1: any,
|
||||
arg2: any,
|
||||
): { component: any; props: any; data: any } => {
|
||||
let createVNode = (arg1: any, arg2: any): { component: any; props: any; data: any } => {
|
||||
return {} as { component: any; props: any; data: any };
|
||||
};
|
||||
let render = (arg1: any, arg2: any) => {
|
||||
@ -14,17 +11,11 @@ let render = (arg1: any, arg2: any) => {
|
||||
};
|
||||
|
||||
try {
|
||||
if (
|
||||
(Vue as any)?.default?.version
|
||||
&& (Vue as any)?.default?.version?.startsWith('2.7.')
|
||||
) {
|
||||
if ((Vue as any)?.default?.version && (Vue as any)?.default?.version?.startsWith('2.7.')) {
|
||||
// >= Vue 2.7.0
|
||||
vueVersion = 2.7;
|
||||
TUIGlobal.Vue = (Vue as any)?.getCurrentInstance()?.appContext?.app;
|
||||
} else if (
|
||||
(Vue as any)?.default?.version
|
||||
&& (Vue as any)?.default?.version?.startsWith('2.')
|
||||
) {
|
||||
} else if ((Vue as any)?.default?.version && (Vue as any)?.default?.version?.startsWith('2.')) {
|
||||
// < Vue 2.7.0
|
||||
vueVersion = 2;
|
||||
TUIGlobal.Vue = (Vue as any).default;
|
||||
|
@ -8,10 +8,7 @@
|
||||
<Icon :file="backSVG" />
|
||||
</div>
|
||||
<div class="chat-header-container">
|
||||
<div
|
||||
v-if="isNotRoomChat"
|
||||
:class="['chat-header-content', !isPC && 'chat-header-h5-content']"
|
||||
>
|
||||
<div v-if="isNotRoomChat" :class="['chat-header-content', !isPC && 'chat-header-h5-content']">
|
||||
{{ currentConversationName }}
|
||||
</div>
|
||||
<div>
|
||||
@ -19,11 +16,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div :class="['chat-header-setting', !isPC && 'chat-header-h5-setting']">
|
||||
<div
|
||||
v-for="(item, index) in props.headerExtensionList"
|
||||
:key="index"
|
||||
@click.stop="handleExtensions(item)"
|
||||
>
|
||||
<div v-for="(item, index) in props.headerExtensionList" :key="index" @click.stop="handleExtensions(item)">
|
||||
<Icon :file="item.icon" />
|
||||
</div>
|
||||
</div>
|
||||
@ -31,12 +24,7 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted, withDefaults } from '../../../adapter-vue';
|
||||
import {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUITranslateService,
|
||||
IConversationModel,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIStore, StoreName, TUITranslateService, IConversationModel } from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIConstants, ExtensionInfo } from '@tencentcloud/tui-core';
|
||||
// import { JoinGroupCard } from '@tencentcloud/call-uikit-vue';
|
||||
import Icon from '../../common/Icon.vue';
|
||||
@ -49,8 +37,8 @@ const props = withDefaults(
|
||||
headerExtensionList: ExtensionInfo[];
|
||||
}>(),
|
||||
{
|
||||
headerExtensionList: () => ([]),
|
||||
},
|
||||
headerExtensionList: () => []
|
||||
}
|
||||
);
|
||||
|
||||
const emits = defineEmits(['closeChat']);
|
||||
@ -62,21 +50,21 @@ const isNotRoomChat = ref<boolean>(TUIChatConfig.getChatType() !== TUIConstants.
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversation: onCurrentConversationUpdated,
|
||||
currentConversation: onCurrentConversationUpdated
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.CHAT, {
|
||||
typingStatus: onTypingStatusUpdated,
|
||||
typingStatus: onTypingStatusUpdated
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CONV, {
|
||||
currentConversation: onCurrentConversationUpdated,
|
||||
currentConversation: onCurrentConversationUpdated
|
||||
});
|
||||
|
||||
TUIStore.unwatch(StoreName.CHAT, {
|
||||
typingStatus: onTypingStatusUpdated,
|
||||
typingStatus: onTypingStatusUpdated
|
||||
});
|
||||
});
|
||||
|
||||
@ -102,7 +90,6 @@ function onTypingStatusUpdated(status: boolean) {
|
||||
currentConversationName.value = currentConversation.value?.getShowName() || '';
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.chat-header {
|
||||
|
@ -26,7 +26,7 @@ class TUIChatConfig {
|
||||
InputQuickReplies: true,
|
||||
InputMention: true,
|
||||
MessageSearch: true,
|
||||
ReadStatus: true,
|
||||
ReadStatus: true
|
||||
};
|
||||
this.theme = 'light';
|
||||
}
|
||||
@ -76,8 +76,6 @@ class TUIChatConfig {
|
||||
const ChatConfig = TUIChatConfig.getInstance();
|
||||
const hideTUIChatFeatures = ChatConfig.hideTUIChatFeatures.bind(ChatConfig);
|
||||
|
||||
export {
|
||||
hideTUIChatFeatures,
|
||||
};
|
||||
export { hideTUIChatFeatures };
|
||||
|
||||
export default ChatConfig;
|
||||
|
@ -77,7 +77,7 @@ export const DEFAULT_BASIC_EMOJI_URL_MAPPING: Record<string, string> = {
|
||||
'[TUIEmoji_Prohibit]': 'emoji_58@2x.png',
|
||||
'[TUIEmoji_Convinced]': 'emoji_59@2x.png',
|
||||
'[TUIEmoji_Knife]': 'emoji_60@2x.png',
|
||||
'[TUIEmoji_Like]': 'emoji_61@2x.png',
|
||||
'[TUIEmoji_Like]': 'emoji_61@2x.png'
|
||||
};
|
||||
|
||||
export const BIG_EMOJI_GROUP_LIST: IEmojiGroupList = [
|
||||
@ -85,30 +85,60 @@ export const BIG_EMOJI_GROUP_LIST: IEmojiGroupList = [
|
||||
emojiGroupID: 1,
|
||||
type: EMOJI_TYPE.BIG,
|
||||
url: DEFAULT_BIG_EMOJI_URL,
|
||||
list: ['yz00', 'yz01', 'yz02', 'yz03', 'yz04', 'yz05', 'yz06', 'yz07', 'yz08',
|
||||
'yz09', 'yz10', 'yz11', 'yz12', 'yz13', 'yz14', 'yz15', 'yz16', 'yz17'],
|
||||
list: [
|
||||
'yz00',
|
||||
'yz01',
|
||||
'yz02',
|
||||
'yz03',
|
||||
'yz04',
|
||||
'yz05',
|
||||
'yz06',
|
||||
'yz07',
|
||||
'yz08',
|
||||
'yz09',
|
||||
'yz10',
|
||||
'yz11',
|
||||
'yz12',
|
||||
'yz13',
|
||||
'yz14',
|
||||
'yz15',
|
||||
'yz16',
|
||||
'yz17'
|
||||
]
|
||||
},
|
||||
{
|
||||
emojiGroupID: 2,
|
||||
type: EMOJI_TYPE.BIG,
|
||||
url: DEFAULT_BIG_EMOJI_URL,
|
||||
list: ['ys00', 'ys01', 'ys02', 'ys03', 'ys04', 'ys05', 'ys06', 'ys07', 'ys08',
|
||||
'ys09', 'ys10', 'ys11', 'ys12', 'ys13', 'ys14', 'ys15'],
|
||||
list: ['ys00', 'ys01', 'ys02', 'ys03', 'ys04', 'ys05', 'ys06', 'ys07', 'ys08', 'ys09', 'ys10', 'ys11', 'ys12', 'ys13', 'ys14', 'ys15']
|
||||
},
|
||||
{
|
||||
emojiGroupID: 3,
|
||||
type: EMOJI_TYPE.BIG,
|
||||
url: DEFAULT_BIG_EMOJI_URL,
|
||||
list: ['gcs00', 'gcs01', 'gcs02', 'gcs03', 'gcs04', 'gcs05', 'gcs06', 'gcs07',
|
||||
'gcs08', 'gcs09', 'gcs10', 'gcs11', 'gcs12', 'gcs13', 'gcs14', 'gcs15', 'gcs16'],
|
||||
},
|
||||
list: [
|
||||
'gcs00',
|
||||
'gcs01',
|
||||
'gcs02',
|
||||
'gcs03',
|
||||
'gcs04',
|
||||
'gcs05',
|
||||
'gcs06',
|
||||
'gcs07',
|
||||
'gcs08',
|
||||
'gcs09',
|
||||
'gcs10',
|
||||
'gcs11',
|
||||
'gcs12',
|
||||
'gcs13',
|
||||
'gcs14',
|
||||
'gcs15',
|
||||
'gcs16'
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export const BASIC_EMOJI_NAME_TO_KEY_MAPPING = {
|
||||
...Object.fromEntries(
|
||||
Object.entries(emojiCNLocales)?.map(([key, val]) => [val, key]),
|
||||
),
|
||||
...Object.fromEntries(
|
||||
Object.entries(emojiENLocales)?.map(([key, val]) => [val, key]),
|
||||
),
|
||||
...Object.fromEntries(Object.entries(emojiCNLocales)?.map(([key, val]) => [val, key])),
|
||||
...Object.fromEntries(Object.entries(emojiENLocales)?.map(([key, val]) => [val, key]))
|
||||
};
|
||||
|
@ -1,6 +1,12 @@
|
||||
import { TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import { CUSTOM_BASIC_EMOJI_URL, CUSTOM_BIG_EMOJI_URL, CUSTOM_BASIC_EMOJI_URL_MAPPING, CUSTOM_BIG_EMOJI_GROUP_LIST } from './custom-emoji';
|
||||
import { DEFAULT_BASIC_EMOJI_URL, BIG_EMOJI_GROUP_LIST, DEFAULT_BASIC_EMOJI_URL_MAPPING, BASIC_EMOJI_NAME_TO_KEY_MAPPING, DEFAULT_BIG_EMOJI_URL } from './default-emoji';
|
||||
import {
|
||||
DEFAULT_BASIC_EMOJI_URL,
|
||||
BIG_EMOJI_GROUP_LIST,
|
||||
DEFAULT_BASIC_EMOJI_URL_MAPPING,
|
||||
BASIC_EMOJI_NAME_TO_KEY_MAPPING,
|
||||
DEFAULT_BIG_EMOJI_URL
|
||||
} from './default-emoji';
|
||||
import { default as emojiCNLocales } from './locales/zh_cn';
|
||||
import { IEmojiGroupList } from '../../../interface';
|
||||
import { EMOJI_TYPE } from '../../../constant';
|
||||
@ -17,10 +23,10 @@ const EMOJI_GROUP_LIST: IEmojiGroupList = [
|
||||
emojiGroupID: 0,
|
||||
type: EMOJI_TYPE.BASIC,
|
||||
url: BASIC_EMOJI_URL,
|
||||
list: Object.keys(BASIC_EMOJI_URL_MAPPING),
|
||||
list: Object.keys(BASIC_EMOJI_URL_MAPPING)
|
||||
},
|
||||
...BIG_EMOJI_GROUP_LIST,
|
||||
...CUSTOM_BIG_EMOJI_GROUP_LIST,
|
||||
...CUSTOM_BIG_EMOJI_GROUP_LIST
|
||||
];
|
||||
|
||||
/**
|
||||
@ -49,7 +55,7 @@ const transformTextWithKeysToEmojiNames = (text: string): string => {
|
||||
const reg = /(\[.+?\])/g;
|
||||
let txt: string = text;
|
||||
if (reg.test(text)) {
|
||||
txt = text.replace(reg, match => BASIC_EMOJI_URL_MAPPING[match] ? convertKeyToEmojiName(match) : match);
|
||||
txt = text.replace(reg, (match) => (BASIC_EMOJI_URL_MAPPING[match] ? convertKeyToEmojiName(match) : match));
|
||||
}
|
||||
return txt;
|
||||
};
|
||||
@ -68,7 +74,7 @@ const transformTextWithEmojiNamesToKeys = (text: string) => {
|
||||
const reg = /(\[.+?\])/g;
|
||||
let txt: string = text;
|
||||
if (reg.test(text)) {
|
||||
txt = text.replace(reg, match => BASIC_EMOJI_NAME_TO_KEY_MAPPING[match] || match);
|
||||
txt = text.replace(reg, (match) => BASIC_EMOJI_NAME_TO_KEY_MAPPING[match] || match);
|
||||
}
|
||||
return txt;
|
||||
};
|
||||
@ -80,8 +86,8 @@ const emojiConfig = {
|
||||
emojiBaseUrl: BASIC_EMOJI_URL,
|
||||
emojiUrlMapping: BASIC_EMOJI_URL_MAPPING,
|
||||
emojiNameMapping: {
|
||||
...emojiCNLocales,
|
||||
},
|
||||
...emojiCNLocales
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -136,5 +142,5 @@ export {
|
||||
parseTextToRenderArray,
|
||||
transformTextWithKeysToEmojiNames,
|
||||
transformTextWithEmojiNamesToKeys,
|
||||
emojiConfig,
|
||||
emojiConfig
|
||||
};
|
||||
|
@ -60,7 +60,7 @@ const Emoji = {
|
||||
'[TUIEmoji_666]': '[666]',
|
||||
'[TUIEmoji_857]': '[857]',
|
||||
'[TUIEmoji_Knife]': '[Knife]',
|
||||
'[TUIEmoji_Like]': '[Like]',
|
||||
'[TUIEmoji_Like]': '[Like]'
|
||||
};
|
||||
|
||||
export default Emoji;
|
||||
|
@ -60,7 +60,7 @@ const Emoji: Record<string, string> = {
|
||||
'[TUIEmoji_666]': '[666]',
|
||||
'[TUIEmoji_857]': '[857]',
|
||||
'[TUIEmoji_Knife]': '[刀]',
|
||||
'[TUIEmoji_Like]': '[赞]',
|
||||
'[TUIEmoji_Like]': '[赞]'
|
||||
};
|
||||
|
||||
export default Emoji;
|
||||
|
@ -60,7 +60,7 @@ const Emoji: Record<string, string> = {
|
||||
'[TUIEmoji_666]': '[666]',
|
||||
'[TUIEmoji_857]': '[857]',
|
||||
'[TUIEmoji_Knife]': '[刀]',
|
||||
'[TUIEmoji_Like]': '[讚]',
|
||||
'[TUIEmoji_Like]': '[讚]'
|
||||
};
|
||||
|
||||
export default Emoji;
|
||||
|
@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<Overlay
|
||||
:visible="isShowForwardPanel"
|
||||
:useMask="false"
|
||||
>
|
||||
<Overlay :visible="isShowForwardPanel" :useMask="false">
|
||||
<Transfer
|
||||
:title="TUITranslateService.t('TUIChat.转发')"
|
||||
:isSearch="false"
|
||||
@ -17,12 +14,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, ref } from '../../../adapter-vue';
|
||||
import TUIChatEngine, {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUIChatService,
|
||||
TUITranslateService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import TUIChatEngine, { TUIStore, StoreName, TUIChatService, TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import Overlay from '../../common/Overlay/index.vue';
|
||||
import Transfer from '../../common/Transfer/index.vue';
|
||||
import { Toast, TOAST_TYPE } from '../../../components/common/Toast';
|
||||
@ -45,14 +37,14 @@ const customConversationList = ref();
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CUSTOM, {
|
||||
singleForwardMessageID: onSingleForwardMessageIDUpdated,
|
||||
multipleForwardMessageID: onMultipleForwardMessageIDUpdated,
|
||||
multipleForwardMessageID: onMultipleForwardMessageIDUpdated
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CUSTOM, {
|
||||
singleForwardMessageID: onSingleForwardMessageIDUpdated,
|
||||
multipleForwardMessageID: onMultipleForwardMessageIDUpdated,
|
||||
multipleForwardMessageID: onMultipleForwardMessageIDUpdated
|
||||
});
|
||||
|
||||
// tuistore data must be cleared when closing the forward panel
|
||||
@ -72,10 +64,7 @@ function onMultipleForwardMessageIDUpdated(params: { isMergeForward: boolean; me
|
||||
return;
|
||||
}
|
||||
isMergeForward = false;
|
||||
const {
|
||||
isMergeForward: _isMergeForward,
|
||||
messageIDList: selectedMessageIDList,
|
||||
} = params || {};
|
||||
const { isMergeForward: _isMergeForward, messageIDList: selectedMessageIDList } = params || {};
|
||||
if (selectedMessageIDList?.length > 0) {
|
||||
isMergeForward = _isMergeForward;
|
||||
selectedToForwardMessageIDList = selectedMessageIDList;
|
||||
@ -83,7 +72,7 @@ function onMultipleForwardMessageIDUpdated(params: { isMergeForward: boolean; me
|
||||
} else {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIChat.未选择消息'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -107,35 +96,29 @@ function openForwardPanel(): void {
|
||||
function finishSelected(selectedConvIDWrapperList: Array<{ userID: string }>): void {
|
||||
if (selectedConvIDWrapperList?.length === 0) return;
|
||||
// to reuse Transfer, so we have to get conversationModel by userID instead of ConversationID
|
||||
const selectedConversationList = selectedConvIDWrapperList.map(IDWrapper => TUIStore.getConversationModel(IDWrapper.userID));
|
||||
const unsentMessageQueue = selectedToForwardMessageIDList
|
||||
.map(messageID => TUIStore.getMessageModel(messageID))
|
||||
.sort((a, b) => a.time - b.time);
|
||||
const selectedConversationList = selectedConvIDWrapperList.map((IDWrapper) => TUIStore.getConversationModel(IDWrapper.userID));
|
||||
const unsentMessageQueue = selectedToForwardMessageIDList.map((messageID) => TUIStore.getMessageModel(messageID)).sort((a, b) => a.time - b.time);
|
||||
const forwardPromises = selectedConversationList.map((conversation) => {
|
||||
const offlinePushInfoCreateParams: IOfflinePushInfoCreateParams = {
|
||||
conversation,
|
||||
messageType: TUIChatEngine.TYPES.MSG_MERGER,
|
||||
messageType: TUIChatEngine.TYPES.MSG_MERGER
|
||||
};
|
||||
return TUIChatService.sendForwardMessage(
|
||||
[conversation],
|
||||
unsentMessageQueue,
|
||||
{
|
||||
return TUIChatService.sendForwardMessage([conversation], unsentMessageQueue, {
|
||||
needMerge: isMergeForward,
|
||||
offlinePushInfo: OfflinePushInfoManager.create(offlinePushInfoCreateParams),
|
||||
params: {
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
||||
},
|
||||
},
|
||||
);
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal()
|
||||
}
|
||||
});
|
||||
});
|
||||
Promise.allSettled(forwardPromises).then((results) => {
|
||||
for (const result of results) {
|
||||
const { status } = result;
|
||||
if (status === 'rejected') {
|
||||
const errorMessage = result.reason.code === 80001 ? TUITranslateService.t('TUIChat.内容包含敏感词汇') : result.reason.message as string;
|
||||
const errorMessage = result.reason.code === 80001 ? TUITranslateService.t('TUIChat.内容包含敏感词汇') : (result.reason.message as string);
|
||||
Toast({
|
||||
message: errorMessage,
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
break;
|
||||
}
|
||||
@ -152,7 +135,7 @@ function getTransforRenderDataList() {
|
||||
// To achieve reusability of Transfer, userID is used here instead of ConversationID
|
||||
userID: conversation.conversationID,
|
||||
nick: conversation.getShowName(),
|
||||
avatar: conversation.getAvatar(),
|
||||
avatar: conversation.getAvatar()
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -6,75 +6,31 @@
|
||||
'emoji-picker-h5': !isPC
|
||||
}"
|
||||
>
|
||||
<ul
|
||||
ref="emojiPickerListRef"
|
||||
:class="['emoji-picker-list', !isPC && 'emoji-picker-h5-list']"
|
||||
>
|
||||
<ul ref="emojiPickerListRef" :class="['emoji-picker-list', !isPC && 'emoji-picker-h5-list']">
|
||||
<li
|
||||
v-for="(childrenItem, childrenIndex) in currentEmojiList"
|
||||
:key="childrenIndex"
|
||||
class="emoji-picker-list-item"
|
||||
@click="select(childrenItem, childrenIndex)"
|
||||
>
|
||||
<img
|
||||
v-if="currentTabItem.type === EMOJI_TYPE.BASIC"
|
||||
class="emoji"
|
||||
:src="currentTabItem.url + BASIC_EMOJI_URL_MAPPING[childrenItem]"
|
||||
>
|
||||
<img
|
||||
v-else-if="currentTabItem.type === EMOJI_TYPE.BIG"
|
||||
class="emoji-big"
|
||||
:src="currentTabItem.url + childrenItem + '@2x.png'"
|
||||
>
|
||||
<img
|
||||
v-else
|
||||
class="emoji-custom emoji-big"
|
||||
:src="currentTabItem.url + childrenItem"
|
||||
>
|
||||
<img v-if="currentTabItem.type === EMOJI_TYPE.BASIC" class="emoji" :src="currentTabItem.url + BASIC_EMOJI_URL_MAPPING[childrenItem]" />
|
||||
<img v-else-if="currentTabItem.type === EMOJI_TYPE.BIG" class="emoji-big" :src="currentTabItem.url + childrenItem + '@2x.png'" />
|
||||
<img v-else class="emoji-custom emoji-big" :src="currentTabItem.url + childrenItem" />
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="emoji-picker-tab">
|
||||
<li
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
class="emoji-picker-tab-item"
|
||||
@click="toggleEmojiTab(index)"
|
||||
>
|
||||
<Icon
|
||||
v-if="item.type === EMOJI_TYPE.BASIC"
|
||||
class="icon"
|
||||
:file="faceIcon"
|
||||
/>
|
||||
<img
|
||||
v-else-if="item.type === EMOJI_TYPE.BIG"
|
||||
class="icon-big"
|
||||
:src="item.url + item.list[0] + '@2x.png'"
|
||||
>
|
||||
<img
|
||||
v-else
|
||||
class="icon-custom icon-big"
|
||||
:src="item.url + item.list[0]"
|
||||
>
|
||||
</li>
|
||||
<li
|
||||
v-if="isUniFrameWork"
|
||||
class="send-btn"
|
||||
@click="sendMessage"
|
||||
>
|
||||
发送
|
||||
<li v-for="(item, index) in list" :key="index" class="emoji-picker-tab-item" @click="toggleEmojiTab(index)">
|
||||
<Icon v-if="item.type === EMOJI_TYPE.BASIC" class="icon" :file="faceIcon" />
|
||||
<img v-else-if="item.type === EMOJI_TYPE.BIG" class="icon-big" :src="item.url + item.list[0] + '@2x.png'" />
|
||||
<img v-else class="icon-custom icon-big" :src="item.url + item.list[0]" />
|
||||
</li>
|
||||
<li v-if="isUniFrameWork" class="send-btn" @click="sendMessage">发送</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted } from '../../../../adapter-vue';
|
||||
import {
|
||||
TUIChatService,
|
||||
TUIStore,
|
||||
StoreName,
|
||||
IConversationModel,
|
||||
SendMessageParams,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIChatService, TUIStore, StoreName, IConversationModel, SendMessageParams } from '@tencentcloud/chat-uikit-engine';
|
||||
import Icon from '../../../common/Icon.vue';
|
||||
import faceIconLight from '../../../../assets/icon/face-light.svg';
|
||||
import faceIconDark from '../../../../assets/icon/face-dark.svg';
|
||||
@ -98,13 +54,13 @@ const currentEmojiList = ref<string[]>(list?.value[0]?.list);
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversation: onCurrentConversationUpdate,
|
||||
currentConversation: onCurrentConversationUpdate
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CONV, {
|
||||
currentConversation: onCurrentConversationUpdate,
|
||||
currentConversation: onCurrentConversationUpdate
|
||||
});
|
||||
});
|
||||
|
||||
@ -121,7 +77,7 @@ const toggleEmojiTab = (index: number) => {
|
||||
const select = (item: any, index: number) => {
|
||||
const options: any = {
|
||||
emoji: { key: item, name: convertKeyToEmojiName(item) },
|
||||
type: currentTabItem?.value?.type,
|
||||
type: currentTabItem?.value?.type
|
||||
};
|
||||
switch (currentTabItem?.value?.type) {
|
||||
case EMOJI_TYPE.BASIC:
|
||||
@ -146,15 +102,13 @@ const select = (item: any, index: number) => {
|
||||
|
||||
const sendFaceMessage = (index: number, listItem: IEmojiGroup) => {
|
||||
const options = {
|
||||
to:
|
||||
currentConversation?.value?.groupProfile?.groupID
|
||||
|| currentConversation?.value?.userProfile?.userID,
|
||||
to: currentConversation?.value?.groupProfile?.groupID || currentConversation?.value?.userProfile?.userID,
|
||||
conversationType: currentConversation?.value?.type,
|
||||
payload: {
|
||||
index: listItem.emojiGroupID,
|
||||
data: listItem.list[index],
|
||||
data: listItem.list[index]
|
||||
},
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal()
|
||||
} as SendMessageParams;
|
||||
TUIChatService.sendFaceMessage(options);
|
||||
};
|
||||
|
@ -1,24 +1,10 @@
|
||||
<template>
|
||||
<ToolbarItemContainer
|
||||
ref="container"
|
||||
:iconFile="faceIcon"
|
||||
title="表情"
|
||||
@onDialogShow="onDialogShow"
|
||||
@onDialogClose="onDialogClose"
|
||||
>
|
||||
<EmojiPickerDialog
|
||||
@insertEmoji="insertEmoji"
|
||||
@sendMessage="sendMessage"
|
||||
@onClose="onClose"
|
||||
/>
|
||||
<ToolbarItemContainer ref="container" :iconFile="faceIcon" title="表情" @onDialogShow="onDialogShow" @onDialogClose="onDialogClose">
|
||||
<EmojiPickerDialog @insertEmoji="insertEmoji" @sendMessage="sendMessage" @onClose="onClose" />
|
||||
</ToolbarItemContainer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
IConversationModel,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIStore, StoreName, IConversationModel } from '@tencentcloud/chat-uikit-engine';
|
||||
import { ref } from '../../../../adapter-vue';
|
||||
import faceIconLight from '../../../../assets/icon/face-light.svg';
|
||||
import faceIconDark from '../../../../assets/icon/face-dark.svg';
|
||||
@ -45,7 +31,7 @@ const container = ref<InstanceType<typeof ToolbarItemContainer>>();
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversation: (conversation: IConversationModel) => {
|
||||
currentConversation.value = conversation;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const onDialogShow = (dialogRef: any) => {
|
||||
@ -75,7 +61,7 @@ const onClose = () => {
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
closeEmojiPicker: onClose,
|
||||
closeEmojiPicker: onClose
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped src="./style/index.scss"></style>
|
||||
|
@ -11,83 +11,39 @@
|
||||
>
|
||||
<div :class="['evaluate', !isPC && 'evaluate-h5']">
|
||||
<div :class="['evaluate-header', !isPC && 'evaluate-h5-header']">
|
||||
<div
|
||||
:class="[
|
||||
'evaluate-header-content',
|
||||
!isPC && 'evaluate-h5-header-content',
|
||||
]"
|
||||
>
|
||||
{{ TUITranslateService.t("Evaluate.请对本次服务进行评价") }}
|
||||
<div :class="['evaluate-header-content', !isPC && 'evaluate-h5-header-content']">
|
||||
{{ TUITranslateService.t('Evaluate.请对本次服务进行评价') }}
|
||||
</div>
|
||||
<div
|
||||
v-if="!isPC"
|
||||
:class="[
|
||||
'evaluate-header-close',
|
||||
!isPC && 'evaluate-h5-header-close',
|
||||
]"
|
||||
@click.stop="closeDialog"
|
||||
>
|
||||
{{ TUITranslateService.t("关闭") }}
|
||||
<div v-if="!isPC" :class="['evaluate-header-close', !isPC && 'evaluate-h5-header-close']" @click.stop="closeDialog">
|
||||
{{ TUITranslateService.t('关闭') }}
|
||||
</div>
|
||||
</div>
|
||||
<div :class="['evaluate-content', !isPC && 'evaluate-h5-content']">
|
||||
<ul
|
||||
:class="[
|
||||
'evaluate-content-list',
|
||||
!isPC && 'evaluate-h5-content-list',
|
||||
]"
|
||||
>
|
||||
<ul :class="['evaluate-content-list', !isPC && 'evaluate-h5-content-list']">
|
||||
<li
|
||||
v-for="(item, index) in starList"
|
||||
:key="index"
|
||||
:class="[
|
||||
'evaluate-content-list-item',
|
||||
!isPC && 'evaluate-h5-content-list-item',
|
||||
]"
|
||||
:class="['evaluate-content-list-item', !isPC && 'evaluate-h5-content-list-item']"
|
||||
@click.stop="selectStar(index)"
|
||||
>
|
||||
<Icon
|
||||
v-if="index <= currentStarIndex"
|
||||
:file="starLightIcon"
|
||||
:width="isPC ? '20px' : '30px'"
|
||||
:height="isPC ? '20px' : '30px'"
|
||||
/>
|
||||
<Icon
|
||||
v-else
|
||||
:file="starIcon"
|
||||
:width="isPC ? '20px' : '30px'"
|
||||
:height="isPC ? '20px' : '30px'"
|
||||
/>
|
||||
<Icon v-if="index <= currentStarIndex" :file="starLightIcon" :width="isPC ? '20px' : '30px'" :height="isPC ? '20px' : '30px'" />
|
||||
<Icon v-else :file="starIcon" :width="isPC ? '20px' : '30px'" :height="isPC ? '20px' : '30px'" />
|
||||
</li>
|
||||
</ul>
|
||||
<textarea
|
||||
v-model="comment"
|
||||
:class="[
|
||||
'evaluate-content-text',
|
||||
!isPC && 'evaluate-h5-content-text',
|
||||
]"
|
||||
/>
|
||||
<div
|
||||
:class="[
|
||||
'evaluate-content-button',
|
||||
!isPC && 'evaluate-h5-content-button',
|
||||
]"
|
||||
>
|
||||
<button
|
||||
:class="['btn', isEvaluateValid ? 'btn-valid' : 'btn-invalid']"
|
||||
@click="submitEvaluate"
|
||||
>
|
||||
{{ TUITranslateService.t("Evaluate.提交评价") }}
|
||||
<textarea v-model="comment" :class="['evaluate-content-text', !isPC && 'evaluate-h5-content-text']" />
|
||||
<div :class="['evaluate-content-button', !isPC && 'evaluate-h5-content-button']">
|
||||
<button :class="['btn', isEvaluateValid ? 'btn-valid' : 'btn-invalid']" @click="submitEvaluate">
|
||||
{{ TUITranslateService.t('Evaluate.提交评价') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="['evaluate-adv', !isPC && 'evaluate-h5-adv']">
|
||||
{{ TUITranslateService.t("Evaluate.服务评价工具") }}
|
||||
{{ "(" + TUITranslateService.t("Evaluate.使用") }}
|
||||
{{ TUITranslateService.t('Evaluate.服务评价工具') }}
|
||||
{{ '(' + TUITranslateService.t('Evaluate.使用') }}
|
||||
<a @click="openLink(Link.customMessage)">
|
||||
{{ TUITranslateService.t(`Evaluate.${Link.customMessage.label}`) }}
|
||||
</a>
|
||||
{{ TUITranslateService.t("Evaluate.搭建") + ")" }}
|
||||
{{ TUITranslateService.t('Evaluate.搭建') + ')' }}
|
||||
</div>
|
||||
</div>
|
||||
</ToolbarItemContainer>
|
||||
@ -100,7 +56,7 @@ import TUIChatEngine, {
|
||||
IConversationModel,
|
||||
TUIChatService,
|
||||
SendMessageParams,
|
||||
SendMessageOptions,
|
||||
SendMessageOptions
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { ref, computed } from '../../../../adapter-vue';
|
||||
import ToolbarItemContainer from '../toolbar-item-container/index.vue';
|
||||
@ -120,8 +76,8 @@ const evaluateIcon = TUIChatConfig.getTheme() === 'dark' ? evaluateIconDark : ev
|
||||
const props = defineProps({
|
||||
starTotal: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
default: 5
|
||||
}
|
||||
});
|
||||
const emits = defineEmits(['onDialogPopupShowOrHide']);
|
||||
|
||||
@ -135,7 +91,7 @@ const currentConversation = ref<IConversationModel>();
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversation: (conversation: IConversationModel) => {
|
||||
currentConversation.value = conversation;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const isEvaluateValid = computed(() => comment.value.length || currentStarIndex.value >= 0);
|
||||
@ -182,26 +138,24 @@ const submitEvaluate = () => {
|
||||
businessID: CHAT_MSG_CUSTOM_TYPE.EVALUATE,
|
||||
version: 1,
|
||||
score: currentStarIndex.value + 1,
|
||||
comment: comment.value,
|
||||
comment: comment.value
|
||||
}),
|
||||
description: '对本次的服务评价',
|
||||
extension: '对本次的服务评价',
|
||||
extension: '对本次的服务评价'
|
||||
};
|
||||
const options = {
|
||||
to:
|
||||
currentConversation?.value?.groupProfile?.groupID
|
||||
|| currentConversation?.value?.userProfile?.userID,
|
||||
to: currentConversation?.value?.groupProfile?.groupID || currentConversation?.value?.userProfile?.userID,
|
||||
conversationType: currentConversation?.value?.type,
|
||||
payload,
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal()
|
||||
};
|
||||
const offlinePushInfoCreateParams: IOfflinePushInfoCreateParams = {
|
||||
conversation: currentConversation.value,
|
||||
payload: options.payload,
|
||||
messageType: TUIChatEngine.TYPES.MSG_CUSTOM,
|
||||
messageType: TUIChatEngine.TYPES.MSG_CUSTOM
|
||||
};
|
||||
const sendMessageOptions: SendMessageOptions = {
|
||||
offlinePushInfo: OfflinePushInfoManager.create(offlinePushInfoCreateParams),
|
||||
offlinePushInfo: OfflinePushInfoManager.create(offlinePushInfoCreateParams)
|
||||
};
|
||||
TUIChatService.sendCustomMessage(options as SendMessageParams, sendMessageOptions);
|
||||
// close dialog after submit evaluate
|
||||
|
@ -8,14 +8,7 @@
|
||||
@onIconClick="onIconClick"
|
||||
>
|
||||
<div :class="['file-upload', !isPC && 'file-upload-h5']">
|
||||
<input
|
||||
ref="inputRef"
|
||||
title="文件"
|
||||
type="file"
|
||||
data-type="file"
|
||||
accept="*"
|
||||
@change="sendFileMessage"
|
||||
>
|
||||
<input ref="inputRef" title="文件" type="file" data-type="file" accept="*" @change="sendFileMessage" />
|
||||
</div>
|
||||
</ToolbarItemContainer>
|
||||
</template>
|
||||
@ -26,7 +19,7 @@ import TUIChatEngine, {
|
||||
StoreName,
|
||||
IConversationModel,
|
||||
SendMessageParams,
|
||||
SendMessageOptions,
|
||||
SendMessageOptions
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { ref } from '../../../../adapter-vue';
|
||||
import ToolbarItemContainer from '../toolbar-item-container/index.vue';
|
||||
@ -44,7 +37,7 @@ const currentConversation = ref<IConversationModel>();
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversation: (conversation: IConversationModel) => {
|
||||
currentConversation.value = conversation;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const onIconClick = () => {
|
||||
@ -60,27 +53,25 @@ const sendFileMessage = (e: any) => {
|
||||
return;
|
||||
}
|
||||
const options = {
|
||||
to:
|
||||
currentConversation?.value?.groupProfile?.groupID
|
||||
|| currentConversation?.value?.userProfile?.userID,
|
||||
to: currentConversation?.value?.groupProfile?.groupID || currentConversation?.value?.userProfile?.userID,
|
||||
conversationType: currentConversation?.value?.type,
|
||||
payload: {
|
||||
file: e?.target,
|
||||
file: e?.target
|
||||
},
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal()
|
||||
} as SendMessageParams;
|
||||
const offlinePushInfoCreateParams: IOfflinePushInfoCreateParams = {
|
||||
conversation: currentConversation.value,
|
||||
payload: options.payload,
|
||||
messageType: TUIChatEngine.TYPES.MSG_FILE,
|
||||
messageType: TUIChatEngine.TYPES.MSG_FILE
|
||||
};
|
||||
const sendMessageOptions: SendMessageOptions = {
|
||||
offlinePushInfo: OfflinePushInfoManager.create(offlinePushInfoCreateParams),
|
||||
offlinePushInfo: OfflinePushInfoManager.create(offlinePushInfoCreateParams)
|
||||
};
|
||||
TUIChatService.sendFileMessage(options, sendMessageOptions);
|
||||
e.target.value = '';
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../../assets/styles/common";
|
||||
@import '../../../../assets/styles/common';
|
||||
</style>
|
||||
|
@ -7,10 +7,7 @@
|
||||
:needDialog="false"
|
||||
@onIconClick="onIconClick"
|
||||
>
|
||||
<div
|
||||
v-if="!isUniFrameWork"
|
||||
:class="['image-upload', !isPC && 'image-upload-h5']"
|
||||
>
|
||||
<div v-if="!isUniFrameWork" :class="['image-upload', !isPC && 'image-upload-h5']">
|
||||
<input
|
||||
ref="inputRef"
|
||||
title="图片"
|
||||
@ -18,7 +15,7 @@
|
||||
data-type="image"
|
||||
accept="image/gif,image/jpeg,image/jpg,image/png,image/bmp,image/webp"
|
||||
@change="sendImageInWeb"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
</ToolbarItemContainer>
|
||||
</template>
|
||||
@ -29,7 +26,7 @@ import TUIChatEngine, {
|
||||
StoreName,
|
||||
IConversationModel,
|
||||
SendMessageParams,
|
||||
SendMessageOptions,
|
||||
SendMessageOptions
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import { ref, computed } from '../../../../adapter-vue';
|
||||
@ -49,8 +46,8 @@ const props = defineProps({
|
||||
// camera: Take a photo using the camera
|
||||
imageSourceType: {
|
||||
type: String,
|
||||
default: 'album',
|
||||
},
|
||||
default: 'album'
|
||||
}
|
||||
});
|
||||
|
||||
const inputRef = ref();
|
||||
@ -59,29 +56,27 @@ const theme = TUIChatConfig.getTheme();
|
||||
const IMAGE_TOOLBAR_SHOW_MAP = {
|
||||
web_album: {
|
||||
icon: theme === 'dark' ? imageIconDark : imageIconLight,
|
||||
title: '图片',
|
||||
title: '图片'
|
||||
},
|
||||
uni_album: {
|
||||
icon: imageUniIcon,
|
||||
title: '图片',
|
||||
title: '图片'
|
||||
},
|
||||
uni_camera: {
|
||||
icon: cameraUniIcon,
|
||||
title: '拍照',
|
||||
},
|
||||
title: '拍照'
|
||||
}
|
||||
};
|
||||
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversation: (conversation: IConversationModel) => {
|
||||
currentConversation.value = conversation;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const imageToolbarForShow = computed((): { icon: string; title: string } => {
|
||||
if (isUniFrameWork) {
|
||||
return props.imageSourceType === 'camera'
|
||||
? IMAGE_TOOLBAR_SHOW_MAP['uni_camera']
|
||||
: IMAGE_TOOLBAR_SHOW_MAP['uni_album'];
|
||||
return props.imageSourceType === 'camera' ? IMAGE_TOOLBAR_SHOW_MAP['uni_camera'] : IMAGE_TOOLBAR_SHOW_MAP['uni_album'];
|
||||
} else {
|
||||
return IMAGE_TOOLBAR_SHOW_MAP['web_album'];
|
||||
}
|
||||
@ -98,7 +93,7 @@ const onIconClick = () => {
|
||||
sourceType: [props.imageSourceType], // Use camera or select from album.
|
||||
success: function (res: any) {
|
||||
sendImageMessage(res);
|
||||
},
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// uni-app H5/App send image
|
||||
@ -107,7 +102,7 @@ const onIconClick = () => {
|
||||
sourceType: [props.imageSourceType], // Use camera or select from album.
|
||||
success: function (res) {
|
||||
sendImageMessage(res);
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@ -130,27 +125,25 @@ const sendImageMessage = (files: any) => {
|
||||
return;
|
||||
}
|
||||
const options = {
|
||||
to:
|
||||
currentConversation?.value?.groupProfile?.groupID
|
||||
|| currentConversation?.value?.userProfile?.userID,
|
||||
to: currentConversation?.value?.groupProfile?.groupID || currentConversation?.value?.userProfile?.userID,
|
||||
conversationType: currentConversation?.value?.type,
|
||||
payload: {
|
||||
file: files,
|
||||
file: files
|
||||
},
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal()
|
||||
} as SendMessageParams;
|
||||
const offlinePushInfoCreateParams: IOfflinePushInfoCreateParams = {
|
||||
conversation: currentConversation.value,
|
||||
payload: options.payload,
|
||||
messageType: TUIChatEngine.TYPES.MSG_IMAGE,
|
||||
messageType: TUIChatEngine.TYPES.MSG_IMAGE
|
||||
};
|
||||
const sendMessageOptions: SendMessageOptions = {
|
||||
offlinePushInfo: OfflinePushInfoManager.create(offlinePushInfoCreateParams),
|
||||
offlinePushInfo: OfflinePushInfoManager.create(offlinePushInfoCreateParams)
|
||||
};
|
||||
TUIChatService.sendImageMessage(options, sendMessageOptions);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../../assets/styles/common";
|
||||
@import '../../../../assets/styles/common';
|
||||
</style>
|
||||
|
@ -1,18 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
:class="[
|
||||
'message-input-toolbar',
|
||||
!isPC && 'message-input-toolbar-h5',
|
||||
isUniFrameWork && 'message-input-toolbar-uni',
|
||||
]"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'message-input-toolbar-list',
|
||||
!isPC && 'message-input-toolbar-h5-list',
|
||||
isUniFrameWork && 'message-input-toolbar-uni-list',
|
||||
]"
|
||||
>
|
||||
<div :class="['message-input-toolbar', !isPC && 'message-input-toolbar-h5', isUniFrameWork && 'message-input-toolbar-uni']">
|
||||
<div :class="['message-input-toolbar-list', !isPC && 'message-input-toolbar-h5-list', isUniFrameWork && 'message-input-toolbar-uni-list']">
|
||||
<EmojiPicker
|
||||
v-if="isRenderedEmojiPicker"
|
||||
ref="emojiPickerRef"
|
||||
@ -21,15 +9,9 @@
|
||||
@dialogCloseInH5="dialogCloseInH5"
|
||||
@changeToolbarDisplayType="(type) => emits('changeToolbarDisplayType', type)"
|
||||
/>
|
||||
<ImageUpload
|
||||
v-if="featureConfig.InputImage"
|
||||
imageSourceType="album"
|
||||
/>
|
||||
<ImageUpload v-if="featureConfig.InputImage" imageSourceType="album" />
|
||||
<FileUpload v-if="featureConfig.InputFile" />
|
||||
<VideoUpload
|
||||
v-if="featureConfig.InputVideo"
|
||||
videoSourceType="album"
|
||||
/>
|
||||
<VideoUpload v-if="featureConfig.InputVideo" videoSourceType="album" />
|
||||
<Evaluate v-if="featureConfig.InputEvaluation" />
|
||||
<Words v-if="featureConfig.InputQuickReplies" />
|
||||
<template v-if="extensionListShowInStart[0]">
|
||||
@ -45,10 +27,7 @@
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
<div
|
||||
v-if="extensionListShowInEnd[0] && isPC"
|
||||
:class="['message-input-toolbar-list-end']"
|
||||
>
|
||||
<div v-if="extensionListShowInEnd[0] && isPC" :class="['message-input-toolbar-list-end']">
|
||||
<ToolbarItemContainer
|
||||
v-for="(extension, index) in extensionListShowInEnd"
|
||||
:key="index"
|
||||
@ -68,21 +47,12 @@
|
||||
@submit="onUserSelectorSubmit"
|
||||
@cancel="onUserSelectorCancel"
|
||||
/>
|
||||
<div
|
||||
v-if="isH5"
|
||||
ref="h5Dialog"
|
||||
:class="['message-input-toolbar-h5-dialog']"
|
||||
/>
|
||||
<div v-if="isH5" ref="h5Dialog" :class="['message-input-toolbar-h5-dialog']" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from '../../../adapter-vue';
|
||||
import TUIChatEngine, {
|
||||
IConversationModel,
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUIReportService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import TUIChatEngine, { IConversationModel, TUIStore, StoreName, TUIReportService } from '@tencentcloud/chat-uikit-engine';
|
||||
import TUICore, { ExtensionInfo, TUIConstants } from '@tencentcloud/tui-core';
|
||||
import EmojiPicker from './emoji-picker/index.vue';
|
||||
import ImageUpload from './image-upload/index.vue';
|
||||
@ -103,10 +73,10 @@ interface IProps {
|
||||
interface IEmits {
|
||||
(e: 'scrollToLatestMessage'): void;
|
||||
(e: 'changeToolbarDisplayType', type: ToolbarDisplayType): void;
|
||||
(e: 'insertEmoji', emoji: any): void
|
||||
(e: 'insertEmoji', emoji: any): void;
|
||||
}
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
displayType: 'none',
|
||||
displayType: 'none'
|
||||
});
|
||||
|
||||
const emits = defineEmits<IEmits>();
|
||||
@ -124,23 +94,26 @@ isRenderedEmojiPicker.value = featureConfig.InputEmoji || featureConfig.InputSti
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CUSTOM, {
|
||||
activeConversation: onActiveConversationUpdate,
|
||||
activeConversation: onActiveConversationUpdate
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CUSTOM, {
|
||||
activeConversation: onActiveConversationUpdate,
|
||||
activeConversation: onActiveConversationUpdate
|
||||
});
|
||||
});
|
||||
|
||||
watch(() => props.displayType, (newValue) => {
|
||||
watch(
|
||||
() => props.displayType,
|
||||
(newValue) => {
|
||||
if (newValue === 'none') {
|
||||
emojiPickerRef.value?.closeEmojiPicker();
|
||||
} else {
|
||||
emits('scrollToLatestMessage');
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const onActiveConversationUpdate = (conversationID: string) => {
|
||||
if (!conversationID) {
|
||||
@ -162,14 +135,14 @@ const getExtensionList = () => {
|
||||
params.filterVideo = true;
|
||||
enableSampleTaskStatus('customerService');
|
||||
}
|
||||
currentExtensionList.value = [
|
||||
...TUICore.getExtensionList(TUIConstants.TUIChat.EXTENSION.INPUT_MORE.EXT_ID, params),
|
||||
].filter((extension: ExtensionInfo) => {
|
||||
currentExtensionList.value = [...TUICore.getExtensionList(TUIConstants.TUIChat.EXTENSION.INPUT_MORE.EXT_ID, params)].filter(
|
||||
(extension: ExtensionInfo) => {
|
||||
if (extension?.data?.name === 'search') {
|
||||
return featureConfig.MessageSearch;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
);
|
||||
reportExtension(currentExtensionList.value);
|
||||
};
|
||||
|
||||
@ -196,7 +169,7 @@ const extensionListShowInStart = computed<ExtensionInfo[]>(() => {
|
||||
|
||||
const extensionListShowInEnd = computed<ExtensionInfo[]>(() => {
|
||||
if (isPC) {
|
||||
const searchExtension = currentExtensionList.value.find(extension => extension?.data?.name === 'search');
|
||||
const searchExtension = currentExtensionList.value.find((extension) => extension?.data?.name === 'search');
|
||||
return searchExtension ? [searchExtension] : [];
|
||||
}
|
||||
return [];
|
||||
@ -222,7 +195,7 @@ const onCallExtensionClicked = (extension: ExtensionInfo, callType: number) => {
|
||||
if (currentConversation?.value?.type === TUIChatEngine.TYPES.CONV_C2C) {
|
||||
extension.listener?.onClicked?.({
|
||||
userIDList: [currentConversation?.value?.conversationID?.slice(3)],
|
||||
type: callType,
|
||||
type: callType
|
||||
});
|
||||
} else if (isGroup.value) {
|
||||
currentUserSelectorExtension.value = extension;
|
||||
@ -268,7 +241,7 @@ const dialogCloseInH5 = (dialogDom: any) => {
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../assets/styles/common";
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
.message-input-toolbar {
|
||||
border-top: 1px solid #f4f5f9;
|
||||
|
@ -1,30 +1,9 @@
|
||||
<template>
|
||||
<div
|
||||
ref="toolbarItemRef"
|
||||
:class="[
|
||||
'toolbar-item-container',
|
||||
!isPC && 'toolbar-item-container-h5',
|
||||
isUniFrameWork && 'toolbar-item-container-uni',
|
||||
]"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'toolbar-item-container-icon',
|
||||
isUniFrameWork && 'toolbar-item-container-uni-icon',
|
||||
]"
|
||||
@click="toggleToolbarItem"
|
||||
>
|
||||
<Icon
|
||||
:file="props.iconFile"
|
||||
class="icon"
|
||||
:width="props.iconWidth"
|
||||
:height="props.iconHeight"
|
||||
/>
|
||||
<div ref="toolbarItemRef" :class="['toolbar-item-container', !isPC && 'toolbar-item-container-h5', isUniFrameWork && 'toolbar-item-container-uni']">
|
||||
<div :class="['toolbar-item-container-icon', isUniFrameWork && 'toolbar-item-container-uni-icon']" @click="toggleToolbarItem">
|
||||
<Icon :file="props.iconFile" class="icon" :width="props.iconWidth" :height="props.iconHeight" />
|
||||
</div>
|
||||
<div
|
||||
v-if="isUniFrameWork"
|
||||
:class="['toolbar-item-container-uni-title']"
|
||||
>
|
||||
<div v-if="isUniFrameWork" :class="['toolbar-item-container-uni-title']">
|
||||
{{ props.title }}
|
||||
</div>
|
||||
<div
|
||||
@ -34,7 +13,7 @@
|
||||
'toolbar-item-container-dialog',
|
||||
isDark && 'toolbar-item-container-dialog-dark',
|
||||
!isPC && 'toolbar-item-container-h5-dialog',
|
||||
isUniFrameWork && 'toolbar-item-container-uni-dialog',
|
||||
isUniFrameWork && 'toolbar-item-container-uni-dialog'
|
||||
]"
|
||||
>
|
||||
<BottomPopup
|
||||
@ -61,30 +40,30 @@ import TUIChatConfig from '../../config';
|
||||
const props = defineProps({
|
||||
iconFile: {
|
||||
type: String,
|
||||
required: true,
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: ''
|
||||
},
|
||||
needDialog: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
iconWidth: {
|
||||
type: String,
|
||||
default: '20px',
|
||||
default: '20px'
|
||||
},
|
||||
iconHeight: {
|
||||
type: String,
|
||||
default: '20px',
|
||||
default: '20px'
|
||||
},
|
||||
// Whether to display the bottom popup dialog on mobile devices
|
||||
// Invalid on PC
|
||||
needBottomPopup: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const emits = defineEmits(['onIconClick', 'onDialogClose', 'onDialogShow']);
|
||||
@ -99,7 +78,7 @@ const toggleToolbarItem = () => {
|
||||
if (isPC) {
|
||||
outsideClick.listen({
|
||||
domRefs: toolbarItemRef.value,
|
||||
handler: closeToolbarItem,
|
||||
handler: closeToolbarItem
|
||||
});
|
||||
}
|
||||
if (!props.needDialog) {
|
||||
@ -132,7 +111,7 @@ const onPopupClose = () => {
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
toggleDialogDisplay,
|
||||
toggleDialogDisplay
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped src="./style/index.scss"></style>
|
||||
|
@ -1,12 +1,5 @@
|
||||
<template>
|
||||
<Dialog
|
||||
:show="show"
|
||||
:isH5="!isPC"
|
||||
:isHeaderShow="false"
|
||||
:isFooterShow="false"
|
||||
:background="false"
|
||||
@update:show="toggleShow"
|
||||
>
|
||||
<Dialog :show="show" :isH5="!isPC" :isHeaderShow="false" :isFooterShow="false" :background="false" @update:show="toggleShow">
|
||||
<Transfer
|
||||
:isSearch="true"
|
||||
:title="title"
|
||||
@ -20,10 +13,7 @@
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
TUIGroupService,
|
||||
TUIUserService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGroupService, TUIUserService } from '@tencentcloud/chat-uikit-engine';
|
||||
import { ref, computed, watch } from '../../../../adapter-vue';
|
||||
import Dialog from '../../../common/Dialog/index.vue';
|
||||
import Transfer from '../../../common/Transfer/index.vue';
|
||||
@ -33,16 +23,16 @@ const props = defineProps({
|
||||
// type: voiceCall/groupCall/...
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: ''
|
||||
},
|
||||
currentConversation: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
default: () => ({})
|
||||
},
|
||||
isGroup: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
default: false
|
||||
}
|
||||
});
|
||||
const emits = defineEmits(['submit', 'cancel']);
|
||||
const show = ref<boolean>(false);
|
||||
@ -52,7 +42,7 @@ const searchMemberList = ref<any[]>([]);
|
||||
const selfUserID = ref<string>('');
|
||||
const titleMap: any = {
|
||||
voiceCall: '发起群语音',
|
||||
videoCall: '发起群视频',
|
||||
videoCall: '发起群视频'
|
||||
};
|
||||
const title = computed(() => {
|
||||
return titleMap[props.type] ? titleMap[props.type] : '';
|
||||
@ -71,11 +61,9 @@ watch(
|
||||
if (props.isGroup && show.value) {
|
||||
groupID.value = props.currentConversation.groupProfile.groupID;
|
||||
TUIGroupService.getGroupMemberList({
|
||||
groupID: groupID.value,
|
||||
groupID: groupID.value
|
||||
}).then((res: any) => {
|
||||
memberList.value = res?.data?.memberList?.filter(
|
||||
(user: any) => user?.userID !== selfUserID.value,
|
||||
);
|
||||
memberList.value = res?.data?.memberList?.filter((user: any) => user?.userID !== selfUserID.value);
|
||||
searchMemberList.value = memberList.value;
|
||||
});
|
||||
} else {
|
||||
@ -86,14 +74,12 @@ watch(
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
const search = (searchInfo: string) => {
|
||||
const results = memberList.value?.filter(
|
||||
(member: any) => member?.userID === searchInfo,
|
||||
);
|
||||
const results = memberList.value?.filter((member: any) => member?.userID === searchInfo);
|
||||
searchMemberList.value = results?.length ? results : memberList.value;
|
||||
};
|
||||
|
||||
@ -122,6 +108,6 @@ const toggleShow = (showStatus: boolean) => {
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
toggleShow,
|
||||
toggleShow
|
||||
});
|
||||
</script>
|
||||
|
@ -4,23 +4,11 @@
|
||||
:title="handleTitle()"
|
||||
:needDialog="false"
|
||||
:iconWidth="isUniFrameWork ? '32px' : '20px'"
|
||||
:iconHeight="isUniFrameWork
|
||||
? props.videoSourceType === 'album'
|
||||
? '20px'
|
||||
: '25px'
|
||||
: '18px'
|
||||
"
|
||||
:iconHeight="isUniFrameWork ? (props.videoSourceType === 'album' ? '20px' : '25px') : '18px'"
|
||||
@onIconClick="onIconClick"
|
||||
>
|
||||
<div :class="['video-upload', !isPC && 'video-upload-h5']">
|
||||
<input
|
||||
ref="inputRef"
|
||||
title="视频"
|
||||
type="file"
|
||||
data-type="video"
|
||||
accept="video/*"
|
||||
@change="sendVideoInWeb"
|
||||
>
|
||||
<input ref="inputRef" title="视频" type="file" data-type="video" accept="video/*" @change="sendVideoInWeb" />
|
||||
</div>
|
||||
</ToolbarItemContainer>
|
||||
</template>
|
||||
@ -31,7 +19,7 @@ import TUIChatEngine, {
|
||||
StoreName,
|
||||
IConversationModel,
|
||||
SendMessageParams,
|
||||
SendMessageOptions,
|
||||
SendMessageOptions
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import { ref } from '../../../../adapter-vue';
|
||||
@ -51,8 +39,8 @@ const props = defineProps({
|
||||
// camera: Take a video using the camera
|
||||
videoSourceType: {
|
||||
type: String,
|
||||
default: 'album',
|
||||
},
|
||||
default: 'album'
|
||||
}
|
||||
});
|
||||
|
||||
const inputRef = ref();
|
||||
@ -61,7 +49,7 @@ const currentConversation = ref<IConversationModel>();
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversation: (conversation: IConversationModel) => {
|
||||
currentConversation.value = conversation;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const handleIcon = (): string => {
|
||||
@ -99,7 +87,7 @@ const onIconClick = () => {
|
||||
maxDuration: 60,
|
||||
success: function (res: any) {
|
||||
sendVideoMessage(res);
|
||||
},
|
||||
}
|
||||
});
|
||||
} else {
|
||||
TUIGlobal?.chooseVideo({
|
||||
@ -108,7 +96,7 @@ const onIconClick = () => {
|
||||
compressed: false,
|
||||
success: function (res: any) {
|
||||
sendVideoMessage(res);
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@ -129,27 +117,25 @@ const sendVideoMessage = (file: any) => {
|
||||
return;
|
||||
}
|
||||
const options = {
|
||||
to:
|
||||
currentConversation?.value?.groupProfile?.groupID
|
||||
|| currentConversation?.value?.userProfile?.userID,
|
||||
to: currentConversation?.value?.groupProfile?.groupID || currentConversation?.value?.userProfile?.userID,
|
||||
conversationType: currentConversation?.value?.type,
|
||||
payload: {
|
||||
file,
|
||||
file
|
||||
},
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal()
|
||||
} as SendMessageParams;
|
||||
const offlinePushInfoCreateParams: IOfflinePushInfoCreateParams = {
|
||||
conversation: currentConversation.value,
|
||||
payload: options.payload,
|
||||
messageType: TUIChatEngine.TYPES.MSG_VIDEO,
|
||||
messageType: TUIChatEngine.TYPES.MSG_VIDEO
|
||||
};
|
||||
const sendMessageOptions: SendMessageOptions = {
|
||||
offlinePushInfo: OfflinePushInfoManager.create(offlinePushInfoCreateParams),
|
||||
offlinePushInfo: OfflinePushInfoManager.create(offlinePushInfoCreateParams)
|
||||
};
|
||||
TUIChatService.sendVideoMessage(options, sendMessageOptions);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../../assets/styles/common";
|
||||
@import '../../../../assets/styles/common';
|
||||
</style>
|
||||
|
@ -1,2 +1,2 @@
|
||||
import Words from "./index.vue";
|
||||
import Words from './index.vue';
|
||||
export default Words;
|
||||
|
@ -12,23 +12,12 @@
|
||||
<div :class="['words', !isPC && 'words-h5']">
|
||||
<div :class="['words-header', !isPC && 'words-h5-header']">
|
||||
<span :class="['words-header-title', !isPC && 'words-h5-header-title']">
|
||||
{{ TUITranslateService.t("Words.常用语-快捷回复工具") }}
|
||||
</span>
|
||||
<span
|
||||
v-if="!isPC"
|
||||
:class="['words-header-close', !isPC && 'words-h5-header-close']"
|
||||
@click="closeDialog"
|
||||
>
|
||||
关闭
|
||||
{{ TUITranslateService.t('Words.常用语-快捷回复工具') }}
|
||||
</span>
|
||||
<span v-if="!isPC" :class="['words-header-close', !isPC && 'words-h5-header-close']" @click="closeDialog"> 关闭 </span>
|
||||
</div>
|
||||
<ul :class="['words-list', !isPC && 'words-h5-list']">
|
||||
<li
|
||||
v-for="(item, index) in wordsList"
|
||||
:key="index"
|
||||
:class="['words-list-item', !isPC && 'words-h5-list-item']"
|
||||
@click="selectWord(item)"
|
||||
>
|
||||
<li v-for="(item, index) in wordsList" :key="index" :class="['words-list-item', !isPC && 'words-h5-list-item']" @click="selectWord(item)">
|
||||
{{ TUITranslateService.t(`Words.${item.value}`) }}
|
||||
</li>
|
||||
</ul>
|
||||
@ -36,14 +25,7 @@
|
||||
</ToolbarItemContainer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
TUITranslateService,
|
||||
TUIStore,
|
||||
StoreName,
|
||||
IConversationModel,
|
||||
SendMessageParams,
|
||||
TUIChatService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUITranslateService, TUIStore, StoreName, IConversationModel, SendMessageParams, TUIChatService } from '@tencentcloud/chat-uikit-engine';
|
||||
import { ref } from '../../../../adapter-vue';
|
||||
import ToolbarItemContainer from '../toolbar-item-container/index.vue';
|
||||
import wordsIconLight from '../../../../assets/icon/words-light.svg';
|
||||
@ -61,19 +43,17 @@ const container = ref();
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversation: (conversation: IConversationModel) => {
|
||||
currentConversation.value = conversation;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const selectWord = (item: any) => {
|
||||
const options = {
|
||||
to:
|
||||
currentConversation?.value?.groupProfile?.groupID
|
||||
|| currentConversation?.value?.userProfile?.userID,
|
||||
to: currentConversation?.value?.groupProfile?.groupID || currentConversation?.value?.userProfile?.userID,
|
||||
conversationType: currentConversation?.value?.type,
|
||||
payload: {
|
||||
text: TUITranslateService.t(`Words.${item.value}`),
|
||||
text: TUITranslateService.t(`Words.${item.value}`)
|
||||
},
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal()
|
||||
} as SendMessageParams;
|
||||
TUIChatService.sendTextMessage(options);
|
||||
// close dialog after submit evaluate
|
||||
|
@ -14,27 +14,15 @@
|
||||
@onTyping="onTyping"
|
||||
@onAt="onAt"
|
||||
/>
|
||||
<MessageInputButton
|
||||
v-if="!props.isMuted"
|
||||
@sendMessage="sendMessage"
|
||||
/>
|
||||
<MessageInputAt
|
||||
v-if="props.enableAt"
|
||||
ref="messageInputAtRef"
|
||||
@insertAt="insertAt"
|
||||
@onAtListOpen="onAtListOpen"
|
||||
/>
|
||||
<MessageInputButton v-if="!props.isMuted" @sendMessage="sendMessage" />
|
||||
<MessageInputAt v-if="props.enableAt" ref="messageInputAtRef" @insertAt="insertAt" @onAtListOpen="onAtListOpen" />
|
||||
</div>
|
||||
<MessageInputQuote />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
IConversationModel,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIStore, StoreName, IConversationModel } from '@tencentcloud/chat-uikit-engine';
|
||||
import { ref } from '../../../adapter-vue';
|
||||
import MessageInputEditor from './message-input-editor.vue';
|
||||
import MessageInputAt from './message-input-at/index.vue';
|
||||
@ -47,32 +35,32 @@ import { isPC, isH5 } from '../../../utils/env';
|
||||
const props = defineProps({
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: 'this is placeholder',
|
||||
default: 'this is placeholder'
|
||||
},
|
||||
isMuted: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
muteText: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: ''
|
||||
},
|
||||
enableInput: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
enableAt: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
enableDragUpload: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
enableTyping: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['sendMessage', 'resetReplyOrReference', 'onTyping']);
|
||||
@ -83,7 +71,7 @@ const currentConversation = ref<IConversationModel>();
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversation: (conversation: IConversationModel) => {
|
||||
currentConversation.value = conversation;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const onTyping = (inputContentEmpty: boolean, inputBlur: boolean) => {
|
||||
@ -103,10 +91,7 @@ const sendMessage = async () => {
|
||||
}
|
||||
return editor;
|
||||
});
|
||||
await sendMessages(
|
||||
editorContentList,
|
||||
currentConversation.value,
|
||||
);
|
||||
await sendMessages(editorContentList, currentConversation.value);
|
||||
emit('sendMessage');
|
||||
editor.value?.resetEditor();
|
||||
};
|
||||
@ -132,12 +117,12 @@ const reEdit = (content: any) => {
|
||||
|
||||
defineExpose({
|
||||
insertEmoji,
|
||||
reEdit,
|
||||
reEdit
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../../assets/styles/common";
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
.message-input-wrapper {
|
||||
box-sizing: border-box;
|
||||
|
@ -1,8 +1,5 @@
|
||||
import { SuggestionProps, SuggestionKeyDownProps } from '@tiptap/suggestion';
|
||||
import TUIChatEngine, {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import TUIChatEngine, { TUIStore, StoreName } from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import { isH5 } from '../../../../utils/env';
|
||||
|
||||
@ -13,17 +10,13 @@ let showMemberList: any[] = [];
|
||||
let selectedIndex: number = 0;
|
||||
let isGroup = false;
|
||||
let command:
|
||||
| ((props: {
|
||||
id?: string | undefined;
|
||||
userID?: string | undefined;
|
||||
isAll?: boolean | undefined;
|
||||
}) => void)
|
||||
| ((props: { id?: string | undefined; userID?: string | undefined; isAll?: boolean | undefined }) => void)
|
||||
| ((arg0: { id: any; label: any }) => any);
|
||||
const all = {
|
||||
userID: TUIChatEngine.TYPES.MSG_AT_ALL,
|
||||
nick: '所有人',
|
||||
isAll: true,
|
||||
avatar: 'https://web.sdk.qcloud.com/im/assets/images/at.svg',
|
||||
avatar: 'https://web.sdk.qcloud.com/im/assets/images/at.svg'
|
||||
};
|
||||
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
@ -40,7 +33,7 @@ TUIStore.watch(StoreName.CONV, {
|
||||
showMemberList = [];
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.CUSTOM, {
|
||||
@ -50,7 +43,7 @@ TUIStore.watch(StoreName.CUSTOM, {
|
||||
allMemberList = [all, ...memberList];
|
||||
showMemberList = allMemberList;
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const MessageInputAtSuggestion = () => {
|
||||
@ -62,8 +55,7 @@ const MessageInputAtSuggestion = () => {
|
||||
}
|
||||
const queryResult = allMemberList?.filter(
|
||||
(item: { nick: string; userID: string }) =>
|
||||
item?.nick?.toLowerCase()?.startsWith(props?.query?.toLowerCase())
|
||||
|| item?.userID?.toLowerCase()?.startsWith(props?.query?.toLowerCase()),
|
||||
item?.nick?.toLowerCase()?.startsWith(props?.query?.toLowerCase()) || item?.userID?.toLowerCase()?.startsWith(props?.query?.toLowerCase())
|
||||
);
|
||||
showMemberList = queryResult?.length ? queryResult : allMemberList;
|
||||
TUIGlobal.setShowMemberList(showMemberList);
|
||||
@ -76,7 +68,7 @@ const MessageInputAtSuggestion = () => {
|
||||
id?: string;
|
||||
userID?: string;
|
||||
isAll?: boolean;
|
||||
}>,
|
||||
}>
|
||||
) => {
|
||||
if (!isGroup) {
|
||||
return;
|
||||
@ -89,7 +81,7 @@ const MessageInputAtSuggestion = () => {
|
||||
if (rect?.left && rect?.top && !isH5) {
|
||||
TUIGlobal.handleAtListPosition({
|
||||
left: rect?.left,
|
||||
top: rect?.top,
|
||||
top: rect?.top
|
||||
});
|
||||
}
|
||||
command = props.command;
|
||||
@ -106,7 +98,7 @@ const MessageInputAtSuggestion = () => {
|
||||
if (rect?.left && rect?.top && !isH5) {
|
||||
TUIGlobal.handleAtListPosition({
|
||||
left: rect?.left,
|
||||
top: rect?.top,
|
||||
top: rect?.top
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -147,18 +139,17 @@ const MessageInputAtSuggestion = () => {
|
||||
showMemberList = allMemberList;
|
||||
TUIGlobal.handleAtListPosition({
|
||||
left: 0,
|
||||
top: 0,
|
||||
top: 0
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const upHandler = () => {
|
||||
if (!showMemberList?.length) return;
|
||||
selectedIndex
|
||||
= (selectedIndex + showMemberList?.length - 1) % showMemberList?.length;
|
||||
selectedIndex = (selectedIndex + showMemberList?.length - 1) % showMemberList?.length;
|
||||
TUIGlobal.setCurrentSelectIndex(selectedIndex);
|
||||
};
|
||||
|
||||
@ -176,10 +167,10 @@ const selectItem = (index: number) => {
|
||||
if (!showMemberList?.length) return;
|
||||
const item = showMemberList[index];
|
||||
if (item) {
|
||||
command
|
||||
&& command({
|
||||
command &&
|
||||
command({
|
||||
id: (item as any)?.userID,
|
||||
label: (item as any)?.nick || (item as any)?.userID,
|
||||
label: (item as any)?.nick || (item as any)?.userID
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -1,23 +1,9 @@
|
||||
<template>
|
||||
<BottomPopup
|
||||
:show="showAtList"
|
||||
@onClose="closeAt"
|
||||
>
|
||||
<div
|
||||
ref="MessageInputAt"
|
||||
:class="[isPC ? 'message-input-at' : 'message-input-at-h5']"
|
||||
>
|
||||
<div
|
||||
ref="dialog"
|
||||
class="member-list"
|
||||
>
|
||||
<header
|
||||
v-if="!isPC"
|
||||
class="member-list-title"
|
||||
>
|
||||
<span class="title">{{
|
||||
TUITranslateService.t("TUIChat.选择提醒的人")
|
||||
}}</span>
|
||||
<BottomPopup :show="showAtList" @onClose="closeAt">
|
||||
<div ref="MessageInputAt" :class="[isPC ? 'message-input-at' : 'message-input-at-h5']">
|
||||
<div ref="dialog" class="member-list">
|
||||
<header v-if="!isPC" class="member-list-title">
|
||||
<span class="title">{{ TUITranslateService.t('TUIChat.选择提醒的人') }}</span>
|
||||
</header>
|
||||
<ul class="member-list-box">
|
||||
<li
|
||||
@ -28,10 +14,7 @@
|
||||
:class="[index === selectedIndex && 'selected']"
|
||||
@click="selectItem(index)"
|
||||
>
|
||||
<img
|
||||
class="member-list-box-body-avatar"
|
||||
:src="handleMemberAvatar(item)"
|
||||
>
|
||||
<img class="member-list-box-body-avatar" :src="handleMemberAvatar(item)" />
|
||||
<span class="member-list-box-body-name">
|
||||
{{ handleMemberName(item) }}
|
||||
</span>
|
||||
@ -42,12 +25,7 @@
|
||||
</BottomPopup>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TUIChatEngine, {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUIGroupService,
|
||||
TUITranslateService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import TUIChatEngine, { TUIStore, StoreName, TUIGroupService, TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import { ref, watch } from '../../../../adapter-vue';
|
||||
import { isPC, isH5 } from '../../../../utils/env';
|
||||
@ -65,7 +43,7 @@ const showMemberList = ref<Array<any>>();
|
||||
const isGroup = ref(false);
|
||||
const position = ref({
|
||||
left: 0,
|
||||
top: 0,
|
||||
top: 0
|
||||
});
|
||||
const selectedIndex = ref(0);
|
||||
const currentConversationID = ref('');
|
||||
@ -74,7 +52,7 @@ const all = {
|
||||
userID: TUIChatEngine.TYPES.MSG_AT_ALL,
|
||||
nick: '所有人',
|
||||
isAll: true,
|
||||
avatar: 'https://web.sdk.qcloud.com/im/assets/images/at.svg',
|
||||
avatar: 'https://web.sdk.qcloud.com/im/assets/images/at.svg'
|
||||
};
|
||||
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
@ -94,7 +72,7 @@ TUIStore.watch(StoreName.CONV, {
|
||||
TUIGroupService.switchGroup('');
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.GRP, {
|
||||
@ -103,7 +81,7 @@ TUIStore.watch(StoreName.GRP, {
|
||||
allMemberList.value = [all, ...memberList.value];
|
||||
showMemberList.value = allMemberList.value;
|
||||
TUIStore.update(StoreName.CUSTOM, 'memberList', memberList.value);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const toggleAtList = (show: boolean) => {
|
||||
@ -132,7 +110,7 @@ TUIGlobal.setCurrentSelectIndex = setCurrentSelectIndex;
|
||||
TUIGlobal.setShowMemberList = setShowMemberList;
|
||||
|
||||
defineExpose({
|
||||
toggleAtList,
|
||||
toggleAtList
|
||||
});
|
||||
|
||||
watch(
|
||||
@ -142,9 +120,8 @@ watch(
|
||||
return;
|
||||
}
|
||||
MessageInputAt.value.style.left = position.value.left + 'px';
|
||||
MessageInputAt.value.style.top
|
||||
= position.value.top - MessageInputAt.value.clientHeight + 'px';
|
||||
},
|
||||
MessageInputAt.value.style.top = position.value.top - MessageInputAt.value.clientHeight + 'px';
|
||||
}
|
||||
);
|
||||
|
||||
const closeAt = () => {
|
||||
@ -152,7 +129,7 @@ const closeAt = () => {
|
||||
showMemberList.value = allMemberList.value;
|
||||
position.value = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
top: 0
|
||||
};
|
||||
};
|
||||
|
||||
@ -164,7 +141,7 @@ const selectItem = (index: number) => {
|
||||
const item = showMemberList?.value[index];
|
||||
emits('insertAt', {
|
||||
id: (item as any)?.userID,
|
||||
label: (item as any)?.nick || (item as any)?.userID,
|
||||
label: (item as any)?.nick || (item as any)?.userID
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -172,10 +149,7 @@ const selectItem = (index: number) => {
|
||||
};
|
||||
|
||||
const handleMemberAvatar = (item: any) => {
|
||||
return (
|
||||
(item as any)?.avatar
|
||||
|| 'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'
|
||||
);
|
||||
return (item as any)?.avatar || 'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png';
|
||||
};
|
||||
|
||||
const handleMemberName = (item: any) => {
|
||||
@ -183,7 +157,7 @@ const handleMemberName = (item: any) => {
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@import "../../../../assets/styles/common";
|
||||
@import '../../../../assets/styles/common';
|
||||
|
||||
.message-input-at {
|
||||
position: fixed;
|
||||
|
@ -1,19 +1,10 @@
|
||||
<template>
|
||||
<div :class="['message-input-button', !isPC && 'message-input-button-h5']">
|
||||
<button
|
||||
v-if="props.enableSend"
|
||||
class="message-input-button-cont"
|
||||
data-type="text"
|
||||
:disabled="false"
|
||||
@click="sendMessage"
|
||||
>
|
||||
<p
|
||||
v-if="displayHover"
|
||||
class="message-input-button-hover"
|
||||
>
|
||||
{{ TUITranslateService.t("TUIChat.按Enter发送,Ctrl+Enter换行") }}
|
||||
<button v-if="props.enableSend" class="message-input-button-cont" data-type="text" :disabled="false" @click="sendMessage">
|
||||
<p v-if="displayHover" class="message-input-button-hover">
|
||||
{{ TUITranslateService.t('TUIChat.按Enter发送,Ctrl+Enter换行') }}
|
||||
</p>
|
||||
{{ TUITranslateService.t("发送") }}
|
||||
{{ TUITranslateService.t('发送') }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@ -27,8 +18,8 @@ import TUIChatConfig from '../config';
|
||||
const props = defineProps({
|
||||
enableSend: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
const displayHover = ref(TUIChatConfig.getChatType() !== TUIConstants.TUIChat.TYPE.ROOM);
|
||||
@ -40,7 +31,7 @@ const sendMessage = () => {
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@import "../../../assets/styles/common";
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
.message-input-button {
|
||||
position: absolute;
|
||||
@ -84,7 +75,7 @@ const sendMessage = () => {
|
||||
opacity: 0.3;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
@ -1,11 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
:class="['message-input-editor-container', isH5 && 'message-input-editor-container-h5']"
|
||||
>
|
||||
<div
|
||||
v-if="isMuted"
|
||||
class="message-input-mute"
|
||||
>
|
||||
<div :class="['message-input-editor-container', isH5 && 'message-input-editor-container-h5']">
|
||||
<div v-if="isMuted" class="message-input-mute">
|
||||
{{ muteText }}
|
||||
</div>
|
||||
<div
|
||||
@ -24,11 +19,7 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { toRefs, ref, onMounted, watch, onUnmounted } from '../../../adapter-vue';
|
||||
import {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
IMessageModel,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIStore, StoreName, IMessageModel } from '@tencentcloud/chat-uikit-engine';
|
||||
import { Editor, JSONContent, Extension } from '@tiptap/core';
|
||||
import Document from '@tiptap/extension-document';
|
||||
import Paragraph from '@tiptap/extension-paragraph';
|
||||
@ -47,36 +38,36 @@ import DraftManager from '../utils/conversationDraft';
|
||||
const props = defineProps({
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: 'this is placeholder',
|
||||
default: 'this is placeholder'
|
||||
},
|
||||
replayOrReferenceMessage: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
default: () => ({})
|
||||
},
|
||||
isMuted: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
muteText: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: ''
|
||||
},
|
||||
enableInput: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
enableAt: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
enableDragUpload: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
enableTyping: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
const emits = defineEmits(['sendMessage', 'onTyping', 'onAt']);
|
||||
@ -92,9 +83,9 @@ const fileMap = new Map<string, any>();
|
||||
const DisableDefaultEnter = Extension.create({
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
Enter: () => true,
|
||||
Enter: () => true
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
function onCurrentConversationIDUpdated(conversationID: string) {
|
||||
@ -104,7 +95,7 @@ function onCurrentConversationIDUpdated(conversationID: string) {
|
||||
currentConversationID.value,
|
||||
getEditorHTML(),
|
||||
DraftManager.generateAbstract(getEditorContent()),
|
||||
currentQuoteMessage.value,
|
||||
currentQuoteMessage.value
|
||||
);
|
||||
}
|
||||
resetEditor();
|
||||
@ -131,18 +122,18 @@ onMounted(() => {
|
||||
DisableDefaultEnter,
|
||||
Placeholder.configure({
|
||||
emptyEditorClass: 'is-editor-empty',
|
||||
placeholder: placeholder.value,
|
||||
placeholder: placeholder.value
|
||||
}),
|
||||
Mention.configure({
|
||||
HTMLAttributes: {
|
||||
class: 'mention',
|
||||
class: 'mention'
|
||||
},
|
||||
suggestion: enableAt.value && (MessageInputAtSuggestion() as any),
|
||||
suggestion: enableAt.value && (MessageInputAtSuggestion() as any)
|
||||
}),
|
||||
CustomImage.configure({
|
||||
inline: true,
|
||||
allowBase64: true,
|
||||
}),
|
||||
allowBase64: true
|
||||
})
|
||||
],
|
||||
autofocus: !isH5,
|
||||
editable: true,
|
||||
@ -151,7 +142,7 @@ onMounted(() => {
|
||||
handlePaste() {
|
||||
// prevent editor's default paste for resolve emoji & marked down line break
|
||||
return true;
|
||||
},
|
||||
}
|
||||
},
|
||||
// handle input editor typing (only in C2C and enable typing)
|
||||
onUpdate({ editor, transaction }) {
|
||||
@ -167,12 +158,8 @@ onMounted(() => {
|
||||
if (isH5 && document?.getElementById('app')?.style) {
|
||||
// set app height when keyboard popup
|
||||
const keyboardHeight = document.body.scrollHeight - window.innerHeight;
|
||||
(
|
||||
document.getElementById('app') as any
|
||||
).style.marginBottom = `${keyboardHeight}px`;
|
||||
(
|
||||
document.getElementById('app') as any
|
||||
).style.height = `calc(100% - ${keyboardHeight}px)`;
|
||||
(document.getElementById('app') as any).style.marginBottom = `${keyboardHeight}px`;
|
||||
(document.getElementById('app') as any).style.height = `calc(100% - ${keyboardHeight}px)`;
|
||||
}
|
||||
if (!enableTyping.value || !isC2C.value) return;
|
||||
isEditorBlur.value = true;
|
||||
@ -185,31 +172,31 @@ onMounted(() => {
|
||||
}
|
||||
if (!enableTyping.value || !isC2C.value) return;
|
||||
isEditorBlur.value = true;
|
||||
},
|
||||
}
|
||||
})
|
||||
: null;
|
||||
|
||||
if (isH5) {
|
||||
const targetBottomDom = document.querySelector('.message-input-toolbar') as HTMLElement || editorDom.value;
|
||||
const targetBottomDom = (document.querySelector('.message-input-toolbar') as HTMLElement) || editorDom.value;
|
||||
riseInput(editorDom.value, targetBottomDom);
|
||||
}
|
||||
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversationID: onCurrentConversationIDUpdated,
|
||||
currentConversationID: onCurrentConversationIDUpdated
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.CHAT, {
|
||||
quoteMessage: onQuoteMessageUpdated,
|
||||
quoteMessage: onQuoteMessageUpdated
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CONV, {
|
||||
currentConversationID: onCurrentConversationIDUpdated,
|
||||
currentConversationID: onCurrentConversationIDUpdated
|
||||
});
|
||||
|
||||
TUIStore.unwatch(StoreName.CHAT, {
|
||||
quoteMessage: onQuoteMessageUpdated,
|
||||
quoteMessage: onQuoteMessageUpdated
|
||||
});
|
||||
|
||||
// clear map store
|
||||
@ -289,25 +276,19 @@ async function handleFileDropOrPaste(e: any, type: string) {
|
||||
if (!enableDragUpload.value && type === 'drop') {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
(type === 'drop' && e.dataTransfer)
|
||||
|| (type === 'paste' && e.clipboardData)
|
||||
) {
|
||||
const files
|
||||
= type === 'drop' ? e?.dataTransfer?.files : e?.clipboardData?.files;
|
||||
if ((type === 'drop' && e.dataTransfer) || (type === 'paste' && e.clipboardData)) {
|
||||
const files = type === 'drop' ? e?.dataTransfer?.files : e?.clipboardData?.files;
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
const isImage = file.type.startsWith('image/');
|
||||
const fileSrc = isImage
|
||||
? URL.createObjectURL(file)
|
||||
: await drawFileCanvasToImageUrl(file);
|
||||
const fileSrc = isImage ? URL.createObjectURL(file) : await drawFileCanvasToImageUrl(file);
|
||||
editor?.commands?.insertContent({
|
||||
type: 'custom-image',
|
||||
attrs: {
|
||||
src: fileSrc,
|
||||
alt: file?.name,
|
||||
class: isImage ? 'normal' : 'file',
|
||||
},
|
||||
class: isImage ? 'normal' : 'file'
|
||||
}
|
||||
});
|
||||
fileMap.set(fileSrc, file);
|
||||
if (i === files.length - 1) {
|
||||
@ -375,18 +356,7 @@ const drawFileCanvasToImageUrl = async (file: any) => {
|
||||
|
||||
const handleFileIconForShow = (type: string) => {
|
||||
const urlBase = 'https://web.sdk.qcloud.com/component/TUIKit/assets/file-';
|
||||
const fileTypes = [
|
||||
'image',
|
||||
'pdf',
|
||||
'text',
|
||||
'ppt',
|
||||
'presentation',
|
||||
'sheet',
|
||||
'zip',
|
||||
'word',
|
||||
'video',
|
||||
'unknown',
|
||||
];
|
||||
const fileTypes = ['image', 'pdf', 'text', 'ppt', 'presentation', 'sheet', 'zip', 'word', 'video', 'unknown'];
|
||||
let url = '';
|
||||
let iconType = '';
|
||||
fileTypes.forEach((typeName: string) => {
|
||||
@ -397,7 +367,7 @@ const handleFileIconForShow = (type: string) => {
|
||||
});
|
||||
return {
|
||||
iconSrc: url ? url : urlBase + 'unknown.svg',
|
||||
iconType: iconType ? iconType : 'unknown',
|
||||
iconType: iconType ? iconType : 'unknown'
|
||||
};
|
||||
};
|
||||
|
||||
@ -431,16 +401,13 @@ function parsePCEditorContent(): ITipTapEditorContent[] {
|
||||
const content: ITipTapEditorContent[] = [];
|
||||
handleEditorContent(editorJSON, content);
|
||||
if (
|
||||
content.length > 0
|
||||
&& content[content.length - 1]
|
||||
&& content[content.length - 1].type === 'text'
|
||||
&& content[content.length - 1].payload?.text?.endsWith('\n')
|
||||
content.length > 0 &&
|
||||
content[content.length - 1] &&
|
||||
content[content.length - 1].type === 'text' &&
|
||||
content[content.length - 1].payload?.text?.endsWith('\n')
|
||||
) {
|
||||
const text = content[content.length - 1].payload.text || '';
|
||||
content[content.length - 1].payload.text = text?.substring(
|
||||
0,
|
||||
text.lastIndexOf('\n'),
|
||||
);
|
||||
content[content.length - 1].payload.text = text?.substring(0, text.lastIndexOf('\n'));
|
||||
}
|
||||
return content;
|
||||
}
|
||||
@ -449,11 +416,7 @@ function handleEditorContent(root: JSONContent, content: ITipTapEditorContent[])
|
||||
if (!root || !root.type) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
root.type !== 'text'
|
||||
&& root.type !== 'custom-image'
|
||||
&& root.type !== 'mention'
|
||||
) {
|
||||
if (root.type !== 'text' && root.type !== 'custom-image' && root.type !== 'mention') {
|
||||
if (root.type === 'paragraph' || root.type === 'hardBreak') {
|
||||
handleEditorNode(root, content);
|
||||
}
|
||||
@ -471,69 +434,49 @@ function handleEditorContent(root: JSONContent, content: ITipTapEditorContent[])
|
||||
function handleEditorNode(node: JSONContent, content: ITipTapEditorContent[]) {
|
||||
// handle enter
|
||||
if (node.type === 'hardBreak') {
|
||||
if (content.length > 0
|
||||
&& content[content.length - 1]
|
||||
&& content[content.length - 1]?.type === 'text'
|
||||
) {
|
||||
if (content.length > 0 && content[content.length - 1] && content[content.length - 1]?.type === 'text') {
|
||||
content[content.length - 1].payload.text += '\n';
|
||||
} else {
|
||||
content.push({
|
||||
type: 'text',
|
||||
payload: { text: '\n' },
|
||||
payload: { text: '\n' }
|
||||
});
|
||||
}
|
||||
} else if (node.type === 'paragraph') {
|
||||
if (
|
||||
content.length > 0
|
||||
&& content[content.length - 1]
|
||||
&& content[content.length - 1]?.type === 'text'
|
||||
) {
|
||||
if (content.length > 0 && content[content.length - 1] && content[content.length - 1]?.type === 'text') {
|
||||
content[content.length - 1].payload.text += '\n';
|
||||
}
|
||||
} else if (
|
||||
node.type === 'text'
|
||||
|| (node.type === 'custom-image' && node?.attrs?.class?.includes('emoji'))
|
||||
) {
|
||||
} else if (node.type === 'text' || (node.type === 'custom-image' && node?.attrs?.class?.includes('emoji'))) {
|
||||
// Process text and emojis
|
||||
const text = node.type === 'text' ? node?.text : node?.attrs?.alt;
|
||||
if (
|
||||
content.length > 0
|
||||
&& content[content.length - 1]
|
||||
&& content[content.length - 1]?.type === 'text'
|
||||
) {
|
||||
if (content.length > 0 && content[content.length - 1] && content[content.length - 1]?.type === 'text') {
|
||||
content[content.length - 1].payload.text += text;
|
||||
} else {
|
||||
content.push({
|
||||
type: 'text',
|
||||
payload: { text: text },
|
||||
payload: { text: text }
|
||||
});
|
||||
}
|
||||
} else if (
|
||||
node.type === 'custom-image' && node?.attrs?.class?.includes('normal')
|
||||
) {
|
||||
} else if (node.type === 'custom-image' && node?.attrs?.class?.includes('normal')) {
|
||||
// Process rich text images
|
||||
content.push({
|
||||
type: 'image',
|
||||
payload: { file: fileMap?.get(node?.attrs?.src) },
|
||||
payload: { file: fileMap?.get(node?.attrs?.src) }
|
||||
});
|
||||
} else if (node.type === 'custom-image' && node?.attrs?.class?.includes('file')) {
|
||||
const file = fileMap?.get(node?.attrs?.src);
|
||||
content.push({
|
||||
type: file?.type?.includes('video') ? 'video' : 'file',
|
||||
payload: { file },
|
||||
payload: { file }
|
||||
});
|
||||
} else if (node.type === 'mention') {
|
||||
const text = '@' + node?.attrs?.label + ' ';
|
||||
if (
|
||||
content.length > 0
|
||||
&& content[content.length - 1]
|
||||
&& content[content.length - 1]?.type === 'text'
|
||||
) {
|
||||
if (content.length > 0 && content[content.length - 1] && content[content.length - 1]?.type === 'text') {
|
||||
content[content.length - 1].payload.text += text;
|
||||
} else {
|
||||
content.push({
|
||||
type: 'text',
|
||||
payload: { text: text },
|
||||
payload: { text: text }
|
||||
});
|
||||
}
|
||||
if (content[content.length - 1]?.payload?.atUserList) {
|
||||
@ -551,10 +494,10 @@ function parseH5EditorContent() {
|
||||
try {
|
||||
for (const child of root.childNodes) {
|
||||
if (
|
||||
child.nodeName === '#text'
|
||||
|| child.nodeName === 'SPAN'
|
||||
|| (child as HTMLElement).classList?.contains('custom-image-emoji')
|
||||
|| (child as HTMLElement).classList?.contains('mention')
|
||||
child.nodeName === '#text' ||
|
||||
child.nodeName === 'SPAN' ||
|
||||
(child as HTMLElement).classList?.contains('custom-image-emoji') ||
|
||||
(child as HTMLElement).classList?.contains('mention')
|
||||
) {
|
||||
text += child.nodeValue || (child as any).alt || child.innerHTML || '';
|
||||
if (child.classList?.contains('mention') && child.id && !atUserList?.includes(child.id)) {
|
||||
@ -572,9 +515,9 @@ function parseH5EditorContent() {
|
||||
type: 'text',
|
||||
payload: {
|
||||
text,
|
||||
atUserList,
|
||||
},
|
||||
},
|
||||
atUserList
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@ -586,8 +529,8 @@ function addEmoji(emojiData: any) {
|
||||
src: emojiData?.url,
|
||||
alt: emojiData?.emoji.key,
|
||||
title: emojiData?.emoji.key,
|
||||
class: 'emoji',
|
||||
},
|
||||
class: 'emoji'
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const emojiImgNode = document.createElement('img');
|
||||
@ -714,8 +657,8 @@ watch(
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
);
|
||||
|
||||
defineExpose({
|
||||
@ -726,12 +669,12 @@ defineExpose({
|
||||
setEditorContent,
|
||||
getEditorHTML,
|
||||
insertEditorContent,
|
||||
blur,
|
||||
blur
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../../assets/styles/common";
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
.message-input-editor {
|
||||
&-container {
|
||||
@ -833,7 +776,6 @@ defineExpose({
|
||||
}
|
||||
|
||||
img {
|
||||
|
||||
/* stylelint-disable-next-line selector-class-pattern */
|
||||
&.ProseMirror-selectednode {
|
||||
outline: 2px solid #68cef8;
|
||||
|
@ -8,31 +8,30 @@ export default Image.extend({
|
||||
...(Image.config as any).addAttributes(),
|
||||
class: {
|
||||
default: 'image',
|
||||
rendered: false,
|
||||
},
|
||||
rendered: false
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
setImage: options => ({ tr, commands }) => {
|
||||
setImage:
|
||||
(options) =>
|
||||
({ tr, commands }) => {
|
||||
if ((tr.selection as any)?.node?.type?.name == 'custom-image') {
|
||||
return commands.updateAttributes('custom-image', options);
|
||||
} else {
|
||||
return commands.insertContent({
|
||||
type: this.name,
|
||||
attrs: options,
|
||||
attrs: options
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
renderHTML({ node, HTMLAttributes }) {
|
||||
HTMLAttributes.class = (node.attrs.class?.includes('custom-image-') ? '' : 'custom-image-') + node.attrs.class;
|
||||
return [
|
||||
'img',
|
||||
HTMLAttributes,
|
||||
];
|
||||
},
|
||||
return ['img', HTMLAttributes];
|
||||
}
|
||||
});
|
||||
|
@ -4,32 +4,19 @@
|
||||
:class="{
|
||||
'input-quote-container': true,
|
||||
'input-quote-container-uni': isUniFrameWork,
|
||||
'input-quote-container-h5': isH5,
|
||||
'input-quote-container-h5': isH5
|
||||
}"
|
||||
>
|
||||
<div class="input-quote-content">
|
||||
<div class="max-one-line">
|
||||
{{ quoteMessage.nick || quoteMessage.from }}: {{ quoteContentText }}
|
||||
</div>
|
||||
<Icon
|
||||
class="input-quote-close-icon"
|
||||
:file="closeIcon"
|
||||
width="11px"
|
||||
height="11px"
|
||||
@onClick="cancelQuote"
|
||||
/>
|
||||
<div class="max-one-line">{{ quoteMessage.nick || quoteMessage.from }}: {{ quoteContentText }}</div>
|
||||
<Icon class="input-quote-close-icon" :file="closeIcon" width="11px" height="11px" @onClick="cancelQuote" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from '../../../../adapter-vue';
|
||||
import TUIChatEngine, {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUITranslateService,
|
||||
IMessageModel,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import TUIChatEngine, { TUIStore, StoreName, TUITranslateService, IMessageModel } from '@tencentcloud/chat-uikit-engine';
|
||||
import Icon from '../../../common/Icon.vue';
|
||||
import closeIcon from '../../../../assets/icon/icon-close.svg';
|
||||
import { isH5, isUniFrameWork } from '../../../../utils/env';
|
||||
@ -41,7 +28,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
displayType: 'editor',
|
||||
displayType: 'editor'
|
||||
});
|
||||
|
||||
const TYPES = TUIChatEngine.TYPES;
|
||||
@ -49,13 +36,13 @@ const quoteMessage = ref<IMessageModel>();
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CHAT, {
|
||||
quoteMessage: onQuoteMessageUpdated,
|
||||
quoteMessage: onQuoteMessageUpdated
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CHAT, {
|
||||
quoteMessage: onQuoteMessageUpdated,
|
||||
quoteMessage: onQuoteMessageUpdated
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,60 +1,28 @@
|
||||
<template>
|
||||
<div
|
||||
class="tui-chat"
|
||||
:class="[isH5 ? 'tui-chat-h5' : '']"
|
||||
>
|
||||
<div class="tui-chat" :class="[isH5 ? 'tui-chat-h5' : '']">
|
||||
<!-- <JoinGroupCard v-if="isH5" /> -->
|
||||
<div
|
||||
id="tui-chat-main"
|
||||
class="tui-chat-main"
|
||||
@click="closeChatPop"
|
||||
>
|
||||
<div id="tui-chat-main" class="tui-chat-main" @click="closeChatPop">
|
||||
<!-- Safe Tips -->
|
||||
<div
|
||||
v-if="isOfficial"
|
||||
class="tui-chat-safe-tips"
|
||||
>
|
||||
<div v-if="isOfficial" class="tui-chat-safe-tips">
|
||||
<span>
|
||||
{{
|
||||
TUITranslateService.t(
|
||||
"TUIChat.【安全提示】本 APP 仅用于体验腾讯云即时通信 IM 产品功能,不可用于业务洽谈与拓展。请勿轻信汇款、中奖等涉及钱款的信息,勿轻易拨打陌生电话,谨防上当受骗。"
|
||||
'TUIChat.【安全提示】本 APP 仅用于体验腾讯云即时通信 IM 产品功能,不可用于业务洽谈与拓展。请勿轻信汇款、中奖等涉及钱款的信息,勿轻易拨打陌生电话,谨防上当受骗。'
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
<a @click="openComplaintLink(Link.complaint)">
|
||||
{{ TUITranslateService.t("TUIChat.点此投诉") }}
|
||||
{{ TUITranslateService.t('TUIChat.点此投诉') }}
|
||||
</a>
|
||||
</div>
|
||||
<MessageGroupApplication
|
||||
v-if="isGroup"
|
||||
:key="props.groupID"
|
||||
:groupID="props.groupID"
|
||||
/>
|
||||
<MessageGroupApplication v-if="isGroup" :key="props.groupID" :groupID="props.groupID" />
|
||||
<!-- Message List -->
|
||||
<ul
|
||||
id="messageScrollList"
|
||||
ref="messageListRef"
|
||||
class="tui-message-list"
|
||||
@click="onMessageListBackgroundClick"
|
||||
>
|
||||
<p
|
||||
v-if="!isCompleted"
|
||||
class="message-more"
|
||||
@click="getHistoryMessageList"
|
||||
>
|
||||
{{ TUITranslateService.t("TUIChat.查看更多") }}
|
||||
<ul id="messageScrollList" ref="messageListRef" class="tui-message-list" @click="onMessageListBackgroundClick">
|
||||
<p v-if="!isCompleted" class="message-more" @click="getHistoryMessageList">
|
||||
{{ TUITranslateService.t('TUIChat.查看更多') }}
|
||||
</p>
|
||||
<li
|
||||
v-for="(item, index) in messageList"
|
||||
:id="'tui-' + item.ID"
|
||||
:key="item.ID"
|
||||
ref="messageElementListRef"
|
||||
class="message-li"
|
||||
>
|
||||
<MessageTimestamp
|
||||
:currTime="item.time"
|
||||
:prevTime="index > 0 ? messageList[index - 1].time : 0"
|
||||
/>
|
||||
<li v-for="(item, index) in messageList" :id="'tui-' + item.ID" :key="item.ID" ref="messageElementListRef" class="message-li">
|
||||
<MessageTimestamp :currTime="item.time" :prevTime="index > 0 ? messageList[index - 1].time : 0" />
|
||||
<div class="message-item">
|
||||
<MessageTip
|
||||
v-if="item.type === TYPES.MSG_GRP_TIP || isCreateGroupCustomMessage(item)"
|
||||
@ -79,7 +47,7 @@
|
||||
<div
|
||||
v-else
|
||||
:class="{
|
||||
'message-event-bind-div': true,
|
||||
'message-event-bind-div': true
|
||||
}"
|
||||
@longpress="handleToggleMessageItem($event, item, true)"
|
||||
@click.prevent.right="handleToggleMessageItemForPC($event, item)"
|
||||
@ -100,31 +68,12 @@
|
||||
@setReadReceiptPanelVisible="setReadReceiptPanelVisible"
|
||||
>
|
||||
<template #messageElement>
|
||||
<MessageText
|
||||
v-if="item.type === TYPES.MSG_TEXT"
|
||||
:content="item.getMessageContent()"
|
||||
:messageItem="item"
|
||||
/>
|
||||
<ProgressMessage
|
||||
v-else-if="item.type === TYPES.MSG_IMAGE"
|
||||
:content="item.getMessageContent()"
|
||||
:messageItem="item"
|
||||
>
|
||||
<MessageImage
|
||||
:content="item.getMessageContent()"
|
||||
:messageItem="item"
|
||||
@previewImage="handleImagePreview"
|
||||
/>
|
||||
<MessageText v-if="item.type === TYPES.MSG_TEXT" :content="item.getMessageContent()" :messageItem="item" />
|
||||
<ProgressMessage v-else-if="item.type === TYPES.MSG_IMAGE" :content="item.getMessageContent()" :messageItem="item">
|
||||
<MessageImage :content="item.getMessageContent()" :messageItem="item" @previewImage="handleImagePreview" />
|
||||
</ProgressMessage>
|
||||
<ProgressMessage
|
||||
v-else-if="item.type === TYPES.MSG_VIDEO"
|
||||
:content="item.getMessageContent()"
|
||||
:messageItem="item"
|
||||
>
|
||||
<MessageVideo
|
||||
:content="item.getMessageContent()"
|
||||
:messageItem="item"
|
||||
/>
|
||||
<ProgressMessage v-else-if="item.type === TYPES.MSG_VIDEO" :content="item.getMessageContent()" :messageItem="item">
|
||||
<MessageVideo :content="item.getMessageContent()" :messageItem="item" />
|
||||
</ProgressMessage>
|
||||
<MessageAudio
|
||||
v-else-if="item.type === TYPES.MSG_AUDIO"
|
||||
@ -132,40 +81,19 @@
|
||||
:messageItem="item"
|
||||
@setAudioPlayed="setAudioPlayed"
|
||||
/>
|
||||
<ProgressMessage
|
||||
v-else-if="item.type === TYPES.MSG_FILE"
|
||||
:content="item.getMessageContent()"
|
||||
:messageItem="item"
|
||||
>
|
||||
<MessageFile
|
||||
:content="item.getMessageContent()"
|
||||
:messageItem="item"
|
||||
/>
|
||||
<ProgressMessage v-else-if="item.type === TYPES.MSG_FILE" :content="item.getMessageContent()" :messageItem="item">
|
||||
<MessageFile :content="item.getMessageContent()" :messageItem="item" />
|
||||
</ProgressMessage>
|
||||
<MessageRecord
|
||||
v-else-if="item.type === TYPES.MSG_MERGER"
|
||||
:renderData="item.payload"
|
||||
:messageItem="item"
|
||||
/>
|
||||
<MessageFace
|
||||
v-else-if="item.type === TYPES.MSG_FACE"
|
||||
:content="item.getMessageContent()"
|
||||
/>
|
||||
<MessageLocation
|
||||
v-else-if="item.type === TYPES.MSG_LOCATION"
|
||||
:content="item.getMessageContent()"
|
||||
/>
|
||||
<MessageRecord v-else-if="item.type === TYPES.MSG_MERGER" :renderData="item.payload" :messageItem="item" />
|
||||
<MessageFace v-else-if="item.type === TYPES.MSG_FACE" :content="item.getMessageContent()" />
|
||||
<MessageLocation v-else-if="item.type === TYPES.MSG_LOCATION" :content="item.getMessageContent()" />
|
||||
<MessageStreamMarkdown
|
||||
v-else-if="isBotMessage(item)"
|
||||
:payloadData="item.payload.data"
|
||||
:message="item"
|
||||
@onStreaming="scrollStreamMessageToBottom"
|
||||
/>
|
||||
<MessageCustom
|
||||
v-else-if="item.type === TYPES.MSG_CUSTOM"
|
||||
:content="item.getMessageContent()"
|
||||
:messageItem="item"
|
||||
/>
|
||||
<MessageCustom v-else-if="item.type === TYPES.MSG_CUSTOM" :content="item.getMessageContent()" :messageItem="item" />
|
||||
</template>
|
||||
<template #TUIEmojiPlugin>
|
||||
<TUIEmojiPlugin
|
||||
@ -192,20 +120,13 @@
|
||||
@toggleMultipleSelectMode="() => emits('toggleMultipleSelectMode')"
|
||||
>
|
||||
<template #TUIEmojiPlugin>
|
||||
<TUIEmojiPlugin
|
||||
v-if="isShowEmojiPlugin"
|
||||
:message="item"
|
||||
:emojiConfig="emojiConfig"
|
||||
/>
|
||||
<TUIEmojiPlugin v-if="isShowEmojiPlugin" :message="item" :emojiConfig="emojiConfig" />
|
||||
</template>
|
||||
</MessageTool>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ScrollButton
|
||||
ref="scrollButtonInstanceRef"
|
||||
@scrollToLatestMessage="scrollToLatestMessage"
|
||||
/>
|
||||
<ScrollButton ref="scrollButtonInstanceRef" @scrollToLatestMessage="scrollToLatestMessage" />
|
||||
<Dialog
|
||||
v-if="reSendDialogShow"
|
||||
class="resend-dialog"
|
||||
@ -217,15 +138,10 @@
|
||||
@update:show="(e) => (reSendDialogShow = e)"
|
||||
>
|
||||
<p class="delDialog-title">
|
||||
{{ TUITranslateService.t("TUIChat.确认重发该消息?") }}
|
||||
{{ TUITranslateService.t('TUIChat.确认重发该消息?') }}
|
||||
</p>
|
||||
</Dialog>
|
||||
<ImagePreviewer
|
||||
v-if="showImagePreview"
|
||||
:currentImage="currentImagePreview"
|
||||
:imageList="imageMessageList"
|
||||
@close="onImagePreviewerClose"
|
||||
/>
|
||||
<ImagePreviewer v-if="showImagePreview" :currentImage="currentImagePreview" :imageList="imageMessageList" @close="onImagePreviewerClose" />
|
||||
<ReadReceiptPanel
|
||||
v-if="isShowReadUserStatusPanel"
|
||||
:message="Object.assign({}, readStatusMessage)"
|
||||
@ -237,13 +153,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick, computed, onMounted, onUnmounted, watch } from '../../../adapter-vue';
|
||||
import TUIChatEngine, {
|
||||
IMessageModel,
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUITranslateService,
|
||||
TUIChatService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import TUIChatEngine, { IMessageModel, TUIStore, StoreName, TUITranslateService, TUIChatService } from '@tencentcloud/chat-uikit-engine';
|
||||
import TUICore, { TUIConstants } from '@tencentcloud/tui-core';
|
||||
import { outsideClick, getBoundingClientRect, getScrollInfo } from '@tencentcloud/universal-api';
|
||||
import { TUIEmojiPlugin } from '@tencentcloud/tui-emoji-plugin';
|
||||
@ -306,7 +216,7 @@ const props = withDefaults(defineProps<IProps>(), {
|
||||
isGroup: false,
|
||||
groupID: '',
|
||||
isNotInGroup: false,
|
||||
isMultipleSelectMode: false,
|
||||
isMultipleSelectMode: false
|
||||
});
|
||||
|
||||
let groupType: string | undefined;
|
||||
@ -346,7 +256,7 @@ const currentImagePreview = ref<IMessageModel>();
|
||||
const imageMessageList = computed(() =>
|
||||
messageList?.value?.filter((item: IMessageModel) => {
|
||||
return !item.isRevoked && !item.hasRiskContent && item.type === TYPES.value.MSG_IMAGE;
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
// resend message dialog
|
||||
@ -355,7 +265,7 @@ const resendMessageData = ref();
|
||||
|
||||
const isShowEmojiPlugin = computed(() => {
|
||||
const msgPopMenuExtensionList = TUICore.getExtensionList(TUIConstants.TUIChat.EXTENSION.MSG_POP_MENU.EXT_ID, {
|
||||
enabledEmojiPlugin,
|
||||
enabledEmojiPlugin
|
||||
});
|
||||
return msgPopMenuExtensionList.some((item) => {
|
||||
return item.text === 'TUIEmojiPlugin';
|
||||
@ -369,15 +279,15 @@ onMounted(() => {
|
||||
TUIStore.watch(StoreName.CHAT, {
|
||||
messageList: onMessageListUpdated,
|
||||
messageSource: onMessageSourceUpdated,
|
||||
isCompleted: isCompletedUpdated,
|
||||
isCompleted: isCompletedUpdated
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversationID: onCurrentConversationIDUpdated,
|
||||
currentConversationID: onCurrentConversationIDUpdated
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.CUSTOM, {
|
||||
isShowMessagePopMenu: isShowMessagePopMenuUpdated,
|
||||
isShowMessagePopMenu: isShowMessagePopMenuUpdated
|
||||
});
|
||||
});
|
||||
|
||||
@ -389,15 +299,15 @@ onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CHAT, {
|
||||
messageList: onMessageListUpdated,
|
||||
messageSource: onMessageSourceUpdated,
|
||||
isCompleted: isCompletedUpdated,
|
||||
isCompleted: isCompletedUpdated
|
||||
});
|
||||
|
||||
TUIStore.unwatch(StoreName.CONV, {
|
||||
currentConversationID: onCurrentConversationIDUpdated,
|
||||
currentConversationID: onCurrentConversationIDUpdated
|
||||
});
|
||||
|
||||
TUIStore.unwatch(StoreName.CUSTOM, {
|
||||
isShowMessagePopMenu: isShowMessagePopMenuUpdated,
|
||||
isShowMessagePopMenu: isShowMessagePopMenuUpdated
|
||||
});
|
||||
|
||||
messageListRef.value?.removeEventListener('scroll', handelScrollListScroll);
|
||||
@ -429,11 +339,7 @@ async function onMessageListUpdated(list: IMessageModel[]) {
|
||||
}
|
||||
const newLastMessage = messageList.value?.[messageList.value?.length - 1];
|
||||
if (messageTarget.value) {
|
||||
if (
|
||||
messageList.value?.findIndex(
|
||||
(message: IMessageModel) => message?.ID === messageTarget.value?.ID,
|
||||
) >= 0
|
||||
) {
|
||||
if (messageList.value?.findIndex((message: IMessageModel) => message?.ID === messageTarget.value?.ID) >= 0) {
|
||||
const tempMessage = messageTarget.value;
|
||||
messageTarget.value = undefined;
|
||||
await scrollToPosition({ scrollToMessage: tempMessage });
|
||||
@ -441,15 +347,12 @@ async function onMessageListUpdated(list: IMessageModel[]) {
|
||||
}
|
||||
} else if (beforeHistoryGetScrollHeight.value) {
|
||||
await scrollToPosition({
|
||||
scrollToOffset: { bottom: beforeHistoryGetScrollHeight.value },
|
||||
scrollToOffset: { bottom: beforeHistoryGetScrollHeight.value }
|
||||
});
|
||||
beforeHistoryGetScrollHeight.value = 0;
|
||||
} else if (scrollButtonInstanceRef.value?.isScrollButtonVisible && newLastMessage?.flow === 'in') {
|
||||
return;
|
||||
} else if (
|
||||
newLastMessage?.ID
|
||||
&& JSON.stringify(oldLastMessage) !== JSON.stringify(newLastMessage)
|
||||
) {
|
||||
} else if (newLastMessage?.ID && JSON.stringify(oldLastMessage) !== JSON.stringify(newLastMessage)) {
|
||||
await scrollToPosition({ scrollToBottom: true });
|
||||
} else if (hasEmojiReaction && isCurrentListInBottomPosition()) {
|
||||
await scrollToPosition({ scrollToBottom: true });
|
||||
@ -462,13 +365,11 @@ async function onMessageListUpdated(list: IMessageModel[]) {
|
||||
|
||||
function isCurrentListInBottomPosition() {
|
||||
return (
|
||||
messageListRef.value
|
||||
&& typeof messageListRef.value.scrollTop === 'number'
|
||||
&& typeof messageListRef.value.scrollHeight === 'number'
|
||||
&& typeof messageListRef.value.clientHeight === 'number'
|
||||
&& Math.ceil(
|
||||
messageListRef.value.scrollTop + messageListRef.value.clientHeight,
|
||||
) >= messageListRef.value.scrollHeight
|
||||
messageListRef.value &&
|
||||
typeof messageListRef.value.scrollTop === 'number' &&
|
||||
typeof messageListRef.value.scrollHeight === 'number' &&
|
||||
typeof messageListRef.value.clientHeight === 'number' &&
|
||||
Math.ceil(messageListRef.value.scrollTop + messageListRef.value.clientHeight) >= messageListRef.value.scrollHeight
|
||||
);
|
||||
}
|
||||
|
||||
@ -486,9 +387,7 @@ async function scrollToPosition(config: ScrollConfig = {}): Promise<void> {
|
||||
if (config.scrollToBottom) {
|
||||
container!.scrollTop = container!.scrollHeight;
|
||||
} else if (config.scrollToMessage) {
|
||||
const targetMessageDom = messageElementListRef.value?.find(
|
||||
(dom: HTMLElement) => dom?.id === `tui-${config.scrollToMessage?.ID}`,
|
||||
);
|
||||
const targetMessageDom = messageElementListRef.value?.find((dom: HTMLElement) => dom?.id === `tui-${config.scrollToMessage?.ID}`);
|
||||
if (targetMessageDom?.scrollIntoView) {
|
||||
targetMessageDom.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
@ -511,11 +410,7 @@ async function onMessageSourceUpdated(message: IMessageModel) {
|
||||
// Only the second case needs to add scrollToTarget when listening here
|
||||
messageTarget.value = message;
|
||||
if (messageTarget.value) {
|
||||
if (
|
||||
messageList.value?.findIndex(
|
||||
(message: IMessageModel) => message?.ID === messageTarget.value?.ID,
|
||||
) >= 0
|
||||
) {
|
||||
if (messageList.value?.findIndex((message: IMessageModel) => message?.ID === messageTarget.value?.ID) >= 0) {
|
||||
const tempMessage = messageTarget.value;
|
||||
messageTarget.value = undefined;
|
||||
await scrollToPosition({ scrollToMessage: tempMessage });
|
||||
@ -600,7 +495,7 @@ const handleToggleMessageItemForPC = (e: MouseEvent, message: IMessageModel) =>
|
||||
domRefs: targetMessageDom.value,
|
||||
ignoreDomRefs: ignoreDomRefs,
|
||||
handler: closeChatPop,
|
||||
button: e.button,
|
||||
button: e.button
|
||||
});
|
||||
filterTopMessageDom(e.target);
|
||||
});
|
||||
@ -683,16 +578,16 @@ async function scrollToLatestMessage() {
|
||||
}
|
||||
}
|
||||
|
||||
const handelScrollListScroll = throttle(function (e: Event) {
|
||||
const handelScrollListScroll = throttle(
|
||||
function (e: Event) {
|
||||
scrollButtonInstanceRef.value?.judgeScrollOverOneScreen(e);
|
||||
}, 150, { leading: true });
|
||||
},
|
||||
150,
|
||||
{ leading: true }
|
||||
);
|
||||
|
||||
async function bindIntersectionObserver() {
|
||||
if (
|
||||
!messageList.value
|
||||
|| !messageListRef.value
|
||||
|| messageList.value.length === 0
|
||||
) {
|
||||
if (!messageList.value || !messageListRef.value || messageList.value.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -701,32 +596,34 @@ async function bindIntersectionObserver() {
|
||||
return;
|
||||
}
|
||||
|
||||
const mappingFromIDToMessage: Record<string, {
|
||||
const mappingFromIDToMessage: Record<
|
||||
string,
|
||||
{
|
||||
msgDom: HTMLElement;
|
||||
msgModel: IMessageModel | undefined;
|
||||
}> = {};
|
||||
}
|
||||
> = {};
|
||||
|
||||
observer?.disconnect();
|
||||
observer = new IntersectionObserver((entries) => {
|
||||
observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
const { isIntersecting, target } = entry;
|
||||
if (isIntersecting) {
|
||||
const { msgDom, msgModel } = mappingFromIDToMessage[target.id];
|
||||
if (
|
||||
msgModel
|
||||
&& !msgModel.readReceiptInfo?.isPeerRead
|
||||
&& !sentReceiptMessageIDSet.has(msgModel.ID)
|
||||
) {
|
||||
if (msgModel && !msgModel.readReceiptInfo?.isPeerRead && !sentReceiptMessageIDSet.has(msgModel.ID)) {
|
||||
TUIChatService.sendMessageReadReceipt([msgModel]);
|
||||
sentReceiptMessageIDSet.add(msgModel.ID);
|
||||
observer?.unobserve(msgDom);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, {
|
||||
},
|
||||
{
|
||||
root: messageListRef.value,
|
||||
threshold: 0.7,
|
||||
});
|
||||
threshold: 0.7
|
||||
}
|
||||
);
|
||||
|
||||
const arrayOfMessageLi = messageListRef.value?.querySelectorAll('.message-li');
|
||||
if (arrayOfMessageLi) {
|
||||
@ -735,14 +632,10 @@ async function bindIntersectionObserver() {
|
||||
const matchingMessage = messageList.value.find((message: IMessageModel) => {
|
||||
return messageElement.id.slice(4) === message.ID;
|
||||
});
|
||||
if (
|
||||
matchingMessage
|
||||
&& matchingMessage.needReadReceipt
|
||||
&& matchingMessage.flow === 'in'
|
||||
) {
|
||||
if (matchingMessage && matchingMessage.needReadReceipt && matchingMessage.flow === 'in') {
|
||||
mappingFromIDToMessage[messageElement.id] = {
|
||||
msgDom: messageElement,
|
||||
msgModel: matchingMessage,
|
||||
msgModel: matchingMessage
|
||||
};
|
||||
observer?.observe(messageElement);
|
||||
}
|
||||
@ -750,7 +643,6 @@ async function bindIntersectionObserver() {
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const isSignalingMessage = (message: IMessageModel) => {
|
||||
return message?.type === TYPES.value.MSG_CUSTOM && message?.getSignalingInfo();
|
||||
};
|
||||
@ -775,14 +667,17 @@ function onMessageListBackgroundClick() {
|
||||
emits('closeInputToolBar');
|
||||
}
|
||||
|
||||
watch(() => props.isMultipleSelectMode, (newValue) => {
|
||||
watch(
|
||||
() => props.isMultipleSelectMode,
|
||||
(newValue) => {
|
||||
if (!newValue) {
|
||||
changeSelectMessageIDList({
|
||||
type: 'clearAll',
|
||||
messageID: '',
|
||||
messageID: ''
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
function changeSelectMessageIDList({ type, messageID }: { type: 'add' | 'remove' | 'clearAll'; messageID: string }) {
|
||||
// TODO need to delete this
|
||||
@ -791,35 +686,35 @@ function changeSelectMessageIDList({ type, messageID }: { type: 'add' | 'remove'
|
||||
} else if (type === 'add' && !multipleSelectedMessageIDList.value.includes(messageID)) {
|
||||
multipleSelectedMessageIDList.value.push(messageID);
|
||||
} else if (type === 'remove') {
|
||||
multipleSelectedMessageIDList.value = multipleSelectedMessageIDList.value.filter(id => id !== messageID);
|
||||
multipleSelectedMessageIDList.value = multipleSelectedMessageIDList.value.filter((id) => id !== messageID);
|
||||
}
|
||||
}
|
||||
|
||||
function mergeForwardMessage() {
|
||||
TUIStore.update(StoreName.CUSTOM, 'multipleForwardMessageID', {
|
||||
isMergeForward: true,
|
||||
messageIDList: multipleSelectedMessageIDList.value,
|
||||
messageIDList: multipleSelectedMessageIDList.value
|
||||
});
|
||||
}
|
||||
|
||||
function oneByOneForwardMessage() {
|
||||
TUIStore.update(StoreName.CUSTOM, 'multipleForwardMessageID', {
|
||||
isMergeForward: false,
|
||||
messageIDList: multipleSelectedMessageIDList.value,
|
||||
messageIDList: multipleSelectedMessageIDList.value
|
||||
});
|
||||
}
|
||||
|
||||
function setAudioPlayed(messageID: string) {
|
||||
audioPlayedMapping.value = {
|
||||
...audioPlayedMapping.value,
|
||||
[messageID]: true,
|
||||
[messageID]: true
|
||||
};
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
oneByOneForwardMessage,
|
||||
mergeForwardMessage,
|
||||
scrollToLatestMessage,
|
||||
scrollToLatestMessage
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -1,23 +1,23 @@
|
||||
const Link = {
|
||||
product: {
|
||||
label: '产品文档',
|
||||
url: 'https://cloud.tencent.com/document/product/269/1499#.E7.BE.A4.E7.BB.84.E5.8A.9F.E8.83.BD',
|
||||
url: 'https://cloud.tencent.com/document/product/269/1499#.E7.BE.A4.E7.BB.84.E5.8A.9F.E8.83.BD'
|
||||
},
|
||||
customMessage: {
|
||||
label: '自定义消息',
|
||||
url: 'https://web.sdk.qcloud.com/im/doc/zh-cn/SDK.html#createCustomMessage',
|
||||
url: 'https://web.sdk.qcloud.com/im/doc/zh-cn/SDK.html#createCustomMessage'
|
||||
},
|
||||
complaint: {
|
||||
label: '点此投诉',
|
||||
url: 'https://cloud.tencent.com/apply/p/xc3oaubi98g',
|
||||
url: 'https://cloud.tencent.com/apply/p/xc3oaubi98g'
|
||||
},
|
||||
implement: {
|
||||
label: '集成TUICallKit',
|
||||
url: 'https://cloud.tencent.com/document/product/269/79861',
|
||||
url: 'https://cloud.tencent.com/document/product/269/79861'
|
||||
},
|
||||
purchase: {
|
||||
label: '开通腾讯实时音视频服务',
|
||||
url: 'https://cloud.tencent.com/document/product/1640/79968',
|
||||
},
|
||||
url: 'https://cloud.tencent.com/document/product/1640/79968'
|
||||
}
|
||||
};
|
||||
export default Link;
|
||||
|
@ -1,32 +1,15 @@
|
||||
<template>
|
||||
<div
|
||||
class="message-audio"
|
||||
:class="[
|
||||
isMobile && 'message-audio-h5',
|
||||
message.flow === 'out' && 'reserve',
|
||||
message.hasRiskContent && 'disable',
|
||||
]"
|
||||
:class="[isMobile && 'message-audio-h5', message.flow === 'out' && 'reserve', message.hasRiskContent && 'disable']"
|
||||
@click.stop="play"
|
||||
>
|
||||
<div class="audio-icon-container">
|
||||
<div :class="{ 'mask': true, 'play': isAudioPlaying }" />
|
||||
<Icon
|
||||
class="icon"
|
||||
width="16px"
|
||||
height="20px"
|
||||
:file="audioIcon"
|
||||
/>
|
||||
<Icon class="icon" width="16px" height="20px" :file="audioIcon" />
|
||||
</div>
|
||||
<span
|
||||
class="time"
|
||||
:style="{ width: `${data.second * 10 + 20}px` }"
|
||||
>
|
||||
{{ data.second || 1 }} "
|
||||
</span>
|
||||
<audio
|
||||
ref="audioRef"
|
||||
:src="data.url"
|
||||
/>
|
||||
<span class="time" :style="{ width: `${data.second * 10 + 20}px` }"> {{ data.second || 1 }} " </span>
|
||||
<audio ref="audioRef" :src="data.url" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -44,12 +27,12 @@ const emits = defineEmits<IEmits>();
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
default: () => ({})
|
||||
},
|
||||
messageItem: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
|
||||
const data = ref();
|
||||
@ -109,7 +92,7 @@ function onAudioPaused() {
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../../assets/styles/common";
|
||||
@import '../../../../assets/styles/common';
|
||||
|
||||
$flow-in-bg-color: #fbfbfb;
|
||||
$flow-out-bg-color: #dceafd;
|
||||
|
@ -1,12 +1,7 @@
|
||||
<template>
|
||||
<div :class="containerClassNameList">
|
||||
<!-- multiple select radio -->
|
||||
<RadioSelect
|
||||
v-if="props.isMultipleSelectMode"
|
||||
class="multiple-select-radio"
|
||||
:isSelected="isMultipleSelected"
|
||||
@onChange="toggleMultipleSelect"
|
||||
/>
|
||||
<RadioSelect v-if="props.isMultipleSelectMode" class="multiple-select-radio" :isSelected="isMultipleSelected" @onChange="toggleMultipleSelect" />
|
||||
<div
|
||||
:class="{
|
||||
'control-reverse': message.flow === 'out'
|
||||
@ -14,22 +9,10 @@
|
||||
>
|
||||
<!-- message-bubble-container -->
|
||||
<div class="message-bubble-content">
|
||||
<div
|
||||
class="message-bubble-main-content"
|
||||
:class="[message.flow === 'in' ? '' : 'reverse']"
|
||||
>
|
||||
<Avatar
|
||||
useSkeletonAnimation
|
||||
:url="message.avatar || ''"
|
||||
/>
|
||||
<main
|
||||
class="message-body"
|
||||
@click.stop
|
||||
>
|
||||
<div
|
||||
v-if="message.flow === 'in' && message.conversationType === 'GROUP'"
|
||||
class="message-body-nick-name"
|
||||
>
|
||||
<div class="message-bubble-main-content" :class="[message.flow === 'in' ? '' : 'reverse']">
|
||||
<Avatar useSkeletonAnimation :url="message.avatar || ''" />
|
||||
<main class="message-body" @click.stop>
|
||||
<div v-if="message.flow === 'in' && message.conversationType === 'GROUP'" class="message-body-nick-name">
|
||||
{{ props.content.showName }}
|
||||
</div>
|
||||
<div :class="['message-body-main', message.flow === 'out' && 'message-body-main-reverse']">
|
||||
@ -41,44 +24,29 @@
|
||||
message.hasRiskContent && 'content-has-risk',
|
||||
isNoPadding ? 'content-no-padding' : '',
|
||||
isNoPadding && isBlink ? 'blink-shadow' : '',
|
||||
!isNoPadding && isBlink ? 'blink-content' : '',
|
||||
!isNoPadding && isBlink ? 'blink-content' : ''
|
||||
]"
|
||||
>
|
||||
<div class="content-main">
|
||||
<img
|
||||
v-if="
|
||||
(message.type === TYPES.MSG_IMAGE || message.type === TYPES.MSG_VIDEO) &&
|
||||
message.hasRiskContent
|
||||
"
|
||||
v-if="(message.type === TYPES.MSG_IMAGE || message.type === TYPES.MSG_VIDEO) && message.hasRiskContent"
|
||||
:class="['message-risk-replace', !isPC && 'message-risk-replace-h5']"
|
||||
:src="riskImageReplaceUrl"
|
||||
>
|
||||
/>
|
||||
<template v-else>
|
||||
<slot name="messageElement" />
|
||||
<slot name="TUIEmojiPlugin" />
|
||||
</template>
|
||||
</div>
|
||||
<!-- Risk Content Tips -->
|
||||
<div
|
||||
v-if="message.hasRiskContent"
|
||||
class="content-has-risk-tips"
|
||||
>
|
||||
<div v-if="message.hasRiskContent" class="content-has-risk-tips">
|
||||
{{ riskContentText }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- audio unplay mark -->
|
||||
<div
|
||||
v-if="isDisplayUnplayMark"
|
||||
class="audio-unplay-mark"
|
||||
/>
|
||||
<div v-if="isDisplayUnplayMark" class="audio-unplay-mark" />
|
||||
<!-- Send Fail Icon -->
|
||||
<div
|
||||
v-if="message.status === 'fail' || message.hasRiskContent"
|
||||
class="message-label fail"
|
||||
@click="resendMessage()"
|
||||
>
|
||||
!
|
||||
</div>
|
||||
<div v-if="message.status === 'fail' || message.hasRiskContent" class="message-label fail" @click="resendMessage()">!</div>
|
||||
<!-- Loading Icon -->
|
||||
<Icon
|
||||
v-if="message.status === 'unSend' && needLoadingIconMessageType.includes(message.type)"
|
||||
@ -88,28 +56,16 @@
|
||||
:height="'15px'"
|
||||
/>
|
||||
<!-- Read & Unread -->
|
||||
<ReadStatus
|
||||
class="message-label align-self-bottom"
|
||||
:message="shallowCopyMessage(message)"
|
||||
@openReadUserPanel="openReadUserPanel"
|
||||
/>
|
||||
<ReadStatus class="message-label align-self-bottom" :message="shallowCopyMessage(message)" @openReadUserPanel="openReadUserPanel" />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<!-- message extra area -->
|
||||
<div
|
||||
class="message-bubble-extra-content"
|
||||
>
|
||||
<div class="message-bubble-extra-content">
|
||||
<!-- extra: message translation -->
|
||||
<MessageTranslate
|
||||
:class="message.flow === 'out' ? 'reverse' : 'flex-row'"
|
||||
:message="message"
|
||||
/>
|
||||
<MessageTranslate :class="message.flow === 'out' ? 'reverse' : 'flex-row'" :message="message" />
|
||||
<!-- extra: message convert voice to text -->
|
||||
<MessageConvert
|
||||
:class="message.flow === 'out' ? 'reverse' : 'flex-row'"
|
||||
:message="message"
|
||||
/>
|
||||
<MessageConvert :class="message.flow === 'out' ? 'reverse' : 'flex-row'" :message="message" />
|
||||
<!-- extra: message quote -->
|
||||
<MessageQuote
|
||||
:class="message.flow === 'out' ? 'reverse' : 'flex-row'"
|
||||
@ -159,24 +115,18 @@ interface IEmits {
|
||||
const emits = defineEmits<IEmits>();
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
messageItem: () => ({} as IMessageModel),
|
||||
messageItem: () => ({}) as IMessageModel,
|
||||
content: () => ({}),
|
||||
isAudioPlayed: false,
|
||||
blinkMessageIDList: () => [],
|
||||
classNameList: () => [],
|
||||
isMultipleSelectMode: false,
|
||||
multipleSelectedMessageIDList: () => [],
|
||||
multipleSelectedMessageIDList: () => []
|
||||
});
|
||||
|
||||
const TYPES = TUIChatEngine.TYPES;
|
||||
const riskImageReplaceUrl = 'https://web.sdk.qcloud.com/component/TUIKit/assets/has_risk_default.png';
|
||||
const needLoadingIconMessageType = [
|
||||
TYPES.MSG_LOCATION,
|
||||
TYPES.MSG_TEXT,
|
||||
TYPES.MSG_CUSTOM,
|
||||
TYPES.MSG_MERGER,
|
||||
TYPES.MSG_FACE,
|
||||
];
|
||||
const needLoadingIconMessageType = [TYPES.MSG_LOCATION, TYPES.MSG_TEXT, TYPES.MSG_CUSTOM, TYPES.MSG_MERGER, TYPES.MSG_FACE];
|
||||
|
||||
const { blinkMessageIDList, messageItem: message } = toRefs(props);
|
||||
|
||||
@ -185,18 +135,11 @@ const isMultipleSelected = computed<boolean>(() => {
|
||||
});
|
||||
|
||||
const isDisplayUnplayMark = computed<boolean>(() => {
|
||||
return message.value.flow === 'in'
|
||||
&& message.value.status === 'success'
|
||||
&& message.value.type === TYPES.MSG_AUDIO
|
||||
&& !props.isAudioPlayed;
|
||||
return message.value.flow === 'in' && message.value.status === 'success' && message.value.type === TYPES.MSG_AUDIO && !props.isAudioPlayed;
|
||||
});
|
||||
|
||||
const containerClassNameList = computed(() => {
|
||||
return [
|
||||
'message-bubble',
|
||||
isMultipleSelected.value ? 'multiple-selected' : '',
|
||||
...props.classNameList,
|
||||
];
|
||||
return ['message-bubble', isMultipleSelected.value ? 'multiple-selected' : '', ...props.classNameList];
|
||||
});
|
||||
|
||||
// When an emoji is deleted, the `reactionList` will update the corresponding emoji's `totalUserCount`.
|
||||
@ -213,9 +156,7 @@ const riskContentText = computed<string>(() => {
|
||||
if (message.value.flow === 'out') {
|
||||
content += TUITranslateService.t('TUIChat.发送失败');
|
||||
} else {
|
||||
content += TUITranslateService.t(
|
||||
message.value.type === TYPES.MSG_AUDIO ? 'TUIChat.无法收听' : 'TUIChat.无法查看',
|
||||
);
|
||||
content += TUITranslateService.t(message.value.type === TYPES.MSG_AUDIO ? 'TUIChat.无法收听' : 'TUIChat.无法查看');
|
||||
}
|
||||
return content;
|
||||
});
|
||||
@ -230,7 +171,7 @@ const isBlink = computed(() => {
|
||||
function toggleMultipleSelect(isSelected: boolean) {
|
||||
emits('changeSelectMessageIDList', {
|
||||
type: isSelected ? 'add' : 'remove',
|
||||
messageID: message.value.ID,
|
||||
messageID: message.value.ID
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
class="message-convert-container"
|
||||
:style="{
|
||||
height: calculateHeight > 0 ? `${calculateHeight}px` : 'auto',
|
||||
width: calculateWidth > 0 ? `${calculateWidth}px` : 'auto',
|
||||
width: calculateWidth > 0 ? `${calculateWidth}px` : 'auto'
|
||||
}"
|
||||
>
|
||||
<div
|
||||
@ -11,7 +11,7 @@
|
||||
ref="convertContentRef"
|
||||
:class="{
|
||||
'convert-content': true,
|
||||
'occur': calculateHeight > 0,
|
||||
'occur': calculateHeight > 0
|
||||
}"
|
||||
>
|
||||
{{ convertText }}
|
||||
@ -30,10 +30,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, nextTick } from '../../../../../adapter-vue';
|
||||
import {
|
||||
IMessageModel,
|
||||
TUITranslateService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { IMessageModel, TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import { getBoundingClientRectSync } from '@tencentcloud/universal-api';
|
||||
import { convertor } from '../../../utils/convertVoiceToText';
|
||||
|
||||
@ -50,8 +47,8 @@ interface IEmits {
|
||||
|
||||
const emits = defineEmits<IEmits>();
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
message: () => ({} as IMessageModel),
|
||||
isSingleConvert: false,
|
||||
message: () => ({}) as IMessageModel,
|
||||
isSingleConvert: false
|
||||
});
|
||||
|
||||
const convertFinished = ref<boolean>(false);
|
||||
@ -62,9 +59,12 @@ const calculateWidth = ref<number>(0);
|
||||
const convertLoadingRef = ref<HTMLDivElement>();
|
||||
const convertContentRef = ref<HTMLDivElement>();
|
||||
|
||||
watch(() => props.contentVisible, (newVal: boolean) => {
|
||||
watch(
|
||||
() => props.contentVisible,
|
||||
(newVal: boolean) => {
|
||||
if (newVal) {
|
||||
convertor.get(props.message)
|
||||
convertor
|
||||
.get(props.message)
|
||||
.then((text) => {
|
||||
convertFinished.value = true;
|
||||
convertText.value = text;
|
||||
@ -99,9 +99,11 @@ watch(() => props.contentVisible, (newVal: boolean) => {
|
||||
convertText.value = err.message;
|
||||
});
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
});
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -109,7 +111,9 @@ watch(() => props.contentVisible, (newVal: boolean) => {
|
||||
min-height: 20px;
|
||||
min-width: 80px;
|
||||
position: relative;
|
||||
transition: width 0.15s ease-out, height 0.15s ease-out, ;
|
||||
transition:
|
||||
width 0.15s ease-out,
|
||||
height 0.15s ease-out;
|
||||
font-size: 14px;
|
||||
|
||||
.loading {
|
||||
|
@ -5,7 +5,7 @@
|
||||
:class="{
|
||||
'message-convert': true,
|
||||
'reverse': props.message.flow === 'out',
|
||||
'error': hasConvertError,
|
||||
'error': hasConvertError
|
||||
}"
|
||||
>
|
||||
<ConvertContent
|
||||
@ -20,11 +20,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted } from '../../../../../adapter-vue';
|
||||
import {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
IMessageModel,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIStore, StoreName, IMessageModel } from '@tencentcloud/chat-uikit-engine';
|
||||
import ConvertContent from './convert-content.vue';
|
||||
import { IConvertInfo } from '../../../../../interface';
|
||||
|
||||
@ -33,7 +29,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
message: () => ({} as IMessageModel),
|
||||
message: () => ({}) as IMessageModel
|
||||
});
|
||||
|
||||
const convertVisible = ref<boolean>(false);
|
||||
@ -44,13 +40,13 @@ let isSingleConvert = true;
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CHAT, {
|
||||
voiceToTextInfo: onMessageConvertUpdated,
|
||||
voiceToTextInfo: onMessageConvertUpdated
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CHAT, {
|
||||
voiceToTextInfo: onMessageConvertUpdated,
|
||||
voiceToTextInfo: onMessageConvertUpdated
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -4,22 +4,11 @@
|
||||
<div>
|
||||
<h1>
|
||||
<label>{{ extension.title }}</label>
|
||||
<a
|
||||
v-if="extension.hyperlinks_text"
|
||||
:href="extension.hyperlinks_text.value"
|
||||
target="view_window"
|
||||
>{{ extension.hyperlinks_text.key }}</a>
|
||||
<a v-if="extension.hyperlinks_text" :href="extension.hyperlinks_text.value" target="view_window">{{ extension.hyperlinks_text.key }}</a>
|
||||
</h1>
|
||||
<ul v-if="extension.item && extension.item.length > 0">
|
||||
<li
|
||||
v-for="(item, index) in extension.item"
|
||||
:key="index"
|
||||
>
|
||||
<a
|
||||
v-if="isUrl(item.value)"
|
||||
:href="item.value"
|
||||
target="view_window"
|
||||
>{{ item.key }}</a>
|
||||
<li v-for="(item, index) in extension.item" :key="index">
|
||||
<a v-if="isUrl(item.value)" :href="item.value" target="view_window">{{ item.key }}</a>
|
||||
<p v-else>
|
||||
{{ item.key }}
|
||||
</p>
|
||||
@ -30,30 +19,18 @@
|
||||
</template>
|
||||
<template v-else-if="customData.businessID === CHAT_MSG_CUSTOM_TYPE.EVALUATE">
|
||||
<div class="evaluate">
|
||||
<h1>{{ TUITranslateService.t("message.custom.对本次服务评价") }}</h1>
|
||||
<h1>{{ TUITranslateService.t('message.custom.对本次服务评价') }}</h1>
|
||||
<ul class="evaluate-list">
|
||||
<li
|
||||
v-for="(item, index) in Math.max(customData.score, 0)"
|
||||
:key="index"
|
||||
class="evaluate-list-item"
|
||||
>
|
||||
<Icon
|
||||
:file="star"
|
||||
class="file-icon"
|
||||
/>
|
||||
<li v-for="(item, index) in Math.max(customData.score, 0)" :key="index" class="evaluate-list-item">
|
||||
<Icon :file="star" class="file-icon" />
|
||||
</li>
|
||||
</ul>
|
||||
<article>{{ customData.comment }}</article>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="customData.businessID === CHAT_MSG_CUSTOM_TYPE.ORDER">
|
||||
<div
|
||||
class="order"
|
||||
@click="openLink(customData.link)"
|
||||
>
|
||||
<img
|
||||
:src="customData.imageUrl"
|
||||
>
|
||||
<div class="order" @click="openLink(customData.link)">
|
||||
<img :src="customData.imageUrl" />
|
||||
<main>
|
||||
<h1>{{ customData.title }}</h1>
|
||||
<p>{{ customData.description }}</p>
|
||||
@ -64,12 +41,7 @@
|
||||
<template v-else-if="customData.businessID === CHAT_MSG_CUSTOM_TYPE.LINK">
|
||||
<div class="textLink">
|
||||
<p>{{ customData.text }}</p>
|
||||
<a
|
||||
:href="customData.link"
|
||||
target="view_window"
|
||||
>{{
|
||||
TUITranslateService.t("message.custom.查看详情>>")
|
||||
}}</a>
|
||||
<a :href="customData.link" target="view_window">{{ TUITranslateService.t('message.custom.查看详情>>') }}</a>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
@ -93,14 +65,14 @@ interface Props {
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
messageItem: undefined,
|
||||
content: undefined,
|
||||
content: undefined
|
||||
});
|
||||
|
||||
const custom = ref();
|
||||
const message = ref<IMessageModel>();
|
||||
const extension = ref();
|
||||
const customData = ref<ICustomMessagePayload>({
|
||||
businessID: '',
|
||||
businessID: ''
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
@ -118,7 +90,7 @@ const openLink = (url: any) => {
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../../assets/styles/common";
|
||||
@import '../../../../assets/styles/common';
|
||||
|
||||
a {
|
||||
color: #679ce1;
|
||||
|
@ -1,12 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="message-image"
|
||||
>
|
||||
<img
|
||||
mode="aspectFit"
|
||||
class="message-image"
|
||||
:src="url"
|
||||
>
|
||||
<div class="message-image">
|
||||
<img mode="aspectFit" class="message-image" :src="url" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -17,8 +11,8 @@ import { CUSTOM_BIG_EMOJI_URL } from '../../emoji-config';
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
|
||||
const url = ref(props.content.url);
|
||||
@ -35,7 +29,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../../assets/styles/common";
|
||||
@import '../../../../assets/styles/common';
|
||||
|
||||
.message-image {
|
||||
width: 80px;
|
||||
|
@ -1,13 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="file-message-montainer"
|
||||
:title="TUITranslateService.t('TUIChat.单击下载')"
|
||||
@click="download"
|
||||
>
|
||||
<Icon
|
||||
:file="files"
|
||||
class="file-icon"
|
||||
/>
|
||||
<div class="file-message-montainer" :title="TUITranslateService.t('TUIChat.单击下载')" @click="download">
|
||||
<Icon :file="files" class="file-icon" />
|
||||
<div>
|
||||
<div>{{ props.content.name }}</div>
|
||||
<div>{{ props.content.size }}</div>
|
||||
@ -28,9 +21,9 @@ const props = withDefaults(
|
||||
messageItem: IMessageModel;
|
||||
}>(),
|
||||
{
|
||||
content: () => ({} as IFileMessageContent),
|
||||
messageItem: () => ({} as IMessageModel),
|
||||
},
|
||||
content: () => ({}) as IFileMessageContent,
|
||||
messageItem: () => ({}) as IMessageModel
|
||||
}
|
||||
);
|
||||
|
||||
const download = () => {
|
||||
@ -40,13 +33,13 @@ const download = () => {
|
||||
const option = {
|
||||
mode: 'cors',
|
||||
headers: new Headers({
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
}),
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
})
|
||||
} as RequestInit;
|
||||
// If the browser supports fetch, use blob to download, so as to avoid the browser clicking the a tag and jumping to the preview of the new page
|
||||
if ((window as any)?.fetch) {
|
||||
fetch(props.content.url, option)
|
||||
.then(res => res.blob())
|
||||
.then((res) => res.blob())
|
||||
.then((blob) => {
|
||||
const a = document.createElement('a');
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
@ -64,7 +57,7 @@ const download = () => {
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../../assets/styles/common";
|
||||
@import '../../../../assets/styles/common';
|
||||
|
||||
.file-message-montainer {
|
||||
display: flex;
|
||||
|
@ -1,15 +1,11 @@
|
||||
<template>
|
||||
<div
|
||||
ref="skeletonDomRef"
|
||||
class="image-container"
|
||||
@click.self="toggleShow"
|
||||
>
|
||||
<div ref="skeletonDomRef" class="image-container" @click.self="toggleShow">
|
||||
<img
|
||||
:class="['message-image', !isPC && 'message-image-h5']"
|
||||
:src="props.content.url"
|
||||
:width="props.content.width"
|
||||
:height="props.content.height"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -28,17 +24,13 @@ const props = withDefaults(
|
||||
}>(),
|
||||
{
|
||||
content: () => ({}),
|
||||
messageItem: () => ({} as IMessageModel),
|
||||
},
|
||||
messageItem: () => ({}) as IMessageModel
|
||||
}
|
||||
);
|
||||
const skeletonDomRef = ref();
|
||||
|
||||
onMounted(() => {
|
||||
if (
|
||||
props.messageItem?.status === 'success'
|
||||
|| props.messageItem?.status === 'fail'
|
||||
|| props.messageItem?.progress === 1
|
||||
) {
|
||||
if (props.messageItem?.status === 'success' || props.messageItem?.status === 'fail' || props.messageItem?.progress === 1) {
|
||||
autoFixSkeletonSize();
|
||||
}
|
||||
});
|
||||
@ -49,7 +41,7 @@ watch(
|
||||
if (newVal > oldVal) {
|
||||
autoFixSkeletonSize();
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
function autoFixSkeletonSize() {
|
||||
@ -70,7 +62,7 @@ function toggleShow() {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../../assets/styles/common";
|
||||
@import '../../../../assets/styles/common';
|
||||
|
||||
.image-container {
|
||||
overflow: hidden;
|
||||
|
@ -1,12 +1,7 @@
|
||||
<template>
|
||||
<a
|
||||
class="message-location"
|
||||
:href="data.href"
|
||||
target="_blank"
|
||||
title="点击查看详情"
|
||||
>
|
||||
<a class="message-location" :href="data.href" target="_blank" title="点击查看详情">
|
||||
<span class="el-icon-location-outline">{{ data.description }}</span>
|
||||
<img :src="data.url">
|
||||
<img :src="data.url" />
|
||||
</a>
|
||||
</template>
|
||||
|
||||
@ -15,8 +10,8 @@ import { watchEffect, ref } from '../../../../adapter-vue';
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
const data = ref();
|
||||
watchEffect(() => {
|
||||
@ -24,7 +19,7 @@ watchEffect(() => {
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../../assets/styles/common";
|
||||
@import '../../../../assets/styles/common';
|
||||
|
||||
.message-location {
|
||||
display: flex;
|
||||
|
@ -3,33 +3,20 @@
|
||||
v-if="hasQuoteContent"
|
||||
:class="{
|
||||
'reference-content': true,
|
||||
'reverse': message.flow === 'out',
|
||||
'reverse': message.flow === 'out'
|
||||
}"
|
||||
@click="scrollToOriginalMessage"
|
||||
>
|
||||
<div
|
||||
v-if="isMessageRevoked"
|
||||
class="revoked-text"
|
||||
>
|
||||
<div v-if="isMessageRevoked" class="revoked-text">
|
||||
{{ TUITranslateService.t('TUIChat.引用内容已撤回') }}
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="max-double-line"
|
||||
>
|
||||
{{ messageQuoteContent.messageSender }}: {{ transformTextWithKeysToEmojiNames(messageQuoteText) }}
|
||||
</div>
|
||||
<div v-else class="max-double-line">{{ messageQuoteContent.messageSender }}: {{ transformTextWithKeysToEmojiNames(messageQuoteText) }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, onMounted } from '../../../../../adapter-vue';
|
||||
import {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
IMessageModel,
|
||||
TUITranslateService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIStore, StoreName, IMessageModel, TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import { getBoundingClientRect, getScrollInfo } from '@tencentcloud/universal-api';
|
||||
import { isUniFrameWork } from '../../../../../utils/env';
|
||||
import { Toast, TOAST_TYPE } from '../../../../../components/common/Toast/index';
|
||||
@ -47,7 +34,7 @@ export interface IEmits {
|
||||
|
||||
const emits = defineEmits<IEmits>();
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
message: () => ({} as IMessageModel),
|
||||
message: () => ({}) as IMessageModel
|
||||
});
|
||||
|
||||
let selfAddValue = 0;
|
||||
@ -116,12 +103,7 @@ function performQuoteContent(params: IQuoteContent) {
|
||||
messageKey = '[消息]';
|
||||
break;
|
||||
}
|
||||
if (
|
||||
[
|
||||
MessageQuoteTypeEnum.TYPE_TEXT,
|
||||
MessageQuoteTypeEnum.TYPE_MERGER,
|
||||
].includes(params.messageType)
|
||||
) {
|
||||
if ([MessageQuoteTypeEnum.TYPE_TEXT, MessageQuoteTypeEnum.TYPE_MERGER].includes(params.messageType)) {
|
||||
quoteContent = params.messageAbstract;
|
||||
}
|
||||
return quoteContent ? quoteContent : TUITranslateService.t(`TUIChat.${messageKey}`);
|
||||
@ -133,7 +115,7 @@ async function scrollToOriginalMessage() {
|
||||
}
|
||||
const originMessageID = messageQuoteContent.value?.messageID;
|
||||
const currentMessageList = TUIStore.getData(StoreName.CHAT, 'messageList');
|
||||
const isOriginalMessageInScreen = currentMessageList.some(msg => msg.ID === originMessageID);
|
||||
const isOriginalMessageInScreen = currentMessageList.some((msg) => msg.ID === originMessageID);
|
||||
if (originMessageID && isOriginalMessageInScreen) {
|
||||
try {
|
||||
const scrollViewRect = await getBoundingClientRect('#messageScrollList', 'messageList');
|
||||
@ -156,7 +138,7 @@ async function scrollToOriginalMessage() {
|
||||
} else {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIChat.无法定位到原消息'),
|
||||
type: TOAST_TYPE.WARNING,
|
||||
type: TOAST_TYPE.WARNING
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -56,5 +56,5 @@ export enum MessageQuoteTypeEnum {
|
||||
/**
|
||||
* merge forward message
|
||||
*/
|
||||
TYPE_MERGER = 10,
|
||||
TYPE_MERGER = 10
|
||||
}
|
||||
|
@ -1,20 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="message-record-container"
|
||||
@click="openMergeDetail"
|
||||
>
|
||||
<div
|
||||
class="record-title"
|
||||
>
|
||||
<div class="message-record-container" @click="openMergeDetail">
|
||||
<div class="record-title">
|
||||
{{ props.renderData.title }}
|
||||
</div>
|
||||
<div class="record-abstract-container">
|
||||
<div
|
||||
v-for="(item, index) in props.renderData.abstractList.slice(0, 7)"
|
||||
:key="index"
|
||||
class="record-abstract-item"
|
||||
>
|
||||
<div v-for="(item, index) in props.renderData.abstractList.slice(0, 7)" :key="index" class="record-abstract-item">
|
||||
{{ transformTextWithKeysToEmojiNames(item) }}
|
||||
</div>
|
||||
</div>
|
||||
@ -22,11 +13,7 @@
|
||||
{{ TUITranslateService.t('TUIChat.聊天记录') }}
|
||||
</div>
|
||||
</div>
|
||||
<Overlay
|
||||
v-if="!props.disabled && isPC"
|
||||
:visible="isMessageListVisible"
|
||||
@onOverlayClick="isMessageListVisible = false"
|
||||
>
|
||||
<Overlay v-if="!props.disabled && isPC" :visible="isMessageListVisible" @onOverlayClick="isMessageListVisible = false">
|
||||
<SimpleMessageList
|
||||
:isMounted="isMessageListVisible"
|
||||
:renderData="props.renderData"
|
||||
@ -83,7 +70,7 @@ interface IProps {
|
||||
const emits = defineEmits<IEmits>();
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
messageItem: () => ({}) as IMessageModel,
|
||||
disabled: false,
|
||||
disabled: false
|
||||
});
|
||||
|
||||
const isMessageListVisible = ref(false);
|
||||
|
@ -1,14 +1,7 @@
|
||||
<template>
|
||||
<div class="message-stream">
|
||||
<pre
|
||||
ref="messageContentRef"
|
||||
:class="['message-marked', 'message-typewriter']"
|
||||
v-html="markedContent"
|
||||
/>
|
||||
<StreamOperation
|
||||
v-show="isOperationShow"
|
||||
:content="streamContent"
|
||||
/>
|
||||
<pre ref="messageContentRef" :class="['message-marked', 'message-typewriter']" v-html="markedContent" />
|
||||
<StreamOperation v-show="isOperationShow" :content="streamContent" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
@ -30,7 +23,7 @@ const props = withDefaults(defineProps<IProps>(), {
|
||||
payloadData: () => '',
|
||||
enableMarkdown: true,
|
||||
enableStreaming: true,
|
||||
enableOperation: true,
|
||||
enableOperation: true
|
||||
});
|
||||
|
||||
const emits = defineEmits(['onStreaming']);
|
||||
@ -54,7 +47,7 @@ const typeWriter = new TypeWriter({
|
||||
},
|
||||
onComplete() {
|
||||
isStreaming.value = false;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const generateMarkedContent = (content: string) => {
|
||||
@ -77,7 +70,8 @@ function startStreaming(content: string[]) {
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.payloadData,
|
||||
watch(
|
||||
() => props.payloadData,
|
||||
(newValue: string, oldValue: string) => {
|
||||
if (newValue === oldValue) {
|
||||
return;
|
||||
@ -125,14 +119,17 @@ watch(() => props.payloadData,
|
||||
}
|
||||
|
||||
prevChunksLength.value = chunks.value?.length;
|
||||
}, {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
watch(() => isStreaming.value, (newValue: boolean, oldValue: boolean) => {
|
||||
watch(
|
||||
() => isStreaming.value,
|
||||
(newValue: boolean, oldValue: boolean) => {
|
||||
if (newValue === oldValue) {
|
||||
return;
|
||||
}
|
||||
@ -147,9 +144,11 @@ onMounted(() => {
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
});
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
@ -168,6 +167,5 @@ function copyCode(event: Event) {
|
||||
CopyManager.copyTextOrHtml(codeElement.textContent, 'text');
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<style lang="scss" src="./index.scss"></style>
|
||||
|
@ -15,19 +15,19 @@ const escapeReplacements: Iescape = {
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
'\'': ''',
|
||||
"'": '''
|
||||
};
|
||||
const getEscapeReplacement = (ch: string): string => escapeReplacements[ch as keyof Iescape];
|
||||
|
||||
export const marked = new Marked(
|
||||
{
|
||||
mangle: false,
|
||||
headerIds: false,
|
||||
headerIds: false
|
||||
},
|
||||
markedHighlight({
|
||||
highlight(code: string) {
|
||||
return hljs.highlightAuto(code).value;
|
||||
},
|
||||
}
|
||||
}),
|
||||
{
|
||||
renderer: {
|
||||
@ -48,9 +48,9 @@ export const marked = new Marked(
|
||||
},
|
||||
link(this: any, href: string | null, title: string | null, text: string) {
|
||||
return `<a target="_blank" rel="noreferrer noopenner" class="message-marked_link" href="${href}" title="${title}">${text}</a>`;
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const markedWithPurify = (text: string) => {
|
||||
|
@ -1,25 +1,17 @@
|
||||
<template>
|
||||
<div
|
||||
ref="operationContainerRef"
|
||||
class="message-stream_operation_container"
|
||||
>
|
||||
<div ref="operationContainerRef" class="message-stream_operation_container">
|
||||
<div class="message-stream_operation_list">
|
||||
<div
|
||||
v-for="(operation, key) in operationConfig"
|
||||
:key="key"
|
||||
:class="{
|
||||
'message-stream_operation_item': true,
|
||||
'message-stream_operation_item_disabled': operation.isDisabled,
|
||||
'message-stream_operation_item_disabled': operation.isDisabled
|
||||
}"
|
||||
:title="operation.name"
|
||||
@click="(e) => operation.onClick(e, operation.key)"
|
||||
>
|
||||
<Icon
|
||||
class="message-stream_operation_icon"
|
||||
:file="operation.icon"
|
||||
:name="operation.name"
|
||||
size="14px"
|
||||
/>
|
||||
<Icon class="message-stream_operation_icon" :file="operation.icon" :name="operation.name" size="14px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -45,7 +37,7 @@ interface IEmits {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
operations: () => [IOperationType.Copy],
|
||||
operations: () => [IOperationType.Copy]
|
||||
});
|
||||
const emits = defineEmits<IEmits>();
|
||||
|
||||
@ -57,8 +49,8 @@ const defaultOperationConfig: Record<string, IOperation> = {
|
||||
isDisabled: false,
|
||||
onClick: () => {
|
||||
CopyManager.copyTextOrHtml(props.content, 'text');
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const operationConfig = computed(() => {
|
||||
@ -76,7 +68,7 @@ const operationConfig = computed(() => {
|
||||
}
|
||||
config.onClick(e, key);
|
||||
emits('onOperationClick', e, key);
|
||||
},
|
||||
}
|
||||
};
|
||||
})
|
||||
.filter(Boolean) as IOperation[];
|
||||
|
@ -1,6 +1,6 @@
|
||||
/** type & interface */
|
||||
export enum IOperationType {
|
||||
Copy = 'copy',
|
||||
Copy = 'copy'
|
||||
// Retry is not supported now
|
||||
// Retry = 'retry',
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
const chineseRegex = /[\u4e00-\u9fa5]/;
|
||||
const wordAndNonWordRegex = /\b\w+\b|[^\w]+/g;
|
||||
const isStringArray = (test: any): boolean => {
|
||||
return Array.isArray(test) && !test.some(value => typeof value !== 'string');
|
||||
return Array.isArray(test) && !test.some((value) => typeof value !== 'string');
|
||||
};
|
||||
|
||||
export class TypeWriter {
|
||||
@ -141,7 +141,7 @@ export class TypeWriter {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((this.curArrayPos >= this.strings.length)) {
|
||||
if (this.curArrayPos >= this.strings.length) {
|
||||
this.isTyping = false;
|
||||
this.onComplete?.(this);
|
||||
return;
|
||||
|
@ -1,28 +1,13 @@
|
||||
<template>
|
||||
<div :class="['message-text-container', isPC && 'text-select']">
|
||||
<span
|
||||
v-for="(item, index) in processedContent"
|
||||
:key="index"
|
||||
>
|
||||
<span
|
||||
v-if="item.name === 'text'"
|
||||
class="text"
|
||||
>
|
||||
<span v-for="(item, index) in processedContent" :key="index">
|
||||
<span v-if="item.name === 'text'" class="text">
|
||||
{{ item.text }}
|
||||
</span>
|
||||
<span
|
||||
v-else-if="item.name === 'url'"
|
||||
class="url-link"
|
||||
@click="navigateToUrl(item.url)"
|
||||
>
|
||||
<span v-else-if="item.name === 'url'" class="url-link" @click="navigateToUrl(item.url)">
|
||||
{{ item.text }}
|
||||
</span>
|
||||
<img
|
||||
v-else
|
||||
class="emoji"
|
||||
:src="item.src"
|
||||
:alt="item.emojiKey"
|
||||
>
|
||||
<img v-else class="emoji" :src="item.src" :alt="item.emojiKey" />
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
@ -51,8 +36,8 @@ interface TextItem {
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
content: () => ({}),
|
||||
messageItem: () => ({} as IMessageModel),
|
||||
enableURLHighlight: false,
|
||||
messageItem: () => ({}) as IMessageModel,
|
||||
enableURLHighlight: false
|
||||
});
|
||||
|
||||
const processedContent = ref<TextItem>([]);
|
||||
@ -80,7 +65,8 @@ watch(
|
||||
return;
|
||||
}
|
||||
|
||||
processedContent.value = processedContent.value.map((item: TextItem) => {
|
||||
processedContent.value = processedContent.value
|
||||
.map((item: TextItem) => {
|
||||
// handle custom emoji
|
||||
if (item.name === 'img' && item?.type === 'custom') {
|
||||
if (!CUSTOM_BASIC_EMOJI_URL) {
|
||||
@ -108,17 +94,18 @@ watch(
|
||||
return segments.map((segment) => ({
|
||||
name: segment.type,
|
||||
text: segment.text,
|
||||
url: segment.url,
|
||||
url: segment.url
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
})?.flat();
|
||||
})
|
||||
?.flat();
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
@ -152,7 +139,9 @@ function navigateToUrl(url: string) {
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.text,.emoji,.url-link{
|
||||
.text,
|
||||
.emoji,
|
||||
.url-link {
|
||||
&::selection {
|
||||
background-color: #b4d5fe;
|
||||
color: inherit;
|
||||
@ -167,7 +156,8 @@ function navigateToUrl(url: string) {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.text, .url-link {
|
||||
.text,
|
||||
.url-link {
|
||||
font-size: 14px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
|
@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="timestampShowFlag"
|
||||
class="message-timestamp"
|
||||
>
|
||||
<div v-if="timestampShowFlag" class="message-timestamp">
|
||||
{{ timestampShowContent }}
|
||||
</div>
|
||||
</template>
|
||||
@ -13,12 +10,12 @@ import { calculateTimestamp } from '../../utils/utils';
|
||||
const props = defineProps({
|
||||
currTime: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
default: 0
|
||||
},
|
||||
prevTime: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
default: 0
|
||||
}
|
||||
});
|
||||
const { currTime, prevTime } = toRefs(props);
|
||||
const timestampShowFlag = ref(false);
|
||||
@ -48,19 +45,16 @@ watch(
|
||||
if (newVal?.toString() === oldVal?.toString()) {
|
||||
return;
|
||||
} else {
|
||||
timestampShowContent.value = handleItemTime(
|
||||
currTime.value,
|
||||
prevTime.value,
|
||||
);
|
||||
timestampShowContent.value = handleItemTime(currTime.value, prevTime.value);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../../assets/styles/common";
|
||||
@import '../../../../assets/styles/common';
|
||||
|
||||
.message-timestamp {
|
||||
margin: 10px auto;
|
||||
|
@ -9,13 +9,13 @@ import { computed } from '../../../../adapter-vue';
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
const tipContent = computed(() => props.content?.text || props.content?.custom || '');
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../../assets/styles/common";
|
||||
@import '../../../../assets/styles/common';
|
||||
|
||||
.message-tip {
|
||||
margin: 0 auto;
|
||||
|
@ -5,7 +5,7 @@
|
||||
:class="{
|
||||
'message-translation': true,
|
||||
'reverse': props.message.flow === 'out',
|
||||
'error': hasTranslationError,
|
||||
'error': hasTranslationError
|
||||
}"
|
||||
>
|
||||
<TranslationContent
|
||||
@ -16,10 +16,7 @@
|
||||
@toggleErrorStatus="toggleErrorStatus"
|
||||
/>
|
||||
<div class="copyright">
|
||||
<Icon
|
||||
:file="checkIcon"
|
||||
size="13px"
|
||||
/>
|
||||
<Icon :file="checkIcon" size="13px" />
|
||||
<div class="copyright-text">
|
||||
{{ TUITranslateService.t('TUIChat.由IM提供翻译支持') }}
|
||||
</div>
|
||||
@ -29,12 +26,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted } from '../../../../../adapter-vue';
|
||||
import {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
IMessageModel,
|
||||
TUITranslateService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIStore, StoreName, IMessageModel, TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import Icon from '../../../../common/Icon.vue';
|
||||
import TranslationContent from './translation-content.vue';
|
||||
import checkIcon from '../../../../../assets/icon/check-sm.svg';
|
||||
@ -45,7 +37,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
message: () => ({} as IMessageModel),
|
||||
message: () => ({}) as IMessageModel
|
||||
});
|
||||
|
||||
const translationVisible = ref<boolean>(false);
|
||||
@ -57,13 +49,13 @@ let isSingleTranslation = true;
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CHAT, {
|
||||
translateTextInfo: onMessageTranslationUpdated,
|
||||
translateTextInfo: onMessageTranslationUpdated
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CHAT, {
|
||||
translateTextInfo: onMessageTranslationUpdated,
|
||||
translateTextInfo: onMessageTranslationUpdated
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
class="message-translation-container"
|
||||
:style="{
|
||||
height: calculateHeight > 0 ? `${calculateHeight}px` : 'auto',
|
||||
width: calculateWidth > 0 ? `${calculateWidth}px` : 'auto',
|
||||
width: calculateWidth > 0 ? `${calculateWidth}px` : 'auto'
|
||||
}"
|
||||
>
|
||||
<div
|
||||
@ -11,25 +11,13 @@
|
||||
ref="translationContentRef"
|
||||
:class="{
|
||||
'translation-content': true,
|
||||
'occur': calculateHeight > 0,
|
||||
'occur': calculateHeight > 0
|
||||
}"
|
||||
>
|
||||
<template
|
||||
v-if="translationTextList.length > 0"
|
||||
>
|
||||
<span
|
||||
v-for="(text, index) in translationTextList"
|
||||
:key="index"
|
||||
>
|
||||
<img
|
||||
v-if="text.type === 'face'"
|
||||
class="text-face"
|
||||
:src="text.value"
|
||||
>
|
||||
<span
|
||||
v-else
|
||||
class="text-plain"
|
||||
>{{ text.value }}</span>
|
||||
<template v-if="translationTextList.length > 0">
|
||||
<span v-for="(text, index) in translationTextList" :key="index">
|
||||
<img v-if="text.type === 'face'" class="text-face" :src="text.value" />
|
||||
<span v-else class="text-plain">{{ text.value }}</span>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
@ -50,10 +38,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, nextTick } from '../../../../../adapter-vue';
|
||||
import {
|
||||
IMessageModel,
|
||||
TUITranslateService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { IMessageModel, TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import { getBoundingClientRectSync } from '@tencentcloud/universal-api';
|
||||
import { TranslationTextType, translator } from '../../../utils/translation';
|
||||
|
||||
@ -70,7 +55,7 @@ interface IEmits {
|
||||
|
||||
const emits = defineEmits<IEmits>();
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
message: () => ({} as IMessageModel),
|
||||
message: () => ({}) as IMessageModel
|
||||
});
|
||||
|
||||
const translationFinished = ref<boolean>(false);
|
||||
@ -82,9 +67,12 @@ const calculateWidth = ref<number>(0);
|
||||
const translationLoadingRef = ref<HTMLDivElement>();
|
||||
const translationContentRef = ref<HTMLDivElement>();
|
||||
|
||||
watch(() => props.translationContentVisible, (newVal: boolean) => {
|
||||
watch(
|
||||
() => props.translationContentVisible,
|
||||
(newVal: boolean) => {
|
||||
if (newVal) {
|
||||
translator.get(props.message)
|
||||
translator
|
||||
.get(props.message)
|
||||
.then((result) => {
|
||||
translationFinished.value = true;
|
||||
translationTextList.value = result;
|
||||
@ -121,7 +109,9 @@ watch(() => props.translationContentVisible, (newVal: boolean) => {
|
||||
translationErrorText.value = err.message;
|
||||
});
|
||||
}
|
||||
}, { immediate: true });
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -129,7 +119,9 @@ watch(() => props.translationContentVisible, (newVal: boolean) => {
|
||||
min-height: 16px;
|
||||
min-width: 80px;
|
||||
position: relative;
|
||||
transition: width 0.15s ease-out, height 0.15s ease-out, ;
|
||||
transition:
|
||||
width 0.15s ease-out,
|
||||
height 0.15s ease-out;
|
||||
font-size: 14px;
|
||||
|
||||
.loading {
|
||||
|
@ -3,21 +3,15 @@
|
||||
<div
|
||||
ref="skeleton"
|
||||
class="message-video-box"
|
||||
:class="[
|
||||
(!props.messageItem.progress || props.messageItem.progress === 1)
|
||||
&& !isPC
|
||||
&& 'message-video-cover',
|
||||
]"
|
||||
:class="[(!props.messageItem.progress || props.messageItem.progress === 1) && !isPC && 'message-video-cover']"
|
||||
@click="toggleVideoPreviewer"
|
||||
>
|
||||
<img
|
||||
v-if="(props.messageItem.progress > 0 && props.messageItem.progress < 1 && poster) ||
|
||||
(!isPC && poster)
|
||||
"
|
||||
v-if="(props.messageItem.progress > 0 && props.messageItem.progress < 1 && poster) || (!isPC && poster)"
|
||||
class="message-img"
|
||||
:class="[isWidth ? 'is-width' : 'is-height']"
|
||||
:src="poster"
|
||||
>
|
||||
/>
|
||||
<video
|
||||
v-else-if="!isPC"
|
||||
ref="videoRef"
|
||||
@ -27,51 +21,21 @@
|
||||
preload="auto"
|
||||
muted
|
||||
/>
|
||||
<video
|
||||
v-else
|
||||
ref="videoRef"
|
||||
class="message-img video-web"
|
||||
:src="props.content.url"
|
||||
controls
|
||||
preload="metadata"
|
||||
:poster="poster"
|
||||
/>
|
||||
<video v-else ref="videoRef" class="message-img video-web" :src="props.content.url" controls preload="metadata" :poster="poster" />
|
||||
</div>
|
||||
<div
|
||||
v-if="isShow && !isPC"
|
||||
class="dialog-video"
|
||||
>
|
||||
<div
|
||||
class="dialog-video-close"
|
||||
@click.stop="toggleVideoPreviewer"
|
||||
>
|
||||
<div v-if="isShow && !isPC" class="dialog-video">
|
||||
<div class="dialog-video-close" @click.stop="toggleVideoPreviewer">
|
||||
<Icon :file="closeSVG" />
|
||||
</div>
|
||||
<div
|
||||
class="dialog-video-box"
|
||||
:class="[!isPC ? 'dialog-video-h5' : '']"
|
||||
@click.self="toggleVideoPreviewer"
|
||||
>
|
||||
<video
|
||||
:class="[isWidth ? 'is-width' : 'is-height']"
|
||||
:src="props.content.url"
|
||||
controls
|
||||
autoplay
|
||||
/>
|
||||
<div class="dialog-video-box" :class="[!isPC ? 'dialog-video-h5' : '']" @click.self="toggleVideoPreviewer">
|
||||
<video :class="[isWidth ? 'is-width' : 'is-height']" :src="props.content.url" controls autoplay />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
ref,
|
||||
watch,
|
||||
computed,
|
||||
nextTick,
|
||||
watchEffect,
|
||||
withDefaults,
|
||||
} from '../../../../adapter-vue';
|
||||
import { ref, watch, computed, nextTick, watchEffect, withDefaults } from '../../../../adapter-vue';
|
||||
import { IMessageModel } from '@tencentcloud/chat-uikit-engine';
|
||||
import { handleSkeletonSize } from '../../utils/utils';
|
||||
import Icon from '../../../common/Icon.vue';
|
||||
@ -85,9 +49,9 @@ const props = withDefaults(
|
||||
messageItem: IMessageModel;
|
||||
}>(),
|
||||
{
|
||||
content: () => ({} as IVideoMessageContent),
|
||||
messageItem: () => ({} as IMessageModel),
|
||||
},
|
||||
content: () => ({}) as IVideoMessageContent,
|
||||
messageItem: () => ({}) as IMessageModel
|
||||
}
|
||||
);
|
||||
|
||||
const emits = defineEmits(['uploading']);
|
||||
@ -103,8 +67,7 @@ watchEffect(async () => {
|
||||
if (!props.content) return;
|
||||
poster.value = await handlePosterUrl(props.content, props.messageItem);
|
||||
nextTick(async () => {
|
||||
const containerWidth
|
||||
= document.getElementById('messageScrollList')?.clientWidth || 0;
|
||||
const containerWidth = document.getElementById('messageScrollList')?.clientWidth || 0;
|
||||
const max = !isPC ? Math.min(containerWidth - 172, 300) : 300;
|
||||
let size;
|
||||
if (props.messageItem.status === 'success') {
|
||||
@ -116,10 +79,8 @@ watchEffect(async () => {
|
||||
snapshotHeight = posterHeight.value;
|
||||
}
|
||||
size = handleSkeletonSize(snapshotWidth, snapshotHeight, max, max);
|
||||
skeleton?.value?.style
|
||||
&& (skeleton.value.style.width = `${size.width}px`);
|
||||
skeleton?.value?.style
|
||||
&& (skeleton.value.style.height = `${size.height}px`);
|
||||
skeleton?.value?.style && (skeleton.value.style.width = `${size.width}px`);
|
||||
skeleton?.value?.style && (skeleton.value.style.height = `${size.height}px`);
|
||||
if (isPC) {
|
||||
videoRef?.value?.style && (videoRef.value.style.width = `${size.width}px`);
|
||||
videoRef?.value?.style && (videoRef.value.style.height = `${size.height}px`);
|
||||
@ -135,11 +96,14 @@ const isWidth = computed(() => {
|
||||
return snapshotWidth >= snapshotHeight;
|
||||
});
|
||||
|
||||
watch(() => props.messageItem.status, (newVal: string, oldVal: string) => {
|
||||
watch(
|
||||
() => props.messageItem.status,
|
||||
(newVal: string, oldVal: string) => {
|
||||
if (newVal === 'success' && oldVal !== 'success') {
|
||||
emits('uploading');
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
function toggleVideoPreviewer() {
|
||||
// Video upload process does not support full-screen playback.
|
||||
@ -172,7 +136,7 @@ function getVideoBase64(url: string) {
|
||||
posterHeight.value = height;
|
||||
resolve(dataURL);
|
||||
},
|
||||
{ once: true },
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -183,19 +147,17 @@ async function handlePosterUrl(messgeContent: IVideoMessageContent, messageItem:
|
||||
return await getVideoBase64(messgeContent.url);
|
||||
} else {
|
||||
return (
|
||||
(messgeContent.snapshotUrl !== transparentPosterUrl && messgeContent.snapshotUrl)
|
||||
|| (messageItem?.payload?.snapshotUrl !== transparentPosterUrl
|
||||
&& messageItem?.payload?.snapshotUrl)
|
||||
|| (messageItem.payload?.thumbUrl !== transparentPosterUrl
|
||||
&& messageItem?.payload?.thumbUrl)
|
||||
|| (await getVideoBase64(messgeContent.url))
|
||||
(messgeContent.snapshotUrl !== transparentPosterUrl && messgeContent.snapshotUrl) ||
|
||||
(messageItem?.payload?.snapshotUrl !== transparentPosterUrl && messageItem?.payload?.snapshotUrl) ||
|
||||
(messageItem.payload?.thumbUrl !== transparentPosterUrl && messageItem?.payload?.thumbUrl) ||
|
||||
(await getVideoBase64(messgeContent.url))
|
||||
);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../../assets/styles/common";
|
||||
@import '../../../../assets/styles/common';
|
||||
|
||||
.message-video {
|
||||
position: relative;
|
||||
@ -223,7 +185,7 @@ async function handlePosterUrl(messgeContent: IVideoMessageContent, messageItem:
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
img[src=""],
|
||||
img[src=''],
|
||||
img:not([src]) {
|
||||
opacity: 0;
|
||||
}
|
||||
@ -236,7 +198,7 @@ async function handlePosterUrl(messgeContent: IVideoMessageContent, messageItem:
|
||||
&::before {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
content: "";
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 10px solid transparent;
|
||||
|
@ -4,7 +4,7 @@
|
||||
:class="{
|
||||
'message-label': true,
|
||||
'unread': isUseUnreadStyle,
|
||||
'finger-point': isHoverFingerPointer,
|
||||
'finger-point': isHoverFingerPointer
|
||||
}"
|
||||
@click="openReadUserPanel"
|
||||
>
|
||||
@ -14,12 +14,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, onMounted, onUnmounted } from '../../../../../adapter-vue';
|
||||
import TUIChatEngine, {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
IMessageModel,
|
||||
TUITranslateService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import TUIChatEngine, { TUIStore, StoreName, IMessageModel, TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import TUIChatConfig from '../../../config';
|
||||
|
||||
interface IProps {
|
||||
@ -32,7 +27,7 @@ interface IEmits {
|
||||
|
||||
const emits = defineEmits<IEmits>();
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
message: () => ({}) as IMessageModel,
|
||||
message: () => ({}) as IMessageModel
|
||||
});
|
||||
const ReadStatus = TUIChatConfig.getFeatureConfig('ReadStatus');
|
||||
|
||||
@ -41,7 +36,7 @@ enum ReadState {
|
||||
Unread,
|
||||
AllRead,
|
||||
NotShow,
|
||||
PartiallyRead,
|
||||
PartiallyRead
|
||||
}
|
||||
|
||||
const TYPES = TUIChatEngine.TYPES;
|
||||
@ -50,13 +45,13 @@ const isDisplayMessageReadReceipt = ref<boolean>(TUIStore.getData(StoreName.USER
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.USER, {
|
||||
displayMessageReadReceipt: onDisplayMessageReadReceiptUpdate,
|
||||
displayMessageReadReceipt: onDisplayMessageReadReceiptUpdate
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.USER, {
|
||||
displayMessageReadReceipt: onDisplayMessageReadReceiptUpdate,
|
||||
displayMessageReadReceipt: onDisplayMessageReadReceiptUpdate
|
||||
});
|
||||
});
|
||||
|
||||
@ -67,16 +62,7 @@ const isShowReadStatus = computed<boolean>(() => {
|
||||
if (!isDisplayMessageReadReceipt.value) {
|
||||
return false;
|
||||
}
|
||||
const {
|
||||
ID,
|
||||
type,
|
||||
flow,
|
||||
status,
|
||||
hasRiskContent,
|
||||
conversationID,
|
||||
conversationType,
|
||||
needReadReceipt = false,
|
||||
} = props.message;
|
||||
const { ID, type, flow, status, hasRiskContent, conversationID, conversationType, needReadReceipt = false } = props.message;
|
||||
|
||||
// Asynchronous message strike: Determine if there is risky content after the message has been sent
|
||||
if (hasRiskContent) {
|
||||
@ -163,9 +149,9 @@ const isUseUnreadStyle = computed(() => {
|
||||
|
||||
const isHoverFingerPointer = computed<boolean>(() => {
|
||||
return (
|
||||
props.message.needReadReceipt
|
||||
&& props.message.conversationType === 'GROUP'
|
||||
&& (readState.value === ReadState.PartiallyRead || readState.value === ReadState.Unread)
|
||||
props.message.needReadReceipt &&
|
||||
props.message.conversationType === 'GROUP' &&
|
||||
(readState.value === ReadState.PartiallyRead || readState.value === ReadState.Unread)
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -2,19 +2,12 @@
|
||||
<div
|
||||
:class="{
|
||||
'simple-message-list-container': true,
|
||||
'simple-message-list-container-mobile': isMobile,
|
||||
'simple-message-list-container-mobile': isMobile
|
||||
}"
|
||||
>
|
||||
<div class="header-container">
|
||||
<span
|
||||
class="back"
|
||||
@click="backPreviousLevel"
|
||||
>
|
||||
<Icon
|
||||
class="close-icon"
|
||||
:file="addIcon"
|
||||
:size="'18px'"
|
||||
/>
|
||||
<span class="back" @click="backPreviousLevel">
|
||||
<Icon class="close-icon" :file="addIcon" :size="'18px'" />
|
||||
<span v-if="isReturn">{{ TUITranslateService.t('TUIChat.返回') }}</span>
|
||||
<span v-else>{{ TUITranslateService.t('TUIChat.关闭') }}</span>
|
||||
</span>
|
||||
@ -23,135 +16,61 @@
|
||||
{{ currentMergeMessageInfo.title }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="isDownloadOccurError">
|
||||
Load Merge Message Error
|
||||
</div>
|
||||
<div
|
||||
v-else-if="isMergeMessageInfoLoaded"
|
||||
ref="simpleMessageListRef"
|
||||
class="message-list"
|
||||
>
|
||||
<div v-if="isDownloadOccurError">Load Merge Message Error</div>
|
||||
<div v-else-if="isMergeMessageInfoLoaded" ref="simpleMessageListRef" class="message-list">
|
||||
<div
|
||||
v-for="item in currentMergeMessageInfo.messageList"
|
||||
:key="item.ID"
|
||||
:class="{
|
||||
'message-item': true,
|
||||
'message-item': true
|
||||
}"
|
||||
>
|
||||
<MessageContainer
|
||||
:sender="item.nick"
|
||||
:avatar="item.avatar"
|
||||
:type="item.messageBody[0].type"
|
||||
:time="item.time"
|
||||
>
|
||||
<MessageContainer :sender="item.nick" :avatar="item.avatar" :type="item.messageBody[0].type" :time="item.time">
|
||||
<!-- text -->
|
||||
<div
|
||||
v-if="item.messageBody[0].type === TYPES.MSG_TEXT"
|
||||
class="message-text"
|
||||
>
|
||||
<div v-if="item.messageBody[0].type === TYPES.MSG_TEXT" class="message-text">
|
||||
<span
|
||||
v-for="(textInfo, index) in parseTextToRenderArray(item.messageBody[0].payload['text'])"
|
||||
:key="index"
|
||||
class="message-text-container"
|
||||
>
|
||||
<span
|
||||
v-if="textInfo.type === 'text'"
|
||||
class="text"
|
||||
>{{ textInfo.content }}</span>
|
||||
<img
|
||||
v-else
|
||||
class="simple-emoji"
|
||||
:src="textInfo.content"
|
||||
alt="small-face"
|
||||
>
|
||||
<span v-if="textInfo.type === 'text'" class="text">{{ textInfo.content }}</span>
|
||||
<img v-else class="simple-emoji" :src="textInfo.content" alt="small-face" />
|
||||
</span>
|
||||
</div>
|
||||
<!-- image -->
|
||||
<div
|
||||
v-else-if="item.messageBody[0].type === TYPES.MSG_IMAGE"
|
||||
class="message-image"
|
||||
>
|
||||
<img
|
||||
class="image"
|
||||
:src="(item.messageBody[0].payload)['imageInfoArray'][2]['url']"
|
||||
mode="widthFix"
|
||||
alt="image"
|
||||
>
|
||||
<div v-else-if="item.messageBody[0].type === TYPES.MSG_IMAGE" class="message-image">
|
||||
<img class="image" :src="item.messageBody[0].payload['imageInfoArray'][2]['url']" mode="widthFix" alt="image" />
|
||||
</div>
|
||||
<!-- video -->
|
||||
<div
|
||||
v-else-if="item.messageBody[0].type === TYPES.MSG_VIDEO"
|
||||
class="message-video"
|
||||
>
|
||||
<div
|
||||
v-if="isUniFrameWork"
|
||||
@click="previewVideoInUniapp((item.messageBody[0].payload)['remoteVideoUrl'])"
|
||||
>
|
||||
<image
|
||||
class="image"
|
||||
:src="(item.messageBody[0].payload)['thumbUrl']"
|
||||
mode="widthFix"
|
||||
alt="image"
|
||||
/>
|
||||
<Icon
|
||||
class="video-play-icon"
|
||||
:file="playIcon"
|
||||
/>
|
||||
<div v-else-if="item.messageBody[0].type === TYPES.MSG_VIDEO" class="message-video">
|
||||
<div v-if="isUniFrameWork" @click="previewVideoInUniapp(item.messageBody[0].payload['remoteVideoUrl'])">
|
||||
<image class="image" :src="item.messageBody[0].payload['thumbUrl']" mode="widthFix" alt="image" />
|
||||
<Icon class="video-play-icon" :file="playIcon" />
|
||||
</div>
|
||||
<video
|
||||
v-else
|
||||
class="video"
|
||||
controls
|
||||
:poster="(item.messageBody[0].payload)['thumbUrl']"
|
||||
>
|
||||
<source
|
||||
:src="(item.messageBody[0].payload)['remoteVideoUrl']"
|
||||
type="video/mp4"
|
||||
>
|
||||
<video v-else class="video" controls :poster="item.messageBody[0].payload['thumbUrl']">
|
||||
<source :src="item.messageBody[0].payload['remoteVideoUrl']" type="video/mp4" />
|
||||
</video>
|
||||
</div>
|
||||
<!-- audio -->
|
||||
<div
|
||||
v-else-if="item.messageBody[0].type === TYPES.MSG_AUDIO"
|
||||
class="message-audio"
|
||||
>
|
||||
<span>{{ TUITranslateService.t("TUIChat.语音") }} </span>
|
||||
<div v-else-if="item.messageBody[0].type === TYPES.MSG_AUDIO" class="message-audio">
|
||||
<span>{{ TUITranslateService.t('TUIChat.语音') }} </span>
|
||||
<span>{{ item.messageBody[0].payload.second }}s</span>
|
||||
</div>
|
||||
<!-- big face -->
|
||||
<div
|
||||
v-else-if="item.messageBody[0].type === TYPES.MSG_FACE"
|
||||
class="message-face"
|
||||
>
|
||||
<img
|
||||
class="image"
|
||||
:src="resolveBigFaceUrl(item.messageBody[0].payload.data)"
|
||||
alt="face"
|
||||
>
|
||||
<div v-else-if="item.messageBody[0].type === TYPES.MSG_FACE" class="message-face">
|
||||
<img class="image" :src="resolveBigFaceUrl(item.messageBody[0].payload.data)" alt="face" />
|
||||
</div>
|
||||
<!-- file -->
|
||||
<div
|
||||
v-else-if="item.messageBody[0].type === TYPES.MSG_FILE"
|
||||
class="message-file"
|
||||
>
|
||||
<div v-else-if="item.messageBody[0].type === TYPES.MSG_FILE" class="message-file">
|
||||
{{ TUITranslateService.t('TUIChat.[文件]') }}
|
||||
</div>
|
||||
<!-- location -->
|
||||
<div
|
||||
v-else-if="item.messageBody[0].type === TYPES.MSG_LOCATION"
|
||||
>
|
||||
<div v-else-if="item.messageBody[0].type === TYPES.MSG_LOCATION">
|
||||
{{ TUITranslateService.t('TUIChat.[地理位置]') }}
|
||||
</div>
|
||||
<!-- merger -->
|
||||
<div
|
||||
v-else-if="item.messageBody[0].type === TYPES.MSG_MERGER"
|
||||
class="message-merger"
|
||||
@click.capture="entryNextLevel($event, item)"
|
||||
>
|
||||
<MessageRecord
|
||||
disabled
|
||||
:renderData="item.messageBody[0].payload"
|
||||
/>
|
||||
<div v-else-if="item.messageBody[0].type === TYPES.MSG_MERGER" class="message-merger" @click.capture="entryNextLevel($event, item)">
|
||||
<MessageRecord disabled :renderData="item.messageBody[0].payload" />
|
||||
</div>
|
||||
<!-- custom -->
|
||||
<div v-else-if="item.messageBody[0].type === TYPES.MSG_CUSTOM">
|
||||
@ -165,11 +84,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from '../../../../../adapter-vue';
|
||||
import TUIChatEngine, {
|
||||
TUIStore,
|
||||
TUIChatService,
|
||||
TUITranslateService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import TUIChatEngine, { TUIStore, TUIChatService, TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import addIcon from '../../../../../assets/icon/back.svg';
|
||||
import playIcon from '../../../../../assets/icon/video-play.png';
|
||||
import Icon from '../../../../common/Icon.vue';
|
||||
@ -197,7 +112,7 @@ interface IEmits {
|
||||
const emits = defineEmits<IEmits>();
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
messageID: '',
|
||||
isMounted: false,
|
||||
isMounted: false
|
||||
});
|
||||
|
||||
const TYPES = TUIChatEngine.TYPES;
|
||||
@ -205,11 +120,13 @@ const isDownloadOccurError = ref(false);
|
||||
const messageListStack = ref<IMergeMessageContent[]>([]);
|
||||
const currentMergeMessageInfo = ref<Partial<IMergeMessageContent>>({
|
||||
title: '',
|
||||
messageList: [],
|
||||
messageList: []
|
||||
});
|
||||
const simpleMessageListRef = ref<HTMLElement>();
|
||||
|
||||
watch(() => messageListStack.value.length, async (newValue) => {
|
||||
watch(
|
||||
() => messageListStack.value.length,
|
||||
async (newValue) => {
|
||||
isDownloadOccurError.value = false;
|
||||
if (newValue < 1) {
|
||||
return;
|
||||
@ -219,7 +136,7 @@ watch(() => messageListStack.value.length, async (newValue) => {
|
||||
try {
|
||||
const res = await TUIChatService.downloadMergedMessages({
|
||||
payload: stackTopMessageInfo,
|
||||
type: TUIChatEngine.TYPES.MSG_MERGER,
|
||||
type: TUIChatEngine.TYPES.MSG_MERGER
|
||||
} as any);
|
||||
// if download complete message, cover the original message in stack top
|
||||
messageListStack.value[messageListStack.value.length - 1] = res.payload;
|
||||
@ -228,9 +145,12 @@ watch(() => messageListStack.value.length, async (newValue) => {
|
||||
}
|
||||
}
|
||||
currentMergeMessageInfo.value = messageListStack.value[messageListStack.value.length - 1];
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
watch(() => props.isMounted, (newValue) => {
|
||||
watch(
|
||||
() => props.isMounted,
|
||||
(newValue) => {
|
||||
// For compatibility with uniapp, use watch to implement onMounted
|
||||
if (newValue) {
|
||||
if (!props.messageID) {
|
||||
@ -241,9 +161,11 @@ watch(() => props.isMounted, (newValue) => {
|
||||
} else {
|
||||
messageListStack.value = [];
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
});
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
const isReturn = computed(() => {
|
||||
return messageListStack.value.length > 1;
|
||||
@ -269,7 +191,7 @@ function previewVideoInUniapp(url: string) {
|
||||
if (isUniFrameWork) {
|
||||
const encodedUrl = encodeURIComponent(url);
|
||||
uni.navigateTo({
|
||||
url: `/TUIKit/components/TUIChat/video-play?videoUrl=${encodedUrl}`,
|
||||
url: `/TUIKit/components/TUIChat/video-play?videoUrl=${encodedUrl}`
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -344,7 +266,6 @@ function resolveBigFaceUrl(bigFaceKey: string): string {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.message-list {
|
||||
|
@ -39,7 +39,7 @@ interface IProps {
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
sender: '',
|
||||
avatar: '',
|
||||
avatar: ''
|
||||
});
|
||||
|
||||
const TYPES = TUIChatEngine.TYPES;
|
||||
|
@ -1,17 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
v-if="groupApplicationCount > 0"
|
||||
class="application-tips"
|
||||
>
|
||||
<div>
|
||||
{{ groupApplicationCount }}{{ TUITranslateService.t("TUIChat.条入群申请") }}
|
||||
</div>
|
||||
<div
|
||||
class="application-tips-btn"
|
||||
@click="toggleGroupApplicationDrawerShow"
|
||||
>
|
||||
{{ TUITranslateService.t("TUIChat.点击处理") }}
|
||||
<div v-if="groupApplicationCount > 0" class="application-tips">
|
||||
<div>{{ groupApplicationCount }}{{ TUITranslateService.t('TUIChat.条入群申请') }}</div>
|
||||
<div class="application-tips-btn" @click="toggleGroupApplicationDrawerShow">
|
||||
{{ TUITranslateService.t('TUIChat.点击处理') }}
|
||||
</div>
|
||||
</div>
|
||||
<Drawer
|
||||
@ -25,12 +17,12 @@
|
||||
bottom: {
|
||||
minHeight: '60vh',
|
||||
maxHeight: '80vh',
|
||||
borderRadius: '12px 12px 0 0',
|
||||
borderRadius: '12px 12px 0 0'
|
||||
},
|
||||
right: {
|
||||
width: '360px',
|
||||
borderRadius: '12px 0 0 12px',
|
||||
boxShadow: '0 0 10px 0 #d0d0d0',
|
||||
boxShadow: '0 0 10px 0 #d0d0d0'
|
||||
}
|
||||
}"
|
||||
@onOverlayClick="toggleGroupApplicationDrawerShow"
|
||||
@ -41,18 +33,10 @@
|
||||
}"
|
||||
>
|
||||
<header class="application-header">
|
||||
<div
|
||||
@click="toggleGroupApplicationDrawerShow"
|
||||
>
|
||||
<Icon
|
||||
v-if="isPC"
|
||||
:file="closeIcon"
|
||||
:size="'16px'"
|
||||
/>
|
||||
<div @click="toggleGroupApplicationDrawerShow">
|
||||
<Icon v-if="isPC" :file="closeIcon" :size="'16px'" />
|
||||
<div v-else>
|
||||
{{
|
||||
TUITranslateService.t('关闭')
|
||||
}}
|
||||
{{ TUITranslateService.t('关闭') }}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@ -62,12 +46,12 @@
|
||||
:key="item.nick"
|
||||
:class="{
|
||||
'application-item': true,
|
||||
'removed': item.isRemoved,
|
||||
'removed': item.isRemoved
|
||||
}"
|
||||
>
|
||||
<Avatar
|
||||
:style="{
|
||||
flex: '0 0 auto',
|
||||
flex: '0 0 auto'
|
||||
}"
|
||||
:url="item.avatar"
|
||||
:useSkeletonAnimation="true"
|
||||
@ -77,23 +61,15 @@
|
||||
{{ item.nick }}
|
||||
</div>
|
||||
<div class="application-item-note">
|
||||
{{ TUITranslateService.t("TUIChat.申请加入") }}
|
||||
{{ TUITranslateService.t('TUIChat.申请加入') }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="application-item-operation"
|
||||
>
|
||||
<div
|
||||
class="agree"
|
||||
@click="handleApplication(item, 'Agree', index)"
|
||||
>
|
||||
{{ TUITranslateService.t("TUIChat.同意") }}
|
||||
<div class="application-item-operation">
|
||||
<div class="agree" @click="handleApplication(item, 'Agree', index)">
|
||||
{{ TUITranslateService.t('TUIChat.同意') }}
|
||||
</div>
|
||||
<div
|
||||
class="reject"
|
||||
@click="handleApplication(item, 'Reject', index)"
|
||||
>
|
||||
{{ TUITranslateService.t("TUIChat.拒绝") }}
|
||||
<div class="reject" @click="handleApplication(item, 'Reject', index)">
|
||||
{{ TUITranslateService.t('TUIChat.拒绝') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -105,13 +81,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, watch } from '../../../../adapter-vue';
|
||||
import {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUITranslateService,
|
||||
TUIUserService,
|
||||
TUIGroupService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIStore, StoreName, TUITranslateService, TUIUserService, TUIGroupService } from '@tencentcloud/chat-uikit-engine';
|
||||
import Icon from '../../../common/Icon.vue';
|
||||
import Avatar from '../../../common/Avatar/index.vue';
|
||||
import Drawer from '../../../common/Drawer/index.vue';
|
||||
@ -130,7 +100,7 @@ interface ICustomGroupApplication {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
groupID: '',
|
||||
groupID: ''
|
||||
});
|
||||
|
||||
const drawerDomInstanceRef = ref<InstanceType<typeof Drawer>>();
|
||||
@ -147,11 +117,14 @@ watch(isGroupApplicationDrawerShow, (newVal) => {
|
||||
}
|
||||
});
|
||||
|
||||
watch(() => customGroupApplicationList.value.length, (newVal, oldVal) => {
|
||||
watch(
|
||||
() => customGroupApplicationList.value.length,
|
||||
(newVal, oldVal) => {
|
||||
if (oldVal > 0 && newVal === 0) {
|
||||
isGroupApplicationDrawerShow.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieves the current group application list based on the provided groupID.
|
||||
@ -160,7 +133,7 @@ watch(() => customGroupApplicationList.value.length, (newVal, oldVal) => {
|
||||
*/
|
||||
async function getCurrentGroupApplicationList(): Promise<IGroupApplication[]> {
|
||||
const result: IChatResponese<{ applicationList: IGroupApplication[] }> = await TUIGroupService.getGroupApplicationList();
|
||||
const currentGroupApplicationList = result.data.applicationList.filter(application => application.groupID === props.groupID);
|
||||
const currentGroupApplicationList = result.data.applicationList.filter((application) => application.groupID === props.groupID);
|
||||
return currentGroupApplicationList;
|
||||
}
|
||||
|
||||
@ -173,8 +146,8 @@ async function generateCustomGroupApplicationList(): Promise<ICustomGroupApplica
|
||||
if (applicationList.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const userIDList = applicationList.map(application => application.applicationType === 0 ? application.applicant : application.userID);
|
||||
const { data: userProfileList } = await TUIUserService.getUserProfile({ userIDList }) as IChatResponese<IUserProfile[]>;
|
||||
const userIDList = applicationList.map((application) => (application.applicationType === 0 ? application.applicant : application.userID));
|
||||
const { data: userProfileList } = (await TUIUserService.getUserProfile({ userIDList })) as IChatResponese<IUserProfile[]>;
|
||||
const mappingFromUserID2Profile: Record<string, IUserProfile> = {};
|
||||
userProfileList.forEach((profile: IUserProfile) => {
|
||||
mappingFromUserID2Profile[profile.userID] = profile;
|
||||
@ -185,7 +158,7 @@ async function generateCustomGroupApplicationList(): Promise<ICustomGroupApplica
|
||||
nick: profile.nick || profile.userID || 'anonymous',
|
||||
avatar: profile.avatar || '',
|
||||
isRemoved: false,
|
||||
application: application,
|
||||
application: application
|
||||
};
|
||||
});
|
||||
|
||||
@ -195,14 +168,16 @@ async function generateCustomGroupApplicationList(): Promise<ICustomGroupApplica
|
||||
function handleApplication(customApplication: ICustomGroupApplication, action: 'Agree' | 'Reject', index: number) {
|
||||
TUIGroupService.handleGroupApplication({
|
||||
handleAction: action,
|
||||
application: customApplication.application,
|
||||
}).then(() => {
|
||||
application: customApplication.application
|
||||
})
|
||||
.then(() => {
|
||||
customGroupApplicationList.value[index].isRemoved = true;
|
||||
setTimeout(() => {
|
||||
customGroupApplicationList.value.splice(index, 1);
|
||||
groupApplicationCount.value -= 1;
|
||||
}, 150);
|
||||
}).catch(() => {
|
||||
})
|
||||
.catch(() => {
|
||||
// TODO: handle error
|
||||
});
|
||||
}
|
||||
@ -215,13 +190,13 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.GRP, {
|
||||
groupSystemNoticeList: onGroupSystemNoticeListUpdated,
|
||||
groupSystemNoticeList: onGroupSystemNoticeListUpdated
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.GRP, {
|
||||
groupSystemNoticeList: onGroupSystemNoticeListUpdated,
|
||||
groupSystemNoticeList: onGroupSystemNoticeListUpdated
|
||||
});
|
||||
});
|
||||
|
||||
@ -318,13 +293,13 @@ function onGroupSystemNoticeListUpdated() {
|
||||
|
||||
.agree {
|
||||
color: #679ce1;
|
||||
cursor: pointer
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.reject {
|
||||
margin-left: 12px;
|
||||
color: #fb355d;
|
||||
cursor: pointer
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,26 +4,11 @@
|
||||
ref="messageToolDom"
|
||||
:class="['dialog-item', !isPC ? 'dialog-item-h5' : 'dialog-item-web']"
|
||||
>
|
||||
<slot
|
||||
v-if="featureConfig.EmojiReaction"
|
||||
name="TUIEmojiPlugin"
|
||||
/>
|
||||
<div
|
||||
class="dialog-item-list"
|
||||
:class="!isPC ? 'dialog-item-list-h5' : 'dialog-item-list-web'"
|
||||
>
|
||||
<slot v-if="featureConfig.EmojiReaction" name="TUIEmojiPlugin" />
|
||||
<div class="dialog-item-list" :class="!isPC ? 'dialog-item-list-h5' : 'dialog-item-list-web'">
|
||||
<template v-for="(item, index) in actionItems">
|
||||
<div
|
||||
v-if="item.renderCondition()"
|
||||
:key="item.key"
|
||||
class="list-item"
|
||||
@click="getFunction(index)"
|
||||
@mousedown="beforeCopy(item.key)"
|
||||
>
|
||||
<Icon
|
||||
:file="item.iconUrl"
|
||||
:size="'15px'"
|
||||
/>
|
||||
<div v-if="item.renderCondition()" :key="item.key" class="list-item" @click="getFunction(index)" @mousedown="beforeCopy(item.key)">
|
||||
<Icon :file="item.iconUrl" :size="'15px'" />
|
||||
<span class="list-item-text">{{ item.text }}</span>
|
||||
</div>
|
||||
</template>
|
||||
@ -32,12 +17,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import TUIChatEngine, {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUITranslateService,
|
||||
IMessageModel,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import TUIChatEngine, { TUIStore, StoreName, TUITranslateService, IMessageModel } from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import { ref, watchEffect, computed, onMounted, onUnmounted } from '../../../../adapter-vue';
|
||||
import Icon from '../../../common/Icon.vue';
|
||||
@ -73,7 +53,7 @@ interface IEmits {
|
||||
const emits = defineEmits<IEmits>();
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
isMultipleSelectMode: false,
|
||||
messageItem: () => ({}) as IMessageModel,
|
||||
messageItem: () => ({}) as IMessageModel
|
||||
});
|
||||
const featureConfig = TUIChatConfig.getFeatureConfig();
|
||||
|
||||
@ -86,11 +66,9 @@ const actionItems = ref([
|
||||
iconUrl: copyIcon,
|
||||
renderCondition() {
|
||||
if (!featureConfig.DownloadFile || !message.value) return false;
|
||||
return isPC && (message.value?.type === TYPES.MSG_FILE
|
||||
|| message.value.type === TYPES.MSG_VIDEO
|
||||
|| message.value.type === TYPES.MSG_IMAGE);
|
||||
return isPC && (message.value?.type === TYPES.MSG_FILE || message.value.type === TYPES.MSG_VIDEO || message.value.type === TYPES.MSG_IMAGE);
|
||||
},
|
||||
clickEvent: openMessage,
|
||||
clickEvent: openMessage
|
||||
},
|
||||
{
|
||||
key: 'copy',
|
||||
@ -100,7 +78,7 @@ const actionItems = ref([
|
||||
if (!featureConfig.CopyMessage || !message.value) return false;
|
||||
return message.value.type === TYPES.MSG_TEXT;
|
||||
},
|
||||
clickEvent: copyMessage,
|
||||
clickEvent: copyMessage
|
||||
},
|
||||
{
|
||||
key: 'revoke',
|
||||
@ -110,7 +88,7 @@ const actionItems = ref([
|
||||
if (!featureConfig.RevokeMessage || !message.value) return false;
|
||||
return message.value.flow === 'out' && message.value.status === 'success';
|
||||
},
|
||||
clickEvent: revokeMessage,
|
||||
clickEvent: revokeMessage
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
@ -120,7 +98,7 @@ const actionItems = ref([
|
||||
if (!featureConfig.DeleteMessage || !message.value) return false;
|
||||
return message.value.status === 'success';
|
||||
},
|
||||
clickEvent: deleteMessage,
|
||||
clickEvent: deleteMessage
|
||||
},
|
||||
{
|
||||
key: 'forward',
|
||||
@ -130,7 +108,7 @@ const actionItems = ref([
|
||||
if (!featureConfig.ForwardMessage || !message.value) return false;
|
||||
return message.value.status === 'success';
|
||||
},
|
||||
clickEvent: forwardSingleMessage,
|
||||
clickEvent: forwardSingleMessage
|
||||
},
|
||||
{
|
||||
key: 'quote',
|
||||
@ -141,7 +119,7 @@ const actionItems = ref([
|
||||
const _message = TUIStore.getMessageModel(message.value.ID);
|
||||
return message.value.status === 'success' && !_message.getSignalingInfo();
|
||||
},
|
||||
clickEvent: quoteMessage,
|
||||
clickEvent: quoteMessage
|
||||
},
|
||||
{
|
||||
key: 'translate',
|
||||
@ -152,7 +130,7 @@ const actionItems = ref([
|
||||
if (!featureConfig.TranslateMessage || !message.value) return false;
|
||||
return message.value.status === 'success' && message.value.type === TYPES.MSG_TEXT;
|
||||
},
|
||||
clickEvent: translateMessage,
|
||||
clickEvent: translateMessage
|
||||
},
|
||||
{
|
||||
key: 'convert',
|
||||
@ -163,7 +141,7 @@ const actionItems = ref([
|
||||
if (!featureConfig.VoiceToText || !message.value) return false;
|
||||
return message.value.status === 'success' && message.value.type === TYPES.MSG_AUDIO;
|
||||
},
|
||||
clickEvent: convertVoiceToText,
|
||||
clickEvent: convertVoiceToText
|
||||
},
|
||||
{
|
||||
key: 'multi-select',
|
||||
@ -173,8 +151,8 @@ const actionItems = ref([
|
||||
if (!featureConfig.MultiSelection || !message.value) return false;
|
||||
return message.value.status === 'success';
|
||||
},
|
||||
clickEvent: multipleSelectMessage,
|
||||
},
|
||||
clickEvent: multipleSelectMessage
|
||||
}
|
||||
]);
|
||||
|
||||
const message = ref<IMessageModel>();
|
||||
@ -183,14 +161,14 @@ const messageToolDom = ref<HTMLElement>();
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CHAT, {
|
||||
translateTextInfo: onMessageTranslationInfoUpdated,
|
||||
voiceToTextInfo: onMessageConvertInfoUpdated,
|
||||
voiceToTextInfo: onMessageConvertInfoUpdated
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CHAT, {
|
||||
translateTextInfo: onMessageTranslationInfoUpdated,
|
||||
voiceToTextInfo: onMessageConvertInfoUpdated,
|
||||
voiceToTextInfo: onMessageConvertInfoUpdated
|
||||
});
|
||||
});
|
||||
|
||||
@ -242,7 +220,7 @@ function revokeMessage() {
|
||||
const message = TUITranslateService.t('TUIChat.已过撤回时限');
|
||||
Toast({
|
||||
message,
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -257,7 +235,7 @@ function deleteMessage() {
|
||||
async function copyMessage() {
|
||||
if (isUniFrameWork) {
|
||||
TUIGlobal?.setClipboardData({
|
||||
data: transformTextWithKeysToEmojiNames(message.value?.payload?.text),
|
||||
data: transformTextWithKeysToEmojiNames(message.value?.payload?.text)
|
||||
});
|
||||
} else {
|
||||
// uni-app conditional compilation will not run the following code
|
||||
@ -295,17 +273,17 @@ function translateMessage() {
|
||||
if (!enable) {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIChat.请开通翻译功能'),
|
||||
type: TOAST_TYPE.WARNING,
|
||||
type: TOAST_TYPE.WARNING
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!message.value) return;
|
||||
const index = actionItems.value.findIndex(item => item.key === 'translate');
|
||||
const index = actionItems.value.findIndex((item) => item.key === 'translate');
|
||||
TUIStore.update(StoreName.CHAT, 'translateTextInfo', {
|
||||
conversationID: message.value.conversationID,
|
||||
messageID: message.value.ID,
|
||||
visible: !actionItems.value[index].visible,
|
||||
visible: !actionItems.value[index].visible
|
||||
});
|
||||
}
|
||||
|
||||
@ -313,17 +291,17 @@ function convertVoiceToText() {
|
||||
const enable = TUIStore.getData(StoreName.APP, 'enabledVoiceToText');
|
||||
if (!enable) {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIChat.请开通语音转文字功能'),
|
||||
message: TUITranslateService.t('TUIChat.请开通语音转文字功能')
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!message.value) return;
|
||||
const index = actionItems.value.findIndex(item => item.key === 'convert');
|
||||
const index = actionItems.value.findIndex((item) => item.key === 'convert');
|
||||
TUIStore.update(StoreName.CHAT, 'voiceToTextInfo', {
|
||||
conversationID: message.value.conversationID,
|
||||
messageID: message.value.ID,
|
||||
visible: !actionItems.value[index].visible,
|
||||
visible: !actionItems.value[index].visible
|
||||
});
|
||||
}
|
||||
|
||||
@ -334,7 +312,7 @@ function multipleSelectMessage() {
|
||||
function onMessageTranslationInfoUpdated(info: Map<string, ITranslateInfo[]>) {
|
||||
if (info === undefined) return;
|
||||
const translationInfoList = info.get(props.messageItem.conversationID) || [];
|
||||
const idx = actionItems.value.findIndex(item => item.key === 'translate');
|
||||
const idx = actionItems.value.findIndex((item) => item.key === 'translate');
|
||||
for (let i = 0; i < translationInfoList.length; ++i) {
|
||||
const { messageID, visible } = translationInfoList[i];
|
||||
if (messageID === props.messageItem.ID) {
|
||||
@ -349,7 +327,7 @@ function onMessageTranslationInfoUpdated(info: Map<string, ITranslateInfo[]>) {
|
||||
function onMessageConvertInfoUpdated(info: Map<string, IConvertInfo[]>) {
|
||||
if (info === undefined) return;
|
||||
const convertInfoList = info.get(props.messageItem.conversationID) || [];
|
||||
const idx = actionItems.value.findIndex(item => item.key === 'convert');
|
||||
const idx = actionItems.value.findIndex((item) => item.key === 'convert');
|
||||
for (let i = 0; i < convertInfoList.length; ++i) {
|
||||
const { messageID, visible } = convertInfoList[i];
|
||||
if (messageID === props.messageItem.ID) {
|
||||
@ -362,12 +340,12 @@ function onMessageConvertInfoUpdated(info: Map<string, IConvertInfo[]>) {
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
messageToolDom,
|
||||
messageToolDom
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../../assets/styles/common";
|
||||
@import '../../../../assets/styles/common';
|
||||
|
||||
.dialog-item-web {
|
||||
background: #fff;
|
||||
|
@ -1,14 +1,10 @@
|
||||
<template>
|
||||
<div class="revoke">
|
||||
<span v-if="message.flow === 'in'">{{ message.nick || message.from }}</span>
|
||||
<span v-else-if="message.from === message.revoker">{{ TUITranslateService.t("TUIChat.您") }}</span>
|
||||
<span v-else-if="message.from === message.revoker">{{ TUITranslateService.t('TUIChat.您') }}</span>
|
||||
<span v-else>{{ message.revoker }}</span>
|
||||
<span>{{ TUITranslateService.t("TUIChat.撤回了一条消息") }}</span>
|
||||
<span
|
||||
v-if="message.flow === 'out' && isEditMsg"
|
||||
class="edit"
|
||||
@click="messageEdit"
|
||||
>{{ TUITranslateService.t("TUIChat.重新编辑") }}</span>
|
||||
<span>{{ TUITranslateService.t('TUIChat.撤回了一条消息') }}</span>
|
||||
<span v-if="message.flow === 'out' && isEditMsg" class="edit" @click="messageEdit">{{ TUITranslateService.t('TUIChat.重新编辑') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -18,12 +14,12 @@ import { TUITranslateService, IMessageModel } from '@tencentcloud/chat-uikit-eng
|
||||
const props = defineProps({
|
||||
isEdit: {
|
||||
type: Boolean,
|
||||
default: () => false,
|
||||
default: () => false
|
||||
},
|
||||
messageItem: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
|
||||
const message = ref<IMessageModel>();
|
||||
@ -39,7 +35,7 @@ const messageEdit = () => {
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../../assets/styles/common";
|
||||
@import '../../../../assets/styles/common';
|
||||
|
||||
.revoke {
|
||||
display: flex;
|
||||
|
@ -1,27 +1,19 @@
|
||||
<template>
|
||||
<Overlay
|
||||
:maskColor="'transparent'"
|
||||
@onOverlayClick="closeReadReceiptPanel"
|
||||
>
|
||||
<Overlay :maskColor="'transparent'" @onOverlayClick="closeReadReceiptPanel">
|
||||
<div
|
||||
:class="{
|
||||
'read-receipt-panel': true,
|
||||
'read-receipt-panel-mobile': isMobile,
|
||||
'read-receipt-panel-uni': isUniFrameWork,
|
||||
'read-receipt-panel-close-mobile': isMobile && isPanelClose,
|
||||
'read-receipt-panel-close-mobile': isMobile && isPanelClose
|
||||
}"
|
||||
>
|
||||
<div class="header">
|
||||
<div class="header-text">
|
||||
{{ TUITranslateService.t("TUIChat.消息详情") }}
|
||||
{{ TUITranslateService.t('TUIChat.消息详情') }}
|
||||
</div>
|
||||
<div class="header-close-icon">
|
||||
<Icon
|
||||
size="12px"
|
||||
hotAreaSize="8"
|
||||
:file="closeIcon"
|
||||
@onClick="closeReadReceiptPanel"
|
||||
/>
|
||||
<Icon size="12px" hotAreaSize="8" :file="closeIcon" @onClick="closeReadReceiptPanel" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="read-status-counter-container">
|
||||
@ -30,7 +22,7 @@
|
||||
:key="tabName"
|
||||
:class="{
|
||||
'read-status-counter': true,
|
||||
'active': tabName === currentTabName,
|
||||
'active': tabName === currentTabName
|
||||
}"
|
||||
@click="toggleTabName(tabName)"
|
||||
>
|
||||
@ -38,60 +30,34 @@
|
||||
{{ tabInfo[tabName].tabName }}
|
||||
</div>
|
||||
<div class="status-count">
|
||||
{{ tabInfo[tabName].count === undefined ? "" : tabInfo[tabName].count }}
|
||||
{{ tabInfo[tabName].count === undefined ? '' : tabInfo[tabName].count }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="read-status-member-list">
|
||||
<div
|
||||
v-if="tabInfo[currentTabName].count === 0 && isFirstLoadFinished"
|
||||
class="empty-list-tip"
|
||||
>
|
||||
<div v-if="tabInfo[currentTabName].count === 0 && isFirstLoadFinished" class="empty-list-tip">
|
||||
- {{ TUITranslateService.t('TUIChat.空') }} -
|
||||
</div>
|
||||
<template v-else-if="isFirstLoadFinished">
|
||||
<template v-if="currentTabName === 'unread'">
|
||||
<div
|
||||
v-for="item in tabInfo[currentTabName].memberList"
|
||||
:key="item.userID"
|
||||
class="read-status-member-container"
|
||||
>
|
||||
<Avatar
|
||||
class="read-status-avatar"
|
||||
useSkeletonAnimation
|
||||
:url="item.avatar || ''"
|
||||
/>
|
||||
<div v-for="item in tabInfo[currentTabName].memberList" :key="item.userID" class="read-status-member-container">
|
||||
<Avatar class="read-status-avatar" useSkeletonAnimation :url="item.avatar || ''" />
|
||||
<div class="username">
|
||||
{{ item.nick || item.userID }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="currentTabName === 'read'">
|
||||
<div
|
||||
v-for="item in tabInfo[currentTabName].memberList"
|
||||
:key="item.userID"
|
||||
class="read-status-member-container"
|
||||
>
|
||||
<Avatar
|
||||
class="read-status-avatar"
|
||||
useSkeletonAnimation
|
||||
:url="item.avatar"
|
||||
/>
|
||||
<div v-for="item in tabInfo[currentTabName].memberList" :key="item.userID" class="read-status-member-container">
|
||||
<Avatar class="read-status-avatar" useSkeletonAnimation :url="item.avatar" />
|
||||
<div class="username">
|
||||
{{ item.nick || item.userID }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<div
|
||||
v-if="isFirstLoadFinished"
|
||||
class="fetch-more-container"
|
||||
>
|
||||
<FetchMore
|
||||
:isFetching="isPullDownFetching"
|
||||
:isTerminateObserve="isStopFetchMore"
|
||||
@onExposed="pullDownFetchMoreData"
|
||||
/>
|
||||
<div v-if="isFirstLoadFinished" class="fetch-more-container">
|
||||
<FetchMore :isFetching="isPullDownFetching" :isTerminateObserve="isStopFetchMore" @onExposed="pullDownFetchMoreData" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -122,7 +88,7 @@ interface IEmits {
|
||||
|
||||
const emits = defineEmits<IEmits>();
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
message: () => ({}) as IMessageModel,
|
||||
message: () => ({}) as IMessageModel
|
||||
});
|
||||
|
||||
let lastUnreadCursor: string = '';
|
||||
@ -131,7 +97,7 @@ const tabNameList: TabName[] = ['unread', 'read'];
|
||||
const isListFetchCompleted: Record<TabName, boolean> = {
|
||||
unread: false,
|
||||
read: false,
|
||||
close: false,
|
||||
close: false
|
||||
};
|
||||
|
||||
const isPullDownFetching = ref<boolean>(false);
|
||||
@ -152,7 +118,7 @@ watch(
|
||||
() => props.message.readReceiptInfo.readCount,
|
||||
() => {
|
||||
initAndRefetchReceiptInfomation();
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
async function fetchGroupMessageRecriptMemberListByType(readType: ReadType = 'all') {
|
||||
@ -166,7 +132,7 @@ async function fetchGroupMessageRecriptMemberListByType(readType: ReadType = 'al
|
||||
message,
|
||||
filter: 1,
|
||||
cursor: lastUnreadCursor,
|
||||
count: 100,
|
||||
count: 100
|
||||
});
|
||||
if (unreadResult) {
|
||||
lastUnreadCursor = unreadResult.data.cursor;
|
||||
@ -181,7 +147,7 @@ async function fetchGroupMessageRecriptMemberListByType(readType: ReadType = 'al
|
||||
message,
|
||||
filter: 0,
|
||||
cursor: lastReadCursor,
|
||||
count: 100,
|
||||
count: 100
|
||||
});
|
||||
if (readResult) {
|
||||
lastReadCursor = readResult.data.cursor;
|
||||
@ -197,12 +163,12 @@ async function fetchGroupMessageRecriptMemberListByType(readType: ReadType = 'al
|
||||
return {
|
||||
unreadResult: {
|
||||
count: totalUnreadCount,
|
||||
...unreadResult.data,
|
||||
...unreadResult.data
|
||||
},
|
||||
readResult: {
|
||||
count: totalReadCount,
|
||||
...readResult.data,
|
||||
},
|
||||
...readResult.data
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -281,18 +247,18 @@ function generateInitalTabInfo(): ITabInfo {
|
||||
read: {
|
||||
tabName: TUITranslateService.t('TUIChat.已读'),
|
||||
count: undefined,
|
||||
memberList: [],
|
||||
memberList: []
|
||||
},
|
||||
unread: {
|
||||
tabName: TUITranslateService.t('TUIChat.未读'),
|
||||
count: undefined,
|
||||
memberList: [],
|
||||
memberList: []
|
||||
},
|
||||
close: {
|
||||
tabName: TUITranslateService.t('TUIChat.关闭'),
|
||||
count: undefined,
|
||||
memberList: [],
|
||||
},
|
||||
memberList: []
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="isScrollButtonVisible"
|
||||
class="scroll-button"
|
||||
@click="scrollToMessageListBottom"
|
||||
>
|
||||
<Icon
|
||||
width="10px"
|
||||
height="10px"
|
||||
:file="doubleArrowIcon"
|
||||
/>
|
||||
<div v-if="isScrollButtonVisible" class="scroll-button" @click="scrollToMessageListBottom">
|
||||
<Icon width="10px" height="10px" :file="doubleArrowIcon" />
|
||||
<div class="scroll-button-text">
|
||||
{{ scrollButtonContent }}
|
||||
</div>
|
||||
@ -17,13 +9,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted, computed, watch } from '../../../../adapter-vue';
|
||||
import {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
IMessageModel,
|
||||
IConversationModel,
|
||||
TUITranslateService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIStore, StoreName, IMessageModel, IConversationModel, TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import Icon from '../../../common/Icon.vue';
|
||||
import doubleArrowIcon from '../../../../assets/icon/double-arrow.svg';
|
||||
import { getBoundingClientRect } from '@tencentcloud/universal-api';
|
||||
@ -42,36 +28,37 @@ const isScrollOverOneScreen = ref<boolean>(false);
|
||||
const isExistLastMessage = ref<boolean>(false);
|
||||
const isScrollButtonVisible = ref<boolean>(false);
|
||||
const scrollButtonContent = computed(() =>
|
||||
newMessageCount.value ? `${newMessageCount.value}${TUITranslateService.t('TUIChat.条新消息')}` : TUITranslateService.t('TUIChat.回到最新位置'),
|
||||
newMessageCount.value ? `${newMessageCount.value}${TUITranslateService.t('TUIChat.条新消息')}` : TUITranslateService.t('TUIChat.回到最新位置')
|
||||
);
|
||||
|
||||
watch(() => [isScrollOverOneScreen.value, isExistLastMessage.value],
|
||||
watch(
|
||||
() => [isScrollOverOneScreen.value, isExistLastMessage.value],
|
||||
() => {
|
||||
isScrollButtonVisible.value = isScrollOverOneScreen.value || isExistLastMessage.value;
|
||||
if (!isScrollButtonVisible.value) {
|
||||
resetNewMessageCount();
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CHAT, {
|
||||
messageList: onMessageListUpdated,
|
||||
newMessageList: onNewMessageListUpdated,
|
||||
newMessageList: onNewMessageListUpdated
|
||||
});
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversation: onCurrentConversationUpdated,
|
||||
currentConversation: onCurrentConversationUpdated
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CHAT, {
|
||||
messageList: onMessageListUpdated,
|
||||
newMessageList: onNewMessageListUpdated,
|
||||
newMessageList: onNewMessageListUpdated
|
||||
});
|
||||
TUIStore.unwatch(StoreName.CONV, {
|
||||
currentConversation: onCurrentConversationUpdated,
|
||||
currentConversation: onCurrentConversationUpdated
|
||||
});
|
||||
});
|
||||
|
||||
@ -82,15 +69,19 @@ function isTypingMessage(message: IMessageModel): boolean {
|
||||
function onMessageListUpdated(newMessageList: IMessageModel[]) {
|
||||
messageList.value = newMessageList || [];
|
||||
const lastMessage = messageList.value?.[messageList.value?.length - 1];
|
||||
isExistLastMessage.value = !!(
|
||||
lastMessage && lastMessage?.time < currentLastMessageTime?.value
|
||||
);
|
||||
isExistLastMessage.value = !!(lastMessage && lastMessage?.time < currentLastMessageTime?.value);
|
||||
}
|
||||
|
||||
function onNewMessageListUpdated(newMessageList: IMessageModel[]) {
|
||||
if (Array.isArray(newMessageList) && isScrollButtonVisible.value) {
|
||||
newMessageList.forEach((message: IMessageModel) => {
|
||||
if (message && message.conversationID === currentConversationID.value && !message.isDeleted && !message.isRevoked && !isTypingMessage(message)) {
|
||||
if (
|
||||
message &&
|
||||
message.conversationID === currentConversationID.value &&
|
||||
!message.isDeleted &&
|
||||
!message.isRevoked &&
|
||||
!isTypingMessage(message)
|
||||
) {
|
||||
newMessageCount.value += 1;
|
||||
}
|
||||
});
|
||||
@ -109,7 +100,7 @@ function onCurrentConversationUpdated(conversation: IConversationModel | undefin
|
||||
async function judgeScrollOverOneScreen(e: Event) {
|
||||
if (e.target) {
|
||||
try {
|
||||
const { height } = await getBoundingClientRect(`#${(e.target as HTMLElement)?.id}`, 'messageList') || {};
|
||||
const { height } = (await getBoundingClientRect(`#${(e.target as HTMLElement)?.id}`, 'messageList')) || {};
|
||||
const scrollHeight = (e.target as HTMLElement)?.scrollHeight || (e.detail as HTMLElement)?.scrollHeight;
|
||||
const scrollTop = (e.target as HTMLElement)?.scrollTop || (e.detail as HTMLElement)?.scrollTop || 0;
|
||||
// while scroll over one screen show this scroll button.
|
||||
@ -144,7 +135,7 @@ function scrollToMessageListBottom() {
|
||||
|
||||
defineExpose({
|
||||
judgeScrollOverOneScreen,
|
||||
isScrollButtonVisible,
|
||||
isScrollButtonVisible
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -2,52 +2,35 @@
|
||||
<div
|
||||
:class="{
|
||||
'mulitple-select-panel': true,
|
||||
'mulitple-select-panel-mobile': isMobile,
|
||||
'mulitple-select-panel-mobile': isMobile
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="forward-button"
|
||||
@click="oneByOneForwardMessage"
|
||||
>
|
||||
<Icon
|
||||
:file="ForwardEachIcon"
|
||||
:size="iconSize"
|
||||
/>
|
||||
<div class="forward-button" @click="oneByOneForwardMessage">
|
||||
<Icon :file="ForwardEachIcon" :size="iconSize" />
|
||||
<span
|
||||
:class="{
|
||||
'forward-button-text': true,
|
||||
'forward-button-text-mobile': isMobile,
|
||||
'forward-button-text-mobile': isMobile
|
||||
}"
|
||||
>{{ TUITranslateService.t('TUIChat.逐条转发') }}</span>
|
||||
>{{ TUITranslateService.t('TUIChat.逐条转发') }}</span
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="forward-button"
|
||||
@click="mergeForwardMessage"
|
||||
>
|
||||
<Icon
|
||||
:file="ForwardMergeIcon"
|
||||
:size="iconSize"
|
||||
/>
|
||||
<div class="forward-button" @click="mergeForwardMessage">
|
||||
<Icon :file="ForwardMergeIcon" :size="iconSize" />
|
||||
<span
|
||||
:class="{
|
||||
'forward-button-text': true,
|
||||
'forward-button-text-mobile': isMobile,
|
||||
'forward-button-text-mobile': isMobile
|
||||
}"
|
||||
>{{ TUITranslateService.t('TUIChat.合并转发') }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="forward-button"
|
||||
@click="cancelMultipleSelect"
|
||||
>{{ TUITranslateService.t('TUIChat.合并转发') }}</span
|
||||
>
|
||||
<Icon
|
||||
class="cancel-button-icon"
|
||||
:file="AddIcon"
|
||||
:size="iconSize"
|
||||
/>
|
||||
</div>
|
||||
<div class="forward-button" @click="cancelMultipleSelect">
|
||||
<Icon class="cancel-button-icon" :file="AddIcon" :size="iconSize" />
|
||||
<span
|
||||
:class="{
|
||||
'forward-button-text': true,
|
||||
'forward-button-text-mobile': isMobile,
|
||||
'forward-button-text-mobile': isMobile
|
||||
}"
|
||||
>
|
||||
{{ TUITranslateService.t('TUIChat.取消') }}
|
||||
@ -58,9 +41,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from '../../../adapter-vue';
|
||||
import {
|
||||
TUITranslateService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import Icon from '../../common/Icon.vue';
|
||||
import ForwardEachIcon from '../../../assets/icon/forward-each.svg';
|
||||
import ForwardMergeIcon from '../../../assets/icon/forward-merge.svg';
|
||||
@ -104,7 +85,7 @@ function cancelMultipleSelect() {
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
background-color: #EBF0F6;
|
||||
background-color: #ebf0f6;
|
||||
|
||||
&-mobile {
|
||||
height: 64px;
|
||||
|
@ -8,10 +8,10 @@ export const DEFAULT_DESC: any = {
|
||||
[TUIChatEngine.TYPES.MSG_VIDEO]: '[视频]',
|
||||
[TUIChatEngine.TYPES.MSG_LOCATION]: '[地理位置]',
|
||||
[TUIChatEngine.TYPES.MSG_MERGER]: '[聊天记录]',
|
||||
[TUIChatEngine.TYPES.MSG_CUSTOM]: '[自定义消息]',
|
||||
[TUIChatEngine.TYPES.MSG_CUSTOM]: '[自定义消息]'
|
||||
};
|
||||
|
||||
export enum PUSH_SCENE {
|
||||
CHAT = 'chat',
|
||||
CALL = 'call',
|
||||
CALL = 'call'
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { IChatOfflinePushInfo, ICallOfflinePushInfo } from './interface';
|
||||
|
||||
export const chatOfflinePushInfo: IChatOfflinePushInfo = {
|
||||
androidInfo: {},
|
||||
apnsInfo: {},
|
||||
apnsInfo: {}
|
||||
};
|
||||
|
||||
export const callOfflinePushInfo: ICallOfflinePushInfo = {};
|
||||
|
@ -1,9 +1,6 @@
|
||||
import TUIChatEngine, { IConversationModel, StoreName, TUIStore, TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import { transformTextWithKeysToEmojiNames } from '../emoji-config';
|
||||
import {
|
||||
IChatOfflinePushInfo,
|
||||
IOfflinePushInfoCreateParams,
|
||||
} from './interface';
|
||||
import { IChatOfflinePushInfo, IOfflinePushInfoCreateParams } from './interface';
|
||||
import { chatOfflinePushInfo, callOfflinePushInfo } from './info';
|
||||
import { DEFAULT_DESC, PUSH_SCENE } from './const';
|
||||
|
||||
@ -16,7 +13,7 @@ class OfflinePushInfoManager {
|
||||
private constructor() {
|
||||
this.offlinePushInfo = {
|
||||
[PUSH_SCENE.CHAT]: chatOfflinePushInfo,
|
||||
[PUSH_SCENE.CALL]: callOfflinePushInfo,
|
||||
[PUSH_SCENE.CALL]: callOfflinePushInfo
|
||||
};
|
||||
}
|
||||
|
||||
@ -62,13 +59,13 @@ class OfflinePushInfoManager {
|
||||
nickName: userInfo?.nick,
|
||||
chatType: conversation.type === TUIChatEngine.TYPES.CONV_GROUP ? 2 : 1,
|
||||
version: 1,
|
||||
action: 1,
|
||||
action: 1
|
||||
};
|
||||
return {
|
||||
title: this.genTitle(conversation, userInfo),
|
||||
description: this.genDesc(messageType, payload),
|
||||
extension: JSON.stringify({ entity }),
|
||||
...this.offlinePushInfo[PUSH_SCENE.CHAT],
|
||||
...this.offlinePushInfo[PUSH_SCENE.CHAT]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,5 @@
|
||||
import TUICore, { TUIConstants } from '@tencentcloud/tui-core';
|
||||
import {
|
||||
IMessageModel,
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUIChatService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { IMessageModel, TUIStore, StoreName, TUIChatService } from '@tencentcloud/chat-uikit-engine';
|
||||
import TUIChatConfig from './config';
|
||||
|
||||
export default class TUIChatServer {
|
||||
@ -21,7 +16,7 @@ export default class TUIChatServer {
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversationID: (id: string) => {
|
||||
this.currentConversationID = id;
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
StoreName,
|
||||
TUIConversationService,
|
||||
TUIStore,
|
||||
TUITranslateService,
|
||||
TUITranslateService
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { transformTextWithKeysToEmojiNames } from '../emoji-config';
|
||||
import { JSONToObject } from '../../../utils/index';
|
||||
@ -22,7 +22,12 @@ class ConversationDraftManager {
|
||||
return ConversationDraftManager.instance;
|
||||
}
|
||||
|
||||
public setStore(conversationID: string, draftContent: string, abstract: string, quoteMessage?: { type: 'quote' | 'reply'; message: IMessageModel }) {
|
||||
public setStore(
|
||||
conversationID: string,
|
||||
draftContent: string,
|
||||
abstract: string,
|
||||
quoteMessage?: { type: 'quote' | 'reply'; message: IMessageModel }
|
||||
) {
|
||||
if (conversationID && (this.isEditorNotEmpty(draftContent) || quoteMessage?.message?.ID)) {
|
||||
let additionalDraftInfo = {};
|
||||
if (quoteMessage?.message?.ID) {
|
||||
@ -34,8 +39,8 @@ class ConversationDraftManager {
|
||||
draftInfo: {
|
||||
html: draftContent,
|
||||
abstract: abstract,
|
||||
...additionalDraftInfo,
|
||||
},
|
||||
...additionalDraftInfo
|
||||
}
|
||||
};
|
||||
TUIConversationService.setConversationDraft(draftParams);
|
||||
TUIStore.update(StoreName.CHAT, 'quoteMessage', { message: undefined, type: 'quote' });
|
||||
@ -49,7 +54,10 @@ class ConversationDraftManager {
|
||||
}
|
||||
if (conversation.conversationID && conversation.draftText) {
|
||||
const draftObject = JSONToObject(conversation.draftText);
|
||||
TUIStore.update(StoreName.CHAT, 'quoteMessage', { message: this.quoteMessageMap.get(draftObject.messageID) || undefined, type: draftObject.type });
|
||||
TUIStore.update(StoreName.CHAT, 'quoteMessage', {
|
||||
message: this.quoteMessageMap.get(draftObject.messageID) || undefined,
|
||||
type: draftObject.type
|
||||
});
|
||||
setEditorContentCallback(draftObject.html);
|
||||
}
|
||||
TUIConversationService.setConversationDraft({ conversationID: conversation.conversationID });
|
||||
|
@ -1,8 +1,4 @@
|
||||
import {
|
||||
IMessageModel,
|
||||
TUIChatService,
|
||||
TUIStore,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { IMessageModel, TUIChatService, TUIStore } from '@tencentcloud/chat-uikit-engine';
|
||||
import { IChatResponese } from '../../../interface';
|
||||
|
||||
class Convertor {
|
||||
@ -35,7 +31,7 @@ class Convertor {
|
||||
}
|
||||
// step3: get response from api
|
||||
const response: IChatResponese<{ result: string }> = await TUIChatService.convertVoiceToText({
|
||||
message: currentMessage,
|
||||
message: currentMessage
|
||||
});
|
||||
let { data: { result } = {} } = response;
|
||||
if (result) {
|
||||
|
@ -33,7 +33,7 @@ class CopyManager {
|
||||
anchorNode: selection.anchorNode,
|
||||
anchorOffset: selection.anchorOffset,
|
||||
focusNode: selection.focusNode,
|
||||
focusOffset: selection.focusOffset,
|
||||
focusOffset: selection.focusOffset
|
||||
};
|
||||
}
|
||||
|
||||
@ -49,9 +49,8 @@ class CopyManager {
|
||||
}
|
||||
|
||||
const range = document.createRange();
|
||||
const isForwardSelection = anchorNode === focusNode
|
||||
? anchorOffset <= focusOffset
|
||||
: anchorNode.compareDocumentPosition(focusNode) & Node.DOCUMENT_POSITION_FOLLOWING;
|
||||
const isForwardSelection =
|
||||
anchorNode === focusNode ? anchorOffset <= focusOffset : anchorNode.compareDocumentPosition(focusNode) & Node.DOCUMENT_POSITION_FOLLOWING;
|
||||
if (isForwardSelection) {
|
||||
range.setStart(anchorNode, anchorOffset);
|
||||
range.setEnd(focusNode, focusOffset);
|
||||
@ -88,7 +87,7 @@ class CopyManager {
|
||||
}
|
||||
|
||||
public async copyTextOrHtml(content: string, type: 'text' | 'html'): Promise<void> {
|
||||
const mimeType = (type === 'html') ? 'text/html' : 'text/plain';
|
||||
const mimeType = type === 'html' ? 'text/html' : 'text/plain';
|
||||
// prefer use clipboard api to copy node or text
|
||||
if (navigator.clipboard) {
|
||||
try {
|
||||
@ -97,7 +96,7 @@ class CopyManager {
|
||||
await navigator.clipboard.write([clipboardItem]);
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIChat.复制成功'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
return;
|
||||
} catch (err) {
|
||||
@ -132,12 +131,12 @@ class CopyManager {
|
||||
document.execCommand('copy');
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIChat.复制成功'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
} catch (err) {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIChat.此机型暂不支持复制'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
console.warn('use document.execCommand copy failed:', err);
|
||||
} finally {
|
||||
|
@ -4,7 +4,7 @@ import TUIChatEngine, {
|
||||
StoreName,
|
||||
TUITranslateService,
|
||||
IConversationModel,
|
||||
SendMessageParams,
|
||||
SendMessageParams
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { Toast, TOAST_TYPE } from '../../common/Toast/index';
|
||||
import { isEnabledMessageReadReceiptGlobal } from '../utils/utils';
|
||||
@ -21,7 +21,7 @@ export const sendMessageErrorCodeMap: Map<number, string> = new Map([
|
||||
[8001, '消息长度超出限制,消息长度不要超过12K'],
|
||||
[80001, '消息或者资料中文本存在敏感内容,发送失败'],
|
||||
[80004, '消息中图片存在敏感内容,发送失败'],
|
||||
[10017, '您已被禁止聊天'],
|
||||
[10017, '您已被禁止聊天']
|
||||
]);
|
||||
|
||||
export const createOfflinePushInfo = (conversation: IConversationModel) => {
|
||||
@ -33,12 +33,12 @@ export const createOfflinePushInfo = (conversation: IConversationModel) => {
|
||||
nickName: userInfo.nick,
|
||||
chatType: conversation.type === TUIChatEngine.TYPES.CONV_GROUP ? 2 : 1,
|
||||
version: 1,
|
||||
action: 1,
|
||||
action: 1
|
||||
};
|
||||
return {
|
||||
extension: JSON.stringify({ entity }),
|
||||
androidInfo,
|
||||
apnsInfo,
|
||||
apnsInfo
|
||||
};
|
||||
};
|
||||
|
||||
@ -47,10 +47,7 @@ export const createOfflinePushInfo = (conversation: IConversationModel) => {
|
||||
* @param messageList
|
||||
* @param currentConversation
|
||||
*/
|
||||
export const sendMessages = async (
|
||||
messageList: ITipTapEditorContent[],
|
||||
currentConversation: IConversationModel,
|
||||
) => {
|
||||
export const sendMessages = async (messageList: ITipTapEditorContent[], currentConversation: IConversationModel) => {
|
||||
// In case of messageJumping, the sent message is automatically cleared and returns to the bottom
|
||||
if (TUIStore.getData(StoreName.CHAT, 'messageSource')) {
|
||||
TUIStore.update(StoreName.CHAT, 'messageSource', undefined);
|
||||
@ -61,17 +58,17 @@ export const sendMessages = async (
|
||||
to: currentConversation?.groupProfile?.groupID || currentConversation?.userProfile?.userID,
|
||||
conversationType: currentConversation?.type as any,
|
||||
payload: {},
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal(),
|
||||
needReadReceipt: isEnabledMessageReadReceiptGlobal()
|
||||
};
|
||||
// handle message typing
|
||||
let textMessageContent;
|
||||
const sendMessageOptions = {
|
||||
offlinePushInfo: {},
|
||||
offlinePushInfo: {}
|
||||
};
|
||||
const offlinePushInfoCreateParams: IOfflinePushInfoCreateParams = {
|
||||
conversation: currentConversation,
|
||||
payload: content.payload,
|
||||
messageType: '',
|
||||
messageType: ''
|
||||
};
|
||||
switch (content?.type) {
|
||||
case 'text':
|
||||
@ -81,7 +78,7 @@ export const sendMessages = async (
|
||||
break;
|
||||
}
|
||||
options.payload = {
|
||||
text: textMessageContent,
|
||||
text: textMessageContent
|
||||
};
|
||||
offlinePushInfoCreateParams.messageType = TUIChatEngine.TYPES.MSG_TEXT;
|
||||
sendMessageOptions.offlinePushInfo = OfflinePushInfoManager.create(offlinePushInfoCreateParams);
|
||||
@ -94,7 +91,7 @@ export const sendMessages = async (
|
||||
break;
|
||||
case 'image':
|
||||
options.payload = {
|
||||
file: content.payload?.file,
|
||||
file: content.payload?.file
|
||||
};
|
||||
offlinePushInfoCreateParams.messageType = TUIChatEngine.TYPES.MSG_IMAGE;
|
||||
sendMessageOptions.offlinePushInfo = OfflinePushInfoManager.create(offlinePushInfoCreateParams);
|
||||
@ -102,7 +99,7 @@ export const sendMessages = async (
|
||||
break;
|
||||
case 'video':
|
||||
options.payload = {
|
||||
file: content.payload?.file,
|
||||
file: content.payload?.file
|
||||
};
|
||||
offlinePushInfoCreateParams.messageType = TUIChatEngine.TYPES.MSG_VIDEO;
|
||||
sendMessageOptions.offlinePushInfo = OfflinePushInfoManager.create(offlinePushInfoCreateParams);
|
||||
@ -110,7 +107,7 @@ export const sendMessages = async (
|
||||
break;
|
||||
case 'file':
|
||||
options.payload = {
|
||||
file: content.payload?.file,
|
||||
file: content.payload?.file
|
||||
};
|
||||
offlinePushInfoCreateParams.messageType = TUIChatEngine.TYPES.MSG_FILE;
|
||||
sendMessageOptions.offlinePushInfo = OfflinePushInfoManager.create(offlinePushInfoCreateParams);
|
||||
@ -125,7 +122,7 @@ export const sendMessages = async (
|
||||
message: sendMessageErrorCodeMap.get(error?.code)
|
||||
? TUITranslateService.t(`TUIChat.${sendMessageErrorCodeMap.get(error.code) as string}`)
|
||||
: error?.message,
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
// If the message fails to be sent and the message is a reference message, clear the reference message information
|
||||
if (TUIStore.getData(StoreName.CHAT, 'quoteMessage')) {
|
||||
@ -141,7 +138,7 @@ export const handleMessageWithTyping = (cloudCustomData: any) => {
|
||||
}
|
||||
cloudCustomData.messageFeature = {
|
||||
needTyping: 1,
|
||||
version: 1,
|
||||
version: 1
|
||||
};
|
||||
return cloudCustomData;
|
||||
};
|
||||
|
@ -1,10 +1,4 @@
|
||||
import TUIChatEngine, {
|
||||
IMessageModel,
|
||||
TUIChatService,
|
||||
TUIStore,
|
||||
TUITranslateService,
|
||||
TUIUserService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import TUIChatEngine, { IMessageModel, TUIChatService, TUIStore, TUITranslateService, TUIUserService } from '@tencentcloud/chat-uikit-engine';
|
||||
import { IChatResponese, IUserProfile } from '../../../interface';
|
||||
|
||||
/**
|
||||
@ -80,20 +74,22 @@ class Translator {
|
||||
|
||||
// step4: filter plain text to be translated
|
||||
const needTranslateTextIndex: number[] = [];
|
||||
const needTranslateText = textList.filter((item, index) => {
|
||||
const needTranslateText = textList
|
||||
.filter((item, index) => {
|
||||
if (item.type === 'text' && item.value.trim() !== '') {
|
||||
needTranslateTextIndex.push(index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}).map(item => item.value);
|
||||
})
|
||||
.map((item) => item.value);
|
||||
|
||||
if (needTranslateText.length === 0) {
|
||||
this.translationCache.set(currentMessage.ID, textList);
|
||||
return textList;
|
||||
}
|
||||
// step5: get final translation result
|
||||
const translationResult = await this.getTranslationStandard(needTranslateText) as string[];
|
||||
const translationResult = (await this.getTranslationStandard(needTranslateText)) as string[];
|
||||
translationResult.forEach((item, index) => {
|
||||
textList[needTranslateTextIndex[index]].value = item;
|
||||
});
|
||||
@ -122,11 +118,11 @@ class Translator {
|
||||
return new Promise((resolve, reject) => {
|
||||
TUIChatService.translateText({
|
||||
sourceTextList: originTextList,
|
||||
sourceLanguage: 'auto',
|
||||
sourceLanguage: 'auto'
|
||||
})
|
||||
.then((response: IChatResponese<{ translatedTextList: string[] }>) => {
|
||||
const {
|
||||
data: { translatedTextList },
|
||||
data: { translatedTextList }
|
||||
} = response;
|
||||
resolve(translatedTextList);
|
||||
})
|
||||
@ -149,7 +145,7 @@ class Translator {
|
||||
splittingList.push(`@${TUITranslateService.t('TUIChat.所有人')}`);
|
||||
}
|
||||
if (atUserList.length > 0) {
|
||||
const { data: userProfileList } = await TUIUserService.getUserProfile({ userIDList: atUserList }) as IChatResponese<IUserProfile[]>;
|
||||
const { data: userProfileList } = (await TUIUserService.getUserProfile({ userIDList: atUserList })) as IChatResponese<IUserProfile[]>;
|
||||
userProfileList.forEach((user) => {
|
||||
const atNick = `@${user.nick || user.userID}`;
|
||||
splittingList.push(atNick);
|
||||
@ -195,7 +191,7 @@ class Translator {
|
||||
}
|
||||
return {
|
||||
transSplitingList,
|
||||
atNickList,
|
||||
atNickList
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -27,21 +27,13 @@ export function deepCopy(data: any, hash = new WeakMap()) {
|
||||
return newData;
|
||||
}
|
||||
|
||||
export const handleSkeletonSize = (
|
||||
width: number,
|
||||
height: number,
|
||||
maxWidth: number,
|
||||
maxHeight: number,
|
||||
): { width: number; height: number } => {
|
||||
export const handleSkeletonSize = (width: number, height: number, maxWidth: number, maxHeight: number): { width: number; height: number } => {
|
||||
const widthToHeight = width / height;
|
||||
const maxWidthToHeight = maxWidth / maxHeight;
|
||||
if (width <= maxWidth && height <= maxHeight) {
|
||||
return { width, height };
|
||||
}
|
||||
if (
|
||||
(width <= maxWidth && height > maxHeight)
|
||||
|| (width > maxWidth && height > maxHeight && widthToHeight <= maxWidthToHeight)
|
||||
) {
|
||||
if ((width <= maxWidth && height > maxHeight) || (width > maxWidth && height > maxHeight && widthToHeight <= maxWidthToHeight)) {
|
||||
return { width: width * (maxHeight / height), height: maxHeight };
|
||||
}
|
||||
return { width: maxWidth, height: height * (maxWidth / width) };
|
||||
@ -76,10 +68,7 @@ export function getImgLoad(container: any, className: string, callback: any) {
|
||||
}
|
||||
|
||||
export const isCreateGroupCustomMessage = (message: IMessageModel) => {
|
||||
return (
|
||||
message.type === TUIChatEngine.TYPES.MSG_CUSTOM
|
||||
&& message?.getMessageContent()?.businessID === 'group_create'
|
||||
);
|
||||
return message.type === TUIChatEngine.TYPES.MSG_CUSTOM && message?.getMessageContent()?.businessID === 'group_create';
|
||||
};
|
||||
|
||||
/**
|
||||
@ -91,8 +80,7 @@ export const isCreateGroupCustomMessage = (message: IMessageModel) => {
|
||||
* @return {boolean} - Returns a boolean value indicating if the message read receipt is enabled globally.
|
||||
*/
|
||||
export function isEnabledMessageReadReceiptGlobal(): boolean {
|
||||
return TUIStore.getData(StoreName.USER, 'displayMessageReadReceipt')
|
||||
&& TUIStore.getData(StoreName.APP, 'enabledMessageReadReceipt');
|
||||
return TUIStore.getData(StoreName.USER, 'displayMessageReadReceipt') && TUIStore.getData(StoreName.APP, 'enabledMessageReadReceipt');
|
||||
}
|
||||
|
||||
export function shallowCopyMessage(message: IMessageModel) {
|
||||
@ -102,15 +90,7 @@ export function shallowCopyMessage(message: IMessageModel) {
|
||||
// calculate timestamp
|
||||
export function calculateTimestamp(timestamp: number): string {
|
||||
const todayZero = new Date().setHours(0, 0, 0, 0);
|
||||
const thisYear = new Date(
|
||||
new Date().getFullYear(),
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
).getTime();
|
||||
const thisYear = new Date(new Date().getFullYear(), 0, 1, 0, 0, 0, 0).getTime();
|
||||
const target = new Date(timestamp);
|
||||
|
||||
const oneDay = 24 * 60 * 60 * 1000;
|
||||
@ -127,35 +107,17 @@ export function calculateTimestamp(timestamp: number): string {
|
||||
return `${formatNum(target.getHours())}:${formatNum(target.getMinutes())}`;
|
||||
} else if (diff <= oneDay) {
|
||||
// yesterday, display yesterday:hour:minute
|
||||
return `${TUITranslateService.t('time.昨天')} ${formatNum(
|
||||
target.getHours(),
|
||||
)}:${formatNum(target.getMinutes())}`;
|
||||
return `${TUITranslateService.t('time.昨天')} ${formatNum(target.getHours())}:${formatNum(target.getMinutes())}`;
|
||||
} else if (diff <= oneWeek - oneDay) {
|
||||
// Within a week, display weekday hour:minute
|
||||
const weekdays = [
|
||||
'星期日',
|
||||
'星期一',
|
||||
'星期二',
|
||||
'星期三',
|
||||
'星期四',
|
||||
'星期五',
|
||||
'星期六',
|
||||
];
|
||||
const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
||||
const weekday = weekdays[target.getDay()];
|
||||
return `${TUITranslateService.t('time.' + weekday)} ${formatNum(
|
||||
target.getHours(),
|
||||
)}:${formatNum(target.getMinutes())}`;
|
||||
return `${TUITranslateService.t('time.' + weekday)} ${formatNum(target.getHours())}:${formatNum(target.getMinutes())}`;
|
||||
} else if (target.getTime() >= thisYear) {
|
||||
// Over a week, within this year, display mouth/day hour:minute
|
||||
return `${target.getMonth() + 1}/${target.getDate()} ${formatNum(
|
||||
target.getHours(),
|
||||
)}:${formatNum(target.getMinutes())}`;
|
||||
return `${target.getMonth() + 1}/${target.getDate()} ${formatNum(target.getHours())}:${formatNum(target.getMinutes())}`;
|
||||
} else {
|
||||
// Not within this year, display year/mouth/day hour:minute
|
||||
return `${target.getFullYear()}/${
|
||||
target.getMonth() + 1
|
||||
}/${target.getDate()} ${formatNum(target.getHours())}:${formatNum(
|
||||
target.getMinutes(),
|
||||
)}`;
|
||||
return `${target.getFullYear()}/${target.getMonth() + 1}/${target.getDate()} ${formatNum(target.getHours())}:${formatNum(target.getMinutes())}`;
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,29 @@
|
||||
export const wordsList = [
|
||||
{
|
||||
value: '在吗?在吗?在吗?重要的话说三遍。',
|
||||
value: '在吗?在吗?在吗?重要的话说三遍。'
|
||||
},
|
||||
{
|
||||
value: '好久没聊天了,快来和我说说话~',
|
||||
value: '好久没聊天了,快来和我说说话~'
|
||||
},
|
||||
{
|
||||
value: '好的,就这么说定了。',
|
||||
value: '好的,就这么说定了。'
|
||||
},
|
||||
{
|
||||
value: '感恩的心,感谢有你。',
|
||||
value: '感恩的心,感谢有你。'
|
||||
},
|
||||
{
|
||||
value: '糟糕!是心动的感觉!',
|
||||
value: '糟糕!是心动的感觉!'
|
||||
},
|
||||
{
|
||||
value: '心疼地抱抱自己,我太难了!',
|
||||
value: '心疼地抱抱自己,我太难了!'
|
||||
},
|
||||
{
|
||||
value: '没关系,别在意,事情过去就过去了。',
|
||||
value: '没关系,别在意,事情过去就过去了。'
|
||||
},
|
||||
{
|
||||
value: '早上好,今天也是让人期待的一天呢!',
|
||||
value: '早上好,今天也是让人期待的一天呢!'
|
||||
},
|
||||
{
|
||||
value: '熬夜有什么用,又没人陪你聊天,早点休息吧。',
|
||||
},
|
||||
value: '熬夜有什么用,又没人陪你聊天,早点休息吧。'
|
||||
}
|
||||
];
|
||||
|
@ -1,9 +1,5 @@
|
||||
import { TUIStore, StoreName } from '@tencentcloud/chat-uikit-engine';
|
||||
import {
|
||||
CONTACT_INFO_LABEL_POSITION,
|
||||
CONTACT_INFO_MORE_EDIT_TYPE,
|
||||
CONTACT_INFO_BUTTON_TYPE,
|
||||
} from '../../../constant';
|
||||
import { CONTACT_INFO_LABEL_POSITION, CONTACT_INFO_MORE_EDIT_TYPE, CONTACT_INFO_BUTTON_TYPE } from '../../../constant';
|
||||
import {
|
||||
updateFriendRemark,
|
||||
deleteFriend,
|
||||
@ -15,7 +11,7 @@ import {
|
||||
acceptFriendApplication,
|
||||
refuseFriendApplication,
|
||||
addToBlacklist,
|
||||
removeFromBlacklist,
|
||||
removeFromBlacklist
|
||||
} from '../utils/index';
|
||||
|
||||
export const contactMoreInfoConfig = {
|
||||
@ -28,11 +24,7 @@ export const contactMoreInfoConfig = {
|
||||
editable: true,
|
||||
editType: CONTACT_INFO_MORE_EDIT_TYPE.INPUT,
|
||||
editing: false,
|
||||
editSubmitHandler: (props: {
|
||||
item: any;
|
||||
contactInfoData: any;
|
||||
[propsName: string]: any;
|
||||
}) => {
|
||||
editSubmitHandler: (props: { item: any; contactInfoData: any; [propsName: string]: any }) => {
|
||||
if (props?.isBothFriend) {
|
||||
const newRemarkValue = props?.item?.data;
|
||||
updateFriendRemark(props?.contactInfoData?.userID, newRemarkValue);
|
||||
@ -41,7 +33,7 @@ export const contactMoreInfoConfig = {
|
||||
} else {
|
||||
props?.item?.editing && (props.item.editing = false);
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
// blocked list
|
||||
blackList: {
|
||||
@ -52,18 +44,14 @@ export const contactMoreInfoConfig = {
|
||||
editable: true,
|
||||
editType: CONTACT_INFO_MORE_EDIT_TYPE.SWITCH,
|
||||
editing: true,
|
||||
editSubmitHandler: (props: {
|
||||
item: any;
|
||||
contactInfoData: any;
|
||||
[propsName: string]: any;
|
||||
}) => {
|
||||
editSubmitHandler: (props: { item: any; contactInfoData: any; [propsName: string]: any }) => {
|
||||
if (props?.isInBlackList) {
|
||||
removeFromBlacklist(props?.contactInfoData?.userID);
|
||||
} else {
|
||||
addToBlacklist(props?.contactInfoData?.userID);
|
||||
TUIStore.update(StoreName.CUSTOM, 'currentContactListKey', 'blackList');
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
// Fill in verification words (applicant)
|
||||
setWords: {
|
||||
@ -73,7 +61,7 @@ export const contactMoreInfoConfig = {
|
||||
labelPosition: CONTACT_INFO_LABEL_POSITION.TOP,
|
||||
editable: true,
|
||||
editType: CONTACT_INFO_MORE_EDIT_TYPE.TEXTAREA,
|
||||
editing: true,
|
||||
editing: true
|
||||
},
|
||||
// Display verification words (application recipient)
|
||||
displayWords: {
|
||||
@ -81,8 +69,8 @@ export const contactMoreInfoConfig = {
|
||||
label: '验证信息',
|
||||
data: '',
|
||||
labelPosition: CONTACT_INFO_LABEL_POSITION.LEFT,
|
||||
editable: false,
|
||||
},
|
||||
editable: false
|
||||
}
|
||||
};
|
||||
|
||||
export const contactButtonConfig = {
|
||||
@ -95,7 +83,7 @@ export const contactButtonConfig = {
|
||||
type: CONTACT_INFO_BUTTON_TYPE.CANCEL,
|
||||
onClick: (props: { contactInfoData: any; [propsName: string]: any }) => {
|
||||
dismissGroup(props?.contactInfoData?.groupID);
|
||||
},
|
||||
}
|
||||
},
|
||||
quitGroup: {
|
||||
key: 'quitGroup',
|
||||
@ -103,34 +91,23 @@ export const contactButtonConfig = {
|
||||
type: CONTACT_INFO_BUTTON_TYPE.CANCEL,
|
||||
onClick: (props: { contactInfoData: any; [propsName: string]: any }) => {
|
||||
quitGroup(props?.contactInfoData?.groupID);
|
||||
},
|
||||
}
|
||||
},
|
||||
joinGroup: {
|
||||
key: 'joinGroup',
|
||||
label: '发送申请',
|
||||
type: CONTACT_INFO_BUTTON_TYPE.SUBMIT,
|
||||
onClick: (props: {
|
||||
contactInfoData: any;
|
||||
contactInfoMoreList: any;
|
||||
[propsName: string]: any;
|
||||
}) => {
|
||||
joinGroup(
|
||||
props?.contactInfoData?.groupID,
|
||||
props?.contactInfoMoreList[0]?.data,
|
||||
);
|
||||
},
|
||||
onClick: (props: { contactInfoData: any; contactInfoMoreList: any; [propsName: string]: any }) => {
|
||||
joinGroup(props?.contactInfoData?.groupID, props?.contactInfoMoreList[0]?.data);
|
||||
}
|
||||
},
|
||||
joinAVChatGroup: {
|
||||
key: 'joinAVChatGroup',
|
||||
label: '加入直播群',
|
||||
type: CONTACT_INFO_BUTTON_TYPE.SUBMIT,
|
||||
onClick: (props: {
|
||||
contactInfoData: any;
|
||||
contactInfoMoreList: any;
|
||||
[propsName: string]: any;
|
||||
}) => {
|
||||
onClick: (props: { contactInfoData: any; contactInfoMoreList: any; [propsName: string]: any }) => {
|
||||
joinGroup(props?.contactInfoData?.groupID);
|
||||
},
|
||||
}
|
||||
},
|
||||
enterGroupConversation: {
|
||||
key: 'enterGroupConversation',
|
||||
@ -138,7 +115,7 @@ export const contactButtonConfig = {
|
||||
type: CONTACT_INFO_BUTTON_TYPE.SUBMIT,
|
||||
onClick: (props: { contactInfoData: any; [propsName: string]: any }) => {
|
||||
enterConversation(props?.contactInfoData);
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
// ---------------------
|
||||
@ -148,18 +125,14 @@ export const contactButtonConfig = {
|
||||
key: 'addFriend',
|
||||
label: '发送申请',
|
||||
type: CONTACT_INFO_BUTTON_TYPE.SUBMIT,
|
||||
onClick: (props: {
|
||||
contactInfoData: any;
|
||||
contactInfoMoreList: any;
|
||||
[propsName: string]: any;
|
||||
}) => {
|
||||
onClick: (props: { contactInfoData: any; contactInfoMoreList: any; [propsName: string]: any }) => {
|
||||
addFriend({
|
||||
to: props?.contactInfoData?.userID,
|
||||
source: 'AddSource_Type_Web',
|
||||
remark: props?.contactInfoMoreList[1]?.data,
|
||||
wording: props?.contactInfoMoreList[0]?.data,
|
||||
wording: props?.contactInfoMoreList[0]?.data
|
||||
});
|
||||
},
|
||||
}
|
||||
},
|
||||
deleteFriend: {
|
||||
key: 'deleteFriend',
|
||||
@ -167,7 +140,7 @@ export const contactButtonConfig = {
|
||||
type: CONTACT_INFO_BUTTON_TYPE.CANCEL,
|
||||
onClick: (props: { contactInfoData: any; [propsName: string]: any }) => {
|
||||
deleteFriend(props?.contactInfoData?.userID);
|
||||
},
|
||||
}
|
||||
},
|
||||
enterC2CConversation: {
|
||||
key: 'enterC2CConversation',
|
||||
@ -175,7 +148,7 @@ export const contactButtonConfig = {
|
||||
type: CONTACT_INFO_BUTTON_TYPE.SUBMIT,
|
||||
onClick: (props: { contactInfoData: any; [propsName: string]: any }) => {
|
||||
enterConversation(props?.contactInfoData);
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
// ---------------------
|
||||
@ -188,7 +161,7 @@ export const contactButtonConfig = {
|
||||
onClick: (props: { contactInfoData: any; [propsName: string]: any }) => {
|
||||
acceptFriendApplication(props?.contactInfoData?.userID);
|
||||
TUIStore.update(StoreName.CUSTOM, 'currentContactListKey', 'friendList');
|
||||
},
|
||||
}
|
||||
},
|
||||
refuseFriendApplication: {
|
||||
key: 'refuseFriendApplication',
|
||||
@ -196,6 +169,6 @@ export const contactButtonConfig = {
|
||||
type: CONTACT_INFO_BUTTON_TYPE.CANCEL,
|
||||
onClick: (props: { contactInfoData: any; [propsName: string]: any }) => {
|
||||
refuseFriendApplication(props?.contactInfoData?.userID);
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -3,53 +3,23 @@
|
||||
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"
|
||||
>
|
||||
<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 :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',
|
||||
]"
|
||||
>
|
||||
<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',
|
||||
]"
|
||||
:class="['tui-contact-info-basic-text-other', !isPC && 'tui-contact-info-h5-basic-text-other']"
|
||||
>
|
||||
{{
|
||||
`${TUITranslateService.t(`TUIContact.${item.label}`)}:
|
||||
@ -57,117 +27,62 @@
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<img
|
||||
:class="[
|
||||
'tui-contact-info-basic-avatar',
|
||||
!isPC && 'tui-contact-info-h5-basic-avatar',
|
||||
]"
|
||||
:src="generateAvatar(contactInfoData)"
|
||||
>
|
||||
<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-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',
|
||||
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',
|
||||
]"
|
||||
>
|
||||
<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',
|
||||
]"
|
||||
: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"
|
||||
/>
|
||||
<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',
|
||||
]"
|
||||
: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',
|
||||
]"
|
||||
: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)"
|
||||
>
|
||||
<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',
|
||||
]"
|
||||
>
|
||||
<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`,
|
||||
item.type === CONTACT_INFO_BUTTON_TYPE.CANCEL ? `tui-contact-info-button-item-cancel` : `tui-contact-info-button-item-submit`
|
||||
]"
|
||||
@click="onContactInfoButtonClicked(item)"
|
||||
>
|
||||
@ -177,43 +92,19 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import TUIChatEngine, {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUITranslateService,
|
||||
IGroupModel,
|
||||
Friend,
|
||||
FriendApplication,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
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 { 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 { 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;
|
||||
@ -229,9 +120,7 @@ const setEditing = (item: any) => {
|
||||
item.editing = true;
|
||||
};
|
||||
|
||||
const isGroup = computed((): boolean =>
|
||||
(contactInfoData.value as IGroupModel)?.groupID ? true : false,
|
||||
);
|
||||
const isGroup = computed((): boolean => ((contactInfoData.value as IGroupModel)?.groupID ? true : false));
|
||||
|
||||
const isApplication = computed((): boolean => {
|
||||
return isApplicationType(contactInfoData?.value);
|
||||
@ -248,11 +137,8 @@ const isGroupMember = computed((): boolean => {
|
||||
// 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
|
||||
!isGroup.value &&
|
||||
blackList.value?.findIndex((item: IBlackListUserItem) => item?.userID === (contactInfoData.value as IBlackListUserItem)?.userID) >= 0
|
||||
);
|
||||
});
|
||||
|
||||
@ -260,19 +146,19 @@ const blackList = ref<IBlackListUserItem[]>([]);
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CUSTOM, {
|
||||
currentContactInfo: onCurrentContactInfoUpdated,
|
||||
currentContactInfo: onCurrentContactInfoUpdated
|
||||
});
|
||||
TUIStore.watch(StoreName.USER, {
|
||||
userBlacklist: onUserBlacklistUpdated,
|
||||
userBlacklist: onUserBlacklistUpdated
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CUSTOM, {
|
||||
currentContactInfo: onCurrentContactInfoUpdated,
|
||||
currentContactInfo: onCurrentContactInfoUpdated
|
||||
});
|
||||
TUIStore.unwatch(StoreName.USER, {
|
||||
userBlacklist: onUserBlacklistUpdated,
|
||||
userBlacklist: onUserBlacklistUpdated
|
||||
});
|
||||
});
|
||||
|
||||
@ -290,25 +176,22 @@ const resetContactSearchingUIData = () => {
|
||||
};
|
||||
|
||||
const onContactInfoEmitSubmit = (item: any) => {
|
||||
item.editSubmitHandler
|
||||
&& item.editSubmitHandler({
|
||||
item.editSubmitHandler &&
|
||||
item.editSubmitHandler({
|
||||
item,
|
||||
contactInfoData: contactInfoData.value,
|
||||
isBothFriend: isBothFriend.value,
|
||||
isInBlackList: isInBlackList.value,
|
||||
isInBlackList: isInBlackList.value
|
||||
});
|
||||
};
|
||||
|
||||
const onContactInfoButtonClicked = (item: any) => {
|
||||
item.onClick
|
||||
&& item.onClick({
|
||||
item.onClick &&
|
||||
item.onClick({
|
||||
contactInfoData: contactInfoData.value,
|
||||
contactInfoMoreList: contactInfoMoreList.value,
|
||||
contactInfoMoreList: contactInfoMoreList.value
|
||||
});
|
||||
if (
|
||||
item.key === 'enterGroupConversation'
|
||||
|| item.key === 'enterC2CConversation'
|
||||
) {
|
||||
if (item.key === 'enterGroupConversation' || item.key === 'enterC2CConversation') {
|
||||
emits('switchConversation', contactInfoData.value);
|
||||
resetContactSearchingUIData();
|
||||
}
|
||||
@ -317,17 +200,14 @@ const onContactInfoButtonClicked = (item: any) => {
|
||||
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)
|
||||
(!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.data = (contactInfoData.value as Friend)?.remark || '';
|
||||
contactMoreInfoConfig.setRemark.editing = false;
|
||||
contactInfoMoreList.value.push(contactMoreInfoConfig.setRemark);
|
||||
}
|
||||
@ -336,8 +216,7 @@ const generateMoreInfo = async () => {
|
||||
contactInfoMoreList.value.push(contactMoreInfoConfig.blackList);
|
||||
}
|
||||
} else {
|
||||
contactMoreInfoConfig.displayWords.data
|
||||
= (contactInfoData.value as FriendApplication)?.wording || '';
|
||||
contactMoreInfoConfig.displayWords.data = (contactInfoData.value as FriendApplication)?.wording || '';
|
||||
contactInfoMoreList.value.push(contactMoreInfoConfig.displayWords);
|
||||
}
|
||||
};
|
||||
@ -347,16 +226,9 @@ const generateButton = () => {
|
||||
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,
|
||||
);
|
||||
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) {
|
||||
@ -368,20 +240,16 @@ const generateButton = () => {
|
||||
contactInfoButtonList?.value?.push(contactButtonConfig.quitGroup);
|
||||
break;
|
||||
}
|
||||
contactInfoButtonList?.value?.push(
|
||||
contactButtonConfig.enterGroupConversation,
|
||||
);
|
||||
contactInfoButtonList?.value?.push(contactButtonConfig.enterGroupConversation);
|
||||
} else if (!isGroup.value && isBothFriend.value) {
|
||||
contactInfoButtonList?.value?.push(contactButtonConfig.deleteFriend);
|
||||
contactInfoButtonList?.value?.push(
|
||||
contactButtonConfig.enterC2CConversation,
|
||||
);
|
||||
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,
|
||||
: contactButtonConfig.joinGroup
|
||||
);
|
||||
} else {
|
||||
contactInfoButtonList?.value?.push(contactButtonConfig.addFriend);
|
||||
@ -395,11 +263,7 @@ function onUserBlacklistUpdated(userBlacklist: IBlackListUserItem[]) {
|
||||
}
|
||||
|
||||
async function onCurrentContactInfoUpdated(contactInfo: IContactInfoType) {
|
||||
if (
|
||||
contactInfoData.value
|
||||
&& contactInfo
|
||||
&& JSON.stringify(contactInfoData.value) === JSON.stringify(contactInfo)
|
||||
) {
|
||||
if (contactInfoData.value && contactInfo && JSON.stringify(contactInfoData.value) === JSON.stringify(contactInfo)) {
|
||||
return;
|
||||
}
|
||||
resetContactInfoUIData();
|
||||
@ -408,9 +272,7 @@ async function onCurrentContactInfoUpdated(contactInfo: IContactInfoType) {
|
||||
if (!contactInfoData.value || Object.keys(contactInfoData.value)?.length === 0) {
|
||||
return;
|
||||
}
|
||||
contactInfoBasicList.value = generateContactInfoBasic(
|
||||
contactInfoData.value,
|
||||
);
|
||||
contactInfoBasicList.value = generateContactInfoBasic(contactInfoData.value);
|
||||
isBothFriend.value = await isFriend(contactInfoData.value);
|
||||
generateMoreInfo();
|
||||
generateButton();
|
||||
|
@ -1,17 +1,13 @@
|
||||
<template>
|
||||
<div :class="['tui-contact-list-card', !isPC && 'tui-contact-list-card-h5']">
|
||||
<div class="tui-contact-list-card-left">
|
||||
<Avatar
|
||||
class="tui-contact-list-card-left-avatar"
|
||||
useSkeletonAnimation
|
||||
:url="generateAvatar(props.item)"
|
||||
/>
|
||||
<Avatar class="tui-contact-list-card-left-avatar" useSkeletonAnimation :url="generateAvatar(props.item)" />
|
||||
<div
|
||||
v-if="props.displayOnlineStatus && props.item"
|
||||
:class="{
|
||||
'online-status': true,
|
||||
'online-status-online': isOnline,
|
||||
'online-status-offline': !isOnline,
|
||||
'online-status-offline': !isOnline
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
@ -19,28 +15,16 @@
|
||||
<div class="tui-contact-list-card-main-name">
|
||||
{{ generateName(props.item) }}
|
||||
</div>
|
||||
<div
|
||||
v-if="otherContentForSow"
|
||||
class="tui-contact-list-card-main-other"
|
||||
>
|
||||
<div v-if="otherContentForSow" class="tui-contact-list-card-main-other">
|
||||
{{ otherContentForSow }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tui-contact-list-card-right">
|
||||
<div
|
||||
v-if="groupTypeForShow"
|
||||
class="tui-contact-list-card-right-group-type"
|
||||
>
|
||||
<div v-if="groupTypeForShow" class="tui-contact-list-card-right-group-type">
|
||||
{{ groupTypeForShow }}
|
||||
</div>
|
||||
<div
|
||||
v-if="showApplicationStatus"
|
||||
class="tui-contact-list-card-right-application"
|
||||
>
|
||||
<div
|
||||
v-if="showApplicationStatus.style === 'text'"
|
||||
class="tui-contact-list-card-right-application-text"
|
||||
>
|
||||
<div v-if="showApplicationStatus" class="tui-contact-list-card-right-application">
|
||||
<div v-if="showApplicationStatus.style === 'text'" class="tui-contact-list-card-right-application-text">
|
||||
{{ TUITranslateService.t(`TUIContact.${showApplicationStatus.label}`) }}
|
||||
</div>
|
||||
<button
|
||||
@ -56,12 +40,7 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, withDefaults, inject, watch, ref, Ref } from '../../../../adapter-vue';
|
||||
import TUIChatEngine, {
|
||||
TUITranslateService,
|
||||
IGroupModel,
|
||||
FriendApplication,
|
||||
Friend,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import TUIChatEngine, { TUITranslateService, IGroupModel, FriendApplication, Friend } from '@tencentcloud/chat-uikit-engine';
|
||||
import { IContactInfoType, IUserStatus } from '../../../../interface';
|
||||
import Avatar from '../../../common/Avatar/index.vue';
|
||||
import { generateAvatar, generateName, acceptFriendApplication } from '../../utils';
|
||||
@ -73,9 +52,9 @@ const props = withDefaults(
|
||||
displayOnlineStatus?: boolean;
|
||||
}>(),
|
||||
{
|
||||
item: () => ({} as IContactInfoType),
|
||||
displayOnlineStatus: false,
|
||||
},
|
||||
item: () => ({}) as IContactInfoType,
|
||||
displayOnlineStatus: false
|
||||
}
|
||||
);
|
||||
const userOnlineStatusMap = inject<Ref<Map<string, IUserStatus>>>('userOnlineStatusMap');
|
||||
const isOnline = ref<boolean>(false);
|
||||
@ -85,14 +64,14 @@ const groupType = {
|
||||
[TUIChatEngine.TYPES.GRP_AVCHATROOM]: 'AVChatRoom',
|
||||
[TUIChatEngine.TYPES.GRP_PUBLIC]: 'Public',
|
||||
[TUIChatEngine.TYPES.GRP_MEETING]: 'Meeting',
|
||||
[TUIChatEngine.TYPES.GRP_COMMUNITY]: 'Community',
|
||||
[TUIChatEngine.TYPES.GRP_COMMUNITY]: 'Community'
|
||||
};
|
||||
|
||||
const otherContentForSow = computed((): string => {
|
||||
let content = '';
|
||||
if (
|
||||
(props.item as FriendApplication)?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_TO_ME
|
||||
|| (props.item as FriendApplication)?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_BY_ME
|
||||
(props.item as FriendApplication)?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_TO_ME ||
|
||||
(props.item as FriendApplication)?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_BY_ME
|
||||
) {
|
||||
content = (props.item as FriendApplication)?.wording || '';
|
||||
} else if ((props.item as IGroupModel)?.groupID) {
|
||||
@ -110,22 +89,18 @@ const groupTypeForShow = computed((): string => {
|
||||
});
|
||||
|
||||
const showApplicationStatus = computed(() => {
|
||||
if (
|
||||
(props.item as FriendApplication)?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_BY_ME
|
||||
) {
|
||||
if ((props.item as FriendApplication)?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_BY_ME) {
|
||||
return {
|
||||
style: 'text',
|
||||
label: '等待验证',
|
||||
label: '等待验证'
|
||||
};
|
||||
} else if (
|
||||
(props.item as FriendApplication)?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_TO_ME
|
||||
) {
|
||||
} else if ((props.item as FriendApplication)?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_TO_ME) {
|
||||
return {
|
||||
style: 'button',
|
||||
label: '同意',
|
||||
onClick: () => {
|
||||
acceptFriendApplication((props.item as FriendApplication)?.userID);
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
return false;
|
||||
@ -138,16 +113,16 @@ watch(
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
);
|
||||
|
||||
function getOnlineStatus(): boolean {
|
||||
return !!(
|
||||
props.displayOnlineStatus
|
||||
&& userOnlineStatusMap?.value
|
||||
&& (props.item as Friend)?.userID
|
||||
&& userOnlineStatusMap.value?.[(props.item as Friend).userID]?.statusType === TUIChatEngine.TYPES.USER_STATUS_ONLINE
|
||||
props.displayOnlineStatus &&
|
||||
userOnlineStatusMap?.value &&
|
||||
(props.item as Friend)?.userID &&
|
||||
userOnlineStatusMap.value?.[(props.item as Friend).userID]?.statusType === TUIChatEngine.TYPES.USER_STATUS_ONLINE
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
@ -1,30 +1,13 @@
|
||||
<template>
|
||||
<ul
|
||||
v-if="!contactSearchingStatus"
|
||||
:class="['tui-contact-list', !isPC && 'tui-contact-list-h5']"
|
||||
>
|
||||
<li
|
||||
v-for="(contactListObj, key) in contactListMap"
|
||||
:key="key"
|
||||
class="tui-contact-list-item"
|
||||
>
|
||||
<header
|
||||
class="tui-contact-list-item-header"
|
||||
@click="toggleCurrentContactList(key)"
|
||||
>
|
||||
<ul v-if="!contactSearchingStatus" :class="['tui-contact-list', !isPC && 'tui-contact-list-h5']">
|
||||
<li v-for="(contactListObj, key) in contactListMap" :key="key" class="tui-contact-list-item">
|
||||
<header class="tui-contact-list-item-header" @click="toggleCurrentContactList(key)">
|
||||
<div class="tui-contact-list-item-header-left">
|
||||
<Icon
|
||||
:file="currentContactListKey === key ? downSVG : rightSVG"
|
||||
width="16px"
|
||||
height="16px"
|
||||
/>
|
||||
<Icon :file="currentContactListKey === key ? downSVG : rightSVG" width="16px" height="16px" />
|
||||
<div>{{ TUITranslateService.t(`TUIContact.${contactListObj.title}`) }}</div>
|
||||
</div>
|
||||
<div class="tui-contact-list-item-header-right">
|
||||
<span
|
||||
v-if="contactListObj.unreadCount"
|
||||
class="tui-contact-list-item-header-right-unread"
|
||||
>
|
||||
<span v-if="contactListObj.unreadCount" class="tui-contact-list-item-header-right-unread">
|
||||
{{ contactListObj.unreadCount }}
|
||||
</span>
|
||||
</div>
|
||||
@ -46,19 +29,9 @@
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ul
|
||||
v-else
|
||||
class="tui-contact-list"
|
||||
>
|
||||
<li
|
||||
v-for="(item, key) in contactSearchResult"
|
||||
:key="key"
|
||||
class="tui-contact-list-item"
|
||||
>
|
||||
<div
|
||||
v-if="item.list[0]"
|
||||
class="tui-contact-search-list"
|
||||
>
|
||||
<ul v-else class="tui-contact-list">
|
||||
<li v-for="(item, key) in contactSearchResult" :key="key" class="tui-contact-list-item">
|
||||
<div v-if="item.list[0]" class="tui-contact-search-list">
|
||||
<div class="tui-contact-search-list-title">
|
||||
{{ TUITranslateService.t(`TUIContact.${item.label}`) }}
|
||||
</div>
|
||||
@ -69,18 +42,12 @@
|
||||
:class="['selected']"
|
||||
@click="selectItem(listItem)"
|
||||
>
|
||||
<ContactListItem
|
||||
:item="listItem"
|
||||
:displayOnlineStatus="false"
|
||||
/>
|
||||
<ContactListItem :item="listItem" :displayOnlineStatus="false" />
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<div
|
||||
v-if="isContactSearchNoResult"
|
||||
class="tui-contact-search-list-default"
|
||||
>
|
||||
{{ TUITranslateService.t("TUIContact.无搜索结果") }}
|
||||
<div v-if="isContactSearchNoResult" class="tui-contact-search-list-default">
|
||||
{{ TUITranslateService.t('TUIContact.无搜索结果') }}
|
||||
</div>
|
||||
</ul>
|
||||
</template>
|
||||
@ -93,21 +60,14 @@ import {
|
||||
TUIFriendService,
|
||||
Friend,
|
||||
FriendApplication,
|
||||
TUIUserService,
|
||||
TUIUserService
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import TUICore, { TUIConstants } from '@tencentcloud/tui-core';
|
||||
import { ref, computed, onMounted, onUnmounted, provide } from '../../../adapter-vue';
|
||||
import Icon from '../../common/Icon.vue';
|
||||
import downSVG from '../../../assets/icon/down-icon.svg';
|
||||
import rightSVG from '../../../assets/icon/right-icon.svg';
|
||||
import {
|
||||
IContactList,
|
||||
IContactSearchResult,
|
||||
IBlackListUserItem,
|
||||
IUserStatus,
|
||||
IUserStatusMap,
|
||||
IContactInfoType,
|
||||
} from '../../../interface';
|
||||
import { IContactList, IContactSearchResult, IBlackListUserItem, IUserStatus, IUserStatusMap, IContactInfoType } from '../../../interface';
|
||||
import ContactListItem from './contact-list-item/index.vue';
|
||||
import { deepCopy } from '../../TUIChat/utils/utils';
|
||||
import { isPC } from '../../../utils/env';
|
||||
@ -119,23 +79,23 @@ const contactListMap = ref<IContactList>({
|
||||
key: 'friendApplicationList',
|
||||
title: '新的联系人',
|
||||
list: [] as FriendApplication[],
|
||||
unreadCount: 0,
|
||||
unreadCount: 0
|
||||
},
|
||||
blackList: {
|
||||
key: 'blackList',
|
||||
title: '黑名单',
|
||||
list: [] as IBlackListUserItem[],
|
||||
list: [] as IBlackListUserItem[]
|
||||
},
|
||||
groupList: {
|
||||
key: 'groupList',
|
||||
title: '我的群聊',
|
||||
list: [] as IGroupModel[],
|
||||
list: [] as IGroupModel[]
|
||||
},
|
||||
friendList: {
|
||||
key: 'friendList',
|
||||
title: '我的好友',
|
||||
list: [] as Friend[],
|
||||
},
|
||||
list: [] as Friend[]
|
||||
}
|
||||
});
|
||||
const contactSearchingStatus = ref<boolean>(false);
|
||||
const contactSearchResult = ref<IContactSearchResult>();
|
||||
@ -143,67 +103,64 @@ const displayOnlineStatus = ref<boolean>(false);
|
||||
const userOnlineStatusMap = ref<IUserStatusMap>();
|
||||
|
||||
const isContactSearchNoResult = computed((): boolean => {
|
||||
return (
|
||||
!contactSearchResult?.value?.user?.list[0]
|
||||
&& !contactSearchResult?.value?.group?.list[0]
|
||||
);
|
||||
return !contactSearchResult?.value?.user?.list[0] && !contactSearchResult?.value?.group?.list[0];
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.APP, {
|
||||
enabledCustomerServicePlugin: onCustomerServiceCommercialPluginUpdated,
|
||||
enabledCustomerServicePlugin: onCustomerServiceCommercialPluginUpdated
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.GRP, {
|
||||
groupList: onGroupListUpdated,
|
||||
groupList: onGroupListUpdated
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.USER, {
|
||||
userBlacklist: onUserBlacklistUpdated,
|
||||
displayOnlineStatus: onDisplayOnlineStatusUpdated,
|
||||
userStatusList: onUserStatusListUpdated,
|
||||
userStatusList: onUserStatusListUpdated
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.FRIEND, {
|
||||
friendList: onFriendListUpdated,
|
||||
friendApplicationList: onFriendApplicationListUpdated,
|
||||
friendApplicationUnreadCount: onFriendApplicationUnreadCountUpdated,
|
||||
friendApplicationUnreadCount: onFriendApplicationUnreadCountUpdated
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.CUSTOM, {
|
||||
currentContactSearchingStatus: onCurrentContactSearchingStatusUpdated,
|
||||
currentContactSearchResult: onCurrentContactSearchResultUpdated,
|
||||
currentContactListKey: onCurrentContactListKeyUpdated,
|
||||
currentContactInfo: onCurrentContactInfoUpdated,
|
||||
currentContactInfo: onCurrentContactInfoUpdated
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.APP, {
|
||||
enabledCustomerServicePlugin: onCustomerServiceCommercialPluginUpdated,
|
||||
enabledCustomerServicePlugin: onCustomerServiceCommercialPluginUpdated
|
||||
});
|
||||
|
||||
TUIStore.unwatch(StoreName.GRP, {
|
||||
groupList: onGroupListUpdated,
|
||||
groupList: onGroupListUpdated
|
||||
});
|
||||
|
||||
TUIStore.unwatch(StoreName.USER, {
|
||||
userBlacklist: onUserBlacklistUpdated,
|
||||
displayOnlineStatus: onDisplayOnlineStatusUpdated,
|
||||
userStatusList: onUserStatusListUpdated,
|
||||
userStatusList: onUserStatusListUpdated
|
||||
});
|
||||
|
||||
TUIStore.unwatch(StoreName.FRIEND, {
|
||||
friendList: onFriendListUpdated,
|
||||
friendApplicationList: onFriendApplicationListUpdated,
|
||||
friendApplicationUnreadCount: onFriendApplicationUnreadCountUpdated,
|
||||
friendApplicationUnreadCount: onFriendApplicationUnreadCountUpdated
|
||||
});
|
||||
|
||||
TUIStore.unwatch(StoreName.CUSTOM, {
|
||||
currentContactSearchingStatus: onCurrentContactSearchingStatusUpdated,
|
||||
currentContactSearchResult: onCurrentContactSearchResultUpdated,
|
||||
currentContactListKey: onCurrentContactListKeyUpdated,
|
||||
currentContactInfo: onCurrentContactInfoUpdated,
|
||||
currentContactInfo: onCurrentContactInfoUpdated
|
||||
});
|
||||
});
|
||||
|
||||
@ -230,11 +187,11 @@ function selectItem(item: any) {
|
||||
let targetListItem;
|
||||
if ((currentContactInfo.value as Friend)?.userID) {
|
||||
targetListItem = contactListMap.value?.friendList?.list?.find(
|
||||
(item: IContactInfoType) => (item as Friend)?.userID === (currentContactInfo.value as Friend)?.userID,
|
||||
(item: IContactInfoType) => (item as Friend)?.userID === (currentContactInfo.value as Friend)?.userID
|
||||
);
|
||||
} else if ((currentContactInfo.value as IGroupModel)?.groupID) {
|
||||
targetListItem = contactListMap.value?.groupList?.list?.find(
|
||||
(item: IContactInfoType) => (item as IGroupModel)?.groupID === (currentContactInfo.value as IGroupModel)?.groupID,
|
||||
(item: IContactInfoType) => (item as IGroupModel)?.groupID === (currentContactInfo.value as IGroupModel)?.groupID
|
||||
);
|
||||
}
|
||||
if (targetListItem) {
|
||||
@ -283,10 +240,10 @@ function onCustomerServiceCommercialPluginUpdated(isEnabled: boolean) {
|
||||
...item,
|
||||
renderKey: generateRenderKey('customerList', item, index),
|
||||
infoKeyList: [],
|
||||
btnKeyList: ['enterC2CConversation'],
|
||||
btnKeyList: ['enterC2CConversation']
|
||||
};
|
||||
}),
|
||||
key: 'customerList',
|
||||
key: 'customerList'
|
||||
};
|
||||
contactListMap.value = { ...contactListMap.value, customerList };
|
||||
}
|
||||
@ -317,28 +274,27 @@ function onFriendApplicationListUpdated(friendApplicationList: FriendApplication
|
||||
|
||||
function updateContactListMap(key: keyof IContactList, list: IContactInfoType[]) {
|
||||
contactListMap.value[key].list = list;
|
||||
contactListMap.value[key].list.map((item: IContactInfoType, index: number) => item.renderKey = generateRenderKey(key, item, index));
|
||||
contactListMap.value[key].list.map((item: IContactInfoType, index: number) => (item.renderKey = generateRenderKey(key, item, index)));
|
||||
updateCurrentContactInfoFromList(contactListMap.value[key].list, key);
|
||||
}
|
||||
|
||||
function updateCurrentContactInfoFromList(list: IContactInfoType[], type: keyof IContactList) {
|
||||
if (
|
||||
!(currentContactInfo.value as Friend)?.userID
|
||||
&& !(currentContactInfo.value as IGroupModel)?.groupID
|
||||
) {
|
||||
if (!(currentContactInfo.value as Friend)?.userID && !(currentContactInfo.value as IGroupModel)?.groupID) {
|
||||
return;
|
||||
}
|
||||
if (type === currentContactListKey.value || contactSearchingStatus.value) {
|
||||
currentContactInfo.value = list?.find(
|
||||
currentContactInfo.value =
|
||||
list?.find(
|
||||
(item: any) =>
|
||||
(item?.groupID && item?.groupID === (currentContactInfo.value as IGroupModel)?.groupID) || (item?.userID && item?.userID === (currentContactInfo.value as Friend)?.userID),
|
||||
) || {} as IContactInfoType;
|
||||
(item?.groupID && item?.groupID === (currentContactInfo.value as IGroupModel)?.groupID) ||
|
||||
(item?.userID && item?.userID === (currentContactInfo.value as Friend)?.userID)
|
||||
) || ({} as IContactInfoType);
|
||||
TUIStore.update(StoreName.CUSTOM, 'currentContactInfo', currentContactInfo.value);
|
||||
}
|
||||
}
|
||||
|
||||
function generateRenderKey(contactListMapKey: keyof IContactList, contactInfo: IContactInfoType, index: number) {
|
||||
return `${contactListMapKey}-${(contactInfo as Friend).userID || (contactInfo as IGroupModel).groupID || ('index' + index)}`;
|
||||
return `${contactListMapKey}-${(contactInfo as Friend).userID || (contactInfo as IGroupModel).groupID || 'index' + index}`;
|
||||
}
|
||||
|
||||
function onCurrentContactSearchResultUpdated(searchResult: IContactSearchResult) {
|
||||
|
@ -2,43 +2,21 @@
|
||||
<div :class="['tui-contact-search', !isPC && 'tui-contact-search-h5']">
|
||||
<div
|
||||
v-if="!isSearching || !isPC"
|
||||
:class="[
|
||||
'tui-contact-search-header',
|
||||
!isPC && 'tui-contact-search-h5-header',
|
||||
isSearching && 'tui-contact-searching-h5-header',
|
||||
]"
|
||||
:class="['tui-contact-search-header', !isPC && 'tui-contact-search-h5-header', isSearching && 'tui-contact-searching-h5-header']"
|
||||
@click="changeContactSearchingStatus(true)"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-search-header-icon',
|
||||
!isPC && 'tui-contact-search-h5-header-icon',
|
||||
]"
|
||||
:class="['tui-contact-search-header-icon', !isPC && 'tui-contact-search-h5-header-icon']"
|
||||
@click.stop="changeContactSearchingStatus(!isSearching)"
|
||||
>
|
||||
<Icon
|
||||
:file="isSearching ? backSVG : addSVG"
|
||||
:width="isSearching ? '20px' : '14px'"
|
||||
:height="isSearching ? '20px' : '14px'"
|
||||
/>
|
||||
<Icon :file="isSearching ? backSVG : addSVG" :width="isSearching ? '20px' : '14px'" :height="isSearching ? '20px' : '14px'" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-search-header-title',
|
||||
!isPC && 'tui-contact-search-h5-header-title',
|
||||
]"
|
||||
>
|
||||
{{ TUITranslateService.t("TUIContact.添加好友/群聊") }}
|
||||
<div :class="['tui-contact-search-header-title', !isPC && 'tui-contact-search-h5-header-title']">
|
||||
{{ TUITranslateService.t('TUIContact.添加好友/群聊') }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="isSearching"
|
||||
:class="[
|
||||
'tui-contact-search-main',
|
||||
!isPC && 'tui-contact-search-h5-main',
|
||||
]"
|
||||
>
|
||||
<div v-if="isSearching" :class="['tui-contact-search-main', !isPC && 'tui-contact-search-h5-main']">
|
||||
<input
|
||||
v-model="searchValue"
|
||||
class="tui-contact-search-main-input"
|
||||
@ -48,23 +26,16 @@
|
||||
@keyup.enter="search"
|
||||
@blur="search"
|
||||
@confirm="search"
|
||||
>
|
||||
<div
|
||||
class="tui-contact-search-main-cancel"
|
||||
@click="isSearching = false"
|
||||
>
|
||||
{{ TUITranslateService.t("取消") }}
|
||||
/>
|
||||
<div class="tui-contact-search-main-cancel" @click="isSearching = false">
|
||||
{{ TUITranslateService.t('取消') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from '../../../adapter-vue';
|
||||
import {
|
||||
TUITranslateService,
|
||||
TUIStore,
|
||||
StoreName,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUITranslateService, TUIStore, StoreName } from '@tencentcloud/chat-uikit-engine';
|
||||
import TUICore, { TUIConstants } from '@tencentcloud/tui-core';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import Icon from '../../common/Icon.vue';
|
||||
@ -80,12 +51,12 @@ const searchValue = ref<string>('');
|
||||
const searchResult = ref<IContactSearchResult>({
|
||||
user: {
|
||||
label: '联系人',
|
||||
list: [],
|
||||
list: []
|
||||
},
|
||||
group: {
|
||||
label: '群聊',
|
||||
list: [],
|
||||
},
|
||||
list: []
|
||||
}
|
||||
});
|
||||
|
||||
const changeContactSearchingStatus = debounce(function (status: boolean) {
|
||||
@ -100,8 +71,8 @@ const search = async () => {
|
||||
serviceName: TUIConstants.TUISearch.SERVICE.NAME,
|
||||
method: TUIConstants.TUISearch.SERVICE.METHOD.SEARCH_USER,
|
||||
params: {
|
||||
userID: searchValue.value,
|
||||
},
|
||||
userID: searchValue.value
|
||||
}
|
||||
})
|
||||
.then((res: any) => {
|
||||
searchResult.value.user.list = res.data;
|
||||
@ -114,8 +85,8 @@ const search = async () => {
|
||||
serviceName: TUIConstants.TUISearch.SERVICE.NAME,
|
||||
method: TUIConstants.TUISearch.SERVICE.METHOD.SEARCH_GROUP,
|
||||
params: {
|
||||
groupID: searchValue.value,
|
||||
},
|
||||
groupID: searchValue.value
|
||||
}
|
||||
})
|
||||
.then((res: any) => {
|
||||
searchResult.value.group.list = [res.data.group];
|
||||
@ -128,25 +99,17 @@ const search = async () => {
|
||||
watch(
|
||||
() => searchResult.value,
|
||||
() => {
|
||||
TUIStore.update(
|
||||
StoreName.CUSTOM,
|
||||
'currentContactSearchResult',
|
||||
searchResult.value,
|
||||
);
|
||||
TUIStore.update(StoreName.CUSTOM, 'currentContactSearchResult', searchResult.value);
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => isSearching.value,
|
||||
() => {
|
||||
TUIStore.update(
|
||||
StoreName.CUSTOM,
|
||||
'currentContactSearchingStatus',
|
||||
isSearching.value,
|
||||
);
|
||||
TUIStore.update(StoreName.CUSTOM, 'currentContactSearchingStatus', isSearching.value);
|
||||
if (isSearching.value) {
|
||||
searchValue.value = '';
|
||||
searchResult.value.user.list = [];
|
||||
@ -155,8 +118,8 @@ watch(
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
TUIGlobal.updateContactSearch = search;
|
||||
|
@ -1,17 +1,11 @@
|
||||
<template>
|
||||
<SelectFriend v-if="isShowSelectFriend" />
|
||||
<div
|
||||
v-else-if="isShowContactList"
|
||||
:class="['tui-contact', !isPC && 'tui-contact-h5']"
|
||||
>
|
||||
<div v-else-if="isShowContactList" :class="['tui-contact', !isPC && 'tui-contact-h5']">
|
||||
<div :class="['tui-contact-left', !isPC && 'tui-contact-h5-left']">
|
||||
<ContactSearch />
|
||||
<ContactList :class="['tui-contact-left-list', !isPC && 'tui-contact-h5-left-list']" />
|
||||
</div>
|
||||
<div
|
||||
v-if="isShowContactInfo"
|
||||
:class="['tui-contact-right', !isPC && 'tui-contact-h5-right']"
|
||||
>
|
||||
<div v-if="isShowContactInfo" :class="['tui-contact-right', !isPC && 'tui-contact-h5-right']">
|
||||
<ContactInfo @switchConversation="switchConversation" />
|
||||
</div>
|
||||
</div>
|
||||
@ -34,8 +28,8 @@ const props = defineProps({
|
||||
displayType: {
|
||||
type: String,
|
||||
default: 'contactList', // "contactList" / "selectFriend"
|
||||
require: false,
|
||||
},
|
||||
require: false
|
||||
}
|
||||
});
|
||||
|
||||
const displayTypeRef = ref<string>(props.displayType || 'contactList');
|
||||
@ -64,26 +58,27 @@ TUIStore.watch(StoreName.CUSTOM, {
|
||||
isShowSelectFriend.value = false;
|
||||
if (isUniFrameWork) {
|
||||
displayTypeRef.value = props.displayType;
|
||||
TUIGlobal?.showTabBar()?.catch(() => { /* ignore */ });
|
||||
TUIGlobal?.showTabBar()?.catch(() => {
|
||||
/* ignore */
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
currentContactInfo: (contactInfo: any) => {
|
||||
isShowContactInfo.value = isPC || (contactInfo && typeof contactInfo === 'object' && Object.keys(contactInfo)?.length > 0);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const switchConversation = (data: any) => {
|
||||
isUniFrameWork
|
||||
&& TUIGlobal?.navigateTo({
|
||||
url: '/TUIKit/components/TUIChat/index',
|
||||
isUniFrameWork &&
|
||||
TUIGlobal?.navigateTo({
|
||||
url: '/TUIKit/components/TUIChat/index'
|
||||
});
|
||||
emits('switchConversation', data);
|
||||
};
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../assets/styles/common";
|
||||
@import '../../assets/styles/common';
|
||||
|
||||
.tui-contact {
|
||||
width: 100%;
|
||||
|
@ -9,12 +9,7 @@
|
||||
/>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
TUIFriendService,
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUITranslateService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIFriendService, TUIStore, StoreName, TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import { ref, watchEffect } from '../../../adapter-vue';
|
||||
import { Toast, TOAST_TYPE } from '../../common/Toast/index';
|
||||
import TUICore from '@tencentcloud/tui-core';
|
||||
@ -30,7 +25,7 @@ const TUISearchServer = ref<any>(null);
|
||||
const selectOptions = ref({
|
||||
isRadio: false,
|
||||
isNeedSearch: false,
|
||||
title: '',
|
||||
title: ''
|
||||
});
|
||||
|
||||
const generateSearchServer = (isNeedSearch: any) => {
|
||||
@ -50,10 +45,12 @@ watchEffect(() => {
|
||||
if (params.isNeedSearch) {
|
||||
generateSearchServer(params.isNeedSearch);
|
||||
}
|
||||
TUIFriendService.getFriendList().then((res: any) => {
|
||||
TUIFriendService.getFriendList()
|
||||
.then((res: any) => {
|
||||
friendList.value = res.data.map((item: any) => item.profile);
|
||||
userList.value = friendList.value;
|
||||
}).catch((err: any) => {
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.warn('getFriendList error:', err);
|
||||
});
|
||||
});
|
||||
@ -67,14 +64,14 @@ const handleSelectedResult = (memberList: Array<any>) => {
|
||||
const searchFail = () => {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIGroup.该用户不存在'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
userList.value = [...friendList.value];
|
||||
};
|
||||
|
||||
const handleSearch = async (val: string) => {
|
||||
if (!val) {
|
||||
return userList.value = friendList.value;
|
||||
return (userList.value = friendList.value);
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -35,8 +35,9 @@ export default class TUIContactServer {
|
||||
this.onCallCallbackMap.set(method, callback);
|
||||
if (method === TUIConstants.TUIContact.SERVICE.METHOD.SELECT_FRIEND) {
|
||||
TUIStore.update(StoreName.CUSTOM, 'isShowSelectFriendComponent', true);
|
||||
isUniFrameWork && TUIGlobal?.reLaunch({
|
||||
url: '/TUIKit/components/TUIContact/index',
|
||||
isUniFrameWork &&
|
||||
TUIGlobal?.reLaunch({
|
||||
url: '/TUIKit/components/TUIContact/index'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -5,69 +5,49 @@ import TUIChatEngine, {
|
||||
TUIUserService,
|
||||
TUITranslateService,
|
||||
AddFriendParams,
|
||||
JoinGroupParams,
|
||||
JoinGroupParams
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import { Toast, TOAST_TYPE } from '../../common/Toast/index';
|
||||
|
||||
export const generateAvatar = (item: any): string => {
|
||||
return (
|
||||
item?.avatar
|
||||
|| item?.profile?.avatar
|
||||
|| (item?.groupID && 'https://web.sdk.qcloud.com/im/assets/images/Public.svg')
|
||||
|| 'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'
|
||||
item?.avatar ||
|
||||
item?.profile?.avatar ||
|
||||
(item?.groupID && 'https://web.sdk.qcloud.com/im/assets/images/Public.svg') ||
|
||||
'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'
|
||||
);
|
||||
};
|
||||
|
||||
export const generateName = (item: any): string => {
|
||||
return (
|
||||
item?.remark
|
||||
|| item?.name
|
||||
|| item?.profile?.nick
|
||||
|| item?.nick
|
||||
|| item?.groupID
|
||||
|| item?.userID
|
||||
|| ''
|
||||
);
|
||||
return item?.remark || item?.name || item?.profile?.nick || item?.nick || item?.groupID || item?.userID || '';
|
||||
};
|
||||
|
||||
export const generateContactInfoName = (item: any): string => {
|
||||
return (
|
||||
item?.name
|
||||
|| item?.profile?.nick
|
||||
|| item?.nick
|
||||
|| item?.groupID
|
||||
|| item?.userID
|
||||
|| ''
|
||||
);
|
||||
return item?.name || item?.profile?.nick || item?.nick || item?.groupID || item?.userID || '';
|
||||
};
|
||||
|
||||
// Parse the basic information display content of the contactInfo module
|
||||
// Group information display: group ID group type
|
||||
// User information display: user ID personal signature
|
||||
export const generateContactInfoBasic = (
|
||||
contactInfo: any,
|
||||
): any[] => {
|
||||
export const generateContactInfoBasic = (contactInfo: any): any[] => {
|
||||
const res = [
|
||||
{
|
||||
label: contactInfo?.groupID ? '群ID' : 'ID',
|
||||
data: contactInfo?.groupID || contactInfo?.userID || '',
|
||||
},
|
||||
data: contactInfo?.groupID || contactInfo?.userID || ''
|
||||
}
|
||||
];
|
||||
if (!isApplicationType(contactInfo)) {
|
||||
res.push({
|
||||
label: contactInfo?.groupID ? '群类型' : '个性签名',
|
||||
data: contactInfo?.type || contactInfo?.profile?.selfSignature || '',
|
||||
data: contactInfo?.type || contactInfo?.profile?.selfSignature || ''
|
||||
});
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
export const isApplicationType = (info: any) => {
|
||||
return (
|
||||
info?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_TO_ME
|
||||
|| info?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_BY_ME
|
||||
);
|
||||
return info?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_TO_ME || info?.type === TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_BY_ME;
|
||||
};
|
||||
|
||||
export const isFriend = (info: any): Promise<boolean> => {
|
||||
@ -82,7 +62,7 @@ export const isFriend = (info: any): Promise<boolean> => {
|
||||
}
|
||||
TUIFriendService.checkFriend({
|
||||
userIDList: [info?.userID],
|
||||
type: TUIChatEngine.TYPES.SNS_CHECK_TYPE_BOTH,
|
||||
type: TUIChatEngine.TYPES.SNS_CHECK_TYPE_BOTH
|
||||
})
|
||||
.then((res: any) => {
|
||||
switch (res?.data?.successUserIDList[0]?.relation) {
|
||||
@ -116,29 +96,28 @@ export const isFriend = (info: any): Promise<boolean> => {
|
||||
|
||||
// Change friend‘s remark
|
||||
export const updateFriendRemark = (userID: string, remark: string) => {
|
||||
// eslint-disable-next-line no-control-regex
|
||||
if (remark?.replace(/[^\u0000-\u00ff]/g, 'aa')?.length > 96) {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.修改备注失败: 备注长度不得超过 96 字节'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
return;
|
||||
}
|
||||
TUIFriendService.updateFriend({
|
||||
userID,
|
||||
remark,
|
||||
remark
|
||||
})
|
||||
.then(() => {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.修改备注成功'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.warn('update friend remark failed:', error);
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.修改备注失败'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -147,19 +126,19 @@ export const updateFriendRemark = (userID: string, remark: string) => {
|
||||
export const deleteFriend = (userID: string) => {
|
||||
TUIFriendService.deleteFriend({
|
||||
userIDList: [userID],
|
||||
type: TUIChatEngine.TYPES.SNS_DELETE_TYPE_BOTH,
|
||||
type: TUIChatEngine.TYPES.SNS_DELETE_TYPE_BOTH
|
||||
})
|
||||
.then((res: any) => {
|
||||
const { successUserIDList } = res.data;
|
||||
if (successUserIDList[0].userID === userID) {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.删除好友成功'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
} else {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.删除好友失败'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
}
|
||||
})
|
||||
@ -167,7 +146,7 @@ export const deleteFriend = (userID: string) => {
|
||||
console.warn('delete friend failed:', error);
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.删除好友失败'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -178,51 +157,47 @@ export const addFriend = (params: AddFriendParams) => {
|
||||
.then(() => {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.申请已发送'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.warn('delete friend failed:', error);
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.申请发送失败'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Enter conversation
|
||||
export const enterConversation = (item: any) => {
|
||||
const conversationID = item?.groupID
|
||||
? `GROUP${item?.groupID}`
|
||||
: `C2C${item?.userID}`;
|
||||
TUIConversationService.switchConversation(conversationID).catch(
|
||||
(error: any) => {
|
||||
const conversationID = item?.groupID ? `GROUP${item?.groupID}` : `C2C${item?.userID}`;
|
||||
TUIConversationService.switchConversation(conversationID).catch((error: any) => {
|
||||
console.warn('switch conversation failed:', error);
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.进入会话失败'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
// Accept friend application
|
||||
export const acceptFriendApplication = (userID: string) => {
|
||||
TUIFriendService.acceptFriendApplication({
|
||||
userID,
|
||||
type: TUIChatEngine.TYPES.SNS_APPLICATION_AGREE_AND_ADD,
|
||||
type: TUIChatEngine.TYPES.SNS_APPLICATION_AGREE_AND_ADD
|
||||
})
|
||||
.then(() => {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.添加好友成功'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.warn('accept friend application failed:', error);
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.同意好友申请失败'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -233,14 +208,14 @@ export const refuseFriendApplication = (userID: string) => {
|
||||
.then(() => {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.拒绝成功'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.warn('accept friend application failed:', error);
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.拒绝好友申请失败'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -251,7 +226,7 @@ export const dismissGroup = (groupID: string) => {
|
||||
.then(() => {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.解散群聊成功'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
TUIGlobal?.updateContactSearch && TUIGlobal?.updateContactSearch();
|
||||
})
|
||||
@ -259,7 +234,7 @@ export const dismissGroup = (groupID: string) => {
|
||||
console.warn('dismiss group failed:', error);
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.解散群聊失败'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -270,14 +245,14 @@ export const quitGroup = (groupID: string) => {
|
||||
.then(() => {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.退出群组成功'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.warn('quit group failed:', error);
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.退出群组失败'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -286,26 +261,26 @@ export const quitGroup = (groupID: string) => {
|
||||
export const joinGroup = (groupID: string, applyMessage?: string) => {
|
||||
TUIGroupService.joinGroup({
|
||||
groupID,
|
||||
applyMessage,
|
||||
applyMessage
|
||||
} as JoinGroupParams)
|
||||
.then((imResponse: { data: { status?: string } }) => {
|
||||
switch (imResponse?.data?.status) {
|
||||
case TUIChatEngine.TYPES.JOIN_STATUS_WAIT_APPROVAL: // Wait for administrator approval
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.等待管理员同意'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
break;
|
||||
case TUIChatEngine.TYPES.JOIN_STATUS_SUCCESS: // Join group successfully
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.加群成功'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
break;
|
||||
case TUIChatEngine.TYPES.JOIN_STATUS_ALREADY_IN_GROUP: // Already in the group
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.您已是群成员'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
break;
|
||||
default:
|
||||
@ -316,7 +291,7 @@ export const joinGroup = (groupID: string, applyMessage?: string) => {
|
||||
console.warn('join group failed:', error);
|
||||
Toast({
|
||||
message: '申请入群失败',
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -324,7 +299,7 @@ export const joinGroup = (groupID: string, applyMessage?: string) => {
|
||||
// Add to blacklist
|
||||
export const addToBlacklist = (userID: string, successCallBack?: () => void) => {
|
||||
TUIUserService.addToBlacklist({
|
||||
userIDList: [userID],
|
||||
userIDList: [userID]
|
||||
})
|
||||
.then(() => {
|
||||
successCallBack && successCallBack();
|
||||
@ -333,18 +308,15 @@ export const addToBlacklist = (userID: string, successCallBack?: () => void) =>
|
||||
console.warn('add to blacklist failed:', error);
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.加入黑名单失败'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Remove from Blacklist
|
||||
export const removeFromBlacklist = (
|
||||
userID: string,
|
||||
successCallBack?: () => void,
|
||||
) => {
|
||||
export const removeFromBlacklist = (userID: string, successCallBack?: () => void) => {
|
||||
TUIUserService.removeFromBlacklist({
|
||||
userIDList: [userID],
|
||||
userIDList: [userID]
|
||||
})
|
||||
.then(() => {
|
||||
successCallBack && successCallBack();
|
||||
@ -353,7 +325,7 @@ export const removeFromBlacklist = (
|
||||
console.warn('remove from blacklist failed:', error);
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIContact.移除黑名单失败'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -1,54 +1,44 @@
|
||||
<template>
|
||||
<Overlay
|
||||
:maskColor="'transparent'"
|
||||
@onOverlayClick="() => emits('closeConversationActionMenu')"
|
||||
>
|
||||
<Overlay :maskColor="'transparent'" @onOverlayClick="() => emits('closeConversationActionMenu')">
|
||||
<div
|
||||
id="conversation-actions-menu"
|
||||
ref="actionsMenuDomRef"
|
||||
:class="[
|
||||
isPC && 'actions-menu-pc',
|
||||
'actions-menu',
|
||||
!isHiddenActionsMenu && 'cancel-hidden',
|
||||
]"
|
||||
:class="[isPC && 'actions-menu-pc', 'actions-menu', !isHiddenActionsMenu && 'cancel-hidden']"
|
||||
:style="{
|
||||
top: `${_actionsMenuPosition.top}px`,
|
||||
left: `${_actionsMenuPosition.left}px`,
|
||||
left: `${_actionsMenuPosition.left}px`
|
||||
}"
|
||||
>
|
||||
<div
|
||||
:class="['actions-menu-item']"
|
||||
@click.stop="deleteConversation()"
|
||||
>
|
||||
{{ TUITranslateService.t("TUIConversation.删除会话") }}
|
||||
<div :class="['actions-menu-item']" @click.stop="deleteConversation()">
|
||||
{{ TUITranslateService.t('TUIConversation.删除会话') }}
|
||||
</div>
|
||||
<div
|
||||
v-if="!(props.selectedConversation && props.selectedConversation.isPinned)"
|
||||
:class="['actions-menu-item']"
|
||||
@click.stop="handleItem({ name: CONV_OPERATION.ISPINNED })"
|
||||
>
|
||||
{{ TUITranslateService.t("TUIConversation.置顶会话") }}
|
||||
{{ TUITranslateService.t('TUIConversation.置顶会话') }}
|
||||
</div>
|
||||
<div
|
||||
v-if="props.selectedConversation && props.selectedConversation.isPinned"
|
||||
:class="['actions-menu-item']"
|
||||
@click.stop="handleItem({ name: CONV_OPERATION.DISPINNED })"
|
||||
>
|
||||
{{ TUITranslateService.t("TUIConversation.取消置顶") }}
|
||||
{{ TUITranslateService.t('TUIConversation.取消置顶') }}
|
||||
</div>
|
||||
<div
|
||||
v-if="!(props.selectedConversation && props.selectedConversation.isMuted)"
|
||||
:class="['actions-menu-item']"
|
||||
@click.stop="handleItem({ name: CONV_OPERATION.MUTE })"
|
||||
>
|
||||
{{ TUITranslateService.t("TUIConversation.消息免打扰") }}
|
||||
{{ TUITranslateService.t('TUIConversation.消息免打扰') }}
|
||||
</div>
|
||||
<div
|
||||
v-if="props.selectedConversation && props.selectedConversation.isMuted"
|
||||
:class="['actions-menu-item']"
|
||||
@click.stop="handleItem({ name: CONV_OPERATION.NOTMUTE })"
|
||||
>
|
||||
{{ TUITranslateService.t("TUIConversation.取消免打扰") }}
|
||||
{{ TUITranslateService.t('TUIConversation.取消免打扰') }}
|
||||
</div>
|
||||
</div>
|
||||
<Dialog
|
||||
@ -66,18 +56,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ref,
|
||||
nextTick,
|
||||
onMounted,
|
||||
computed,
|
||||
getCurrentInstance,
|
||||
} from '../../../adapter-vue';
|
||||
import TUIChatEngine, {
|
||||
IConversationModel,
|
||||
TUIStore,
|
||||
TUITranslateService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { ref, nextTick, onMounted, computed, getCurrentInstance } from '../../../adapter-vue';
|
||||
import TUIChatEngine, { IConversationModel, TUIStore, TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import { CONV_OPERATION } from '../../../constant';
|
||||
import { isPC, isUniFrameWork } from '../../../utils/env';
|
||||
@ -101,9 +81,7 @@ const thisInstance = getCurrentInstance()?.proxy || getCurrentInstance();
|
||||
const actionsMenuDomRef = ref<HTMLElement | null>();
|
||||
const isHiddenActionsMenu = ref(true);
|
||||
const isShowDeleteConversationDialog = ref<boolean>(false);
|
||||
const currentConversation = TUIStore.getConversationModel(
|
||||
props.selectedConversation?.conversationID || '',
|
||||
);
|
||||
const currentConversation = TUIStore.getConversationModel(props.selectedConversation?.conversationID || '');
|
||||
const _actionsMenuPosition = ref<{
|
||||
top: number;
|
||||
left?: number;
|
||||
@ -117,7 +95,9 @@ onMounted(() => {
|
||||
const deleteConversationDialogTitle = computed(() => {
|
||||
return props.selectedConversation?.type === TUIChatEngine.TYPES.CONV_C2C
|
||||
? 'TUIConversation.删除后,将清空该聊天的消息记录'
|
||||
: props.selectedConversation?.type === TUIChatEngine.TYPES.CONV_GROUP ? 'TUIConversation.删除后,将清空该群聊的消息记录' : '';
|
||||
: props.selectedConversation?.type === TUIChatEngine.TYPES.CONV_GROUP
|
||||
? 'TUIConversation.删除后,将清空该群聊的消息记录'
|
||||
: '';
|
||||
});
|
||||
function checkExceedBounds() {
|
||||
// When the component is initially rendered, it executes and self-checks whether the boundary exceeds the screen, and handles it in nextTick.
|
||||
@ -133,10 +113,7 @@ function checkExceedBounds() {
|
||||
if (data.bottom > TUIGlobal?.getWindowInfo?.().windowHeight) {
|
||||
_actionsMenuPosition.value = {
|
||||
...props.actionsMenuPosition,
|
||||
top:
|
||||
props.actionsMenuPosition.top
|
||||
- (props.actionsMenuPosition.conversationHeight || 0)
|
||||
- data.height,
|
||||
top: props.actionsMenuPosition.top - (props.actionsMenuPosition.conversationHeight || 0) - data.height
|
||||
};
|
||||
}
|
||||
// check if actionsMenu is exceed right of the screen
|
||||
@ -155,10 +132,7 @@ function checkExceedBounds() {
|
||||
_actionsMenuPosition.value.left = props.actionsMenuPosition.left;
|
||||
}
|
||||
if (rect && rect.bottom > window.innerHeight) {
|
||||
_actionsMenuPosition.value.top
|
||||
= props.actionsMenuPosition.top
|
||||
- (props.actionsMenuPosition.conversationHeight || 0)
|
||||
- rect.height;
|
||||
_actionsMenuPosition.value.top = props.actionsMenuPosition.top - (props.actionsMenuPosition.conversationHeight || 0) - rect.height;
|
||||
}
|
||||
isHiddenActionsMenu.value = false;
|
||||
}
|
||||
|
@ -1,54 +1,20 @@
|
||||
<template>
|
||||
<div
|
||||
:ref="convHeaderRef"
|
||||
class="tui-conversation-header"
|
||||
>
|
||||
<ul
|
||||
v-if="menuList.length > 0"
|
||||
class="list"
|
||||
>
|
||||
<li
|
||||
v-for="(item, index) in menuList"
|
||||
:key="index"
|
||||
class="list-item"
|
||||
>
|
||||
<main
|
||||
class="tui-conversation-header-item"
|
||||
@click.stop="handleMenu(item)"
|
||||
>
|
||||
<Icon
|
||||
v-if="item.icon && !item.data.children"
|
||||
class="tui-conversation-header-item-icon"
|
||||
:file="item.icon"
|
||||
/>
|
||||
<i
|
||||
v-else
|
||||
class="plus"
|
||||
/>
|
||||
<div :ref="convHeaderRef" class="tui-conversation-header">
|
||||
<ul v-if="menuList.length > 0" class="list">
|
||||
<li v-for="(item, index) in menuList" :key="index" class="list-item">
|
||||
<main class="tui-conversation-header-item" @click.stop="handleMenu(item)">
|
||||
<Icon v-if="item.icon && !item.data.children" class="tui-conversation-header-item-icon" :file="item.icon" />
|
||||
<i v-else class="plus" />
|
||||
<h1 class="tui-conversation-header-item-title">
|
||||
{{ item.text }}
|
||||
</h1>
|
||||
</main>
|
||||
</li>
|
||||
</ul>
|
||||
<ul
|
||||
v-if="showChildren.length > 0"
|
||||
class="tui-conversation-header-children list"
|
||||
>
|
||||
<li
|
||||
v-for="(childrenItem, childrenIndex) in showChildren"
|
||||
:key="childrenIndex"
|
||||
class="list-item"
|
||||
>
|
||||
<main
|
||||
class="tui-conversation-header-item"
|
||||
@click="handleMenu(childrenItem)"
|
||||
>
|
||||
<Icon
|
||||
v-if="childrenItem.icon"
|
||||
class="tui-conversation-header-item-icon"
|
||||
:file="childrenItem.icon"
|
||||
/>
|
||||
<ul v-if="showChildren.length > 0" class="tui-conversation-header-children list">
|
||||
<li v-for="(childrenItem, childrenIndex) in showChildren" :key="childrenIndex" class="list-item">
|
||||
<main class="tui-conversation-header-item" @click="handleMenu(childrenItem)">
|
||||
<Icon v-if="childrenItem.icon" class="tui-conversation-header-item-icon" :file="childrenItem.icon" />
|
||||
<h1 class="tui-conversation-header-item-title">
|
||||
{{ childrenItem.text }}
|
||||
</h1>
|
||||
@ -74,7 +40,10 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
const handleMenu = (item: IMenuItem) => {
|
||||
const { data: { children }, listener = { onClicked: () => {} } } = item;
|
||||
const {
|
||||
data: { children },
|
||||
listener = { onClicked: () => {} }
|
||||
} = item;
|
||||
if (children) {
|
||||
showChildren.value = showChildren.value.length > 0 ? [] : children;
|
||||
} else {
|
||||
@ -88,9 +57,8 @@ const closeChildren = () => {
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
closeChildren,
|
||||
closeChildren
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped src="../style/index.scss"></style>
|
||||
|
@ -30,13 +30,15 @@ export default class ConversationHeaderServer {
|
||||
public getMenu(): any[] {
|
||||
const list = this.generateMenuList();
|
||||
if (!isPC && list.length > 0) {
|
||||
return [{
|
||||
return [
|
||||
{
|
||||
text: TUITranslateService.t('TUIConversation.发起会话'),
|
||||
data: {
|
||||
name: 'all',
|
||||
children: list,
|
||||
},
|
||||
}];
|
||||
children: list
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@ -47,22 +49,22 @@ export default class ConversationHeaderServer {
|
||||
icon: C2C,
|
||||
text: TUITranslateService.t('TUIConversation.发起单聊'),
|
||||
data: {
|
||||
name: CONV_CREATE_TYPE.TYPEC2C,
|
||||
name: CONV_CREATE_TYPE.TYPEC2C
|
||||
},
|
||||
listener: {
|
||||
onClicked: this.createConversation.bind(this),
|
||||
},
|
||||
onClicked: this.createConversation.bind(this)
|
||||
}
|
||||
},
|
||||
{
|
||||
icon: createGroup,
|
||||
text: TUITranslateService.t('TUIConversation.发起群聊'),
|
||||
data: {
|
||||
name: CONV_CREATE_TYPE.TYPEGROUP,
|
||||
name: CONV_CREATE_TYPE.TYPEGROUP
|
||||
},
|
||||
listener: {
|
||||
onClicked: this.createConversation.bind(this),
|
||||
},
|
||||
},
|
||||
onClicked: this.createConversation.bind(this)
|
||||
}
|
||||
}
|
||||
];
|
||||
return list;
|
||||
}
|
||||
@ -72,7 +74,7 @@ export default class ConversationHeaderServer {
|
||||
TUICore.callService({
|
||||
serviceName: TUIConstants.TUIConversation.SERVICE.NAME,
|
||||
method: TUIConstants.TUIConversation.SERVICE.METHOD.CREATE_CONVERSATION,
|
||||
params: item,
|
||||
params: item
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<div
|
||||
ref="conversationListInnerDomRef"
|
||||
class="tui-conversation-list"
|
||||
>
|
||||
<div ref="conversationListInnerDomRef" class="tui-conversation-list">
|
||||
<ActionsMenu
|
||||
v-if="isShowOverlay"
|
||||
:selectedConversation="currentConversation"
|
||||
@ -14,55 +11,36 @@
|
||||
v-for="(conversation, index) in conversationList"
|
||||
:id="`convlistitem-${index}`"
|
||||
:key="index"
|
||||
:class="[
|
||||
'tui-conversation-content',
|
||||
isMobile && 'tui-conversation-content-h5 disable-select',
|
||||
]"
|
||||
:class="['tui-conversation-content', isMobile && 'tui-conversation-content-h5 disable-select']"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
isPC && 'isPC',
|
||||
'tui-conversation-item',
|
||||
currentConversationID === conversation.conversationID &&
|
||||
'tui-conversation-item-selected',
|
||||
conversation.isPinned && 'tui-conversation-item-pinned',
|
||||
currentConversationID === conversation.conversationID && 'tui-conversation-item-selected',
|
||||
conversation.isPinned && 'tui-conversation-item-pinned'
|
||||
]"
|
||||
@click="enterConversationChat(conversation.conversationID)"
|
||||
@longpress="showConversationActionMenu($event, conversation, index)"
|
||||
@contextmenu="showConversationActionMenu($event, conversation, index, true)"
|
||||
>
|
||||
<aside class="left">
|
||||
<Avatar
|
||||
useSkeletonAnimation
|
||||
:url="conversation.getAvatar()"
|
||||
size="30px"
|
||||
/>
|
||||
<Avatar useSkeletonAnimation :url="conversation.getAvatar()" size="30px" />
|
||||
<div
|
||||
v-if="userOnlineStatusMap && isShowUserOnlineStatus(conversation)"
|
||||
:class="[
|
||||
'online-status',
|
||||
Object.keys(userOnlineStatusMap).length > 0 &&
|
||||
Object.keys(userOnlineStatusMap).includes(
|
||||
conversation.userProfile.userID
|
||||
) &&
|
||||
userOnlineStatusMap[conversation.userProfile.userID]
|
||||
.statusType === 1
|
||||
Object.keys(userOnlineStatusMap).includes(conversation.userProfile.userID) &&
|
||||
userOnlineStatusMap[conversation.userProfile.userID].statusType === 1
|
||||
? 'online-status-online'
|
||||
: 'online-status-offline',
|
||||
: 'online-status-offline'
|
||||
]"
|
||||
/>
|
||||
<span
|
||||
v-if="conversation.unreadCount > 0 && !conversation.isMuted"
|
||||
class="num"
|
||||
>
|
||||
{{
|
||||
conversation.unreadCount > 99 ? "99+" : conversation.unreadCount
|
||||
}}
|
||||
<span v-if="conversation.unreadCount > 0 && !conversation.isMuted" class="num">
|
||||
{{ conversation.unreadCount > 99 ? '99+' : conversation.unreadCount }}
|
||||
</span>
|
||||
<span
|
||||
v-if="conversation.unreadCount > 0 && conversation.isMuted"
|
||||
class="num-notify"
|
||||
/>
|
||||
<span v-if="conversation.unreadCount > 0 && conversation.isMuted" class="num-notify" />
|
||||
</aside>
|
||||
<div class="content">
|
||||
<div class="content-header">
|
||||
@ -70,29 +48,22 @@
|
||||
<p class="name">{{ conversation.getShowName() }}</p>
|
||||
</label>
|
||||
<div class="middle-box">
|
||||
<span v-if="conversation.draftText && conversation.conversationID !== currentConversationID" class="middle-box-draft">{{
|
||||
TUITranslateService.t('TUIChat.[草稿]')
|
||||
}}</span>
|
||||
<span
|
||||
v-if="conversation.draftText && conversation.conversationID !== currentConversationID"
|
||||
class="middle-box-draft"
|
||||
>{{ TUITranslateService.t('TUIChat.[草稿]') }}</span>
|
||||
<span
|
||||
v-else-if="
|
||||
conversation.type === 'GROUP' &&
|
||||
conversation.groupAtInfoList &&
|
||||
conversation.groupAtInfoList.length > 0
|
||||
"
|
||||
v-else-if="conversation.type === 'GROUP' && conversation.groupAtInfoList && conversation.groupAtInfoList.length > 0"
|
||||
class="middle-box-at"
|
||||
>{{ conversation.getGroupAtInfo() }}</span>
|
||||
>{{ conversation.getGroupAtInfo() }}</span
|
||||
>
|
||||
<div class="middle-box-content">
|
||||
{{ conversation.getLastMessage("text") }}
|
||||
{{ conversation.getLastMessage('text') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-footer">
|
||||
<span class="time">{{ conversation.getLastMessage("time") }}</span>
|
||||
<Icon
|
||||
v-if="conversation.isMuted"
|
||||
:file="muteIcon"
|
||||
/>
|
||||
<span class="time">{{ conversation.getLastMessage('time') }}</span>
|
||||
<Icon v-if="conversation.isMuted" :file="muteIcon" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -111,13 +82,7 @@ interface IUserStatusMap {
|
||||
}
|
||||
|
||||
import { ref, onMounted, onUnmounted } from '../../../adapter-vue';
|
||||
import TUIChatEngine, {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUIConversationService,
|
||||
TUITranslateService,
|
||||
IConversationModel,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import TUIChatEngine, { TUIStore, StoreName, TUIConversationService, TUITranslateService, IConversationModel } from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGlobal, isIOS, addLongPressListener } from '@tencentcloud/universal-api';
|
||||
import Icon from '../../common/Icon.vue';
|
||||
import Avatar from '../../common/Avatar/index.vue';
|
||||
@ -140,7 +105,7 @@ const actionsMenuPosition = ref<{
|
||||
}>({
|
||||
top: 0,
|
||||
left: undefined,
|
||||
conversationHeight: undefined,
|
||||
conversationHeight: undefined
|
||||
});
|
||||
const displayOnlineStatus = ref(false);
|
||||
const userOnlineStatusMap = ref<IUserStatusMap>();
|
||||
@ -151,12 +116,12 @@ onMounted(() => {
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversationID: onCurrentConversationIDUpdated,
|
||||
conversationList: onConversationListUpdated,
|
||||
currentConversation: onCurrentConversationUpdated,
|
||||
currentConversation: onCurrentConversationUpdated
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.USER, {
|
||||
displayOnlineStatus: onDisplayOnlineStatusUpdated,
|
||||
userStatusList: onUserStatusListUpdated,
|
||||
userStatusList: onUserStatusListUpdated
|
||||
});
|
||||
|
||||
if (!isUniFrameWork && isIOS && !isPC) {
|
||||
@ -168,28 +133,20 @@ onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CONV, {
|
||||
currentConversationID: onCurrentConversationIDUpdated,
|
||||
conversationList: onConversationListUpdated,
|
||||
currentConversation: onCurrentConversationUpdated,
|
||||
currentConversation: onCurrentConversationUpdated
|
||||
});
|
||||
|
||||
TUIStore.unwatch(StoreName.USER, {
|
||||
displayOnlineStatus: onDisplayOnlineStatusUpdated,
|
||||
userStatusList: onUserStatusListUpdated,
|
||||
userStatusList: onUserStatusListUpdated
|
||||
});
|
||||
});
|
||||
|
||||
const isShowUserOnlineStatus = (conversation: IConversationModel): boolean => {
|
||||
return (
|
||||
displayOnlineStatus.value
|
||||
&& conversation.type === TUIChatEngine.TYPES.CONV_C2C
|
||||
);
|
||||
return displayOnlineStatus.value && conversation.type === TUIChatEngine.TYPES.CONV_C2C;
|
||||
};
|
||||
|
||||
const showConversationActionMenu = (
|
||||
event: Event,
|
||||
conversation: IConversationModel,
|
||||
index: number,
|
||||
isContextMenuEvent?: boolean,
|
||||
) => {
|
||||
const showConversationActionMenu = (event: Event, conversation: IConversationModel, index: number, isContextMenuEvent?: boolean) => {
|
||||
if (isContextMenuEvent) {
|
||||
event.preventDefault();
|
||||
if (isUniFrameWork) {
|
||||
@ -203,10 +160,7 @@ const showConversationActionMenu = (
|
||||
|
||||
const closeConversationActionMenu = () => {
|
||||
// Prevent continuous triggering of overlay tap events
|
||||
if (
|
||||
lastestOpenActionsMenuTime
|
||||
&& Date.now() - lastestOpenActionsMenuTime > 300
|
||||
) {
|
||||
if (lastestOpenActionsMenuTime && Date.now() - lastestOpenActionsMenuTime > 300) {
|
||||
currentConversation.value = undefined;
|
||||
isShowOverlay.value = false;
|
||||
}
|
||||
@ -218,25 +172,28 @@ const getActionsMenuPosition = (event: Event, index: number) => {
|
||||
emits('getPassingRef', conversationListDomRef);
|
||||
}
|
||||
const query = TUIGlobal?.createSelectorQuery().in(conversationListDomRef.value);
|
||||
query.select(`#convlistitem-${index}`).boundingClientRect((data) => {
|
||||
query
|
||||
.select(`#convlistitem-${index}`)
|
||||
.boundingClientRect((data) => {
|
||||
if (data) {
|
||||
actionsMenuPosition.value = {
|
||||
// The uni-page-head of uni-h5 is not considered a member of the viewport, so the height of the head is manually increased.
|
||||
top: data.bottom + (isH5 ? 44 : 0),
|
||||
// @ts-expect-error in uniapp event has touches property
|
||||
left: event.touches[0].pageX,
|
||||
conversationHeight: data.height,
|
||||
conversationHeight: data.height
|
||||
};
|
||||
isShowOverlay.value = true;
|
||||
}
|
||||
}).exec();
|
||||
})
|
||||
.exec();
|
||||
} else {
|
||||
const rect = ((event.currentTarget || event.target) as HTMLElement)?.getBoundingClientRect() || {};
|
||||
if (rect) {
|
||||
actionsMenuPosition.value = {
|
||||
top: rect.bottom,
|
||||
left: isPC ? (event as MouseEvent).clientX : undefined,
|
||||
conversationHeight: rect.height,
|
||||
conversationHeight: rect.height
|
||||
};
|
||||
}
|
||||
isShowOverlay.value = true;
|
||||
@ -260,9 +217,9 @@ function addLongPressHandler() {
|
||||
},
|
||||
options: {
|
||||
eventDelegation: {
|
||||
subSelector: '.tui-conversation-content',
|
||||
},
|
||||
},
|
||||
subSelector: '.tui-conversation-content'
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -284,13 +241,10 @@ function onDisplayOnlineStatusUpdated(status: boolean) {
|
||||
|
||||
function onUserStatusListUpdated(list: Map<string, IUserStatus>) {
|
||||
if (list.size !== 0) {
|
||||
userOnlineStatusMap.value = [...list.entries()].reduce(
|
||||
(obj, [key, value]) => {
|
||||
userOnlineStatusMap.value = [...list.entries()].reduce((obj, [key, value]) => {
|
||||
obj[key] = value;
|
||||
return obj;
|
||||
},
|
||||
{} as IUserStatusMap,
|
||||
);
|
||||
}, {} as IUserStatusMap);
|
||||
}
|
||||
}
|
||||
// Expose to the parent component and close actionsMenu when a sliding event is detected
|
||||
|
@ -1,32 +1,21 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="isNotNetwork"
|
||||
class="network"
|
||||
>
|
||||
<div v-if="isNotNetwork" class="network">
|
||||
<i class="icon icon-error">!</i>
|
||||
<p class="network-content">
|
||||
{{
|
||||
TUITranslateService.t("TUIConversation.网络异常,请您检查网络设置")
|
||||
}}
|
||||
{{ TUITranslateService.t('TUIConversation.网络异常,请您检查网络设置') }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TUIChatEngine, {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUITranslateService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import {
|
||||
ref,
|
||||
} from '../../../adapter-vue';
|
||||
import TUIChatEngine, { TUIStore, StoreName, TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import { ref } from '../../../adapter-vue';
|
||||
|
||||
const isNotNetwork = ref(false);
|
||||
|
||||
TUIStore.watch(StoreName.USER, {
|
||||
netStateChange: (value: string) => {
|
||||
isNotNetwork.value = (value === TUIChatEngine.TYPES.NET_STATE_DISCONNECTED);
|
||||
},
|
||||
isNotNetwork.value = value === TUIChatEngine.TYPES.NET_STATE_DISCONNECTED;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import TUIConversation from "./index.vue";
|
||||
import TUIConversationServer from "./server";
|
||||
import TUIConversation from './index.vue';
|
||||
import TUIConversationServer from './server';
|
||||
new TUIConversationServer();
|
||||
|
||||
export default TUIConversation;
|
||||
|
@ -1,12 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="tui-conversation"
|
||||
@click="handleClickConv"
|
||||
>
|
||||
<ConversationHeader
|
||||
v-if="isShowConversationHeader"
|
||||
ref="headerRef"
|
||||
/>
|
||||
<div class="tui-conversation" @click="handleClickConv">
|
||||
<ConversationHeader v-if="isShowConversationHeader" ref="headerRef" />
|
||||
<ConversationNetwork />
|
||||
<ConversationList @handleSwitchConversation="handleSwitchConversation" />
|
||||
</div>
|
||||
@ -27,13 +21,13 @@ const isShowConversationHeader = ref<boolean>(true);
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
totalUnreadCount: (count: number) => {
|
||||
totalUnreadCount.value = count;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
TUIStore.watch(StoreName.CUSTOM, {
|
||||
isShowConversationHeader: (showStatus: boolean) => {
|
||||
isShowConversationHeader.value = showStatus !== false;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const handleSwitchConversation = (conversationID: string) => {
|
||||
|
@ -1,10 +1,5 @@
|
||||
import TUICore, { TUIConstants } from '@tencentcloud/tui-core';
|
||||
import {
|
||||
TUITranslateService,
|
||||
TUIConversationService,
|
||||
TUIStore,
|
||||
StoreName,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUITranslateService, TUIConversationService, TUIStore, StoreName } from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import { CONV_CREATE_TYPE } from '../../constant';
|
||||
import { isUniFrameWork } from '../../utils/env';
|
||||
@ -61,23 +56,23 @@ export default class TUIConversationServer {
|
||||
icon: createC2CIcon,
|
||||
text: TUITranslateService.t('TUIConversation.发起单聊'),
|
||||
data: {
|
||||
name: CONV_CREATE_TYPE.TYPEC2C,
|
||||
name: CONV_CREATE_TYPE.TYPEC2C
|
||||
},
|
||||
listener: {
|
||||
onClicked: this.createConversation.bind(this),
|
||||
},
|
||||
onClicked: this.createConversation.bind(this)
|
||||
}
|
||||
},
|
||||
{
|
||||
weight: 100,
|
||||
icon: createGroupIcon,
|
||||
text: TUITranslateService.t('TUIConversation.发起群聊'),
|
||||
data: {
|
||||
name: CONV_CREATE_TYPE.TYPEGROUP,
|
||||
name: CONV_CREATE_TYPE.TYPEGROUP
|
||||
},
|
||||
listener: {
|
||||
onClicked: this.createConversation.bind(this),
|
||||
},
|
||||
},
|
||||
onClicked: this.createConversation.bind(this)
|
||||
}
|
||||
}
|
||||
];
|
||||
return list;
|
||||
}
|
||||
@ -91,7 +86,7 @@ export default class TUIConversationServer {
|
||||
params: {
|
||||
title: item.text,
|
||||
isRadio: item.data.name !== CONV_CREATE_TYPE.TYPEGROUP,
|
||||
isNeedSearch: !TUIStore.getData(StoreName.APP, 'isOfficial'),
|
||||
isNeedSearch: !TUIStore.getData(StoreName.APP, 'isOfficial')
|
||||
},
|
||||
callback: async (memberList: any[]) => {
|
||||
if (!memberList || memberList.length === 0) {
|
||||
@ -107,7 +102,7 @@ export default class TUIConversationServer {
|
||||
await this.generateConversation(`C2C${userID}`);
|
||||
this.routerForward(`C2C${userID}`);
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -117,7 +112,7 @@ export default class TUIConversationServer {
|
||||
method: TUIConstants.TUIGroup.SERVICE.METHOD.CREATE_GROUP,
|
||||
params: {
|
||||
title: TUITranslateService.t('TUIConversation.发起群聊'),
|
||||
memberList,
|
||||
memberList
|
||||
},
|
||||
callback: async (group: any) => {
|
||||
let conversationID: string | null = null;
|
||||
@ -127,18 +122,18 @@ export default class TUIConversationServer {
|
||||
conversationID = `GROUP${groupID}`;
|
||||
}
|
||||
this.routerForward(conversationID);
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async routerForward(conversationID: string | null): Promise<void> {
|
||||
if (isUniFrameWork) {
|
||||
await TUIGlobal?.reLaunch({
|
||||
url: '/TUIKit/components/TUIConversation/index',
|
||||
url: '/TUIKit/components/TUIConversation/index'
|
||||
});
|
||||
if (conversationID) {
|
||||
TUIGlobal?.navigateTo({
|
||||
url: '/TUIKit/components/TUIChat/index',
|
||||
url: '/TUIKit/components/TUIChat/index'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -6,36 +6,36 @@ const groupIntroConfig = [
|
||||
label: '陌生人社交群(Public)',
|
||||
type: TUIChatEngine.TYPES.GRP_PUBLIC,
|
||||
detail: '类似 QQ 群,创建后群主可以指定群管理员,用户搜索群 ID 发起加群申请后,需要群主或管理员审批通过才能入群。详见',
|
||||
src: '产品文档',
|
||||
src: '产品文档'
|
||||
},
|
||||
{
|
||||
icon: 'https://web.sdk.qcloud.com/im/assets/images/Meeting.svg',
|
||||
label: '临时会议群(Meeting)',
|
||||
type: TUIChatEngine.TYPES.GRP_MEETING,
|
||||
detail: '创建后可以随意进出,且支持查看入群前消息;适合用于音视频会议场景、在线教育场景等与实时音视频产品结合的场景。详见',
|
||||
src: '产品文档',
|
||||
src: '产品文档'
|
||||
},
|
||||
{
|
||||
icon: 'https://web.sdk.qcloud.com/im/assets/images/Work.svg',
|
||||
label: '好友工作群(Work)',
|
||||
type: TUIChatEngine.TYPES.GRP_WORK,
|
||||
detail: '类似普通微信群,创建后仅支持已在群内的好友邀请加群,且无需被邀请方同意或群主审批。详见',
|
||||
src: '产品文档',
|
||||
src: '产品文档'
|
||||
},
|
||||
{
|
||||
icon: 'https://web.sdk.qcloud.com/im/assets/images/AVChatroom.svg',
|
||||
label: '直播群(AVChatroom)',
|
||||
type: TUIChatEngine.TYPES.GRP_AVCHATROOM,
|
||||
detail: '创建后可以随意进出,没有群成员数量上限,但不支持历史消息存储;适合与直播产品结合,用于弹幕聊天场景。详见',
|
||||
src: '产品文档',
|
||||
src: '产品文档'
|
||||
},
|
||||
{
|
||||
icon: 'https://web.sdk.qcloud.com/im/assets/images/Community.png',
|
||||
label: '社群(Community)',
|
||||
type: TUIChatEngine.TYPES.GRP_COMMUNITY,
|
||||
detail: '创建后可以随意进出,最多支持100000人,支持历史消息存储,用户搜索群 ID 发起加群申请后,无需管理员审批即可进群。详见',
|
||||
src: '产品文档',
|
||||
},
|
||||
src: '产品文档'
|
||||
}
|
||||
];
|
||||
|
||||
const findGroupIntroConfig = (type: string) => {
|
||||
@ -44,7 +44,4 @@ const findGroupIntroConfig = (type: string) => {
|
||||
})[0];
|
||||
};
|
||||
|
||||
export {
|
||||
groupIntroConfig,
|
||||
findGroupIntroConfig,
|
||||
};
|
||||
export { groupIntroConfig, findGroupIntroConfig };
|
||||
|
@ -1,34 +1,18 @@
|
||||
<template>
|
||||
<ul class="group-introduction-list select">
|
||||
<li
|
||||
v-for="(item, index) in type"
|
||||
:key="index"
|
||||
class="select-item"
|
||||
:class="[selectType === item.type && 'selected']"
|
||||
@click="selected(item)"
|
||||
>
|
||||
<li v-for="(item, index) in type" :key="index" class="select-item" :class="[selectType === item.type && 'selected']" @click="selected(item)">
|
||||
<main class="select-item-type">
|
||||
<div class="select-item-header">
|
||||
<aside class="left">
|
||||
<Icon
|
||||
class="icon"
|
||||
:file="item.icon"
|
||||
/>
|
||||
<Icon class="icon" :file="item.icon" />
|
||||
<span class="select-item-label">{{ TUITranslateService.t(`TUIGroup.${item.label}`) }}</span>
|
||||
</aside>
|
||||
<Icon
|
||||
v-if="selectType === item.type"
|
||||
:file="selectedIcon"
|
||||
/>
|
||||
<Icon v-if="selectType === item.type" :file="selectedIcon" />
|
||||
</div>
|
||||
<span class="select-item-detail">{{ TUITranslateService.t(`TUIGroup.${item.detail}`) }}</span>
|
||||
<a
|
||||
class="link"
|
||||
:href="documentLink.product.url"
|
||||
target="_blank"
|
||||
@click="openUrl(documentLink.product.url)"
|
||||
>{{
|
||||
TUITranslateService.t(`TUIGroup.${item.src}`) }}</a>
|
||||
<a class="link" :href="documentLink.product.url" target="_blank" @click="openUrl(documentLink.product.url)">{{
|
||||
TUITranslateService.t(`TUIGroup.${item.src}`)
|
||||
}}</a>
|
||||
</main>
|
||||
</li>
|
||||
</ul>
|
||||
@ -46,8 +30,8 @@ import { isUniFrameWork } from '../../../../utils/env';
|
||||
const props = defineProps({
|
||||
groupType: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const type = groupIntroConfig;
|
||||
@ -70,6 +54,5 @@ const openUrl = (link: string) => {
|
||||
TUIGlobal?.open(link);
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped src="../style/index.scss"></style>
|
||||
|
@ -1,54 +1,23 @@
|
||||
<template>
|
||||
<Dialog
|
||||
:show="true"
|
||||
:isH5="!isPC"
|
||||
:isHeaderShow="false"
|
||||
:isFooterShow="false"
|
||||
:background="false"
|
||||
@update:show="closeCreated"
|
||||
>
|
||||
<div
|
||||
class="group"
|
||||
:class="[!isPC ? 'group-h5' : '']"
|
||||
>
|
||||
<Dialog :show="true" :isH5="!isPC" :isHeaderShow="false" :isFooterShow="false" :background="false" @update:show="closeCreated">
|
||||
<div class="group" :class="[!isPC ? 'group-h5' : '']">
|
||||
<div class="group-box">
|
||||
<header class="group-box-header">
|
||||
<Icon
|
||||
:file="isPC ? closeIcon : backIcon"
|
||||
class="icon-close"
|
||||
size="16px"
|
||||
@onClick="closeCreated"
|
||||
/>
|
||||
<Icon :file="isPC ? closeIcon : backIcon" class="icon-close" size="16px" @onClick="closeCreated" />
|
||||
<h1 class="group-box-header-title">
|
||||
{{ headerTitle }}
|
||||
</h1>
|
||||
</header>
|
||||
<ul
|
||||
v-if="!groupInfo.isEdit"
|
||||
class="group-list"
|
||||
>
|
||||
<ul v-if="!groupInfo.isEdit" class="group-list">
|
||||
<li class="group-list-item">
|
||||
<label class="group-list-item-label">{{ TUITranslateService.t('TUIGroup.群头像') }}</label>
|
||||
<Avatar :url="groupInfo.profile.avatar" />
|
||||
</li>
|
||||
<ul>
|
||||
<li
|
||||
v-for="(item, index) in createInfo"
|
||||
:key="index"
|
||||
class="group-list-item"
|
||||
>
|
||||
<li v-for="(item, index) in createInfo" :key="index" class="group-list-item">
|
||||
<label class="group-list-item-label">{{ item.name }}</label>
|
||||
<input
|
||||
v-if="isPC"
|
||||
v-model="groupInfo.profile[item.key]"
|
||||
type="text"
|
||||
:placeholder="item.placeholder"
|
||||
>
|
||||
<span
|
||||
v-else
|
||||
class="group-h5-list-item-content"
|
||||
@click="edit(item.key)"
|
||||
>
|
||||
<input v-if="isPC" v-model="groupInfo.profile[item.key]" type="text" :placeholder="item.placeholder" />
|
||||
<span v-else class="group-h5-list-item-content" @click="edit(item.key)">
|
||||
<p class="content">{{ groupInfo.profile[item.key] }}</p>
|
||||
<Icon :file="rightIcon" />
|
||||
</span>
|
||||
@ -56,30 +25,16 @@
|
||||
<li class="group-list-introduction">
|
||||
<div class="group-list-item">
|
||||
<label class="group-list-item-label">{{ TUITranslateService.t('TUIGroup.群类型') }}</label>
|
||||
<GroupIntroduction
|
||||
v-if="isPC"
|
||||
:groupType="groupInfo.profile.type"
|
||||
@selectType="selected"
|
||||
/>
|
||||
<span
|
||||
v-else
|
||||
class="group-h5-list-item-content"
|
||||
@click="edit('type')"
|
||||
>
|
||||
<GroupIntroduction v-if="isPC" :groupType="groupInfo.profile.type" @selectType="selected" />
|
||||
<span v-else class="group-h5-list-item-content" @click="edit('type')">
|
||||
<p class="content">{{ groupTypeDetail.label }}</p>
|
||||
<Icon :file="rightIcon" />
|
||||
</span>
|
||||
</div>
|
||||
<article
|
||||
v-if="!isPC"
|
||||
class="group-h5-list-item-introduction"
|
||||
>
|
||||
<article v-if="!isPC" class="group-h5-list-item-introduction">
|
||||
<label class="introduction-name">{{ groupTypeDetail.label }}:</label>
|
||||
<span class="introduction-detail">{{ groupTypeDetail.detail }}</span>
|
||||
<a
|
||||
:href="documentLink.product.url"
|
||||
target="view_window"
|
||||
>
|
||||
<a :href="documentLink.product.url" target="view_window">
|
||||
{{ TUITranslateService.t(`TUIGroup.${groupTypeDetail.src}`) }}
|
||||
</a>
|
||||
</article>
|
||||
@ -87,37 +42,21 @@
|
||||
</ul>
|
||||
</ul>
|
||||
<!-- Edit Group Name -->
|
||||
<div
|
||||
v-else
|
||||
class="group-list group-list-edit"
|
||||
>
|
||||
<div v-else class="group-list group-list-edit">
|
||||
<input
|
||||
v-if="groupInfo.groupConfig.type === 'input'"
|
||||
v-model="groupInfo.groupConfig.value"
|
||||
class="group-name-input"
|
||||
type="text"
|
||||
:placeholder="TUITranslateService.t(`TUIGroup.${groupInfo.groupConfig.placeholder}`)"
|
||||
>
|
||||
<GroupIntroduction
|
||||
v-else
|
||||
class="group-introduction-list"
|
||||
:groupType="groupInfo.groupConfig.value"
|
||||
@selectType="selected"
|
||||
/>
|
||||
<GroupIntroduction v-else class="group-introduction-list" :groupType="groupInfo.groupConfig.value" @selectType="selected" />
|
||||
</div>
|
||||
<footer class="group-profile-footer">
|
||||
<button
|
||||
v-if="isPC && !groupInfo.isEdit"
|
||||
class="btn-default"
|
||||
@click="closeCreated"
|
||||
>
|
||||
<button v-if="isPC && !groupInfo.isEdit" class="btn-default" @click="closeCreated">
|
||||
{{ TUITranslateService.t('TUIGroup.取消') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn-submit"
|
||||
:disabled="submitDisabledStatus"
|
||||
@click="submit"
|
||||
>
|
||||
<button class="btn-submit" :disabled="submitDisabledStatus" @click="submit">
|
||||
{{ TUITranslateService.t('TUIGroup.确认') }}
|
||||
</button>
|
||||
</footer>
|
||||
@ -126,12 +65,7 @@
|
||||
</Dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import TUIChatEngine, {
|
||||
TUITranslateService,
|
||||
TUIGroupService,
|
||||
TUIStore,
|
||||
StoreName,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import TUIChatEngine, { TUITranslateService, TUIGroupService, TUIStore, StoreName } from '@tencentcloud/chat-uikit-engine';
|
||||
import { computed, reactive, watchEffect } from '../../../adapter-vue';
|
||||
import documentLink from '../../../utils/documentLink';
|
||||
import { isPC } from '../../../utils/env';
|
||||
@ -159,16 +93,16 @@ const groupInfo = reactive<any>({
|
||||
notification: '',
|
||||
// joinOption: '',
|
||||
memberList: [],
|
||||
isSupportTopic: false,
|
||||
isSupportTopic: false
|
||||
},
|
||||
groupConfig: {
|
||||
title: '',
|
||||
value: '',
|
||||
key: '',
|
||||
type: '',
|
||||
placeholder: '',
|
||||
placeholder: ''
|
||||
},
|
||||
isEdit: false,
|
||||
isEdit: false
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
@ -193,16 +127,14 @@ const createInfo = computed(() => {
|
||||
const groupNameInput = {
|
||||
name: TUITranslateService.t('TUIGroup.群名称'),
|
||||
key: 'name',
|
||||
placeholder: TUITranslateService.t('TUIGroup.请输入群名称'),
|
||||
placeholder: TUITranslateService.t('TUIGroup.请输入群名称')
|
||||
};
|
||||
const groupIDInput = {
|
||||
name: `${TUITranslateService.t('TUIGroup.群ID')}(${TUITranslateService.t('TUIGroup.选填')})`,
|
||||
key: 'groupID',
|
||||
placeholder: TUITranslateService.t('TUIGroup.请输入群ID'),
|
||||
placeholder: TUITranslateService.t('TUIGroup.请输入群ID')
|
||||
};
|
||||
return groupInfo.profile.type === TUIChatEngine.TYPES.GRP_COMMUNITY
|
||||
? [groupNameInput]
|
||||
: [groupNameInput, groupIDInput];
|
||||
return groupInfo.profile.type === TUIChatEngine.TYPES.GRP_COMMUNITY ? [groupNameInput] : [groupNameInput, groupIDInput];
|
||||
});
|
||||
|
||||
const submitDisabledStatus = computed(() => {
|
||||
@ -232,18 +164,18 @@ const createGroup = async (options: any) => {
|
||||
if (type === TUIChatEngine.TYPES.GRP_AVCHATROOM) {
|
||||
await TUIGroupService.joinGroup({
|
||||
groupID: res.data.group.groupID,
|
||||
applyMessage: '',
|
||||
applyMessage: ''
|
||||
});
|
||||
}
|
||||
handleCompleteCreate(res.data.group);
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIGroup.群组创建成功'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
} catch (err: any) {
|
||||
Toast({
|
||||
message: err.message,
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -252,7 +184,7 @@ const submit = () => {
|
||||
const { profile } = groupInfo;
|
||||
if (groupInfo.isEdit) {
|
||||
groupInfo.profile[groupInfo.groupConfig.key] = groupInfo.groupConfig.value;
|
||||
return groupInfo.isEdit = !groupInfo.isEdit;
|
||||
return (groupInfo.isEdit = !groupInfo.isEdit);
|
||||
} else {
|
||||
createGroup(profile);
|
||||
}
|
||||
@ -260,7 +192,7 @@ const submit = () => {
|
||||
|
||||
const closeCreated = () => {
|
||||
if (groupInfo.isEdit) {
|
||||
return groupInfo.isEdit = !groupInfo.isEdit;
|
||||
return (groupInfo.isEdit = !groupInfo.isEdit);
|
||||
}
|
||||
handleCompleteCreate(null);
|
||||
};
|
||||
@ -292,6 +224,5 @@ const handleCompleteCreate = (group: any) => {
|
||||
const callback = TUIGroupServer.getOnCallCallback(TUIConstants.TUIGroup.SERVICE.METHOD.CREATE_GROUP);
|
||||
callback && callback(group);
|
||||
};
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped src="./style/index.scss"></style>
|
||||
|
@ -6,10 +6,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIStore, StoreName } from '@tencentcloud/chat-uikit-engine';
|
||||
import { ref } from '../../adapter-vue';
|
||||
|
||||
import CreateGroup from './create-group/index.vue';
|
||||
@ -41,9 +38,8 @@ TUIStore.watch(StoreName.GRP, {
|
||||
} else {
|
||||
isShowSelectMember.value = false;
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tui-group {
|
||||
|
@ -1,36 +1,16 @@
|
||||
<template>
|
||||
<div
|
||||
ref="manageRef"
|
||||
class="manage"
|
||||
>
|
||||
<header
|
||||
v-if="!isUniFrameWork || currentTab ==='admin'"
|
||||
class="manage-header"
|
||||
>
|
||||
<Icon
|
||||
:file="backSVG"
|
||||
@onClick="back"
|
||||
/>
|
||||
<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"
|
||||
/>
|
||||
<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')"
|
||||
>
|
||||
<header class="user-info-header" @click="setCurrentTab('member')">
|
||||
<label class="user-info-header-left">
|
||||
{{ TUITranslateService.t(`TUIGroup.群成员`) }}
|
||||
</label>
|
||||
@ -43,143 +23,71 @@
|
||||
</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)"
|
||||
>
|
||||
<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'
|
||||
"
|
||||
: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 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 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')"
|
||||
>
|
||||
<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>
|
||||
<label class="label">{{ TUITranslateService.t(`TUIGroup.群公告`) }}</label>
|
||||
<article class="article">
|
||||
{{ currentGroup.notification }}
|
||||
</article>
|
||||
</aside>
|
||||
<Icon
|
||||
:file="rightIcon"
|
||||
class="end"
|
||||
/>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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'
|
||||
"
|
||||
: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>
|
||||
<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>
|
||||
<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')"
|
||||
>
|
||||
<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)"
|
||||
>
|
||||
<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)"
|
||||
>
|
||||
<li v-else class="list-item" @click.stop="quitGroup(currentGroup)">
|
||||
{{ TUITranslateService.t(`TUIGroup.退出群组`) }}
|
||||
</li>
|
||||
</ul>
|
||||
@ -195,11 +103,7 @@
|
||||
@handleMemberProfileShow="handleMemberProfileShow"
|
||||
@close="setCurrentTab('')"
|
||||
/>
|
||||
<ManageProfile
|
||||
v-if="currentTab === 'profile'"
|
||||
:userInfo="currentMember"
|
||||
@close="setCurrentTab('')"
|
||||
/>
|
||||
<ManageProfile v-if="currentTab === 'profile'" :userInfo="currentMember" @close="setCurrentTab('')" />
|
||||
<ManageNotification
|
||||
v-if="currentTab === 'notification'"
|
||||
:isAuthor="isOwner || isAdmin || isWorkGroup"
|
||||
@ -220,10 +124,7 @@
|
||||
@removeMute="toggleMask('removeMute')"
|
||||
@close="setCurrentTab('')"
|
||||
/>
|
||||
<MaskLayer
|
||||
:show="mask"
|
||||
@update:show="(e) => (mask = e)"
|
||||
>
|
||||
<MaskLayer :show="mask" @update:show="(e) => (mask = e)">
|
||||
<Transfer
|
||||
:title="TUITranslateService.t(`TUIGroup.${transferTitle}`)"
|
||||
:list="transferList"
|
||||
@ -246,29 +147,17 @@
|
||||
@submit="handleManage(deletedUserList, 'remove')"
|
||||
@update:show="(e) => (delDialogShow = e)"
|
||||
>
|
||||
<p
|
||||
v-if="deletedUserList.length === 1"
|
||||
class="del-dialog-title"
|
||||
>
|
||||
<p v-if="deletedUserList.length === 1" class="del-dialog-title">
|
||||
{{ TUITranslateService.t(`TUIGroup.确定从群聊中删除该成员?`) }}
|
||||
</p>
|
||||
<p
|
||||
v-if="deletedUserList.length > 1"
|
||||
class="del-dialog-title"
|
||||
>
|
||||
<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 { ref, computed, watchEffect, onMounted, nextTick } from '../../../adapter-vue';
|
||||
import TUIChatEngine, {
|
||||
TUITranslateService,
|
||||
TUIGroupService,
|
||||
@ -277,7 +166,7 @@ import TUIChatEngine, {
|
||||
StoreName,
|
||||
IGroupModel,
|
||||
TUIConversationService,
|
||||
IConversationModel,
|
||||
IConversationModel
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGlobal, outsideClick } from '@tencentcloud/universal-api';
|
||||
import MaskLayer from '../../common/MaskLayer/index.vue';
|
||||
@ -303,12 +192,12 @@ const TUIConstants = TUIGroupServer.constants;
|
||||
const props = defineProps({
|
||||
groupID: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: ''
|
||||
},
|
||||
groupCurrentTab: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const manageRef = ref<any>(undefined);
|
||||
@ -319,7 +208,7 @@ const transferType = ref('');
|
||||
const mask = ref(false);
|
||||
const currentGroupID = ref('');
|
||||
const userInfo = ref({
|
||||
list: [] as IGroupMember[],
|
||||
list: [] as IGroupMember[]
|
||||
});
|
||||
const currentMember = ref<IGroupMember>({});
|
||||
const typeName = ref({
|
||||
@ -330,12 +219,12 @@ const typeName = ref({
|
||||
[TUIChatEngine.TYPES.GRP_COMMUNITY]: '社群',
|
||||
[TUIChatEngine.TYPES.JOIN_OPTIONS_FREE_ACCESS]: '自由加入',
|
||||
[TUIChatEngine.TYPES.JOIN_OPTIONS_NEED_PERMISSION]: '需要验证',
|
||||
[TUIChatEngine.TYPES.JOIN_OPTIONS_DISABLE_APPLY]: '禁止加群',
|
||||
[TUIChatEngine.TYPES.JOIN_OPTIONS_DISABLE_APPLY]: '禁止加群'
|
||||
});
|
||||
const member = ref({
|
||||
admin: [] as IGroupMember[],
|
||||
member: [] as IGroupMember[],
|
||||
muteMember: [] as IGroupMember[],
|
||||
muteMember: [] as IGroupMember[]
|
||||
});
|
||||
const transferList = ref<IGroupMember[]>([]);
|
||||
const transferTitle = ref('');
|
||||
@ -354,7 +243,7 @@ onMounted(() => {
|
||||
if (manageRef.value && !isUniFrameWork) {
|
||||
outsideClick.listen({
|
||||
domRefs: manageRef.value,
|
||||
handler: handleCompleteManage,
|
||||
handler: handleCompleteManage
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -372,7 +261,7 @@ TUIStore.watch(StoreName.GRP, {
|
||||
member.value = {
|
||||
admin: [],
|
||||
member: [],
|
||||
muteMember: [],
|
||||
muteMember: []
|
||||
};
|
||||
Array.from(memberList).map((item: any) => {
|
||||
switch (item?.role) {
|
||||
@ -388,16 +277,14 @@ TUIStore.watch(StoreName.GRP, {
|
||||
return item;
|
||||
});
|
||||
const time: number = new Date().getTime();
|
||||
member.value.muteMember = Array.from(memberList).filter(
|
||||
(item: any) => item?.muteUntil * 1000 - time > 0,
|
||||
);
|
||||
},
|
||||
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(() => {
|
||||
@ -460,7 +347,7 @@ const updateProfile = async (newGroupProfile: any) => {
|
||||
const { key, value } = newGroupProfile;
|
||||
const options: any = {
|
||||
groupID: currentGroup.value.groupID,
|
||||
[key]: value,
|
||||
[key]: value
|
||||
};
|
||||
TUIGroupService.updateGroupProfile(options)
|
||||
.then((res: any) => {
|
||||
@ -470,7 +357,7 @@ const updateProfile = async (newGroupProfile: any) => {
|
||||
.catch((error: any) => {
|
||||
Toast({
|
||||
message: error?.message,
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -504,9 +391,7 @@ const toggleMask = async (type?: string) => {
|
||||
break;
|
||||
case 'remove':
|
||||
isRadio.value = false;
|
||||
transferList.value = groupMemberList.value.filter(
|
||||
(item: any) => item.userID !== currentGroup?.value?.selfInfo?.userID,
|
||||
);
|
||||
transferList.value = groupMemberList.value.filter((item: any) => item.userID !== currentGroup?.value?.selfInfo?.userID);
|
||||
transferTitle.value = '删除成员';
|
||||
break;
|
||||
case 'addAdmin':
|
||||
@ -550,22 +435,14 @@ const toggleMask = async (type?: string) => {
|
||||
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,
|
||||
),
|
||||
);
|
||||
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
|
||||
);
|
||||
return userRole === TUIChatEngine.TYPES.GRP_MBR_ROLE_OWNER && groupType !== TUIChatEngine.TYPES.GRP_WORK;
|
||||
});
|
||||
|
||||
const isShowAddMember = computed(() => {
|
||||
@ -589,7 +466,7 @@ const getMember = async (type?: string) => {
|
||||
const options = {
|
||||
groupID,
|
||||
count: 100,
|
||||
offset: type && type === 'more' ? userInfo.value.list.length : 0,
|
||||
offset: type && type === 'more' ? userInfo.value.list.length : 0
|
||||
};
|
||||
await TUIGroupService.getGroupMemberList(options).then((res: any) => {
|
||||
if (type && type === 'more') {
|
||||
@ -620,7 +497,7 @@ const dismissGroup = async (group: any) => {
|
||||
enableSampleTaskStatus('dismissGroup');
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIGroup.群组解散成功'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
clearGroupInfo();
|
||||
};
|
||||
@ -628,7 +505,7 @@ const dismissGroup = async (group: any) => {
|
||||
const clearGroupInfo = () => {
|
||||
if (isUniFrameWork) {
|
||||
TUIGlobal?.switchTab({
|
||||
url: '/TUIKit/components/TUIConversation/index',
|
||||
url: '/TUIKit/components/TUIConversation/index'
|
||||
});
|
||||
} else {
|
||||
handleCompleteManage();
|
||||
@ -643,12 +520,12 @@ const setAllMuteTime = (value: boolean) => {
|
||||
enableSampleTaskStatus('muteGroup');
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIGroup.禁言设置成功'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
} else {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIGroup.取消禁言成功'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -658,15 +535,13 @@ const handleSearchMember = async (value: string) => {
|
||||
let imMemberResponse: any = {};
|
||||
const options: any = {
|
||||
groupID: currentGroupID.value,
|
||||
userIDList: [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.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) => {
|
||||
@ -680,7 +555,7 @@ const handleSearchMember = async (value: string) => {
|
||||
const message = TUITranslateService.t('TUIGroup.该用户不存在');
|
||||
Toast({
|
||||
message,
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
}
|
||||
break;
|
||||
@ -691,23 +566,18 @@ const handleSearchMember = async (value: string) => {
|
||||
const message = TUITranslateService.t('TUIGroup.该用户不在群组内');
|
||||
Toast({
|
||||
message,
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
}
|
||||
transferList.value = transferList.value.filter(
|
||||
(item: any) => item.userID !== imResponse?.data?.memberList[0]?.userID,
|
||||
);
|
||||
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,
|
||||
];
|
||||
transferList.value = [...transferList.value, ...imResponse.data.memberList];
|
||||
}
|
||||
} catch (error: any) {
|
||||
const message = TUITranslateService.t('TUIGroup.该用户不存在');
|
||||
Toast({
|
||||
message,
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
}
|
||||
break;
|
||||
@ -752,7 +622,7 @@ const handleManage = (userList: any, type: any) => {
|
||||
const addMember = async (userIDList: any) => {
|
||||
const options: any = {
|
||||
groupID: currentGroupID.value,
|
||||
userIDList,
|
||||
userIDList
|
||||
};
|
||||
await TUIGroupService.addGroupMember(options);
|
||||
};
|
||||
@ -760,7 +630,7 @@ const addMember = async (userIDList: any) => {
|
||||
const changeOwner = async (userID: any) => {
|
||||
const options: any = {
|
||||
groupID: currentGroupID.value,
|
||||
newOwnerID: userID,
|
||||
newOwnerID: userID
|
||||
};
|
||||
const imResponse = await TUIGroupService.changeGroupOwner(options);
|
||||
currentGroup.value = {};
|
||||
@ -771,7 +641,7 @@ const setMemberMuteTime = async (userID: string, type?: string) => {
|
||||
const options: any = {
|
||||
groupID: currentGroupID.value,
|
||||
userID,
|
||||
muteTime: type === 'add' ? 60 * 60 * 24 * 30 : 0,
|
||||
muteTime: type === 'add' ? 60 * 60 * 24 * 30 : 0
|
||||
};
|
||||
await TUIGroupService.setGroupMemberMuteTime(options);
|
||||
};
|
||||
@ -789,7 +659,7 @@ const handleAdmin = async (user: any) => {
|
||||
const options: any = {
|
||||
groupID: currentGroupID.value,
|
||||
userID: user.userID,
|
||||
role,
|
||||
role
|
||||
};
|
||||
await TUIGroupService.setGroupMemberRole(options);
|
||||
};
|
||||
@ -798,7 +668,7 @@ const deleteGroupMember = async (userIDList: any) => {
|
||||
const options: any = {
|
||||
groupID: currentGroupID.value,
|
||||
userIDList,
|
||||
reason: '',
|
||||
reason: ''
|
||||
};
|
||||
await TUIGroupService.deleteGroupMember(options);
|
||||
};
|
||||
|
@ -5,123 +5,66 @@
|
||||
{{ TUITranslateService.t(`TUIGroup.群管理员`) }}
|
||||
</div>
|
||||
<ul class="admin-manage-list">
|
||||
<li
|
||||
v-for="(item, index) in memberAdmin.admin"
|
||||
:key="index"
|
||||
class="admin-manage-list-item"
|
||||
>
|
||||
<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'
|
||||
"
|
||||
: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 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 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 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.全员禁言开启后,只允许群主和管理员发言。`
|
||||
)
|
||||
}}
|
||||
{{ TUITranslateService.t(`TUIGroup.全员禁言开启后,只允许群主和管理员发言。`) }}
|
||||
</div>
|
||||
</div>
|
||||
<Slider
|
||||
:open="currentGroupAdmin.muteAllMembers"
|
||||
@change="setAllMuteTime"
|
||||
/>
|
||||
<Slider :open="currentGroupAdmin.muteAllMembers" @change="setAllMuteTime" />
|
||||
</div>
|
||||
<div
|
||||
v-if="isAdminSetMuteTime"
|
||||
class="admin-mute"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<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'
|
||||
"
|
||||
: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 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 v-if="memberAdmin.muteMember.length > 0" class="item-main" @click="removeMute">
|
||||
<Icon :file="minusSVG" width="16px" height="16px" />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@ -130,10 +73,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
TUITranslateService,
|
||||
IGroupModel,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
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';
|
||||
@ -144,23 +84,23 @@ import { IGroupMember } from '../../../interface';
|
||||
const props = defineProps({
|
||||
member: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
default: () => {}
|
||||
},
|
||||
isSetMuteTime: {
|
||||
type: Boolean,
|
||||
default: () => false,
|
||||
default: () => false
|
||||
},
|
||||
currentGroup: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
default: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
const isAdminSetMuteTime = ref(false);
|
||||
const memberAdmin = ref({
|
||||
admin: [] as Array<IGroupMember>,
|
||||
member: [] as Array<IGroupMember>,
|
||||
muteMember: [] as Array<IGroupMember>,
|
||||
muteMember: [] as Array<IGroupMember>
|
||||
});
|
||||
const currentGroupAdmin = ref<IGroupModel>();
|
||||
|
||||
@ -174,14 +114,7 @@ watchEffect(() => {
|
||||
currentGroupAdmin.value = props.currentGroup;
|
||||
});
|
||||
|
||||
const emits = defineEmits([
|
||||
'addAdmin',
|
||||
'removeAdmin',
|
||||
'setAllMuteTime',
|
||||
'addMute',
|
||||
'removeMute',
|
||||
'close',
|
||||
]);
|
||||
const emits = defineEmits(['addAdmin', 'removeAdmin', 'setAllMuteTime', 'addMute', 'removeMute', 'close']);
|
||||
|
||||
const addAdmin = () => {
|
||||
emits('addAdmin');
|
||||
@ -205,7 +138,7 @@ const removeMute = () => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../assets/styles/common";
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
.admin {
|
||||
width: 100%;
|
||||
@ -218,7 +151,7 @@ const removeMute = () => {
|
||||
padding: 10px;
|
||||
|
||||
&-left {
|
||||
font-family: "PingFang SC", sans-serif;
|
||||
font-family: 'PingFang SC', sans-serif;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
line-height: 50px;
|
||||
@ -227,7 +160,7 @@ const removeMute = () => {
|
||||
}
|
||||
|
||||
&-close {
|
||||
font-family: "PingFang SC", sans-serif;
|
||||
font-family: 'PingFang SC', sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 48px;
|
||||
@ -253,7 +186,7 @@ const removeMute = () => {
|
||||
|
||||
&-header {
|
||||
padding-left: 10px;
|
||||
font-family: "PingFang SC", sans-serif;
|
||||
font-family: 'PingFang SC', sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
@ -314,7 +247,7 @@ const removeMute = () => {
|
||||
|
||||
&-title {
|
||||
padding-left: 10px;
|
||||
font-family: "PingFang SC", sans-serif;
|
||||
font-family: 'PingFang SC', sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
@ -325,7 +258,7 @@ const removeMute = () => {
|
||||
&-content {
|
||||
color: #999;
|
||||
padding-left: 10px;
|
||||
font-family: "PingFang SC", sans-serif;
|
||||
font-family: 'PingFang SC', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 17px;
|
||||
|
@ -1,99 +1,50 @@
|
||||
<template>
|
||||
<main
|
||||
v-if="!isUniFrameWork"
|
||||
class="member"
|
||||
>
|
||||
<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)"
|
||||
>
|
||||
<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'
|
||||
"
|
||||
: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'"
|
||||
/>
|
||||
<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"
|
||||
>
|
||||
<li v-if="memberList.length < totalMember" class="list-item" @click="getMore">
|
||||
{{ TUITranslateService.t(`TUIGroup.查看更多`) }}
|
||||
</li>
|
||||
</ul>
|
||||
</main>
|
||||
<div
|
||||
v-else
|
||||
class="edit-h5"
|
||||
>
|
||||
<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>
|
||||
<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)"
|
||||
>
|
||||
<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'
|
||||
"
|
||||
: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'"
|
||||
/>
|
||||
<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"
|
||||
>
|
||||
<li v-if="memberList.length < totalMember" class="list-item" @click="getMore">
|
||||
{{ TUITranslateService.t(`TUIGroup.查看更多`) }}
|
||||
</li>
|
||||
</ul>
|
||||
@ -103,9 +54,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import TUIChatEngine, {
|
||||
TUITranslateService,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
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';
|
||||
@ -115,20 +64,20 @@ import { isUniFrameWork } from '../../../utils/env';
|
||||
const props = defineProps({
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
default: () => []
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: () => 0,
|
||||
default: () => 0
|
||||
},
|
||||
isShowDel: {
|
||||
type: Boolean,
|
||||
default: () => false,
|
||||
default: () => false
|
||||
},
|
||||
self: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
|
||||
const totalMember = ref(0);
|
||||
@ -182,7 +131,7 @@ const close = (tabName: string) => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../assets/styles/common";
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
.member {
|
||||
flex: 1;
|
||||
|
@ -4,83 +4,38 @@
|
||||
<div
|
||||
v-if="isEdit"
|
||||
:class="{
|
||||
'edit-h5': isMobile,
|
||||
'edit-h5': isMobile
|
||||
}"
|
||||
>
|
||||
<main class="edit-h5-main">
|
||||
<header
|
||||
v-if="!isPC"
|
||||
class="edit-h5-header"
|
||||
>
|
||||
<header v-if="!isPC" class="edit-h5-header">
|
||||
<aside class="left">
|
||||
<h1>{{ TUITranslateService.t(`TUIGroup.修改群聊名称`) }}</h1>
|
||||
<span>{{
|
||||
TUITranslateService.t(
|
||||
`TUIGroup.修改群聊名称后,将在群内通知其他成员`
|
||||
)
|
||||
}}</span>
|
||||
<span>{{ TUITranslateService.t(`TUIGroup.修改群聊名称后,将在群内通知其他成员`) }}</span>
|
||||
</aside>
|
||||
<span
|
||||
class="close"
|
||||
@click="toggleEditStatus"
|
||||
>{{
|
||||
TUITranslateService.t(`关闭`)
|
||||
}}</span>
|
||||
<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>
|
||||
<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"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
<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 { 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';
|
||||
@ -89,12 +44,12 @@ import { isMobile, isPC } from '../../../utils/env';
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
default: () => ({})
|
||||
},
|
||||
isAuthor: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const groupProfile = ref<IGroupModel>({});
|
||||
@ -111,7 +66,7 @@ const updateProfile = () => {
|
||||
if (!inputGroupName.value) {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIGroup.群名称不能为空'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
} else {
|
||||
if (inputGroupName.value !== groupProfile.value.name) {
|
||||
@ -120,7 +75,7 @@ const updateProfile = () => {
|
||||
inputGroupName.value = '';
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIGroup.群名称修改成功'),
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
type: TOAST_TYPE.SUCCESS
|
||||
});
|
||||
}
|
||||
toggleEditStatus();
|
||||
@ -144,12 +99,12 @@ watch(
|
||||
nameInputRef.value?.focus();
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../assets/styles/common";
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
.group-name {
|
||||
padding: 14px 20px;
|
||||
|
@ -1,14 +1,6 @@
|
||||
<template>
|
||||
<main
|
||||
v-if="!isUniFrameWork"
|
||||
class="notification"
|
||||
>
|
||||
<textarea
|
||||
v-if="isEdit"
|
||||
v-model="input"
|
||||
class="textarea"
|
||||
@keyup.enter="updateProfile"
|
||||
/>
|
||||
<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.暂无公告`) }}
|
||||
@ -18,75 +10,37 @@
|
||||
</article>
|
||||
</section>
|
||||
<footer v-if="isAuthorNotification">
|
||||
<button
|
||||
v-if="isEdit"
|
||||
class="btn"
|
||||
@click="updateProfile"
|
||||
>
|
||||
<button v-if="isEdit" class="btn" @click="updateProfile">
|
||||
{{ TUITranslateService.t(`TUIGroup.发布`) }}
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn"
|
||||
@click="isEdit = !isEdit"
|
||||
>
|
||||
<button v-else class="btn" @click="isEdit = !isEdit">
|
||||
{{ TUITranslateService.t(`TUIGroup.编辑`) }}
|
||||
</button>
|
||||
</footer>
|
||||
</main>
|
||||
<div
|
||||
v-else
|
||||
class="edit-h5"
|
||||
>
|
||||
<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>
|
||||
<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"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<button v-else class="btn" @click="isEdit = !isEdit">
|
||||
{{ TUITranslateService.t(`TUIGroup.编辑`) }}
|
||||
</button>
|
||||
</footer>
|
||||
@ -97,10 +51,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick } from '../../../adapter-vue';
|
||||
import {
|
||||
TUITranslateService,
|
||||
IGroupModel,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
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';
|
||||
@ -108,12 +59,12 @@ import { isUniFrameWork } from '../../../utils/env';
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
default: () => ({})
|
||||
},
|
||||
isAuthor: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const groupProfile = ref<IGroupModel>({});
|
||||
@ -133,7 +84,7 @@ const updateProfile = () => {
|
||||
if (input.value.length > 150) {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIGroup.群公告字数超出限制,最大长度为150'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -152,7 +103,7 @@ const close = (tabName: string) => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../assets/styles/common";
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
.notification {
|
||||
flex: 1;
|
||||
|
@ -1,17 +1,11 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="!isUniFrameWork"
|
||||
class="memeber-profile"
|
||||
>
|
||||
<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'
|
||||
"
|
||||
: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>
|
||||
@ -19,44 +13,28 @@
|
||||
<span>{{ userInfoManager.userID }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<label>{{ TUITranslateService.t("TUIContact.个性签名") }}:</label>
|
||||
<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 v-if="showEnter()" class="button" @click="enter(userInfoManager.userID, 'C2C')">
|
||||
{{ TUITranslateService.t('TUIContact.发送消息') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="edit-h5"
|
||||
>
|
||||
<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>
|
||||
<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"
|
||||
/>
|
||||
<Avatar class="avatar" :url="userInfoManager.avatar" size="60px" />
|
||||
<ul class="list">
|
||||
<h1>{{ userInfoManager.nick || userInfoManager.userID }}</h1>
|
||||
<li>
|
||||
@ -64,18 +42,14 @@
|
||||
<span>{{ userInfoManager.userID }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<label>{{ TUITranslateService.t("TUIContact.个性签名") }}:</label>
|
||||
<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 v-if="showEnter()" class="button" @click="enter(userInfoManager.userID, 'C2C')">
|
||||
{{ TUITranslateService.t('TUIContact.发送消息') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -90,7 +64,7 @@ import TUIChatEngine, {
|
||||
TUIConversationService,
|
||||
TUIFriendService,
|
||||
TUIStore,
|
||||
StoreName,
|
||||
StoreName
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import Avatar from '../../common/Avatar/index.vue';
|
||||
@ -100,8 +74,8 @@ import { isUniFrameWork } from '../../../utils/env';
|
||||
const props = defineProps({
|
||||
userInfo: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
|
||||
const isFriendShip = ref(false);
|
||||
@ -110,26 +84,22 @@ const userInfoManager = ref<IUserProfile>({});
|
||||
watchEffect(() => {
|
||||
userInfoManager.value = props.userInfo;
|
||||
});
|
||||
const emits = defineEmits([
|
||||
'handleSwitchConversation',
|
||||
'close',
|
||||
'openConversation',
|
||||
]);
|
||||
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],
|
||||
userIDList: [props.userInfo.userID]
|
||||
});
|
||||
userInfoManager.value = res?.data[0];
|
||||
checkFriend();
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
const enter = async (ID: any, type: string) => {
|
||||
@ -151,10 +121,10 @@ const checkFriend = async () => {
|
||||
if (!(userInfoManager.value as any).userID) return;
|
||||
TUIFriendService.checkFriend({
|
||||
userIDList: [userInfoManager.value.userID],
|
||||
type: TUIChatEngine.TYPES.SNS_CHECK_TYPE_BOTH,
|
||||
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);
|
||||
isFriendShip.value = relation === TUIChatEngine.TYPES.SNS_TYPE_BOTH_WAY;
|
||||
});
|
||||
};
|
||||
|
||||
@ -167,7 +137,7 @@ const close = (tabName: string) => {
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../assets/styles/common";
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
.memeber-profile {
|
||||
flex: 1;
|
||||
@ -267,7 +237,6 @@ const close = (tabName: string) => {
|
||||
.avatar {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,14 +11,7 @@
|
||||
/>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
TUIGroupService,
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUITranslateService,
|
||||
IGroupMember,
|
||||
IGroupModel,
|
||||
} from '@tencentcloud/chat-uikit-engine';
|
||||
import { TUIGroupService, TUIStore, StoreName, TUITranslateService, IGroupMember, IGroupModel } from '@tencentcloud/chat-uikit-engine';
|
||||
import { ref, watchEffect } from '../../../adapter-vue';
|
||||
import { Toast, TOAST_TYPE } from '../../common/Toast/index';
|
||||
import TUICore from '@tencentcloud/tui-core';
|
||||
@ -36,7 +29,7 @@ const selectOptions: any = ref({
|
||||
isRadio: false,
|
||||
isNeedSearch: false,
|
||||
title: '',
|
||||
filterUserIDList: [] as Array<string>,
|
||||
filterUserIDList: [] as Array<string>
|
||||
});
|
||||
const TUISearchServer = ref<any>(null);
|
||||
|
||||
@ -72,7 +65,7 @@ TUIStore.watch(StoreName.GRP, {
|
||||
}
|
||||
});
|
||||
userList.value = memberList.value;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const getMember = async () => {
|
||||
@ -80,7 +73,7 @@ const getMember = async () => {
|
||||
const options = {
|
||||
groupID,
|
||||
count: 100,
|
||||
offset: memberList.value.length,
|
||||
offset: memberList.value.length
|
||||
};
|
||||
await TUIGroupService.getGroupMemberList(options);
|
||||
};
|
||||
@ -94,14 +87,14 @@ const handleSelectedResult = (memberList: Array<any>) => {
|
||||
const searchFail = () => {
|
||||
Toast({
|
||||
message: TUITranslateService.t('TUIGroup.该用户不存在'),
|
||||
type: TOAST_TYPE.ERROR,
|
||||
type: TOAST_TYPE.ERROR
|
||||
});
|
||||
userList.value = [...memberList.value];
|
||||
};
|
||||
|
||||
const handleSearch = async (val: string) => {
|
||||
if (!val) {
|
||||
return userList.value = memberList.value;
|
||||
return (userList.value = memberList.value);
|
||||
}
|
||||
|
||||
try {
|
||||
@ -116,5 +109,4 @@ const handleSearch = async (val: string) => {
|
||||
return searchFail();
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user