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

View File

@@ -0,0 +1,37 @@
import TUIChatEngine, { IMessageModel } from '@tencentcloud/chat-uikit-engine';
import { isCustomerServicePluginMessage } from './message-customer/index';
import { JSONToObject } from '../../utils/type-check';
export function isCallMessage(message: IMessageModel): boolean {
const payloadData = JSONToObject(message?.payload?.data);
if (payloadData?.businessID === 1 && payloadData?.data) {
const payloadDataData = JSONToObject(payloadData.data);
if (payloadDataData.businessID === 'av_call') {
return true;
}
}
return false;
}
export function isRoomSignalingMessage(message: IMessageModel): boolean {
const payloadData = JSONToObject(message?.payload?.data);
return (
payloadData?.businessID === 'ROOM_INVITE_ACTION'
|| payloadData?.businessID === 'tuikit_engine_room'
);
}
export function isRoomCardMessage(message: IMessageModel): boolean {
const payloadData = JSONToObject(message?.payload?.data);
return payloadData?.businessID === 'group_room_message';
}
export function isPluginMessage(message: IMessageModel): boolean {
return (
message.type === TUIChatEngine.TYPES.MSG_CUSTOM
&& (isCallMessage(message)
|| isCustomerServicePluginMessage(message as any)
|| isRoomCardMessage(message)
|| isRoomSignalingMessage(message))
);
}

View File

@@ -0,0 +1,116 @@
<template>
<div
v-if="isCallMessage && conversationType === TYPES.CONV_C2C"
class="call"
:class="['call-' + conversationType, message.flow === 'out' && 'call-reverse']"
@click="callAgain"
>
<div :class="['icon', message.flow === 'out' && callInfo.type === 2 && 'icon-reverse']">
<Icon :file="callInfo.icon" />
</div>
<span class="call-content">{{ custom }}</span>
</div>
</template>
<script setup lang="ts">
import TUICore, { TUIConstants } from '@tencentcloud/tui-core';
import TUIChatEngine from '@tencentcloud/chat-uikit-engine';
import { computed, ref } from '../../../adapter-vue';
import { JSONToObject } from '../../../utils/index';
import Icon from '../../../components/common/Icon.vue';
import callVideoSVG from '../../../assets/icon/call-video.svg';
import callVoiceSVG from '../../../assets/icon/call-voice.svg';
import OfflinePushInfoManager, { PUSH_SCENE } from '../../../components/TUIChat/offlinePushInfoManager/index';
const props = defineProps({
message: {
type: Object,
default: () => ({}),
},
signalingInfo: {
type: Object,
default: () => ({}),
},
customContent: {
type: Object,
default: () => ({}),
},
});
const TYPES = ref(TUIChatEngine.TYPES);
const isCallMessage = computed(() => props.signalingInfo != null);
const callInfo = computed((): { type: number; icon: string } => {
const callType = JSONToObject(props.signalingInfo?.data)?.call_type;
switch (callType) {
case 1:
return {
type: 1,
icon: callVoiceSVG,
};
case 2:
return {
type: 2,
icon: callVideoSVG,
};
default:
break;
}
return {
type: 0,
icon: '',
};
});
const conversationType = computed(() => props.message?.conversationType);
const custom = computed(() => props.customContent?.custom);
const callAgain = () => {
if (conversationType.value === TUIChatEngine.TYPES.CONV_C2C) {
const userID = props.message?.flow === 'out' ? props.message?.to : props.message?.from;
TUICore.callService({
serviceName: TUIConstants.TUICalling.SERVICE.NAME,
method: TUIConstants.TUICalling.SERVICE.METHOD.START_CALL,
params: {
userIDList: [userID],
type: callInfo?.value?.type,
callParams: {
offlinePushInfo: OfflinePushInfoManager.getOfflinePushInfo(PUSH_SCENE.CALL),
},
},
});
}
};
</script>
<style scoped lang="scss">
.call {
display: flex;
flex-direction: row;
align-items: center;
&-C2C {
cursor: pointer;
}
&-GROUP {
cursor: default;
}
&-content {
padding-left: 5px;
}
.icon {
width: 20px;
height: 20px;
}
&-reverse {
flex-direction: row-reverse;
.icon-reverse {
transform: rotate(180deg);
}
.call-content {
padding-right: 5px;
padding-left: 0;
}
}
}
</style>

View File

@@ -0,0 +1,51 @@
<template>
<div
v-if="isCallMessage && conversationType === TYPES.CONV_GROUP"
:class="{ 'blink-text': isBlink }"
>
{{ custom }}
</div>
</template>
<script setup lang="ts">
import { computed } from '../../../adapter-vue';
import TUIChatEngine, { IMessageModel } from '@tencentcloud/chat-uikit-engine';
interface IProps {
message: IMessageModel;
signalingInfo: any;
customContent: any;
blinkMessageIDList?: string[];
}
const props = withDefaults(defineProps<IProps>(), {
message: () => ({}) as IMessageModel,
signalingInfo: () => ({}),
customContent: () => ({}),
blinkMessageIDList: () => [],
});
const TYPES = TUIChatEngine.TYPES;
const isCallMessage = computed(() => !!props.signalingInfo);
const conversationType = computed(() => props.message?.conversationType);
const custom = computed(() => props.customContent?.custom);
const isBlink = computed(() => {
if (props.message?.ID) {
return props.blinkMessageIDList?.includes(props.message.ID);
}
return false;
});
</script>
<style scoped lang="scss">
@keyframes blink-text {
50% {
color: #ff9c19;
}
}
.blink-text {
animation: blinkText 1s linear 3;
}
</style>

View File

@@ -0,0 +1,5 @@
import {
isCustomerServicePluginMessage,
isMessageInvisible as isCustomServiceMessageInvisible,
} from '../../../tui-customer-service-plugin';
export { isCustomerServicePluginMessage, isCustomServiceMessageInvisible };

View File

@@ -0,0 +1,16 @@
<template>
<TUICustomerServicePlugin :message="props.message" />
</template>
<script setup lang="ts">
import { IMessageModel } from '@tencentcloud/chat-uikit-engine';
import TUICustomerServicePlugin from '../../../tui-customer-service-plugin/index.vue';
interface IProps {
message: IMessageModel;
}
const props = withDefaults(defineProps<IProps>(), {
message: () => ({} as IMessageModel),
});
</script>
<style lang="scss" scoped>
/* stylelint-disable-next-line no-empty-source */
</style>

View File

@@ -0,0 +1,123 @@
<template>
<div class="message-plugin">
<!-- The following is displayed in the form of messageTip -->
<div
v-if="props.showStyle === 'tip'"
class="message-plugin-tip"
>
<slot name="messageTip" />
</div>
<!-- The following is displayed in the form of messageBubble -->
<div
v-else-if="props.showStyle === 'bubble'"
class="message-plugin-bubble-content"
@longpress="handleToggleMessageItem($event, messageModel, true)"
@click.prevent.right="handleToggleMessageItem($event, messageModel)"
@touchstart="handleH5LongPress($event, messageModel, 'touchstart')"
@touchend="handleH5LongPress($event, messageModel, 'touchend')"
@mouseover="handleH5LongPress($event, messageModel, 'touchend')"
>
<MessageBubble
:messageItem="messageModel"
:content="messageModel.getMessageContent()"
:blinkMessageIDList="props.blinkMessageIDList"
:classNameList="props.bubbleClassNameList"
@resendMessage="resendMessage(messageModel)"
>
<!-- web message-bubble is a named slot, content area slotName is messageElement -->
<template #messageElement>
<slot
v-if="!isUniFrameWork"
name="messageBubble"
/>
</template>
<!-- uni-app message-bubble is an anonymous slot, no slotName -->
<slot
v-if="isUniFrameWork"
name="messageBubble"
/>
</MessageBubble>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed } from '../../adapter-vue';
import { TUIStore, IMessageModel } from '@tencentcloud/chat-uikit-engine';
import MessageBubble from '../../components/TUIChat/message-list/message-elements/message-bubble.vue';
import { isUniFrameWork } from '../../utils/env';
interface IProps {
message: IMessageModel;
showStyle: string;
bubbleClassNameList?: string[];
blinkMessageIDList?: string[];
}
const props = withDefaults(defineProps<IProps>(), {
message: () => ({} as IMessageModel),
showStyle: '',
bubbleClassNameList: () => [] as string[],
blinkMessageIDList: () => [] as string[],
});
const emits = defineEmits(['resendMessage', 'handleToggleMessageItem', 'handleH5LongPress']);
const messageModel = computed(() => TUIStore.getMessageModel(props.message?.ID));
// The following is for external interaction such as messageTool, no special processing is required, do not change
const resendMessage = (message: IMessageModel) => {
emits('resendMessage', message);
};
const handleToggleMessageItem = (e: any, message: IMessageModel, isLongpress = false) => {
emits('handleToggleMessageItem', e, message, isLongpress);
};
const handleH5LongPress = (e: any, message: IMessageModel, type: string) => {
emits('handleH5LongPress', e, message, type);
};
</script>
<style lang="scss" scoped>
.message-plugin-tip {
color: #999;
font-size: 12px;
overflow-wrap: anywhere;
display: flex;
place-content: center center;
align-items: center;
text-align: center;
margin: 0 10px 10px;
}
.message-tip-highlight {
animation: highlight 1000ms infinite;
@keyframes highlight {
50% {
color: #ff9c19;
}
}
@keyframes highlight {
50% {
color: #ff9c19;
}
}
}
:deep(.message-bubble-room) {
.message-bubble-main-content {
.message-body {
.message-body-main {
.message-body-content {
&.content-in,
&.content-out {
background-color: transparent;
border-radius: 0;
padding: 0;
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,106 @@
<template>
<MessagePluginLayout
:message="props.message"
:showStyle="pluginMessageType.showStyle"
:bubbleClassNameList="[pluginMessageType.pluginType === 'room' ? 'message-bubble-room':'']"
@resendMessage="resendMessage"
@handleToggleMessageItem="handleToggleMessageItem"
@handleH5LongPress="handleH5LongPress"
>
<template #messageTip>
<MessageCallGroup
:message="props.message"
:signalingInfo="messageSignalingInfo"
:customContent="messageCustomContent"
:blinkMessageIDList="props.blinkMessageIDList"
/>
</template>
<template #messageBubble>
<MessageCallC2C
v-if="pluginMessageType.pluginType === 'call'"
:message="props.message"
:signalingInfo="messageSignalingInfo"
:customContent="messageCustomContent"
/>
<MessageCustomerService
v-if="pluginMessageType.pluginType === 'customer'"
:message="props.message"
/>
<MessageRoom
v-if="pluginMessageType.pluginType === 'room'"
:message="props.message"
/>
</template>
</MessagePluginLayout>
</template>
<script lang="ts" setup>
import { computed } from '../../adapter-vue';
import { TUIStore } from '@tencentcloud/chat-uikit-engine';
import TUIChatEngine, { IMessageModel } from '@tencentcloud/chat-uikit-engine';
import {
isCustomerServicePluginMessage,
isCustomServiceMessageInvisible,
} from './message-customer/index';
import { isCallMessage, isRoomCardMessage } from './index';
import MessagePluginLayout from './message-plugin-layout.vue';
import MessageCallGroup from './message-call/message-call-group.vue';
import MessageCallC2C from './message-call/message-call-c2c.vue';
import MessageCustomerService from './message-customer/message-customer-service.vue';
// If TUIRoom is not integrated, please introduce the following path
import MessageRoom from './message-room/message-room-default.vue';
// After integrating TUIRoom, please comment the above path and open the following path to import
// import MessageRoom from './message-room/message-room.vue';
interface IProps {
message: IMessageModel;
blinkMessageIDList?: string[];
}
const props = withDefaults(defineProps<IProps>(), {
message: () => ({} as IMessageModel),
blinkMessageIDList: () => [] as string[],
});
const emits = defineEmits(['resendMessage', 'handleToggleMessageItem', 'handleH5LongPress']);
const messageModel = computed(() => TUIStore.getMessageModel(props.message.ID));
const messageSignalingInfo = computed(() => messageModel?.value?.getSignalingInfo());
const messageCustomContent = computed(() => messageModel?.value?.getMessageContent());
const pluginMessageType = computed<{ pluginType: string; showStyle: string }>(() => {
let typeObj = { pluginType: '', showStyle: '' };
if (isCallMessage(messageModel.value)) {
typeObj = {
pluginType: 'call',
showStyle:
messageModel.value?.conversationType === TUIChatEngine.TYPES.CONV_GROUP ? 'tip' : 'bubble',
};
} else if (isRoomCardMessage(messageModel.value)) {
typeObj = {
pluginType: 'room',
showStyle: 'bubble',
};
} else if (isCustomerServicePluginMessage(messageModel.value)) {
typeObj = {
pluginType: 'customer',
showStyle: isCustomServiceMessageInvisible(messageModel.value) ? '' : 'bubble',
};
}
return typeObj;
});
// The following are for external interaction such as messageTool, no special processing is required, please do not touch
const resendMessage = (message: IMessageModel) => {
emits('resendMessage', message);
};
const handleToggleMessageItem = (e: any, message: IMessageModel, isLongpress = false) => {
emits('handleToggleMessageItem', e, message, isLongpress);
};
const handleH5LongPress = (e: any, message: IMessageModel, type: string) => {
emits('handleH5LongPress', e, message, type);
};
</script>
<style lang="scss" scoped>
/* stylelint-disable-next-line no-empty-source */
</style>

View File

@@ -0,0 +1,42 @@
<template>
<div
:class="[
'room-default',
isUniFrameWork && 'room-default-uni',
props.message.flow === 'in' ? 'room-default-in' : 'room-default-out',
]"
>
{{ TUITranslateService.t("message.custom.自定义消息") }}
</div>
</template>
<script setup lang="ts">
import { TUITranslateService, IMessageModel } from '@tencentcloud/chat-uikit-engine';
import { isUniFrameWork } from '../../../utils/env';
interface IProps {
message: IMessageModel;
}
const props = withDefaults(defineProps<IProps>(), {
message: () => ({} as IMessageModel),
});
</script>
<style lang="scss" scoped>
.room-default {
padding: 12px;
font-size: 14px;
&-uni{
padding: 0;
}
&-in {
background: #fbfbfb;
border-radius: 0 10px;
}
&-out {
background: #dceafd;
border-radius: 10px 0 10px 10px;
}
}
</style>

View File

@@ -0,0 +1,19 @@
<!-- eslint-disable vue/valid-template-root -->
<template>
<RoomMessageCard :message="props.message" />
</template>
<script setup lang="ts">
import { IMessageModel } from '@tencentcloud/chat-uikit-engine';
import { RoomMessageCard } from '@tencentcloud/roomkit-web-vue3';
interface IProps {
message: IMessageModel;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const props = withDefaults(defineProps<IProps>(), {
message: () => ({} as IMessageModel),
});
</script>
<style lang="scss" scoped>
/* stylelint-disable-next-line no-empty-source */
</style>