YingXingAI/pages/chat/index.vue

595 lines
16 KiB
Vue
Raw Normal View History

<template>
<view class="chat-page">
<!-- 顶部导航 -->
<header-bar
:title="vuex_msgUser.receiverName"
leftIcon="arrow-left"
@leftClick="handleLeftClick"
></header-bar>
<!-- 消息列表 -->
<view class="chat-container">
<scroll-view
class="chat-scroll"
scroll-y
:scroll-into-view="scrollToView"
scroll-with-animation
>
<!-- 教师信息 -->
<div class="teacher-info-card">
<image class="teacher-avatar" :src="receiverHeadSculptureUrl"></image>
<div class="teacher-info">
<div class="teacher-name">{{ vuex_msgUser.receiverName }}</div>
<div class="teacher-school">
<image
class="school-icon"
src="/static/common/images/icon_college.png"
></image>
<text class="school-text">{{ vuex_msgUser.collegeName }}</text>
</div>
<div class="teacher-college">
<image
class="college-icon"
src="/static/common/images/icon_major.png"
></image>
<text class="college-text">{{ vuex_msgUser.collegeName }}</text>
</div>
</div>
</div>
<view
v-for="(message, index) in vuex_msgList"
:key="message.id"
:id="'msg-' + message.id"
>
<!-- 时间 -->
<view class="message-time" v-if="isShowTime(index)">
{{ formatShowTime(message.sendDate) }}
</view>
<!-- 0 发送消息 -->
<view
class="message-right"
v-if="message.interactMode === 0"
:id="'msg-' + message.id"
>
<view class="message-content">
<text>{{ message.message }}</text>
</view>
<image
class="user-avatar"
:src="headSculptureUrl"
mode="scaleToFill"
/>
</view>
<!-- 1 收到消息 -->
<view
class="message-left"
v-if="message.interactMode === 1"
:id="'msg-' + message.id"
>
<image
class="ai-avatar"
:src="receiverHeadSculptureUrl"
mode="scaleToFill"
/>
<view class="message-content">
<text>{{ message.message }}</text>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 输入栏 -->
<view class="chat-footer">
<view class="input-area">
<input
v-model="messageValue"
type="text"
class="chat-input"
:focus="true"
placeholder="请输入内容"
placeholder-style="color: #adadad;"
@confirm="handleSend"
/>
<view class="send-btn" @click="handleSend">
<image
class="send-icon"
src="/static/common/images/icon_send.png"
mode="scaleToFill"
/>
</view>
</view>
</view>
</view>
</template>
<script>
import HeaderBar from "@/components/HeaderBar.vue"; // 导入头部组件
import dayjs from "dayjs"; // 导入 dayjs
export default {
name: "ChatDetail",
components: {
HeaderBar, // 注册头部组件
},
data() {
return {
baseUrl: "",
// 头像
myAvatar: "/static/avatar/default-avatar.png",
otherAvatar: "/static/avatar/default-avatar.png",
// 消息列表
messageList: [
{
id: "9cac8661-bf09-4b63-ab15-1a0a36b921e0",
message: "你知道今年的录取分数线吗",
sendDate: "2025-07-10T15:11:32.886075",
isSend: true,
isRead: false,
interactMode: 0,
messageType: 0,
},
{
id: "02306fc3-c821-4a23-ad66-0bd77d154105",
message:
"目前2025年的录取分数线还没有正式公布因为学校需要等招生工作结束后才会公布最终的录取分数线。不过你可以参考2023-2024年的录取分数线作为大致的参考。\n\n如果你想知道具体专业的录取分数线可以告诉我你所在省份和科类历史类或物理类我可以根据往年数据给你一些参考建议。如果需要更准确的信息建议关注江西新能源科技职业学院的官网www.jxnee.edu.cn或者直接联系招生办电话0790-6764666/6765666。",
sendDate: "2025-07-10T15:11:32.88644",
isSend: true,
isRead: false,
interactMode: 1,
messageType: 0,
},
{
id: "9cac8661-bf09-4b63-ab15-1a0a36b91110",
message: "你知道今年的录取分数线吗",
sendDate: "2025-08-10T15:11:32.886075",
isSend: true,
isRead: false,
interactMode: 0,
messageType: 0,
},
{
id: "02306fc3-c821-4a23-ad66-0bd788854105",
message: "回答。",
sendDate: "2025-08-10T15:11:36.88644",
isSend: true,
isRead: false,
interactMode: 1,
messageType: 0,
},
],
// 输入框
messageValue: "",
// 滚动位置
scrollToView: "",
PageIndex: 1,
PageSize: 20,
};
},
onLoad(options) {
console.log(this.vuex_msgList);
this.baseUrl = this.$u.http.config.baseUrl;
// 加载历史消息
this.getMsgList();
// 滚动到底部
this.$nextTick(() => {
this.scrollToBottom();
});
},
computed: {
receiverHeadSculptureUrl() {
if (this.vuex_msgUser.receiverHeadSculptureUrl) {
return this.baseUrl + "/" + this.vuex_msgUser.receiverHeadSculptureUrl;
}
return "/static/common/images/avatar_default.jpg";
},
headSculptureUrl() {
if (this.vuex_user.HeadSculptureUrl) {
return this.baseUrl + "/" + this.vuex_user.HeadSculptureUrl;
}
return "/static/common/images/avatar.png";
},
},
methods: {
// 返回
handleLeftClick() {
uni.navigateBack();
},
// this.vuex_msgUser = {
// dialogueManagementId: "08de36ed-8bc0-4b79-86e7-0128010ccc4b",
// receiverId: "08de33d5-b517-4801-8474-4a4ad5642691",
// receiverName: "华忠林",
// receiverHeadSculptureUrl: "tx.jpg",
// title: "",
// startTime: "2025-12-09T14:38:21.295504",
// lastMessageTime: "0001-01-01T00:00:00",
// isOverhead: false,
// unReadCount: 0,
// };
// 点击发送
handleSend() {
if (!this.messageValue) {
return;
}
// 构建消息对象
const message = {
dialogueManagementId: this.vuex_msgUser.dialogueManagementId,
receiverId: this.vuex_msgUser.receiverId,
message: this.messageValue,
messageType: 0,
filePath: "",
ip: "",
};
this.sendMsgFn(message);
},
// 发送消息
sendMsgFn(message) {
this.$u.api
.SendMessage_PrivateApi(message)
.then((res) => {
console.log(res, "发送消息成功");
if (res.succeed) {
// 添加到消息列表
const msgUserData = {
id: Math.random().toString(36).substring(2),
dialogueManagementId: this.vuex_msgUser.dialogueManagementId,
senderId: this.vuex_user.Id,
receiverId: this.vuex_msgUser.receiverId,
sendDate: new Date().toISOString(),
message: this.messageValue,
messageType: 0,
filePath: "",
interactMode: 0,
};
this.$store.commit("push_Msg", msgUserData);
// 清空输入框
this.messageValue = "";
// 滚动到底部
this.$nextTick(() => {
this.scrollToBottom();
});
}
})
.catch((error) => {
return msg.warning("发送失败");
});
},
// 接收消息
receiveMessage(content) {
const message = {
id: Date.now(),
content: content,
isSelf: false,
timestamp: this.formatTime(new Date()),
};
this.messageList.push(message);
this.$nextTick(() => {
this.scrollToBottom();
});
},
// 加载对话消息
getMsgList() {
this.$u.api
.GetChatHistoryDataApi({
"Item1.DialogueManagementId": this.vuex_msgUser.dialogueManagementId,
PageIndex: this.PageIndex,
PageSize: this.PageSize,
})
.then((res) => {
const msgList = res.data.item1.reverse();
// 注:现在的消息返回值缺少 interactMode 字段先手动mock
for (var i = 0; i < msgList.length; i++) {
msgList[i].interactMode = msgList[i].interactMode || 0;
// if (this.PageIndex != 1) {
// this.$store.commit("unshift_Msg", msgList[i]);
// } else {
// this.$store.commit("push_Msg", msgList[i]);
// }
}
this.$store.commit("push_MsgList", msgList);
});
},
// 滚动到底部
scrollToBottom() {
if (this.vuex_msgList.length > 0) {
const lastMsg = this.vuex_msgList[this.vuex_msgList.length - 1];
this.scrollToView = "msg-" + lastMsg.id;
}
},
// 格式化时间
formatTime(date) {
const hours = date.getHours().toString().padStart(2, "0");
const minutes = date.getMinutes().toString().padStart(2, "0");
return `${hours}:${minutes}`;
},
// 是否显示时间
isShowTime(index) {
if (index == 0) {
return true;
}
let isTime = new Date(this.vuex_msgList[index].sendDate).getTime(); //当前时间
const time = new Date(this.vuex_msgList[index - 1].sendDate).getTime(); //上条消息的时间
// 30 分钟内不显示时间提示
return isTime - time > 30 * 60 * 1000;
},
// 格式化显示时间
formatShowTime(sendDate) {
// 《消息时间为今天发送》 1分钟内显示 ‘刚刚’ 超过一分钟 且小于60分钟 显示 x分钟前 大于等于60分钟显示 ‘今天 hh:mm
// 《消息时间为昨天发送》显示 ‘昨天 hh:mm
// 《消息时间为昨天以前,并且是今年发起》 显示 MM月DD日 hh:mm
// 《消息时间为往年发送》 显示 YYYY年MM月DD日
var isTime = dayjs(); //当前时间
var msgTime = dayjs(sendDate); //消息发送时间
if (isTime.diff(msgTime, "second") < 60) {
return "刚刚";
}
if (isTime.diff(msgTime, "hour") < 1) {
return `${isTime.diff(msgTime, "minute")}分钟前`;
}
// 使用 startOf('day') 获取当天 00:00:00
var today = dayjs().startOf("day");
var msgDay = dayjs(sendDate).startOf("day");
var dayDiff = today.diff(msgDay, "day");
if (dayDiff === 0) {
return `今天 ${msgTime.format("HH:mm")}`;
}
if (dayDiff === 1) {
return `昨天 ${msgTime.format("HH:mm")}`;
}
// 使用 startOf('year') 获取当年第一天
var thisYear = dayjs().startOf("year");
var msgYear = dayjs(sendDate).startOf("year");
var yearDiff = thisYear.diff(msgYear, "year");
if (yearDiff === 0) {
// 今年
return `${msgTime.format("MM月DD日 HH:mm")}`; // 原代码这里有一处是 HH:mm:ss但注释和逻辑看来 HH:mm 更一致,根据需求调整
}
// 往年
return `${msgTime.format("YYYY年MM月DD日 HH:mm")}`;
},
},
};
</script>
<style lang="scss" scoped>
.chat-page {
height: 100vh;
padding-top: 88rpx;
background-image: url("@/static/common/images/images_bg.png");
width: 100%;
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: 0 88rpx;
background-attachment: fixed;
.chat-container {
display: flex;
flex-direction: column;
padding: 0 30rpx;
box-sizing: border-box;
height: calc(100vh - 88rpx - 146rpx);
position: relative;
overflow: hidden;
.chat-scroll {
flex: 1;
height: 100%;
overflow-y: scroll;
// .loading-more {
// text-align: center;
// margin-bottom: 32rpx;
// }
// .no-more-data {
// text-align: center;
// font-size: 24rpx;
// color: #999;
// margin-bottom: 32rpx;
// padding: 10rpx 0;
// }
.teacher-info-card {
background-color: #ffffff;
padding: 30rpx;
border-radius: 16rpx;
margin: 30rpx 0;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
margin-bottom: 36rpx;
.teacher-avatar {
width: 120rpx;
height: 120rpx;
border-radius: 10rpx;
margin-right: 30rpx;
object-fit: cover;
}
.teacher-info {
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 8rpx;
flex: 1;
.teacher-name {
font-family: PingFang SC;
font-weight: bold;
font-size: 32rpx;
color: #333333;
}
.teacher-school,
.teacher-college {
display: flex;
align-items: center;
.school-icon,
.college-icon {
margin-right: 16rpx;
width: 24rpx;
height: 24rpx;
display: inline-block;
text-align: center;
}
.school-text,
.college-text {
font-size: 24rpx;
color: #666666;
}
}
}
}
.message-time {
text-align: center;
font-size: 24rpx;
color: #999999;
padding: 20rpx;
}
.message-left,
.message-right {
display: flex;
margin-bottom: 40rpx;
align-items: flex-start;
}
.message-left {
justify-content: flex-start;
flex-wrap: wrap; /* 允许反馈区换行到消息下方 */
.ai-avatar {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
margin-right: 16rpx;
background-color: #f0f0f0;
flex-shrink: 0;
}
.message-content {
background-color: #ffffff;
max-width: 70%;
padding: 20rpx 24rpx;
border-radius: 0 16rpx 16rpx 16rpx;
font-size: 28rpx;
line-height: 1.5;
}
}
.message-right {
justify-content: flex-end;
.user-avatar {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
margin-left: 16rpx;
background-color: #f0f0f0;
flex-shrink: 0;
}
.message-content {
background-color: #4370fe;
color: #ffffff;
max-width: 70%;
padding: 20rpx 24rpx;
border-radius: 16rpx 0 16rpx 16rpx;
font-size: 28rpx;
line-height: 1.5;
text-align: left;
}
}
}
}
.chat-footer {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 32rpx;
box-sizing: border-box;
background-color: #ffffff;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
.input-area {
display: flex;
align-items: center;
background-color: #f2f4f9;
border-radius: 20rpx;
padding: 16rpx 24rpx;
box-sizing: border-box;
.chat-input {
flex: 1;
height: 40rpx;
font-size: 28rpx;
color: #333;
background-color: transparent;
}
.send-btn {
width: 50rpx;
height: 50rpx;
display: flex;
align-items: center;
justify-content: center;
.send-icon {
width: 40rpx;
height: 46rpx;
}
}
}
}
}
</style>