index.vue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. <template>
  2. <div
  3. v-if="translationVisible"
  4. ref="translationWrapperRef"
  5. :class="{
  6. 'message-translation': true,
  7. 'reverse': props.message.flow === 'out',
  8. 'error': hasTranslationError,
  9. }"
  10. >
  11. <TranslationContent
  12. :message="props.message"
  13. :translationContentVisible="translationVisible"
  14. :translationWrapperRef="translationWrapperRef"
  15. :isSingleTranslation="isSingleTranslation"
  16. @toggleErrorStatus="toggleErrorStatus"
  17. />
  18. <div class="copyright">
  19. <Icon
  20. :file="checkIcon"
  21. size="13px"
  22. />
  23. <div class="copyright-text">
  24. {{ TUITranslateService.t('TUIChat.由IM提供翻译支持') }}
  25. </div>
  26. </div>
  27. </div>
  28. </template>
  29. <script lang="ts" setup>
  30. import { ref, onMounted, onUnmounted } from '../../../../../adapter-vue';
  31. import {
  32. TUIStore,
  33. StoreName,
  34. IMessageModel,
  35. TUITranslateService,
  36. } from '@tencentcloud/chat-uikit-engine';
  37. import Icon from '../../../../common/Icon.vue';
  38. import TranslationContent from './translation-content.vue';
  39. import checkIcon from '../../../../../assets/icon/check-sm.svg';
  40. import { ITranslateInfo } from '../../../../../interface';
  41. interface IProps {
  42. message: IMessageModel;
  43. }
  44. const props = withDefaults(defineProps<IProps>(), {
  45. message: () => ({} as IMessageModel),
  46. });
  47. const translationVisible = ref<boolean>(false);
  48. const hasTranslationError = ref<boolean>(false);
  49. const translationWrapperRef = ref<HTMLDivElement>();
  50. let isSingleTranslation = true;
  51. onMounted(() => {
  52. TUIStore.watch(StoreName.CHAT, {
  53. translateTextInfo: onMessageTranslationUpdated,
  54. });
  55. });
  56. onUnmounted(() => {
  57. TUIStore.unwatch(StoreName.CHAT, {
  58. translateTextInfo: onMessageTranslationUpdated,
  59. });
  60. });
  61. function toggleErrorStatus(hasError: boolean) {
  62. hasTranslationError.value = hasError;
  63. }
  64. function onMessageTranslationUpdated(info: Map<string, ITranslateInfo[]>) {
  65. if (info === undefined) return;
  66. isSingleTranslation = false;
  67. const translationInfoList = info.get(props.message.conversationID) || [];
  68. for (let i = 0; i < translationInfoList.length; ++i) {
  69. const { messageID, visible } = translationInfoList[i];
  70. if (messageID === props.message.ID && visible !== undefined) {
  71. if (translationInfoList.length === 1 && visible) {
  72. isSingleTranslation = true;
  73. }
  74. hasTranslationError.value = false;
  75. translationVisible.value = visible;
  76. break;
  77. }
  78. }
  79. }
  80. </script>
  81. <style lang="scss" scoped>
  82. .message-translation {
  83. margin-top: 4px;
  84. margin-left: 44px;
  85. padding: 10px;
  86. background-color: #f2f7ff;
  87. border-radius: 10px;
  88. display: flex;
  89. flex-direction: column !important;
  90. transition: background-color 0.15s ease-out;
  91. &.error {
  92. background-color: #ffdfdf;
  93. }
  94. .copyright {
  95. display: flex;
  96. align-items: center;
  97. margin-top: 10px;
  98. .copyright-text {
  99. margin-left: 2px;
  100. font-size: 12px;
  101. color: #999;
  102. }
  103. }
  104. }
  105. .message-translation.reverse {
  106. margin-right: 44px;
  107. margin-left: auto;
  108. }
  109. </style>