邹方成 5f0807c5b7 feat(chat): 优化聊天界面功能并添加消息管理
refactor: 重构消息处理和用户交互逻辑

fix(env): 更新环境变量配置

style: 调整用户卡片样式和布局

perf: 优化消息发送和接收性能

test: 添加消息排序和去重逻辑测试

chore: 清理无用代码和文件
2025-10-29 17:49:12 +08:00

441 lines
12 KiB
Vue

<template>
<div v-for="(element, index) in list">
<div class="materia-element" :class="{ robotSend : element.sender_id != sendeInfo.userInfo.user_id }">
<!-- <el-image v-if="element.sender_id == sendeInfo.robotInfo.wx_id" class="tobot-image" :src="sendeInfo.robotInfo.head_url"></el-image>
<template v-else> -->
<el-image v-if="sendeInfo.userInfo.user_id != element.sender_id" class="tobot-image" :src="robot_avatar"></el-image>
<el-image v-if="sendeInfo.userInfo.user_id == element.sender_id" class="tobot-image" :src="sendeInfo.userInfo.user_avatar"></el-image>
<!-- <span class="tobot-image text-logo" v-else> {{ element.sender_name.charAt(0) }} </span> -->
<!-- </template> -->
<div class="materia-info">
<!-- <span class="robot-name">{{ element.sender_name }}</span> -->
<!-- <span v-if="element.sender_id != sendeInfo.userInfo.user_id" class="robot-name">{{ sendeInfo.robotInfo.name }}</span>
<span v-if="element.sender_id == sendeInfo.userInfo.user_id" class="robot-name">{{ sendeInfo.userInfo.nickname }}</span> -->
<div class="text" v-if="element.msg_type == 1">
<pre>{{ element.content.messages }}</pre>
</div>
<div class="image" v-if="element.msg_type == 2">
<el-image :src="element.content.messages" fit="cover"
:preview-src-list="[element.content.messages]" preview-teleported>
<template #placeholder>
<div class="image-slot">Loading<span class="dot">...</span></div>
</template>
</el-image>
</div>
<div class="time">{{ element.send_time }}
<el-icon v-if="element._failed" class="retry" @click.stop="emits('retry', element._id)">
<svg class="icon" aria-hidden="true"><use xlink:href="#icon-jinggao"></use></svg>
</el-icon>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, watch } from 'vue'
import mihoutai from '@/assets/images/mihoutai.png'
import addess_img from '@/assets/images/addess.png'
import user_avatar from '@/assets/images/user_avatar.png'
import robot_avatar from '@/assets/images/robot.png'
const emits = defineEmits(["update:modelValue", "editMateria", "retry"])
const props = defineProps({
msgList: {
type: Array,
default: []
},
sendeInfo: {
type: Object,
default: {}
},
msgType: {
type: String,
default: 'user'
}
})
const keyWordsText = ref(props.keyWords)
const list = ref(props.msgList)
watch(() => props.msgList, (val) => {
console.log('ChatRecord 接收到消息列表:', val.length, '条消息')
list.value = val
})
onMounted(() => {
console.log('ChatRecord 组件挂载,初始消息数:', props.msgList.length)
})
</script>
<style lang="scss" scoped>
.materia-element {
overflow: hidden;
margin-bottom: 20px;
.tobot-image {
float: left;
width: 36px;
height: 36px;
margin-right: 15px;
border-radius: 4px;
}
.text-logo{
background-color: var(--el-color-primary);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
}
.materia-info {
width: 300px;
float: left;
.robot-name {
display: block;
font-size: 12px;
color: var(--el-text-color-secondary);
margin-bottom: 6px;
.edit {
color: var(--el-color-primary);
margin-left: 6px;
transform: translateY(3px);
cursor: pointer;
font-size: 16px;
}
.remove {
color: var(--el-color-danger);
margin-left: 6px;
transform: translateY(3px);
cursor: pointer;
font-size: 16px;
}
}
.text {
display: inline-block;
line-height: 22px;
background-color: #fff;
border-radius: 8px;
padding: 6px 12px;
font-size: 14px;
position: relative;
pre {
white-space: break-spaces;
}
&::before {
position: absolute;
width: 0;
height: 0;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
border-right: 10px solid #fff;
content: '';
top: 10px;
left: -8px;
}
}
.image {
height: auto;
.el-image {
width: 120px;
height: auto;
border-radius: 6px;
}
}
.gif {
.el-image {
width: 120px;
height: auto;
}
}
.audio {
width: 100px;
.el-icon {
font-size: 20px;
transform: translateY(5px);
}
}
.video {
video {
width: 180px;
height: auto;
}
}
.mini {
height: 95px;
width: 220px;
background-color: #fff;
padding: 0;
.card-user {
overflow: hidden;
padding: 10px;
}
.el-image {
height: 50px;
width: 50px;
border-radius: 4px;
vertical-align: middle;
float: left;
}
.name {
font-size: 15px;
margin-left: 5px;
transform: translateY(7px);
display: inline-block;
float: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
p {
height: 20px;
line-height: 22px;
border-top: 1px solid var(--el-border-color);
font-size: 12px;
color: var(--el-text-color-secondary);
padding-left: 10px;
}
}
.chengxu {
height: 103px;
.el-image {
height: 30px;
width: 30px;
margin-right: 5px;
}
.name {
transform: translateY(0px);
color: var(--el-text-color-secondary);
font-size: 12px;
line-height: 30px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.cg-text {
display: block;
padding-left: 10px;
padding-right: 10px;
line-height: 20px;
margin-bottom: 6px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
p {
line-height: 25px;
}
}
.address {
padding: 0;
// overflow: hidden;
width: 220px;
h4 {
line-height: 28px;
font-size: 14px;
margin-bottom: 5px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
position: relative;
z-index: 0;
font-weight: normal;
padding: 0 10px;
margin-top: 5px;
}
p {
font-size: 12px;
color: var(--el-text-color-secondary);
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
position: relative;
z-index: 0;
padding: 0 10px;
}
.el-image {
height: 86px;
width: 100%;
position: relative;
z-index: 0;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
top: 5px;
}
}
.link {
width: 220px;
height: 100px;
.el-image {
height: 46px;
width: 46px;
float: right;
// transform: translateY(-17px);
margin-right: 0;
}
div {
// margin-top: 13px;
height: 50px;
h4 {
font-size: 14px;
line-height: 22px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
p {
color: var(--el-text-color-secondary);
height: 48px;
font-size: 12px;
float: left;
width: calc(100% - 55px);
line-height: normal;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
/* 防止文本换行 */
display: -webkit-box;
-webkit-line-clamp: 3; //显示两行文本
-webkit-box-orient: vertical;
}
}
}
.file {
width: 220px;
height: 75px;
.file-box {
float: left;
width: calc(100% - 50px);
}
.el-image {
width: 46px;
height: 46px;
float: right;
margin-right: 0;
transform: translateY(10px);
}
.el-icon {
float: right;
font-size: 40px;
color: var(--el-color-primary);
transform: translateY(12px);
}
label {
display: block;
line-height: 30px;
transform: translateY(2px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-bottom: 6px;
}
span {
display: inline-block;
font-size: 12px;
color: var(--el-text-color-secondary);
line-height: normal;
transform: translateY(-8px);
}
}
.interval-seconds {
font-size: 12px;
color: var(--el-text-color-secondary);
margin-top: 10px;
}
.time{
font-size: 12px;
color: var(--el-text-color-secondary);
margin-top: 10px;
}
}
}
.robotSend {
.tobot-image {
float: right;
margin-right: 0;
margin-left: 15px;
}
.materia-info {
text-align: right;
float: right;
.robot-name{
text-align: right;
}
.text {
display: inline-block;
text-align: left;
background-color: var(--el-color-primary);
color: #fff;
&::before {
position: absolute;
width: 0;
height: 0;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
border-left: 10px solid var(--el-color-primary);
border-right: 0;
content: '';
top: 10px;
right: -8px;
left: auto;
}
}
}
}
.image-slot {
height: 100%;
background-color: #F0F2F5;
display: flex;
align-items: center;
justify-content: center;
color: var(--el-text-color-secondary);
}
</style>