dongyukun
21 小时以前 e295922209fb87c6dcd68ea1560fd16c3e6d808c
src/views/ai/dashboard/components/message/MessageList.vue
@@ -3,9 +3,58 @@
    <div class="chat-list" v-for="(item, index) in list" :key="index">
      <!-- 靠左 message:system、assistant 类型 -->
      <div class="left-message message-item" v-if="item.type !== 'user'">
        <div class="avatar">
          <el-avatar :src="roleAvatar" />
        </div>
        <div class="message">
          <div class="left-text-container" ref="markdownViewRef">
            <MarkdownView class="left-text" :content="item.content" />
          <div>
            <el-text class="time">{{ formatDate(item.createTime) }}</el-text>
          </div>
          <div v-if="item.thinkingFlag" class="left-text-container-thinking" ref="markdownViewRef">
            <MarkdownView v-if="item.thinking" class="left-text thinking" :content="item.thinking" />
            <MarkdownView v-else class="left-text thinking" :content="item.content" />
          </div>
          <div v-else-if="item.thinking" class="left-text-container-thinking" ref="markdownViewRef">
            <MarkdownView class="left-text thinking" :content="item.thinking" />
          </div>
          <div class="left-text-container-conclusion" ref="markdownViewRef">
            <MarkdownView class="left-text" :content="item.conclusion" />
          </div>
          <div class="left-btns">
            <el-button class="btn-cus" link @click="copyContent(item.content)">
              <img class="btn-image" src="@/assets/ai/zhuanlu/copy.png" />
            </el-button>
            <el-button v-if="item.id > 0" class="btn-cus" link @click="onDelete(item.id)">
              <img class="btn-image h-17px" src="@/assets/ai/zhuanlu/delete.png" />
            </el-button>
          </div>
        </div>
      </div>
      <!-- 靠右 message:user 类型 -->
      <div class="right-message message-item" v-if="item.type === 'user'">
        <div class="avatar">
          <el-avatar :src="userAvatar" />
        </div>
        <div class="message">
          <div>
            <el-text class="time">{{ formatDate(item.createTime) }}</el-text>
          </div>
          <div class="right-text-container">
            <div class="right-text">{{ item.content }}</div>
          </div>
          <div class="right-btns">
            <el-button class="btn-cus" link @click="copyContent(item.content)">
              <img class="btn-image" src="@/assets/ai/zhuanlu/copy.png" />
            </el-button>
            <el-button class="btn-cus" link @click="onDelete(item.id)">
              <img class="btn-image h-17px mr-12px" src="@/assets/ai/zhuanlu/delete.png" />
            </el-button>
            <el-button class="btn-cus" link @click="onRefresh(item)">
              <img class="btn-image h-17px mr-12px" src="@/assets/ai/zhuanlu/refresh.png" />
            </el-button>
            <el-button class="btn-cus" link @click="onEdit(item)">
              <img class="btn-image h-17px mr-12px" src="@/assets/ai/zhuanlu/edit.png" />
            </el-button>
          </div>
        </div>
      </div>
@@ -18,12 +67,15 @@
</template>
<script setup lang="ts">
import { PropType } from 'vue'
import { formatDate } from '@/utils/formatTime'
import MarkdownView from '@/components/MarkdownView/index.vue'
import { useClipboard } from '@vueuse/core'
import { ArrowDownBold} from '@element-plus/icons-vue'
import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message'
import { ChatConversationVO } from '@/api/ai/chat/conversation'
import { useUserStore } from '@/store/modules/user'
import userAvatarDefaultImg from '@/assets/ai/zhuanlu/user.png'
import roleAvatarDefaultImg from '@/assets/ai/zhuanlu/assistant.png'
const message = useMessage() // 消息弹窗
const { copy } = useClipboard() // 初始化 copy 到粘贴板
@@ -32,6 +84,10 @@
// 判断“消息列表”滚动的位置(用于判断是否需要滚动到消息最下方)
const messageContainer: any = ref(null)
const isScrolling = ref(false) //用于判断用户是否在滚动
const userAvatar = computed(() => userAvatarDefaultImg)
// const userAvatar = computed(() => userStore.user.avatar || userAvatarDefaultImg)
const roleAvatar = computed(() => props.conversation.roleAvatar ?? roleAvatarDefaultImg)
// 定义 props
const props = defineProps({
@@ -87,7 +143,7 @@
  scrollContainer.scrollTop = 0
}
defineExpose({ scrollToBottom, handlerGoTop }) // 提供方法给 parent 调用
defineExpose({ scrollToBottom, handlerGoTop, handleGoBottom }) // 提供方法给 parent 调用
// ============ 处理消息操作 ==============
@@ -100,7 +156,7 @@
/** 删除 */
const onDelete = async (id) => {
  // 删除 message
  await ChatMessageApi.deleteEnergyChatMessage(id)
  await ChatMessageApi.deleteChatMessage(id)
  message.success('删除成功!')
  // 回调
  emits('onDeleteSuccess')
@@ -143,15 +199,24 @@
    flex-direction: row;
  }
  .right-message {
    display: flex;
    flex-direction: row-reverse;
    justify-content: flex-start;
  }
  .message {
    display: flex;
    flex-direction: column;
    text-align: left;
    height: 462px;
    margin: 0 15px;
    .left-text-container {
      width: 855px;
      height: 462px;
    .time {
      text-align: left;
      line-height: 30px;
    }
    .left-text-container-thinking {
      position: relative;
      display: flex;
      flex-direction: column;
@@ -160,14 +225,55 @@
      border-radius: 4px 4px 4px 4px;
      border-left: 1px solid #73C4FF;
      padding: 10px 10px 5px 10px;
      .left-text {
        font-weight: 400;
        font-size: 14px;
        color: rgba(219,238,255,0.6);
        color: rgba(219,238,255,0.5);
        font-size: 0.85rem;
      }
    }
    .left-text-container-conclusion {
      position: relative;
      display: flex;
      flex-direction: column;
      overflow-wrap: break-word;
      background: rgba(115,196,255,0);
      border-radius: 4px 4px 4px 4px;
      padding: 0 10px 0 0;
      .left-text {
        color: rgba(219,238,255,0.8);
        font-size: 1rem;
      }
    }
    .right-text-container {
      display: flex;
      flex-direction: row-reverse;
      .right-text {
        font-size: 0.95rem;
        color: #DBEEFF;
        display: inline;
        background: rgba(40,139,255,0.1);
        box-shadow: 0 0 0 1px rgba(40,139,255,0.3);
        border-radius: 10px;
        padding: 10px;
        width: auto;
        overflow-wrap: break-word;
        white-space: pre-wrap;
      }
    }
    .left-btns {
      display: flex;
      flex-direction: row;
      margin-top: 8px;
    }
    .right-btns {
      display: flex;
      flex-direction: row-reverse;
      margin-top: 8px;
    }
  }
  // 复制、删除按钮
@@ -187,11 +293,23 @@
  }
}
// 回到底部
.to-bottom {
  position: absolute;
  z-index: 1000;
  bottom: 0;
  right: 50%;
  .el-button {
    background: rgba(255, 255, 255, 0.1);
    border: solid 1px rgba(255, 215, 0, 0.6);
    color: rgba(255, 215, 0, 0.5);
  }
  .el-button:hover {
    cursor: pointer;
    background-color: rgba(255, 255, 255, 0.4);
    border: solid 2px rgba(255, 215, 0);
    color: rgba(255, 215, 0);
  }
}
</style>