修正销售管理和录音管理的路径
This commit is contained in:
@@ -13,7 +13,7 @@ export function getAudioList(params) {
|
|||||||
dealershipId: params.dealershipId,
|
dealershipId: params.dealershipId,
|
||||||
intentionLevel: params.intentionLevel,
|
intentionLevel: params.intentionLevel,
|
||||||
projectId: params.projectId,
|
projectId: params.projectId,
|
||||||
salesId: params.salesId,
|
salesPhone: params.salesPhone,
|
||||||
customerName: params.customerName,
|
customerName: params.customerName,
|
||||||
editStatus: params.editStatus,
|
editStatus: params.editStatus,
|
||||||
maxDuration: params.maxDuration,
|
maxDuration: params.maxDuration,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export function getSalesList(params) {
|
|||||||
current: params.pageNum || 1,
|
current: params.pageNum || 1,
|
||||||
size: params.pageSize || 10,
|
size: params.pageSize || 10,
|
||||||
salesName: params.salesName,
|
salesName: params.salesName,
|
||||||
|
salesPhone: params.salesPhone,
|
||||||
loginAccount: params.loginAccount,
|
loginAccount: params.loginAccount,
|
||||||
dealershipId: params.storeId,
|
dealershipId: params.storeId,
|
||||||
status: params.status,
|
status: params.status,
|
||||||
@@ -76,3 +77,19 @@ export function getSalesStatistics() {
|
|||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 分页查询TopSales评分结果
|
||||||
|
export function getTopSalesPage(params) {
|
||||||
|
return request({
|
||||||
|
url: '/topsales/page',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
current: params.current || 1,
|
||||||
|
size: params.size || 10,
|
||||||
|
workflowId: params.workflowId,
|
||||||
|
status: params.status,
|
||||||
|
salesName: params.salesName,
|
||||||
|
dealershipName: params.dealershipName
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -98,6 +98,12 @@ export const constantRoutes = [
|
|||||||
name: 'SalesAudio',
|
name: 'SalesAudio',
|
||||||
component: () => import('@/views/audio/index'),
|
component: () => import('@/views/audio/index'),
|
||||||
meta: { title: '录音管理', icon: 'el-icon-microphone' }
|
meta: { title: '录音管理', icon: 'el-icon-microphone' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dimension-analysis',
|
||||||
|
name: 'DimensionAnalysis',
|
||||||
|
component: () => import('@/views/sales/dimension-analysis'),
|
||||||
|
meta: { title: '维度分析', icon: 'el-icon-data-analysis' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,15 +26,13 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="所属销售">
|
<el-form-item label="销售电话">
|
||||||
<el-select v-model="queryParams.salesId" placeholder="请选择销售" clearable>
|
<el-input
|
||||||
<el-option
|
v-model="queryParams.salesPhone"
|
||||||
v-for="item in salesOptions"
|
placeholder="请输入销售电话"
|
||||||
:key="item.value"
|
clearable
|
||||||
:label="item.label"
|
style="width: 200px"
|
||||||
:value="item.value"
|
|
||||||
/>
|
/>
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="录音名称">
|
<el-form-item label="录音名称">
|
||||||
@@ -148,8 +146,30 @@
|
|||||||
<el-table-column type="selection" width="55" />
|
<el-table-column type="selection" width="55" />
|
||||||
<el-table-column label="序号" type="index" width="50" />
|
<el-table-column label="序号" type="index" width="50" />
|
||||||
<el-table-column label="录音名称" prop="recordingName" min-width="150" />
|
<el-table-column label="录音名称" prop="recordingName" min-width="150" />
|
||||||
|
<el-table-column label="录音文本" prop="recordingText" min-width="150">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<div v-if="scope.row.recordingText && scope.row.recordingText.trim()">
|
||||||
|
<span class="recording-text-preview">
|
||||||
|
{{ getTextPreview(scope.row.recordingText) }}
|
||||||
|
</span>
|
||||||
|
<el-button
|
||||||
|
type="text"
|
||||||
|
size="mini"
|
||||||
|
icon="el-icon-view"
|
||||||
|
style="margin-left: 8px;"
|
||||||
|
@click="handleViewText(scope.row)"
|
||||||
|
>
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<span style="color: #999;">暂无文本</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="录音时间" prop="recordingTime" min-width="150" />
|
<el-table-column label="录音时间" prop="recordingTime" min-width="150" />
|
||||||
<el-table-column label="所属销售" prop="salesName" min-width="120" />
|
<el-table-column label="所属销售" prop="salesName" min-width="120" />
|
||||||
|
<el-table-column label="销售电话" prop="salesPhone" min-width="120" />
|
||||||
<el-table-column label="录音时长" prop="duration" min-width="100">
|
<el-table-column label="录音时长" prop="duration" min-width="100">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
{{ scope.row.duration }} 分钟
|
{{ scope.row.duration }} 分钟
|
||||||
@@ -189,30 +209,6 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="录音文本" prop="recordingText" min-width="150">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<div v-if="scope.row.recordingText && scope.row.recordingText.trim()">
|
|
||||||
<span class="recording-text-preview">
|
|
||||||
{{ getTextPreview(scope.row.recordingText) }}
|
|
||||||
</span>
|
|
||||||
<el-button
|
|
||||||
type="text"
|
|
||||||
size="mini"
|
|
||||||
icon="el-icon-view"
|
|
||||||
style="margin-left: 8px;"
|
|
||||||
@click="handleViewText(scope.row)"
|
|
||||||
>
|
|
||||||
查看
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<el-tag type="info" size="mini">
|
|
||||||
<i class="el-icon-warning" />
|
|
||||||
无文本
|
|
||||||
</el-tag>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="音频文件" prop="audioFileUrl" min-width="250">
|
<el-table-column label="音频文件" prop="audioFileUrl" min-width="250">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<div v-if="scope.row.audioFileUrl">
|
<div v-if="scope.row.audioFileUrl">
|
||||||
@@ -666,7 +662,12 @@
|
|||||||
<div class="text-content">
|
<div class="text-content">
|
||||||
<h4>录音文本:</h4>
|
<h4>录音文本:</h4>
|
||||||
<div class="text-display">
|
<div class="text-display">
|
||||||
<pre>{{ currentTextRow.recordingText }}</pre>
|
<div class="formatted-text">
|
||||||
|
<div v-for="(line, index) in getFormattedLines(currentTextRow.recordingText)" :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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1017,11 +1018,11 @@ export default {
|
|||||||
// 查询参数
|
// 查询参数
|
||||||
queryParams: {
|
queryParams: {
|
||||||
current: 1,
|
current: 1,
|
||||||
size: 20,
|
size: 10,
|
||||||
recordingName: '',
|
recordingName: '',
|
||||||
dealershipId: '',
|
dealershipId: '',
|
||||||
projectId: '',
|
projectId: '',
|
||||||
salesId: '',
|
salesPhone: '',
|
||||||
customerName: '',
|
customerName: '',
|
||||||
customerPhone: '',
|
customerPhone: '',
|
||||||
editStatus: '',
|
editStatus: '',
|
||||||
@@ -1032,6 +1033,10 @@ export default {
|
|||||||
recordingTimeRange: [],
|
recordingTimeRange: [],
|
||||||
keyword: ''
|
keyword: ''
|
||||||
},
|
},
|
||||||
|
// 待处理的分支机构名称(从URL参数传入)
|
||||||
|
pendingDealershipName: '',
|
||||||
|
// 标记是否需要在选项加载完成后自动查询
|
||||||
|
autoQueryAfterOptionsLoaded: false,
|
||||||
// 分配项目表单
|
// 分配项目表单
|
||||||
allocateProjectForm: {
|
allocateProjectForm: {
|
||||||
audioIds: [],
|
audioIds: [],
|
||||||
@@ -1107,12 +1112,40 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
// 检查URL参数,如果是从销售管理页面跳转过来的
|
||||||
|
this.handleUrlParams()
|
||||||
this.getList()
|
this.getList()
|
||||||
this.getDealershipOptions()
|
this.getDealershipOptions()
|
||||||
this.getProjectOptions()
|
this.getProjectOptions()
|
||||||
this.getSalesOptions()
|
this.getSalesOptions()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// 处理URL参数
|
||||||
|
handleUrlParams() {
|
||||||
|
const { salesPhone, salesName, dealershipName } = this.$route.query
|
||||||
|
|
||||||
|
if (salesPhone) {
|
||||||
|
// 直接设置销售电话查询条件
|
||||||
|
this.queryParams.salesPhone = salesPhone
|
||||||
|
|
||||||
|
// 如果有分支机构名称,设置到门店选择中
|
||||||
|
if (dealershipName) {
|
||||||
|
// 这里需要根据分支机构名称找到对应的ID
|
||||||
|
// 暂时先设置一个标识,等门店选项加载完成后再处理
|
||||||
|
this.pendingDealershipName = dealershipName
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('从销售管理页面跳转,查询参数:', {
|
||||||
|
salesPhone,
|
||||||
|
salesName,
|
||||||
|
dealershipName
|
||||||
|
})
|
||||||
|
|
||||||
|
// 设置查询条件后,标记需要自动查询
|
||||||
|
this.autoQueryAfterOptionsLoaded = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 获取列表数据
|
// 获取列表数据
|
||||||
getList() {
|
getList() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
@@ -1132,94 +1165,48 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log('发送给后端的查询参数:', params)
|
||||||
|
|
||||||
getAudioList(params).then(response => {
|
getAudioList(params).then(response => {
|
||||||
console.log('API响应:', response)
|
console.log('API响应:', response)
|
||||||
if (response.code === 20000) {
|
if (response.code === 20000) {
|
||||||
this.audioList = response.data.records || response.data.data || response.data || []
|
this.audioList = response.data.records || response.data.data || response.data || []
|
||||||
this.total = response.data.total || response.data.length || 0
|
this.total = response.data.total || response.data.length || 0
|
||||||
|
console.log('查询到的录音数量:', this.audioList.length)
|
||||||
} else {
|
} else {
|
||||||
this.$message.error(response.message || '查询失败')
|
this.$message.error(response.message || '查询失败')
|
||||||
}
|
}
|
||||||
this.loading = false
|
this.loading = false
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.log('API错误:', error)
|
console.error('API调用失败:', error)
|
||||||
// 开发环境使用模拟数据
|
this.$message.error('查询录音失败,请检查网络连接')
|
||||||
this.audioList = [
|
this.audioList = []
|
||||||
{
|
this.total = 0
|
||||||
id: '1',
|
|
||||||
recordingName: '销售录音_001',
|
|
||||||
recordingTime: '2025-08-08 10:30:15',
|
|
||||||
salesName: '销冠【普通】',
|
|
||||||
duration: 15,
|
|
||||||
customerName: '张先生',
|
|
||||||
customerPhone: '13800138000',
|
|
||||||
uploadTime: '2025-08-08 10:35:20',
|
|
||||||
intentionLevel: 'A',
|
|
||||||
dealershipName: '销售演示-上汽',
|
|
||||||
projectName: '电话接访话术',
|
|
||||||
speechModel: 'GPT-4',
|
|
||||||
uploadStatus: '已上传',
|
|
||||||
syncStatus: '已同步',
|
|
||||||
isMerged: false,
|
|
||||||
audioFileUrl: '/api/audio/1953606689980715010_db3c711d-e581-49bb-814f-4709c0431021.mp3',
|
|
||||||
audioFileOriginalName: 'kk 2024-08-29 10-活动.mp3',
|
|
||||||
audioFileExtension: 'mp3',
|
|
||||||
audioFileSize: 79488,
|
|
||||||
remarks: '客户对产品价格比较关注,需要进一步跟进',
|
|
||||||
recordingText: '销售:您好,我是XX汽车销售顾问小王,很高兴为您服务。请问您今天是想了解哪款车型呢?客户:我想看看你们的新款SUV,听说性价比不错。销售:好的,这款车确实很受欢迎。请问您平时用车主要是家庭出行还是商务用途?客户:主要是家庭用,周末会带家人出去玩。销售:那这款车的空间和舒适性很适合您。您比较关注哪些方面?是动力、油耗还是配置?客户:油耗比较重要,现在油价这么高。销售:理解您的考虑。这款车搭载了最新的混动技术,百公里油耗只有6.5升,比同级别车型省油30%。而且还有智能驾驶辅助系统,让您开车更安全。客户:听起来不错,价格怎么样?销售:现在有优惠活动,原价25万,现在只要22.8万,还送3年免费保养。您觉得怎么样?'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
recordingName: '客户咨询录音_002',
|
|
||||||
recordingTime: '2025-08-08 11:20:30',
|
|
||||||
salesName: '粤语销售',
|
|
||||||
duration: 25,
|
|
||||||
customerName: '李女士',
|
|
||||||
customerPhone: '13900139000',
|
|
||||||
uploadTime: '2025-08-08 11:25:45',
|
|
||||||
intentionLevel: 'B',
|
|
||||||
dealershipName: 'DCC演示',
|
|
||||||
projectName: '广丰项目',
|
|
||||||
speechModel: 'GPT-3.5',
|
|
||||||
uploadStatus: '已上传',
|
|
||||||
syncStatus: '已同步',
|
|
||||||
isMerged: true,
|
|
||||||
audioFileUrl: '/api/audio/1953606689980715011_db3c711d-e581-49bb-814f-4709c0431022.mp3',
|
|
||||||
audioFileOriginalName: '客户咨询录音_002.mp3',
|
|
||||||
audioFileExtension: 'mp3',
|
|
||||||
audioFileSize: 125000,
|
|
||||||
remarks: '客户对产品功能很感兴趣,有购买意向',
|
|
||||||
recordingText: '销售:欢迎光临,我是销售顾问小李。请问您今天想了解什么车型?客户:我想看看你们的中型轿车,预算在20万左右。销售:好的,我推荐您看看这款新上市的B级车,正好在您的预算范围内。客户:这车有什么特点?销售:这款车最大的亮点是安全配置,配备了8个安全气囊、主动刹车系统,还有360度全景影像。而且内饰用料很考究,真皮座椅,12.3寸大屏。客户:听起来不错,试驾需要预约吗?销售:不需要,现在就可以安排。您有驾照吗?客户:有的。销售:那太好了,我这就去拿钥匙,您稍等。试驾过程中我会详细介绍各项功能。'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
recordingName: '产品介绍录音_003',
|
|
||||||
recordingTime: '2025-08-08 14:15:45',
|
|
||||||
salesName: '上海销售',
|
|
||||||
duration: 18,
|
|
||||||
customerName: '王先生',
|
|
||||||
customerPhone: '13700137000',
|
|
||||||
uploadTime: '2025-08-08 14:20:10',
|
|
||||||
intentionLevel: 'C',
|
|
||||||
dealershipName: '销售演示-奔驰',
|
|
||||||
projectName: '销售部',
|
|
||||||
speechModel: 'GPT-4',
|
|
||||||
uploadStatus: '上传中',
|
|
||||||
syncStatus: '未同步',
|
|
||||||
isMerged: false,
|
|
||||||
audioFileUrl: '',
|
|
||||||
audioFileOriginalName: '',
|
|
||||||
audioFileExtension: '',
|
|
||||||
audioFileSize: 0,
|
|
||||||
remarks: '客户需要更多产品详细信息',
|
|
||||||
recordingText: ''
|
|
||||||
}
|
|
||||||
]
|
|
||||||
this.total = this.audioList.length
|
|
||||||
this.loading = false
|
this.loading = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 处理待处理的分支机构名称
|
||||||
|
handlePendingDealershipName() {
|
||||||
|
if (!this.pendingDealershipName) return
|
||||||
|
|
||||||
|
// 根据分支机构名称找到对应的ID
|
||||||
|
const matchedDealership = this.dealershipOptions.find(
|
||||||
|
item => item.label === this.pendingDealershipName
|
||||||
|
)
|
||||||
|
|
||||||
|
if (matchedDealership) {
|
||||||
|
this.queryParams.dealershipId = matchedDealership.value
|
||||||
|
console.log('已设置分支机构ID:', matchedDealership.value, matchedDealership.label)
|
||||||
|
} else {
|
||||||
|
console.warn('未找到匹配的分支机构:', this.pendingDealershipName)
|
||||||
|
this.$message.warning(`未找到分支机构"${this.pendingDealershipName}",请手动选择`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除待处理的分支机构名称
|
||||||
|
this.pendingDealershipName = ''
|
||||||
|
},
|
||||||
|
|
||||||
// 获取门店选项
|
// 获取门店选项
|
||||||
getDealershipOptions() {
|
getDealershipOptions() {
|
||||||
getDealershipList({ size: 1000 }).then(response => {
|
getDealershipList({ size: 1000 }).then(response => {
|
||||||
@@ -1231,6 +1218,18 @@ export default {
|
|||||||
value: item.id,
|
value: item.id,
|
||||||
label: item.dealershipName || item.name
|
label: item.dealershipName || item.name
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
// 处理待处理的分支机构名称
|
||||||
|
if (this.pendingDealershipName) {
|
||||||
|
this.handlePendingDealershipName()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否需要自动查询
|
||||||
|
if (this.autoQueryAfterOptionsLoaded) {
|
||||||
|
this.autoQueryAfterOptionsLoaded = false
|
||||||
|
console.log('选项加载完成,自动执行查询')
|
||||||
|
this.getList()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error('获取门店选项失败:', response.message)
|
console.error('获取门店选项失败:', response.message)
|
||||||
}
|
}
|
||||||
@@ -1243,6 +1242,13 @@ export default {
|
|||||||
{ value: '3', label: '销售演示-奔驰' },
|
{ value: '3', label: '销售演示-奔驰' },
|
||||||
{ value: '4', label: '销售演示-比亚迪' }
|
{ value: '4', label: '销售演示-比亚迪' }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// 检查是否需要自动查询
|
||||||
|
if (this.autoQueryAfterOptionsLoaded) {
|
||||||
|
this.autoQueryAfterOptionsLoaded = false
|
||||||
|
console.log('选项加载完成(模拟数据),自动执行查询')
|
||||||
|
this.getList()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1262,9 +1268,7 @@ export default {
|
|||||||
console.error('获取项目选项错误:', error)
|
console.error('获取项目选项错误:', error)
|
||||||
// 开发环境使用模拟数据
|
// 开发环境使用模拟数据
|
||||||
this.projectOptions = [
|
this.projectOptions = [
|
||||||
{ value: '1', label: '电话接访话术' },
|
{ value: '1', label: '电话接访话术' }
|
||||||
{ value: '2', label: '广丰项目' },
|
|
||||||
{ value: '3', label: '销售部' }
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -1301,6 +1305,7 @@ export default {
|
|||||||
|
|
||||||
// 筛选查询
|
// 筛选查询
|
||||||
handleQuery() {
|
handleQuery() {
|
||||||
|
console.log('点击筛选按钮,当前查询参数:', this.queryParams)
|
||||||
this.queryParams.current = 1
|
this.queryParams.current = 1
|
||||||
this.getList()
|
this.getList()
|
||||||
},
|
},
|
||||||
@@ -1310,11 +1315,11 @@ export default {
|
|||||||
this.$refs.queryForm.resetFields()
|
this.$refs.queryForm.resetFields()
|
||||||
this.queryParams = {
|
this.queryParams = {
|
||||||
current: 1,
|
current: 1,
|
||||||
size: 20,
|
size: 10,
|
||||||
recordingName: '',
|
recordingName: '',
|
||||||
dealershipId: '',
|
dealershipId: '',
|
||||||
projectId: '',
|
projectId: '',
|
||||||
salesId: '',
|
salesPhone: '',
|
||||||
customerName: '',
|
customerName: '',
|
||||||
customerPhone: '',
|
customerPhone: '',
|
||||||
editStatus: '',
|
editStatus: '',
|
||||||
@@ -2242,6 +2247,133 @@ export default {
|
|||||||
this.textViewVisible = true
|
this.textViewVisible = true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 获取格式化的文本行数组
|
||||||
|
getFormattedLines(text) {
|
||||||
|
console.log('格式化录音文本:', 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]
|
||||||
|
console.log('文本没有换行符,按说话人模式分割:', textContent)
|
||||||
|
|
||||||
|
// 使用更精确的正则表达式来分割
|
||||||
|
// 匹配 "销售顾问:"、"客户:" 等说话人标签
|
||||||
|
const speakerRegex = /(销售顾问[::]|顾问[::]|客户[::])/g
|
||||||
|
const matches = []
|
||||||
|
let lastIndex = 0
|
||||||
|
let match
|
||||||
|
|
||||||
|
// 找到所有说话人标签的位置
|
||||||
|
while ((match = speakerRegex.exec(textContent)) !== null) {
|
||||||
|
matches.push({
|
||||||
|
index: match.index,
|
||||||
|
speaker: match[0],
|
||||||
|
text: textContent.substring(lastIndex, match.index).trim()
|
||||||
|
})
|
||||||
|
lastIndex = match.index
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加最后一段
|
||||||
|
if (lastIndex < textContent.length) {
|
||||||
|
matches.push({
|
||||||
|
index: lastIndex,
|
||||||
|
speaker: '',
|
||||||
|
text: textContent.substring(lastIndex).trim()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新组织为完整的对话行
|
||||||
|
lines = []
|
||||||
|
for (let i = 0; i < matches.length; i++) {
|
||||||
|
const current = matches[i]
|
||||||
|
if (current.speaker) {
|
||||||
|
// 找到下一个说话人标签,确定当前说话人的内容范围
|
||||||
|
let content = ''
|
||||||
|
if (i + 1 < matches.length) {
|
||||||
|
content = textContent.substring(current.index + current.speaker.length, matches[i + 1].index).trim()
|
||||||
|
} else {
|
||||||
|
content = textContent.substring(current.index + current.speaker.length).trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有当内容不为空时才添加这一行
|
||||||
|
if (content && content.length > 0) {
|
||||||
|
lines.push(current.speaker + content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('分割后的行数:', lines.length)
|
||||||
|
console.log('分割后的内容:', lines)
|
||||||
|
|
||||||
|
return lines.map(line => {
|
||||||
|
line = line.trim()
|
||||||
|
console.log('处理行:', line)
|
||||||
|
|
||||||
|
// 识别说话人类型
|
||||||
|
if (line.startsWith('销售顾问:') || line.startsWith('销售顾问:') || line.startsWith('顾问:') || line.startsWith('顾问:')) {
|
||||||
|
const content = line.replace(/^(销售顾问[::]|顾问[::])/, '')
|
||||||
|
return {
|
||||||
|
type: 'sales',
|
||||||
|
speaker: '销售顾问:',
|
||||||
|
content: content
|
||||||
|
}
|
||||||
|
} else if (line.startsWith('客户:') || line.startsWith('客户:')) {
|
||||||
|
console.log('识别为客户文本:', line)
|
||||||
|
const content = line.replace(/^客户[::]/, '')
|
||||||
|
return {
|
||||||
|
type: 'customer',
|
||||||
|
speaker: '客户:',
|
||||||
|
content: content
|
||||||
|
}
|
||||||
|
} else if (line.includes(':') || line.includes(':')) {
|
||||||
|
// 其他说话人
|
||||||
|
const colonIndex = line.indexOf(':') !== -1 ? line.indexOf(':') : line.indexOf(':')
|
||||||
|
if (colonIndex !== -1) {
|
||||||
|
const speaker = line.substring(0, colonIndex + 1)
|
||||||
|
const content = line.substring(colonIndex + 1)
|
||||||
|
return {
|
||||||
|
type: 'other',
|
||||||
|
speaker: speaker,
|
||||||
|
content: content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 普通文本行
|
||||||
|
return {
|
||||||
|
type: 'text',
|
||||||
|
speaker: '',
|
||||||
|
content: line
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取行的CSS类
|
||||||
|
getLineClass(line) {
|
||||||
|
if (line.type === 'empty') return 'no-text'
|
||||||
|
if (line.type === 'sales') return 'speaker sales'
|
||||||
|
if (line.type === 'customer') return 'speaker customer'
|
||||||
|
if (line.type === 'other') return 'speaker other'
|
||||||
|
return 'text-line'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取说话人标签
|
||||||
|
getSpeakerLabel(line) {
|
||||||
|
return line.speaker || ''
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取说话内容
|
||||||
|
getSpeakerContent(line) {
|
||||||
|
return line.content || ''
|
||||||
|
},
|
||||||
|
|
||||||
// 复制文本
|
// 复制文本
|
||||||
copyText() {
|
copyText() {
|
||||||
if (!this.currentTextRow.recordingText) {
|
if (!this.currentTextRow.recordingText) {
|
||||||
@@ -2724,6 +2856,59 @@ export default {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 格式化录音文本样式 */
|
||||||
|
.formatted-text {
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formatted-text .speaker {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border-left: 4px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formatted-text .speaker.sales {
|
||||||
|
background-color: #f0f9ff;
|
||||||
|
border-left-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formatted-text .speaker.customer {
|
||||||
|
background-color: #f0f9f0;
|
||||||
|
border-left-color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formatted-text .speaker.other {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-left-color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formatted-text .speaker-label {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formatted-text .speaker-content {
|
||||||
|
color: #555;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formatted-text .text-line {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
padding: 4px 0;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formatted-text .no-text {
|
||||||
|
text-align: center;
|
||||||
|
color: #999;
|
||||||
|
font-style: italic;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.text-view-container {
|
.text-view-container {
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,16 @@
|
|||||||
<!-- 查询条件区域 -->
|
<!-- 查询条件区域 -->
|
||||||
<el-card class="filter-container" shadow="never">
|
<el-card class="filter-container" shadow="never">
|
||||||
<el-form ref="queryForm" :model="queryParams" :inline="true" label-width="100px">
|
<el-form ref="queryForm" :model="queryParams" :inline="true" label-width="100px">
|
||||||
<el-form-item label="选择门店">
|
<el-form-item label="选择分支机构">
|
||||||
<el-select v-model="queryParams.storeId" placeholder="请选择所属门店" clearable>
|
<el-select
|
||||||
|
v-model="queryParams.storeId"
|
||||||
|
placeholder="请选择或输入分支机构名称"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
allow-create
|
||||||
|
default-first-option
|
||||||
|
style="width: 200px"
|
||||||
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in storeOptions"
|
v-for="item in storeOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
@@ -23,6 +31,15 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="销售电话">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.salesPhone"
|
||||||
|
placeholder="请输入销售电话"
|
||||||
|
clearable
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="销售创建时间">
|
<el-form-item label="销售创建时间">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="queryParams.createTimeRange"
|
v-model="queryParams.createTimeRange"
|
||||||
@@ -36,7 +53,15 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="所属项目">
|
<el-form-item label="所属项目">
|
||||||
<el-select v-model="queryParams.projectId" placeholder="请选择所属项目" clearable>
|
<el-select
|
||||||
|
v-model="queryParams.projectId"
|
||||||
|
placeholder="请选择或输入项目名称"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
allow-create
|
||||||
|
default-first-option
|
||||||
|
style="width: 200px"
|
||||||
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in projectOptions"
|
v-for="item in projectOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
@@ -93,7 +118,7 @@
|
|||||||
<el-table-column label="平均录音时间(分钟)" prop="avgRecordingTime" sortable min-width="150" />
|
<el-table-column label="平均录音时间(分钟)" prop="avgRecordingTime" sortable min-width="150" />
|
||||||
<el-table-column label="录音总时长(小时)" prop="totalRecordingTime" sortable min-width="150" />
|
<el-table-column label="录音总时长(小时)" prop="totalRecordingTime" sortable min-width="150" />
|
||||||
<el-table-column label="所属项目" prop="projectName" min-width="150" />
|
<el-table-column label="所属项目" prop="projectName" min-width="150" />
|
||||||
<el-table-column label="所属门店" prop="dealershipName" min-width="150" />
|
<el-table-column label="所属分支机构" prop="dealershipName" min-width="150" />
|
||||||
<el-table-column label="角色" prop="role" min-width="100" />
|
<el-table-column label="角色" prop="role" min-width="100" />
|
||||||
<el-table-column label="创建时间" prop="createTime" min-width="150" />
|
<el-table-column label="创建时间" prop="createTime" min-width="150" />
|
||||||
<el-table-column label="最近录音时间" prop="lastRecordingTime" min-width="150" />
|
<el-table-column label="最近录音时间" prop="lastRecordingTime" min-width="150" />
|
||||||
@@ -143,7 +168,14 @@
|
|||||||
<el-input v-model="salesForm.loginAccount" placeholder="请输入登录账号" />
|
<el-input v-model="salesForm.loginAccount" placeholder="请输入登录账号" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="所属项目" prop="projectId">
|
<el-form-item label="所属项目" prop="projectId">
|
||||||
<el-select v-model="salesForm.projectId" placeholder="请选择项目" style="width: 100%">
|
<el-select
|
||||||
|
v-model="salesForm.projectId"
|
||||||
|
placeholder="请选择或输入项目名称"
|
||||||
|
style="width: 100%"
|
||||||
|
filterable
|
||||||
|
allow-create
|
||||||
|
default-first-option
|
||||||
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in projectOptions"
|
v-for="item in projectOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
@@ -152,8 +184,15 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="所属门店" prop="dealershipId">
|
<el-form-item label="所属分支机构" prop="dealershipId">
|
||||||
<el-select v-model="salesForm.dealershipId" placeholder="请选择所属门店" style="width: 100%">
|
<el-select
|
||||||
|
v-model="salesForm.dealershipId"
|
||||||
|
placeholder="请选择或输入分支机构名称"
|
||||||
|
style="width: 100%"
|
||||||
|
filterable
|
||||||
|
allow-create
|
||||||
|
default-first-option
|
||||||
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in storeOptions"
|
v-for="item in storeOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
@@ -200,13 +239,14 @@ export default {
|
|||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
salesName: '',
|
salesName: '',
|
||||||
|
salesPhone: '',
|
||||||
storeId: '',
|
storeId: '',
|
||||||
projectId: '',
|
projectId: '',
|
||||||
status: '',
|
status: '',
|
||||||
createTimeRange: [],
|
createTimeRange: [],
|
||||||
recordingTimeRange: []
|
recordingTimeRange: []
|
||||||
},
|
},
|
||||||
// 门店选项
|
// 分支机构选项
|
||||||
storeOptions: [],
|
storeOptions: [],
|
||||||
// 项目选项
|
// 项目选项
|
||||||
projectOptions: [],
|
projectOptions: [],
|
||||||
@@ -225,7 +265,7 @@ export default {
|
|||||||
salesRules: {
|
salesRules: {
|
||||||
salesName: [{ required: true, message: '请输入销售名称', trigger: 'blur' }],
|
salesName: [{ required: true, message: '请输入销售名称', trigger: 'blur' }],
|
||||||
loginAccount: [{ required: true, message: '请输入登录账号', trigger: 'blur' }],
|
loginAccount: [{ required: true, message: '请输入登录账号', trigger: 'blur' }],
|
||||||
dealershipId: [{ required: true, message: '请选择所属门店', trigger: 'change' }],
|
dealershipId: [{ required: true, message: '请选择所属分支机构', trigger: 'change' }],
|
||||||
role: [{ required: true, message: '请选择角色', trigger: 'change' }],
|
role: [{ required: true, message: '请选择角色', trigger: 'change' }],
|
||||||
status: [{ required: true, message: '请选择销售状态', trigger: 'change' }]
|
status: [{ required: true, message: '请选择销售状态', trigger: 'change' }]
|
||||||
}
|
}
|
||||||
@@ -311,7 +351,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取门店选项
|
// 获取分支机构选项
|
||||||
getStoreOptions() {
|
getStoreOptions() {
|
||||||
getDealershipList().then(response => {
|
getDealershipList().then(response => {
|
||||||
this.storeOptions = response.data.map(item => ({
|
this.storeOptions = response.data.map(item => ({
|
||||||
@@ -359,6 +399,7 @@ export default {
|
|||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
salesName: '',
|
salesName: '',
|
||||||
|
salesPhone: '',
|
||||||
storeId: '',
|
storeId: '',
|
||||||
projectId: '',
|
projectId: '',
|
||||||
status: '',
|
status: '',
|
||||||
@@ -388,7 +429,22 @@ export default {
|
|||||||
// 查看录音
|
// 查看录音
|
||||||
handleViewRecordings(row) {
|
handleViewRecordings(row) {
|
||||||
console.log('查看录音', row)
|
console.log('查看录音', row)
|
||||||
this.$message.info('查看录音功能待实现')
|
|
||||||
|
// 检查是否有销售电话
|
||||||
|
if (!row.loginAccount || row.loginAccount.trim() === '') {
|
||||||
|
this.$message.warning('该销售暂无电话信息,无法查看录音')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转到录音管理页面,并传递销售电话参数
|
||||||
|
this.$router.push({
|
||||||
|
path: '/sales/audio',
|
||||||
|
query: {
|
||||||
|
salesPhone: row.loginAccount,
|
||||||
|
salesName: row.salesName || '',
|
||||||
|
dealershipName: row.dealershipName || ''
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 编辑
|
// 编辑
|
||||||
|
|||||||
Reference in New Issue
Block a user