This commit is contained in:
JiXinHui 2025-12-19 10:12:06 +08:00
commit 0a3a521a32
6 changed files with 509 additions and 172 deletions

View File

@ -236,18 +236,21 @@ const install = (Vue, vm) => {
// 获取消息接收方用户信息
let GetReceiverUserInfoApi = (params = {}) =>
vm.$u.get("api/Dialogue/GetReceiverUserInfo", params);
// 置顶一个会话
let OverheadOneDialogueApi = (params = {}) =>
vm.$u.post("api/Dialogue/OverheadOneDialogue", params);
// 将会话消息标记为已读
let ReadMessageApi = (params = {}) =>
vm.$u.post("api/Dialogue/ReadMessage", params);
// 置顶一个会话
let OverheadOneDialogueApi = (params = {}) =>
vm.$u.post("api/Dialogue/OverheadOneDialogue", params);
// 将会话消息标记为已读
let ReadMessageApi = (params = {}) =>
vm.$u.post("api/Dialogue/ReadMessage", params);
// 删除会话
let DeleteDialogueApi = (params = {}) =>
vm.$u.post("api/Dialogue/DeleteDialogue", params);
// 转人工服务
let TransferToALiveAgentApi = (params = {}) =>
vm.$u.post("api/Dialogue/TransferToALiveAgent", params);
// 将各个定义的接口名称统一放进对象挂载到vm.$u.api(因为vm就是this也即this.$u.api)下
vm.$u.api = {
vm.$u.api = {
UploadSingleImage,
getTeacherInfo,
getData,
@ -306,16 +309,17 @@ const install = (Vue, vm) => {
UpdateUserApi,
GetTeacherListApi,
GetDialogueListApi,
GetDialogueList_UserApi,
AddDialogueApi,
SendMessage_PrivateApi,
GetChatHistoryDataApi,
GetReceiverUserInfoApi,
OverheadOneDialogueApi,
ReadMessageApi,
DeleteDialogueApi,
};
};
GetDialogueList_UserApi,
AddDialogueApi,
SendMessage_PrivateApi,
GetChatHistoryDataApi,
GetReceiverUserInfoApi,
OverheadOneDialogueApi,
ReadMessageApi,
DeleteDialogueApi,
TransferToALiveAgentApi,
};
};
export default {
install,

View File

@ -374,13 +374,6 @@ export default {
});
},
//
formatTime(date) {
const hours = date.getHours().toString().padStart(2, "0");
const minutes = date.getMinutes().toString().padStart(2, "0");
return `${hours}:${minutes}`;
},
//
isShowTime(index) {
return shouldShowTime(this.vuex_msgList, index);

View File

@ -52,7 +52,7 @@
<view class="date-header">{{ group.id }}</view>
<view v-for="item in group.conversation" :key="item.id">
<uni-swipe-action ref="swipeActionRef">
<uni-swipe-action :ref="'swipe-' + item.id">
<uni-swipe-action-item
:disabled="isBatchDeleteMode"
:threshold="0.3"
@ -123,7 +123,10 @@
</view>
<!-- 批量删除操作栏 -->
<view class="batch-actions" v-if="isBatchDeleteMode && currentHistoryItems.length">
<view
class="batch-actions"
v-if="isBatchDeleteMode && currentHistoryItems.length"
>
<view class="select-all-container" @click="toggleSelectAll">
<view class="checkbox" :class="{ checked: selectAll }">
<text class="checkmark" v-if="selectAll"></text>
@ -164,6 +167,7 @@ export default {
chatHistoryAI: [], // ai
chatHistoryTeacher: [], //
openedItems: {}, //
openedSwipeId: "", // id
};
},
computed: {
@ -207,16 +211,9 @@ export default {
//
closeAllSwipeActions() {
const swipeActionRefs = this.$refs.swipeActionRef || [];
const refs = Array.isArray(swipeActionRefs)
? swipeActionRefs
: [swipeActionRefs];
refs.forEach((ref) => {
if (ref && typeof ref.closeAll === "function") {
ref.closeAll();
}
});
const ids = this.currentHistoryItemIds || [];
ids.forEach((id) => this.closeSwipeActionById(id));
this.openedSwipeId = "";
},
// openedItems closeAllSwipeActions()
@ -225,6 +222,16 @@ export default {
this.closeAllSwipeActions();
},
closeSwipeActionById(itemId) {
if (!itemId) return;
const swipeRef = this.$refs[`swipe-${itemId}`];
const ref = Array.isArray(swipeRef) ? swipeRef[0] : swipeRef;
if (ref && typeof ref.closeAll === "function") {
ref.closeAll();
}
this.$set(this.openedItems, itemId, "none");
},
// ai
async getChatHistoryList() {
this.$u.api.GetConversationPage().then((res) => {
@ -465,6 +472,16 @@ export default {
//
handleSwipeChange(e, itemId) {
this.$set(this.openedItems, itemId, e);
if (e === "right" || e === "left") {
if (this.openedSwipeId && this.openedSwipeId !== itemId) {
this.closeSwipeActionById(this.openedSwipeId);
}
this.openedSwipeId = itemId;
return;
}
if (e === "none" && this.openedSwipeId === itemId) {
this.openedSwipeId = "";
}
},
//

View File

@ -128,7 +128,7 @@
<!-- 消息循环渲染 -->
<block
v-for="(message, index) in messageGroups"
v-for="(message, index) in aiMessageGroups"
:key="'msg-' + index"
>
<!-- 时间 -->
@ -136,11 +136,77 @@
{{ message.displayTime }}
</view>
<!-- 转人工选择卡片独立消息不属于AI回复不展示头像/反馈 -->
<view
class="message-transfer"
v-if="message.customType === 'transferCard'"
:id="'msg-' + message.id"
>
<view class="transfer-card">
<view class="transfer-card-title">
请选择您要咨询的类型以便系统为您转接对应的客服
</view>
<view class="transfer-card-options">
<view
class="transfer-option"
:class="{
selected: message.selectedConsultationType === 1,
disabled:
isTransferSubmitting ||
message.transferStatus === 'done',
}"
@click.stop="handleTransferTypeSelect(1)"
>
<u-icon
name="account"
size="44"
:color="
message.selectedConsultationType === 1
? '#4A6CF7'
: '#C0C4CC'
"
></u-icon>
<text class="transfer-option-text">招生咨询</text>
</view>
<view
class="transfer-option"
:class="{
selected: message.selectedConsultationType === 0,
disabled:
isTransferSubmitting ||
message.transferStatus === 'done',
}"
@click.stop="handleTransferTypeSelect(0)"
>
<u-icon
name="account"
size="44"
:color="
message.selectedConsultationType === 0
? '#4A6CF7'
: '#C0C4CC'
"
></u-icon>
<text class="transfer-option-text">迎新咨询</text>
</view>
</view>
</view>
<view
class="transfer-card-status"
v-if="
isTransferSubmitting ||
!!message.transferTipText
"
>
{{ getTransferCardTip(message) }}
</view>
</view>
<!-- 用户消息 -->
<!-- 0 用户消息 -->
<view
class="message-right"
v-if="message.interactMode === 0"
v-else-if="message.interactMode === 0"
:id="'msg-' + message.id"
>
<view class="message-content">
@ -157,7 +223,9 @@
<!-- 1 Ai回复 3 热门回复 -->
<view
class="message-left"
v-if="message.interactMode === 1 || message.interactMode === 3"
v-else-if="
message.interactMode === 1 || message.interactMode === 3
"
:id="'msg-' + message.id"
>
<image
@ -285,6 +353,11 @@ import AdvicePhone from "@/components/AdvicePhone.vue";
import PerfectInfo from "@/components/PerfectInfo.vue";
import ChatHistory from "@/components/ChatHistory.vue"; //
import HeaderBar from "@/components/HeaderBar.vue"; //
import {
processChatMessageContent,
scrollToBottomByContentHeight,
sortChatMessages,
} from "@/utils/chat.js";
export default {
components: {
@ -301,6 +374,8 @@ export default {
isChat: false,
currentConversationId: "", // ID
currentDMid: "", // DMId
activeTransferCardId: "", // ID
isTransferSubmitting: false, // messageGroups
advicePhoneShow: false,
perfectInfoShow: false,
isLoading: false, // loadingMore
@ -396,32 +471,6 @@ export default {
"我什么时候能推知道自己是否被录取?",
],
scrollToView: "",
// messageGroups
messageGroups: [
// {
// id: "9cac8661-bf09-4b63-ab15-1a0a36b921e0",
// message: "线",
// sendDate: "2025-07-10T15:11:32.886075",
// isSend: true,
// isRead: false,
// interactMode: 0,
// messageType: 0,
// timeLabel: 1,
// displayTime: "15:11",
// },
// {
// id: "02306fc3-c821-4a23-ad66-0bd77d154105",
// message:
// "2025线线2023-2024线\n\n线西www.jxnee.edu.cn0790-6764666/6765666",
// sendDate: "2025-07-10T15:11:32.88644",
// isSend: true,
// isRead: false,
// interactMode: 1,
// messageType: 0,
// timeLabel: 0,
// displayTime: "",
// },
],
scrollTop: 0,
messageValue: "", //
@ -436,11 +485,16 @@ export default {
return "/static/common/images/avatar.png";
},
aiMessageGroups() {
return Array.isArray(this.vuex_aiMessageGroups)
? this.vuex_aiMessageGroups
: [];
},
},
watch: {
//
messageGroups: {
aiMessageGroups: {
handler() {
//
if (!this.isLoadingMore) {
@ -484,7 +538,7 @@ export default {
this.isSwitchingConversation = true;
this.currentConversationId = conversationId;
this.currentDMid = dmid;
this.messageGroups = [];
this.$store.commit("push_AiMsgList", []);
this.pageQuery.PageIndex = 1;
this.isLoadingMore = false;
this.noMoreData = false;
@ -550,28 +604,9 @@ export default {
//
if (this.isLoadingMore) return;
// 使nextTickDOM
this.$nextTick(() => {
// setTimeout(() => {
const query = uni.createSelectorQuery().in(this);
query
.select(".chat-content")
.boundingClientRect((data) => {
if (data) {
console.log("chat-content高度:", data.height);
// scrollTopscrollToView
this.scrollTop = data.height + 200;
// if (this.messageGroups.length > 0) {
// const lastMessage =
// this.messageGroups[this.messageGroups.length - 1];
// this.scrollToView = "msg-" + lastMessage.id;
// console.log("scrollToView:", this.scrollToView);
// }
}
})
.exec();
// }, 300);
scrollToBottomByContentHeight(this, {
selector: ".chat-content",
extraOffset: 200,
});
},
handleFeatureClick(item) {
@ -580,6 +615,11 @@ export default {
return;
}
if (item.title === "转人工") {
this.handleTransferEntryClick();
return;
}
if (item.title === "电话咨询") {
this.advicePhoneShow = true;
return;
@ -598,6 +638,153 @@ export default {
}
},
createLocalUserTextMessage(messageText) {
return {
id: Math.random().toString(36).substring(2, 15),
message: messageText,
sendDate: "",
isSend: true,
isRead: false,
interactMode: 0,
messageType: 0,
timeLabel: 0,
displayTime: "",
};
},
createTransferCardMessage() {
return {
id: "transfer_" + Math.random().toString(36).substring(2, 15),
message: "",
sendDate: "",
isSend: true,
isRead: false,
interactMode: 1,
messageType: 0,
timeLabel: 0,
displayTime: "",
customType: "transferCard",
transferStatus: "idle",
selectedConsultationType: null,
transferTipText: "",
};
},
handleTransferEntryClick() {
if (!this.isChat) {
this.resetChatState();
}
this.isLoadingMore = false;
this.isTransferSubmitting = false;
const nextList = this.aiMessageGroups.filter(
(m) => !(m && m.customType === "transferCard")
);
nextList.push(this.createLocalUserTextMessage("转人工"));
const card = this.createTransferCardMessage();
this.activeTransferCardId = card.id;
nextList.push(card);
this.$store.commit("push_AiMsgList", nextList);
},
getTransferCardTip(message) {
if (!message) return "";
if (this.isTransferSubmitting) return "正在为您转接人工客服,请稍等...";
if (message.transferTipText) return message.transferTipText;
return "";
},
handleTransferTypeSelect(consultationType) {
const cardId = this.activeTransferCardId;
const findCardIndex = (list) => {
if (cardId) {
const byId = list.findIndex(
(m) => m && m.id === cardId
);
if (byId >= 0) return byId;
}
return list.findIndex(
(m) => m && m.customType === "transferCard"
);
};
const list = this.aiMessageGroups;
const idx = findCardIndex(list);
if (idx < 0) return;
const current = list[idx] || {};
if (this.isTransferSubmitting || current.transferStatus === "done") return;
const next = {
...current,
transferStatus: "idle",
selectedConsultationType: consultationType,
transferTipText: "",
};
const nextList = list.slice();
nextList.splice(idx, 1, next);
this.$store.commit("push_AiMsgList", nextList);
this.isTransferSubmitting = true;
const params = {
consultationType,
message: "转人工",
ip: "",
};
if (this.currentDMid) {
params.dialogueManagementId = this.currentDMid;
}
this.$u.api
.TransferToALiveAgentApi(params)
.then((res) => {
this.isTransferSubmitting = false;
const latestList = this.aiMessageGroups;
const nextIdx = findCardIndex(latestList);
if (nextIdx < 0) return;
const succeed = res && res.succeed !== false;
const serverTip =
(res && res.data && res.data.message) ||
(res && res.error) ||
"";
if (!succeed) {
const updated = {
...latestList[nextIdx],
transferStatus: "idle",
transferTipText: serverTip,
};
const updatedList = latestList.slice();
updatedList.splice(nextIdx, 1, updated);
this.$store.commit("push_AiMsgList", updatedList);
return;
}
const updated = {
...latestList[nextIdx],
transferStatus: "done",
transferTipText: serverTip,
};
const updatedList = latestList.slice();
updatedList.splice(nextIdx, 1, updated);
this.$store.commit("push_AiMsgList", updatedList);
})
.catch(() => {
this.isTransferSubmitting = false;
const latestList = this.aiMessageGroups;
const nextIdx = findCardIndex(latestList);
if (nextIdx < 0) return;
const updated = {
...latestList[nextIdx],
transferStatus: "idle",
transferTipText: "转人工失败,请稍后重试",
};
const updatedList = latestList.slice();
updatedList.splice(nextIdx, 1, updated);
this.$store.commit("push_AiMsgList", updatedList);
});
},
//
sendMessageFn() {
if (!this.messageValue) {
@ -624,7 +811,7 @@ export default {
};
//
this.messageGroups.push(userMessage);
this.$store.commit("push_AiMsg", userMessage);
this.messageValue = "";
// AI
@ -642,7 +829,7 @@ export default {
};
//
this.messageGroups.push(loadingMessage);
this.$store.commit("push_AiMsg", loadingMessage);
const params = {
query: sendMessage,
@ -663,7 +850,7 @@ export default {
this.currentDMid = data.dmid;
//
this.messageGroups = this.messageGroups.filter(
const baseList = this.aiMessageGroups.filter(
(msg) => !msg.isLoading
);
@ -683,13 +870,13 @@ export default {
};
//
this.messageGroups.push(aiMessage);
this.$store.commit("push_AiMsgList", baseList.concat([aiMessage]));
})
.catch((error) => {
console.error("API请求失败:", error);
//
this.messageGroups = this.messageGroups.filter(
const baseList = this.aiMessageGroups.filter(
(msg) => !msg.isLoading
);
@ -706,7 +893,10 @@ export default {
displayTime: "",
};
this.messageGroups.push(errorMessage);
this.$store.commit(
"push_AiMsgList",
baseList.concat([errorMessage])
);
});
},
@ -737,7 +927,7 @@ export default {
};
//
this.messageGroups.push(userMessage);
this.$store.commit("push_AiMsg", userMessage);
// AI
const loadingMessage = {
@ -754,7 +944,7 @@ export default {
};
//
this.messageGroups.push(loadingMessage);
this.$store.commit("push_AiMsg", loadingMessage);
const params = {
id: item.id,
@ -772,7 +962,7 @@ export default {
const data = res.data.entityInfo;
//
this.messageGroups = this.messageGroups.filter(
const baseList = this.aiMessageGroups.filter(
(msg) => !msg.isLoading
);
@ -792,11 +982,15 @@ export default {
};
//
this.messageGroups.push(aiMessage);
this.$store.commit(
"push_AiMsgList",
baseList.concat([aiMessage])
);
} else {
//
this.messageGroups = this.messageGroups.filter(
(msg) => !msg.isLoading
this.$store.commit(
"push_AiMsgList",
this.aiMessageGroups.filter((msg) => !msg.isLoading)
);
}
})
@ -804,7 +998,7 @@ export default {
console.error("API请求失败:", error);
//
this.messageGroups = this.messageGroups.filter(
const baseList = this.aiMessageGroups.filter(
(msg) => !msg.isLoading
);
@ -821,17 +1015,13 @@ export default {
displayTime: "",
};
this.messageGroups.push(errorMessage);
this.$store.commit(
"push_AiMsgList",
baseList.concat([errorMessage])
);
});
},
//
formatTime(date) {
const hours = date.getHours().toString().padStart(2, "0");
const minutes = date.getMinutes().toString().padStart(2, "0");
return `${hours}:${minutes}`;
},
//
handleSelectConversation(data) {
//
@ -848,7 +1038,7 @@ export default {
this.isSwitchingConversation = true;
//
this.messageGroups = [];
this.$store.commit("push_AiMsgList", []);
this.pageQuery.PageIndex = 1;
this.isLoadingMore = false;
this.noMoreData = false;
@ -859,52 +1049,12 @@ export default {
// JSON
processMessageContent(message) {
try {
// JSON
const parsed = JSON.parse(message);
//
if (typeof parsed === "object" && parsed !== null) {
let content = "";
//
const { answerTypeLabel, imageUrl, nearbyPaths } = parsed;
//
if (answerTypeLabel) content += `${answerTypeLabel}\n`;
if (imageUrl) content += `${imageUrl}\n`;
if (nearbyPaths) content += `${nearbyPaths}\n`;
//
//
return content ? content.trim() : message;
}
} catch (e) {
// JSON
return message;
}
return message;
return processChatMessageContent(message);
},
// (interactMode=0)
sortMessages(list = []) {
const processedList = (list || []).map((item) => {
//
return {
...item,
message: this.processMessageContent(item.message),
};
});
// console.log("processedList", processedList);
return processedList.sort((a, b) => {
const timeA = new Date(a.sendDate).getTime();
const timeB = new Date(b.sendDate).getTime();
if (timeA === timeB) return a.interactMode - b.interactMode;
return timeA - timeB;
});
return sortChatMessages(list);
},
//
@ -922,7 +1072,10 @@ export default {
const currentList = res.item2 || [];
// sendDate(interactMode=0)
this.messageGroups = this.sortMessages(currentList);
this.$store.commit(
"push_AiMsgList",
this.sortMessages(currentList)
);
})
.finally(() => {
//
@ -957,11 +1110,17 @@ export default {
})
.then((res3) => {
const prevList = res3?.item2 || [];
this.messageGroups = this.sortMessages(prevList);
this.$store.commit(
"push_AiMsgList",
this.sortMessages(prevList)
);
this.pageQuery.PageIndex = prevIndex;
});
} else {
this.messageGroups = this.sortMessages(currentList);
this.$store.commit(
"push_AiMsgList",
this.sortMessages(currentList)
);
}
});
},
@ -1020,23 +1179,12 @@ export default {
}
// sendDate(interactMode=0)
const nextPageMessageGroups = res.item2.sort((a, b) => {
const timeA = new Date(a.sendDate).getTime();
const timeB = new Date(b.sendDate).getTime();
const nextPageMessageGroups = sortChatMessages(res.item2 || []);
// interactMode01
if (timeA === timeB) {
return a.interactMode - b.interactMode;
}
//
return timeA - timeB;
});
this.messageGroups = [
...nextPageMessageGroups,
...this.messageGroups,
];
this.$store.commit(
"prepend_AiMsgList",
nextPageMessageGroups
);
})
.finally(() => {
setTimeout(() => {
@ -1521,6 +1669,79 @@ export default {
text-align: left;
}
}
.message-transfer {
width: 100%;
margin-bottom: 40rpx;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}
.transfer-card {
width: 100%;
background-color: #ffffff;
border-radius: 16rpx;
padding: 40rpx 36rpx;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.06);
}
.transfer-card-title {
padding: 0 16rpx;
font-family: PingFang SC;
font-size: 32rpx;
color: #333333;
line-height: 1.6;
font-weight: 500;
}
.transfer-card-options {
margin-top: 32rpx;
// padding: 24rpx;
border-radius: 16rpx;
// background-color: #f6f8ff;
display: flex;
gap: 36rpx;
}
.transfer-option {
flex: 1;
height: 112rpx;
border-radius: 16rpx;
background-color: #f6f8fa;
display: flex;
align-items: center;
justify-content: center;
gap: 16rpx;
}
.transfer-option.selected {
background-color: #eef0fe;
border-color: #dbe3ff;
}
.transfer-option.disabled {
opacity: 0.7;
}
.transfer-option-text {
font-family: PingFang SC;
font-size: 30rpx;
color: #333333;
font-weight: 500;
}
.transfer-option.selected .transfer-option-text {
color: #4a6cf7;
}
.transfer-card-status {
margin-top: 24rpx;
text-align: center;
font-size: 28rpx;
color: #999;
}
}
.chat-footer {

View File

@ -43,6 +43,8 @@ const store = new Vuex.Store({
vuex_token: lifeData.vuex_token ? lifeData.vuex_token : "",
// 如果vuex_version无需保存到本地永久存储无需lifeData.vuex_version方式
vuex_version: "1.0.0",
// 智能客服消息组列表
vuex_aiMessageGroups: [],
// 当前聊天窗口消息列表(数组
vuex_msgList: [],
// 最近联系人/会话列表
@ -179,6 +181,42 @@ const store = new Vuex.Store({
if (!msg) return;
state.vuex_msgList.unshift(msg);
},
// ===== 智能客服:消息组列表维护(参照 vuex_msgList 维护方式) =====
// 覆盖整个列表
push_AiMsgList(state, list) {
state.vuex_aiMessageGroups = Array.isArray(list) ? list : [];
},
// 追加历史消息到头部(分页加载)
prepend_AiMsgList(state, list) {
const prev = state.vuex_aiMessageGroups || [];
const next = Array.isArray(list) ? list : [];
const merged = [...next, ...prev];
const seen = new Set();
state.vuex_aiMessageGroups = merged.filter((item) => {
const id = item && item.id;
if (!id) return true;
if (seen.has(id)) return false;
seen.add(id);
return true;
});
},
// 推送一条新消息(去重)
push_AiMsg(state, msg) {
if (!msg || !msg.id) return;
const exists = (state.vuex_aiMessageGroups || []).some(
(item) => item && item.id === msg.id
);
if (!exists) {
state.vuex_aiMessageGroups.push(msg);
}
},
// 插入一条消息到头部
unshift_AiMsg(state, msg) {
if (!msg) return;
state.vuex_aiMessageGroups.unshift(msg);
},
// 更新置顶状态(本地)
update_TopState(state, { friendId, isTop }) {
state.vuex_userMsgList = (state.vuex_userMsgList || []).map((item) => {

View File

@ -123,3 +123,67 @@ export function scrollToBottomByContentHeight(
.exec();
});
}
/**
* 处理聊天消息内容若内容为 JSON 字符串且符合预期结构则提取并拼接为可读文本
*
* 说明
* - 首页对话存在部分消息内容为 JSON 字符串的情况如包含 `answerTypeLabel/imageUrl/nearbyPaths`
* - 这里做尽量解析解析失败回退原文的处理避免误伤普通文本消息
*
* @param {string} message - 原始消息文本
* @returns {string} - 处理后的文本
*/
export function processChatMessageContent(message) {
try {
const parsed = JSON.parse(message);
if (typeof parsed === "object" && parsed !== null) {
let content = "";
const { answerTypeLabel, imageUrl, nearbyPaths } = parsed;
if (answerTypeLabel) content += `${answerTypeLabel}\n`;
if (imageUrl) content += `${imageUrl}\n`;
if (nearbyPaths) content += `${nearbyPaths}\n`;
return content ? content.trim() : message;
}
} catch (e) {
return message;
}
return message;
}
/**
* 聊天消息排序 `sendDate` 升序时间相同时用户消息`interactMode=0`排在前面
* 同时会对每条消息的 `message` 字段进行 `processChatMessageContent` 处理
*
* @param {Array<any>} list - 会话详情接口返回的消息列表
* @returns {Array<any>} - 处理并排序后的新数组
*/
export function sortChatMessages(list = []) {
const processedList = (list || []).map((item) => ({
...item,
message: processChatMessageContent(item && item.message),
}));
return processedList.sort((a, b) => {
const timeA = new Date(a && a.sendDate).getTime();
const timeB = new Date(b && b.sendDate).getTime();
if (timeA === timeB) return (a && a.interactMode) - (b && b.interactMode);
return timeA - timeB;
});
}
/**
* Date 格式化为 `HH:mm`
*
* @param {Date} date - 时间对象
* @returns {string} - 格式化字符串
*/
export function formatHHmm(date) {
if (!(date instanceof Date) || Number.isNaN(date.getTime())) return "";
const hours = date.getHours().toString().padStart(2, "0");
const minutes = date.getMinutes().toString().padStart(2, "0");
return `${hours}:${minutes}`;
}