更新
This commit is contained in:
parent
2110645392
commit
c762bab30b
|
@ -54,3 +54,4 @@ export function delCustomer(id) {
|
|||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -9,4 +9,12 @@ export function dailyCreateCountCustomer(params) {
|
|||
})
|
||||
}
|
||||
|
||||
// 客户概览
|
||||
export function customerBrief(params) {
|
||||
return request({
|
||||
url: '/dashboard/customer/brief',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
|
9
src/api/dashboard/task.js
Normal file
9
src/api/dashboard/task.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import request from '@/utils/request';
|
||||
|
||||
export function taskBrief(params) {
|
||||
return request({
|
||||
url: '/dashboard/task/brief',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
|
@ -92,7 +92,7 @@
|
|||
.el-table__header-wrapper, .el-table__fixed-header-wrapper {
|
||||
th {
|
||||
word-break: break-word;
|
||||
background-color: #f8f8f9;
|
||||
background-color: #fff;
|
||||
color: #515a6e;
|
||||
height: 40px;
|
||||
font-size: 13px;
|
||||
|
|
77
src/components/CardTab/index.vue
Normal file
77
src/components/CardTab/index.vue
Normal file
|
@ -0,0 +1,77 @@
|
|||
<template>
|
||||
<div class="card-tab" :class="{'active': active}" v-on="$listeners">
|
||||
<div class="card-tab-title">
|
||||
{{title}}
|
||||
</div>
|
||||
<div class="card-tab-content">
|
||||
<div class="card-tab-item" v-for="(item, index) in data" :key="index">
|
||||
<div class="card-tab-item-label">{{item.label}}</div>
|
||||
<div class="card-tab-item-value">{{item.value}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CardTab",
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-tab {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #e3ebfe;
|
||||
border-radius: 12px 12px 0 0;
|
||||
padding: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s ease;
|
||||
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.1);
|
||||
.card-tab-title {
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.card-tab-content {
|
||||
.card-tab-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.card-tab-item-label {
|
||||
font-size: 14px;
|
||||
color: #212121;
|
||||
}
|
||||
.card-tab-item-value {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background-color: #ebf0ff;
|
||||
.card-tab-title {
|
||||
color: #007aff;
|
||||
}
|
||||
}
|
||||
&.active {
|
||||
background-color: #fff;
|
||||
.card-tab-title {
|
||||
color: #007aff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -33,6 +33,7 @@ export default {
|
|||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.fixed-header + .app-main {
|
||||
|
|
|
@ -52,7 +52,11 @@ export const CustomerStatus = {
|
|||
POTENTIAL: "1", // 潜在
|
||||
INTENTION: "2", // 意向
|
||||
TRANSACTION: "3", // 成交
|
||||
INVALID: "4" // 失效
|
||||
INVALID: "4", // 失效
|
||||
// 跟进中的客户状态
|
||||
following() {
|
||||
return [this.POTENTIAL, this.INTENTION, this.TRANSACTION]
|
||||
}
|
||||
}
|
||||
|
||||
// 客户意向级别
|
||||
|
|
90
src/views/bst/customer/components/CustomerCardTabs.vue
Normal file
90
src/views/bst/customer/components/CustomerCardTabs.vue
Normal file
|
@ -0,0 +1,90 @@
|
|||
<template>
|
||||
<el-row v-loading="loading" :gutter="4">
|
||||
<el-col :xs="4" :sm="3">
|
||||
<card-tab title="全部客户" :data="[{label: brief.total}]" :active="value == null" @click="handleClick(null)"/>
|
||||
</el-col>
|
||||
<el-col :xs="4" :sm="3" v-for="item of dict.type.customer_status" :key="item.value" >
|
||||
<card-tab
|
||||
:title="item.label"
|
||||
:data="getData(item.value)"
|
||||
:active="value === item.value"
|
||||
@click="handleClick(item.value)"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CardTab from "@/components/CardTab";
|
||||
import { customerBrief } from '@/api/dashboard/customer';
|
||||
import { BriefKeys, CustomerStatus } from '@/utils/enums';
|
||||
export default {
|
||||
name: "CustomerCardTabs",
|
||||
dicts: ['customer_status'],
|
||||
components: {
|
||||
CardTab
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
query: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
brief: {},
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getData() {
|
||||
return (key) => {
|
||||
switch(key) {
|
||||
case CustomerStatus.POTENTIAL:
|
||||
return [{label: this.brief.potential}];
|
||||
case CustomerStatus.INTENTION:
|
||||
return [{label: this.brief.intention}];
|
||||
case CustomerStatus.TRANSACTION:
|
||||
return [{label: this.brief.transaction}];
|
||||
case CustomerStatus.INVALID:
|
||||
return [{label: this.brief.invalid}];
|
||||
default:
|
||||
return [{label: 0}];
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getBrief();
|
||||
},
|
||||
methods: {
|
||||
getBrief() {
|
||||
this.loading = true;
|
||||
customerBrief({
|
||||
...this.query,
|
||||
keys: [
|
||||
BriefKeys.CUSTOMER_STATUS
|
||||
]
|
||||
}).then(res => {
|
||||
this.brief = res.data;
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleClick(value) {
|
||||
if (this.value != value) {
|
||||
this.$emit('input', value);
|
||||
this.$emit('change', value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
|
@ -7,35 +7,31 @@
|
|||
<div class="app-container">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px" size="small" v-loading="loading">
|
||||
<el-row>
|
||||
<form-col :span="24" label="客户名称" prop="name">
|
||||
<form-col :span="span" label="客户名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入客户名称" />
|
||||
</form-col>
|
||||
<form-col :span="span" label="状态" prop="status">
|
||||
<el-select v-model="form.status" placeholder="请选择状态" style="width: 100%;">
|
||||
<el-option
|
||||
v-for="dict in dict.type.customer_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</form-col>
|
||||
<form-col :span="span" label="意向强度" prop="intentLevel">
|
||||
<el-select v-model="form.intentLevel" placeholder="请选择意向强度" style="width: 100%;">
|
||||
<el-option
|
||||
v-for="dict in dict.type.customer_intent_level"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</form-col>
|
||||
<form-col :span="span" label="手机号" prop="mobile">
|
||||
<el-input v-model="form.mobile" placeholder="请输入手机号" maxlength="11" show-word-limit />
|
||||
</form-col>
|
||||
<form-col :span="span" label="微信号" prop="wechat">
|
||||
<el-input v-model="form.wechat" placeholder="请输入微信号" maxlength="50" show-word-limit />
|
||||
</form-col>
|
||||
<template v-if="form.id != null">
|
||||
<form-col :span="span" label="状态" prop="status">
|
||||
<el-select v-model="form.status" placeholder="请选择状态" style="width: 100%;">
|
||||
<el-option
|
||||
v-for="dict in dict.type.customer_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</form-col>
|
||||
<form-col :span="span" label="意向强度" prop="intentLevel">
|
||||
<el-select v-model="form.intentLevel" placeholder="请选择意向强度" style="width: 100%;">
|
||||
<el-option
|
||||
v-for="dict in dict.type.customer_intent_level"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</form-col>
|
||||
</template>
|
||||
<form-col :span="span" label="来源" prop="source">
|
||||
<el-select v-model="form.source" placeholder="请选择来源,可自定义" style="width: 100%;" allow-create filterable default-first-option>
|
||||
<el-option
|
||||
|
@ -59,6 +55,12 @@
|
|||
<form-col :span="span" label="跟进人" prop="followId">
|
||||
<user-select v-model="form.followId" :disabled="disabledFollowUser" />
|
||||
</form-col>
|
||||
<form-col :span="span" label="手机号" prop="mobile">
|
||||
<el-input v-model="form.mobile" placeholder="请输入手机号" maxlength="11" show-word-limit />
|
||||
</form-col>
|
||||
<form-col :span="span" label="微信号" prop="wechat">
|
||||
<el-input v-model="form.wechat" placeholder="请输入微信号" maxlength="50" show-word-limit />
|
||||
</form-col>
|
||||
<form-col :span="24" label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" maxlength="200" show-word-limit />
|
||||
</form-col>
|
||||
|
@ -121,12 +123,6 @@ export default {
|
|||
status: [
|
||||
{ required: true, message: "状态不能为空", trigger: "change" }
|
||||
],
|
||||
intentLevel: [
|
||||
{ required: true, message: "意向强度不能为空", trigger: "change" }
|
||||
],
|
||||
source: [
|
||||
{ required: true, message: "来源不能为空", trigger: "change" }
|
||||
],
|
||||
followId: [
|
||||
{ required: true, message: "跟进人不能为空", trigger: "change" }
|
||||
],
|
||||
|
@ -139,6 +135,15 @@ export default {
|
|||
],
|
||||
followTime: [
|
||||
{ required: true, message: "跟进时间不能为空", trigger: "change" }
|
||||
],
|
||||
customerStatus: [
|
||||
{ required: true, message: "客户状态不能为空", trigger: "change" }
|
||||
],
|
||||
customerIntentLevel: [
|
||||
{ required: true, message: "意向强度不能为空", trigger: "change" }
|
||||
],
|
||||
nextFollowTime: [
|
||||
{ required: true, message: "下次跟进时间不能为空", trigger: "change" }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -187,8 +192,8 @@ export default {
|
|||
id: null,
|
||||
code: null,
|
||||
name: null,
|
||||
status: CustomerStatus.POTENTIAL,
|
||||
intentLevel: CustomerIntentLevel.MEDIUM,
|
||||
status: null,
|
||||
intentLevel: null,
|
||||
mobile: null,
|
||||
wechat: null,
|
||||
source: null,
|
||||
|
@ -197,6 +202,9 @@ export default {
|
|||
remark: null,
|
||||
follow: {
|
||||
followTime: parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}'),
|
||||
nextFollowTime: null,
|
||||
customerStatus: CustomerStatus.POTENTIAL,
|
||||
customerIntentLevel: CustomerIntentLevel.MEDIUM,
|
||||
}
|
||||
};
|
||||
this.resetForm("form");
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="客户编号" prop="code">
|
||||
<el-form-item label="客户编号" prop="id" v-if="isShow('id')">
|
||||
<el-input
|
||||
v-model="queryParams.code"
|
||||
v-model="queryParams.id"
|
||||
placeholder="请输入客户编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户名称" prop="name">
|
||||
<el-form-item label="客户名称" prop="name" v-if="isShow('name')">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入客户名称"
|
||||
|
@ -17,7 +17,7 @@
|
|||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<!-- <el-form-item label="状态" prop="status" v-if="isShow('name')">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable @change="handleQuery">
|
||||
<el-option
|
||||
v-for="dict in dict.type.customer_status"
|
||||
|
@ -26,8 +26,8 @@
|
|||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="意向强度" prop="intentLevel">
|
||||
</el-form-item> -->
|
||||
<el-form-item label="意向强度" prop="intentLevel" v-if="isShow('name')">
|
||||
<el-select v-model="queryParams.intentLevel" placeholder="请选择意向强度" clearable @change="handleQuery">
|
||||
<el-option
|
||||
v-for="dict in dict.type.customer_intent_level"
|
||||
|
@ -37,7 +37,7 @@
|
|||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" prop="mobile">
|
||||
<el-form-item label="手机号" prop="mobile" v-if="isShow('mobile')">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号"
|
||||
|
@ -45,7 +45,7 @@
|
|||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="微信号" prop="wechat">
|
||||
<el-form-item label="微信号" prop="wechat" v-if="isShow('wechat')">
|
||||
<el-input
|
||||
v-model="queryParams.wechat"
|
||||
placeholder="请输入微信号"
|
||||
|
@ -53,7 +53,7 @@
|
|||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="来源" prop="source">
|
||||
<el-form-item label="来源" prop="source" v-if="isShow('source')">
|
||||
<el-input
|
||||
v-model="queryParams.source"
|
||||
placeholder="请输入来源"
|
||||
|
@ -61,7 +61,7 @@
|
|||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="意向" prop="intent">
|
||||
<el-form-item label="意向" prop="intent" v-if="isShow('intent')">
|
||||
<el-input
|
||||
v-model="queryParams.intent"
|
||||
placeholder="请输入意向"
|
||||
|
@ -69,7 +69,7 @@
|
|||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="跟进人" prop="followName">
|
||||
<el-form-item label="跟进人" prop="followName" v-if="isShow('followName')">
|
||||
<el-input
|
||||
v-model="queryParams.followName"
|
||||
placeholder="请输入跟进人"
|
||||
|
@ -118,6 +118,8 @@
|
|||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<customer-card-tabs v-model="queryParams.status" :query="{...queryParams, status: null}" @change="handleChangeTab" ref="customerCardTabs" />
|
||||
|
||||
<el-table v-loading="loading" :data="customerList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="onSortChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<template v-for="column of showColumns">
|
||||
|
@ -136,17 +138,13 @@
|
|||
<template v-if="column.key === 'id'">
|
||||
{{d.row[column.key]}}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<dict-tag :options="dict.type.customer_status" :value="d.row[column.key]"/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'intentLevel'">
|
||||
<dict-tag :options="dict.type.customer_intent_level" :value="d.row[column.key]"/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'intents'">
|
||||
{{d.row[column.key].join(',')}}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'name'">
|
||||
<customer-link :id="d.row.id" :text="d.row[column.key]"/>
|
||||
<dict-tag :options="dict.type.customer_status" :value="d.row.status" size="mini" style="margin-left: 4px;"/>
|
||||
<dict-tag :options="dict.type.customer_intent_level" :value="d.row.intentLevel" size="mini" style="margin-left: 4px;"/>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{d.row[column.key]}}
|
||||
|
@ -200,7 +198,12 @@
|
|||
<customer-follow-edit-dialog
|
||||
:show.sync="showFollowDialog"
|
||||
@success="getList"
|
||||
:init-data="{customerId: row.id, customerName: row.name}"
|
||||
:init-data="{
|
||||
customerId: row.id,
|
||||
customerName: row.name,
|
||||
customerStatus: row.status,
|
||||
customerIntentLevel: row.intentLevel
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -211,6 +214,8 @@ import { $showColumns } from '@/utils/mixins';
|
|||
import FormCol from "@/components/FormCol/index.vue";
|
||||
import CustomerLink from '@/components/Business/Customer/CustomerLink.vue';
|
||||
import CustomerFollowEditDialog from '@/views/bst/customerFollow/components/CustomerFollowEditDialog.vue';
|
||||
import CustomerCardTabs from './components/CustomerCardTabs.vue';
|
||||
|
||||
// 默认排序字段
|
||||
const defaultSort = {
|
||||
prop: "createTime",
|
||||
|
@ -221,7 +226,7 @@ export default {
|
|||
name: "Customer",
|
||||
mixins: [$showColumns],
|
||||
dicts: ['customer_intent_level', 'customer_status'],
|
||||
components: {FormCol, CustomerLink, CustomerFollowEditDialog},
|
||||
components: {FormCol, CustomerLink, CustomerFollowEditDialog, CustomerCardTabs},
|
||||
props: {
|
||||
query: {
|
||||
type: Object,
|
||||
|
@ -231,6 +236,10 @@ export default {
|
|||
initShowSearch: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
listApi: {
|
||||
type: Function,
|
||||
default: listCustomer
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
@ -241,17 +250,15 @@ export default {
|
|||
// 字段列表
|
||||
columns: [
|
||||
{key: 'id', visible: false, label: '编号', minWidth: null, sortable: true, overflow: false, align: 'center', width: "80"},
|
||||
{key: 'name', visible: true, label: '名称', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||
{key: 'status', visible: true, label: '状态', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||
{key: 'intentLevel', visible: true, label: '意向强度', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||
{key: 'name', visible: true, label: '名称', minWidth: "200", sortable: true, overflow: false, align: 'left', width: null},
|
||||
{key: 'remark', visible: true, label: '备注', minWidth: null, sortable: true, overflow: true, align: 'center', width: null},
|
||||
{key: 'intents', visible: true, label: '意向', minWidth: null, sortable: true, overflow: true, align: 'center', width: null},
|
||||
{key: 'mobile', visible: true, label: '手机号', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||
{key: 'wechat', visible: true, label: '微信号', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||
{key: 'source', visible: true, label: '来源', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||
{key: 'intents', visible: true, label: '意向', minWidth: null, sortable: true, overflow: true, align: 'center', width: null},
|
||||
{key: 'followName', visible: true, label: '跟进人', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||
{key: 'lastFollowTime', visible: true, label: '最近跟进', minWidth: null, sortable: false, overflow: false, align: 'center', width: "100"},
|
||||
{key: 'nextFollowTime', visible: true, label: '下次跟进', minWidth: null, sortable: false, overflow: false, align: 'center', width: "100"},
|
||||
{key: 'remark', visible: true, label: '备注', minWidth: null, sortable: true, overflow: true, align: 'center', width: null},
|
||||
{key: 'lastFollowTime', visible: true, label: '最近跟进', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100"},
|
||||
{key: 'nextFollowTime', visible: true, label: '下次跟进', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100"},
|
||||
{key: 'createName', visible: true, label: '创建人', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||
{key: 'createTime', visible: true, label: '创建时间', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100"},
|
||||
],
|
||||
|
@ -338,16 +345,24 @@ export default {
|
|||
/** 查询客户列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listCustomer(this.queryParams).then(response => {
|
||||
this.listApi(this.queryParams).then(response => {
|
||||
this.customerList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 切换卡片
|
||||
handleChangeTab() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
if (this.$refs.customerCardTabs) {
|
||||
this.$refs.customerCardTabs.getBrief();
|
||||
}
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
|
|
|
@ -24,6 +24,7 @@ import { getCustomerFollow, addCustomerFollow, updateCustomerFollow } from "@/ap
|
|||
import { mapGetters } from 'vuex';
|
||||
import { parseTime } from '@/utils/ruoyi.js';
|
||||
import CustomerFollowForm from '@/views/bst/customerFollow/components/CustomerFollowForm.vue';
|
||||
|
||||
export default {
|
||||
name: "CustomerFollowEditDialog",
|
||||
components: { CustomerFollowForm },
|
||||
|
@ -61,6 +62,15 @@ export default {
|
|||
],
|
||||
followTime: [
|
||||
{ required: true, message: "跟进时间不能为空", trigger: "change" }
|
||||
],
|
||||
customerStatus: [
|
||||
{ required: true, message: "客户状态不能为空", trigger: "change" }
|
||||
],
|
||||
customerIntentLevel: [
|
||||
{ required: true, message: "意向强度不能为空", trigger: "change" }
|
||||
],
|
||||
nextFollowTime: [
|
||||
{ required: true, message: "下次跟进时间不能为空", trigger: "change" }
|
||||
]
|
||||
},
|
||||
disabledKeys: [],
|
||||
|
@ -101,6 +111,8 @@ export default {
|
|||
userId: null,
|
||||
nextFollowTime: null,
|
||||
followTime: parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}'),
|
||||
customerStatus: null,
|
||||
customerIntentLevel: null,
|
||||
// vo
|
||||
customerName: null,
|
||||
...this.initData
|
||||
|
|
|
@ -4,7 +4,32 @@
|
|||
<image-upload v-model="form.picture" />
|
||||
</form-col>
|
||||
<form-col :span="span" label="客户" prop="customerId" v-if="!isHide('customerId')">
|
||||
<customer-input v-model="form.customerId" :text.sync="form.customerName" :disabled="isDisabled('customerId')"/>
|
||||
<customer-input
|
||||
v-model="form.customerId"
|
||||
:text.sync="form.customerName"
|
||||
:disabled="isDisabled('customerId')"
|
||||
@confirm="handleConfirmCustomer"
|
||||
/>
|
||||
</form-col>
|
||||
<form-col :span="span" label="客户状态" prop="customerStatus" :prop="propPrefix + 'customerStatus'">
|
||||
<el-select v-model="form.customerStatus" placeholder="请选择客户状态" style="width: 100%;" @change="handleChangeCustomerStatus">
|
||||
<el-option
|
||||
v-for="dict in dict.type.customer_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</form-col>
|
||||
<form-col :span="span" label="意向强度" prop="customerIntentLevel" :prop="propPrefix + 'customerIntentLevel'">
|
||||
<el-select v-model="form.customerIntentLevel" placeholder="请选择意向强度" style="width: 100%;">
|
||||
<el-option
|
||||
v-for="dict in dict.type.customer_intent_level"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</form-col>
|
||||
<form-col :span="span" label="跟进方式" :prop="propPrefix + 'type'">
|
||||
<el-select v-model="form.type" placeholder="请选择跟进方式" style="width: 100%;">
|
||||
|
@ -30,7 +55,7 @@
|
|||
placeholder="请选择跟进时间">
|
||||
</el-date-picker>
|
||||
</form-col>
|
||||
<form-col :span="span" label="下次跟进" :prop="propPrefix + 'nextFollowTime'">
|
||||
<form-col :span="span" label="下次跟进" :prop="propPrefix + 'nextFollowTime'" v-if="form.customerStatus !== CustomerStatus.INVALID">
|
||||
<el-date-picker clearable
|
||||
style="width: 100%;"
|
||||
v-model="form.nextFollowTime"
|
||||
|
@ -48,10 +73,12 @@
|
|||
import { DatePickerOptions } from '@/utils/constants.js';
|
||||
import FormCol from '@/components/FormCol/index.vue';
|
||||
import CustomerInput from '@/components/Business/Customer/CustomerInput.vue';
|
||||
import { CustomerStatus } from '@/utils/enums.js';
|
||||
|
||||
export default {
|
||||
name: 'CustomerFollowForm',
|
||||
components: { FormCol, CustomerInput },
|
||||
dicts: ['customer_follow_type'],
|
||||
dicts: ['customer_follow_type', 'customer_status', 'customer_intent_level'],
|
||||
props: {
|
||||
form: {
|
||||
type: Object,
|
||||
|
@ -76,6 +103,7 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
CustomerStatus,
|
||||
DatePickerOptions,
|
||||
}
|
||||
},
|
||||
|
@ -90,6 +118,20 @@ export default {
|
|||
return this.disabled.includes(key);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 确认选中客户
|
||||
handleConfirmCustomer(customer) {
|
||||
this.form.customerStatus = customer.status;
|
||||
this.form.customerIntentLevel = customer.intentLevel;
|
||||
},
|
||||
// 客户状态改变
|
||||
handleChangeCustomerStatus(value) {
|
||||
// 若为失效,则下次跟进时间清空
|
||||
if (value === CustomerStatus.INVALID) {
|
||||
this.form.nextFollowTime = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -11,22 +11,37 @@
|
|||
</el-col>
|
||||
</el-row>
|
||||
<el-card>
|
||||
<el-tabs >
|
||||
<el-tab-pane label="项目列表" lazy v-if="checkPermi(['bst:project:list'])">
|
||||
<project :hide-columns="projectHideColumns" :init-show-search="false"/>
|
||||
<el-tabs>
|
||||
<el-tab-pane label="我的项目" lazy v-if="checkPermi(['bst:project:list'])">
|
||||
<project :hide-columns="projectHideColumns" :query="{joinUserId: userId}" :init-show-search="false"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="任务列表" lazy v-if="checkPermi(['bst:task:list'])">
|
||||
<task :hide-columns="taskHideColumns" :init-show-search="false"/>
|
||||
<el-tab-pane label="我的任务" lazy v-if="checkPermi(['bst:task:list'])">
|
||||
<task :hide-columns="taskHideColumns" :query="{ownerId: userId}" :init-show-search="false"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="今日完成任务" lazy v-if="checkPermi(['bst:task:list'])">
|
||||
<task :hide-columns="taskHideColumns" :query="{passDateRange: [today, today], statusList: TaskStatus.complete()}" :init-show-search="false"/>
|
||||
<task
|
||||
:hide-columns="taskHideColumns"
|
||||
:query="{passDateRange: [today, today], statusList: TaskStatus.complete(), ownerId: userId}"
|
||||
:init-show-search="false"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="今日新增客户" lazy v-if="checkPermi(['bst:customer:list'])">
|
||||
<customer :hide-columns="customerHideColumns" :query="{createDate: today}" :init-show-search="false"/>
|
||||
<customer :hide-columns="customerHideColumns" :query="{createDate: today, createId: userId}" :init-show-search="false"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="待跟进客户" lazy v-if="checkPermi(['bst:customer:list'])">
|
||||
<customer
|
||||
:hide-columns="customerHideColumns"
|
||||
:query="{
|
||||
followId: userId,
|
||||
statusList: CustomerStatus.following(),
|
||||
nextFollowDateEnd: today,
|
||||
orderByColumn: 'nextFollowTime',
|
||||
isAsc: 'ascending'
|
||||
}"
|
||||
:init-show-search="false" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
<!-- <project-list-panel/> -->
|
||||
</el-col>
|
||||
<el-col :xs="24" :lg="5">
|
||||
<el-card class="card-box">
|
||||
|
@ -51,7 +66,8 @@ import Task from '@/views/bst/task/index.vue';
|
|||
import Customer from '@/views/bst/customer/index.vue';
|
||||
import { checkPermi } from '@/utils/permission';
|
||||
import { parseTime } from '@/utils/ruoyi';
|
||||
import { TaskStatus } from '@/utils/enums';
|
||||
import { TaskStatus, CustomerStatus } from '@/utils/enums';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'Index',
|
||||
|
@ -59,7 +75,9 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
TaskStatus,
|
||||
CustomerStatus,
|
||||
today: parseTime(new Date(), '{y}-{m}-{d}'),
|
||||
now: parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}'),
|
||||
projectHideColumns: [
|
||||
'id',
|
||||
'no',
|
||||
|
@ -100,8 +118,13 @@ export default {
|
|||
],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'userId'
|
||||
])
|
||||
},
|
||||
methods: {
|
||||
checkPermi,
|
||||
checkPermi
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
94
src/views/bst/task/components/TaskCardTabs.vue
Normal file
94
src/views/bst/task/components/TaskCardTabs.vue
Normal file
|
@ -0,0 +1,94 @@
|
|||
<template>
|
||||
<el-row v-loading="loading" :gutter="4">
|
||||
<el-col :xs="4" :sm="3">
|
||||
<card-tab title="全部任务" :data="[{label: brief.total}]" :active="value == null" @click="handleClick(null)"/>
|
||||
</el-col>
|
||||
<el-col :xs="4" :sm="3" v-for="item of dict.type.task_status" :key="item.value" >
|
||||
<card-tab
|
||||
:title="item.label"
|
||||
:data="getData(item.value)"
|
||||
:active="value === item.value"
|
||||
@click="handleClick(item.value)"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CardTab from "@/components/CardTab";
|
||||
import { taskBrief } from '@/api/dashboard/task';
|
||||
import { BriefKeys, TaskStatus } from '@/utils/enums';
|
||||
export default {
|
||||
name: "TaskCardTabs",
|
||||
dicts: ['task_status'],
|
||||
components: {
|
||||
CardTab
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
query: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
brief: {},
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getData() {
|
||||
return (key) => {
|
||||
switch(key) {
|
||||
case TaskStatus.WAIT_RECEIVE:
|
||||
return [{label: this.brief.waitReceive}];
|
||||
case TaskStatus.PROCESSING:
|
||||
return [{label: this.brief.inProgress}];
|
||||
case TaskStatus.WAIT_CONFIRM:
|
||||
return [{label: this.brief.waitConfirm}];
|
||||
case TaskStatus.PASS:
|
||||
return [{label: this.brief.completed}];
|
||||
case TaskStatus.REJECT:
|
||||
return [{label: this.brief.reject}];
|
||||
case TaskStatus.CANCEL:
|
||||
return [{label: 0}];
|
||||
default:
|
||||
return [{label: 0}];
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getBrief();
|
||||
},
|
||||
methods: {
|
||||
getBrief() {
|
||||
this.loading = true;
|
||||
taskBrief({
|
||||
...this.query,
|
||||
keys: [
|
||||
BriefKeys.TASK_STATUS
|
||||
]
|
||||
}).then(res => {
|
||||
this.brief = res.data;
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleClick(value) {
|
||||
if (this.value != value) {
|
||||
this.$emit('input', value);
|
||||
this.$emit('change', value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
|
@ -14,7 +14,7 @@
|
|||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="statusList" v-if="isShow('description')">
|
||||
<!-- <el-form-item label="状态" prop="statusList" v-if="isShow('description')">
|
||||
<el-checkbox-group v-model="queryParams.statusList" placeholder="请选择状态" multiple clearable @change="handleQuery">
|
||||
<el-checkbox-button
|
||||
v-for="dict in dict.type.task_status"
|
||||
|
@ -22,7 +22,7 @@
|
|||
:label="dict.value"
|
||||
>{{ dict.label }}</el-checkbox-button>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="优先级" prop="level" v-if="isShow('description')">
|
||||
<el-select v-model="queryParams.level" placeholder="请选择优先级" clearable @change="handleQuery">
|
||||
<el-option
|
||||
|
@ -98,9 +98,11 @@
|
|||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-row class="mb8">
|
||||
<!-- <el-row class="mb8">
|
||||
<task-statistics />
|
||||
</el-row>
|
||||
</el-row> -->
|
||||
|
||||
<task-card-tabs v-model="queryParams.status" :query="{...queryParams, status: null}" @change="handleChangeTab" ref="taskCardTabs" />
|
||||
|
||||
<el-table v-loading="loading" :data="taskList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="onSortChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
|
@ -222,6 +224,8 @@ import UserSelect from '@/components/Business/User/UserSelect.vue';
|
|||
import {TaskStatus} from '@/utils/enums'
|
||||
import BooleanTag from '@/components/BooleanTag'
|
||||
import TaskStatistics from '@/views/bst/task/components/TaskStatistics.vue'
|
||||
import TaskCardTabs from '@/views/bst/task/components/TaskCardTabs.vue'
|
||||
|
||||
// 默认排序字段
|
||||
const defaultSort = {
|
||||
prop: "createTime",
|
||||
|
@ -232,7 +236,7 @@ export default {
|
|||
name: "Task",
|
||||
mixins: [$showColumns, $task],
|
||||
dicts: ['task_status', 'task_level', 'task_type'],
|
||||
components: {FormCol, TaskEditDialog, ProjectSelect, TaskViewDialog, ProjectLink, AvatarList, UserSelect, BooleanTag, TaskStatistics},
|
||||
components: {FormCol, TaskEditDialog, ProjectSelect, TaskViewDialog, ProjectLink, AvatarList, UserSelect, BooleanTag, TaskStatistics, TaskCardTabs},
|
||||
props: {
|
||||
initData: {
|
||||
type: Object,
|
||||
|
@ -352,10 +356,18 @@ export default {
|
|||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 切换任务卡片
|
||||
handleChangeTab() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
if (this.$refs.taskCardTabs) {
|
||||
this.$refs.taskCardTabs.getBrief();
|
||||
}
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
|
|
|
@ -293,6 +293,8 @@ export default {
|
|||
{key: 'status', visible: true, label: '状态', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||
{key: 'phonenumber', visible: true, label: '手机号', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||
{key: 'email', visible: true, label: '邮箱', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||
{key: 'projectCount', visible: true, label: '项目数', minWidth: null, sortable: false, overflow: false, align: 'center', width: null},
|
||||
{key: 'taskCount', visible: true, label: '任务数', minWidth: null, sortable: false, overflow: false, align: 'center', width: null},
|
||||
{key: 'loginIp', visible: true, label: '登录IP', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||
{key: 'loginDate', visible: true, label: '登录时间', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100"},
|
||||
{key: 'createTime', visible: true, label: '创建时间', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100"},
|
||||
|
|
Loading…
Reference in New Issue
Block a user