添加管理员时默认添加登录用户。登录允许手机号登录
This commit is contained in:
@@ -109,6 +109,9 @@ WantedBy=multi-user.target
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
95
src/api/ai-strategy.js
Normal file
95
src/api/ai-strategy.js
Normal file
@@ -0,0 +1,95 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 分页查询AI策略列表
|
||||
export function fetchList(params) {
|
||||
return request({
|
||||
url: '/aiStrategy/list',
|
||||
method: 'get',
|
||||
params: {
|
||||
current: params.page || 1,
|
||||
size: params.limit || 20,
|
||||
keyword: params.keyword,
|
||||
status: params.status,
|
||||
category: params.category
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 创建AI策略
|
||||
export function createStrategy(data) {
|
||||
return request({
|
||||
url: '/aiStrategy/create',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 更新AI策略
|
||||
export function updateStrategy(data) {
|
||||
return request({
|
||||
url: '/aiStrategy/update',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除AI策略
|
||||
export function deleteStrategy(id) {
|
||||
return request({
|
||||
url: `/aiStrategy/delete/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取AI策略详情
|
||||
export function getStrategy(id) {
|
||||
return request({
|
||||
url: `/aiStrategy/detail/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 切换策略状态
|
||||
export function toggleStatus(id, status) {
|
||||
return request({
|
||||
url: `/aiStrategy/toggleStatus/${id}`,
|
||||
method: 'put',
|
||||
data: { status }
|
||||
})
|
||||
}
|
||||
|
||||
// 批量启用策略
|
||||
export function batchEnable(ids) {
|
||||
return request({
|
||||
url: '/aiStrategy/batchEnable',
|
||||
method: 'put',
|
||||
data: { ids }
|
||||
})
|
||||
}
|
||||
|
||||
// 批量禁用策略
|
||||
export function batchDisable(ids) {
|
||||
return request({
|
||||
url: '/aiStrategy/batchDisable',
|
||||
method: 'put',
|
||||
data: { ids }
|
||||
})
|
||||
}
|
||||
|
||||
// 获取策略执行统计
|
||||
export function getStrategyStats(params) {
|
||||
return request({
|
||||
url: '/aiStrategy/stats',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 测试策略
|
||||
export function testStrategy(id, testData) {
|
||||
return request({
|
||||
url: `/aiStrategy/test/${id}`,
|
||||
method: 'post',
|
||||
data: testData
|
||||
})
|
||||
}
|
||||
48
src/api/call-quality-inspection.js
Normal file
48
src/api/call-quality-inspection.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 分页查询通话质检列表
|
||||
export function fetchList(params) {
|
||||
return request({
|
||||
url: '/callQualityInspection/list',
|
||||
method: 'get',
|
||||
params: {
|
||||
current: params.page || 1,
|
||||
size: params.limit || 20,
|
||||
keyword: params.keyword
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 创建通话质检记录
|
||||
export function createRecord(data) {
|
||||
return request({
|
||||
url: '/callQualityInspection/create',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 更新通话质检记录
|
||||
export function updateRecord(data) {
|
||||
return request({
|
||||
url: '/callQualityInspection/update',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除通话质检记录
|
||||
export function deleteRecord(id) {
|
||||
return request({
|
||||
url: `/callQualityInspection/delete/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取通话质检详情
|
||||
export function getRecord(id) {
|
||||
return request({
|
||||
url: `/callQualityInspection/detail/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="right-menu">
|
||||
<el-dropdown class="avatar-container" trigger="click">
|
||||
<div class="avatar-wrapper">
|
||||
<img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar">
|
||||
<span class="user-name">{{ name || 'userName' }}</span>
|
||||
<i class="el-icon-caret-bottom" />
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown" class="user-dropdown">
|
||||
@@ -44,7 +44,8 @@ export default {
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'sidebar',
|
||||
'avatar'
|
||||
'avatar',
|
||||
'name'
|
||||
])
|
||||
},
|
||||
methods: {
|
||||
@@ -117,12 +118,25 @@ export default {
|
||||
.avatar-wrapper {
|
||||
margin-top: 5px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
.user-avatar {
|
||||
cursor: pointer;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
margin-right: 8px;
|
||||
max-width: 100px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.el-icon-caret-bottom {
|
||||
|
||||
@@ -86,6 +86,18 @@ export const constantRoutes = [
|
||||
name: 'IntelligentPickup',
|
||||
component: () => import('@/views/sales/intelligent-pickup'),
|
||||
meta: { title: '智能捡漏', icon: 'el-icon-magic-stick' }
|
||||
},
|
||||
{
|
||||
path: 'call-quality-inspection',
|
||||
name: 'CallQualityInspection',
|
||||
component: () => import('@/views/sales/call-quality-inspection'),
|
||||
meta: { title: '通话质检', icon: 'el-icon-phone' }
|
||||
},
|
||||
{
|
||||
path: 'ai-strategy',
|
||||
name: 'AiStrategy',
|
||||
component: () => import('@/views/sales/ai-strategy'),
|
||||
meta: { title: 'AI策略', icon: 'el-icon-cpu' }
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -124,12 +136,6 @@ export const constantRoutes = [
|
||||
name: 'BrandAnalysis',
|
||||
component: () => import('@/views/dealership/brand-analysis'),
|
||||
meta: { title: '品牌分析', icon: 'el-icon-data-analysis' }
|
||||
},
|
||||
{
|
||||
path: 'customer-profile',
|
||||
name: 'CustomerProfile',
|
||||
component: () => import('@/views/dealership/customer-profile'),
|
||||
meta: { title: '客户画像', icon: 'el-icon-user-solid' }
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -165,6 +171,12 @@ export const constantRoutes = [
|
||||
name: 'CustomerManagement',
|
||||
component: () => import('@/views/customer/index'),
|
||||
meta: { title: '客户管理', icon: 'el-icon-user' }
|
||||
},
|
||||
{
|
||||
path: 'customer-profile',
|
||||
name: 'CustomerProfile',
|
||||
component: () => import('@/views/dealership/customer-profile'),
|
||||
meta: { title: '客户画像', icon: 'el-icon-user-solid' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -16,5 +16,11 @@ export function isExternal(path) {
|
||||
*/
|
||||
export function validUsername(str) {
|
||||
const valid_map = ['admin', 'editor']
|
||||
return valid_map.indexOf(str.trim()) >= 0
|
||||
// 支持原有的用户名
|
||||
if (valid_map.indexOf(str.trim()) >= 0) {
|
||||
return true
|
||||
}
|
||||
// 支持手机号格式(11位数字,以1开头)
|
||||
const phoneRegex = /^1[3-9]\d{9}$/
|
||||
return phoneRegex.test(str.trim())
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<el-input
|
||||
ref="username"
|
||||
v-model="loginForm.username"
|
||||
placeholder="请输入用户名"
|
||||
placeholder="请输入手机号或用户名"
|
||||
name="username"
|
||||
type="text"
|
||||
tabindex="1"
|
||||
@@ -55,7 +55,7 @@ export default {
|
||||
data() {
|
||||
const validateUsername = (rule, value, callback) => {
|
||||
if (!validUsername(value)) {
|
||||
callback(new Error('请输入用户名'))
|
||||
callback(new Error('请输入正确的手机号或用户名'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
|
||||
590
src/views/sales/ai-strategy.vue
Normal file
590
src/views/sales/ai-strategy.vue
Normal file
@@ -0,0 +1,590 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="filter-container">
|
||||
<el-input
|
||||
v-model="listQuery.keyword"
|
||||
placeholder="请输入策略名称或关键词搜索"
|
||||
style="width: 200px;"
|
||||
class="filter-item"
|
||||
@keyup.enter.native="handleFilter"
|
||||
/>
|
||||
<el-select
|
||||
v-model="listQuery.status"
|
||||
placeholder="策略状态"
|
||||
clearable
|
||||
style="width: 120px;"
|
||||
class="filter-item"
|
||||
>
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="启用" value="active" />
|
||||
<el-option label="禁用" value="inactive" />
|
||||
<el-option label="草稿" value="draft" />
|
||||
</el-select>
|
||||
<el-select
|
||||
v-model="listQuery.category"
|
||||
placeholder="策略类型"
|
||||
clearable
|
||||
style="width: 120px;"
|
||||
class="filter-item"
|
||||
>
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="销售策略" value="sales" />
|
||||
<el-option label="客户分析" value="customer" />
|
||||
<el-option label="话术优化" value="script" />
|
||||
<el-option label="跟进提醒" value="followup" />
|
||||
</el-select>
|
||||
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-plus" @click="handleCreate">
|
||||
新增策略
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
:key="tableKey"
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
style="width: 100%;"
|
||||
>
|
||||
<el-table-column label="ID" prop="id" sortable="custom" align="center" width="80">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{ row.id }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="策略名称" min-width="150px">
|
||||
<template slot-scope="{row}">
|
||||
<span class="link-type" @click="handleView(row)">{{ row.strategyName }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="策略类型" width="100px" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag :type="getCategoryType(row.category)">
|
||||
{{ getCategoryText(row.category) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="策略描述" min-width="200px">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{ row.description }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="适用场景" width="120px" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{ row.scenario }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="优先级" width="80px" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag :type="getPriorityType(row.priority)">
|
||||
{{ row.priority }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="80px" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag :type="getStatusType(row.status)">
|
||||
{{ getStatusText(row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" width="150px" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{ row.createTime | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="280" class-name="small-padding fixed-width">
|
||||
<template slot-scope="{row,$index}">
|
||||
<el-button type="primary" size="mini" @click="handleUpdate(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button size="mini" type="success" @click="handleView(row)">
|
||||
查看
|
||||
</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
:type="row.status === 'active' ? 'warning' : 'success'"
|
||||
@click="handleToggleStatus(row)"
|
||||
>
|
||||
{{ row.status === 'active' ? '禁用' : '启用' }}
|
||||
</el-button>
|
||||
<el-button size="mini" type="danger" @click="handleDelete(row,$index)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="800px">
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="100px" style="width: 100%;">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="策略名称" prop="strategyName">
|
||||
<el-input v-model="temp.strategyName" placeholder="请输入策略名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="策略类型" prop="category">
|
||||
<el-select v-model="temp.category" placeholder="请选择策略类型" style="width: 100%;">
|
||||
<el-option label="销售策略" value="sales" />
|
||||
<el-option label="客户分析" value="customer" />
|
||||
<el-option label="话术优化" value="script" />
|
||||
<el-option label="跟进提醒" value="followup" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="适用场景" prop="scenario">
|
||||
<el-input v-model="temp.scenario" placeholder="请输入适用场景" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="优先级" prop="priority">
|
||||
<el-select v-model="temp.priority" placeholder="请选择优先级" style="width: 100%;">
|
||||
<el-option label="高" value="高" />
|
||||
<el-option label="中" value="中" />
|
||||
<el-option label="低" value="低" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="策略描述" prop="description">
|
||||
<el-input v-model="temp.description" type="textarea" :rows="3" placeholder="请输入策略描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="策略内容" prop="content">
|
||||
<el-input v-model="temp.content" type="textarea" :rows="6" placeholder="请输入策略详细内容" />
|
||||
</el-form-item>
|
||||
<el-form-item label="触发条件" prop="triggerCondition">
|
||||
<el-input v-model="temp.triggerCondition" type="textarea" :rows="3" placeholder="请输入触发条件" />
|
||||
</el-form-item>
|
||||
<el-form-item label="执行动作" prop="action">
|
||||
<el-input v-model="temp.action" type="textarea" :rows="3" placeholder="请输入执行动作" />
|
||||
</el-form-item>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="temp.status" placeholder="请选择状态" style="width: 100%;">
|
||||
<el-option label="启用" value="active" />
|
||||
<el-option label="禁用" value="inactive" />
|
||||
<el-option label="草稿" value="draft" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="创建人" prop="creator">
|
||||
<el-input v-model="temp.creator" placeholder="请输入创建人" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<el-dialog title="AI策略详情" :visible.sync="dialogDetailVisible" width="900px">
|
||||
<div v-if="currentRecord">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="策略名称">{{ currentRecord.strategyName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="策略类型">
|
||||
<el-tag :type="getCategoryType(currentRecord.category)">
|
||||
{{ getCategoryText(currentRecord.category) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="适用场景">{{ currentRecord.scenario }}</el-descriptions-item>
|
||||
<el-descriptions-item label="优先级">
|
||||
<el-tag :type="getPriorityType(currentRecord.priority)">
|
||||
{{ currentRecord.priority }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<el-tag :type="getStatusType(currentRecord.status)">
|
||||
{{ getStatusText(currentRecord.status) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">{{ currentRecord.creator }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ currentRecord.createTime | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">{{ currentRecord.updateTime | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div style="margin-top: 20px;">
|
||||
<h4>策略描述:</h4>
|
||||
<p>{{ currentRecord.description }}</p>
|
||||
</div>
|
||||
<div style="margin-top: 20px;">
|
||||
<h4>策略内容:</h4>
|
||||
<p style="white-space: pre-wrap;">{{ currentRecord.content }}</p>
|
||||
</div>
|
||||
<div style="margin-top: 20px;">
|
||||
<h4>触发条件:</h4>
|
||||
<p style="white-space: pre-wrap;">{{ currentRecord.triggerCondition }}</p>
|
||||
</div>
|
||||
<div style="margin-top: 20px;">
|
||||
<h4>执行动作:</h4>
|
||||
<p style="white-space: pre-wrap;">{{ currentRecord.action }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fetchList, createStrategy, updateStrategy, deleteStrategy, toggleStatus } from '@/api/ai-strategy'
|
||||
import waves from '@/directive/waves' // waves directive
|
||||
import { parseTime } from '@/utils'
|
||||
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
|
||||
|
||||
export default {
|
||||
name: 'AiStrategy',
|
||||
components: { Pagination },
|
||||
directives: { waves },
|
||||
filters: {
|
||||
parseTime
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableKey: 0,
|
||||
list: null,
|
||||
total: 0,
|
||||
listLoading: true,
|
||||
listQuery: {
|
||||
page: 1,
|
||||
limit: 20,
|
||||
keyword: undefined,
|
||||
status: undefined,
|
||||
category: undefined
|
||||
},
|
||||
temp: {
|
||||
id: undefined,
|
||||
strategyName: '',
|
||||
category: '',
|
||||
scenario: '',
|
||||
priority: '',
|
||||
description: '',
|
||||
content: '',
|
||||
triggerCondition: '',
|
||||
action: '',
|
||||
status: 'draft',
|
||||
creator: ''
|
||||
},
|
||||
dialogFormVisible: false,
|
||||
dialogDetailVisible: false,
|
||||
dialogStatus: '',
|
||||
textMap: {
|
||||
update: '编辑AI策略',
|
||||
create: '新增AI策略'
|
||||
},
|
||||
rules: {
|
||||
strategyName: [{ required: true, message: '策略名称不能为空', trigger: 'blur' }],
|
||||
category: [{ required: true, message: '策略类型不能为空', trigger: 'change' }],
|
||||
scenario: [{ required: true, message: '适用场景不能为空', trigger: 'blur' }],
|
||||
priority: [{ required: true, message: '优先级不能为空', trigger: 'change' }],
|
||||
description: [{ required: true, message: '策略描述不能为空', trigger: 'blur' }],
|
||||
content: [{ required: true, message: '策略内容不能为空', trigger: 'blur' }],
|
||||
triggerCondition: [{ required: true, message: '触发条件不能为空', trigger: 'blur' }],
|
||||
action: [{ required: true, message: '执行动作不能为空', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '状态不能为空', trigger: 'change' }],
|
||||
creator: [{ required: true, message: '创建人不能为空', trigger: 'blur' }]
|
||||
},
|
||||
currentRecord: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.listLoading = true
|
||||
const params = { ...this.listQuery }
|
||||
|
||||
// 移除空值
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key] === '' || params[key] === null || params[key] === undefined) {
|
||||
delete params[key]
|
||||
}
|
||||
})
|
||||
|
||||
fetchList(params).then(response => {
|
||||
console.log('AI策略API响应:', response) // 调试日志
|
||||
if (response && response.code === 20000) {
|
||||
// 从response.data中获取实际的数据
|
||||
this.list = response.data.records || response.data.data || response.data || []
|
||||
this.total = response.data.total || response.data.length || 0
|
||||
} else {
|
||||
this.$message.error(response?.message || '获取AI策略列表失败')
|
||||
this.list = []
|
||||
this.total = 0
|
||||
}
|
||||
this.listLoading = false
|
||||
}).catch(error => {
|
||||
console.error('获取AI策略列表失败:', error)
|
||||
// 开发环境使用模拟数据
|
||||
this.list = [
|
||||
{
|
||||
id: 1,
|
||||
strategyName: '高意向客户跟进策略',
|
||||
category: 'sales',
|
||||
scenario: '客户表现出购买意向',
|
||||
priority: '高',
|
||||
description: '针对高意向客户的自动跟进策略',
|
||||
content: '当客户在通话中表现出强烈购买意向时,系统自动发送产品详细介绍和优惠信息,并安排销售顾问在24小时内主动联系客户。',
|
||||
triggerCondition: '客户询问价格、配置、交车时间等关键信息',
|
||||
action: '1. 发送产品详情邮件\n2. 安排销售顾问跟进\n3. 推送相关优惠活动',
|
||||
status: 'active',
|
||||
creator: '张经理',
|
||||
createTime: new Date('2024-01-15 10:30:00'),
|
||||
updateTime: new Date('2024-01-15 10:30:00')
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
strategyName: '客户情绪分析策略',
|
||||
category: 'customer',
|
||||
scenario: '客户情绪波动检测',
|
||||
priority: '中',
|
||||
description: '基于语音分析识别客户情绪变化',
|
||||
content: '通过AI语音分析技术,实时监测客户在通话中的情绪变化,当检测到客户情绪低落或不满时,及时提醒销售顾问调整沟通方式。',
|
||||
triggerCondition: '语音分析显示客户情绪指数低于阈值',
|
||||
action: '1. 发送情绪提醒\n2. 推荐话术调整建议\n3. 记录情绪变化节点',
|
||||
status: 'active',
|
||||
creator: '李分析师',
|
||||
createTime: new Date('2024-01-14 14:20:00'),
|
||||
updateTime: new Date('2024-01-14 14:20:00')
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
strategyName: '话术优化建议策略',
|
||||
category: 'script',
|
||||
scenario: '销售话术效果分析',
|
||||
priority: '中',
|
||||
description: '基于通话效果分析提供话术优化建议',
|
||||
content: '分析销售顾问的话术使用效果,识别高效话术和需要改进的地方,为销售团队提供个性化的话术优化建议。',
|
||||
triggerCondition: '通话结束后进行话术效果分析',
|
||||
action: '1. 生成话术分析报告\n2. 提供优化建议\n3. 推荐成功案例话术',
|
||||
status: 'draft',
|
||||
creator: '王培训师',
|
||||
createTime: new Date('2024-01-13 16:45:00'),
|
||||
updateTime: new Date('2024-01-13 16:45:00')
|
||||
}
|
||||
]
|
||||
this.total = this.list.length
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
handleFilter() {
|
||||
this.listQuery.page = 1
|
||||
this.getList()
|
||||
},
|
||||
resetTemp() {
|
||||
this.temp = {
|
||||
id: undefined,
|
||||
strategyName: '',
|
||||
category: '',
|
||||
scenario: '',
|
||||
priority: '',
|
||||
description: '',
|
||||
content: '',
|
||||
triggerCondition: '',
|
||||
action: '',
|
||||
status: 'draft',
|
||||
creator: ''
|
||||
}
|
||||
},
|
||||
handleCreate() {
|
||||
this.resetTemp()
|
||||
this.dialogStatus = 'create'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
createData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
createStrategy(this.temp).then(response => {
|
||||
if (response && response.code === 20000) {
|
||||
this.temp.id = response.data?.id || Date.now()
|
||||
this.temp.createTime = new Date()
|
||||
this.temp.updateTime = new Date()
|
||||
this.list.unshift(this.temp)
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '创建成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
} else {
|
||||
this.$message.error(response?.message || '创建失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('创建失败:', error)
|
||||
this.$message.error('创建失败,请重试')
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleUpdate(row) {
|
||||
this.temp = Object.assign({}, row) // copy obj
|
||||
this.dialogStatus = 'update'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
updateData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
const tempData = Object.assign({}, this.temp)
|
||||
tempData.updateTime = new Date()
|
||||
updateStrategy(tempData).then(response => {
|
||||
if (response && response.code === 20000) {
|
||||
const index = this.list.findIndex(v => v.id === this.temp.id)
|
||||
this.list.splice(index, 1, this.temp)
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '更新成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
} else {
|
||||
this.$message.error(response?.message || '更新失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('更新失败:', error)
|
||||
this.$message.error('更新失败,请重试')
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleDelete(row, index) {
|
||||
this.$confirm('此操作将永久删除该策略, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
deleteStrategy(row.id).then(response => {
|
||||
if (response && response.code === 20000) {
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '删除成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
this.list.splice(index, 1)
|
||||
} else {
|
||||
this.$message.error(response?.message || '删除失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('删除失败:', error)
|
||||
this.$message.error('删除失败,请重试')
|
||||
})
|
||||
})
|
||||
},
|
||||
handleView(row) {
|
||||
this.currentRecord = row
|
||||
this.dialogDetailVisible = true
|
||||
},
|
||||
handleToggleStatus(row) {
|
||||
const newStatus = row.status === 'active' ? 'inactive' : 'active'
|
||||
const action = newStatus === 'active' ? '启用' : '禁用'
|
||||
this.$confirm(`确定要${action}该策略吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
toggleStatus(row.id, newStatus).then(response => {
|
||||
if (response && response.code === 20000) {
|
||||
row.status = newStatus
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: `${action}成功`,
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
} else {
|
||||
this.$message.error(response?.message || `${action}失败`)
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error(`${action}失败:`, error)
|
||||
this.$message.error(`${action}失败,请重试`)
|
||||
})
|
||||
})
|
||||
},
|
||||
getCategoryType(category) {
|
||||
const typeMap = {
|
||||
sales: 'primary',
|
||||
customer: 'success',
|
||||
script: 'warning',
|
||||
followup: 'info'
|
||||
}
|
||||
return typeMap[category] || 'info'
|
||||
},
|
||||
getCategoryText(category) {
|
||||
const textMap = {
|
||||
sales: '销售策略',
|
||||
customer: '客户分析',
|
||||
script: '话术优化',
|
||||
followup: '跟进提醒'
|
||||
}
|
||||
return textMap[category] || '未知'
|
||||
},
|
||||
getPriorityType(priority) {
|
||||
const typeMap = {
|
||||
'高': 'danger',
|
||||
'中': 'warning',
|
||||
'低': 'info'
|
||||
}
|
||||
return typeMap[priority] || 'info'
|
||||
},
|
||||
getStatusType(status) {
|
||||
const typeMap = {
|
||||
active: 'success',
|
||||
inactive: 'info',
|
||||
draft: 'warning'
|
||||
}
|
||||
return typeMap[status] || 'info'
|
||||
},
|
||||
getStatusText(status) {
|
||||
const textMap = {
|
||||
active: '启用',
|
||||
inactive: '禁用',
|
||||
draft: '草稿'
|
||||
}
|
||||
return textMap[status] || '未知'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.filter-container {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.filter-item {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.link-type {
|
||||
color: #409EFF;
|
||||
cursor: pointer;
|
||||
}
|
||||
.link-type:hover {
|
||||
color: #66b1ff;
|
||||
}
|
||||
</style>
|
||||
485
src/views/sales/call-quality-inspection.vue
Normal file
485
src/views/sales/call-quality-inspection.vue
Normal file
@@ -0,0 +1,485 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="filter-container">
|
||||
<el-input
|
||||
v-model="listQuery.keyword"
|
||||
placeholder="请输入客户姓名或销售顾问搜索"
|
||||
style="width: 200px;"
|
||||
class="filter-item"
|
||||
@keyup.enter.native="handleFilter"
|
||||
/>
|
||||
<el-select
|
||||
v-model="listQuery.status"
|
||||
placeholder="质检状态"
|
||||
clearable
|
||||
style="width: 120px;"
|
||||
class="filter-item"
|
||||
>
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="待质检" value="pending" />
|
||||
<el-option label="已质检" value="completed" />
|
||||
<el-option label="不合格" value="failed" />
|
||||
</el-select>
|
||||
<el-select
|
||||
v-model="listQuery.scoreRange"
|
||||
placeholder="评分范围"
|
||||
clearable
|
||||
style="width: 120px;"
|
||||
class="filter-item"
|
||||
>
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="优秀(90-100)" value="90-100" />
|
||||
<el-option label="良好(70-89)" value="70-89" />
|
||||
<el-option label="一般(60-69)" value="60-69" />
|
||||
<el-option label="较差(0-59)" value="0-59" />
|
||||
</el-select>
|
||||
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button class="filter-item" type="primary" icon="el-icon-plus" @click="handleCreate">
|
||||
新增质检
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
:key="tableKey"
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
style="width: 100%;"
|
||||
>
|
||||
<el-table-column label="ID" prop="id" sortable="custom" align="center" width="80">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{ row.id }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户姓名" min-width="120px">
|
||||
<template slot-scope="{row}">
|
||||
<span class="link-type" @click="handleView(row)">{{ row.customerName }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="销售顾问" min-width="120px">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{ row.salesAdvisor }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="通话时间" width="150px" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{ row.callTime | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="通话时长" width="100px" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{ row.duration }}分钟</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="质检评分" width="100px" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag :type="getScoreType(row.score)">
|
||||
{{ row.score }}分
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="质检状态" width="100px" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag :type="getStatusType(row.status)">
|
||||
{{ getStatusText(row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="质检员" width="100px" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{ row.inspector }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="质检时间" width="150px" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{ row.inspectionTime | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="230" class-name="small-padding fixed-width">
|
||||
<template slot-scope="{row,$index}">
|
||||
<el-button type="primary" size="mini" @click="handleUpdate(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button size="mini" type="success" @click="handleView(row)">
|
||||
查看详情
|
||||
</el-button>
|
||||
<el-button size="mini" type="danger" @click="handleDelete(row,$index)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
|
||||
<el-form-item label="客户姓名" prop="customerName">
|
||||
<el-input v-model="temp.customerName" />
|
||||
</el-form-item>
|
||||
<el-form-item label="销售顾问" prop="salesAdvisor">
|
||||
<el-input v-model="temp.salesAdvisor" />
|
||||
</el-form-item>
|
||||
<el-form-item label="通话时间" prop="callTime">
|
||||
<el-date-picker
|
||||
v-model="temp.callTime"
|
||||
type="datetime"
|
||||
placeholder="选择通话时间"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="通话时长" prop="duration">
|
||||
<el-input-number v-model="temp.duration" :min="0" :max="999" style="width: 100%;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="质检评分" prop="score">
|
||||
<el-input-number v-model="temp.score" :min="0" :max="100" style="width: 100%;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="质检状态" prop="status">
|
||||
<el-select v-model="temp.status" placeholder="请选择" style="width: 100%;">
|
||||
<el-option label="待质检" value="pending" />
|
||||
<el-option label="已质检" value="completed" />
|
||||
<el-option label="不合格" value="failed" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="质检员" prop="inspector">
|
||||
<el-input v-model="temp.inspector" />
|
||||
</el-form-item>
|
||||
<el-form-item label="质检备注">
|
||||
<el-input v-model="temp.remark" type="textarea" :rows="3" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<el-dialog title="通话质检详情" :visible.sync="dialogDetailVisible" width="800px">
|
||||
<div v-if="currentRecord">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="客户姓名">{{ currentRecord.customerName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="销售顾问">{{ currentRecord.salesAdvisor }}</el-descriptions-item>
|
||||
<el-descriptions-item label="通话时间">{{ currentRecord.callTime | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="通话时长">{{ currentRecord.duration }}分钟</el-descriptions-item>
|
||||
<el-descriptions-item label="质检评分">
|
||||
<el-tag :type="getScoreType(currentRecord.score)">
|
||||
{{ currentRecord.score }}分
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="质检状态">
|
||||
<el-tag :type="getStatusType(currentRecord.status)">
|
||||
{{ getStatusText(currentRecord.status) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="质检员">{{ currentRecord.inspector }}</el-descriptions-item>
|
||||
<el-descriptions-item label="质检时间">{{ currentRecord.inspectionTime | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div style="margin-top: 20px;">
|
||||
<h4>质检备注:</h4>
|
||||
<p>{{ currentRecord.remark || '无' }}</p>
|
||||
</div>
|
||||
<div style="margin-top: 20px;">
|
||||
<h4>质检详情:</h4>
|
||||
<p>{{ currentRecord.detail || '无' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fetchList, createRecord, updateRecord, deleteRecord } from '@/api/call-quality-inspection'
|
||||
import waves from '@/directive/waves' // waves directive
|
||||
import { parseTime } from '@/utils'
|
||||
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
|
||||
|
||||
export default {
|
||||
name: 'CallQualityInspection',
|
||||
components: { Pagination },
|
||||
directives: { waves },
|
||||
filters: {
|
||||
parseTime
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableKey: 0,
|
||||
list: null,
|
||||
total: 0,
|
||||
listLoading: true,
|
||||
listQuery: {
|
||||
page: 1,
|
||||
limit: 20,
|
||||
keyword: undefined,
|
||||
status: undefined,
|
||||
scoreRange: undefined
|
||||
},
|
||||
temp: {
|
||||
id: undefined,
|
||||
customerName: '',
|
||||
salesAdvisor: '',
|
||||
callTime: '',
|
||||
duration: 0,
|
||||
score: 0,
|
||||
status: 'pending',
|
||||
inspector: '',
|
||||
remark: '',
|
||||
detail: ''
|
||||
},
|
||||
dialogFormVisible: false,
|
||||
dialogDetailVisible: false,
|
||||
dialogStatus: '',
|
||||
textMap: {
|
||||
update: '编辑质检',
|
||||
create: '新增质检'
|
||||
},
|
||||
rules: {
|
||||
customerName: [{ required: true, message: '客户姓名不能为空', trigger: 'blur' }],
|
||||
salesAdvisor: [{ required: true, message: '销售顾问不能为空', trigger: 'blur' }],
|
||||
callTime: [{ required: true, message: '通话时间不能为空', trigger: 'change' }],
|
||||
duration: [{ required: true, message: '通话时长不能为空', trigger: 'blur' }],
|
||||
score: [{ required: true, message: '质检评分不能为空', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '质检状态不能为空', trigger: 'change' }],
|
||||
inspector: [{ required: true, message: '质检员不能为空', trigger: 'blur' }]
|
||||
},
|
||||
currentRecord: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.listLoading = true
|
||||
const params = { ...this.listQuery }
|
||||
|
||||
// 移除空值
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key] === '' || params[key] === null || params[key] === undefined) {
|
||||
delete params[key]
|
||||
}
|
||||
})
|
||||
|
||||
fetchList(params).then(response => {
|
||||
console.log('通话质检API响应:', response) // 调试日志
|
||||
if (response && response.code === 20000) {
|
||||
// 从response.data中获取实际的数据
|
||||
this.list = response.data.records || response.data.data || response.data || []
|
||||
this.total = response.data.total || response.data.length || 0
|
||||
} else {
|
||||
this.$message.error(response?.message || '获取通话质检列表失败')
|
||||
this.list = []
|
||||
this.total = 0
|
||||
}
|
||||
this.listLoading = false
|
||||
}).catch(error => {
|
||||
console.error('获取通话质检列表失败:', error)
|
||||
// 开发环境使用模拟数据
|
||||
this.list = [
|
||||
{
|
||||
id: 1,
|
||||
customerName: '张三',
|
||||
salesAdvisor: '李销售',
|
||||
callTime: new Date('2024-01-15 10:30:00'),
|
||||
duration: 15,
|
||||
score: 85,
|
||||
status: 'completed',
|
||||
inspector: '王质检',
|
||||
remark: '通话质量良好,客户意向明确',
|
||||
detail: '客户对产品表现出浓厚兴趣,销售顾问专业度较高',
|
||||
inspectionTime: new Date('2024-01-15 14:20:00')
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
customerName: '李四',
|
||||
salesAdvisor: '赵销售',
|
||||
callTime: new Date('2024-01-15 11:15:00'),
|
||||
duration: 8,
|
||||
score: 72,
|
||||
status: 'completed',
|
||||
inspector: '王质检',
|
||||
remark: '通话时间较短,需要跟进',
|
||||
detail: '客户询问了价格但未明确表态,建议后续跟进',
|
||||
inspectionTime: new Date('2024-01-15 15:10:00')
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
customerName: '王五',
|
||||
salesAdvisor: '钱销售',
|
||||
callTime: new Date('2024-01-15 14:45:00'),
|
||||
duration: 25,
|
||||
score: 95,
|
||||
status: 'completed',
|
||||
inspector: '王质检',
|
||||
remark: '优秀通话,客户已预约到店',
|
||||
detail: '销售顾问表现专业,客户满意度高,已确定到店时间',
|
||||
inspectionTime: new Date('2024-01-15 16:30:00')
|
||||
}
|
||||
]
|
||||
this.total = this.list.length
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
handleFilter() {
|
||||
this.listQuery.page = 1
|
||||
this.getList()
|
||||
},
|
||||
resetTemp() {
|
||||
this.temp = {
|
||||
id: undefined,
|
||||
customerName: '',
|
||||
salesAdvisor: '',
|
||||
callTime: '',
|
||||
duration: 0,
|
||||
score: 0,
|
||||
status: 'pending',
|
||||
inspector: '',
|
||||
remark: '',
|
||||
detail: ''
|
||||
}
|
||||
},
|
||||
handleCreate() {
|
||||
this.resetTemp()
|
||||
this.dialogStatus = 'create'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
createData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
createRecord(this.temp).then(response => {
|
||||
if (response && response.code === 20000) {
|
||||
this.temp.id = response.data?.id || Date.now()
|
||||
this.list.unshift(this.temp)
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '创建成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
} else {
|
||||
this.$message.error(response?.message || '创建失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('创建失败:', error)
|
||||
this.$message.error('创建失败,请重试')
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleUpdate(row) {
|
||||
this.temp = Object.assign({}, row) // copy obj
|
||||
this.dialogStatus = 'update'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
updateData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
const tempData = Object.assign({}, this.temp)
|
||||
updateRecord(tempData).then(response => {
|
||||
if (response && response.code === 20000) {
|
||||
const index = this.list.findIndex(v => v.id === this.temp.id)
|
||||
this.list.splice(index, 1, this.temp)
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '更新成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
} else {
|
||||
this.$message.error(response?.message || '更新失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('更新失败:', error)
|
||||
this.$message.error('更新失败,请重试')
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleDelete(row, index) {
|
||||
this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
deleteRecord(row.id).then(response => {
|
||||
if (response && response.code === 20000) {
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '删除成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
this.list.splice(index, 1)
|
||||
} else {
|
||||
this.$message.error(response?.message || '删除失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('删除失败:', error)
|
||||
this.$message.error('删除失败,请重试')
|
||||
})
|
||||
})
|
||||
},
|
||||
handleView(row) {
|
||||
this.currentRecord = row
|
||||
this.dialogDetailVisible = true
|
||||
},
|
||||
getScoreType(score) {
|
||||
if (score >= 90) return 'success'
|
||||
if (score >= 70) return 'warning'
|
||||
return 'danger'
|
||||
},
|
||||
getStatusType(status) {
|
||||
const statusMap = {
|
||||
pending: 'info',
|
||||
completed: 'success',
|
||||
failed: 'danger'
|
||||
}
|
||||
return statusMap[status] || 'info'
|
||||
},
|
||||
getStatusText(status) {
|
||||
const statusMap = {
|
||||
pending: '待质检',
|
||||
completed: '已质检',
|
||||
failed: '不合格'
|
||||
}
|
||||
return statusMap[status] || '未知'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.filter-container {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.filter-item {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.link-type {
|
||||
color: #409EFF;
|
||||
cursor: pointer;
|
||||
}
|
||||
.link-type:hover {
|
||||
color: #66b1ff;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user