|
@@ -3,50 +3,51 @@
|
|
|
<!-- 聊天框 -->
|
|
|
<el-scrollbar class="h100" noresize ref="scrollbarRef" max-height="400px" v-if="messageList.length">
|
|
|
<div class="chat-box" ref="chatBoxRef">
|
|
|
- <div v-for="(item, index) in messageList" :key="index" class="chat-item" :class="item.type">
|
|
|
- <div v-if="item.type === 'agent'" class="agent">
|
|
|
+ <div v-for="(item, index) in messageList" :key="index" class="chat-item" :class="item.callSentenceInfo?.role">
|
|
|
+ <div v-if="item.callSentenceInfo?.role === 'agent'" class="agent">
|
|
|
<img v-lazy="getImageUrl('order/service.png')" alt="" class="agent-avatar" src="" />
|
|
|
- <div class="agent-name">{{ item.name }}</div>
|
|
|
- <div class="agent-content">{{ item.content }}</div>
|
|
|
- <div class="agent-date">{{ item.date }}</div>
|
|
|
+ <div class="agent-name">{{ item.calledNumber }}</div>
|
|
|
+ <div class="agent-content">{{ item.callSentenceInfo.text }}</div>
|
|
|
+ <div class="agent-date">{{ formatDate(item.callStartTime, 'YYYY-mm-dd HH:MM:SS') }}</div>
|
|
|
</div>
|
|
|
<div v-else class="user">
|
|
|
<img v-lazy="getImageUrl('order/user.png')" alt="" class="user-avatar" src="" />
|
|
|
- <div class="user-name">{{ item.name }}</div>
|
|
|
- <div class="user-content">{{ item.content }}</div>
|
|
|
- <div class="user-date">{{ item.date }}</div>
|
|
|
+ <div class="user-name">{{ item.callerNumber }}</div>
|
|
|
+ <div class="user-content">{{ item.callSentenceInfo.text }}</div>
|
|
|
+ <div class="user-date">{{ formatDate(item.callStartTime, 'YYYY-mm-dd HH:MM:SS') }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <el-text class="end-call" tag="p" v-if="talkEnd">-- 通话结束 --</el-text>
|
|
|
</div>
|
|
|
+ <!-- 识别内容 通话结束才展示-->
|
|
|
+ <div class="recognize-box" v-if="talkEnd">
|
|
|
+ <transition name="el-zoom-in-bottom">
|
|
|
+ <div v-show="!searchCol" class="transition-box">
|
|
|
+ <el-button link @click="closeSearch" class="transition-box-close">
|
|
|
+ <SvgIcon name="ele-Close" class="ml3" size="18px" />
|
|
|
+ </el-button>
|
|
|
+ <div class="transition-box-title">
|
|
|
+ <el-text tag="b" size="large">识别内容</el-text>
|
|
|
+ </div>
|
|
|
+ <el-scrollbar class="h100 transition-box-content" noresize ref="scrollbarRef" max-height="180px">
|
|
|
|
|
|
- <!-- 识别内容 -->
|
|
|
- <div class="recognize-box" v-if="messageList.length">
|
|
|
- <transition name="el-zoom-in-bottom">
|
|
|
- <div v-show="!searchCol" class="transition-box">
|
|
|
- <el-button link @click="closeSearch" class="transition-box-close">
|
|
|
- <SvgIcon name="ele-Close" class="ml3" size="18px" />
|
|
|
- </el-button>
|
|
|
- <div class="transition-box-title">
|
|
|
- <el-text tag="b" size="large">识别内容</el-text>
|
|
|
- </div>
|
|
|
- <el-scrollbar class="h100 transition-box-content" noresize ref="scrollbarRef" max-height="180px">
|
|
|
- <div class="transition-box-content-item" v-for="(item, index) in recognizeList" :key="index">
|
|
|
- <div class="transition-box-content-item-title">{{ item.title }}</div>
|
|
|
- <div class="transition-box-content-item-content">{{ item.content }}</div>
|
|
|
- </div>
|
|
|
- </el-scrollbar>
|
|
|
- <div class="flex-end mt20">
|
|
|
- <el-button @click="closeSearch" class="default-button"> 取消</el-button>
|
|
|
- <el-button type="primary" @click="fillSingle">一键填单</el-button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </transition>
|
|
|
- <el-button type="primary" @click="closeSearch" v-show="searchCol">
|
|
|
- <SvgIcon name="ele-ArrowUp" class="ml3" size="18px" />
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
+ <pre>
|
|
|
+ {{recognizeList}}
|
|
|
+ </pre>
|
|
|
+ </el-scrollbar>
|
|
|
+ <div class="flex-end mt20">
|
|
|
+ <el-button @click="closeSearch" class="default-button"> 取消</el-button>
|
|
|
+ <el-button type="primary" @click="fillSingle">一键填单</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </transition>
|
|
|
+ <el-button type="primary" @click="closeSearch" v-show="searchCol">
|
|
|
+ <SvgIcon name="ele-ArrowUp" class="ml3" size="18px" />
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
</el-scrollbar>
|
|
|
<Empty v-else />
|
|
|
+
|
|
|
</div>
|
|
|
</template>
|
|
|
<script setup lang="ts">
|
|
@@ -57,47 +58,79 @@ import { getImageUrl } from '/@/utils/tools';
|
|
|
import axios from 'axios';
|
|
|
import { storeToRefs } from 'pinia';
|
|
|
import { useTelStatus } from '/@/stores/telStatus';
|
|
|
+import { useRoute } from 'vue-router';
|
|
|
+import { formatDate } from '/@/utils/formatTime';
|
|
|
+import Empty from '/@/components/Empty/index.vue';
|
|
|
// 消息列表
|
|
|
-const messageList = ref([]); // 消息列表
|
|
|
-const recognizeList = ref([]); // 识别信息
|
|
|
+const messageList = ref<any>([]); // 消息列表
|
|
|
+const recognizeList = ref<any>([]); // 识别信息
|
|
|
|
|
|
const useTelStatusStore = useTelStatus();
|
|
|
const { telStatusInfo } = storeToRefs(useTelStatusStore); // 电话状态
|
|
|
console.log(telStatusInfo.value);
|
|
|
|
|
|
+const socket = ref<any>(null);
|
|
|
// 打开websocket链接
|
|
|
const seatAssistOn = () => {
|
|
|
+ console.log(telStatusInfo.value.telsNo);
|
|
|
if (telStatusInfo.value.telsNo) {
|
|
|
- axios.get(`${import.meta.env.VITE_VOICE_ASSISTANT_API_URL}/rtserver-api/users/getUserByAgentId/${telStatusInfo.value.telsNo}`).then((res) => {
|
|
|
- console.log(res,'获取到的用户信息')
|
|
|
- const { socket, send, on, off } = useSocket(import.meta.env.VITE_VOICE_ASSISTANT_SOCKET_URL);
|
|
|
- on('open', () => {
|
|
|
- console.log('坐席辅助链接已打开');
|
|
|
- send({
|
|
|
- id: '',
|
|
|
- type: 1,
|
|
|
- from: '',
|
|
|
- to: 'sys',
|
|
|
- timestamps: 1,
|
|
|
- body: '',
|
|
|
+ axios
|
|
|
+ .get(`${import.meta.env.VITE_VOICE_ASSISTANT_API_URL}/users/getUserByAgentId/${telStatusInfo.value.telsNo}`)
|
|
|
+ .then((res) => {
|
|
|
+ const { data } = res;
|
|
|
+ const { result } = data;
|
|
|
+ const uid = `8#User-${result.uid}`;
|
|
|
+ const subscribe = `/trans/${result.orgCode}/${result.groupUid}/${uid}`;
|
|
|
+ console.log(result, '获取到的用户信息');
|
|
|
+ socket.value = useSocket(import.meta.env.VITE_VOICE_ASSISTANT_SOCKET_URL, { uid, subscribe });
|
|
|
+ console.log(socket.value);
|
|
|
+ socket.value.on('open', () => {
|
|
|
+ console.log('坐席辅助链接已打开');
|
|
|
+ socket.value.send({
|
|
|
+ // 调用登录
|
|
|
+ id: '',
|
|
|
+ type: 1,
|
|
|
+ from: uid,
|
|
|
+ to: 'sys',
|
|
|
+ timestamps: new Date().getTime(),
|
|
|
+ body: result.userName,
|
|
|
+ });
|
|
|
});
|
|
|
+ socket.value.on('message', wsReceive); // 接收消息
|
|
|
+ socket.value.on('error', onError); // 错误
|
|
|
+ socket.value.on('close', onClose); // 关闭
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ console.log(err, '获取用户信息失败');
|
|
|
});
|
|
|
- on('message', wsReceive); // 接收消息
|
|
|
- on('error', onError); // 错误
|
|
|
- on('close', onClose); // 关闭
|
|
|
- }).catch((err) => {
|
|
|
- console.log(err,'获取用户信息失败');
|
|
|
- });
|
|
|
}
|
|
|
};
|
|
|
// 设置初始化,防止刷新时恢复默认
|
|
|
onMounted(() => {
|
|
|
- seatAssistOn();
|
|
|
+ seatAssistOn();
|
|
|
});
|
|
|
+const route = useRoute();
|
|
|
+const talkEnd = ref(false); // 通话结束
|
|
|
const wsReceive = (message: any) => {
|
|
|
try {
|
|
|
const data = JSON.parse(message.data);
|
|
|
- console.log('坐席辅助收到消息:', data);
|
|
|
+ if (data.body.bisType === 3) {
|
|
|
+ // console.log('坐席辅助收到转写消息:', data);
|
|
|
+ if (route.params.callId === data.body.content.callId) {
|
|
|
+ // 判断是不是当前通话
|
|
|
+ if (data.body.content.callSentenceInfo) {
|
|
|
+ //通话中才显示
|
|
|
+ messageList.value.push(data.body.content);
|
|
|
+ scrollToBottom();
|
|
|
+ }
|
|
|
+ if (data.body.content.callEndInfo) {
|
|
|
+ //通话结束
|
|
|
+ talkEnd.value = true;
|
|
|
+ console.log('通话结束了。')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ console.log(messageList.value, '消息内容');
|
|
|
+ }
|
|
|
} catch (error) {
|
|
|
console.log('坐席辅助收到消息', message);
|
|
|
}
|
|
@@ -123,13 +156,24 @@ const searchCol = ref(true); // 展开/收起
|
|
|
// 展开/收起
|
|
|
const closeSearch = () => {
|
|
|
searchCol.value = !searchCol.value;
|
|
|
- scrollToBottom();
|
|
|
+ if(!searchCol.value){
|
|
|
+ const callId = route.params.callId;
|
|
|
+ axios.get(`${import.meta.env.VITE_VOICE_ASSISTANT_API_URL}/monitor/remote/details/${callId}`).then((res) => {
|
|
|
+ const { data } = res;
|
|
|
+ const { result } = data;
|
|
|
+ recognizeList.value = result.call_detail_content;
|
|
|
+ const str = result.call_detail_content.split('\n');
|
|
|
+
|
|
|
+ console.log(str)
|
|
|
+ console.log(recognizeList.value)
|
|
|
+ });
|
|
|
+ }
|
|
|
};
|
|
|
// 滚动到底部
|
|
|
const scrollToBottom = async () => {
|
|
|
await nextTick(); // 等待 DOM 更新
|
|
|
- const max = chatBoxRef.value!.clientHeight;
|
|
|
- scrollbarRef.value!.setScrollTop(max);
|
|
|
+ const max = chatBoxRef.value?.clientHeight;
|
|
|
+ scrollbarRef.value?.setScrollTop(max);
|
|
|
};
|
|
|
|
|
|
// 一键填单
|
|
@@ -160,8 +204,8 @@ watch(messageList.value, (val) => {
|
|
|
.voice-assistant {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
+ min-height: 400px;
|
|
|
position: relative;
|
|
|
-
|
|
|
.chat-box {
|
|
|
width: 100%;
|
|
|
padding-bottom: 50px;
|
|
@@ -275,6 +319,11 @@ watch(messageList.value, (val) => {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ .end-call {
|
|
|
+ width: 100%;
|
|
|
+ text-align: center;
|
|
|
+ margin-top: 20px;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.recognize-box {
|