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,237 @@
<template>
<!-- Custom message keyword keyword search description, so here only a few custom messages that need to display highlighted description type are parsed -->
<div
:class="['message-abstract-custom']"
@click.capture.stop
>
<template v-if="businessID === CHAT_MSG_CUSTOM_TYPE.SERVICE">
<div :class="['service']">
<h1 :class="['service-header']">
<label :class="['service-header-title']">{{ extensionJSON.title }}</label>
<a
v-if="extensionJSON.hyperlinks_text"
:class="['service-header-link', 'link']"
:href="extensionJSON.hyperlinks_text.value"
target="view_window"
>
{{ extensionJSON.hyperlinks_text.key }}
</a>
</h1>
<ul
v-if="extensionJSON.item && extensionJSON.item.length > 0"
:class="['service-list']"
>
<li
v-for="(item, index) in extensionJSON.item"
:key="index"
:class="['service-list-item']"
>
<a
v-if="isUrl(item.value)"
:class="['service-list-item-link', 'link']"
:href="item.value"
target="view_window"
>{{ item.key }}</a>
<p
v-else
:class="['service-list-item-key']"
>
{{ item.key }}
</p>
</li>
</ul>
<div :class="['service-description', 'description']">
<span
v-for="(contentItem, index) in descriptionForShow"
:key="index"
:class="[(contentItem && contentItem.isHighlight) ? 'highlight' : 'normal']"
>
{{ contentItem.text }}
</span>
</div>
</div>
</template>
<template v-else-if="businessID === CHAT_MSG_CUSTOM_TYPE.EVALUATE">
<div class="evaluate">
<div :class="['evaluate-description', 'description']">
<span
v-for="(contentItem, index) in descriptionForShow"
:key="index"
:class="[(contentItem && contentItem.isHighlight) ? 'highlight' : 'normal']"
>
{{ contentItem.text }}
</span>
</div>
<ul
v-if="extensionJSON.score"
class="evaluate-list"
>
<li
v-for="(item, index) in Math.max(extensionJSON.score, 0)"
:key="index"
class="evaluate-list-item"
>
<Icon
:file="star"
class="file-icon"
/>
</li>
</ul>
<article>{{ extensionJSON.comment }}</article>
</div>
</template>
<template v-else-if="businessID === CHAT_MSG_CUSTOM_TYPE.ORDER">
<div class="order">
<img
class="order-image"
:src="extensionJSON.imageUrl"
alt=""
>
<main class="order-main">
<h1 class="order-main-title">
{{ extensionJSON.title }}
</h1>
<div :class="['order-main-description', 'description']">
<span
v-for="(contentItem, index) in descriptionForShow"
:key="index"
:class="[(contentItem && contentItem.isHighlight) ? 'highlight' : 'normal']"
>
{{ contentItem.text }}
</span>
</div>
<span class="order-main-price">{{ extensionJSON.price }}</span>
</main>
</div>
</template>
<template v-else-if="businessID === CHAT_MSG_CUSTOM_TYPE.LINK">
<div class="text-link">
<div :class="['text-link-description', 'description']">
<p>{{ extensionJSON.text }}</p>
</div>
<a
:class="['link']"
:href="extensionJSON.link"
target="view_window"
>{{
TUITranslateService.t("message.custom.查看详情>>")
}}</a>
</div>
</template>
<template v-else>
<span>{{ defaultMessageContent }}</span>
</template>
</div>
</template>
<script setup lang="ts">
import { TUITranslateService, IMessageModel } from '@tencentcloud/chat-uikit-engine';
import { ref, computed, withDefaults } from '../../../../../adapter-vue';
import { CHAT_MSG_CUSTOM_TYPE } from '../../../../../constant';
import { JSONToObject, isUrl } from '../../../../../utils/index';
import Icon from '../../../../common/Icon.vue';
import star from '../../../../../assets/icon/star-light.png';
import { IHighlightContent } from '../../../type';
import { ISearchResultListItem } from '../../../../../interface';
interface IProps {
contentText: IHighlightContent[];
message: IMessageModel | ISearchResultListItem;
messageContent: Record<string, unknown> | undefined;
}
const props = withDefaults(defineProps<IProps>(), {
contentText: () => ([]) as IHighlightContent[],
message: () => ({}) as IMessageModel,
messageContent: () => ({}) as Record<string, unknown>,
});
const custom = ref<{ data?: string; description?: string; extension?: string }>(
(props?.message as IMessageModel)?.payload,
);
const extensionJSON = computed(() => custom?.value?.data ? JSONToObject(custom.value.data) : custom?.value?.data);
const businessID = computed(() => extensionJSON?.value?.businessID);
const descriptionForShow = ref<Array<{ text: string; isHighlight: boolean }>>(props?.contentText);
const defaultMessageContent = ref<string>(props?.messageContent?.custom as string || '[自定义消息]');
</script>
<style scoped lang="scss">
@import "../../../../../assets/styles/common";
.message-abstract-custom {
.service {
.service-header {
font-size: 14px;
color: #000;
}
.service-list {
.service-list-item {
font-size: 14px;
}
}
}
.evaluate {
.evaluate-list {
padding: 5px 0;
display: flex;
flex-direction: row;
.evaluate-item {
padding: 0 2px;
}
}
}
.order {
display: flex;
.order-main {
padding-left: 5px;
.order-main-title {
font-size: 14px;
color: #000;
}
.order-main-description {
font-family: PingFangSC-Regular, sans-serif;
width: 145px;
line-height: 17px;
font-size: 14px;
color: #999;
letter-spacing: 0;
margin-bottom: 6px;
word-break: break-word;
}
.order-main-price {
font-family: PingFangSC-Regular, sans-serif;
line-height: 25px;
color: #ff7201;
}
}
.order-img {
width: 67px;
height: 67px;
}
}
.link {
font-size: 14px;
color: #679ce1;
}
.description {
font-size: 14px;
color: #000;
.highlight {
background-color: #007aff33;
}
.normal {
font-size: 14px;
color: #000;
}
}
}
</style>

View File

@@ -0,0 +1,152 @@
<template>
<div :class="['message-abstract-file', `message-abstract-file-${displayType}`]">
<div :class="['message-abstract-file-left']">
<img
:class="['message-abstract-file-left-icon']"
:src="typeIcon.iconSrc"
>
</div>
<div :class="['message-abstract-file-main']">
<div :class="['message-abstract-file-main-name']">
<span
v-for="(contentItem, index) in contentText"
:key="index"
:class="[(contentItem && contentItem.isHighlight) ? 'highlight' : 'normal']"
>
{{ contentItem.text }}
</span>
</div>
<div :class="['message-abstract-file-main-size']">
{{ fileSize }}
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, withDefaults } from '../../../../../adapter-vue';
import { IHighlightContent } from '../../../type';
interface IProps {
contentText: Array<IHighlightContent>;
messageContent: Record<string, unknown> | undefined;
displayType: 'bubble' | 'info';
}
const props = withDefaults(defineProps<IProps>(), {
contentText: () => ([]) as Array<IHighlightContent>,
messageContent: () => ({}) as Record<string, unknown>,
displayType: 'bubble',
});
const contentText = ref<Array<{ text: string; isHighlight: boolean }>>(props.contentText);
const typeIcon = computed(() => {
const fileUrl = props?.messageContent?.url as string;
const index = fileUrl?.lastIndexOf('.');
const type = fileUrl?.substring(index + 1);
return handleFileIconForShow(type);
});
const fileSize = computed(() => props?.messageContent?.size);
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',
];
let url = '';
let iconType = '';
fileTypes?.forEach((typeName: string) => {
if (type?.includes(typeName)) {
url = urlBase + typeName + '.svg';
iconType = typeName;
}
});
return {
iconSrc: url ? url : urlBase + 'unknown.svg',
iconType: iconType ? iconType : 'unknown',
};
};
</script>
<style scoped lang="scss">
@import "../../../../../assets/styles/common";
.message-abstract-file {
display: flex;
flex: 1;
overflow: hidden;
flex-direction: row;
justify-content: center;
align-items: center;
&-left {
width: 42px;
height: 32px;
&-icon {
width: 32px;
height: 32px;
margin-right: 10px;
border-radius: 5px;
}
}
&-main {
flex: 1;
overflow: hidden;
&-name {
width: 100%;
color: #000;
font-size: 14px;
height: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.highlight {
background-color: #007aff33;
}
.normal {
color: #000;
}
}
&-size {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #888;
font-size: 12px;
}
}
&-bubble {
background-color: #f1f1f1;
.message-abstract-file-main {
.message-abstract-file-main-name {
color: #1f2329;
.normal {
color: #1f2329;
}
}
}
}
&-file {
margin: 8px 10px 5px;
padding: 10px;
background-color: #f1f1f1;
height: 51px;
}
}
</style>

View File

@@ -0,0 +1,40 @@
<template>
<div :class="['message-abstract-image-container']">
<img
:class="['message-abstract-image']"
:src="imageUrl"
>
</div>
</template>
<script setup lang="ts">
import { withDefaults, computed } from '../../../../../adapter-vue';
import { IImageMessageContent } from '../../../../../interface';
interface IProps {
messageContent: Record<string, unknown> | IImageMessageContent | undefined;
}
const props = withDefaults(defineProps<IProps>(), {
messageContent: () => ({}) as IImageMessageContent,
});
const imageUrl = computed<string>(() => (props.messageContent as IImageMessageContent).url || '');
</script>
<style scoped lang="scss">
@import "../../../../../assets/styles/common";
.message-abstract-image-container {
max-width: 100px;
max-height: 100px;
width: 100px;
height: 100px;
overflow: hidden;
background-color: #fff;
.message-abstract-image {
max-width: 100px;
max-height: 100px;
width: 100px;
height: 100px;
object-fit: contain;
}
}
</style>

View File

@@ -0,0 +1,93 @@
<template>
<div
:class="[
'message-abstract-text',
`message-abstract-text-${highlightType}`,
`message-abstract-text-${displayType}`,
]"
>
<span
v-for="(contentItem, index) in contentText"
:key="index"
:class="[(contentItem && contentItem.isHighlight) ? 'highlight' : 'normal']"
>
{{ transformTextWithKeysToEmojiNames(contentItem.text) }}
</span>
</div>
</template>
<script setup lang="ts">
import { ref, withDefaults } from '../../../../../adapter-vue';
import { transformTextWithKeysToEmojiNames } from '../../../../TUIChat/emoji-config';
import { IHighlightContent } from '../../../type';
interface IProps {
content: IHighlightContent[];
highlightType: 'font' | 'background';
displayType: 'info' | 'bubble';
}
const props = withDefaults(defineProps<IProps>(), {
content: () => ([]) as IHighlightContent[],
highlightType: 'font',
displayType: 'info',
});
const contentText = ref<Array<{ text: string; isHighlight: boolean }>>(props.content);
</script>
<style scoped lang="scss">
@import "../../../../../assets/styles/common";
.message-abstract-text {
justify-content: flex-start;
&-font {
color: #999;
.highlight {
color: #007aff;
}
.normal {
color: #999;
}
}
&-background {
color: #1f2329;
.highlight {
background-color: #007aff33;
}
.normal {
font-size: 14px;
}
}
&-info {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 12px;
.highlight {
font-size: 12px;
}
.normal {
font-size: 12px;
}
}
&-bubble {
font-size: 14px;
.highlight {
font-size: 14px;
}
.normal {
font-size: 14px;
}
}
}
</style>

View File

@@ -0,0 +1,70 @@
<template>
<div :class="['message-abstract-video']">
<div class="message-abstract-video-box">
<img
:src="videoUrl"
:class="['video-snapshot']"
>
<Icon
:file="playIcon"
class="video-play"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from '../../../../../adapter-vue';
import Icon from '../../../../common/Icon.vue';
import playIcon from '../../../../../assets/icon/video-play.png';
import { IVideoMessageContent } from '../../../../../interface';
interface IProps {
messageContent: Record<string, unknown> | IVideoMessageContent | undefined;
}
const props = withDefaults(defineProps<IProps>(), {
messageContent: () => ({}) as IVideoMessageContent,
});
const videoUrl = computed<string>(() => {
return (props.messageContent as IVideoMessageContent).snapshotUrl || (props.messageContent as IVideoMessageContent).url;
});
</script>
<style scoped lang="scss">
@import "../../../../../assets/styles/common";
.message-abstract-video {
max-width: 100px;
max-height: 100px;
width: 100px;
height: 100px;
overflow: hidden;
background-color: #fff;
&-box {
max-width: 100px;
max-height: 100px;
width: 100px;
height: 100px;
overflow: hidden;
background-color: #fff;
position: relative;
.video-snapshot {
max-width: 100px;
max-height: 100px;
width: 100px;
height: 100px;
object-fit: contain;
}
.video-play {
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
z-index: 3;
width: 35px;
height: 35px;
margin: auto;
}
}
}</style>