|
@@ -1,260 +1,144 @@
|
|
|
<template>
|
|
|
<div :class="containerClassNameList">
|
|
|
- <!-- multiple select radio -->
|
|
|
- <RadioSelect
|
|
|
- v-if="props.isMultipleSelectMode"
|
|
|
- class="multiple-select-radio"
|
|
|
- :isSelected="isMultipleSelected"
|
|
|
- @onChange="toggleMultipleSelect"
|
|
|
- />
|
|
|
+ <!-- todo 统一组件处理-->
|
|
|
<div
|
|
|
- :class="{
|
|
|
- 'control-reverse': message.flow === 'out',
|
|
|
- }"
|
|
|
+ class="message-bubble-main-content"
|
|
|
+ :class="[message.flow === 'in' ? '' : 'reverse']"
|
|
|
>
|
|
|
- <!-- message-bubble-container -->
|
|
|
- <div class="message-bubble-content">
|
|
|
+ <Avatar
|
|
|
+ useSkeletonAnimation
|
|
|
+ :url="message.avatar || ''"
|
|
|
+ />
|
|
|
+ <main
|
|
|
+ class="message-body"
|
|
|
+ @click.stop
|
|
|
+ >
|
|
|
<div
|
|
|
- class="message-bubble-main-content"
|
|
|
- :class="[message.flow === 'in' ? '' : 'reverse']"
|
|
|
+ v-if="message.flow === 'in' && message.conversationType === 'GROUP'"
|
|
|
+ class="message-body-nick-name"
|
|
|
>
|
|
|
- <Avatar
|
|
|
- useSkeletonAnimation
|
|
|
- :url="message.avatar || ''"
|
|
|
- :style="{ flex: '0 0 auto' }"
|
|
|
- />
|
|
|
- <main class="message-body" @click.stop>
|
|
|
- <div
|
|
|
- v-if="
|
|
|
- message.flow === 'in' && message.conversationType === 'GROUP'
|
|
|
- "
|
|
|
- class="message-body-nick-name"
|
|
|
- >
|
|
|
- {{ props.content.showName }}
|
|
|
+ {{ props.content.showName }}
|
|
|
+ </div>
|
|
|
+ <div :class="['message-body-main', message.flow === 'out' && 'message-body-main-reverse']">
|
|
|
+ <div
|
|
|
+ :class="[
|
|
|
+ 'blink',
|
|
|
+ 'message-body-content',
|
|
|
+ message.flow === 'out' ? 'content-out' : 'content-in',
|
|
|
+ message.hasRiskContent && 'content-has-risk',
|
|
|
+ isNoPadding ? 'content-no-padding' : '',
|
|
|
+ isNoPadding && isBlink ? 'blink-shadow' : '',
|
|
|
+ !isNoPadding && isBlink ? 'blink-content' : '',
|
|
|
+ ]"
|
|
|
+ >
|
|
|
+ <div class="content-main">
|
|
|
+ <img
|
|
|
+ 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 />
|
|
|
+ </template>
|
|
|
</div>
|
|
|
+ <!-- 敏感信息失败提示 -->
|
|
|
<div
|
|
|
- :class="[
|
|
|
- 'message-body-main',
|
|
|
- message.flow === 'out' && 'message-body-main-reverse',
|
|
|
- ]"
|
|
|
+ v-if="message.hasRiskContent"
|
|
|
+ class="content-has-risk-tips"
|
|
|
>
|
|
|
- <div
|
|
|
- :class="[
|
|
|
- 'blink',
|
|
|
- 'message-body-content',
|
|
|
- messageClass(message),
|
|
|
-
|
|
|
- message.hasRiskContent && 'content-has-risk',
|
|
|
- isNoPadding ? 'content-no-padding' : '',
|
|
|
- isNoPadding && isBlink ? 'blink-shadow' : '',
|
|
|
- !isNoPadding && isBlink ? 'blink-content' : '',
|
|
|
- ]"
|
|
|
- >
|
|
|
- <div class="content-main">
|
|
|
- <img
|
|
|
- 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 />
|
|
|
- </template>
|
|
|
- </div>
|
|
|
- <!-- Risk Content 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" />
|
|
|
- <!-- Fail Icon -->
|
|
|
- <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)
|
|
|
- "
|
|
|
- class="message-label loading-circle"
|
|
|
- :file="loadingIcon"
|
|
|
- :width="'15px'"
|
|
|
- :height="'15px'"
|
|
|
- />
|
|
|
- <!-- Read & Unread -->
|
|
|
- <ReadStatus
|
|
|
- class="message-label align-self-bottom"
|
|
|
- :message="shallowCopyMessage(message)"
|
|
|
- @openReadUserPanel="openReadUserPanel"
|
|
|
- />
|
|
|
+ {{ riskContentText }}
|
|
|
</div>
|
|
|
- </main>
|
|
|
- </div>
|
|
|
- <!-- message extra area -->
|
|
|
- <div class="message-bubble-extra-content">
|
|
|
- <!-- extra: message translation -->
|
|
|
- <MessageTranslate
|
|
|
- :class="message.flow === 'out' ? 'reverse' : 'flex-row'"
|
|
|
- :message="message"
|
|
|
+ </div>
|
|
|
+ <!-- 发送失败 -->
|
|
|
+ <div
|
|
|
+ v-if="message.status === 'fail' || message.hasRiskContent"
|
|
|
+ class="message-label fail"
|
|
|
+ @click="resendMessage()"
|
|
|
+ >
|
|
|
+ !
|
|
|
+ </div>
|
|
|
+ <!-- 加载图标 -->
|
|
|
+ <Icon
|
|
|
+ v-if="message.status === 'unSend' && needLoadingIconMessageType.includes(message.type)"
|
|
|
+ class="message-label loading-circle"
|
|
|
+ :file="loadingIcon"
|
|
|
+ :width="'15px'"
|
|
|
+ :height="'15px'"
|
|
|
/>
|
|
|
- <!-- extra: message convert voice to text -->
|
|
|
- <MessageConvert
|
|
|
- :class="message.flow === 'out' ? 'reverse' : 'flex-row'"
|
|
|
- :message="message"
|
|
|
- />
|
|
|
- <!-- extra: message quote -->
|
|
|
- <MessageQuote
|
|
|
- :class="message.flow === 'out' ? 'reverse' : 'flex-row'"
|
|
|
- :message="message"
|
|
|
- @blinkMessage="blinkMessage"
|
|
|
- @scrollTo="scrollTo"
|
|
|
+ <!-- 已读 & 未读 -->
|
|
|
+ <ReadStatus
|
|
|
+ class="message-label align-self-bottom"
|
|
|
+ :message="shallowCopyMessage(message)"
|
|
|
+ @openReadUserPanel="openReadUserPanel"
|
|
|
/>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </main>
|
|
|
+ </div>
|
|
|
+ <!-- message extra area -->
|
|
|
+ <div class="message-bubble-extra-content">
|
|
|
+ <!-- extra: message translation -->
|
|
|
+ <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"
|
|
|
+ />
|
|
|
+ <!-- extra: message quote -->
|
|
|
+ <MessageQuote
|
|
|
+ :class="message.flow === 'out' ? 'reverse' : 'flex-row'"
|
|
|
+ :message="message"
|
|
|
+ @blinkMessage="blinkMessage"
|
|
|
+ @scrollTo="scrollTo"
|
|
|
+ />
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
-import { computed, toRefs } from "../../../../adapter-vue";
|
|
|
-import TUIChatEngine, {
|
|
|
- TUITranslateService,
|
|
|
- IMessageModel,
|
|
|
-} from "@tencentcloud/chat-uikit-engine";
|
|
|
-import Icon from "../../../common/Icon.vue";
|
|
|
-import ReadStatus from "./read-status/index.vue";
|
|
|
-import MessageQuote from "./message-quote/index.vue";
|
|
|
-import Avatar from "../../../common/Avatar/index.vue";
|
|
|
-import MessageTranslate from "./message-translate/index.vue";
|
|
|
-import MessageConvert from "./message-convert/index.vue";
|
|
|
-import RadioSelect from "../../../common/RadioSelect/index.vue";
|
|
|
-import loadingIcon from "../../../../assets/icon/loading.png";
|
|
|
-import { shallowCopyMessage } from "../../utils/utils";
|
|
|
-import { isPC } from "../../../../utils/env";
|
|
|
-import { watchEffect, ref } from "../../../../adapter-vue";
|
|
|
-import { isUrl, JSONToObject } from "../../../../utils/index";
|
|
|
-import moment from "moment";
|
|
|
+import { computed, toRefs } from '../../../../adapter-vue';
|
|
|
+import TUIChatEngine, { TUITranslateService, IMessageModel } from '@tencentcloud/chat-uikit-engine';
|
|
|
+import Icon from '../../../common/Icon.vue';
|
|
|
+import ReadStatus from './read-status/index.vue';
|
|
|
+import MessageQuote from './message-quote/index.vue';
|
|
|
+import Avatar from '../../../common/Avatar/index.vue';
|
|
|
+import MessageTranslate from './message-translate/index.vue';
|
|
|
+import MessageConvert from './message-convert/index.vue';
|
|
|
+import loadingIcon from '../../../../assets/icon/loading.png';
|
|
|
+import { shallowCopyMessage } from '../../utils/utils';
|
|
|
+import { isPC } from '../../../../utils/env';
|
|
|
+
|
|
|
interface IProps {
|
|
|
messageItem: IMessageModel;
|
|
|
content?: any;
|
|
|
- classNameList?: string[];
|
|
|
blinkMessageIDList?: string[];
|
|
|
- isMultipleSelectMode?: boolean;
|
|
|
- isAudioPlayed?: boolean | undefined;
|
|
|
- multipleSelectedMessageIDList?: string[];
|
|
|
+ classNameList?: string[];
|
|
|
}
|
|
|
|
|
|
interface IEmits {
|
|
|
- (e: "resendMessage"): void;
|
|
|
- (e: "blinkMessage", messageID: string): void;
|
|
|
- (
|
|
|
- e: "setReadReceiptPanelVisible",
|
|
|
- visible: boolean,
|
|
|
- message?: IMessageModel
|
|
|
- ): void;
|
|
|
- (
|
|
|
- e: "changeSelectMessageIDList",
|
|
|
- options: { type: "add" | "remove" | "clearAll"; messageID: string }
|
|
|
- ): void;
|
|
|
- // Only for uni-app
|
|
|
- (e: "scrollTo", scrollHeight: number): void;
|
|
|
+ (e: 'resendMessage'): void;
|
|
|
+ (e: 'blinkMessage', messageID: string): void;
|
|
|
+ (e: 'setReadReceiptPanelVisible', visible: boolean, message?: IMessageModel): void;
|
|
|
+ // 下面的方法是 uniapp 专属
|
|
|
+ (e: 'scrollTo', scrollHeight: number): void;
|
|
|
}
|
|
|
|
|
|
const emits = defineEmits<IEmits>();
|
|
|
-const isCustom = ref();
|
|
|
-watchEffect(() => {
|
|
|
- const { payload } = props.messageItem;
|
|
|
- isCustom.value = payload.data || "";
|
|
|
- isCustom.value = JSONToObject(payload.data);
|
|
|
-});
|
|
|
-function messageClass(message) {
|
|
|
- // 从消息负载数据中提取 businessId
|
|
|
- const businessId =
|
|
|
- message._message &&
|
|
|
- message._message.payload &&
|
|
|
- message._message.payload.data
|
|
|
- ? JSON.parse(message._message.payload.data).businessID
|
|
|
- : "";
|
|
|
- const msgtime =
|
|
|
- message._message &&
|
|
|
- message._message.payload &&
|
|
|
- message._message.payload.data
|
|
|
- ? JSON.parse(message._message.payload.data).expireTime
|
|
|
- : "";
|
|
|
-
|
|
|
- let expireTime = moment(msgtime).format("YYYY-MM-DD");
|
|
|
- let expired = false;
|
|
|
- const now = moment();
|
|
|
- if (expireTime && now.isAfter(expireTime)) {
|
|
|
- expired = true;
|
|
|
- } else if (expireTime) {
|
|
|
- expired = false;
|
|
|
- }
|
|
|
-
|
|
|
- // 判断消息流向是 "out"
|
|
|
- if (message.flow === "out") {
|
|
|
- // 判断 businessId 是否为红包消息相关
|
|
|
- if (businessId === "red_envelope_message") {
|
|
|
- if (expired) {
|
|
|
- return "contentCustom-out-lq";
|
|
|
- } else {
|
|
|
- return "contentCustom-out";
|
|
|
- }
|
|
|
- } else if (businessId === "red_envelope_message_gq") {
|
|
|
- return "contentCustom-out-lq";
|
|
|
- } else {
|
|
|
- return "content-out";
|
|
|
- }
|
|
|
- }
|
|
|
- // 判断消息流向是 "in"
|
|
|
- else if (message.flow === "in") {
|
|
|
- // 判断 businessId 是否为红包消息相关
|
|
|
- if (businessId === "red_envelope_message") {
|
|
|
- if (expired) {
|
|
|
- return "contentCustom-in-lq";
|
|
|
- } else {
|
|
|
- return "contentCustom-in";
|
|
|
- }
|
|
|
- } else if (businessId === "red_envelope_message_gq") {
|
|
|
- return "contentCustom-in-lq";
|
|
|
- } else {
|
|
|
- return "content-in";
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 如果消息流向不是 "in" 或 "out",返回空字符串
|
|
|
- return "";
|
|
|
-}
|
|
|
|
|
|
const props = withDefaults(defineProps<IProps>(), {
|
|
|
- isAudioPlayed: false,
|
|
|
messageItem: () => ({} as IMessageModel),
|
|
|
content: () => ({}),
|
|
|
blinkMessageIDList: () => [],
|
|
|
classNameList: () => [],
|
|
|
- isMultipleSelectMode: false,
|
|
|
- multipleSelectedMessageIDList: () => [],
|
|
|
});
|
|
|
|
|
|
const TYPES = TUIChatEngine.TYPES;
|
|
|
-const riskImageReplaceUrl =
|
|
|
- "https://web.sdk.qcloud.com/component/TUIKit/assets/has_risk_default.png";
|
|
|
+const riskImageReplaceUrl = 'https://web.sdk.qcloud.com/component/TUIKit/assets/has_risk_default.png';
|
|
|
const needLoadingIconMessageType = [
|
|
|
TYPES.MSG_LOCATION,
|
|
|
TYPES.MSG_TEXT,
|
|
@@ -265,42 +149,21 @@ const needLoadingIconMessageType = [
|
|
|
|
|
|
const { blinkMessageIDList, messageItem: message } = toRefs(props);
|
|
|
|
|
|
-const isMultipleSelected = computed<boolean>(() => {
|
|
|
- return props.multipleSelectedMessageIDList.includes(message.value.ID);
|
|
|
-});
|
|
|
-
|
|
|
-const isDisplayUnplayMark = computed<boolean>(() => {
|
|
|
- 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', ...props.classNameList];
|
|
|
});
|
|
|
|
|
|
const isNoPadding = computed(() => {
|
|
|
- return [TYPES.MSG_IMAGE, TYPES.MSG_VIDEO, TYPES.MSG_MERGER].includes(
|
|
|
- message.value.type
|
|
|
- );
|
|
|
+ return [TYPES.MSG_IMAGE, TYPES.MSG_VIDEO].includes(message.value.type);
|
|
|
});
|
|
|
|
|
|
const riskContentText = computed<string>(() => {
|
|
|
- let content = TUITranslateService.t("TUIChat.涉及敏感内容") + ", ";
|
|
|
- if (message.value.flow === "out") {
|
|
|
- content += TUITranslateService.t("TUIChat.发送失败");
|
|
|
+ let content = TUITranslateService.t('TUIChat.涉及敏感内容') + ', ';
|
|
|
+ if (message.value.flow === 'out') {
|
|
|
+ content += TUITranslateService.t('TUIChat.发送失败');
|
|
|
} else {
|
|
|
content += TUITranslateService.t(
|
|
|
- message.value.type === TYPES.MSG_AUDIO
|
|
|
- ? "TUIChat.无法收听"
|
|
|
- : "TUIChat.无法查看"
|
|
|
+ message.value.type === TYPES.MSG_AUDIO ? 'TUIChat.无法收听' : 'TUIChat.无法查看',
|
|
|
);
|
|
|
}
|
|
|
return content;
|
|
@@ -313,82 +176,26 @@ const isBlink = computed(() => {
|
|
|
return false;
|
|
|
});
|
|
|
|
|
|
-function toggleMultipleSelect(isSelected: boolean) {
|
|
|
- emits("changeSelectMessageIDList", {
|
|
|
- type: isSelected ? "add" : "remove",
|
|
|
- messageID: message.value.ID,
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
function resendMessage() {
|
|
|
if (!message.value?.hasRiskContent) {
|
|
|
- emits("resendMessage");
|
|
|
+ emits('resendMessage');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function blinkMessage(messageID: string) {
|
|
|
- emits("blinkMessage", messageID);
|
|
|
+ emits('blinkMessage', messageID);
|
|
|
}
|
|
|
|
|
|
function scrollTo(scrollHeight: number) {
|
|
|
- emits("scrollTo", scrollHeight);
|
|
|
+ emits('scrollTo', scrollHeight);
|
|
|
}
|
|
|
|
|
|
function openReadUserPanel() {
|
|
|
- emits("setReadReceiptPanelVisible", true, message.value);
|
|
|
+ emits('setReadReceiptPanelVisible', true, message.value);
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
-.contentCustom-out{
|
|
|
- height: 100px;
|
|
|
- // background: linear-gradient(225deg, #f74d30 0%, #ff7633 100%);
|
|
|
- background: url("../../../../../static/img/fshb.png") no-repeat;
|
|
|
- background-size: 100% 100%;
|
|
|
- padding: 16px 20px !important;
|
|
|
- display: block !important;
|
|
|
- box-shadow: 0rpx 4rpx 12rpx 0rpx rgba(226, 226, 226, 0.23);
|
|
|
- border-radius: 20px 0px 20px 20px;
|
|
|
-}
|
|
|
-.contentCustom-in {
|
|
|
- height: 100px;
|
|
|
- // background: linear-gradient(225deg, #f74d30 0%, #ff7633 100%);
|
|
|
- background: url("../../../../../static/img/hbbj.png") no-repeat;
|
|
|
- background-size: 100% 100%;
|
|
|
- padding: 16px 20px !important;
|
|
|
- display: block !important;
|
|
|
- box-shadow: 0rpx 4rpx 12rpx 0rpx rgba(226, 226, 226, 0.23);
|
|
|
- border-radius: 0px 20px 20px 20px;
|
|
|
-}
|
|
|
-.contentCustom-in-lq {
|
|
|
- opacity: 0.5;
|
|
|
- height: 100px;
|
|
|
- // background: linear-gradient(225deg, #f74d30 0%, #ff7633 100%);
|
|
|
- background: url("../../../../../static/img/hbbj.png") no-repeat;
|
|
|
- background-size: 100% 100%;
|
|
|
- padding: 16px 20px !important;
|
|
|
- display: block !important;
|
|
|
- box-shadow: 0rpx 4rpx 12rpx 0rpx rgba(226, 226, 226, 0.23);
|
|
|
- border-radius: 0px 20px 20px 20px;
|
|
|
-}
|
|
|
-.contentCustom-out-lq {
|
|
|
- opacity: 0.5;
|
|
|
- height: 100px;
|
|
|
- // background: linear-gradient(225deg, #f74d30 0%, #ff7633 100%);
|
|
|
- background: url("../../../../../static/img/fshb.png") no-repeat;
|
|
|
- background-size: 100% 100%;
|
|
|
- padding: 16px 20px !important;
|
|
|
- display: block !important;
|
|
|
- box-shadow: 0rpx 4rpx 12rpx 0rpx rgba(226, 226, 226, 0.23);
|
|
|
- border-radius: 20px 0px 20px 20px;
|
|
|
-}
|
|
|
-:not(not) {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- min-width: 0;
|
|
|
- box-sizing: border-box;
|
|
|
-}
|
|
|
-
|
|
|
.flex-row {
|
|
|
display: flex;
|
|
|
}
|
|
@@ -400,33 +207,19 @@ function openReadUserPanel() {
|
|
|
}
|
|
|
|
|
|
.message-bubble {
|
|
|
- padding: 10px 15px;
|
|
|
+ width: 100%;
|
|
|
+ box-sizing: border-box;
|
|
|
display: flex;
|
|
|
- flex-direction: row;
|
|
|
+ flex-direction: column;
|
|
|
+ padding: 0 20px 25px;
|
|
|
user-select: none;
|
|
|
- -webkit-touch-callout: none;
|
|
|
- -webkit-user-select: none;
|
|
|
- -khtml-user-select: none;
|
|
|
- -moz-user-select: none;
|
|
|
- -ms-user-select: none;
|
|
|
-
|
|
|
- &.multiple-selected {
|
|
|
- background-color: #f0f0f0;
|
|
|
- }
|
|
|
-
|
|
|
- .multiple-select-radio {
|
|
|
- margin-right: 12px;
|
|
|
- flex: 0 0 auto;
|
|
|
- }
|
|
|
-
|
|
|
- .control-reverse {
|
|
|
- flex: 1 1 auto;
|
|
|
- flex-direction: row-reverse;
|
|
|
- }
|
|
|
-
|
|
|
+ -webkit-touch-callout: none; /* 系统默认菜单被禁用 */
|
|
|
+ -webkit-user-select: none; /* webkit浏览器 */
|
|
|
+ -khtml-user-select: none; /* 早期浏览器 */
|
|
|
+ -moz-user-select: none;/* 火狐 */
|
|
|
+ -ms-user-select: none; /* IE10 */
|
|
|
.message-bubble-main-content {
|
|
|
display: flex;
|
|
|
- flex-direction: row;
|
|
|
|
|
|
.message-avatar {
|
|
|
display: block;
|
|
@@ -442,9 +235,9 @@ function openReadUserPanel() {
|
|
|
flex-direction: column;
|
|
|
align-items: flex-start;
|
|
|
margin: 0 8px;
|
|
|
+ max-width: calc(100% - 54px);
|
|
|
|
|
|
.message-body-nick-name {
|
|
|
- display: block;
|
|
|
margin-bottom: 4px;
|
|
|
font-size: 12px;
|
|
|
color: #999;
|
|
@@ -465,15 +258,6 @@ function openReadUserPanel() {
|
|
|
flex-direction: row-reverse;
|
|
|
}
|
|
|
|
|
|
- .audio-unplay-mark {
|
|
|
- flex: 0 0 auto;
|
|
|
- width: 5px;
|
|
|
- height: 5px;
|
|
|
- border-radius: 50%;
|
|
|
- background-color: #f00;
|
|
|
- margin: 5px;
|
|
|
- }
|
|
|
-
|
|
|
.message-body-content {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
@@ -511,107 +295,99 @@ function openReadUserPanel() {
|
|
|
margin-top: 5px;
|
|
|
border-top: 1px solid #e5c7c7;
|
|
|
padding-top: 5px;
|
|
|
- }
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- .content-in {
|
|
|
- // background: #fbfbfb;
|
|
|
- background: linear-gradient(180deg, #d0fbdd 0%, #ffffff 100%);
|
|
|
- border-radius: 0 10px 10px;
|
|
|
- }
|
|
|
+ .content-in {
|
|
|
+ background: #fbfbfb;
|
|
|
+ border-radius: 0 10px 10px;
|
|
|
+ }
|
|
|
|
|
|
- .content-out {
|
|
|
- background: #00d36d;
|
|
|
- border-radius: 10px 0 10px 10px;
|
|
|
- color: white;
|
|
|
- }
|
|
|
+ .content-out {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 10px 0 10px 10px;
|
|
|
+ }
|
|
|
|
|
|
- .content-no-padding {
|
|
|
- padding: 0;
|
|
|
- background: transparent;
|
|
|
- border-radius: 10px;
|
|
|
- overflow: hidden;
|
|
|
- }
|
|
|
+ .content-no-padding {
|
|
|
+ padding: 0;
|
|
|
+ background: transparent;
|
|
|
+ border-radius: 10px;
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
|
|
|
- .content-no-padding.content-has-risk {
|
|
|
- padding: 12px;
|
|
|
- }
|
|
|
+ .content-no-padding.content-has-risk {
|
|
|
+ padding: 12px;
|
|
|
+ }
|
|
|
|
|
|
- .content-has-risk {
|
|
|
- background: rgba(250, 81, 81, 0.16);
|
|
|
- }
|
|
|
+ .content-has-risk {
|
|
|
+ background: rgba(250, 81, 81, 0.16);
|
|
|
+ }
|
|
|
|
|
|
- .blink-shadow {
|
|
|
- @keyframes shadow-blink {
|
|
|
- 50% {
|
|
|
- box-shadow: rgba(255, 156, 25, 1) 0 0 10px 0;
|
|
|
- }
|
|
|
+ .blink-shadow {
|
|
|
+ @keyframes shadow-blink {
|
|
|
+ 50% {
|
|
|
+ box-shadow: rgba(255, 156, 25, 1) 0 0 10px 0;
|
|
|
}
|
|
|
-
|
|
|
- box-shadow: rgba(255, 156, 25, 0) 0 0 10px 0;
|
|
|
- animation: shadow-blink 1s linear 3;
|
|
|
}
|
|
|
|
|
|
- .blink-content {
|
|
|
- @keyframes reference-blink {
|
|
|
- 50% {
|
|
|
- background-color: #ff9c19;
|
|
|
- }
|
|
|
- }
|
|
|
+ box-shadow: rgba(255, 156, 25, 0) 0 0 10px 0;
|
|
|
+ animation: shadow-blink 1s linear 3;
|
|
|
+ }
|
|
|
|
|
|
- animation: reference-blink 1s linear 3;
|
|
|
+ .blink-content {
|
|
|
+ @keyframes reference-blink {
|
|
|
+ 50% {
|
|
|
+ background-color: #ff9c19;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- .message-label {
|
|
|
- align-self: flex-end;
|
|
|
- font-family: PingFangSC-Regular;
|
|
|
- font-size: 12px;
|
|
|
- color: #b6b8ba;
|
|
|
- word-break: keep-all;
|
|
|
- flex: 0 0 auto;
|
|
|
- margin: 0 8px;
|
|
|
-
|
|
|
- &.fail {
|
|
|
- width: 15px;
|
|
|
- height: 15px;
|
|
|
- border-radius: 15px;
|
|
|
- background: red;
|
|
|
- color: #fff;
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
- cursor: pointer;
|
|
|
- }
|
|
|
+ animation: reference-blink 1s linear 3;
|
|
|
+ }
|
|
|
|
|
|
- &.loading-circle {
|
|
|
- opacity: 0;
|
|
|
- animation: circle-loading 2s linear 1s infinite;
|
|
|
- }
|
|
|
+ .message-label {
|
|
|
+ align-self: flex-end;
|
|
|
+ font-family: PingFangSC-Regular;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #b6b8ba;
|
|
|
+ word-break: keep-all;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ margin: 0 8px;
|
|
|
|
|
|
- @keyframes circle-loading {
|
|
|
- 0% {
|
|
|
- transform: rotate(0);
|
|
|
- opacity: 1;
|
|
|
- }
|
|
|
+ &.fail {
|
|
|
+ width: 15px;
|
|
|
+ height: 15px;
|
|
|
+ border-radius: 15px;
|
|
|
+ background: red;
|
|
|
+ color: #fff;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
|
|
|
- 100% {
|
|
|
- opacity: 1;
|
|
|
- transform: rotate(360deg);
|
|
|
- }
|
|
|
- }
|
|
|
+ &.loading-circle {
|
|
|
+ opacity: 0;
|
|
|
+ animation: circle-loading 2s linear 1s infinite;
|
|
|
+ }
|
|
|
+
|
|
|
+ @keyframes circle-loading {
|
|
|
+ 0% {
|
|
|
+ transform: rotate(0);
|
|
|
+ opacity: 1;
|
|
|
}
|
|
|
|
|
|
- .align-self-bottom {
|
|
|
- align-self: flex-end;
|
|
|
+ 100% {
|
|
|
+ opacity: 1;
|
|
|
+ transform: rotate(360deg);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- .reverse {
|
|
|
- display: flex;
|
|
|
- flex-direction: row-reverse;
|
|
|
- justify-content: flex-start;
|
|
|
+ .align-self-bottom {
|
|
|
+ align-self: flex-end;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.message-bubble-extra-content {
|