音频生产展示
This commit is contained in:
381
src/views/audio-statistics/audio-generation.vue
Normal file
381
src/views/audio-statistics/audio-generation.vue
Normal file
@@ -0,0 +1,381 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<!-- 搜索区域 -->
|
||||
<el-card style="margin-bottom: 20px;">
|
||||
|
||||
<el-form :model="searchForm" :inline="true" class="search-form">
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="searchForm.status" placeholder="请选择状态" clearable style="width: 150px;">
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="成功" value="success" />
|
||||
<el-option label="失败" value="failed" />
|
||||
<el-option label="处理中" value="processing" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="模型">
|
||||
<el-input v-model="searchForm.model" placeholder="请输入模型" clearable style="width: 150px;" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="创建人姓名">
|
||||
<el-input v-model="searchForm.creatorName" placeholder="请输入创建人姓名" clearable style="width: 150px;" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="创建人电话">
|
||||
<el-input v-model="searchForm.creatorPhone" placeholder="请输入创建人电话" clearable style="width: 150px;" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="开始时间">
|
||||
<el-date-picker
|
||||
v-model="searchForm.startTime"
|
||||
type="datetime"
|
||||
placeholder="开始时间"
|
||||
format="yyyy-MM-dd HH:mm:ss"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
style="width: 180px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="结束时间">
|
||||
<el-date-picker
|
||||
v-model="searchForm.endTime"
|
||||
type="datetime"
|
||||
placeholder="结束时间"
|
||||
format="yyyy-MM-dd HH:mm:ss"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
style="width: 180px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="loading" @click="handleSearch">
|
||||
<i class="el-icon-search" />
|
||||
查询
|
||||
</el-button>
|
||||
<el-button @click="handleReset">
|
||||
<i class="el-icon-refresh" />
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<el-card>
|
||||
<div slot="header">
|
||||
<span>TTS请求日志列表</span>
|
||||
<div style="float: right;">
|
||||
<el-button type="primary" size="small" @click="handleRefresh">
|
||||
<i class="el-icon-refresh" />
|
||||
刷新
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableData"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
@sort-change="handleSortChange"
|
||||
>
|
||||
<el-table-column prop="id" label="ID" width="80" sortable="custom" />
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag
|
||||
:type="getStatusType(scope.row.status)"
|
||||
size="small"
|
||||
>
|
||||
{{ getStatusText(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="model" label="模型" width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="voice" label="声音" width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="inputText" label="输入文本" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="audioSizeBytes" label="文件大小" width="100">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.audioSizeBytes ? formatFileSize(scope.row.audioSizeBytes) : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="duration" label="时长(秒)" width="100">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.duration ? scope.row.duration.toFixed(2) : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="minioUrl" label="音频文件" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
v-if="scope.row.minioUrl"
|
||||
type="text"
|
||||
size="small"
|
||||
@click="handlePlayAudio(scope.row.minioUrl)"
|
||||
>
|
||||
播放
|
||||
</el-button>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="processingTimeMs" label="处理时间(ms)" width="120" />
|
||||
<el-table-column prop="createTime" label="创建时间" width="160" sortable="custom" />
|
||||
<el-table-column label="操作" width="120" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="small" @click="handleViewDetail(scope.row)">
|
||||
详情
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.status === 'FAILED'"
|
||||
type="text"
|
||||
size="small"
|
||||
@click="handleRetry(scope.row)"
|
||||
>
|
||||
重试
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-container" style="margin-top: 20px; text-align: right;">
|
||||
<el-pagination
|
||||
:current-page="pagination.pageNum"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<el-dialog
|
||||
title="TTS请求详情"
|
||||
:visible.sync="detailDialogVisible"
|
||||
width="800px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="ID">{{ currentRow.id }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<el-tag :type="getStatusType(currentRow.status)">
|
||||
{{ getStatusText(currentRow.status) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="模型">{{ currentRow.model }}</el-descriptions-item>
|
||||
<el-descriptions-item label="声音">{{ currentRow.voice }}</el-descriptions-item>
|
||||
<el-descriptions-item label="文件大小">{{ formatFileSize(currentRow.audioSizeBytes) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="时长">{{ currentRow.duration ? currentRow.duration.toFixed(2) + '秒' : '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="处理时间">{{ currentRow.processingTimeMs }}ms</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ currentRow.createTime }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="2" label="输入文本">
|
||||
<div style="max-height: 100px; overflow-y: auto;">
|
||||
{{ currentRow.inputText }}
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item v-if="currentRow.errorMessage" :span="2" label="错误信息">
|
||||
<div style="color: #f56c6c;">{{ currentRow.errorMessage }}</div>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="detailDialogVisible = false">关闭</el-button>
|
||||
<el-button
|
||||
v-if="currentRow.minioUrl"
|
||||
type="primary"
|
||||
@click="handlePlayAudio(currentRow.minioUrl)"
|
||||
>
|
||||
播放音频
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getTtsRequestLogs } from '@/api/audio-statistics'
|
||||
|
||||
export default {
|
||||
name: 'AudioGeneration',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
tableData: [],
|
||||
searchForm: {
|
||||
status: '',
|
||||
model: '',
|
||||
creatorName: '',
|
||||
creatorPhone: '',
|
||||
startTime: '',
|
||||
endTime: ''
|
||||
},
|
||||
pagination: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
},
|
||||
sortField: '',
|
||||
sortOrder: '',
|
||||
detailDialogVisible: false,
|
||||
currentRow: {}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
// 获取数据
|
||||
async fetchData() {
|
||||
this.loading = true
|
||||
try {
|
||||
const params = {
|
||||
pageNum: this.pagination.pageNum,
|
||||
pageSize: this.pagination.pageSize,
|
||||
...this.searchForm
|
||||
}
|
||||
|
||||
if (this.sortField) {
|
||||
params.sortField = this.sortField
|
||||
params.sortOrder = this.sortOrder
|
||||
}
|
||||
|
||||
const response = await getTtsRequestLogs(params)
|
||||
|
||||
// 后端直接返回数据,没有包装在success字段中
|
||||
if (response && response.data) {
|
||||
this.tableData = response.data || []
|
||||
this.pagination.total = response.total || 0
|
||||
} else {
|
||||
this.$message.error('获取数据失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取TTS请求日志失败:', error)
|
||||
this.$message.error('获取数据失败')
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
// 搜索
|
||||
handleSearch() {
|
||||
this.pagination.pageNum = 1
|
||||
this.fetchData()
|
||||
},
|
||||
|
||||
// 重置
|
||||
handleReset() {
|
||||
this.searchForm = {
|
||||
status: '',
|
||||
model: '',
|
||||
creatorName: '',
|
||||
creatorPhone: '',
|
||||
startTime: '',
|
||||
endTime: ''
|
||||
}
|
||||
this.pagination.pageNum = 1
|
||||
this.fetchData()
|
||||
},
|
||||
|
||||
// 刷新
|
||||
handleRefresh() {
|
||||
this.fetchData()
|
||||
},
|
||||
|
||||
// 分页大小改变
|
||||
handleSizeChange(val) {
|
||||
this.pagination.pageSize = val
|
||||
this.pagination.pageNum = 1
|
||||
this.fetchData()
|
||||
},
|
||||
|
||||
// 当前页改变
|
||||
handleCurrentChange(val) {
|
||||
this.pagination.pageNum = val
|
||||
this.fetchData()
|
||||
},
|
||||
|
||||
// 排序改变
|
||||
handleSortChange({ column, prop, order }) {
|
||||
this.sortField = prop
|
||||
this.sortOrder = order === 'ascending' ? 'asc' : 'desc'
|
||||
this.fetchData()
|
||||
},
|
||||
|
||||
// 查看详情
|
||||
handleViewDetail(row) {
|
||||
this.currentRow = row
|
||||
this.detailDialogVisible = true
|
||||
},
|
||||
|
||||
// 播放音频
|
||||
handlePlayAudio(audioUrl) {
|
||||
if (audioUrl) {
|
||||
window.open(audioUrl, '_blank')
|
||||
} else {
|
||||
this.$message.warning('音频文件不存在')
|
||||
}
|
||||
},
|
||||
|
||||
// 重试
|
||||
handleRetry(row) {
|
||||
this.$confirm('确定要重试这个TTS请求吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// TODO: 实现重试逻辑
|
||||
this.$message.success('重试请求已提交')
|
||||
this.fetchData()
|
||||
}).catch(() => {
|
||||
this.$message.info('已取消重试')
|
||||
})
|
||||
},
|
||||
|
||||
// 获取状态类型
|
||||
getStatusType(status) {
|
||||
const statusMap = {
|
||||
'SUCCESS': 'success',
|
||||
'FAILED': 'danger',
|
||||
'PROCESSING': 'warning'
|
||||
}
|
||||
return statusMap[status] || 'info'
|
||||
},
|
||||
|
||||
// 获取状态文本
|
||||
getStatusText(status) {
|
||||
const statusMap = {
|
||||
'SUCCESS': '成功',
|
||||
'FAILED': '失败',
|
||||
'PROCESSING': '处理中'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
},
|
||||
|
||||
// 格式化文件大小
|
||||
formatFileSize(bytes) {
|
||||
if (!bytes) return '-'
|
||||
const sizes = ['B', 'KB', 'MB', 'GB']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(1024))
|
||||
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-form {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
@@ -228,116 +228,52 @@
|
||||
title="TopSales评分详情"
|
||||
:visible.sync="detailDialogVisible"
|
||||
width="800px"
|
||||
:close-on-click-modal="false"
|
||||
@close="handleDetailDialogClose"
|
||||
>
|
||||
<div v-if="currentRecord">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="状态">
|
||||
<el-tag :type="getStatusType(currentRecord.status)">{{ currentRecord.status }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="销售人员">{{ currentRecord.salespersonName || currentRecord.salesName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="销售电话">{{ currentRecord.salespersonPhone || currentRecord.salesPhone || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="经销商">{{ currentRecord.dealerName || currentRecord.dealershipName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="经销商电话">{{ currentRecord.dealerPhone || currentRecord.dealershipPhone || '-' }}</el-descriptions-item>
|
||||
<!-- 基础沟通能力 (3个维度) -->
|
||||
<el-descriptions-item label="自然度评分">
|
||||
<el-tag :type="getScoreType(currentRecord.naturalnessScore)" size="small">
|
||||
{{ currentRecord.naturalnessScore || 0 }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="连贯性评分">
|
||||
<el-tag :type="getScoreType(currentRecord.coherenceScore)" size="small">
|
||||
{{ currentRecord.coherenceScore || 0 }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="流畅度评分">
|
||||
<el-tag :type="getScoreType(currentRecord.fluencyScore)" size="small">
|
||||
{{ currentRecord.fluencyScore || 0 }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<div v-if="currentRecord" class="detail-view-container">
|
||||
<div class="detail-header">
|
||||
<h3>{{ currentRecord.salespersonName || currentRecord.salesName || '未知销售' }}</h3>
|
||||
<p><strong>状态:</strong><el-tag :type="getStatusType(currentRecord.status)">{{ currentRecord.status }}</el-tag></p>
|
||||
<p><strong>销售电话:</strong>{{ currentRecord.salespersonPhone || currentRecord.salesPhone || '-' }}</p>
|
||||
<p><strong>经销商:</strong>{{ currentRecord.dealerName || currentRecord.dealershipName || '-' }}</p>
|
||||
<p><strong>创建时间:</strong>{{ currentRecord.createdAt }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 顾问表现 (3个维度) -->
|
||||
<el-descriptions-item label="顾问积极性评分">
|
||||
<el-tag :type="getScoreType(currentRecord.advisorAggressivenessScore)" size="small">
|
||||
{{ currentRecord.advisorAggressivenessScore || 0 }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="顾问一致性评分">
|
||||
<el-tag :type="getScoreType(currentRecord.advisorConsistencyScore)" size="small">
|
||||
{{ currentRecord.advisorConsistencyScore || 0 }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="问候接待评分">
|
||||
<el-tag :type="getScoreType(currentRecord.greetingReceptionScore)" size="small">
|
||||
{{ currentRecord.greetingReceptionScore || 0 }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<div class="basic-info">
|
||||
<h4>基本信息</h4>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<span class="info-label">总用时:</span>
|
||||
<span class="info-value">{{ currentRecord.elapsedTime }}秒</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">综合评分:</span>
|
||||
<el-tag :type="getOverallScoreType(currentRecord)" size="small">
|
||||
{{ getOverallScore(currentRecord) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 产品介绍 (4个维度) -->
|
||||
<el-descriptions-item label="品牌介绍评分">
|
||||
<el-tag :type="getScoreType(currentRecord.brandIntroductionScore)" size="small">
|
||||
{{ currentRecord.brandIntroductionScore || 0 }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="安全介绍评分">
|
||||
<el-tag :type="getScoreType(currentRecord.safetyIntroductionScore)" size="small">
|
||||
{{ currentRecord.safetyIntroductionScore || 0 }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="健康环境介绍评分">
|
||||
<el-tag :type="getScoreType(currentRecord.healthEnvironmentIntroductionScore)" size="small">
|
||||
{{ currentRecord.healthEnvironmentIntroductionScore || 0 }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="技术介绍评分">
|
||||
<el-tag :type="getScoreType(currentRecord.technologyIntroductionScore)" size="small">
|
||||
{{ currentRecord.technologyIntroductionScore || 0 }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<div class="original-content">
|
||||
<h4>对话内容</h4>
|
||||
<div class="content-display">
|
||||
<div class="formatted-content">
|
||||
<div v-for="(line, index) in getFormattedLines(currentRecord.srcContent)" :key="index" :class="getLineClass(line)">
|
||||
<span v-if="getSpeakerLabel(line)" class="speaker-label">{{ getSpeakerLabel(line) }}</span>
|
||||
<span class="speaker-content">{{ getSpeakerContent(line) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 客户理解 (3个维度) -->
|
||||
<el-descriptions-item label="客户需求理解评分">
|
||||
<el-tag :type="getScoreType(currentRecord.customerNeedsUnderstandingScore)" size="small">
|
||||
{{ currentRecord.customerNeedsUnderstandingScore || 0 }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="预算理解评分">
|
||||
<el-tag :type="getScoreType(currentRecord.budgetUnderstandingScore)" size="small">
|
||||
{{ currentRecord.budgetUnderstandingScore || 0 }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="推荐评分">
|
||||
<el-tag :type="getScoreType(currentRecord.recommendationScore)" size="small">
|
||||
{{ currentRecord.recommendationScore || 0 }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
|
||||
<!-- 成交促进 (2个维度) -->
|
||||
<el-descriptions-item label="试驾邀请评分">
|
||||
<el-tag :type="getScoreType(currentRecord.testDriveInvitationScore)" size="small">
|
||||
{{ currentRecord.testDriveInvitationScore || 0 }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="谈判成交评分">
|
||||
<el-tag :type="getScoreType(currentRecord.negotiationDealScore)" size="small">
|
||||
{{ currentRecord.negotiationDealScore || 0 }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="总用时(秒)">{{ currentRecord.elapsedTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ currentRecord.createdAt }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<!-- 显示原始内容 -->
|
||||
<div style="margin-top: 20px;">
|
||||
<h4>原始内容</h4>
|
||||
<el-input
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
:value="currentRecord.srcContent"
|
||||
readonly
|
||||
/>
|
||||
<div class="detail-actions">
|
||||
<el-button type="primary" icon="el-icon-copy-document" @click="copyContent">复制内容</el-button>
|
||||
<el-button type="success" icon="el-icon-download" @click="handleExport(currentRecord)">导出详情</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
||||
</div>
|
||||
@@ -686,6 +622,122 @@ export default {
|
||||
// 详情对话框关闭
|
||||
handleDetailDialogClose() {
|
||||
this.currentRecord = null
|
||||
},
|
||||
|
||||
// 复制内容
|
||||
copyContent() {
|
||||
if (!this.currentRecord || !this.currentRecord.srcContent) {
|
||||
this.$message.warning('暂无内容可复制')
|
||||
return
|
||||
}
|
||||
|
||||
// 创建临时文本区域
|
||||
const textArea = document.createElement('textarea')
|
||||
textArea.value = this.currentRecord.srcContent
|
||||
document.body.appendChild(textArea)
|
||||
textArea.select()
|
||||
|
||||
try {
|
||||
document.execCommand('copy')
|
||||
this.$message.success('内容已复制到剪贴板')
|
||||
} catch (err) {
|
||||
this.$message.error('复制失败,请手动复制')
|
||||
} finally {
|
||||
document.body.removeChild(textArea)
|
||||
}
|
||||
},
|
||||
|
||||
// 获取格式化的文本行数组
|
||||
getFormattedLines(text) {
|
||||
if (!text || text.trim() === '') {
|
||||
return [{ type: 'empty', content: '暂无对话内容' }]
|
||||
}
|
||||
|
||||
// 先尝试按换行符分割
|
||||
let lines = text.split('\n').filter(line => line.trim() !== '')
|
||||
|
||||
// 如果没有换行符,按说话人模式分割
|
||||
if (lines.length === 1) {
|
||||
const textContent = lines[0]
|
||||
// 尝试识别说话人模式:销售:xxx 客户:xxx
|
||||
const speakerPattern = /(销售|客户|顾问)[::]/g
|
||||
const matches = []
|
||||
let match
|
||||
while ((match = speakerPattern.exec(textContent)) !== null) {
|
||||
matches.push({
|
||||
speaker: match[1],
|
||||
index: match.index
|
||||
})
|
||||
}
|
||||
|
||||
if (matches.length > 0) {
|
||||
lines = []
|
||||
for (let i = 0; i < matches.length; i++) {
|
||||
const currentMatch = matches[i]
|
||||
const nextMatch = matches[i + 1]
|
||||
const startIndex = currentMatch.index
|
||||
const endIndex = nextMatch ? nextMatch.index : textContent.length
|
||||
const content = textContent.substring(startIndex, endIndex).trim()
|
||||
if (content) {
|
||||
lines.push(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lines.map(line => {
|
||||
const trimmedLine = line.trim()
|
||||
if (trimmedLine === '') {
|
||||
return { type: 'empty', content: '' }
|
||||
}
|
||||
|
||||
// 检查是否是说话人格式
|
||||
if (trimmedLine.match(/^(销售|客户|顾问)[::]/)) {
|
||||
const speakerMatch = trimmedLine.match(/^(销售|客户|顾问)[::](.*)/)
|
||||
if (speakerMatch) {
|
||||
return {
|
||||
type: 'speaker',
|
||||
speaker: speakerMatch[1],
|
||||
content: speakerMatch[2].trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'text',
|
||||
content: trimmedLine
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 获取行的CSS类
|
||||
getLineClass(line) {
|
||||
if (line.type === 'speaker') {
|
||||
return `speaker ${line.speaker === '销售' || line.speaker === '顾问' ? 'sales' : 'customer'}`
|
||||
}
|
||||
if (line.type === 'empty') {
|
||||
return 'no-text'
|
||||
}
|
||||
return 'text-line'
|
||||
},
|
||||
|
||||
// 获取说话人标签
|
||||
getSpeakerLabel(line) {
|
||||
if (line.type === 'speaker') {
|
||||
return line.speaker + ':'
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
||||
// 获取说话人内容
|
||||
getSpeakerContent(line) {
|
||||
if (line.type === 'speaker') {
|
||||
return line.content || ''
|
||||
}
|
||||
if (line.type === 'empty') {
|
||||
return line.content
|
||||
}
|
||||
return line.content || ''
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -724,4 +776,208 @@ export default {
|
||||
.el-table .el-button--text:hover {
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
/* 详情弹窗样式 - 参考录音管理页面 */
|
||||
.detail-view-container {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
border-left: 4px solid #409eff;
|
||||
}
|
||||
|
||||
.detail-header h3 {
|
||||
margin: 0 0 10px 0;
|
||||
color: #303133;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.detail-header p {
|
||||
margin: 5px 0;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.basic-info {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.basic-info h4 {
|
||||
margin: 0 0 15px 0;
|
||||
color: #303133;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid #409eff;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-weight: 500;
|
||||
color: #606266;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.original-content {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.original-content h4 {
|
||||
margin: 0 0 15px 0;
|
||||
color: #303133;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid #409eff;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.content-display {
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.formatted-content {
|
||||
line-height: 1.8;
|
||||
font-size: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.formatted-content .speaker {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid;
|
||||
background-color: #f8f9fa;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.formatted-content .speaker:hover {
|
||||
transform: translateX(2px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.formatted-content .speaker.sales {
|
||||
background-color: #f0f9ff;
|
||||
border-left-color: #409eff;
|
||||
}
|
||||
|
||||
.formatted-content .speaker.customer {
|
||||
background-color: #f0f9f0;
|
||||
border-left-color: #67c23a;
|
||||
}
|
||||
|
||||
.formatted-content .speaker.other {
|
||||
background-color: #f5f5f5;
|
||||
border-left-color: #909399;
|
||||
}
|
||||
|
||||
.formatted-content .speaker-label {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-right: 8px;
|
||||
font-size: 14px;
|
||||
display: inline-block;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.formatted-content .speaker.sales .speaker-label {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.formatted-content .speaker.customer .speaker-label {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.formatted-content .speaker-content {
|
||||
color: #555;
|
||||
line-height: 1.6;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.formatted-content .text-line {
|
||||
margin-bottom: 12px;
|
||||
padding: 8px 12px;
|
||||
color: #666;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 6px;
|
||||
border-left: 3px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.formatted-content .no-text {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
padding: 40px 20px;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
border: 2px dashed #ddd;
|
||||
}
|
||||
|
||||
.detail-actions {
|
||||
text-align: center;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.detail-actions .el-button {
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.info-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.content-display {
|
||||
padding: 15px;
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
.formatted-content .speaker {
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.formatted-content .speaker-label {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
725
src/views/sales/intelligent-pickup.vue
Normal file
725
src/views/sales/intelligent-pickup.vue
Normal file
@@ -0,0 +1,725 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-header">
|
||||
<h2>智能捡漏</h2>
|
||||
<p class="page-description">基于咨询场景分析的智能捡漏机会识别</p>
|
||||
</div>
|
||||
|
||||
<!-- 查询条件区域 -->
|
||||
<el-card class="filter-container" shadow="never">
|
||||
<el-form ref="queryForm" :model="queryParams" :inline="true" label-width="100px">
|
||||
<el-form-item label="主分类">
|
||||
<el-select v-model="queryParams.mainCategory" placeholder="请选择主分类" clearable>
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="购车相关" value="购车相关" />
|
||||
<el-option label="售后服务" value="售后服务" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="子分类">
|
||||
<el-select v-model="queryParams.subCategory" placeholder="请选择子分类" clearable>
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="销售流程" value="销售流程" />
|
||||
<el-option label="数字化服务" value="数字化服务" />
|
||||
<el-option label="数字化服务 (APP使用)" value="数字化服务 (APP使用)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="置信度">
|
||||
<el-select v-model="queryParams.confidence" placeholder="请选择置信度" clearable>
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="高" value="高" />
|
||||
<el-option label="中" value="中" />
|
||||
<el-option label="低" value="低" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="优先级">
|
||||
<el-select v-model="queryParams.priority" placeholder="请选择优先级" clearable>
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="高" value="高" />
|
||||
<el-option label="中" value="中" />
|
||||
<el-option label="低" value="低" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
<el-button type="success" icon="el-icon-refresh" @click="refreshData">刷新数据</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 统计卡片区域 -->
|
||||
<div class="stats-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-card class="stats-card">
|
||||
<div class="stats-item">
|
||||
<div class="stats-value">{{ totalCount }}</div>
|
||||
<div class="stats-label">总记录数</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="stats-card">
|
||||
<div class="stats-item">
|
||||
<div class="stats-value high-confidence">{{ highConfidenceCount }}</div>
|
||||
<div class="stats-label">高置信度</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="stats-card">
|
||||
<div class="stats-item">
|
||||
<div class="stats-value high-priority">{{ highPriorityCount }}</div>
|
||||
<div class="stats-label">高优先级</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="stats-card">
|
||||
<div class="stats-item">
|
||||
<div class="stats-value sales-related">{{ salesRelatedCount }}</div>
|
||||
<div class="stats-label">购车相关</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格区域 -->
|
||||
<el-card class="table-container" shadow="never">
|
||||
<div class="table-header">
|
||||
<h3>咨询场景列表</h3>
|
||||
<div class="table-actions">
|
||||
<el-button type="primary" icon="el-icon-cpu" @click="showAiAnalysisDialog">AI分析</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableData"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
:default-sort="{ prop: 'createdAt', order: 'descending' }"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
|
||||
<el-table-column prop="mainCategory" label="主分类" width="120" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="getCategoryType(scope.row.mainCategory)">
|
||||
{{ scope.row.mainCategory }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="subCategory" label="子分类" width="150" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag type="info" size="small">
|
||||
{{ scope.row.subCategory }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="confidence" label="置信度" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="getConfidenceType(scope.row.confidence)" size="small">
|
||||
{{ scope.row.confidence }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="priority" label="优先级" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="getPriorityType(scope.row.priority)" size="small">
|
||||
{{ scope.row.priority }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="userDemand" label="用户需求" min-width="200" show-overflow-tooltip />
|
||||
|
||||
<el-table-column prop="involvedVehicleModels" label="涉及车型" width="120" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.involvedVehicleModels">
|
||||
<el-tag type="warning" size="mini">{{ scope.row.involvedVehicleModels }}</el-tag>
|
||||
</span>
|
||||
<span v-else class="text-muted">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="createdAt" label="创建时间" width="160" align="center" sortable>
|
||||
<template slot-scope="scope">
|
||||
{{ formatDate(scope.row.createdAt) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="small" @click="viewDetail(scope.row)">
|
||||
<i class="el-icon-view" /> 详情
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
:current-page="pagination.current"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size="pagination.size"
|
||||
:total="pagination.total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- AI分析弹窗 -->
|
||||
<el-dialog
|
||||
title="AI智能分析"
|
||||
:visible.sync="aiAnalysisDialogVisible"
|
||||
width="60%"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div class="ai-analysis-content">
|
||||
<div class="input-section">
|
||||
<label class="input-label">请输入需要分析的文本内容:</label>
|
||||
<el-input
|
||||
v-model="analysisText"
|
||||
type="textarea"
|
||||
:rows="8"
|
||||
placeholder="请输入咨询对话内容或相关文本,AI将为您分析并提取关键信息..."
|
||||
maxlength="5000"
|
||||
show-word-limit
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="analysisResult" class="result-section">
|
||||
<h4>分析结果:</h4>
|
||||
<div class="analysis-result">
|
||||
<pre>{{ analysisResult }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="aiAnalysisDialogVisible = false">取消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="analysisLoading"
|
||||
:disabled="!analysisText.trim()"
|
||||
@click="performAiAnalysis"
|
||||
>
|
||||
{{ analysisLoading ? '分析中...' : '开始分析' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 详情弹窗 -->
|
||||
<el-dialog
|
||||
title="咨询场景详情"
|
||||
:visible.sync="detailDialogVisible"
|
||||
width="80%"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div v-if="currentRecord" class="detail-content">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="detail-item">
|
||||
<label>主分类:</label>
|
||||
<el-tag :type="getCategoryType(currentRecord.mainCategory)">
|
||||
{{ currentRecord.mainCategory }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="detail-item">
|
||||
<label>子分类:</label>
|
||||
<el-tag type="info">{{ currentRecord.subCategory }}</el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="detail-item">
|
||||
<label>置信度:</label>
|
||||
<el-tag :type="getConfidenceType(currentRecord.confidence)">
|
||||
{{ currentRecord.confidence }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="detail-item">
|
||||
<label>优先级:</label>
|
||||
<el-tag :type="getPriorityType(currentRecord.priority)">
|
||||
{{ currentRecord.priority }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div class="detail-item">
|
||||
<label>用户需求:</label>
|
||||
<div class="detail-text">{{ currentRecord.userDemand }}</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-item">
|
||||
<label>咨询内容:</label>
|
||||
<div class="detail-text">{{ currentRecord.consultingContent }}</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-item">
|
||||
<label>涉及车型:</label>
|
||||
<div class="detail-text">
|
||||
<span v-if="currentRecord.involvedVehicleModels">
|
||||
<el-tag type="warning">{{ currentRecord.involvedVehicleModels }}</el-tag>
|
||||
</span>
|
||||
<span v-else class="text-muted">无</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-item">
|
||||
<label>知识缺口:</label>
|
||||
<div class="detail-text">{{ currentRecord.knowledgeGap }}</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-item">
|
||||
<label>备注:</label>
|
||||
<div class="detail-text">{{ currentRecord.remarks }}</div>
|
||||
</div>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="detail-item">
|
||||
<label>创建时间:</label>
|
||||
<span>{{ formatDate(currentRecord.createdAt) }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="detail-item">
|
||||
<label>更新时间:</label>
|
||||
<span>{{ formatDate(currentRecord.updatedAt) }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="detailDialogVisible = false">关闭</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getConsultingScenarioList, processConsultingScenario } from '@/api/sales'
|
||||
|
||||
export default {
|
||||
name: 'IntelligentPickup',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
tableData: [],
|
||||
detailDialogVisible: false,
|
||||
currentRecord: null,
|
||||
aiAnalysisDialogVisible: false,
|
||||
analysisText: '',
|
||||
analysisResult: '',
|
||||
analysisLoading: false,
|
||||
queryParams: {
|
||||
mainCategory: '',
|
||||
subCategory: '',
|
||||
confidence: '',
|
||||
priority: ''
|
||||
},
|
||||
pagination: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
total: 0,
|
||||
pages: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
totalCount() {
|
||||
return this.pagination.total
|
||||
},
|
||||
highConfidenceCount() {
|
||||
return this.tableData.filter(item => item.confidence === '高').length
|
||||
},
|
||||
highPriorityCount() {
|
||||
return this.tableData.filter(item => item.priority === '高').length
|
||||
},
|
||||
salesRelatedCount() {
|
||||
return this.tableData.filter(item => item.mainCategory === '购车相关').length
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
// 获取数据
|
||||
async fetchData() {
|
||||
this.loading = true
|
||||
try {
|
||||
const params = {
|
||||
current: this.pagination.current,
|
||||
size: this.pagination.size,
|
||||
...this.queryParams
|
||||
}
|
||||
|
||||
// 过滤空值参数
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key] === '') {
|
||||
delete params[key]
|
||||
}
|
||||
})
|
||||
|
||||
const response = await getConsultingScenarioList(params)
|
||||
|
||||
// 添加调试信息
|
||||
console.log('API响应:', response)
|
||||
|
||||
if (response.code === 20000) {
|
||||
this.tableData = response.data || []
|
||||
this.pagination = {
|
||||
current: response.current || 1,
|
||||
size: response.size || 10,
|
||||
total: response.total || 0,
|
||||
pages: response.pages || 1
|
||||
}
|
||||
console.log('设置的tableData:', this.tableData)
|
||||
console.log('设置的pagination:', this.pagination)
|
||||
} else {
|
||||
console.log('API调用失败:', response)
|
||||
this.$message.error(response.message || '获取数据失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取数据失败:', error)
|
||||
this.$message.error('获取数据失败,请稍后重试')
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
// 查询
|
||||
handleQuery() {
|
||||
this.pagination.current = 1
|
||||
this.fetchData()
|
||||
},
|
||||
|
||||
// 重置查询条件
|
||||
resetQuery() {
|
||||
this.queryParams = {
|
||||
mainCategory: '',
|
||||
subCategory: '',
|
||||
confidence: '',
|
||||
priority: ''
|
||||
}
|
||||
this.pagination.current = 1
|
||||
this.fetchData()
|
||||
},
|
||||
|
||||
// 刷新数据
|
||||
refreshData() {
|
||||
this.fetchData()
|
||||
},
|
||||
|
||||
// 分页大小改变
|
||||
handleSizeChange(val) {
|
||||
this.pagination.size = val
|
||||
this.pagination.current = 1
|
||||
this.fetchData()
|
||||
},
|
||||
|
||||
// 当前页改变
|
||||
handleCurrentChange(val) {
|
||||
this.pagination.current = val
|
||||
this.fetchData()
|
||||
},
|
||||
|
||||
// 查看详情
|
||||
viewDetail(row) {
|
||||
this.currentRecord = row
|
||||
this.detailDialogVisible = true
|
||||
},
|
||||
|
||||
// 显示AI分析对话框
|
||||
showAiAnalysisDialog() {
|
||||
this.aiAnalysisDialogVisible = true
|
||||
this.analysisText = ''
|
||||
this.analysisResult = ''
|
||||
},
|
||||
|
||||
// 执行AI分析
|
||||
async performAiAnalysis() {
|
||||
if (!this.analysisText.trim()) {
|
||||
this.$message.warning('请输入需要分析的文本内容')
|
||||
return
|
||||
}
|
||||
|
||||
// 关闭弹窗并开始后台分析
|
||||
this.aiAnalysisDialogVisible = false
|
||||
this.$message.info('AI分析已开始,请稍后查看结果')
|
||||
|
||||
this.analysisLoading = true
|
||||
try {
|
||||
const response = await processConsultingScenario({
|
||||
chat: this.analysisText.trim(),
|
||||
businessType: 'Channel_4in1',
|
||||
sourceCorpusId: '2180459',
|
||||
sourceCorpusTime: new Date().toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
}).replace(/\//g, '-')
|
||||
})
|
||||
|
||||
if (response.code === 20000) {
|
||||
this.$message.success('AI分析完成')
|
||||
// 刷新列表数据以显示新的分析结果
|
||||
this.fetchData()
|
||||
} else {
|
||||
this.$message.error(response.message || 'AI分析失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('AI分析失败:', error)
|
||||
this.$message.error('AI分析失败,请稍后重试')
|
||||
} finally {
|
||||
this.analysisLoading = false
|
||||
}
|
||||
},
|
||||
|
||||
// 格式化日期
|
||||
formatDate(dateString) {
|
||||
if (!dateString) return '-'
|
||||
const date = new Date(dateString)
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
},
|
||||
|
||||
// 获取分类标签类型
|
||||
getCategoryType(category) {
|
||||
const typeMap = {
|
||||
'购车相关': 'success',
|
||||
'售后服务': 'warning'
|
||||
}
|
||||
return typeMap[category] || 'info'
|
||||
},
|
||||
|
||||
// 获取置信度标签类型
|
||||
getConfidenceType(confidence) {
|
||||
const typeMap = {
|
||||
'高': 'success',
|
||||
'中': 'warning',
|
||||
'低': 'danger'
|
||||
}
|
||||
return typeMap[confidence] || 'info'
|
||||
},
|
||||
|
||||
// 获取优先级标签类型
|
||||
getPriorityType(priority) {
|
||||
const typeMap = {
|
||||
'高': 'danger',
|
||||
'中': 'warning',
|
||||
'低': 'success'
|
||||
}
|
||||
return typeMap[priority] || 'info'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 20px;
|
||||
|
||||
h2 {
|
||||
margin: 0 0 8px 0;
|
||||
color: #303133;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.page-description {
|
||||
margin: 0;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.el-form {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.stats-container {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.stats-card {
|
||||
text-align: center;
|
||||
|
||||
.stats-item {
|
||||
.stats-value {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
color: #409EFF;
|
||||
margin-bottom: 8px;
|
||||
|
||||
&.high-confidence {
|
||||
color: #67C23A;
|
||||
}
|
||||
|
||||
&.high-priority {
|
||||
color: #F56C6C;
|
||||
}
|
||||
|
||||
&.sales-related {
|
||||
color: #E6A23C;
|
||||
}
|
||||
}
|
||||
|
||||
.stats-label {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-container {
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
color: #303133;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
.detail-item {
|
||||
margin-bottom: 16px;
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
font-weight: bold;
|
||||
color: #606266;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.detail-text {
|
||||
margin-top: 4px;
|
||||
line-height: 1.6;
|
||||
color: #303133;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #C0C4CC;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// AI分析对话框样式
|
||||
.ai-analysis-content {
|
||||
.input-section {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.input-label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: bold;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
|
||||
.result-section {
|
||||
margin-top: 20px;
|
||||
|
||||
h4 {
|
||||
margin: 0 0 12px 0;
|
||||
color: #303133;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.analysis-result {
|
||||
background: #f5f7fa;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
padding: 16px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: #303133;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式设计
|
||||
@media (max-width: 768px) {
|
||||
.stats-container {
|
||||
.el-col {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
.table-actions {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user