messagesInfo.vue 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. <template>
  2. <view class="messagesInfo">
  3. <view
  4. v-for="(msg, index) in props.messages"
  5. :key="index"
  6. :class="['message-item', msg.speakerType==0? 'is-mine' : '']"
  7. >
  8. <image v-if="msg.speakerType==1"
  9. src="/static/img/service/aijiqiren.png"
  10. class="message-avatar"
  11. mode="aspectFill"
  12. />
  13. <view class="message-content">
  14. <view class="typing-indicator"
  15. v-if="props.isLoading && msg.speakerType==1 && index== props.messages.length-1"
  16. >
  17. <view class="dot"></view>
  18. <view class="dot"></view>
  19. <view class="dot"></view>
  20. </view>
  21. <!-- 0用户消息1AI消息 -->
  22. <view v-if="msg.speakerType==0">
  23. <!-- 0 (文本),1 (图片) -->
  24. <image v-if="msg.chatType==1"
  25. :src="msg.msgContent"
  26. class="content-image"
  27. mode="widthFix"
  28. @load="onImageLoad"
  29. @error="onImageLoad"
  30. />
  31. <text v-else>{{ msg.msgContent }}</text>
  32. </view>
  33. <zero-markdown-view v-else
  34. :markdown="msg.msgContent"
  35. themeColor="#000000"
  36. :aiMode='false'
  37. >
  38. </zero-markdown-view>
  39. <view v-if="msg.messageTime" class="gray font_size20">{{msg.messageTime}}</view>
  40. <view v-else-if="msg.speakerType==1 && msg.messageTimeNow" class="gray font_size20 text_align_right">
  41. {{props.isLoading && index== props.messages.length-1?'':msg.messageTimeNow}}
  42. </view>
  43. </view>
  44. <image v-if="msg.speakerType==0"
  45. :src="appStore?.userInfo?.userAvatar || appStore.logo || '/static/img/service/user-avatar.png'"
  46. class="message-avatar mine-avatar"
  47. mode="aspectFill"
  48. />
  49. </view>
  50. </view>
  51. </template>
  52. <script setup>
  53. import { ref } from 'vue'
  54. import { useAppStore } from "@/stores/app";
  55. const appStore = useAppStore();
  56. const emit = defineEmits(['imageLoaded'])
  57. const props = defineProps({
  58. messages: {
  59. type: Array,
  60. default: [],
  61. },
  62. isLoading: {
  63. type: Boolean,
  64. default: false,
  65. }
  66. });
  67. function onImageLoad() {
  68. emit('imageLoaded')
  69. }
  70. </script>
  71. <style lang="scss" scoped>
  72. .messagesInfo{
  73. .content-image{
  74. width: 200rpx;
  75. }
  76. /* 单个消息样式 */
  77. .message-item {
  78. display: flex;
  79. align-items: flex-start;
  80. margin-bottom: 16rpx;
  81. }
  82. /* 用户自己发送的消息样式 */
  83. .is-mine {
  84. justify-content: flex-end;
  85. }
  86. /* 头像样式 */
  87. .message-avatar {
  88. width: 60rpx;
  89. height: 60rpx;
  90. border-radius: 50%;
  91. margin-right: 16rpx;
  92. &.mine-avatar{
  93. margin-left: 16rpx;
  94. margin-right: 0;
  95. }
  96. }
  97. /* 消息内容样式 */
  98. .message-content {
  99. max-width: 70%;
  100. min-width: 100rpx;
  101. padding: 16rpx;
  102. border-radius: 16rpx;
  103. background-color: #fff;
  104. box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
  105. position: relative;
  106. }
  107. /* 用户自己发送的消息内容样式 */
  108. .is-mine.message-content {
  109. background-color: #00c853;
  110. color: #fff;
  111. }
  112. /* 打字指示器(加载动画) */
  113. .typing-indicator {
  114. // position: absolute;
  115. // top: 20rpx;
  116. // left: 20rpx;
  117. display: flex;
  118. align-items: center;
  119. gap: 8rpx;
  120. padding: 5rpx 0;
  121. }
  122. /* 加载动画关键帧 */
  123. @keyframes typing {
  124. 0% { transform: scale(0); }
  125. 40% { transform: scale(1); }
  126. 80% { transform: scale(0); }
  127. 100% { transform: scale(0); }
  128. }
  129. /* 加载动画的点 */
  130. .dot {
  131. width: 12rpx;
  132. height: 12rpx;
  133. background-color: #666;
  134. border-radius: 50%;
  135. animation: typing 1.4s infinite ease-in-out both;
  136. }
  137. }
  138. </style>