音频管理和维度分析

This commit is contained in:
ZLI263
2025-09-29 22:30:48 +08:00
parent f6fedde26e
commit 8a98492c2a
4 changed files with 842 additions and 50 deletions

View File

@@ -54,18 +54,7 @@ export const constantRoutes = [
meta: { title: '首页', icon: 'dashboard', affix: true }
}]
},
{
path: '/audio',
component: Layout,
children: [
{
path: 'index',
name: 'Audio',
component: () => import('@/views/audio/index'),
meta: { title: '录音管理', icon: 'el-icon-microphone' }
}
]
},
{
path: '/audio-statistics',
component: Layout,
@@ -94,12 +83,21 @@ export const constantRoutes = [
{
path: '/sales',
component: Layout,
redirect: '/sales/index',
name: 'SalesRoot',
meta: { title: '销售管理', icon: 'el-icon-s-custom' },
children: [
{
path: 'index',
name: 'Sales',
component: () => import('@/views/sales/index'),
meta: { title: '销售管理', icon: 'el-icon-s-custom' }
},
{
path: 'audio',
name: 'SalesAudio',
component: () => import('@/views/audio/index'),
meta: { title: '录音管理', icon: 'el-icon-microphone' }
}
]
},
@@ -123,7 +121,7 @@ export const constantRoutes = [
redirect: '/dealership/index',
name: 'Dealership',
meta: {
title: '门店管理',
title: '分支机构',
icon: 'el-icon-s-shop'
},
children: [
@@ -131,7 +129,7 @@ export const constantRoutes = [
path: 'index',
name: 'DealershipIndex',
component: () => import('@/views/dealership/index'),
meta: { title: '门店管理', icon: 'el-icon-s-shop' }
meta: { title: '分支机构', icon: 'el-icon-s-shop' }
},
{
path: 'brand-analysis',

View File

@@ -189,6 +189,30 @@
</el-tag>
</template>
</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">
<template slot-scope="scope">
<div v-if="scope.row.audioFileUrl">
@@ -245,7 +269,7 @@
</div>
</template>
</el-table-column>
<el-table-column label="操作" width="280" fixed="right">
<el-table-column label="操作" width="350" fixed="right">
<template slot-scope="scope">
<el-button
v-if="scope.row.audioFileUrl"
@@ -266,6 +290,13 @@
</el-button>
<el-button type="text" @click="handleView(scope.row)">查看</el-button>
<el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
<el-button
type="text"
icon="el-icon-data-analysis"
@click="handleDimensionAnalysis(scope.row)"
>
维度分析
</el-button>
<el-button type="text" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
@@ -587,8 +618,8 @@
<el-button
type="success"
icon="el-icon-download"
@click="testDownload"
style="margin-right: 10px;"
@click="testDownload"
>
测试下载
</el-button>
@@ -617,6 +648,193 @@
</div>
</el-dialog>
<!-- 录音文本查看对话框 -->
<el-dialog
title="录音文本内容"
:visible.sync="textViewVisible"
width="800px"
:close-on-click-modal="false"
>
<div class="text-view-container">
<div class="text-header">
<h3>{{ currentTextRow.recordingName }}</h3>
<p><strong>客户</strong>{{ currentTextRow.customerName }} ({{ currentTextRow.customerPhone }})</p>
<p><strong>销售</strong>{{ currentTextRow.salesName }}</p>
<p><strong>录音时间</strong>{{ currentTextRow.recordingTime }}</p>
</div>
<div class="text-content">
<h4>录音文本</h4>
<div class="text-display">
<pre>{{ currentTextRow.recordingText }}</pre>
</div>
</div>
<div class="text-actions">
<el-button type="primary" icon="el-icon-copy-document" @click="copyText">复制文本</el-button>
<el-button type="success" icon="el-icon-data-analysis" @click="analyzeFromText">维度分析</el-button>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="textViewVisible = false"> </el-button>
</div>
</el-dialog>
<!-- 维度分析结果对话框 -->
<el-dialog
title="维度分析结果"
:visible.sync="dimensionAnalysisVisible"
width="800px"
:close-on-click-modal="false"
>
<div class="dimension-analysis-container">
<div v-if="dimensionAnalysisLoading" class="analysis-loading">
<i class="el-icon-loading" />
<span>正在分析中请稍候...</span>
</div>
<div v-else-if="dimensionAnalysisResult" class="analysis-result">
<div class="analysis-header">
<h3>{{ currentAnalysisRow.recordingName }}</h3>
<p><strong>客户</strong>{{ currentAnalysisRow.customerName }} ({{ currentAnalysisRow.customerPhone }})</p>
<p><strong>销售</strong>{{ currentAnalysisRow.salesName }}</p>
<p><strong>录音时间</strong>{{ currentAnalysisRow.recordingTime }}</p>
</div>
<div class="analysis-content">
<!-- 基础评分 -->
<div class="score-category">
<h4>基础评分</h4>
<div class="score-grid">
<div class="score-item">
<span class="score-label">自然度</span>
<span class="score-value">{{ dimensionAnalysisResult.naturalnessScore }}</span>
</div>
<div class="score-item">
<span class="score-label">连贯性</span>
<span class="score-value">{{ dimensionAnalysisResult.coherenceScore }}</span>
</div>
<div class="score-item">
<span class="score-label">流畅度</span>
<span class="score-value">{{ dimensionAnalysisResult.fluencyScore }}</span>
</div>
</div>
</div>
<!-- 顾问表现评分 -->
<div class="score-category">
<h4>顾问表现</h4>
<div class="score-grid">
<div class="score-item">
<span class="score-label">主动性</span>
<span class="score-value">{{ dimensionAnalysisResult.advisorAggressivenessScore }}</span>
</div>
<div class="score-item">
<span class="score-label">一致性</span>
<span class="score-value">{{ dimensionAnalysisResult.advisorConsistencyScore }}</span>
</div>
<div class="score-item">
<span class="score-label">接待问候</span>
<span class="score-value">{{ dimensionAnalysisResult.greetingReceptionScore }}</span>
</div>
</div>
</div>
<!-- 产品介绍评分 -->
<div class="score-category">
<h4>产品介绍</h4>
<div class="score-grid">
<div class="score-item">
<span class="score-label">品牌介绍</span>
<span class="score-value">{{ dimensionAnalysisResult.brandIntroductionScore }}</span>
</div>
<div class="score-item">
<span class="score-label">安全介绍</span>
<span class="score-value">{{ dimensionAnalysisResult.safetyIntroductionScore }}</span>
</div>
<div class="score-item">
<span class="score-label">健康环保</span>
<span class="score-value">{{ dimensionAnalysisResult.healthEnvironmentIntroductionScore }}</span>
</div>
<div class="score-item">
<span class="score-label">技术介绍</span>
<span class="score-value">{{ dimensionAnalysisResult.technologyIntroductionScore }}</span>
</div>
</div>
</div>
<!-- 客户理解评分 -->
<div class="score-category">
<h4>客户理解</h4>
<div class="score-grid">
<div class="score-item">
<span class="score-label">需求理解</span>
<span class="score-value">{{ dimensionAnalysisResult.customerNeedsUnderstandingScore }}</span>
</div>
<div class="score-item">
<span class="score-label">预算理解</span>
<span class="score-value">{{ dimensionAnalysisResult.budgetUnderstandingScore }}</span>
</div>
<div class="score-item">
<span class="score-label">推荐匹配</span>
<span class="score-value">{{ dimensionAnalysisResult.recommendationScore }}</span>
</div>
</div>
</div>
<!-- 成交促进评分 -->
<div class="score-category">
<h4>成交促进</h4>
<div class="score-grid">
<div class="score-item">
<span class="score-label">试驾邀请</span>
<span class="score-value">{{ dimensionAnalysisResult.testDriveInvitationScore }}</span>
</div>
<div class="score-item">
<span class="score-label">谈判成交</span>
<span class="score-value">{{ dimensionAnalysisResult.negotiationDealScore }}</span>
</div>
</div>
</div>
<!-- 分析信息 -->
<div class="analysis-info">
<h4>分析信息</h4>
<div class="info-grid">
<div class="info-item">
<span class="info-label">状态</span>
<el-tag :type="dimensionAnalysisResult.status === 'succeeded' ? 'success' : 'danger'">
{{ dimensionAnalysisResult.status === 'succeeded' ? '成功' : '失败' }}
</el-tag>
</div>
<div class="info-item">
<span class="info-label">处理时间</span>
<span class="info-value">{{ dimensionAnalysisResult.elapsedTime }}</span>
</div>
<div class="info-item">
<span class="info-label">Token数量</span>
<span class="info-value">{{ dimensionAnalysisResult.totalTokens }}</span>
</div>
<div class="info-item">
<span class="info-label">分析时间</span>
<span class="info-value">{{ formatDateTime(dimensionAnalysisResult.createdAt) }}</span>
</div>
</div>
</div>
</div>
</div>
<div v-else class="analysis-error">
<i class="el-icon-warning" />
<span>分析失败请重试</span>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="dimensionAnalysisVisible = false"> </el-button>
<el-button type="primary" @click="handleDimensionAnalysis(currentAnalysisRow)">重新分析</el-button>
</div>
</el-dialog>
<!-- 音频播放器对话框 -->
<el-dialog
title="音频播放"
@@ -739,6 +957,7 @@ import {
allocateSales,
mergeRecordings
} from '@/api/audio'
import request from '@/utils/request'
import { getDealershipList } from '@/api/dealership'
import { getProjectList } from '@/api/project'
import { getSalesList } from '@/api/sales'
@@ -779,6 +998,14 @@ export default {
audioDuration: 0,
playbackSpeed: '1.0',
audioLoading: false,
// 维度分析相关
dimensionAnalysisVisible: false,
dimensionAnalysisLoading: false,
dimensionAnalysisResult: null,
currentAnalysisRow: {},
// 录音文本查看相关
textViewVisible: false,
currentTextRow: {},
uploadAction: '/api/audio/upload',
uploadHeaders: {
'Authorization': 'Bearer ' + this.$store.getters.token
@@ -938,7 +1165,8 @@ export default {
audioFileOriginalName: 'kk 2024-08-29 10-活动.mp3',
audioFileExtension: 'mp3',
audioFileSize: 79488,
remarks: '客户对产品价格比较关注,需要进一步跟进'
remarks: '客户对产品价格比较关注,需要进一步跟进',
recordingText: '销售您好我是XX汽车销售顾问小王很高兴为您服务。请问您今天是想了解哪款车型呢客户我想看看你们的新款SUV听说性价比不错。销售好的这款车确实很受欢迎。请问您平时用车主要是家庭出行还是商务用途客户主要是家庭用周末会带家人出去玩。销售那这款车的空间和舒适性很适合您。您比较关注哪些方面是动力、油耗还是配置客户油耗比较重要现在油价这么高。销售理解您的考虑。这款车搭载了最新的混动技术百公里油耗只有6.5升比同级别车型省油30%。而且还有智能驾驶辅助系统让您开车更安全。客户听起来不错价格怎么样销售现在有优惠活动原价25万现在只要22.8万还送3年免费保养。您觉得怎么样'
},
{
id: '2',
@@ -960,7 +1188,8 @@ export default {
audioFileOriginalName: '客户咨询录音_002.mp3',
audioFileExtension: 'mp3',
audioFileSize: 125000,
remarks: '客户对产品功能很感兴趣,有购买意向'
remarks: '客户对产品功能很感兴趣,有购买意向',
recordingText: '销售欢迎光临我是销售顾问小李。请问您今天想了解什么车型客户我想看看你们的中型轿车预算在20万左右。销售好的我推荐您看看这款新上市的B级车正好在您的预算范围内。客户这车有什么特点销售这款车最大的亮点是安全配置配备了8个安全气囊、主动刹车系统还有360度全景影像。而且内饰用料很考究真皮座椅12.3寸大屏。客户:听起来不错,试驾需要预约吗?销售:不需要,现在就可以安排。您有驾照吗?客户:有的。销售:那太好了,我这就去拿钥匙,您稍等。试驾过程中我会详细介绍各项功能。'
},
{
id: '3',
@@ -982,7 +1211,8 @@ export default {
audioFileOriginalName: '',
audioFileExtension: '',
audioFileSize: 0,
remarks: '客户需要更多产品详细信息'
remarks: '客户需要更多产品详细信息',
recordingText: ''
}
]
this.total = this.audioList.length
@@ -1886,6 +2116,191 @@ export default {
}
document.body.removeChild(textArea)
},
// 维度分析
handleDimensionAnalysis(row) {
console.log('维度分析 - 录音数据:', row)
// 检查是否有录音文本内容
if (!row.recordingText || row.recordingText.trim() === '') {
this.$message.warning('该录音暂无文本内容,无法进行维度分析')
return
}
// 保存当前分析的行数据
this.currentAnalysisRow = { ...row }
// 显示弹框并开始分析
this.dimensionAnalysisVisible = true
this.dimensionAnalysisLoading = true
this.dimensionAnalysisResult = null
// 调用维度分析API
this.callDimensionAnalysisAPI(row.recordingText)
},
// 调用维度分析API
callDimensionAnalysisAPI(recordingText) {
console.log('调用维度分析API - 录音文本:', recordingText)
// 构建请求参数 - 根据后端TopSalesScoreRequestDto格式
const requestData = {
chat: recordingText, // 必填字段,用于分析的主要内容
srcContent: recordingText, // 待分析内容(录音文本)
srcId: this.currentAnalysisRow.id, // 录音ID
srcDesc: `录音分析 - ${this.currentAnalysisRow.recordingName}` // 来源说明
}
// 使用request调用API
request({
url: '/topsales/score',
method: 'post',
data: requestData
})
.then(response => {
console.log('维度分析API响应:', response)
this.dimensionAnalysisLoading = false
if (response && response.code === 20000) {
this.dimensionAnalysisResult = response.data
this.$message.success('维度分析完成')
} else {
this.$message.error(response?.message || '维度分析失败')
this.dimensionAnalysisResult = null
}
})
.catch(error => {
console.error('维度分析API调用失败:', error)
this.dimensionAnalysisLoading = false
this.dimensionAnalysisResult = null
// 模拟API响应用于演示
this.simulateDimensionAnalysisResult()
})
},
// 模拟维度分析结果(用于演示)
simulateDimensionAnalysisResult() {
console.log('使用模拟数据展示维度分析结果')
this.dimensionAnalysisResult = {
overallScore: 85,
dimensions: [
{
name: '沟通技巧',
score: 88,
description: '销售人员在沟通过程中表现出良好的倾听能力和表达能力,能够准确把握客户需求。',
suggestions: [
'可以更多地使用开放式问题来了解客户需求',
'在关键信息确认时可以重复客户的话以确保理解准确'
]
},
{
name: '产品知识',
score: 82,
description: '销售人员对产品特性了解较为全面,能够准确回答客户的基本问题。',
suggestions: [
'建议加强对竞品差异化的了解',
'可以准备更多产品使用场景的案例'
]
},
{
name: '客户关系',
score: 90,
description: '与客户建立了良好的信任关系,客户反馈积极,互动自然流畅。',
suggestions: [
'继续保持这种良好的客户关系维护方式',
'可以适当增加一些个性化的关怀'
]
},
{
name: '成交技巧',
score: 75,
description: '在引导客户决策方面还有提升空间,可以更主动地推进成交。',
suggestions: [
'学习更多的成交技巧和话术',
'可以尝试使用假设成交法来测试客户意向',
'在适当时机可以主动提出下一步行动'
]
}
],
summary: '整体表现良好,在客户关系维护方面表现突出,建议在成交技巧方面进行重点提升。销售人员具备良好的沟通能力和产品知识基础,通过针对性的培训可以进一步提升销售效果。'
}
this.$message.success('维度分析完成(演示数据)')
},
// 获取文本预览(截断显示)
getTextPreview(text) {
if (!text || text.trim() === '') return ''
return text.length > 10 ? text.substring(0, 10) + '...' : text
},
// 查看录音文本
handleViewText(row) {
console.log('查看录音文本 - 录音数据:', row)
this.currentTextRow = { ...row }
this.textViewVisible = true
},
// 复制文本
copyText() {
if (!this.currentTextRow.recordingText) {
this.$message.warning('没有可复制的文本内容')
return
}
const text = this.currentTextRow.recordingText
// 使用现代浏览器的 Clipboard API
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text).then(() => {
this.$message.success('文本已复制到剪贴板')
}).catch(() => {
this.fallbackCopyTextToClipboard(text)
})
} else {
this.fallbackCopyTextToClipboard(text)
}
},
// 从文本查看弹框进行维度分析
analyzeFromText() {
if (!this.currentTextRow.recordingText || this.currentTextRow.recordingText.trim() === '') {
this.$message.warning('该录音暂无文本内容,无法进行维度分析')
return
}
// 关闭文本查看弹框
this.textViewVisible = false
// 保存当前分析的行数据
this.currentAnalysisRow = { ...this.currentTextRow }
// 显示维度分析弹框并开始分析
this.dimensionAnalysisVisible = true
this.dimensionAnalysisLoading = true
this.dimensionAnalysisResult = null
// 调用维度分析API
this.callDimensionAnalysisAPI(this.currentTextRow.recordingText)
},
// 格式化日期时间
formatDateTime(dateTimeStr) {
if (!dateTimeStr) return ''
try {
const date = new Date(dateTimeStr)
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
} catch (error) {
return dateTimeStr
}
}
}
}
@@ -2087,4 +2502,383 @@ export default {
margin-bottom: 5px;
line-height: 1.5;
}
/* 维度分析样式 */
.dimension-analysis-container {
padding: 20px 0;
}
.analysis-loading {
text-align: center;
padding: 40px 20px;
}
.analysis-loading i {
font-size: 32px;
color: #409eff;
margin-bottom: 15px;
display: block;
}
.analysis-loading span {
color: #606266;
font-size: 16px;
}
.analysis-result {
max-height: 600px;
overflow-y: auto;
}
.analysis-header {
margin-bottom: 20px;
padding: 15px;
background-color: #f5f7fa;
border-radius: 4px;
border-left: 4px solid #409eff;
}
.analysis-header h3 {
margin: 0 0 10px 0;
color: #303133;
font-size: 18px;
}
.analysis-header p {
margin: 5px 0;
color: #606266;
font-size: 14px;
}
.analysis-content {
padding: 0 10px;
}
.score-section {
margin-bottom: 25px;
text-align: center;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 8px;
color: white;
}
.score-section h4 {
margin: 0 0 15px 0;
font-size: 16px;
font-weight: normal;
}
.score-display {
display: flex;
align-items: baseline;
justify-content: center;
gap: 5px;
}
.score-value {
font-size: 48px;
font-weight: bold;
line-height: 1;
}
.score-label {
font-size: 18px;
opacity: 0.8;
}
.dimensions-section {
margin-bottom: 25px;
}
.dimensions-section h4 {
margin: 0 0 15px 0;
color: #303133;
font-size: 16px;
border-bottom: 2px solid #409eff;
padding-bottom: 8px;
}
.dimensions-list {
display: flex;
flex-direction: column;
gap: 15px;
}
.dimension-item {
border: 1px solid #e4e7ed;
border-radius: 6px;
padding: 15px;
background-color: #fafafa;
transition: all 0.3s ease;
}
.dimension-item:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border-color: #409eff;
}
.dimension-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.dimension-name {
font-weight: bold;
color: #303133;
font-size: 16px;
}
.dimension-score {
background-color: #409eff;
color: white;
padding: 4px 12px;
border-radius: 12px;
font-size: 14px;
font-weight: bold;
}
.dimension-details {
margin-top: 10px;
}
.dimension-description {
margin: 0 0 10px 0;
color: #606266;
line-height: 1.6;
font-size: 14px;
}
.dimension-suggestions {
margin-top: 10px;
padding: 10px;
background-color: #f0f9ff;
border-radius: 4px;
border-left: 3px solid #409eff;
}
.dimension-suggestions strong {
color: #303133;
font-size: 14px;
}
.dimension-suggestions ul {
margin: 8px 0 0 0;
padding-left: 20px;
}
.dimension-suggestions li {
margin-bottom: 5px;
color: #606266;
font-size: 13px;
line-height: 1.5;
}
.summary-section {
margin-top: 25px;
padding: 20px;
background-color: #f8f9fa;
border-radius: 6px;
border-left: 4px solid #67c23a;
}
.summary-section h4 {
margin: 0 0 15px 0;
color: #303133;
font-size: 16px;
}
.summary-content {
margin: 0;
color: #606266;
line-height: 1.8;
font-size: 14px;
}
.analysis-error {
text-align: center;
padding: 40px 20px;
color: #f56c6c;
}
.analysis-error i {
font-size: 32px;
margin-bottom: 15px;
display: block;
}
.analysis-error span {
font-size: 16px;
}
/* 录音文本相关样式 */
.recording-text-preview {
color: #606266;
font-size: 14px;
display: inline-block;
max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.text-view-container {
padding: 20px 0;
}
.text-header {
margin-bottom: 20px;
padding: 15px;
background-color: #f5f7fa;
border-radius: 4px;
border-left: 4px solid #409eff;
}
.text-header h3 {
margin: 0 0 10px 0;
color: #303133;
font-size: 18px;
}
.text-header p {
margin: 5px 0;
color: #606266;
font-size: 14px;
}
.text-content {
margin-bottom: 20px;
}
.text-content h4 {
margin: 0 0 15px 0;
color: #303133;
font-size: 16px;
border-bottom: 2px solid #409eff;
padding-bottom: 8px;
}
.text-display {
border: 1px solid #e4e7ed;
border-radius: 6px;
padding: 15px;
background-color: #fafafa;
max-height: 400px;
overflow-y: auto;
}
.text-display pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
font-size: 14px;
line-height: 1.6;
color: #303133;
}
.text-actions {
display: flex;
gap: 10px;
justify-content: center;
padding: 15px 0;
border-top: 1px solid #e4e7ed;
}
/* 维度分析分类样式 */
.score-category {
margin-bottom: 25px;
border: 1px solid #e4e7ed;
border-radius: 8px;
overflow: hidden;
}
.score-category h4 {
margin: 0;
padding: 15px 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-size: 16px;
font-weight: 600;
}
.score-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 0;
}
.score-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 20px;
border-bottom: 1px solid #f0f0f0;
transition: background-color 0.3s ease;
}
.score-item:last-child {
border-bottom: none;
}
.score-item:hover {
background-color: #f8f9fa;
}
.score-item:nth-child(even) {
background-color: #fafafa;
}
.score-label {
font-weight: 500;
color: #303133;
font-size: 14px;
}
.score-value {
font-weight: 600;
color: #409eff;
font-size: 16px;
}
.analysis-info {
margin-top: 25px;
padding: 20px;
background-color: #f8f9fa;
border-radius: 8px;
border-left: 4px solid #67c23a;
}
.analysis-info h4 {
margin: 0 0 15px 0;
color: #303133;
font-size: 16px;
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
}
.info-label {
font-weight: 500;
color: #606266;
font-size: 14px;
}
.info-value {
font-weight: 600;
color: #303133;
font-size: 14px;
}
</style>

View File

@@ -207,9 +207,9 @@ export default {
this.loading = true
const params = { ...this.queryParams }
// 移除空值和不需要的参数
// 移除空值
Object.keys(params).forEach(key => {
if (params[key] === '' || params[key] === null || params[key] === undefined || key === 'dateRange') {
if (params[key] === '' || params[key] === null || params[key] === undefined) {
delete params[key]
}
})

View File

@@ -3,19 +3,19 @@
<!-- 查询条件区域 -->
<el-card class="filter-container" shadow="never">
<el-form ref="queryForm" :model="queryParams" :inline="true" label-width="100px">
<el-form-item label="经销商名称">
<el-form-item label="机构名称">
<el-input
v-model="queryParams.dealershipName"
placeholder="请输入经销商名称"
placeholder="请输入经机构名称"
clearable
style="width: 200px"
/>
</el-form-item>
<el-form-item label="经销商代码">
<el-form-item label="机构代码">
<el-input
v-model="queryParams.dealershipCode"
placeholder="请输入经销商代码"
placeholder="请输入机构代码"
clearable
style="width: 200px"
/>
@@ -49,7 +49,7 @@
<!-- 操作按钮区域 -->
<el-card class="action-container" shadow="never">
<el-button @click="handleSwitchLayout">切换布局</el-button>
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">添加经销商</el-button>
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">添加机构</el-button>
</el-card>
<!-- 数据表格区域 -->
@@ -62,8 +62,8 @@
>
<el-table-column type="selection" width="55" />
<el-table-column label="序号" type="index" width="50" />
<el-table-column label="经销商" prop="dealershipName" min-width="150" />
<el-table-column label="经销商代码" prop="dealershipCode" min-width="120" />
<el-table-column label="机构名称" prop="dealershipName" min-width="150" />
<el-table-column label="机构代码" prop="dealershipCode" min-width="120" />
<el-table-column label="剩余时长/h" prop="remainingHours" sortable min-width="120">
<template slot-scope="scope">
<span :style="{ color: scope.row.remainingHours < 0 ? '#f56c6c' : '#67c23a' }">
@@ -77,7 +77,7 @@
<el-table-column label="地区" prop="region" min-width="100" />
<el-table-column label="分组" prop="groupName" min-width="100" />
<el-table-column label="语言" prop="language" min-width="100" />
<el-table-column label="经销商类型" prop="dealershipType" min-width="100" />
<el-table-column label="机构类型" prop="dealershipType" min-width="100" />
<el-table-column label="状态" prop="status" min-width="100">
<template slot-scope="scope">
<el-switch
@@ -117,11 +117,11 @@
@close="handleDialogClose"
>
<el-form ref="dealershipForm" :model="dealershipForm" :rules="dealershipRules" label-width="100px">
<el-form-item label="经销商名称" prop="dealershipName">
<el-input v-model="dealershipForm.dealershipName" placeholder="请输入经销商名称" />
<el-form-item label="机构名称" prop="dealershipName">
<el-input v-model="dealershipForm.dealershipName" placeholder="请输入经机构名称" />
</el-form-item>
<el-form-item label="经销商代码" prop="dealershipCode">
<el-input v-model="dealershipForm.dealershipCode" placeholder="请输入经销商代码" />
<el-form-item label="机构代码" prop="dealershipCode">
<el-input v-model="dealershipForm.dealershipCode" placeholder="请输入经机构代码" />
</el-form-item>
<el-form-item label="地区" prop="region">
<el-select v-model="dealershipForm.region" placeholder="请选择地区" style="width: 100%">
@@ -146,8 +146,8 @@
/>
</el-select>
</el-form-item>
<el-form-item label="经销商类型" prop="dealershipType">
<el-select v-model="dealershipForm.dealershipType" placeholder="请选择经销商类型" style="width: 100%">
<el-form-item label="机构类型" prop="dealershipType">
<el-select v-model="dealershipForm.dealershipType" placeholder="请选择机构类型" style="width: 100%">
<el-option
v-for="item in dealershipTypeOptions"
:key="item.value"
@@ -213,16 +213,16 @@ export default {
// 表单验证规则
dealershipRules: {
dealershipName: [
{ required: true, message: '请输入经销商名称', trigger: 'blur' }
{ required: true, message: '请输入机构名称', trigger: 'blur' }
],
dealershipCode: [
{ required: true, message: '请输入经销商代码', trigger: 'blur' }
{ required: true, message: '请输入机构代码', trigger: 'blur' }
],
language: [
{ required: true, message: '请选择语言', trigger: 'change' }
],
dealershipType: [
{ required: true, message: '请选择经销商类型', trigger: 'change' }
{ required: true, message: '请选择机构类型', trigger: 'change' }
],
status: [
{ required: true, message: '请选择状态', trigger: 'change' }
@@ -449,7 +449,7 @@ export default {
// 添加经销商
handleAdd() {
this.dialogTitle = '添加经销商'
this.dialogTitle = '添加机构'
this.isEdit = false
this.dealershipForm = {
id: null,
@@ -466,7 +466,7 @@ export default {
// 编辑经销商
handleEdit(row) {
this.dialogTitle = '编辑经销商'
this.dialogTitle = '编辑机构'
this.isEdit = true
this.dealershipForm = { ...row }
this.dialogVisible = true
@@ -474,7 +474,7 @@ export default {
// 删除经销商
handleDelete(row) {
this.$confirm('确认删除该经销商?', '提示', {
this.$confirm('确认删除该机构?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'