基于vue3.0实现的简易对话功能,仿照微信、QQ聊天界面。
HTML代码块
<template>
<el-container style="height: 100%" ref="bodyform">
<div class="el_main_content">
<div class="main_content_header">这是一个对话框</div>
<div class="main_content_center">
<el-scrollbar
class="faultExpertConsultation_scrollbar"
ref="scrollbarRef"
>
<!--对话内容-->
<div
v-for="(item, index) in messagesWithTimestamps"
:key="index"
v-show="messagesWithTimestamps.length > 0"
>
<!--对话时间-->
<div v-if="item.showTime" class="chat_time">
{{ formatSendTime(item.timestamp) }}
</div>
<!--提问-->
<div class="question chat">
<div class="chat_question chat_common">
<span>{{ item.question }}</span>
</div>
<el-avatar class="avatar">
<span class="me">我</span>
</el-avatar>
</div>
<!--回答-->
<div class="answer chat" v-if="item.answer">
<el-avatar :src="robot" />
<div class="chat_answer chat_common">
<span>{{ item.answer }}</span>
</div>
</div>
</div>
</el-scrollbar>
</div>
<div class="main_content_footer">
<div class="input_box">
<textarea class="chat-input no-border" v-model="question" />
</div>
<div class="btn_box">
<el-button type="primary" class="btn" @click="askClick(question)"
>发送</el-button
>
</div>
</div>
</div>
</el-container>
</template>
JavaScript代码块
<script setup>
import { ref, onMounted, nextTick, computed, watch } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
const question = ref(""); //输入框值
const chatList = ref([]); //循环的聊天数组
const scrollbarRef = ref(null);
//创建新的对话数组,加上属性showTime
const messagesWithTimestamps = computed(() => {
return chatList.value.map((item, index) => ({
...item,
showTime: index === 0 || shouldShowTime(index),
}));
});
//计算两次会话时间是否超过3分钟方法
const shouldShowTime = (index) => {
const current = new Date(chatList.value[index - 1].timestamp);
const next = new Date(chatList.value[index].timestamp);
const diff = next ? next - current : 0;
return diff > 3 * 60 * 1000; // 如果间隔超过3分钟返回true
};
//提问方法
const askClick = (val) => {
if (val != "") {
chatList.value.push({
question: val, //问题
answer: "", //回答
timestamp: new Date(), //时间戳
to: "", //接收者
form: "", //发送者
});
} else {
ElMessage("不能发送空白消息");
}
};
//滚动事件到底部事件
const scrollToBottom = () => {
nextTick(() => {
let chat = document.querySelector(".main_content_center");
scrollbarRef.value.wrapRef.scrollTop = chat.scrollHeight;
});
};
watch(
chatList.value,
(newVal, oldVal) => {
scrollToBottom();
},
{ immediate: true }
);
const formatSendTime = (sendTime) => {
const now = new Date();
const sendDate = new Date(sendTime);
// 计算时间差(以毫秒为单位)
const timeDiff = now - sendDate;
const startOfToday = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate()
);
const startOfTargetDate = new Date(
sendDate.getFullYear(),
sendDate.getMonth(),
sendDate.getDate()
);
// 一天内的毫秒数
const oneDay = 24 * 60 * 60 * 1000;
// 如果发送时间在当前时间之前
if (timeDiff < 0) {
return "Invalid time"; // 或者其他错误处理
}
// 发生在同一天
if (startOfToday.getTime() === startOfTargetDate.getTime()) {
return formatTime(sendDate);
}
// 如果发送时间在一天内
if (timeDiff < oneDay) {
return "昨天 " + formatTime(sendDate);
}
// 如果发送时间在二天至七天内
if (timeDiff < 7 * oneDay) {
const weekday = getWeekday(sendDate);
return weekday + " " + formatTime(sendDate);
}
// 如果发送时间超过七天
return (
sendDate.toLocaleDateString("zh-CN", {
year: "numeric",
month: "2-digit",
day: "2-digit",
}) +
" " +
formatTime(sendDate)
);
};
const formatTime = (date) => {
// 格式化时间为“时:分”
const hours = date.getHours().toString().padStart(2, "0");
const minutes = date.getMinutes().toString().padStart(2, "0");
return hours + ":" + minutes;
};
const getWeekday = (date) => {
// 获取星期几的中文表示
const weekdays = [
"星期天",
"星期一",
"星期二",
"星期三",
"星期四",
"星期五",
"星期六",
];
return weekdays[date.getDay()];
};
</script>
CSS代码块
<style>
.no-border {
border: none;
/* 可选的样式,以去除焦点时的边框(如果需要的话) */
outline: none;
width: none;
height: none;
resize: none;
}
</style>
<style lang="less" scoped>
.el_main_content {
width: 50%;
height: 70%;
border-radius: 5px;
border: 1px solid #e4e7ed;
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
margin: auto;
.main_content_header {
width: 100%;
height: 50px;
border-radius: 5px;
background-color: #7de0bd;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
}
.main_content_center {
width: 100%;
position: relative;
height: calc(100% - 170px);
margin: 10px 0px;
.chat_time {
display: flex;
justify-content: center;
font-size: 10px;
}
.question {
justify-content: flex-end;
}
.chat_question {
background-color: #8ce45f;
margin-right: 5px;
color: #ffffff;
}
.chat_answer {
background-color: #f2f3f5;
margin-left: 5px;
}
.chat {
width: 98%;
margin: 10px auto;
display: flex;
}
.chat_common {
max-width: 40%;
padding: 10px;
border-radius: 2px;
word-break: break-all;
display: flex;
align-items: center;
}
.avatar {
background-color: #409eff;
border: 2px solid #409eff;
}
.me {
font-size: 16px;
color: #ffffff;
font-weight: bold;
}
}
.main_content_footer {
width: 100%;
height: 100px;
border-top: 1px solid #e4e7ed;
.input_box {
width: 100%;
height: 60px;
.chat-input {
width: calc(100% - 20px);
padding: 10px;
margin: auto;
}
}
.btn_box {
width: 100%;
height: 40px;
display: flex;
justify-content: flex-end;
align-items: center;
.btn {
margin-right: 10px;
}
}
}
}
</style>