refactor(QualityCalls): 重构组件为Composition API并简化UI
fix(https): 将API基础路径从http改为https fix(topone): 修正优秀录音数据处理逻辑 fix(CenterOverview): 修改默认显示值为0
This commit is contained in:
@@ -5,7 +5,7 @@ import { useUserStore } from '@/stores/user'
|
|||||||
|
|
||||||
// 创建axios实例
|
// 创建axios实例
|
||||||
const service = axios.create({
|
const service = axios.create({
|
||||||
baseURL: 'http://mldash.nycjy.cn/' || '', // API基础路径,支持完整URL
|
baseURL: 'https://mldash.nycjy.cn/' || '', // API基础路径,支持完整URL
|
||||||
timeout: 100000, // 请求超时时间
|
timeout: 100000, // 请求超时时间
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json;charset=UTF-8'
|
'Content-Type': 'application/json;charset=UTF-8'
|
||||||
|
|||||||
@@ -67,8 +67,8 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="card-trend positive">{{ props.overallData.TotalCallCount?.total_call_count_vs_yesterday}} vs 上期</span>
|
<span class="card-trend positive">{{ props.overallData.TotalCallCount?.total_call_count_vs_yesterday}} vs 上期</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-value">{{ props.overallData.TotalCallCount?.total_call_count || '1,247' }} 次</div>
|
<div class="card-value">{{ props.overallData.TotalCallCount?.total_call_count || '0' }} 次</div>
|
||||||
<div class="card-subtitle">有效通话: {{ props.overallData.TotalCallCount?.center_effective_call_count || '892' }}次</div>
|
<div class="card-subtitle">有效通话: {{ props.overallData.TotalCallCount?.center_effective_call_count || '0' }}次</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="overview-card">
|
<div class="overview-card">
|
||||||
|
|||||||
@@ -4,14 +4,6 @@
|
|||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<div class="chart-header">
|
<div class="chart-header">
|
||||||
<h3>优秀录音</h3>
|
<h3>优秀录音</h3>
|
||||||
<button class="upload-icon-btn" @click="triggerFileUpload" title="上传录音">
|
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M14 2H6C4.9 2 4 2.9 4 4V20C4 21.1 4.89 22 5.99 22H18C19.1 22 20 21.1 20 20V8L14 2Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<polyline points="14,2 14,8 20,8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<line x1="12" y1="18" x2="12" y2="12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<polyline points="9,15 12,12 15,15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="chart-content">
|
<div class="chart-content">
|
||||||
<div class="recording-section">
|
<div class="recording-section">
|
||||||
@@ -29,7 +21,6 @@
|
|||||||
<div class="recording-info">
|
<div class="recording-info">
|
||||||
<div class="recording-name" :title="recording.name">{{ recording.name.length > 10 ? recording.name.substring(0, 10) + '...' : recording.name }}</div>
|
<div class="recording-name" :title="recording.name">{{ recording.name.length > 10 ? recording.name.substring(0, 10) + '...' : recording.name }}</div>
|
||||||
<div class="recording-meta">
|
<div class="recording-meta">
|
||||||
<span class="file-size">{{ formatFileSize(recording.size) }}</span>
|
|
||||||
<span class="upload-time">{{ recording.uploadTime }}</span>
|
<span class="upload-time">{{ recording.uploadTime }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,9 +58,8 @@
|
|||||||
<div class="result-header">
|
<div class="result-header">
|
||||||
<button class="back-btn" @click="backToRecordings">
|
<button class="back-btn" @click="backToRecordings">
|
||||||
<i class="el-icon-arrow-left"></i>
|
<i class="el-icon-arrow-left"></i>
|
||||||
返回录音列表
|
返回
|
||||||
</button>
|
</button>
|
||||||
<h4>{{ isConverting ? '正在转换...' : (currentViewType === 'transcript' ? '转换文本' : '录音分析') }}</h4>
|
|
||||||
<div class="header-actions">
|
<div class="header-actions">
|
||||||
<!-- 视图切换按钮 -->
|
<!-- 视图切换按钮 -->
|
||||||
<div class="view-toggle" v-if="currentTranscript && !isConverting">
|
<div class="view-toggle" v-if="currentTranscript && !isConverting">
|
||||||
@@ -90,7 +80,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<button class="expand-btn" @click="showExpandDialog" v-if="(currentTranscript && currentViewType === 'transcript') || (analysisResult && currentViewType === 'analysis')">
|
<button class="expand-btn" @click="showExpandDialog" v-if="(currentTranscript && currentViewType === 'transcript') || (analysisResult && currentViewType === 'analysis')">
|
||||||
<i class="el-icon-full-screen"></i>
|
<i class="el-icon-full-screen"></i>
|
||||||
展开查看
|
展开
|
||||||
</button>
|
</button>
|
||||||
<button class="copy-btn" @click="copyText" v-if="currentTranscript && currentViewType === 'transcript'">
|
<button class="copy-btn" @click="copyText" v-if="currentTranscript && currentViewType === 'transcript'">
|
||||||
<i class="el-icon-document-copy"></i>
|
<i class="el-icon-document-copy"></i>
|
||||||
@@ -174,309 +164,315 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import { ref, reactive, computed, onMounted, onBeforeUnmount } from 'vue'
|
||||||
import { SimpleChatService } from '@/utils/ChatService.js'
|
import { SimpleChatService } from '@/utils/ChatService.js'
|
||||||
import MarkdownIt from 'markdown-it'
|
import MarkdownIt from 'markdown-it'
|
||||||
|
|
||||||
export default {
|
// Props定义
|
||||||
name: 'QualityCalls',
|
const props = defineProps({
|
||||||
props: {
|
qualityCalls: {
|
||||||
qualityCalls: {
|
type: Object,
|
||||||
type: Object,
|
default: () => ({})
|
||||||
default: () => ({})
|
}
|
||||||
}
|
})
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
staticRecordings: [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: '常家硕-张三丰-亮剑二部-20分钟通话-25-07-16_18-23-04-44196-215.mp3',
|
|
||||||
size: 2048576, // 2MB
|
|
||||||
duration: '00:03:45',
|
|
||||||
date: '2024-01-15',
|
|
||||||
url: '/recordings/sample_call_1.mp3',
|
|
||||||
transcription: null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: '常家硕-张三丰-亮剑二部-20分钟通话-25-07-16_18-23-01-439240-599.mp3',
|
|
||||||
size: 3145728, // 3MB
|
|
||||||
duration: '00:05:20',
|
|
||||||
date: '2024-01-14',
|
|
||||||
url: '/recordings/sample_call_2.mp3',
|
|
||||||
transcription: null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: '常家硕-张三丰-亮剑二部-20分钟通话-25-07-16_18-23-02-754615-508.mp3',
|
|
||||||
size: 2048576, // 2MB
|
|
||||||
duration: '00:03:45',
|
|
||||||
date: '2024-01-15',
|
|
||||||
url: '/recordings/sample_call_1.mp3',
|
|
||||||
transcription: null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
name: '丁传辉-丁传辉-勇士二部-20分钟通话-25-07-10_10-32-54-813815-322.mp3',
|
|
||||||
size: 3145728, // 3MB
|
|
||||||
duration: '00:05:20',
|
|
||||||
date: '2024-01-14',
|
|
||||||
url: '/recordings/sample_call_2.mp3',
|
|
||||||
transcription: null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
selectedRecording: null,
|
|
||||||
currentAudio: null,
|
|
||||||
showTranscriptView: false,
|
|
||||||
isConverting: false,
|
|
||||||
currentTranscript: null,
|
|
||||||
showDialog: false,
|
|
||||||
// 录音分析相关
|
|
||||||
showAnalysisView: false,
|
|
||||||
isAnalyzing: false,
|
|
||||||
analysisResult: '',
|
|
||||||
currentViewType: 'transcript', // 'transcript' 或 'analysis'
|
|
||||||
// Dify API配置
|
|
||||||
DIFY_API_KEY_02: 'app-h4uBo5kOGoiYhjuBF1AHZi8b', // 通话录音分析
|
|
||||||
chatService_02: null,
|
|
||||||
md: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
// 初始化服务
|
|
||||||
this.chatService_02 = new SimpleChatService(this.DIFY_API_KEY_02)
|
|
||||||
this.md = new MarkdownIt({
|
|
||||||
html: true,
|
|
||||||
linkify: true,
|
|
||||||
typographer: true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
// 处理传入的录音数据
|
|
||||||
recordings() {
|
|
||||||
if (!this.qualityCalls || !this.qualityCalls.excellent_record_list) {
|
|
||||||
return this.staticRecordings;
|
|
||||||
}
|
|
||||||
|
|
||||||
const recordings = [];
|
|
||||||
Object.keys(this.qualityCalls.excellent_record_list).forEach(userName => {
|
|
||||||
this.qualityCalls.excellent_record_list[userName].forEach((record, index) => {
|
|
||||||
recordings.push({
|
|
||||||
id: recordings.length + 1,
|
|
||||||
name: record.obj_file_name ? record.obj_file_name.split('/').pop() : `${record.sale_name}-录音-${index + 1}`,
|
|
||||||
size: 2048576, // 默认大小
|
|
||||||
duration: '00:03:45', // 默认时长
|
|
||||||
date: new Date().toISOString().split('T')[0],
|
|
||||||
url: record.obj_file_name,
|
|
||||||
transcription: record.context || null,
|
|
||||||
score: record.score,
|
|
||||||
sop: record.sop,
|
|
||||||
sale_name: record.sale_name
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return recordings;
|
|
||||||
},
|
|
||||||
// 格式化分析结果
|
|
||||||
formattedAnalysisResult() {
|
|
||||||
if (!this.analysisResult) return ''
|
|
||||||
return this.md.render(this.analysisResult)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeUnmount() {
|
|
||||||
if (this.currentAudio) {
|
|
||||||
this.currentAudio.pause()
|
|
||||||
this.currentAudio = null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 录音文件选择
|
|
||||||
handleFileSelect(event) {
|
|
||||||
const file = event.target.files[0]
|
|
||||||
if (file) {
|
|
||||||
const recording = {
|
|
||||||
name: file.name,
|
|
||||||
size: file.size,
|
|
||||||
uploadTime: new Date().toLocaleString(),
|
|
||||||
url: URL.createObjectURL(file),
|
|
||||||
isPlaying: false,
|
|
||||||
isConverting: false,
|
|
||||||
transcript: null
|
|
||||||
}
|
|
||||||
this.recordings.push(recording)
|
|
||||||
// 清空input以便重复选择同一文件
|
|
||||||
event.target.value = ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 选择录音
|
|
||||||
selectRecording(index) {
|
|
||||||
this.selectedRecording = index
|
|
||||||
},
|
|
||||||
// 播放/暂停录音
|
|
||||||
togglePlay(index) {
|
|
||||||
const recording = this.recordings[index]
|
|
||||||
|
|
||||||
// 停止当前播放的音频
|
|
||||||
if (this.currentAudio) {
|
|
||||||
this.currentAudio.pause()
|
|
||||||
this.recordings.forEach(r => r.isPlaying = false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!recording.isPlaying) {
|
|
||||||
this.currentAudio = new Audio(recording.url)
|
|
||||||
this.currentAudio.play()
|
|
||||||
recording.isPlaying = true
|
|
||||||
|
|
||||||
this.currentAudio.onended = () => {
|
|
||||||
recording.isPlaying = false
|
|
||||||
this.currentAudio = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 转换为文本
|
|
||||||
async convertToText(index) {
|
|
||||||
const recording = this.recordings[index]
|
|
||||||
this.selectedRecording = index
|
|
||||||
this.showTranscriptView = true
|
|
||||||
this.isConverting = true
|
|
||||||
this.currentTranscript = null
|
|
||||||
this.currentViewType = 'transcript'
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 模拟转换过程
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
||||||
|
|
||||||
// 这里应该调用实际的语音转文本API
|
|
||||||
// 目前使用模拟数据
|
|
||||||
recording.transcript = `这是 ${recording.name} 的转换文本示例。在实际应用中,这里会显示真实的语音转文本结果。您可以集成百度、阿里云、腾讯云等语音识别服务来实现真正的语音转文本功能。`
|
|
||||||
this.currentTranscript = recording.transcript
|
|
||||||
|
|
||||||
// 转换完成后自动开始录音分析
|
|
||||||
this.startRecordingAnalysis(recording)
|
|
||||||
|
|
||||||
// 添加转换完成的动画效果
|
|
||||||
const resultElement = document.querySelector('.conversion-result')
|
|
||||||
if (resultElement) {
|
|
||||||
resultElement.classList.add('show-result')
|
|
||||||
setTimeout(() => {
|
|
||||||
resultElement.classList.remove('show-result')
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('转换失败:', error)
|
|
||||||
alert('转换失败,请重试')
|
|
||||||
this.showTranscriptView = false
|
|
||||||
} finally {
|
|
||||||
this.isConverting = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 开始通话录音分析
|
|
||||||
async startRecordingAnalysis(recording) {
|
|
||||||
this.isAnalyzing = true
|
|
||||||
this.analysisResult = ''
|
|
||||||
|
|
||||||
// 构建通话录音分析查询
|
|
||||||
const recordingQuery = `请对录音文件 ${recording.name} 进行通话录音分析,包括:
|
|
||||||
1. 通话质量评估
|
|
||||||
2. 客户情绪分析
|
|
||||||
3. 沟通效果评价
|
|
||||||
4. 关键信息提取
|
|
||||||
5. 改进建议
|
|
||||||
|
|
||||||
录音信息:
|
// 响应式数据
|
||||||
文件名:${recording.name}
|
const staticRecordings = ref([
|
||||||
文件大小:${this.formatFileSize(recording.size)}
|
{
|
||||||
转换文本:${recording.transcript}`
|
id: 1,
|
||||||
|
name: '常家硕-张三丰-亮剑二部-20分钟通话-25-07-16_18-23-04-44196-215.mp3',
|
||||||
try {
|
size: 2048576, // 2MB
|
||||||
await this.chatService_02.sendMessage(
|
duration: '00:03:45',
|
||||||
recordingQuery,
|
date: '2024-01-15',
|
||||||
(update) => {
|
url: '/recordings/sample_call_1.mp3',
|
||||||
// 实时更新通话录音分析结果
|
transcription: null
|
||||||
this.analysisResult = update.content
|
}
|
||||||
},
|
])
|
||||||
() => {
|
|
||||||
// 流结束回调
|
const selectedRecording = ref(null)
|
||||||
console.log('通话录音分析完成')
|
const currentAudio = ref(null)
|
||||||
this.isAnalyzing = false
|
const showTranscriptView = ref(false)
|
||||||
}
|
const isConverting = ref(false)
|
||||||
)
|
const currentTranscript = ref(null)
|
||||||
} catch (error) {
|
const showDialog = ref(false)
|
||||||
console.error('通话录音分析失败:', error)
|
// 录音分析相关
|
||||||
this.analysisResult = '通话录音分析失败,请重试。'
|
const showAnalysisView = ref(false)
|
||||||
this.isAnalyzing = false
|
const isAnalyzing = ref(false)
|
||||||
}
|
const analysisResult = ref('')
|
||||||
},
|
const currentViewType = ref('transcript') // 'transcript' 或 'analysis'
|
||||||
|
// Dify API配置
|
||||||
|
const DIFY_API_KEY_02 = 'app-h4uBo5kOGoiYhjuBF1AHZi8b' // 通话录音分析
|
||||||
|
const chatService_02 = ref(null)
|
||||||
|
const md = ref(null)
|
||||||
|
|
||||||
|
// 初始化服务
|
||||||
|
onMounted(() => {
|
||||||
|
chatService_02.value = new SimpleChatService(DIFY_API_KEY_02)
|
||||||
|
md.value = new MarkdownIt({
|
||||||
|
html: true,
|
||||||
|
linkify: true,
|
||||||
|
typographer: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// 计算属性
|
||||||
|
// 处理传入的录音数据
|
||||||
|
const recordings = computed(() => {
|
||||||
|
if (!props.qualityCalls ) {
|
||||||
|
return staticRecordings.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const recordingsList = [];
|
||||||
|
Object.keys(props.qualityCalls).forEach(userName => {
|
||||||
|
props.qualityCalls[userName].forEach((record, index) => {
|
||||||
|
recordingsList.push({
|
||||||
|
id: recordingsList.length + 1,
|
||||||
|
name: record.obj_file_name ? record.obj_file_name.split('/').pop() : `${record.sale_name}-录音-${index + 1}`,
|
||||||
|
date: new Date().toISOString().split('T')[0],
|
||||||
|
url: record.obj_file_name,
|
||||||
|
transcription: record.context || null,
|
||||||
|
score: record.score,
|
||||||
|
sop: record.sop,
|
||||||
|
sale_name: record.sale_name,
|
||||||
|
size: 2048576, // 默认文件大小 2MB
|
||||||
|
uploadTime: new Date().toLocaleDateString('zh-CN')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return recordingsList;
|
||||||
|
})
|
||||||
|
|
||||||
|
// 格式化分析结果
|
||||||
|
const formattedAnalysisResult = computed(() => {
|
||||||
|
if (!analysisResult.value) return ''
|
||||||
|
return md.value.render(analysisResult.value)
|
||||||
|
})
|
||||||
|
// 生命周期钩子
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (currentAudio.value) {
|
||||||
|
currentAudio.value.pause()
|
||||||
|
currentAudio.value = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 方法定义
|
||||||
|
// 录音文件选择
|
||||||
|
const handleFileSelect = (event) => {
|
||||||
|
const file = event.target.files[0]
|
||||||
|
if (file) {
|
||||||
|
const recording = {
|
||||||
|
name: file.name,
|
||||||
|
size: file.size,
|
||||||
|
uploadTime: new Date().toLocaleString(),
|
||||||
|
url: URL.createObjectURL(file),
|
||||||
|
isPlaying: false,
|
||||||
|
isConverting: false,
|
||||||
|
transcript: null
|
||||||
|
}
|
||||||
|
staticRecordings.value.push(recording)
|
||||||
|
// 清空input以便重复选择同一文件
|
||||||
|
event.target.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择录音
|
||||||
|
const selectRecording = (index) => {
|
||||||
|
selectedRecording.value = index
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放/暂停录音
|
||||||
|
const togglePlay = (index) => {
|
||||||
|
const recording = recordings.value[index]
|
||||||
|
|
||||||
|
// 停止当前播放的音频
|
||||||
|
if (currentAudio.value) {
|
||||||
|
currentAudio.value.pause()
|
||||||
|
recordings.value.forEach(r => r.isPlaying = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!recording.isPlaying) {
|
||||||
|
currentAudio.value = new Audio(recording.url)
|
||||||
|
currentAudio.value.play()
|
||||||
|
recording.isPlaying = true
|
||||||
|
|
||||||
// 切换视图类型
|
currentAudio.value.onended = () => {
|
||||||
switchViewType(type) {
|
recording.isPlaying = false
|
||||||
this.currentViewType = type
|
currentAudio.value = null
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 转换为文字
|
||||||
|
const convertToText = async (index) => {
|
||||||
|
const recording = recordings.value[index]
|
||||||
|
selectedRecording.value = index
|
||||||
|
showTranscriptView.value = true
|
||||||
|
isConverting.value = true
|
||||||
|
currentTranscript.value = null
|
||||||
|
currentViewType.value = 'transcript'
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 模拟转换过程
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
|
||||||
// 返回录音列表
|
// 使用从API获取的transcription数据
|
||||||
backToRecordings() {
|
if (recording.transcription) {
|
||||||
this.showTranscriptView = false
|
recording.transcript = recording.transcription
|
||||||
this.currentTranscript = null
|
currentTranscript.value = recording.transcription
|
||||||
this.analysisResult = ''
|
} else {
|
||||||
this.currentViewType = 'transcript'
|
// 如果没有transcription数据,显示提示信息
|
||||||
this.isAnalyzing = false
|
recording.transcript = '暂无转换文本数据'
|
||||||
},
|
currentTranscript.value = '暂无转换文本数据'
|
||||||
// 复制文本
|
}
|
||||||
copyText() {
|
|
||||||
if (this.currentTranscript) {
|
// 添加转换完成的动画效果
|
||||||
navigator.clipboard.writeText(this.currentTranscript)
|
const resultElement = document.querySelector('.conversion-result')
|
||||||
|
if (resultElement) {
|
||||||
|
resultElement.classList.add('show-result')
|
||||||
|
setTimeout(() => {
|
||||||
|
resultElement.classList.remove('show-result')
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('转换失败:', error)
|
||||||
|
alert('转换失败,请重试')
|
||||||
|
showTranscriptView.value = false
|
||||||
|
} finally {
|
||||||
|
isConverting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始通话录音分析
|
||||||
|
const startRecordingAnalysis = async (recording) => {
|
||||||
|
isAnalyzing.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用从API获取的sop数据作为录音分析结果
|
||||||
|
if (recording.sop) {
|
||||||
|
analysisResult.value = recording.sop
|
||||||
|
} else {
|
||||||
|
analysisResult.value = '暂无录音分析数据'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟分析过程
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500))
|
||||||
|
|
||||||
|
console.log('录音分析完成')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('录音分析失败:', error)
|
||||||
|
analysisResult.value = '录音分析失败,请重试。'
|
||||||
|
} finally {
|
||||||
|
isAnalyzing.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换视图类型
|
||||||
|
const switchViewType = (type) => {
|
||||||
|
currentViewType.value = type
|
||||||
|
|
||||||
|
// 如果切换到录音分析视图,且还没有分析结果,则开始分析
|
||||||
|
if (type === 'analysis' && !analysisResult.value && selectedRecording.value !== null) {
|
||||||
|
const recording = recordings.value[selectedRecording.value]
|
||||||
|
startRecordingAnalysis(recording)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回录音列表
|
||||||
|
const backToRecordings = () => {
|
||||||
|
showTranscriptView.value = false
|
||||||
|
currentTranscript.value = null
|
||||||
|
analysisResult.value = ''
|
||||||
|
currentViewType.value = 'transcript'
|
||||||
|
isAnalyzing.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制文本
|
||||||
|
const copyText = async () => {
|
||||||
|
if (currentTranscript.value) {
|
||||||
|
try {
|
||||||
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
|
await navigator.clipboard.writeText(currentTranscript.value)
|
||||||
|
alert('文本已复制到剪贴板')
|
||||||
|
} else {
|
||||||
|
// 降级方案:使用传统的复制方法
|
||||||
|
const textArea = document.createElement('textarea')
|
||||||
|
textArea.value = currentTranscript.value
|
||||||
|
document.body.appendChild(textArea)
|
||||||
|
textArea.select()
|
||||||
|
document.execCommand('copy')
|
||||||
|
document.body.removeChild(textArea)
|
||||||
alert('文本已复制到剪贴板')
|
alert('文本已复制到剪贴板')
|
||||||
}
|
}
|
||||||
},
|
} catch (error) {
|
||||||
// 复制分析结果
|
console.error('复制失败:', error)
|
||||||
copyAnalysisText() {
|
alert('复制失败,请手动复制')
|
||||||
if (this.analysisResult) {
|
}
|
||||||
navigator.clipboard.writeText(this.analysisResult)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制分析结果
|
||||||
|
const copyAnalysisText = async () => {
|
||||||
|
if (analysisResult.value) {
|
||||||
|
try {
|
||||||
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
|
await navigator.clipboard.writeText(analysisResult.value)
|
||||||
|
alert('分析结果已复制到剪贴板')
|
||||||
|
} else {
|
||||||
|
// 降级方案:使用传统的复制方法
|
||||||
|
const textArea = document.createElement('textarea')
|
||||||
|
textArea.value = analysisResult.value
|
||||||
|
document.body.appendChild(textArea)
|
||||||
|
textArea.select()
|
||||||
|
document.execCommand('copy')
|
||||||
|
document.body.removeChild(textArea)
|
||||||
alert('分析结果已复制到剪贴板')
|
alert('分析结果已复制到剪贴板')
|
||||||
}
|
}
|
||||||
},
|
} catch (error) {
|
||||||
// 显示展开弹框
|
console.error('复制失败:', error)
|
||||||
showExpandDialog() {
|
alert('复制失败,请手动复制')
|
||||||
this.showDialog = true
|
|
||||||
},
|
|
||||||
// 关闭弹框
|
|
||||||
closeDialog() {
|
|
||||||
this.showDialog = false
|
|
||||||
},
|
|
||||||
// 格式化文件大小
|
|
||||||
formatFileSize(bytes) {
|
|
||||||
if (bytes === 0) return '0 Bytes'
|
|
||||||
const k = 1024
|
|
||||||
const sizes = ['Bytes', 'KB', 'MB', 'GB']
|
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
|
||||||
},
|
|
||||||
triggerFileUpload() {
|
|
||||||
const fileInput = document.createElement('input')
|
|
||||||
fileInput.type = 'file'
|
|
||||||
fileInput.accept = 'audio/*'
|
|
||||||
fileInput.style.display = 'none'
|
|
||||||
fileInput.addEventListener('change', this.handleFileSelect)
|
|
||||||
document.body.appendChild(fileInput)
|
|
||||||
fileInput.click()
|
|
||||||
document.body.removeChild(fileInput)
|
|
||||||
},
|
|
||||||
downloadRecording(index) {
|
|
||||||
const recording = this.recordings[index]
|
|
||||||
if (recording && recording.url) {
|
|
||||||
const link = document.createElement('a')
|
|
||||||
link.href = recording.url
|
|
||||||
link.download = recording.name
|
|
||||||
document.body.appendChild(link)
|
|
||||||
link.click()
|
|
||||||
document.body.removeChild(link)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 显示展开弹框
|
||||||
|
const showExpandDialog = () => {
|
||||||
|
showDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹框
|
||||||
|
const closeDialog = () => {
|
||||||
|
showDialog.value = false
|
||||||
|
}
|
||||||
|
// 格式化文件大小
|
||||||
|
const formatFileSize = (bytes) => {
|
||||||
|
if (bytes === 0) return '0 Bytes'
|
||||||
|
const k = 1024
|
||||||
|
const sizes = ['Bytes', 'KB', 'MB', 'GB']
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
const triggerFileUpload = () => {
|
||||||
|
const fileInput = document.createElement('input')
|
||||||
|
fileInput.type = 'file'
|
||||||
|
fileInput.accept = 'audio/*'
|
||||||
|
fileInput.style.display = 'none'
|
||||||
|
fileInput.addEventListener('change', handleFileSelect)
|
||||||
|
document.body.appendChild(fileInput)
|
||||||
|
fileInput.click()
|
||||||
|
document.body.removeChild(fileInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadRecording = (index) => {
|
||||||
|
const recording = recordings.value[index]
|
||||||
|
if (recording && recording.url) {
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = recording.url
|
||||||
|
link.download = recording.name
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -558,8 +554,8 @@ export default {
|
|||||||
|
|
||||||
.recording-section {
|
.recording-section {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 300px;
|
min-height: 200px;
|
||||||
max-height: 500px;
|
max-height: 300px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -495,20 +495,8 @@ const params={
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const res = await getExcellentRecordFile(params)
|
const res = await getExcellentRecordFile(params)
|
||||||
excellentRecord.value = res.data
|
excellentRecord.value = res.data.excellent_record_list
|
||||||
/**
|
console.log(111111,res.data.excellent_record_list)
|
||||||
* "user_name": "赵世敬",
|
|
||||||
"user_level": 5,
|
|
||||||
"excellent_record_list": {
|
|
||||||
"马然": [
|
|
||||||
{
|
|
||||||
"sale_name": "马然",
|
|
||||||
"sop": ...,
|
|
||||||
"context": "...",
|
|
||||||
"obj_file_name": "http://192.168.3.112:5000/api/record/download/马然-20分钟通话-25-08-20_20-24-43-653520-759.mp3",
|
|
||||||
"score": 55.0
|
|
||||||
},]}
|
|
||||||
*/
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取优秀录音失败:", error);
|
console.error("获取优秀录音失败:", error);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user