feat(chat): 添加转人工客服功能

This commit is contained in:
yangzhe 2025-12-18 15:23:44 +08:00
parent 909cf26549
commit fc97ec241c
3 changed files with 292 additions and 19 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

@ -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
@ -306,6 +374,8 @@ export default {
isChat: false,
currentConversationId: "", // ID
currentDMid: "", // DMId
activeTransferCardId: "", // ID
isTransferSubmitting: false, // messageGroups
advicePhoneShow: false,
perfectInfoShow: false,
isLoading: false, // loadingMore
@ -566,6 +636,11 @@ export default {
return;
}
if (item.title === "转人工") {
this.handleTransferEntryClick();
return;
}
if (item.title === "电话咨询") {
this.advicePhoneShow = true;
return;
@ -584,6 +659,126 @@ 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;
this.messageGroups = (this.messageGroups || []).filter(
(m) => !(m && m.customType === "transferCard")
);
this.messageGroups.push(this.createLocalUserTextMessage("转人工"));
const card = this.createTransferCardMessage();
this.activeTransferCardId = card.id;
this.messageGroups.push(card);
},
getTransferCardTip(message) {
if (!message) return "";
if (this.isTransferSubmitting) return "正在为您转接人工客服,请稍等...";
if (message.transferTipText) return message.transferTipText;
return "";
},
handleTransferTypeSelect(consultationType) {
const cardId = this.activeTransferCardId;
const findCardIndex = () => {
if (cardId) {
const byId = this.messageGroups.findIndex(
(m) => m && m.id === cardId
);
if (byId >= 0) return byId;
}
return this.messageGroups.findIndex(
(m) => m && m.customType === "transferCard"
);
};
const idx = findCardIndex();
if (idx < 0) return;
const current = this.messageGroups[idx] || {};
if (this.isTransferSubmitting || current.transferStatus === "done") return;
const next = {
...current,
transferStatus: "idle",
selectedConsultationType: consultationType,
transferTipText: "",
};
this.$set(this.messageGroups, idx, next);
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 nextIdx = findCardIndex();
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 = {
...this.messageGroups[nextIdx],
transferStatus: "idle",
transferTipText: serverTip,
};
this.$set(this.messageGroups, nextIdx, updated);
return;
}
const updated = {
...this.messageGroups[nextIdx],
transferStatus: "done",
transferTipText: serverTip,
};
this.$set(this.messageGroups, nextIdx, updated);
});
},
//
sendMessageFn() {
if (!this.messageValue) {
@ -1449,6 +1644,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,7 @@ 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: [],
// 最近联系人/会话列表