Commit 525f13f3 by 李宁

1

1 parent e21e4653
......@@ -159,7 +159,18 @@
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="所属区域" prop="region">
<el-input v-model="personnelForm.region" placeholder="如:南京市玄武区"></el-input>
<el-select v-model="personnelForm.type" @change="handlePersonnelTypeChange" style="width: 100%;">
<el-option label="装维师傅" value="installer"></el-option>
<el-option label="营销人员" value="sales"></el-option>
</el-select>
<el-select v-model="personnelForm.type" @change="handlePersonnelTypeChange" style="width: 100%;">
<el-option label="装维师傅" value="installer"></el-option>
<el-option label="营销人员" value="sales"></el-option>
</el-select>
<el-select v-model="personnelForm.type" @change="handlePersonnelTypeChange" style="width: 100%;">
<el-option label="装维师傅" value="installer"></el-option>
<el-option label="营销人员" value="sales"></el-option>
</el-select>
</el-form-item>
</el-col>
......
......@@ -5,139 +5,334 @@
</el-page-header>
</div>
<el-card class="detail-card">
<div class="card-header">
<h3>基本信息</h3>
</div>
<div class="card-content">
<el-row :gutter="20">
<el-col :span="8">
<div class="info-item">
<span class="info-label">商机ID:</span>
<span class="info-value">{{ opportunity.id }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">创建时间:</span>
<span class="info-value">{{ opportunity.createTime }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="detail-container">
<!-- 左侧:详细信息区域 -->
<div class="left-content">
<el-card class="detail-card">
<div class="card-header">
<h3>基本信息</h3>
</div>
<div class="card-content">
<el-row :gutter="20">
<el-col :span="8">
<div class="info-item">
<span class="info-label">商机ID:</span>
<span class="info-value">{{ opportunity.id }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">当前状态:</span>
<span class="info-value">
<el-tag :type="getStatusType(opportunity.status)">
{{ getStatusLabel(opportunity.status) }}
</el-tag>
</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<div class="info-item">
<span class="info-label">客户地址:</span>
<span class="info-value">{{ opportunity.customerAddress }}</span>
</div>
</el-col>
<el-col :span="10">
<div class="info-item">
<span class="info-label">客户联系方式:</span>
<span class="info-value">{{ opportunity.customerPhone }}</span>
</div>
</el-col>
</el-row>
</div>
</el-card>
<el-card class="detail-card">
<div class="card-header">
<h3>装维师傅信息</h3>
</div>
<div class="card-content">
<el-row :gutter="20">
<el-col :span="8">
<div class="info-item">
<span class="info-label">姓名:</span>
<span class="info-value">{{ opportunity.installerName }} ({{ opportunity.installerId }})</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">联系电话:</span>
<span class="info-value">{{ opportunity.installerPhone }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">工号:</span>
<span class="info-value">{{ opportunity.assignedToName || '-' }} ({{ opportunity.assignedTo || '-' }})</span>
</div>
</el-col>
</el-row>
</div>
</el-card>
<el-card class="detail-card">
<div class="card-header">
<h3>营销人员信息</h3>
</div>
<div class="card-content">
<el-row :gutter="20">
<el-col :span="8">
<div class="info-item">
<span class="info-label">姓名:</span>
<span class="info-value">{{ opportunity.installerName }} ({{ opportunity.installerId }})</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">电话:</span>
<span class="info-value">{{ opportunity.installerPhone }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">工号:</span>
<span class="info-value">{{ opportunity.assignedToName || '-' }} ({{ opportunity.assignedTo || '-' }})</span>
</div>
</el-col>
</el-row>
</div>
</el-card>
<el-card class="detail-card">
<div class="card-header">
<h3>商机详情</h3>
</div>
<div class="card-content">
<div class="info-item">
<span class="info-label">当前状态</span>
<span class="info-label">商机标签</span>
<span class="info-value">
<el-tag :type="getStatusType(opportunity.status)">
{{ getStatusLabel(opportunity.status) }}
<el-tag
v-for="tag in opportunity.tags"
:key="tag"
size="small"
style="margin-right: 8px;"
>
{{ tag }}
</el-tag>
</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<div class="info-item">
<span class="info-label">客户地址:</span>
<span class="info-value">{{ opportunity.customerAddress }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">用户账号:</span>
<span class="info-value">{{ opportunity.customerPhone }}</span>
<div class="info-item" style="margin-top: 15px;">
<span class="info-label">商机描述:</span>
<span class="info-value">{{ opportunity.description }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">最新跟进:</span>
<span class="info-value">{{ opportunity.lastFollowTime || '-' }}</span>
<div>
</div>
</el-col>
</el-row>
</div>
</el-card>
</div>
</el-card>
<el-card class="detail-card">
<div class="card-header">
<h3>人员信息</h3>
<el-card class="detail-card">
<div class="card-header">
<h3>跟进记录</h3>
</div>
<div class="card-content">
<el-timeline>
<el-timeline-item
v-for="(activity, index) in activities"
:key="index"
:timestamp="activity.timestamp"
:type="activity.type"
>
<div class="activity-content">
<p>{{ activity.content }}</p>
<p v-if="activity.operator" class="operator">操作人:{{ activity.operator }}</p>
</div>
</el-timeline-item>
</el-timeline>
</div>
</el-card>
</div>
<div class="card-content">
<el-row :gutter="20">
<el-col :span="8">
<div class="info-item">
<span class="info-label">装维师傅:</span>
<span class="info-value">{{ opportunity.installerName }} ({{ opportunity.installerId }})</span>
<!-- 右侧:快捷操作、关键信息、管理员备注 -->
<div class="right-sidebar">
<!-- 快捷操作 -->
<el-card class="sidebar-card">
<div class="card-header">
<h3>快捷操作</h3>
</div>
<div class="card-content">
<div class="quick-actions">
<el-button
type="primary"
icon="el-icon-edit"
class="action-btn"
@click="assignBusi"
>
分配商机
</el-button>
<el-button
type="success"
icon="el-icon-s-promotion"
class="action-btn"
@click="makeOrder"
>
标记成单
</el-button>
<el-button
type="warning"
icon="el-icon-s-check"
class="action-btn"
@click="audioBusi"
>
商机审核
</el-button>
<el-button
type="info"
icon="el-icon-circle-close"
class="action-btn"
@click="shutBusi"
>
关闭商机
</el-button>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">联系电话:</span>
<span class="info-value">{{ opportunity.installerPhone }}</span>
</div>
</el-card>
<!-- 关键信息 -->
<el-card class="sidebar-card">
<div class="card-header">
<h3>关键信息</h3>
</div>
<div class="card-content">
<div class="key-info">
<div class="key-item">
<span class="key-label">创建时间:</span>
<span class="key-value">{{ opportunity.region }}</span>
</div>
<div class="key-item">
<span class="key-label">分配时间:</span>
<span class="key-value">{{ opportunity.region }}</span>
</div>
<div class="key-item">
<span class="key-label">处理人:</span>
<span class="key-value">{{ opportunity.gridName }}</span>
</div>
<div class="key-item">
<span class="key-label">最新跟进时间:</span>
<span class="key-value">{{ opportunity.gridName }}</span>
</div>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">营销人员:</span>
<span class="info-value">{{ opportunity.assignedToName || '-' }} ({{ opportunity.assignedTo || '-' }})</span>
</div>
</el-card>
<!-- 管理员备注 -->
<el-card class="sidebar-card">
<div class="card-header adminMemo">
<h3>管理员备注</h3>
<el-button
type="text"
icon="el-icon-edit-outline"
class="action-btn"
@click="memoStore.isEdit = true"
>
编辑
</el-button>
</div>
<div class="card-content">
<!-- 添加备注输入框 -->
<div class="add-note-section">
<el-input
v-model="memoStore.value"
type="textarea"
:rows="3"
placeholder="请输入备注内容..."
class="note-input"
maxlength="500"
:disabled="!memoStore.isEdit"
show-word-limit
></el-input>
</div>
</el-col>
</el-row>
</div>
<div v-if="memoStore.isEdit" style="margin-top: 10px;">
<el-button @click="memoEditCancel">取消</el-button>
<el-button type="primary" @click="memoEditSubmit">提交审核</el-button>
</div>
</el-card>
</div>
</el-card>
</div>
<el-card class="detail-card">
<div class="card-header">
<h3>商机详情</h3>
</div>
<div class="card-content">
<div class="info-item">
<span class="info-label">商机标签:</span>
<span class="info-value">
<el-tag
v-for="tag in opportunity.tags"
:key="tag"
size="small"
style="margin-right: 8px;"
>
{{ tag }}
</el-tag>
</span>
</div>
<!-- 审核弹窗 -->
<el-dialog
title="商机审核"
:visible.sync="audioBusiStore.isShow"
class="audioBusiness"
width="500px">
<div style="margin-bottom: 10px;">请选择审核结果,如果审核不通过需要填写驳回理由。</div>
<el-form :model="audioBusiStore" label-width="100px">
<el-form-item label="审核结果" required>
<el-radio-group v-model="audioBusiStore.result">
<el-radio label="approved">审核通过</el-radio>
<el-radio label="rejected">审核不通过</el-radio>
</el-radio-group>
</el-form-item>
<div class="info-item" style="margin-top: 15px;">
<span class="info-label">商机描述:</span>
<span class="info-value">{{ opportunity.description }}</span>
</div>
<el-form-item v-if="audioBusiStore.result === 'rejected'" label="不通过理由" required>
<el-input
v-model="audioBusiStore.reason"
type="textarea"
:rows="4"
placeholder="请输入审核不通过的理由"
></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="audioBusiStore.isShow = false">取消</el-button>
<el-button type="primary" @click="audioBusiSubmit">提交审核</el-button>
</div>
</el-card>
</el-dialog>
<el-card class="detail-card">
<div class="card-header">
<h3>跟进记录</h3>
</div>
<div class="card-content">
<el-timeline>
<el-timeline-item
v-for="(activity, index) in activities"
:key="index"
:timestamp="activity.timestamp"
:type="activity.type"
>
<div class="activity-content">
<p>{{ activity.content }}</p>
<p v-if="activity.operator" class="operator">操作人:{{ activity.operator }}</p>
</div>
</el-timeline-item>
</el-timeline>
<!-- 新增商机弹窗 -->
<el-dialog
title="分配商机"
:visible.sync="assignBusiStore.isShow"
width="500px">
<div style="margin-bottom: 10px;">请输入营销人员工号来分配此商机,系统将自动显示对应的营销人员信息。</div>
<el-form :model="newOpportunity" :rules="opportunityRules" ref="opportunityForm" label-width="120px">
<el-form-item label="选择营销人员" prop="businessType" required>
<el-select v-model="assignBusiStore.person" placeholder="请选择营销人员" style="width: 100%;">
<el-option label="宽带新装" value="宽带新装"></el-option>
<el-option label="宽带续费" value="宽带续费"></el-option>
<el-option label="宽带提速" value="宽带提速"></el-option>
<el-option label="ITV新装" value="ITV新装"></el-option>
<el-option label="套餐升级" value="套餐升级"></el-option>
<el-option label="家庭组网" value="家庭组网"></el-option>
<el-option label="智能家居" value="智能家居"></el-option>
</el-select>
</el-form-item>
<el-form-item label="文字描述" prop="description">
<el-input
v-model="assignBusiStore.des"
type="textarea"
:rows="3"
placeholder="请输入商机描述信息"
></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="assignBusiStore.isShow = false">取消</el-button>
<el-button type="primary" @click="assingBusiSubmit">提交</el-button>
</div>
</el-card>
</el-dialog>
<div class="actions">
<el-button @click="goBack">返回</el-button>
<el-button type="primary" @click="handleFollowUp">跟进</el-button>
<el-button type="success" @click="handleComplete">成单</el-button>
</div>
</div>
</template>
......@@ -203,10 +398,47 @@ export default {
data() {
return {
opportunity: mockOpportunity,
activities: mockActivities
activities: mockActivities,
// 管理员备注相关数据
newNote: '',
audioBusiStore:{
isShow: false,
result: '',
reason: ''
},
assignBusiStore:{
isShow: false,
person: '',
des: ''
},
memoStore:{
isEdit: false,
originValue: '',
value: ''
}
}
},
created() {
},
methods: {
memoEditCancel(){
let d = this.memoStore
d.value = d.originValue
d.isEdit = false
},
memoEditSubmit(){
},
audioBusiSubmit(){
},
goBack() {
this.$router.go(-1)
},
......@@ -216,11 +448,22 @@ export default {
getStatusLabel(status) {
return statusMap[status]?.label || status
},
handleFollowUp() {
this.$message.info('跟进功能开发中')
assignBusi() {
this.assignBusiStore.des = ''
this.assignBusiStore.person = ''
this.assignBusiStore.isShow = true
},
makeOrder(){
this.$message.success('标记成单')
},
audioBusi() {
this.audioBusiStore.reason = ''
this.audioBusiStore.isShow = true
},
handleComplete() {
this.$message.info('成单功能开发中')
shutBusi() {
this.$message.success('关闭商机')
}
}
}
......@@ -232,6 +475,22 @@ export default {
margin-bottom: 24px;
}
.detail-container {
display: flex;
gap: 20px;
margin-bottom: 24px;
}
.left-content {
flex: 1;
min-width: 0;
}
.right-sidebar {
width: 380px;
flex-shrink: 0;
}
.detail-card {
margin-bottom: 24px;
......@@ -250,24 +509,23 @@ export default {
.info-item {
display: flex;
margin-bottom: 15px;
.info-label {
width: 100px;
color: #606266;
font-weight: 500;
}
.info-value {
flex: 1;
color: #303133;
}
}
.activity-content {
p {
margin: 0 0 5px 0;
}
.operator {
font-size: 12px;
color: #909399;
......@@ -276,13 +534,190 @@ export default {
}
}
.actions {
.sidebar-card {
margin-bottom: 20px;
.card-header {
margin-bottom: 16px;
h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: #303133;
}
}
.adminMemo{
display: flex;
align-items: center;
justify-content: space-between;
}
.card-content {
.quick-actions {
display: flex;
flex-direction: column;
gap: 12px;
.el-button{
margin-left: 0;
}
.action-btn {
width: 100%;
justify-content: flex-start;
.el-icon {
margin-right: 8px;
}
}
}
}
.key-info {
.key-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
padding: 8px 12px;
background-color: #f8f9fa;
border-radius: 6px;
.key-label {
font-weight: 500;
color: #606266;
font-size: 14px;
}
.key-value {
font-weight: 600;
color: #303133;
font-size: 14px;
}
.el-tag {
border-radius: 4px;
}
}
}
.admin-notes {
.note-item {
margin-bottom: 16px;
padding: 12px;
background-color: #fafafa;
border-radius: 6px;
border-left: 3px solid #67c23a;
.note-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
.note-time {
font-size: 12px;
color: #909399;
}
.note-author {
font-size: 12px;
font-weight: 600;
color: #67c23a;
background-color: rgba(103, 194, 58, 0.1);
padding: 2px 6px;
border-radius: 10px;
}
}
.note-content {
font-size: 13px;
color: #606266;
line-height: 1.6;
}
}
}
}
.bottom-actions {
text-align: center;
padding: 20px 0;
border-top: 1px solid #ebeef5;
.el-button {
margin: 0 10px;
}
}
}
// 响应式设计
@media (max-width: 1200px) {
.opportunity-detail {
.detail-container {
flex-direction: column;
.right-sidebar {
width: 100%;
order: -1;
}
}
}
}
@media (max-width: 768px) {
.opportunity-detail {
.detail-container {
gap: 16px;
}
.detail-card,
.sidebar-card {
.card-content {
.info-item {
flex-direction: column;
align-items: flex-start;
margin-bottom: 12px;
.info-label {
width: auto;
margin-bottom: 4px;
}
}
.key-info .key-item {
flex-direction: column;
align-items: flex-start;
text-align: left;
.key-label,
.key-value {
width: 100%;
margin-bottom: 4px;
}
}
.admin-notes .note-item {
.note-header {
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
}
}
}
.bottom-actions {
.el-button {
margin: 5px;
display: block;
width: 100%;
&:not(:last-child) {
margin-bottom: 10px;
}
}
}
}
}
</style>
\ No newline at end of file
......@@ -257,10 +257,10 @@
</template>
</el-table-column>
<el-table-column prop="lastFollowTime" label="最新跟进" width="180"></el-table-column>
<el-table-column label="操作" min-width="150">
<el-table-column label="操作" min-width="200">
<template slot-scope="scope">
<el-button type="text" @click="checkDetail(scope.row)">查看详情</el-button>
<el-button type="text" @click="checkAudio(scope.row)">审核</el-button>
<el-button type="text" size="small" @click="checkDetail(scope.row)">查看详情</el-button>
<el-button type="text" size="small" @click="checkAudio(scope.row)">审核</el-button>
</template>
</el-table-column>
</el-table>
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!