|
|
@@ -125,9 +125,9 @@
|
|
125
|
125
|
</template>
|
|
126
|
126
|
<div class="space-y-4">
|
|
127
|
127
|
<!-- 实时语音识别区域 -->
|
|
128
|
|
- <div class="bg-white rounded-lg p-4 shadow-sm">
|
|
|
128
|
+ <h3 class="text-lg font-medium">语音识别</h3>
|
|
|
129
|
+ <div class="bg-white rounded-lg p-4 shadow-sm overflow-y-auto max-h-96 min-h-96">
|
|
129
|
130
|
<div class="flex justify-between items-center mb-4">
|
|
130
|
|
- <h3 class="text-lg font-medium">语音识别</h3>
|
|
131
|
131
|
<!-- <div class="flex items-center space-x-2">
|
|
132
|
132
|
<el-tag :type="voiceStatus === 'active' ? 'success' : 'info'" size="small">
|
|
133
|
133
|
{{ voiceStatus === 'active' ? '识别中' : '未开始' }}
|
|
|
@@ -160,26 +160,41 @@ from-white to-transparent pointer-events-none z-10"></div>
|
|
160
|
160
|
class="absolute left-0 bottom-0 w-full h-16 bg-gradient-to-t from-white to-transparent pointer-events-none">
|
|
161
|
161
|
</div>
|
|
162
|
162
|
</div>
|
|
163
|
|
- <!-- 关键词提示 -->
|
|
164
|
|
- <!-- <div class="space-y-2">
|
|
165
|
|
- <h4 class="font-medium text-sm text-gray-600">关键词提示</h4>
|
|
166
|
|
- <div class="flex flex-wrap gap-2">
|
|
167
|
|
- <el-tag v-for="(keyword, idx) in keywords" :key="idx" :type="keyword.type"
|
|
168
|
|
- effect="plain" size="small">
|
|
169
|
|
- {{ keyword.text }}
|
|
170
|
|
- </el-tag>
|
|
171
|
|
- </div>
|
|
172
|
|
- </div> -->
|
|
173
|
|
- <!-- 建议话术 -->
|
|
174
|
|
- <!-- <div class="mt-4 space-y-2">
|
|
175
|
|
- <h4 class="font-medium text-sm text-gray-600">建议话术</h4>
|
|
176
|
|
- <div class="space-y-2">
|
|
177
|
|
- <div v-for="(suggestion, idx) in suggestions" :key="idx"
|
|
178
|
|
- class="p-2 bg-gray-50 rounded text-sm text-gray-600 cursor-pointer hover:bg-gray-100">
|
|
179
|
|
- {{ suggestion }}
|
|
|
163
|
+
|
|
|
164
|
+ </div>
|
|
|
165
|
+
|
|
|
166
|
+ <!-- 关键词提示 -->
|
|
|
167
|
+ <div class="space-y-2">
|
|
|
168
|
+ <h4 class="font-medium text-sm text-gray-600">关键词提示</h4>
|
|
|
169
|
+ <div class="flex flex-wrap gap-2">
|
|
|
170
|
+ <el-tag v-for="(keyword, idx) in keywords" class="relative transition-transform duration-300 ease-in-out hover:scale-105" :key="idx" :type="keyword.type"
|
|
|
171
|
+ effect="plain" size="small" @click="getKnowledgeBaseList(keyword.text)">
|
|
|
172
|
+ {{ keyword.text }}
|
|
|
173
|
+ <span
|
|
|
174
|
+ class="absolute -top-1 -right-1 bg-blue-500 text-white text-[10px] rounded-full w-4 h-4 flex items-center justify-center">
|
|
|
175
|
+ {{ keyword.count }}
|
|
|
176
|
+ </span>
|
|
|
177
|
+ </el-tag>
|
|
|
178
|
+ </div>
|
|
|
179
|
+ </div>
|
|
|
180
|
+ <!-- 推荐知识 -->
|
|
|
181
|
+ <div class="mt-4 space-y-4">
|
|
|
182
|
+ <h4 class="font-medium text-sm text-gray-600">推荐知识</h4>
|
|
|
183
|
+ <div class="space-y-4">
|
|
|
184
|
+ <div v-for="(item, idx) in recommendedKnowledge" :key="idx"
|
|
|
185
|
+ class="p-4 bg-white rounded-lg shadow-sm hover:shadow-md transition-all cursor-pointer">
|
|
|
186
|
+ <div class="flex items-center justify-between mb-2">
|
|
|
187
|
+ <h5 class="font-medium">{{ item.title }}</h5>
|
|
|
188
|
+ <el-tag size="small" type="default">{{ item.directoryname }}</el-tag>
|
|
|
189
|
+ </div>
|
|
|
190
|
+ <p class="text-sm text-gray-600 line-clamp-2 mb-2">{{
|
|
|
191
|
+ stripHtmlByRegex(item.content) }}</p>
|
|
|
192
|
+ <div class="flex items-center justify-between text-xs text-gray-500">
|
|
|
193
|
+ <span>发布时间:{{ item.createTime }}</span>
|
|
|
194
|
+ <span>阅读:{{ item.reads }}</span>
|
|
180
|
195
|
</div>
|
|
181
|
196
|
</div>
|
|
182
|
|
- </div> -->
|
|
|
197
|
+ </div>
|
|
183
|
198
|
</div>
|
|
184
|
199
|
</div>
|
|
185
|
200
|
</el-tab-pane>
|
|
|
@@ -342,11 +357,11 @@ from-white to-transparent pointer-events-none z-10"></div>
|
|
342
|
357
|
<div class="w-[65%] space-y-6">
|
|
343
|
358
|
<div class="bg-white rounded-lg p-6 shadow-sm">
|
|
344
|
359
|
<div class="flex justify-between items-center mb-6">
|
|
345
|
|
- <h2 class="text-xl font-medium">工单信息</h2>
|
|
346
|
|
-
|
|
347
|
|
- <el-button :loading-icon="Eleme" v-if="showAI && showAsr" class="flex items-center gap-1" type="primary" link @click="aiSubmit"
|
|
348
|
|
- :loading="aiLoading">智能填单</el-button>
|
|
349
|
|
- </div>
|
|
|
360
|
+ <h2 class="text-xl font-medium">工单信息</h2>
|
|
|
361
|
+
|
|
|
362
|
+ <el-button :loading-icon="Eleme" v-if="showAI && showAsr" class="flex items-center gap-1"
|
|
|
363
|
+ type="primary" link @click="aiSubmit" :loading="aiLoading">智能填单</el-button>
|
|
|
364
|
+ </div>
|
|
350
|
365
|
<el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
|
|
351
|
366
|
<el-row :gutter="20">
|
|
352
|
367
|
<el-col :span="12">
|
|
|
@@ -410,6 +425,7 @@ from-white to-transparent pointer-events-none z-10"></div>
|
|
410
|
425
|
<script lang="ts" setup name="CallScreen">
|
|
411
|
426
|
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue';
|
|
412
|
427
|
import moment from 'moment';
|
|
|
428
|
+import { findKeyword } from '@/utils/trie';
|
|
413
|
429
|
import {
|
|
414
|
430
|
UserFilled,
|
|
415
|
431
|
Phone,
|
|
|
@@ -435,14 +451,14 @@ import {
|
|
435
|
451
|
import { hidePhone } from '@/utils/tools';
|
|
436
|
452
|
import { ElMessage } from 'element-plus';
|
|
437
|
453
|
import useSelectStore from '@/store/commonSelect/common';
|
|
|
454
|
+import useKeysStore from'@/store/modules/keys'
|
|
438
|
455
|
import { getPageListData, createPageData } from '@/api/main/system/system';
|
|
439
|
456
|
import { userDecryptToAsterisk } from '@/utils/aes';
|
|
440
|
457
|
import knowledgeList from "@/views/main/knowledgeBase/knowledgeList/cpns/konwlegelist/konwlegelist";
|
|
441
|
458
|
|
|
442
|
|
-import { max,flatten } from 'lodash';
|
|
|
459
|
+import { max,flatten, keys } from 'lodash';
|
|
443
|
460
|
import { getOfffixNuber, getCallSate } from '@/utils/index';
|
|
444
|
|
-
|
|
445
|
|
-
|
|
|
461
|
+import { stripHtmlByRegex } from '@/utils/tools';
|
|
446
|
462
|
|
|
447
|
463
|
const router = useRouter()
|
|
448
|
464
|
let { proxy } = getCurrentInstance()
|
|
|
@@ -828,23 +844,24 @@ const transcripts: any = ref([
|
|
828
|
844
|
// page_content: '你好,中国热线请假。',
|
|
829
|
845
|
])
|
|
830
|
846
|
// 关键词提示
|
|
831
|
|
-const keywords = ref([
|
|
832
|
|
- {
|
|
833
|
|
- text: '产品使用方法',
|
|
834
|
|
- type: 'primary'
|
|
835
|
|
- },
|
|
836
|
|
- {
|
|
837
|
|
- text: '操作流程',
|
|
838
|
|
- type: 'success'
|
|
839
|
|
- },
|
|
|
847
|
+const keywords: any = ref([
|
|
840
|
848
|
{
|
|
841
|
|
- text: '正面反馈',
|
|
842
|
|
- type: 'success'
|
|
|
849
|
+ text: '强迫症',
|
|
|
850
|
+ type: 'primary',
|
|
|
851
|
+ count: 0,
|
|
843
|
852
|
},
|
|
844
|
|
- {
|
|
845
|
|
- text: '满意度高',
|
|
846
|
|
- type: 'success'
|
|
847
|
|
- }
|
|
|
853
|
+ // {
|
|
|
854
|
+ // text: '操作流程',
|
|
|
855
|
+ // type: 'success'
|
|
|
856
|
+ // },
|
|
|
857
|
+ // {
|
|
|
858
|
+ // text: '正面反馈',
|
|
|
859
|
+ // type: 'success'
|
|
|
860
|
+ // },
|
|
|
861
|
+ // {
|
|
|
862
|
+ // text: '满意度高',
|
|
|
863
|
+ // type: 'success'
|
|
|
864
|
+ // }
|
|
848
|
865
|
]);
|
|
849
|
866
|
// 建议话术
|
|
850
|
867
|
const suggestions = ref([
|
|
|
@@ -893,7 +910,7 @@ window.addEventListener('AsrMessageEvent', (msg: any) => {
|
|
893
|
910
|
}
|
|
894
|
911
|
|
|
895
|
912
|
console.log(msgInfo, 'msgInfo');
|
|
896
|
|
-
|
|
|
913
|
+ if (msgInfo.page_content.length > 2) checkKeys(msgInfo.page_content);
|
|
897
|
914
|
transcripts.value.push(msgInfo);
|
|
898
|
915
|
})
|
|
899
|
916
|
|
|
|
@@ -1041,7 +1058,89 @@ async function getSearchDocs (text) {
|
|
1041
|
1058
|
|
|
1042
|
1059
|
}
|
|
1043
|
1060
|
|
|
|
1061
|
+// 推荐知识
|
|
|
1062
|
+const recommendedKnowledge: any = ref([
|
|
|
1063
|
+ // {
|
|
|
1064
|
+ // title: '智能客服系统功能配置指南',
|
|
|
1065
|
+ // content: '本文详细介绍了智能客服系统的核心功能配置方法,包括自动应答规则设置、多渠道接入配置、知识库管理等内容。通过本指南,您可以快速掌握系统的基础配置和高级功能设置。',
|
|
|
1066
|
+ // category: '使用指南',
|
|
|
1067
|
+ // type: 'primary',
|
|
|
1068
|
+ // publishTime: '2024-01-20',
|
|
|
1069
|
+ // reads: 1234
|
|
|
1070
|
+ // },
|
|
|
1071
|
+ // {
|
|
|
1072
|
+ // title: '常见故障诊断与解决方案',
|
|
|
1073
|
+ // content: '汇总了系统使用过程中最常见的技术问题和解决方案,包括登录异常、数据同步失败、性能优化等问题的处理方法。本文档由技术支持团队整理,持续更新维护。',
|
|
|
1074
|
+ // category: '技术支持',
|
|
|
1075
|
+ // type: 'warning',
|
|
|
1076
|
+ // publishTime: '2024-01-18',
|
|
|
1077
|
+ // reads: 856
|
|
|
1078
|
+ // },
|
|
|
1079
|
+ // {
|
|
|
1080
|
+ // title: '客服质量管理最佳实践',
|
|
|
1081
|
+ // content: '本文从客服管理者的角度,详细介绍了如何提升客服团队的服务质量,包括话术规范、服务标准、质检方案等内容,帮助管理者建立高效的客服质量管理体系。',
|
|
|
1082
|
+ // category: '最佳实践',
|
|
|
1083
|
+ // type: 'success',
|
|
|
1084
|
+ // publishTime: '2024-01-15',
|
|
|
1085
|
+ // reads: 678
|
|
|
1086
|
+ // },
|
|
|
1087
|
+ // {
|
|
|
1088
|
+ // title: '数据分析报表解读指南',
|
|
|
1089
|
+ // content: '详细说明了系统各类数据分析报表的含义和使用方法,包括客服效率分析、满意度趋势、问题分类统计等报表的查看和导出方式,帮助您更好地利用数据指导工作。',
|
|
|
1090
|
+ // category: '数据分析',
|
|
|
1091
|
+ // type: 'info',
|
|
|
1092
|
+ // publishTime: '2024-01-12',
|
|
|
1093
|
+ // reads: 945
|
|
|
1094
|
+ // }
|
|
|
1095
|
+]);
|
|
|
1096
|
+
|
|
|
1097
|
+
|
|
|
1098
|
+const keys = useKeysStore().knowledgeKeys;
|
|
|
1099
|
+
|
|
|
1100
|
+console.log(keys, 'keys')
|
|
|
1101
|
+
|
|
|
1102
|
+const getKnowledgeBaseList = (key) => {
|
|
|
1103
|
+ if (key.length < 2) {
|
|
|
1104
|
+ return
|
|
|
1105
|
+ }
|
|
|
1106
|
+ suggestions.value = []
|
|
|
1107
|
+
|
|
|
1108
|
+ getPageListData('/km/doc', {
|
|
|
1109
|
+ keywords: key,
|
|
|
1110
|
+ pageNum: 1,
|
|
|
1111
|
+ pageSize: 5,
|
|
|
1112
|
+ }).then(({ data, total }) => {
|
|
|
1113
|
+ recommendedKnowledge.value = data;
|
|
|
1114
|
+ });
|
|
1044
|
1115
|
|
|
|
1116
|
+}
|
|
|
1117
|
+
|
|
|
1118
|
+const checkKeys = (text: string) => {
|
|
|
1119
|
+ const list = findKeyword(text, keys);
|
|
|
1120
|
+ if (list?.length) {
|
|
|
1121
|
+ setKeys(list)
|
|
|
1122
|
+ }
|
|
|
1123
|
+}
|
|
|
1124
|
+
|
|
|
1125
|
+const setKeys = (keys: Array<string>) => {
|
|
|
1126
|
+ const types = ['primary', 'success', 'warning', 'danger'];
|
|
|
1127
|
+ for (const key of keys) {
|
|
|
1128
|
+ const index = keywords.value.findIndex((item) => item.text === key);
|
|
|
1129
|
+ if (index > -1) {
|
|
|
1130
|
+ keywords.value[index].count++;
|
|
|
1131
|
+ keywords.value[index].type = types[keywords.value[index].count % types.length];
|
|
|
1132
|
+ } else {
|
|
|
1133
|
+ keywords.value.push({
|
|
|
1134
|
+ text: key,
|
|
|
1135
|
+ type: 'primary',
|
|
|
1136
|
+ count: 1,
|
|
|
1137
|
+ });
|
|
|
1138
|
+ }
|
|
|
1139
|
+ }
|
|
|
1140
|
+
|
|
|
1141
|
+ keywords.value.sort((a, b) => b.count - a.count);
|
|
|
1142
|
+
|
|
|
1143
|
+}
|
|
1045
|
1144
|
|
|
1046
|
1145
|
</script>
|
|
1047
|
1146
|
<style scoped>
|