闪电 1 год назад
Родитель
Сommit
b41a000b7d

+ 104 - 94
src/components/callLogs/audioPlayer.vue

@@ -10,7 +10,7 @@
10 10
     <!-- 音频播放器 -->
11 11
     <div class="bg-gray-50 rounded-lg p-6 mb-6">
12 12
       <div class="flex justify-center mb-4">
13
-        <audio ref="audioPlayer" :src="filePath" @timeupdate="updateTime"></audio>
13
+        <audio ref="audioPlayer" @timeupdate="updateTime"></audio>
14 14
         <el-button class="w-16 h-16 !rounded-full flex items-center justify-center"
15 15
           :type="isPlaying ? 'danger' : 'primary'" @click="togglePlay">
16 16
           <el-icon class="text-4xl">
@@ -42,8 +42,7 @@
42 42
       v-if="transcriptionMessages.length">
43 43
       <h3 class="text-lg font-medium mb-4">语音转写内容</h3>
44 44
       <div class="text-gray-600 leading-relaxed overflow-y-auto flex-grow space-y-2">
45
-        <div v-for="(message, index) in transcriptionMessages" :key="index" @click="jumpToTime(message.startTime)"
46
-          :class="[
45
+        <div v-for="(message, index) in transcriptionMessages" :key="index" :class="[
47 46
             'p-3 transition-colors rounded-lg cursor-pointer group',
48 47
             message.direction === 1 ? 'bg-gradient-to-r from-gray-100 to-gray-200 hover:from-gray-200 hover:to-gray-300' : 'bg-gradient-to-r from-blue-50 to-blue-100 hover:from-blue-100 hover:to-blue-200'
49 48
           ]">
@@ -57,12 +56,15 @@
57 56
                 {{ message.direction === 1 ? '客服' : '客户' }}
58 57
               </div>
59 58
             </div>
60
-            <el-button class="!rounded-button opacity-0 group-hover:opacity-100 transition-opacity" size="small"
61
-              type="text" @click.stop="playSegment(message.startTime, message.endTime)">
59
+            <el-button v-if="currentIndex !== index" class="!rounded-button opacity-0 group-hover:opacity-100 transition-opacity" size="small"
60
+              type="text" @click.stop="playSegment(message.startTime, message.endTime, index)">
62 61
               <el-icon class="text-lg">
63 62
                 <VideoPlay />
64 63
               </el-icon>
65 64
             </el-button>
65
+            <el-icon v-else class="text-lg">
66
+              <VideoPause />
67
+            </el-icon>
66 68
           </div>
67 69
           <div class="text-gray-700">{{ message.page_content }}</div>
68 70
           <!-- <div class="text-xs text-gray-400 mt-1">置信度: {{ message.confidence }}%</div> -->
@@ -78,7 +80,7 @@
78 80
           {{ keyword.text }}
79 81
           <span
80 82
             class="absolute -top-2 -right-2 flex items-center justify-center w-5 h-5 text-xs font-bold text-white bg-red-500 rounded-full">{{
81
-              keyword.count }}</span>
83
+            keyword.count }}</span>
82 84
         </div>
83 85
       </div>
84 86
     </div>
@@ -93,26 +95,39 @@ import { flatten } from 'lodash';
93 95
 
94 96
 import { getPageListData } from '@/api/main/system/system';
95 97
 import { stripHtmlByRegex, formatMilliseconds } from '@/utils/tools';
98
+import { exportFileWav } from '@/utils';
96 99
 
97 100
 const drawerVisible = ref(false);
98 101
 const deleteDialogVisible = ref(false);
99 102
 const isPlaying = ref(false);
100
-const callId = ref(0);
101
-const filePath = ref('');
103
+// const recordId = ref(null);
104
+// const filePath = ref(null);
102 105
 const audioPlayer = ref(null);
103 106
 const props = defineProps({
104
-  callId: {
107
+  recordId: {
105 108
     type: Number,
109
+    default: null,
106 110
   },
107 111
   filePath: {
108 112
     type: String,
113
+    default: null,
109 114
   },
110 115
 });
116
+
111 117
 watch(props, (newValue, oldValue) => {
112 118
   if (newValue) {
113
-    callId.value = props.callId;
114
-    filePath.value = props.filePath;
119
+    console.log(newValue, 'newValue')
120
+    if (newValue.recordId !== oldValue.recordId) {
121
+      getDetail();
122
+    }
123
+    if (newValue.filePath !== oldValue.filePath) {
124
+      loadRemoteAudio()
125
+    }
126
+    
127
+   
115 128
   }
129
+}, {
130
+  deep: true,
116 131
 });
117 132
 
118 133
 const progress = ref(0);
@@ -120,58 +135,49 @@ const currentTime = ref(0);
120 135
 const duration = ref(180); // 示例时长3分钟
121 136
 const transcriptionMessages: any = ref([]);
122 137
 const formatTime = (seconds: number): string => {
138
+  console.log(seconds, 'seconds');
123 139
   const minutes = Math.floor(seconds / 60);
124 140
   const remainingSeconds = Math.floor(seconds % 60);
125 141
   return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
126 142
 };
127
-const togglePlay = () => {
143
+
144
+const timer = ref(null);
145
+
146
+const togglePlay = (isFragment = false) => {
147
+  if (!isFragment) currentIndex.value = 0;
128 148
   isPlaying.value = !isPlaying.value;
129 149
   if (isPlaying.value) {
130
-    pause();
150
+    play();
131 151
     // 模拟播放进度
132
-    const timer = setInterval(() => {
133
-      if (progress.value >= 100) {
134
-        clearInterval(timer);
135
-        isPlaying.value = false;
136
-        progress.value = 0;
137
-        currentTime.value = 0;
152
+    timer.value = setInterval(() => {
153
+      if (progress.value >= 100 || (isFragment && currentTime.value >= endTime.value)) {
154
+        pause();
138 155
         return;
139 156
       }
140
-      progress.value += 1;
157
+
158
+      currentTime.value+= 1;
159
+
160
+      progress.value = (currentTime.value / duration.value) * 100;
141 161
       currentTime.value = (progress.value / 100) * duration.value;
142 162
     }, 1000);
163
+  } else {
164
+    pause();
143 165
   }
144 166
 };
145 167
 const handleProgressChange = (value: number) => {
146
-  currentTime.value = (value / 100) * duration.value;
147
-};
148
-const jumpToTime = (time: number) => {
149
-  progress.value = (time / duration.value) * 100;
150
-  currentTime.value = time;
168
+  // currentTime.value = (value / 100) * duration.value;
151 169
 };
152 170
 
153 171
 const handleDownload = () => {
154 172
   ElMessage.success('录音文件下载中...');
155
-};
156
-const handleDelete = () => {
157
-  deleteDialogVisible.value = true;
158
-};
159
-const confirmDelete = () => {
160
-  deleteDialogVisible.value = false;
161
-  drawerVisible.value = false;
162
-  ElMessage.success('录音已删除');
163
-};
164
-const openDrawer = () => {
165
-  drawerVisible.value = true;
166
-  progress.value = 0;
167
-  currentTime.value = 0;
168
-  isPlaying.value = false;
173
+  exportFileWav(props.filePath);
169 174
 };
170 175
 let keywords = reactive([]);
171 176
 
172 177
 const getDetail = () => {
173
-  callId.value = 526;
174
-  getPageListData(`/call/translate/${callId.value}`).then(({ data }) => {
178
+  // props.recordId = 526;
179
+  if (!props.recordId) return;
180
+  getPageListData(`/call/translate/${props.recordId}`).then(({ data }) => {
175 181
     console.log(data, 'res')
176 182
     if (data.translate) {
177 183
       const translate = JSON.parse(data.translate);
@@ -216,79 +222,87 @@ const endTime = ref(0);
216 222
 // const duration = ref(0);
217 223
 // const isPlaying = ref(false);
218 224
 // const currentTime = ref(0);
225
+const currentIndex = ref(-1);
226
+const playSegment = (sTime: number, eTime: number, index) => {
227
+  currentIndex.value = index;
228
+  if (isPlaying.value) isPlaying.value = false
229
+  if (timer.value) clearInterval(timer.value);
230
+  progress.value = (sTime / 1000 / duration.value) * 100;
231
+  currentTime.value = sTime / 1000;
232
+  audioPlayer.value.currentTime = currentTime.value;
233
+  endTime.value = eTime / 1000;
234
+
235
+  togglePlay(true);
219 236
 
220
-const playSegment = (sTime: number, eTime: number) => {
221
-  progress.value = (sTime / duration.value) * 100;
222
-  currentTime.value = sTime;
223
-  endTime.value = eTime;
224 237
   // isPlaying.value = true;
225
-  playVideoSegment();
238
+  // playVideoSegment();
226 239
 };
227 240
 
228
-// 播放指定区间
229
-const playVideoSegment = () => {
230
-  if (!audioPlayer.src) return;
241
+// // 播放指定区间
242
+// const playVideoSegment = () => {
243
+//   if (!audioPlayer.src) return;
231 244
 
232
-  audioPlayer.currentTime = startTime.value;
233
-  audioPlayer.play();
234
-  isPlaying.value = true;
245
+//   audioPlayer.currentTime = startTime.value;
246
+//   audioPlayer.play();
247
+//   isPlaying.value = true;
235 248
 
236
-  // 模拟播放到指定片段结束
237
-  const timer = setInterval(() => {
238
-    if (currentTime.value >= endTime.value || !isPlaying.value) {
239
-      clearInterval(timer);
240
-      isPlaying.value = false;
241
-      return;
242
-    }
243
-    progress.value = (currentTime.value / duration.value) * 100;
244
-    currentTime.value += 1;
245
-  }, 1000);
246
-
247
-  // 设置区间结束监听
248
-  const checkEnd = () => {
249
-    if (audioPlayer.currentTime >= endTime.value) {
250
-      pause();
251
-      audioPlayer.currentTime = startTime.value; // 回到起点
252
-    } else if (isPlaying.value) {
253
-      requestAnimationFrame(checkEnd);
254
-    }
255
-  };
256
-  requestAnimationFrame(checkEnd);
257
-};
249
+//   // 模拟播放到指定片段结束
250
+//   const timer = setInterval(() => {
251
+//     if (currentTime.value >= endTime.value || !isPlaying.value) {
252
+//       clearInterval(timer);
253
+//       isPlaying.value = false;
254
+//       return;
255
+//     }
256
+//     progress.value = (currentTime.value / duration.value) * 100;
257
+//     currentTime.value += 1;
258
+//   }, 1000);
259
+
260
+//   // 设置区间结束监听
261
+//   const checkEnd = () => {
262
+//     if (audioPlayer.currentTime >= endTime.value) {
263
+//       pause();
264
+//       audioPlayer.currentTime = startTime.value; // 回到起点
265
+//     } else if (isPlaying.value) {
266
+//       requestAnimationFrame(checkEnd);
267
+//     }
268
+//   };
269
+//   requestAnimationFrame(checkEnd);
270
+// };
258 271
 
259 272
 // 暂停播放
260 273
 const pause = () => {
261 274
   console.log(audioPlayer, 'audioPlayer');
262
-  audioPlayer.play();
275
+  audioPlayer.value.pause();
263 276
   isPlaying.value = false;
277
+  currentIndex.value = -1;
278
+  // 清除定时器
279
+  clearInterval(timer.value);
264 280
 }
265
-
266
-// 停止并重置
267
-const stop = () => {
268
-  pause();
269
-  audioPlayer.currentTime = startTime.value;
270
-  currentTime.value = startTime.value;
281
+const play = () => {
282
+  audioPlayer.value.play();
283
+  // isPlaying.value = true;
271 284
 }
272 285
 
273 286
 // 更新当前时间
274 287
 const updateTime = () => {
275
-  currentTime.value = audioPlayer.currentTime;
288
+  console.log('audioPlayer.value.currentTime', audioPlayer.value.currentTime)
289
+  currentTime.value = audioPlayer.value.currentTime;
276 290
 }
277 291
 
278 292
 const loadRemoteAudio = async () => {
279
-  if (!filePath.value) return
293
+  if (!props.filePath) return
280 294
 
281 295
   try {
282
-
283
-
284 296
     // 创建音频对象
285 297
     const audio = audioPlayer.value
286
-    audio.src = filePath.value
298
+    audio.src = props.filePath
287 299
 
288 300
     // 等待元数据加载
289 301
     await new Promise((resolve, reject) => {
290 302
       audio.onloadedmetadata = () => {
291 303
         duration.value = audio.duration
304
+        console.log(duration.value, 'duration.value')
305
+        startTime.value = 0
292 306
         endTime.value = audio.duration
293 307
         resolve(1)
294 308
       }
@@ -309,14 +323,10 @@ const loadRemoteAudio = async () => {
309 323
 }
310 324
 
311 325
 onMounted(() => {
312
-  callId.value = props.callId;
313
-  filePath.value = 'http://1.194.161.64:9000/files/luyin/20250228/1011/8a5d1910-a6cf-4674-acb9-61f29f00cb05.wav';
314
-  if (filePath.value) {
315
-    // audioPlayer.src = filePath.value;
316
-    // audioPlayer.onloadedmetadata = () => {
317
-    //   duration.value = audioPlayer.duration;
318
-    //   endTime.value = audioPlayer.duration;
319
-    // };
326
+  // props.recordId = props.recordId;
327
+  console.log('recordId', props.recordId, props.recordId, `props.filePath=>${props.filePath}`);
328
+  // props.filePath = 'http://1.194.161.64:9000/files/luyin/20250228/1002/eb445cb0-8fbb-44dc-af52-d1e11b778d53.wav';
329
+  if (props.filePath) {
320 330
     loadRemoteAudio();
321 331
   } 
322 332
   getDetail();

+ 19 - 0
src/views/main/phone/index.vue

@@ -559,6 +559,20 @@ const loadMoreCallData = async () => {
559 559
     }
560 560
     callLoading.value = false;
561 561
 };
562
+
563
+const saveTranslate = (callid, translate) => {
564
+    console.log(callid, translate, 'adsfasdf')
565
+    if (!translate || !translate.length || !callid) return;
566
+    createPageData('/call/translate', {
567
+        callid,
568
+        translateJson: JSON.stringify(translate),
569
+    }).then(() => {
570
+
571
+    }).catch(() => {
572
+
573
+    })
574
+}
575
+
562 576
 const handleCallScroll = async (event: Event) => {
563 577
     const target = event.target as HTMLElement;
564 578
     const scrollBottom = target.scrollHeight - target.scrollTop - target.clientHeight;
@@ -762,6 +776,11 @@ const submit = async () => {
762 776
                 workordercate: form.value.type[form.value.type.length - 1],
763 777
             };
764 778
             if (callid.value) params.callId = callid.value;
779
+
780
+            if (keywords.value.length > 0) {
781
+
782
+            }
783
+
765 784
             // 提交表单
766 785
             createPageData('/order/workorder', params).then((data) => {
767 786
                 console.log(data, 'submit');

+ 5 - 2
src/views/main/telephone/callRecord/callRecord.vue

@@ -51,7 +51,7 @@
51 51
       </template>
52 52
     </page-content>
53 53
     <el-drawer title="通话录音" v-model="open" width="500px" :with-header="false" append-to-body direction="rtl">
54
-      <audio-player ref="pageModalRef" :callId="clickId" :filePath="filePath"/>
54
+      <audio-player v-if="open" :filePath="filePath" :recordId="clickId"/>
55 55
     
56 56
     </el-drawer>
57 57
     <el-dialog
@@ -118,7 +118,7 @@
118 118
 
119 119
       const pageModalRef = ref(audioPlayer);
120 120
       const recordData = ref({});
121
-      const open = ref(true);
121
+      const open = ref(false);
122 122
       const openCreatOrder = ref(false);
123 123
       const openViewOrder = ref(false);
124 124
       const filePath = ref('');
@@ -141,7 +141,9 @@
141 141
       const handleViewData = (data) => {
142 142
         console.log(data.recordPath);
143 143
         filePath.value = data.recordPath;
144
+        console.log(data, 'data')
144 145
         clickId.value = data.id;
146
+        console.log(clickId.value, 'clickId.value')
145 147
         open.value = true;
146 148
       };
147 149
 
@@ -205,6 +207,7 @@
205 207
       }
206 208
 
207 209
       return {
210
+        clickId,
208 211
         getCallSate,
209 212
         router,
210 213
         getTimeLimit,