505 lines
13 KiB
Vue
505 lines
13 KiB
Vue
<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
|
||
>
|
||
<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="/static/common/images/avatar_ai.png"
|
||
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: {
|
||
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 = {
|
||
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: "",
|
||
};
|
||
|
||
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;
|
||
// }
|
||
|
||
.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>
|