Commit df16fe20 authored by 沈良进's avatar 沈良进

save

parent 731d34c4
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -8,6 +8,7 @@
},
"dependencies": {
"@tinymce/tinymce-vue": "^3.1.0",
"@vue/composition-api": "^1.7.1",
"axios": "^0.18.0",
"chart.js": "^2.9.3",
"co": "^4.6.0",
......@@ -19,7 +20,7 @@
"jspdf": "^1.4.1",
"less-loader": "^5.0.0",
"moment": "^2.22.2",
"pinyin": "^2.9.0",
"pinyin": "^2.8.3",
"register-service-worker": "^1.0.0",
"sass-loader": "^8.0.2",
"tinymce": "^5.1.6",
......@@ -30,12 +31,12 @@
"vue-quill-editor": "^3.0.6",
"vue-router": "^3.0.1",
"vue2.0-zoom": "^2.1.1",
"vuedraggable": "^2.24.3",
"vuex": "^3.0.1",
"vxe-table": "^2.7.24",
"webpack-require-http": "^0.4.3",
"x-data-spreadsheet": "^1.0.31",
"xe-utils": "^2.3.0",
"pinyin": "^2.8.3"
"xe-utils": "^2.3.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.4.1",
......@@ -49,7 +50,7 @@
"stylus-loader": "^3.0.2",
"typescript": "^3.8.2",
"vue-cli-plugin-element": "^1.0.0",
"vue-template-compiler": "^2.5.17"
"vue-template-compiler": "^2.6.14"
},
"postcss": {
"plugins": {
......
......@@ -41,6 +41,11 @@
<rankConfig v-if="selectTitle==3"></rankConfig>
<!-- 管理配置 -->
<ManageConfiguration v-if="selectTitle==4"></ManageConfiguration>
<!-- 客户字段 -->
<customerField v-if="selectTitle==5"></customerField>
<!-- 标签 -->
<!-- <customerlabel v-if="selectTitle==6"></customerlabel> -->
</div>
</template>
<script>
......@@ -48,12 +53,16 @@
import distributionRules from "./distributionRules";
import rankConfig from "./rankConfig";
import ManageConfiguration from "./ManageConfiguration";
import customerField from '../../views/customerSet/components/customerField.vue'
// import customerlabel from '../../views/customerSet/components/customerlabel.vue'
export default {
components: {
customerSource,
distributionRules,
rankConfig,
ManageConfiguration
ManageConfiguration,
customerField,
// customerlabel
},
data() {
return {
......@@ -75,6 +84,14 @@
title: '管理配置',
id: 4
},
{
title: '客户字段',
id: 5
},
{
title: '标签',
id: 6
},
]
};
},
......
......@@ -24,6 +24,8 @@ import vueQuillEditor from 'vue-quill-editor'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import pinyin from 'pinyin'
import VueCompositionAPI from '@vue/composition-api'
Vue.use(VueCompositionAPI)
Vue.prototype.pinyin = pinyin
// 使用Vue.use()方法就会调用工具方法中的install方法
Vue.use(htmlToPdf)
......
import axios from 'axios'
import domainManager from './domainManager'
const getLocalStorage = function () {
var localStorageData = window.localStorage["userInfo"];
if (localStorageData != undefined) {
return JSON.parse(localStorageData);
} else {
return null;
}
}
//CRM接口
export default function(cmd,content, msg, successCall, faildCall) {
console.log('CRM接口', content)
if (msg == null || msg == "") {
msg = {}
}
var apiurl = domainManager().PostUrl + cmd;
var timestamp = (new Date()).valueOf();
var token = "";
var key = "";
if (getLocalStorage() != null) {
token = getLocalStorage().token;
key = getLocalStorage().secretKey;
}
var encodeMsg = encodeURIComponent(JSON.stringify(msg)).toLowerCase();
var md5Str = md5(`cmd=${cmd}&msg=${encodeMsg}&timestamp=${timestamp}&token=${token}&key=${key}`);
var postData = {
"msg": msg,
"timestamp": timestamp,
"token": token,
"sign": md5Str
}
if (localStorage.g && localStorage.g != 'undefined') {
postData.groupId = 2;
}
axios.post(apiurl, postData, {
headers: {
'Content-Type': 'application/json',
'Referer-Viitto': content.$route.path
}
})
.then(res => {
if (res.data.resultCode == 10000 || res.data.resultCode == 10001) {
content.$router.push({
path: '/login'
})
} else if (res.data.resultCode == 10005) {
content.$router.go(-1)
} else {
successCall(res)
}
}, faildCall)
}
\ No newline at end of file
const isOnline = function() {
return process.env.NODE_ENV !== 'development';
}
export default function() {
//CRM API
let domainUrl = "";
// domainUrl = "http://192.168.10.46:8500";
domainUrl = "http://192.168.10.128:8098";
// domainUrl = "http://crm.oytour.com"
//domainUrl = "http://localhost:5003";
let locationName = window.location.hostname;
//旅游ERPApi
let domainPostUrl = ""
domainPostUrl = "http://192.168.10.9:8083";
//domainPostUrl = "http://192.168.10.46";
if (isOnline()) {
if (window.location.host.indexOf('fcrmyx.oytour.com') != -1) {
domainUrl = "http://crm.oytour.com"
domainPostUrl = "http://reborn.oytour.com"
} else {
domainUrl = "http://testcrm.oytour.com"
domainPostUrl = "http://testapi.oytour.com"
}
}
var obj = {
//主地址
DomainUrl: domainUrl,
DownLoadContractUrl: domainPostUrl,
//阿里服务器地址
AliUrl: "https://reborndev.oss-cn-hangzhou.aliyuncs.com",
//CRMApi地址
PostUrl: domainUrl,
//旅游ERPApi地址
ERPApiUrl: domainPostUrl + "/api/common/post",
javaUrl: locationName.indexOf('testb2b') == -1 ? "http://efficient.oytour.com" : locationName.indexOf('testb2b.oytour.com') !== -1 ? "http://47.96.12.235:9001" : "http://192.168.10.215:9000",
ViittoFileUrl: locationName.indexOf('oytour') !== -1 || locationName.indexOf('testb2b.oytour.com') !== -1 ? "http://staticfile.oytour.com" : 'http://192.168.10.214:8130',
UploadUrl: locationName.indexOf('oytour') !== -1 || locationName.indexOf('testb2b.oytour.com') !== -1 ? "http://uploadfile.oytour.com" : "http://192.168.10.214:8120",
erpRoutingUrl: locationName.indexOf('oytour') !== -1 || locationName.indexOf('testb2b.oytour.com') !== -1 ? "http://yx.oytour.com/#/" : "http://localhost:8081/#/"// "http://www.test.com:8081/#/",
};
return obj;
}
\ No newline at end of file
import md5 from 'js-md5'
import domainManager from './domainManager'
export default {
data: {
loginUser: {},
apiurl: ''
},
domainManager,
install(Vue, options) {
Vue.prototype.isOnline = function() {
return process.env.NODE_ENV !== 'development';
},
//域名管理对象
Vue.prototype.domainManager = function() {
//CRM API
let domainUrl = "";
// domainUrl = "http://192.168.10.46:8500";
domainUrl = "http://192.168.10.9:8098";
// domainUrl = "http://crm.oytour.com"
//domainUrl = "http://localhost:5003";
let locationName = window.location.hostname;
//旅游ERPApi
let domainPostUrl = ""
domainPostUrl = "http://192.168.10.9:8083";
//domainPostUrl = "http://192.168.10.46";
if (this.isOnline()) {
if (window.location.host.indexOf('fcrmyx.oytour.com') != -1) {
domainUrl = "http://crm.oytour.com"
domainPostUrl = "http://reborn.oytour.com"
} else {
domainUrl = "http://testcrm.oytour.com"
domainPostUrl = "http://testapi.oytour.com"
}
}
var obj = {
//主地址
DomainUrl: domainUrl,
DownLoadContractUrl: domainPostUrl,
//阿里服务器地址
AliUrl: "https://reborndev.oss-cn-hangzhou.aliyuncs.com",
//CRMApi地址
PostUrl: domainUrl,
//旅游ERPApi地址
ERPApiUrl: domainPostUrl + "/api/common/post",
javaUrl: locationName.indexOf('testb2b') == -1 ? "http://efficient.oytour.com" : locationName.indexOf('testb2b.oytour.com') !== -1 ? "http://47.96.12.235:9001" : "http://192.168.10.215:9000",
ViittoFileUrl: locationName.indexOf('oytour') !== -1 || locationName.indexOf('testb2b.oytour.com') !== -1 ? "http://staticfile.oytour.com" : 'http://192.168.10.214:8130',
UploadUrl: locationName.indexOf('oytour') !== -1 || locationName.indexOf('testb2b.oytour.com') !== -1 ? "http://uploadfile.oytour.com" : "http://192.168.10.214:8120",
erpRoutingUrl: locationName.indexOf('oytour') !== -1 || locationName.indexOf('testb2b.oytour.com') !== -1 ? "http://yx.oytour.com/#/" : "http://localhost:8081/#/"// "http://www.test.com:8081/#/",
};
return obj;
},
Vue.prototype.domainManager = domainManager,
//消息成功提示
Vue.prototype.Success = function(msg) {
......@@ -171,6 +135,7 @@ export default {
if (msg == null || msg == "") {
msg = {}
}
console.log('CRM接口', this)
var apiurl = this.domainManager().PostUrl + cmd;
var timestamp = (new Date()).valueOf();
var token = "";
......
<template>
<div class="q-pa-md customerLib">
<div class="q-py-md">
<div class="flex justify-between q-mb-md">
<div>
<q-btn color="primary" unelevated label="+新增客户库" class="q-mr-md" @click="jumpPage('/editor/editCustomerLib')"/>
<n-popover trigger="hover" :width="620" placement="right">
<template #trigger>
<span>了解多客户库的好处</span>
</template>
<div class="q-pa-md">
<div class="tip-title">
有效实现业务独立,提高管理效率,降低客户资源浪费
</div>
<div class="tip-content">
<div class="tip-header">
多公海
</div>
<div class="tip-text">
每个客户库都有自己独立的公海,对本客户库外的数据隔离,有效提高线索流转的效率。
</div>
<div class="tip-list-text">
<div style="white-space:nowrap">
例如:
</div>
<div>
场景一:设置“北京客户库”、“南京客户库” 保持业务的独立性;<br/>
场景二:设置“市场客户库”、“电销客户库”、“面销客户库”,区分客户的流转阶段/属性;
</div>
</div>
</div>
<div class="tip-content">
<div class="tip-header">
多规则
</div>
<div class="tip-text">
每个客户库都有自己独立的规则,不同部门之间可能存在不同的规则:
</div>
<div class="tip-list-text">
<div style="white-space:nowrap">
例如:
</div>
<div>
回收客户的规则:不同的部门/员工,可设置不同的客户回收规则,合理利用资源;<br/>
员工的客户上限:不同的部门/员工,设置不同的客户上限,降低资源浪费的可能;<br/>
录入客户的标准:有的部门支持创建重复客户,有的部门则不支持;
</div>
</div>
</div>
</div>
</n-popover>
</div>
<q-btn text-color="accent" outline unelevated label="设置查重规则" @click="showRuleDig=true" />
</div>
<q-table class="sticky-right-column-table" :rows="list" flat :columns="columns" hide-bottom row-key="Id">
<template v-slot:body-cell-range="props">
<q-td :props="props">
<div v-for="(item,index) in props.row.DeptList" :key="index">{{item.DeptName}}</div>
</q-td>
</template>
<template v-slot:body-cell-seasRule="props">
<q-td :props="props">
<div>查看规则</div>
</q-td>
</template>
<template v-slot:body-cell-libRule="props">
<q-td :props="props">
<div>查看规则</div>
</q-td>
</template>
<template v-slot:body-cell-IsAllowRepeat="props">
<q-td :props="props">
<div v-if="props.row.IsAllowRepeat===1"></div>
<div v-if="props.row.IsAllowRepeat===2"></div>
</q-td>
</template>
<template v-slot:body-cell-operate="props">
<q-td :props="props">
<q-btn color="accent" flat label="编辑" />
<q-btn color="negative" flat label="删除" />
</q-td>
</template>
</q-table>
</div>
<!-- 查重规则弹窗 -->
<q-dialog v-model="showRuleDig" full-height maximized position="right" transition-show="slide-left">
<q-card>
<q-card-section class="row items-center q-pb-none">
<div class="text-h6">查重规则设置 </div>
<q-space />
<q-btn icon="close" flat round dense v-close-popup />
</q-card-section>
<q-card-section>
<div style="width:750px">
<q-item tag="label">
<q-item-section avatar top>
<q-radio v-model="LookRepeat" :val="1" color="primary" />
</q-item-section>
<q-item-section>
<div class="ruleset-title" style="color:#858598"><span style="color:#606266">全局查重</span> (一个客户在<span
style="color:#f00">整个企业</span>内,只允许被一个员工跟进)</div>
<div class="ruleset-box">
<div class="ruleset-txt">
一个客户,在整个企业内查重(整个企业不允许创建重复客户)
</div>
<div class="ruleset-img">
<img src="@/assets/images/customer/customerSet/rule1.jpg" />
</div>
</div>
</q-item-section>
</q-item>
<q-item tag="label">
<q-item-section avatar top>
<q-radio v-model="LookRepeat" :val="2" color="primary" />
</q-item-section>
<q-item-section>
<div class="ruleset-title" style="color:#858598"><span style="color:#606266">客户库查重</span> (一个客户在<span
style="color:#f00">单个客户库</span>内,只允许被一个员工跟进)</div>
<div class="ruleset-box">
<div class="ruleset-txt">
一个客户,在整个企业内查重(整个企业不允许创建重复客户)
</div>
<div class="ruleset-img">
<img src="@/assets/images/customer/customerSet/rule2.jpg" />
</div>
</div>
</q-item-section>
</q-item>
</div>
</q-card-section>
<q-card-actions align="left" class="bg-white text-teal">
<q-btn color="primary" unelevated label="确定" class="q-ml-lg" @click="setLookRepeatRule" />
</q-card-actions>
</q-card>
</q-dialog>
</div>
</template>
<script lang="ts">
import {
defineComponent,
reactive,
toRefs,
onMounted
} from 'vue'
// import { Dialog } from 'quasar'
import message from '@/utils/message'
import customerSetService from '@/api/customerSet';
import router from '@/router/index'
export default defineComponent({
setup() {
const msg = reactive({
Name: ""
})
const data = reactive({
list: [],
columns: [{
name: 'Name',
label: '名称',
field: 'Name',
align: 'left',
}, {
name: 'range',
label: '适用范围',
field: 'range',
align: 'left',
}, {
name: 'seasRule',
label: '公海回收规则',
field: 'seasRule',
align: 'left',
}, {
name: 'libRule',
label: '私库限额规则',
field: 'libRule',
align: 'left',
}, {
name: 'IsAllowRepeat',
label: '是否允许重复客户',
field: 'IsAllowRepeat',
align: 'left',
}, {
name: 'UpdateTime',
label: '创建时间',
field: 'UpdateTime',
align: 'left',
}, {
name: 'operate',
label: '操作',
field: 'operate',
align: 'left',
style: 'width: 200px'
}],
showRuleDig: false
})
const getList = () => {
customerSetService.getCustomerLibraryList(msg).then(res => {
data.list = res.data.Data
})
}
const LookRepeatMsg = reactive({ //查重规则 1客户库查重 2全局查重
LookRepeat: 1
})
const getLookRepeatRule = () => {
customerSetService.getCustomerLibraryLookRepeat({}).then(res => {
LookRepeatMsg.LookRepeat = res.data.Data
})
}
const setLookRepeatRule = () => {
customerSetService.setCustomerLibraryLookRepeat(LookRepeatMsg).then(res => {
message.successMsg(res.data.Message)
data.showRuleDig = false
})
}
const jumpPage = (url: string, Id = 0) => {
router.push({
path: url,
query: {
Id
}
})
}
onMounted(() => {
getList()
getLookRepeatRule()
})
return {
...toRefs(data),
...toRefs(LookRepeatMsg),
setLookRepeatRule,
jumpPage
}
}
})
</script>
<style lang="scss" scoped>
.ruleset-title {
line-height: 40px;
}
.ruleset-box {
width: 606px;
align-items: center;
color: #333;
padding: 30px 0 42px 0;
margin: 10px 0;
border-radius: 8px;
border: 1px solid #ccc;
.ruleset-txt {
text-align: center;
}
.ruleset-img {
margin-top: 24px;
display: flex;
justify-content: center;
}
}
.tip-title {
font-size: 18px;
margin-bottom: 15px;
font-weight: bolder;
}
.tip-content {
background-color: #f4f4f6;
padding: 30px;
border-radius: 10px;
margin-bottom: 15px;
}
.tip-header {
color: var(--q-accent);
font-size: 18px;
font-weight: bolder;
margin-bottom: 15px;
}
.tip-text {
font-size: 14px;
margin-bottom: 5px;
}
.tip-list-text {
color: var(--q-accent);
font-size: 12px;
display: flex;
align-items: baseline;
justify-content: flex-start;
font-weight: 500;
line-height:22px;
}
</style>
<template>
<div class="q-mt-md q-pa-md CustomerOperation flex">
<div class="customer-operation-category">
<q-btn label="保存" @click="setCustomerConfig"></q-btn>
<div v-for="item in leftList" :key="item.Id" :class="{ active: curItem == item.Id }"
class="customer-operation-category--item" @click="changeActive(item)">{{ item.Name }}</div>
</div>
<div class="customer-operation-content">
<div v-if="curItem == 1" class="row">
<div class="collaborator-title">领取公海客户:</div>
<div class="col-10">
<div class="flex items-center">
<q-toggle v-model="data.IsPublicAudit" :true-value="1" :false-value="2" />
开启后,员工领取公海客户时需要企微端设置的部门上级审批,审批同意后才可领取。
<n-popover trigger="hover" placement="right-start">
<template #trigger>
<q-icon name="help" size="xs" color="grey-4" />
</template>
<div class="tipBox">
<div class="text-center q-mb-xs">部门上级:指在【企业微信后台】-【通讯录】设置的部门上级</div>
<img src="@/assets/images/tip/dept-superior.png" style="width: 450px" />
</div>
</n-popover>
</div>
<div class="flex items-center">
<q-toggle v-model="PublicGetDeptState" @update:model-value="ChangePublicGetDeptState" />以下部门允许领取公海客户
<q-btn color="primary" :disable="!PublicGetDeptState" unelevated label="修改" size="xs" class="q-ml-lg"
@click="checkedPublicGetDept" />
</div>
<div v-if="PublicGetDeptState">
<div v-for="(item, index) in PublicGetDeptNameList" :key="index" class="dept-item">
<div class="flex justify-center items-center">
<q-icon name="work" color="primary" v-if="item.Type == 1" />
<q-icon name="person" color="primary" v-if="item.Type == 2" />
<span class="q-ml-sm">{{ item.DeptName }}</span>
</div>
</div>
</div>
</div>
</div>
<div v-if="curItem == 2">
<Abandon v-model:AabandonType="data.AbandonType" v-model:AbandonDept="data.AbandonDept"
v-model:AbandonAudit="data.AbandonAudit" :AbandonReasonList="data.AbandonReasonList"
v-model:AbandonReason="data.AbandonReason" v-model:AbandonSelectType="data.AbandonSelectType"
:AbandonEmpList="data.AbandonEmpList" :AbandonDeptList="data.AbandonDeptList" />
</div>
<div v-if="curItem == 3" class="row">
<div class="collaborator-title">申请成为协作人:</div>
<div class="col-10">
<div class="flex items-center">
<q-toggle v-model="data.ApplyForTeamDept" :true-value="1" :false-value="0" />以下部门允许申请成为协作人
<q-btn color="primary" label="修改" unelevated size="xs" class="q-ml-lg" @click="checkedPublicGetDept" />
</div>
<div>
<!-- <div v-for="item in items" :key="item.id">{{ item }}</div> -->
</div>
</div>
</div>
<div v-if="curItem == 4" class="row">
<div class="collaborator-title">添加协作人:</div>
<div class="col-10">
<div class="flex items-center">
<q-toggle v-model="data.AddTeamDept" :true-value="1" :false-value="0" />以下部门允许申请成为协作人
<q-btn color="primary" label="修改" unelevated size="xs" class="q-ml-lg" @click="checkedPublicGetDept" />
</div>
<div>
<!-- <div v-for="item in items" :key="item.id">{{ item }}</div> -->
</div>
<div class="flex">
<q-checkbox v-model="data.AddTeamDept" label="协作人被移除后(若该协作人已添加客户为好友),自动创建新的客户" />
</div>
</div>
</div>
<div v-if="curItem == 5">
<div class="row">
<div class="collaborator-title">转移客户:</div>
<div class="col-10">
<div class="flex items-center">
<q-toggle v-model="data.ApplyForTeamDept" :true-value="1" :false-value="0" />
以下部门允许转移客户
<q-btn color="primary" label="修改" unelevated size="xs" class="q-ml-lg" @click="checkedPublicGetDept" />
</div>
</div>
</div>
<div class="row">
<div class="collaborator-title">转移范围:</div>
<div class="col-10">
<div class="flex items-center">
<q-toggle v-model="data.ApplyForTeamDept" :true-value="1" :false-value="0" />
转移客户时允许选择的范围
<q-btn color="primary" label="设置" unelevated size="xs" class="q-ml-lg" />
</div>
</div>
</div>
</div>
<div class="row" v-if="curItem == 6">
<div class="collaborator-title">删除客户跟进记录:</div>
<div class="col-10">
<div class="flex items-center">
<q-toggle v-model="data.ApplyForTeamDept" :true-value="1" :false-value="0" />
开启后,允许员工删除自己填写的客户跟进记录
</div>
<div class="flex items-center">
<q-toggle v-model="data.ApplyForTeamDept" :true-value="1" :false-value="0" />
开启后,仅允许添加跟进记录后
<q-input outlined v-model="data.text" dense type="number" :min="0" style="width: 100px; margin: 0 10px" />
分钟内删除
</div>
</div>
</div>
<div v-if="curItem == 7">
<div class="row">
<div class="collaborator-title">签到拜访限制:</div>
<div class="col-10">
<div class="flex items-center">
<q-toggle v-model="data.ApplyForTeamDept" :true-value="1" :false-value="0" />
开启后,仅企业客户允许签到拜访
</div>
</div>
</div>
<div class="row">
<div class="collaborator-title">签到拜访拍照:</div>
<div class="col-10">
<div class="flex items-center">
<q-toggle v-model="data.ApplyForTeamDept" :true-value="1" :false-value="0" />
开启后,签到拜访时必须拍照
</div>
</div>
</div>
</div>
<div v-if="curItem == 8">
<div class="row">
<div class="collaborator-title" style="min-width: 140px">创建企业时查重方式:</div>
<div class="col-10">
<div class="q-gutter-sm">
<q-radio v-model="data.AbandonType" :val="1" label="模糊查重" />
<q-radio v-model="data.AbandonType" :val="2" label="精准查重 " />
</div>
</div>
</div>
<div class="row">
<div class="collaborator-title" style="min-width: 140px">创建企业客户:</div>
<div class="col-10">
<div class="flex items-center">
<q-toggle v-model="data.ApplyForTeamDept" :true-value="1" :false-value="0" />
仅支持通过工商信息库来创建企业
</div>
</div>
</div>
</div>
<div v-if="curItem == 9">
<div class="row flex justify-end">
<q-btn color="primary" label="添加自定义字段" unelevated />
</div>
</div>
</div>
<departmentStaff v-model="showDptDialog" :defaultArray="DptDataTreeDefault" nodeKey="newId" labelKey="DeptName"
childrenKey="ChildList" strategy="leaf" :treeData="DptDataTree" @select="getdpt" />
</div>
</template>
<script lang="ts">
import {
ref,
toRefs,
reactive,
defineComponent,
onMounted,
provide,
watch
} from 'vue'
import message from '@/utils/message'
import customerSetService from '@/api/customerSet'
import customerService from '@/api/customer'
import departmentStaff from '@/components/common/departmentStaff.vue'
import Abandon from './CustomerOperationComponents/Abandon.vue'
interface dataParams {
data: {
AbandonReasonList: string[]
[key: string]: any
}
[key: string]: any
}
export default defineComponent({
components: {
departmentStaff,
Abandon
},
setup() {
const leftList = ref([{
Id: 1,
Name: '领取公海客户'
},
{
Id: 2,
Name: '放弃到公海'
},
{
Id: 3,
Name: '申请成为协作人'
},
{
Id: 4,
Name: '添加协作人'
},
{
Id: 5,
Name: '转移客户'
},
{
Id: 6,
Name: '删除客户跟进记录'
},
{
Id: 7,
Name: '签到拜访设置'
},
{
Id: 8,
Name: '创建企业'
},
{
Id: 9,
Name: '写跟进'
}
])
let curItem = ref(1)
let DptDataTree = ref < any[] > ([])
let data = reactive < dataParams > ({
data: {
IsPublicAudit: 1,
PublicGetDept: -1,
AbandonType: 1,
AbandonAudit: 0,
AbandonReasonList: []
},
PublicGetDeptState: false, //以下部门允许领取公海客户
PublicGetDeptNameList: [], //允许领取公海客户列表
showDptDialog: false,
DptDataTreeDefault: [] //部门默认值
})
provide('DptDataTree', DptDataTree)
provide('data', data)
const changeActive = item => {
curItem.value = item.Id
}
//获取客户操作配置
const getConfig = () => {
customerSetService.getCustomerConfig({}).then(res => {
console.log('客户操作配置', res.data.Data)
data.data = res.data.Data
if (data.data.PublicGetDept == -1) {
data.PublicGetDeptState = false
} else {
data.PublicGetDeptState = true
}
const PublicGetDeptList = data.data.PublicGetDeptList.map(e => {
e.Type = 1
return e
})
const PublicGetEmpList = data.data.PublicGetEmpList.map(e => {
e.Type = 2
return e
})
data.PublicGetDeptNameList = [...PublicGetDeptList, ...PublicGetEmpList]
})
}
const getDptTree = () => {
customerService.getEmployeeData({}).then(res => {
DptDataTree.value = res.data.Data
if (DptDataTree.value.length == 0) return
DptDataTree.value.forEach(x => {
x.newId = x.DataType + '-' + x.DeptId
if (x.ChildList && x.ChildList.length > 0) {
getChildList(x.ChildList)
}
})
})
}
const getChildList = (ChildList: Array < any > ) => {
ChildList.forEach(x => {
x.newId = x.DataType + '-' + x.DeptId
if (x.ChildList && x.ChildList.length > 0) {
getChildList(x.ChildList)
}
})
}
//保存客户操作配置
const setCustomerConfig = () => {
customerSetService.setCustomerConfig(data.data).then(() => {
message.successMsg('设置成功')
getConfig()
})
}
//部门弹窗
const checkedPublicGetDept = () => {
data.showDptDialog = true
if (curItem.value === 1) {
// data.DptDataTreeDefault = data.data.PublicGetDept.split(',')
} else if (curItem.value === 2) {
// data.DptDataTreeDefault = data.data.AbandonDept.split(',')
}
}
const getdpt = val => {
data.showDptDialog = false
const ids = val.map(e => e.Id.slice(2)).toString()
const dept = JSON.parse(JSON.stringify(val))
if (curItem.value === 1) {
data.data.PublicGetDept = ids
data.PublicGetDeptNameList = dept
}
if (curItem.value === 2) {
data.data.AbandonDept = ids
data.data.AbandonDeptList = dept
}
console.log('dpt', dept)
}
//以下部门允许领取公海客户
const ChangePublicGetDeptState = val => {
if (!val) {
data.data.PublicGetDept = '-1'
}
}
watch(
() => data.data,
val => {
console.log('watch', val)
}, {
deep: true
}
)
onMounted(() => {
getDptTree()
getConfig()
})
return {
leftList,
curItem,
changeActive,
...toRefs(data),
DptDataTree,
getdpt,
checkedPublicGetDept,
setCustomerConfig,
ChangePublicGetDeptState
}
}
})
</script>
<style lang="scss" scoped>
.CustomerOperation {
background-color: rgb(243, 246, 249);
}
.customer-operation-category {
margin-right: 10px;
width: 200px;
flex: none;
background-color: #fff;
border-radius: 8px;
padding: 15px 0;
overflow: auto;
.customer-operation-category--item {
padding: 15px 25px;
cursor: pointer;
font-weight: 600;
user-select: none;
color: #333;
font-size: 14px;
}
.active {
background-color: #eaf0ff;
color: #3470ff;
}
}
.customer-operation-content {
background: #fff;
flex: 1;
border-radius: 8px;
padding: 25px;
overflow: auto;
}
.collaborator-title {
min-width: 80px;
line-height: 40px;
text-align: right;
margin-right: 10px;
font-weight: 600;
white-space: nowrap;
}
.dept-item {
border: 1px solid #e1f3d8;
background-color: #f0f9eb;
color: #67c23a;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
height: 32px;
line-height: 32px;
border-radius: 3px;
padding: 0 14px;
margin: 6px;
}
</style>
<template>
<div>
<div class="row">
<div class="collaborator-title">可放弃客户:</div>
<div class="col-10">
<div class="q-gutter-sm">
<q-radio v-model="Type" :val="1" @update:model-value="changeAbandonType" label="全部客户" />
<q-radio v-model="Type" :val="2" @update:model-value="changeAbandonType" label="部分客户" />
</div>
<div v-if="Type == 2">
<requestGroup v-model:addCondition="SelectType" :defaultData="AbandonKHSelect" @change="getCondition" />
</div>
</div>
</div>
<div class="row">
<div class="collaborator-title">可操作员工:</div>
<div class="col-10">
<div class="flex items-center">
<q-toggle v-model="AbandonDeptState" @update:model-value="ChangeAbandonDeptState" />以下部门允许将客户放弃到公海
<q-btn color="primary" :disable="!AbandonDeptState" label="修改" unelevated size="xs" class="q-ml-lg"
@click="checkedPublicGetDept" />
</div>
<div>
<div v-for="(item, index) in AbandonDeptNameList" :key="index" class="dept-item">
<div class="flex justify-center items-center">
<q-icon name="work" color="primary" v-if="item.Type == 1" />
<q-icon name="person" color="primary" v-if="item.Type == 2" />
<span class="q-ml-sm">{{ item.DeptName }}</span>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="collaborator-title">放弃后审核:</div>
<div class="col-10">
<div class="flex items-center">
<q-toggle v-model="Audit" :true-value="1" :false-value="2" @update:model-value="ChangeAbandonAuditState" />
开启后,员工放弃客户时需要企微端设置的部门上级审批,审批同意后才可放弃。
<n-popover trigger="hover" placement="right-start">
<template #trigger>
<q-icon name="help" size="xs" color="grey-4" />
</template>
<div class="tipBox">
<div class="text-center q-mb-xs">
部门上级:指在【企业微信后台】-【通讯录】设置的部门上级
</div>
<img src="@/assets/images/tip/dept-superior.png" style="width:450px" />
</div>
</n-popover>
</div>
</div>
</div>
<div class="row">
<div class="collaborator-title" style="padding-top: 15px">放弃原因:</div>
<div class="col-10">
<draggable v-model="ReasonList" tag="div" item-key="Id">
<template #item="{ element, index }">
<div class="flex items-center">
<i class="iconfont icon-drag" style="font-size: 20px; color: #777"></i>
<div class="AbandonReasonItem flex items-center">{{ element }}</div>
<n-popover trigger="hover">
<template #trigger>
<i class="iconfont icon-edit_light" style="font-size: 20px; color: #111"
@click="editAbandonReason(element, index)"></i>
</template>
<span>编辑</span>
</n-popover>
<n-popover trigger="hover">
<template #trigger>
<i class="iconfont icon-delete_light q-ml-md" style="font-size: 20px; color: #111"
@click="delAbandonReaso(index)"></i>
</template>
<span>删除</span>
</n-popover>
</div>
</template>
</draggable>
<div class="flex items-center q-pl-sm q-mt-md">
<q-btn color="primary" label="添加" unelevated class="q-ml-lg q-mr-md" @click="addAbandonReason" />
已设/可设上限:{{ ReasonList.length }}/10
</div>
<q-dialog v-model="AbandonReasonDialog">
<q-card>
<q-card-section class="row items-center q-pb-none">
<div class="text-h6" v-if="AbandonReasonType == 0">添加</div>
<div class="text-h6" v-if="AbandonReasonType == 1">编辑</div>
<q-space />
<q-btn icon="close" flat round dense v-close-popup />
</q-card-section>
<q-card-section class="q-py-lg" style="width: 460px; max-width: 600px">
<n-input maxlength="10" size="large" v-model:value="AbandonReasonText" show-count clearable
placeholder="请输入放弃原因" />
</q-card-section>
<q-card-actions align="right">
<q-btn flat label="取消" color="primary" v-close-popup />
<q-btn unelevated label="确定" color="primary" @click="saveAbandonReason" />
</q-card-actions>
</q-card>
</q-dialog>
</div>
</div>
<departmentStaff v-model="DptDialog" :defaultArray="DptDataDefault" nodeKey="newId" labelKey="DeptName"
childrenKey="ChildList" strategy="leaf" :treeData="DptDataTree" @select="getAbandonDpt" />
</div>
</template>
<script lang="ts">
import {
ref,
toRefs,
reactive,
defineComponent,
inject,
} from 'vue'
import departmentStaff from '@/components/common/departmentStaff.vue'
import requestGroup from '@/components/customer/request-group.vue'
import draggable from 'vuedraggable'
import message from '@/utils/message'
interface dataParams {
AbandonReasonList: string[]
[key: string]: any
}
export default defineComponent({
components: {
departmentStaff,
requestGroup,
draggable
},
props: {
AabandonType: {
type: Number,
default: 1
},
AbandonDept: {
type: String,
default: "-1"
},
AbandonDeptList: {
type: Array,
default: () => []
},
AbandonEmpList: {
type: Array,
default: () => []
},
AbandonAudit: {
type: Number,
default: 2
},
AbandonReason: {
type: String,
default: ''
},
AbandonReasonList: {
type: Array,
default: () => []
},
AbandonSelectType: { //可放弃部分客户的查询类型 1并且 2或者
type: Number,
default: 1
},
},
setup(props, ctx) {
const DptDataTree: any = inject('DptDataTree')
const FaData: any = inject('data')
let DptDialog = ref(false)
let DptDataDefault = ref < string[] > ([])
let data = reactive < dataParams > ({
AbandonDeptNameList: [], //可放弃到公海的部门
AbandonReasonDialog: false, //放弃原因弹窗
AbandonReasonText: '', //放弃原因
AbandonReasonIndex: 0, //放弃原因index
AbandonReasonType: 0, //修改类型 0:新增,1:编辑
AbandonDeptState: false, //以下部门允许将客户放弃到公海
AbandonReasonList: [],
})
const ReasonList = ref(props.AbandonReasonList)
if (props.AbandonDept == '-1') {
data.AbandonDeptState = false
} else {
data.AbandonDeptState = true
const dept = props.AbandonDeptList.map((e: any) => '1-' + e.DeptId)
const deptName = props.AbandonDeptList.map((e: any) => {
e.Type = 1
return e
})
const emp = props.AbandonEmpList.map((e: any) => '2-' + e.DeptId)
const empName = props.AbandonEmpList.map((e: any) => {
e.Type = 2
return e
})
DptDataDefault.value = [...dept, ...emp]
data.AbandonDeptNameList = [...deptName, ...empName]
}
//以下部门允许将客户放弃到公海
const ChangeAbandonDeptState = (val) => {
if (!val) {
ctx.emit("update:AbandonDept", '-1')
}
}
//放弃后是否需要审核
const ChangeAbandonAuditState = (val) => {
ctx.emit("update:AbandonAudit", val)
}
//部门弹窗
const checkedPublicGetDept = () => {
DptDialog.value = true
}
const getAbandonDpt = val => {
DptDialog.value = false
const ids = val.map(e => e.Id.slice(2)).toString()
const dept = JSON.parse(JSON.stringify(val))
data.AbandonDeptNameList = dept
ctx.emit("update:AbandonDept", ids)
}
//新增放弃原因
const addAbandonReason = () => {
data.AbandonReasonDialog = true
data.AbandonReasonText = '',
data.AbandonReasonIndex = 0
data.AbandonReasonType = 0
}
//修改放弃原因
const editAbandonReason = (val, index) => {
data.AbandonReasonDialog = true
data.AbandonReasonText = val,
data.AbandonReasonIndex = index
data.AbandonReasonType = 1
}
//保存放弃原因
const saveAbandonReason = () => {
if (!data.AbandonReasonText) {
message.warnMsg('不能为空')
return
}
if (data.AbandonReasonType === 0) {
ReasonList.value.push(data.AbandonReasonText)
} else {
ReasonList.value[data.AbandonReasonIndex] = data.AbandonReasonText
}
ctx.emit("update:AbandonReason", ReasonList.value.toString())
data.AbandonReasonDialog = false
data.AbandonReasonText = ''
}
// 删除放弃原因
const delAbandonReaso = (index: number) => {
ReasonList.value.splice(index, 1)
}
const changeAbandonType = (val: number) => {
ctx.emit('update:AabandonType', val)
}
const getCondition = (val) => {
FaData.data.AbandonKHSelect = JSON.parse(JSON.stringify(val))
}
return {
Type: ref(props.AabandonType),
Audit: ref(props.AbandonAudit),
SelectType: ref(props.AbandonSelectType),
AbandonKHSelect: ref(FaData.data.AbandonKHSelect),
ReasonList,
DptDataTree,
...toRefs(data),
DptDialog,
DptDataDefault,
ChangeAbandonDeptState,
ChangeAbandonAuditState,
addAbandonReason,
editAbandonReason,
saveAbandonReason,
delAbandonReaso,
checkedPublicGetDept,
getAbandonDpt,
changeAbandonType,
getCondition
}
}
})
</script>
<style lang="scss" scoped>
.collaborator-title {
min-width: 80px;
line-height: 40px;
text-align: right;
margin-right: 10px;
font-weight: 600;
white-space: nowrap;
}
.AbandonReasonItem {
width: 453px;
height: 50px;
line-height: 50px;
padding-left: 22px;
background-color: #fff;
border-radius: 5px;
border: 1px solid #d7d7d7;
margin: 10px;
}
.dept-item {
border: 1px solid #e1f3d8;
background-color: #f0f9eb;
color: #67c23a;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
height: 32px;
line-height: 32px;
border-radius: 3px;
padding: 0 14px;
margin: 6px;
}
.tipBox {
font-size: 12px;
line-height: 1.8;
}
</style>
<template>
<div style="padding: 0;margin: 0;">
<q-dialog v-model="Isadd" persistent>
<q-card style="width: 700px; max-width: 80vw;" class="addfield">
<q-card-section>
<div class="text-h6">新建/修改字段</div>
</q-card-section>
<q-separator />
<q-card-section class="q-pt-none" style="margin-top: 15px;">
<q-splitter v-model="splitterModel">
<template v-slot:before>
<q-tabs v-model="tab" vertical @click="getdefault">
<q-tab v-for="(x,y) in tabsList" :key='y' :name="x.Id" :label="x.Name"
:disable="x.disable" />
</q-tabs>
</template>
<template v-slot:after>
<div style="height: 100%; width: 100%;padding: 16px">
<div class="rightrow" style="margin-top: 0px;">
<div class="leftwidth">字段名称:</div>
<q-input clearable outlined v-model="addMsg.Name" counter maxlength="8" placeholder="请输入字段名称"
:rules="[val => !!val || '请输入字段名称']" dense style="width: 400px;" />
</div>
<div class="rightrow" style="margin-top: 0px;">
<div class="leftwidth">部门选择:</div>
<div>
<q-radio v-model="departmentType" val="1" label="公司字段"
@input='addMsg.DeptId=0' />
<q-radio v-model="departmentType" val="2" label="部门字段"
@input='addMsg.DeptId=0' />
<div v-if="departmentType==2">
<el-tree :treeData='deptl' :defaultArray="returnString" nodeKey="DeptId" style="width: 300px;"
:multiple="false" labelKey="DeptName" childrenKey="ChildList"
tipText="选择部门" @getChild="getChild" classStr="col-6 q-pb-lg">
</el-tree>
</div>
</div>
</div>
<div class="rightrow" v-if='tab==1'>
<div class="leftwidth">字数限制:</div>
<q-input v-model.number="addMsg.WordNum" outlined dense type="number" :max='50' placeholder="请输入"
:min='0' maxlength="8" style="width: 400px" />
</div>
<div class="rightrow" v-if='tab==1'>
<div class="leftwidth">输入类型:</div>
<q-checkbox v-model="addMsg.InputType" v-for="(x,y) in checkboxList" :key='y'
:val="x.Id" :label="x.Name" />
</div>
<div class="rightrow" v-if='tab==2'>
<div class="leftwidth">文字字数:</div>
<q-input v-model.number="addMsg.WordNum" outlined dense type="number" :max='2000' placeholder="请输入"
:min='0' maxlength="2000" style="width: 400px" />
</div>
<div class="rightrow" v-if='tab==3|| tab==4'>
<div class="leftwidth">列表选项:</div>
<draggable v-model="addMsg.OptionsList" item-key="Id">
<template #item="{ element ,index}">
<div style="display: flex;margin-bottom: 10px;">
<i class="iconfont icon-drag"
style="font-size: 26px;color: #777;margin-right: 10px;"></i>
<q-input v-model.number="element.Name" outlined dense clearable placeholder="请输入"
style="width: 250px" />
<div
style="display: flex;align-items: center;margin-top: 8px;margin-left: 10px;height: 26px;">
<div style="width: 26px;height: 26px;margin-right: 10px;"
v-if="addMsg.OptionsList.length>1">
<q-btn round size="xs" color="deep-orange" icon="delete"
@click="addMsg.OptionsList.splice(index,1)" />
</div>
<div style="width: 26px;height: 26px;"
v-if="addMsg.OptionsList.length==index+1">
<q-btn round size="xs" color="primary" icon="add"
@click="addMsg.OptionsList.push({Id:0,Name:''})" />
</div>
</div>
</div>
</template>
</draggable>
</div>
<div class="rightrow" v-if='tab==7'>
<div class="leftwidth">小数位数:</div>
<q-select filled stack-label option-value="Id" option-label="Id"
style="width: 300px;" v-model="addMsg.Digits" ref="Id" :options="decimalslist"
dense map-options />
</div>
<div class="rightrow">
<div class="leftwidth">属性设置:</div>
<div class="q-gutter-sm">
<q-toggle v-model="addMsg.Required" label="必填项" :true-value="1"
:false-value="2" />
<q-toggle v-model="addMsg.Enable" label="启用" :true-value="1" :false-value="2" />
</div>
</div>
<!-- <el-form label-width="120px">
<el-form-item label="部门选择:">
<div v-if="departmentType==2">
</div>
</el-form-item>
</el-form> -->
</div>
</template>
</q-splitter>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white">
<q-btn class="q-mr-md" label="取消" @click="getcancel()" />
<q-btn color="accent" class="q-mr-md" label="确定" @click="savewb()" :loading="loading" />
</q-card-actions>
</q-card>
</q-dialog>
</div>
</template>
<script>
import { defineComponent, ref, onMounted, } from '@vue/composition-api'
import draggable from "vuedraggable";
export default defineComponent({
components: { draggable },
props: {
passobj: {
type: Object,
default: () => { return {} },
},
passdeptl: {
type: Array,
default: () => [],
},
passtabsList: {
type: Array,
default: () => [],
},
passcheckboxList: {
type: Array,
default: () => [],
},
},
setup(props, ctx) {
// const $q = useQuasar()
let addMsg = ref(props.passobj)
let deptl = ref(props.passdeptl)
let departmentType = ref('1')
let tab = ref(1)
let loading = ref(false)
let tabsList = ref(props.passtabsList)
let checkboxList = ref(props.passcheckboxList)
let decimalslist = ref([{ Id: 0 }, { Id: 1 }, { Id: 2 }, { Id: 3 }, { Id: 4 }, { Id: 5 }, { Id: 6 }, { Id: 7 }, { Id: 8 }, { Id: 9 }, { Id: 10 }, { Id: 11 },])
let returnString = ref([])
if (addMsg.value.Id > 0) {
tab.value = addMsg.value.Type
addMsg.value.InputType = addMsg.value.InputType.split(',').map(Number)
if (addMsg.value.DeptId > 0) {
departmentType.value = '2'
returnString.value = []
setTimeout(() => {
returnString.value.push(addMsg.value.DeptId)
}, 500)
}
tabsList.value.forEach(x => {
if (x.Id != addMsg.value.Type) {
x.disable = true
}
})
} else {
addMsg.value.InputType = [];
checkboxList.value.forEach((x) => {
addMsg.value.InputType.push(x.Id)
})
}
const getcancel = () => {
ctx.emit('getcancel', 1)
}
const savewb = () => {//数据保存
let submitobj = JSON.parse(JSON.stringify(addMsg.value))
submitobj.Type = tab.value //先类型赋值
if (submitobj.Name == '') {
$q.notify({
type: 'negative',
message: `请填写字段名称`,
position: 'top'
})
return
}
if ((submitobj.Type == 1 || submitobj.Type == 2) && submitobj.WordNum == '') {
$q.notify({
type: 'negative',
message: `字数不能为空`,
position: 'top'
})
return
}
if (departmentType.value == '2' && (submitobj.DeptId == 0 || submitobj.DeptId == '')) {
$q.notify({
type: 'negative',
message: `请选择部门`,
position: 'top'
})
return
}
if (submitobj.Type == 3 || submitobj.Type == 4) {
for (let i = 0; i < submitobj.OptionsList.length; i++) {
let x = submitobj.OptionsList[i]
if (x.Name == ''|| x.Name == null) {
$q.notify({
type: 'negative',
message: `请填写列表选项值`,
position: 'top'
})
return
}
submitobj.OptionsList[i].Id = i + 1
}
}
if (submitobj.Type == 1) {
if (submitobj.InputType.length == 0) {
$q.notify({
type: 'negative',
message: `至少选择一种输入类型`,
position: 'top'
})
return
} else {
submitobj.InputType = submitobj.InputType.join(',')
}
} else {
submitobj.InputType = '1'
}
loading.value = true
Vue.prototype.apipost.setCustomerFiledInfo(submitobj).then((res) => {
if (res.data.Code == 1) {
console.log(res)
$q.notify({
icon: 'iconfont icon-chenggong',
color: 'accent',
timeout: 2000,
message: res.data.Message,
position: 'top'
})
loading.value = false
ctx.emit('getcancel', 2)
} else {//如果移动失败 重新获取列表
loading.value = false
}
})
}
const getChild = (deptArray) => {
var tempStr = "";
if (deptArray && deptArray != '') {
tempStr = deptArray;
}
addMsg.value.DeptId = tempStr
}
const getdefault = () => {//切换模式的时候 初始化
addMsg.value = {
Id: 0,
Type: 1,
Name: '',
Required: 2,
DeptId: 0,
WordNum: 1,
InputType: '',
OptionsList: [{
Id: 1,
Name: ''
}],
Digits: 0,
Enable: 1,
};
}
return {
getcancel,//关闭弹窗
savewb,//保存数据
Isadd: ref(true),
addMsg,
deptl,
tabsList,
checkboxList,
splitterModel: ref(13),
tab,
departmentType,
decimalslist,
returnString,
getChild,
loading,
getdefault
}
}
})
</script>
<style>
.addfield .rightrow {
display: flex;
margin-top: 10px;
align-items: flex-start;
}
.addfield .leftwidth {
width: 120px;
text-align: right;
height: 40px;
vertical-align: middle;
line-height: 40px;
font-size: 14px;
color: #606266;
padding: 0 12px 0 0;
}
</style>
\ No newline at end of file
<template>
<div class="q-pa-md businessModel">
<div class="full-width business-header">
请选择您的业务模式:
</div>
<div class="box-businessModel" :class="{ 'checked-border': msg.type == 1 }" @click="chooseChange(1)">
<i class="iconfont icon-customer" style="font-size:38px"></i>
<div>个人客户</div>
<div class="right" v-if="msg.type == 1">
<q-icon name="check" class="check-icon" />
</div>
</div>
<div class="box-businessModel" :class="{ 'checked-border': msg.type == 2 }" @click="chooseChange(2)">
<div >
<i class="iconfont icon-customer" style="font-size:38px"></i>
<span class="q-mx-md" style="font-size:28px">+</span>
<i class="iconfont icon-qiye" style="font-size:38px"></i>
</div>
<div>个人客户 + 企业客户</div>
<div class="right" v-if="msg.type == 2">
<q-icon name="check" class="check-icon" />
</div>
</div>
</div>
</template>
<script lang="ts">
import {
defineComponent,
reactive,
} from 'vue'
export default defineComponent({
setup() {
const msg = reactive({
type: 1
})
const chooseChange = (n) => {
msg.type = n;
console.log(msg)
}
return {
msg,
chooseChange
}
}
})
</script>
<style lang="scss" scoped>
.businessModel {
overflow: hidden;
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 700px;
justify-content: space-between;
margin-top: 191px;
.business-header {
font-size: 16px;
font-weight: 700;
margin-bottom: 15px;
}
.box-businessModel {
cursor: pointer;
width: 311px;
height: 198px;
background: #f9f9f9;
border-radius: 5px;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
position: relative;
.right {
position: absolute;
right: -20px;
bottom: -20px;
display: block;
width: 0;
height: 0;
transform: rotate(45deg);
border: 20px solid transparent;
border-left: 20px solid #3470ff;
.check-icon {
position: absolute;
top: -8px;
left: -19px;
transform: rotate(-45deg);
font-size: 14px;
font-weight: 700;
color: #fff;
}
}
}
}
.checked-border {
background-color: #edf2fd !important;
border: 2px solid #3470ff !important;
color: #3470ff;
}
</style>
<template>
<div class="q-pa-md">
<div class="q-gutter-lg">
<q-radio v-model="configMsg.ClueType" :val="2" @update:model-value="configChange"> 自动分配
<n-popover trigger="hover" placement="right-start">
<template #trigger>
<q-icon name="help" size="xs" color="grey-4" />
</template>
<div class="tipBox">
场景一:线索认定有效后,流转至待分配客户库,进行自动分配;<br />
场景二:将客户导入到待分配客户库,进行自动分配;
</div>
</n-popover>
</q-radio>
<q-radio v-model="configMsg.ClueType" :val="1" @update:model-value="configChange">手动分配
<n-popover trigger="hover" placement="right-start">
<template #trigger>
<q-icon name="help" size="xs" color="grey-4" />
</template>
<div class="tipBox">
在线索库或待分配客户库,可以进行手动分配
</div>
</n-popover>
</q-radio>
</div>
<div class="q-pa-md" v-if="configMsg.ClueType === 2">
<div class="aoto-matic-header">
若客户满足多条规则,会优先按照编号顺序执行规则
<q-btn color="primary" label="+新增规则" style="margin-left: 10px;" @click="jumpPage('/editor/editRule')" />
</div>
<table class="payTable">
<thead>
<tr>
<th>排序</th>
<!-- <th>查询类型</th> -->
<th>规则</th>
<th>部门列表</th>
<th>人员列表</th>
<th>分配方式</th>
<th width="10%">编辑</th>
<th width="5%"></th>
</tr>
</thead>
<tr v-if="data.length == 0 && defaultData.length == 0">
<td :colspan="9" align="center">暂无数据</td>
</tr>
<template v-if="defaultData.length > 0">
<tr v-for="(item,index) in defaultData" :key="index">
<td></td>
<!-- <td>
<span v-if="item.RuleSelectType === 1">并且</span>
<span v-if="item.RuleSelectType === 2">或者</span>
</td>-->
<td>
<div v-if="item.ConditionStrList.length > 0">
<div v-for="(e,i) in item.ConditionStrList" :key="i">{{ e }}</div>
</div>
<div v-else>未匹配设定规则的客户</div>
</td>
<td>
<div v-if="item.DeptList.length > 0">
<div v-for="(e) in item.DeptList" :key="e.DeptId">{{ e.DeptName }}</div>
</div>
<div v-else>-</div>
</td>
<td>
<div v-if="item.EmpList.length > 0">
<div v-for="(e) in item.EmpList" :key="e.Id">{{ e.EmployeeName }}</div>
</div>
<div v-else>-</div>
</td>
<td>
<span v-if="item.RuleAllotWay === 1">依次轮流</span>
<span v-if="item.RuleAllotWay === 2">随机分配</span>
</td>
<td>
<q-btn flat size="xs" icon="edit" style="font-weight:400;color: #3FC4FF" class="q-mr-xs" label="编辑"
@click="jumpPage('/editor/editRule', item.Id)" />
</td>
<td></td>
</tr>
</template>
<draggable v-model="data" tag="tbody" item-key="Id" @update="datadragEnd" v-if="data.length > 0"
:move="getDragId">
<template #item="{ element, index }">
<tr>
<td>
<span style="color:#606266">{{ index + 1 }}</span>
</td>
<!-- <td>
<span v-if="element.RuleSelectType === 1">并且</span>
<span v-if="element.RuleSelectType === 2">或者</span>
</td>-->
<td>
<div v-if="element.ConditionStrList.length > 0">
<div v-for="(e,i) in element.ConditionStrList" :key="i">{{ e }}</div>
</div>
<div v-else>未匹配设定规则的客户</div>
</td>
<td>
<div v-if="element.DeptList.length > 0">
<div v-for="(e) in element.DeptList" :key="e.DeptId">{{ e.DeptName }}</div>
</div>
<div v-else>-</div>
</td>
<td>
<div v-if="element.EmpList.length > 0">
<div v-for="(e) in element.EmpList" :key="e.Id">{{ e.EmployeeName }}</div>
</div>
<div v-else>-</div>
</td>
<td>
<span v-if="element.RuleAllotWay === 1">依次轮流</span>
<span v-if="element.RuleAllotWay === 2">随机分配</span>
</td>
<td>
<q-btn flat size="xs" icon="edit" style="font-weight:400;color: #3FC4FF" class="q-mr-xs" label="编辑"
@click="jumpPage('/editor/editRule', element.Id)" />
<q-btn v-if="element.IsDefault != 1" flat size="xs" icon="delete" color="negative" class="q-mr-xs"
label="删除" @click="del(element.Id)" />
</td>
<td>
<i class="iconfont icon-drag" style="font-size: 20px;color: #777;"></i>
</td>
</tr>
</template>
</draggable>
</table>
</div>
<div class="q-pa-md manual" v-if="configMsg.ClueType === 1">
<div class="box-manual" :class="{ 'checked-border': configMsg.ClueAllotWay == 1 }"
@click="configMsg.ClueAllotWay = 1; configChange()">
<q-icon name="autorenew" size="lg" />
<div>按已选员工顺序依次轮流分配</div>
<div class="right" v-if="configMsg.ClueAllotWay == 1">
<q-icon name="check" class="check-icon" />
</div>
</div>
<div class="box-manual" :class="{ 'checked-border': configMsg.ClueAllotWay == 2 }"
@click="configMsg.ClueAllotWay = 2; configChange()">
<q-icon name="shuffle" size="lg" />
<div>按已选员工随机分配</div>
<div class="right" v-if="configMsg.ClueAllotWay == 2">
<q-icon name="check" class="check-icon" />
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import {
ref,
defineComponent,
reactive,
onMounted
} from 'vue'
import draggable from "vuedraggable";
import {
Dialog
} from 'quasar'
import message from '@/utils/message'
import customerSetService from '@/api/customerSet';
import router from '@/router/index'
interface dataType {
Id: number,
RuleSelectType: number,
IsDefault: number,
DeptList: Array < any > ,
EmpList: Array < any > ,
RuleAllotWay: number,
ConditionList: Array < any > ,
ConditionStrList: Array < string > ,
Sort: number
}
interface dataConfigType {
Id ? : number,
ClueType: number,
ClueAllotWay: number
}
export default defineComponent({
components: {
draggable,
},
setup() {
let defaultData = ref < Array < dataType >> ([])
let data = ref < Array < dataType >> ([])
let dragId = ref(0)
const getDragId = (e: any) => {
dragId.value = e.draggedContext.element.Id
}
const datadragEnd = (e: any) => {
let olddragId = 0
if (e.newIndex > 0) {
olddragId = data.value[e.newIndex - 1].Id
} else {
olddragId = 0
}
let msg = {
RuleId: dragId.value,
Type: 1,
TargetId: olddragId,
}
setState(msg)
}
const getClueList = () => {
defaultData.value = []
data.value = []
customerSetService.getCustomerClueRuleList({}).then(res => {
res.data.Data.map((item: dataType) => {
if (item.IsDefault === 1) {
defaultData.value.push(item)
} else {
data.value.push(item)
}
})
})
}
let configMsg = reactive < dataConfigType > ({
ClueType: 1, // 1手动分配 2自动分配
ClueAllotWay: 1, // 1依次轮流 2随机分配
})
const getCustomerClueRuleConfig = () => {
customerSetService.getCustomerClueRuleConfig({}).then(res => {
let data = res.data.Data
configMsg.ClueType = data.ClueType
configMsg.ClueAllotWay = data.ClueAllotWay
})
}
const jumpPage = (url: string, Id = 0) => {
router.push({
path: url,
query: {
Id
}
})
}
const del = (id) => {
Dialog.create({
title: "提示",
message: "确认删除?",
cancel: '取消',
ok: "确认"
}).onOk(() => {
console.log('>>>> OK', id)
let msg = {
RuleId: id,
Type: 2,
}
setState(msg)
})
}
const configChange = () => {
customerSetService.setCustomerClueRuleConfig(configMsg).then(res => {
console.log(228, res.data)
})
}
const setState = (msg) => {
customerSetService.setCustomerClueRuleState(msg).then(res => {
message.successMsg(res.data.Message)
if (msg.Type == 2) {
getClueList()
}
})
}
onMounted(() => {
getClueList();
getCustomerClueRuleConfig();
})
return {
getDragId,
configChange,
defaultData,
data,
datadragEnd,
jumpPage,
configMsg,
del
}
}
})
</script>
<style lang="scss" scoped>
.payTable {
width: 100%;
border-collapse: collapse;
}
.payTable tr th {
background: #fff;
height: 60px;
font-size: 12px;
font-weight: bold;
color: #2d2d2d;
background: #dddee0;
}
.payTable tr {
background: #fff;
text-align: center;
height: 60px;
}
.payTable tr td {
font-size: 13px;
text-align: center;
color: #2d2d2d;
box-sizing: border-box;
padding: 10px;
font-weight: bold;
border-bottom: 1px solid #e5e5e5;
}
.aoto-matic-header {
display: flex;
align-items: center;
justify-content: flex-end;
margin-bottom: 10px;
margin-left: 2px;
color: #858598;
font-size: 14px;
}
.manual {
height: 100%;
display: flex;
margin: 0 auto;
width: 800px;
justify-content: space-around;
margin-top: 191px;
.box-manual {
cursor: pointer;
width: 311px;
height: 198px;
background: #f9f9f9;
border-radius: 5px;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
position: relative;
.right {
position: absolute;
right: -20px;
bottom: -20px;
display: block;
width: 0;
height: 0;
transform: rotate(45deg);
border: 20px solid transparent;
border-left: 20px solid #3470ff;
.check-icon {
position: absolute;
top: -8px;
left: -19px;
transform: rotate(-45deg);
font-size: 14px;
font-weight: 700;
color: #fff;
}
}
}
}
.checked-border {
background-color: #edf2fd !important;
border: 2px solid #3470ff !important;
color: #3470ff;
}
.tipBox {
font-size: 13px;
line-height: 1.8;
}
</style>
<template>
<div class="q-pa-md customerField">
<div style="text-align: right;">
<el-button color="accent" style="float:right;margin-bottom: 10px;" size="sm" class="q-mr-md" icon="add"
label="新增字段" @click="goaddfield()"></el-button>
</div>
<table class="payTable">
<thead>
<tr>
<th>字段名称</th>
<th>字段类型</th>
<th>所属部门</th>
<th>是否启用</th>
<th>是否必填</th>
<th width="13%">操作信息</th>
<th width="12%">操作</th>
<th width="4%"></th>
</tr>
</thead>
<tr v-if="data && data.length==0">
<td :colspan="8" align="center">暂无数据</td>
</tr>
<draggable v-model="data" tag="tbody" item-key="Id" :move="getdata" @update="datadragEnd">
<template #item="{ element }">
<tr>
<td><span>{{element.Name}}</span></td>
<td>
<span v-if="element.IsSystem!=1">{{element.TypeName}}</span>
<span v-if="element.IsSystem==1">系统字段</span>
</td>
<td><span>{{element.DeptName}}</span></td>
<td>
<q-toggle v-if="element.IsLock==1" v-model="element.Enable" :true-value="1" :false-value="2"
icon="lock" disable />
<q-toggle v-else v-model="element.Enable" :true-value="1" :false-value="2"
@update:model-value="godelete(element,1)" />
</td>
<td>
<q-toggle v-model="element.Required" :true-value="1" :false-value="2"
@update:model-value="godelete(element,2)" />
</td>
<td>
<div>{{element.UpdateByName}}</div>
<div>{{element.UpdateTime}}</div>
</td>
<td>
<el-button flat size="xs" icon="edit" style="font-weight:400;color: #3FC4FF" class="q-mr-xs"
label="编辑" @click="goedit(element)" />
<el-button v-if='element.IsDefault!=1' flat size="xs" icon="delete" color="negative"
class="q-mr-xs" label="删除" @click="godelete(element,3)" />
</td>
<td>
<i class="iconfont icon-drag" style="font-size: 20px;color: #777;"></i>
</td>
</tr>
</template>
</draggable>
</table>
<div>
<addfield v-if="Isadd" @getcancel='getcancel' :passobj="addMsg" :passdeptl="deptl" :passtabsList="tabsList"
:passcheckboxList="checkboxList"></addfield>
</div>
</div>
</template>
<scr
<script>
import draggable from "vuedraggable";
import Vue from 'vue'
import addfield from './addfield.vue'
export default {
components: { draggable, addfield },
data() {
return {
data: [],
tabsList: [],
deptl: [],
checkboxList: [],
dragId: 0,
Isadd: false,
addMsg: {
Id: 0,
Type: 1,
Name: '',
Required: 2,
DeptId: 0,
WordNum: 1,
InputType: '',
OptionsList: [{
Id: 1,
Name: ''
}],
Digits: 0,
Enable: 1,
}
}
},
mounted() {
this.changePage()
this.getCustomerFieldType()
this.getDepartList()//获取部门
this.getFieldInputType()//输入类型
},
methods: {
changePage() {
this.apipost("/QYCustomer/GetCustomerFiledList", {Enable: -1}, (res) => {
if (res.data.resultCode == 1) {
this.data.value = res.data.Data
}
});
},
datadragEnd(e) {
// console.log('拖动结束',e)
let olddragId = 0
if (e.newIndex > 0) {
olddragId = data.value[e.newIndex - 1].Id
} else {
olddragId = 0
}
let msg = {
Type: 4,
FiledId: dragId.value,
SortFiledId: olddragId,
}
this.apipost('/QYCustomer/SetCustomerFiledState',msg,(res) => {
if (res.data.Code == 1) {
console.log(res)
} else {//如果移动失败 重新获取列表
this.changePage()
}
})
},
getdata(e) {
this.dragId.value = e.draggedContext.element.Id
},
setFiledState(item, Type) {
let msg = {
Type: Type,
FiledId: item.Id,
SortFiledId: 0,
}
this.apipost('/QYCustomer/SetCustomerFiledState',msg,(res) => {
if (res.data.Code == 1) {
this.$message.success({
icon: 'iconfont icon-chenggong',
color: 'accent',
timeout: 2000,
message: `操作成功`,
position: 'top'
})
this.changePage()
} else {//如果移动失败 重新获取列表
}
})
},
godelete(item, type) {
let text = ''
if (type == 1) {//tyep等于1和2的时候 先让弹出提示 然后再给予掉接口 所以把原本的值暂时先赋值给本身
text = '确定修改启用状态'
if (item.Enable == 2) {
item.Enable = 1
} else {
item.Enable = 2
}
} else if (type == 2) {
text = '确定修改必填状态'
if (item.Required == 2) {
item.Required = 1
} else {
item.Required = 2
}
} else if (type == 3) {
text = '删除后字段不在显示,是否删除?'
}
this.$confirm({
title: "提示信息",
message: text,
cancel: {
label: "取消",
flat: true
},
ok: {
label: "确认",
flat: true,
}
}).onOk(() => {
this.setFiledState(item, type)
});
},
getCustomerFieldType() {
this.apipost('/QYCustomer/GetCustomerFieldTypeEnumList',{},(res) => {
if (res.data.Code == 1) {
this.tabsList.value = res.data.Data
this.tabsList.value.forEach(x => {
x.disable = false
})
}
})
},
goedit(item) {
this.addMsg.value = JSON.parse(JSON.stringify(item))
this.Isadd.value = true;
},
goaddfield() {
this.Isadd.value = true;
this.addMsg.value = {
Id: 0,
Type: 1,
Name: '',
Required: 2,
DeptId: 0,
WordNum: 1,
InputType: '',
OptionsList: [{
Id: 1,
Name: ''
}],
Digits: 0,
Enable: 1,
};
},
getcancel(type) {
this.Isadd.value = false;
this.tabsList.value.forEach(x => {
x.disable = false
})
if (type == 2) {
this.changePage()
}
},
getDepartList() {
this.apipost('/User/GetDeptTree',{ School_Id: 0 },(res) => {
if (res.data.Code == 1) {
this.deptl.value = res.data.Data
}
})
},
getFieldInputType() {
this.apipost('/QYCustomer/GetCustomerFieldInputTypeEnumList', {},(res) => {
if (res.data.Code == 1) {
this.checkboxList.value = res.data.Data
}
})
}
},
}
</script>
<style>
.customerField .payTable {
width: 100%;
border-collapse: collapse;
}
.customerField .payTable tr th {
background: #fff;
height: 40px;
font-size: 12px;
font-weight: bold;
color: #2D2D2D;
background: #f5f5fa;
}
.customerField .payTable tr {
background: #fff;
text-align: center;
height: 40px;
}
.customerField .payTable tr td {
font-size: 13px;
text-align: center;
color: #2D2D2D;
padding: 10px 0;
font-weight: bold;
border-bottom: 1px solid #E5E5E5;
}
</style>
\ No newline at end of file
<template>
<div class="customerPhase page-body">
<div style="display: flex;align-items: center;justify-content: space-between;">
<div style="display: flex;align-items: center;">
<q-btn
v-if="state.data.length < 20"
color="accent"
style="margin-bottom: 10px;"
class="q-mr-md"
label="创建阶段流程"
@click="jumpPage('/editor/createStageRange')"
size="sm"
></q-btn>
<span
style="margin-left: 20px;color: rgb(133, 133, 152);"
>已添加:{{ state.data.length }}/20</span>
</div>
<q-btn
color="white"
text-color="black"
style="margin-bottom: 10px;"
class="q-mr-md"
label="阶段管理"
size="sm"
@click="jumpPage('/editor/manageStageRange')"
></q-btn>
</div>
<table class="payTable">
<thead>
<tr>
<th>名称</th>
<th>适用范围</th>
<th>启用</th>
<th>阶段</th>
<th width="13%">操作信息</th>
<th width="12%">操作</th>
</tr>
</thead>
<tr v-if="state.data.length == 0">
<td :colspan="6" align="center">暂无数据</td>
</tr>
<tr v-for="(item,index) in state.data" :key="index">
<td>
<span>{{ item.FlowName }}</span>
</td>
<td>
<div v-if="item.DeptEmpList && item.DeptEmpList.length > 0">
<span v-for="(x,y) in item.DeptEmpList" :key="y">
<q-chip
color="blue-1"
text-color="blue"
icon="folder"
size="sm"
v-if="x.Type == 1"
>{{ x.Name }}</q-chip>
<q-chip
color="green-1"
text-color="green"
icon="person"
size="sm"
v-if="x.Type == 2"
>{{ x.Name }}</q-chip>
</span>
</div>
<span v-else>未适配其他规则的阶段</span>
</td>
<td>
<q-toggle
v-if="item.IsDefault == 1"
v-model="item.Enable"
:true-value="1"
:false-value="2"
icon="lock"
disable
/>
<q-toggle
v-else
v-model="item.Enable"
:true-value="1"
:false-value="2"
@update:model-value="setFiledState(item.Id, 1)"
/>
</td>
<td>
<span
v-for="(x,y) in item.StageList"
:key="y"
>{{ x }}{{ item.StageList.length != y + 1 ? '->' : '' }}</span>
</td>
<td>
<div>{{ item.UpdateByName }}</div>
<div>{{ item.UpdateTime }}</div>
</td>
<td>
<q-btn
v-if="item.IsDefault != 1"
flat
size="xs"
icon="edit"
style="font-weight:400;color: #3FC4FF"
class="q-mr-xs"
label="编辑"
@click="goedit(item.Id)"
/>
<q-btn
v-if="item.IsDefault != 1"
flat
size="xs"
icon="delete"
color="negative"
class="q-mr-xs"
label="删除"
@click="godelete(item.Id)"
/>
</td>
</tr>
</table>
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, reactive } from 'vue'
import customerSetService from '@/api/customerSet'
import { useQuasar } from 'quasar'
import router from '@/router/index'
export default defineComponent({
setup() {
const $q = useQuasar()
const state = reactive({
data: [],
})
const getList = () => {
customerSetService.getCustomerStageFlowList({ Enable: 0 }).then((res) => {
if (res.data.Code == 1) {
state.data = res.data.Data
} else {//如果移动失败 重新获取列表
}
})
}
const godelete = (Id: number) => {
$q.dialog({
title: "提示信息",
message: '是否删除该阶段流程',
cancel: {
label: "取消",
flat: true
},
ok: {
label: "确认",
flat: true,
}
}).onOk(() => {
setFiledState(Id, 2)
});
}
const setFiledState = (Id: number, Type: number) => {
console.log(Id, Type)
let msg = {
Type: Type,
FlowId: Id,
}
customerSetService.setCustomerStageFlowState(msg).then((res) => {
if (res.data.Code == 1) {
$q.notify({
icon: 'iconfont icon-chenggong',
color: 'accent',
timeout: 2000,
message: res.data.Message,
position: 'top'
})
getList()
} else {//如果移动失败 重新获取列表
}
})
}
const jumpPage = (url: string) => {
router.push({
path: url,
query: {
}
})
}
const goedit = (Id: number) => {
router.push({
path: '/editor/createStageRange',
query: {
FlowId:Id
}
})
}
onMounted(() => {
getList()
})
return {
getList,
state,
setFiledState,
godelete,
jumpPage,
goedit
}
}
})
</script>
<style>
.customerPhase .payTable {
width: 100%;
border-collapse: collapse;
}
.customerPhase .payTable tr th {
background: #fff;
height: 40px;
font-size: 12px;
font-weight: bold;
color: #2d2d2d;
background: #f5f5fa;
}
.customerPhase .payTable tr {
background: #fff;
text-align: center;
height: 40px;
}
.customerPhase .payTable tr td {
font-size: 13px;
text-align: center;
color: #2d2d2d;
padding: 10px 0;
font-weight: bold;
border-bottom: 1px solid #e5e5e5;
}
</style>
\ No newline at end of file
<template>
<div class="customerlabel row">
<div class="col-5 customer-category-content">
<div class="row header-box">
<div style="font-weight: 700;">标签分组</div>
<el-button
color="accent"
size="sm"
class="q-mr-md"
icon="add"
label="新增分組"
@click="EditCategory()"
></el-button>
</div>
<div class="category-header">
<div style="width: 60px;">ID</div>
<div style="flex:1;text-align: center;">分組名称</div>
<div class="w90" style="width: 110px;">操作</div>
</div>
<div class="category-list">
<ul>
<span v-for="(x,y) in this.dataList" :key="y">
<li @click="getSelectlist(x,y)">
<div class="category-item" :class="{ 'Selectstyle': this.SelectID == x.Id }">
<div style="width: 60px;">{{ x.Id }}</div>
<div style="display: flex;">{{ x.Name }}</div>
<div class="w90" style="width: 110px;display: flex;">
<el-button flat icon="edit" class="icons" @click="getSelectlist(x,y), getParent(x, 1)" />
<el-button
flat
icon="delete"
class="icons"
@click="getSelectlist(x,y), goDetailed(x.Id)"
/>
</div>
</div>
</li>
</span>
<div
v-if="this.dataList && this.dataList.length == 0"
style="width: 100%;height: 50px;line-height: 50px;text-align: center;"
>暂无数据</div>
</ul>
</div>
</div>
<div class="col-7 customer-category-content" style="padding-left: 15px;">
<div class="row header-box">
<div style="font-weight: 700;">
标签分组:
<span style="color: #606266;">{{ this.groupName }}</span>
</div>
<el-button
color="accent"
size="sm"
class="q-mr-md"
icon="add"
label="新增分組"
@click="getParent({}, 3)"
></el-button>
</div>
<div class="category-header">
<div style="width: 60px;">ID</div>
<div style="flex:1;text-align: center;">标签名称</div>
<div class="w90">使用客户</div>
<div class="w90">排序</div>
<div class="w90" style="width: 110px;">操作</div>
</div>
<div class="category-list">
<ul>
<span v-for="(x,y) in this.ChildList" :key="y">
<li>
<div class="category-item" :class="{ 'Selectstyle': this.SelectID == x.Id }">
<div style="width: 60px;">{{ x.Id }}</div>
<div style="flex:1;text-align: center;">{{ x.Name }}</div>
<div class="w90">0</div>
<div class="w90">{{ x.Sort }}</div>
<div class="w90" style="width: 110px;display: flex;">
<el-button flat icon="edit" class="icons" @click="getParent(x, 2)" />
<el-button flat icon="delete" class="icons" @click="goDetailed(x.Id)" />
</div>
</div>
</li>
</span>
<div
v-if="this.ChildList && this.ChildList.length == 0"
style="width: 100%;height: 50px;line-height: 50px;text-align: center;"
>暂无数据</div>
</ul>
</div>
</div>
<q-dialog v-model="data.isShow" persistent>
<q-card style="width: 500px; max-width: 80vw;">
<q-card-section class="row items-center q-pb-none">
<div class="text-h6">新增标签组</div>
<q-space />
<el-button icon="close" flat round dense v-close-popup />
</q-card-section>
<q-separator />
<q-card-section class="q-pt-none" style="margin-top: 15px;padding: 20px;">
<q-input
outlined
counter
maxlength="8"
clearable
standout="bg-primary text-white"
v-model="data.addMsg.Name"
placeholder="标签组名称"
:rules="[val => !!val || '请输入分组名称']"
ref="Name"
/>
<q-input
outlined
clearable
type="number"
standout="bg-primary text-white"
v-model="data.addMsg.Sort"
placeholder="标签组排序"
:rules="[val => !!val || '请输入分组排序']"
ref="Sort"
/>
<div style="color: #787878;">*注:标签组/标签,排序值越大,排序越靠前!</div>
<div
style="width: 100%;"
v-if="data.addMsg.ChildList && data.addMsg.ChildList.length > 0"
>
<div
class="col row wrap q-mr-lg q-col-gutter-md"
v-for="(x,y) in data.addMsg.ChildList"
:key="y"
style="margin-top: 0px;"
>
<div class="col-7">
<q-input
outlined
counter
maxlength="15"
clearable
standout="bg-primary text-white"
v-model="x.Name"
placeholder="输入标签名称"
/>
</div>
<div class="col-3">
<q-input
filled
standout="bg-primary text-white"
v-model="x.Sort"
placeholder="输入排序"
maxlength="20"
type="number"
/>
</div>
<div class="col-2">
<el-button
flat
icon="delete"
color="negative"
class="q-mr-xs"
label
@click="data.addMsg.ChildList.splice(y, 1)"
/>
</div>
</div>
</div>
<el-button
flat
size="sm"
icon="add"
style="font-weight:400;color: #3FC4FF;margin-top: 10px;"
label="添加标签"
@click="addbiaoqian()"
/>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white">
<el-button class="q-mr-md" label="取消" @click="data.isShow = false" />
<el-button
color="accent"
class="q-mr-md"
label="确定"
@click="submit()"
:loading="data.btnloading"
/>
</q-card-actions>
</q-card>
</q-dialog>
<!-- 修改标签组名称 -->
<q-dialog v-model="data.isParent" persistent>
<q-card style="width: 500px; max-width: 80vw;">
<q-card-section class="row items-center q-pb-none">
<div class="text-h6">修改标签信息</div>
<q-space />
<el-button icon="close" flat round dense v-close-popup />
</q-card-section>
<q-separator />
<q-card-section class="q-pt-none" style="margin-top: 15px;padding: 20px;">
<q-input
v-if="data.edittype == 1"
outlined
counter
maxlength="8"
clearable
standout="bg-primary text-white"
v-model="data.addMsg.Name"
placeholder="标签组名称"
:rules="[val => !!val || '请输入分组名称']"
ref="eName"
/>
<q-input
v-if="data.edittype == 2 || data.edittype == 3"
outlined
counter
maxlength="15"
clearable
standout="bg-primary text-white"
v-model="data.addMsg.Name"
placeholder="标签名称"
:rules="[val => !!val || '请输入标签名称']"
ref="eName"
/>
<q-input
outlined
clearable
standout="bg-primary text-white"
v-model="data.addMsg.Sort"
placeholder="标签组排序"
:rules="[val => !!val || '请输入分组排序']"
ref="eSort"
/>
<div style="color: #787878;">*注:标签组/标签,排序值越大,排序越靠前!</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white">
<el-button class="q-mr-md" label="取消" @click="data.isParent = false" />
<el-button
color="accent"
class="q-mr-md"
label="确定"
@click="submitParent()"
:loading="data.btnloading"
/>
</q-card-actions>
</q-card>
</q-dialog>
</div>
</template>
<script>
export default {
data() {
return {
dataList: [],
SelectID: 0,
ChildList: [],
groupName: '',
isShow: false,
addMsg: {},
btnloading: false,
isName: false,
issort: false,
isalladopt: false,
isParent: false,
edittype: 1,
selectindex:0,
}
},
mounted() {
this.getList()
},
methods: {
getList() {
this.apipost('/QYWeChat/GetWeChatLableList',{},(res) => {
if (res.data.Code == 1) {
this.dataList = res.data.Data;
this.SelectID = this.dataList[this.selectindex].Id
this.ChildList = this.dataList[this.selectindex].ChildList
this.groupName = this.dataList[this.selectindex].Name
}
})
},
getSelectlist(x,y) {
this.SelectID = x.Id;
this.ChildList = x.ChildList;
this.groupName = x.Name
this.selectindex = y
},
EditCategory() {
this.addMsg = {
Id: 0,
ParentId: 0,
Name: '',
Sort: 1,
ChildList: [{ Name: '', Sort: 1, }],
}
this.isShow = true
},
addbiaoqian() {
let obj = {
Name: '',
Sort: 1,
}
this.addMsg.ChildList.push(obj)
},
isChildList() {
if (this.addMsg.Name == '' || this.addMsg.Name == null) {
this.$message.success({
type: 'negative',
message: `请填写标签分组名称`,
position: 'top'
})
this.isalladopt = false
return
}
if (this.addMsg.Sort == '' || this.addMsg.Sort == null) {
this.$message.success({
type: 'negative',
message: `请填写标签分组序号`,
position: 'top'
})
this.isalladopt = false
return
}
this.isName = false;
this.issort = false;
if (this.addMsg.ChildList.length > 0) {
this.addMsg.ChildList.map(x => {
if (x.Name == '') {
this.isName = true;
return
}
if (x.Sort == '') {
this.issort = true;
return
}
})
}
if (this.isName == true) {
this.$message.success({
type: 'negative',
message: `请填写标签名称`,
position: 'top'
})
this.isalladopt = false
return
}
if (this.issort == true) {
this.$message.success({
type: 'negative',
message: `请填写标签排序`,
position: 'top'
})
this.isalladopt = false
return
}
},
setlable(msg) {
this.apipost('/QYWeChat/SetWeChatLable',msg,(res) => {
if (res.data.Code == 1) {
this.$message.success({
icon: 'iconfont icon-chenggong',
color: 'accent',
timeout: 2000,
message: '数据保存成功!',
position: 'top'
})
this.getList()
this.isShow = false;
this.isParent = false;
}
})
},
submit() {
this.isalladopt = true
this.isChildList()//表单验证
if (this.isalladopt == true) {
this.setlable(this.addMsg)
}
},
goDetailed(Id) {
this.$confirm({
title: "提示信息",
message: '确定删除?',
cancel: {
label: "取消",
flat: true
},
ok: {
label: "确认",
flat: true,
}
}).onOk(() => {
this.apipost('/QYWeChat/DelWeChatLable',{ LableId: Id },(res) => {
if (res.data.Code == 1) {
this.$message.success({
icon: 'iconfont icon-chenggong',
color: 'accent',
timeout: 2000,
message: '数据保存成功!',
position: 'top'
})
if(this.selectindex+1 == this.dataList.length){
this.selectindex--
}
this.getList()
}
})
});
},
getParent(row, type) {
if (type == 1) {
this.addMsg = JSON.parse(JSON.stringify(row))
} else if (type == 2) {
this.addMsg = {
ParentId: row.ParentId,
Name: row.Name,
Id: row.Id,
Sort: row.Sort,
}
} else {
this.addMsg = {
ParentId: this.SelectID,
Name: '',
Id: 0,
Sort: '1',
}
}
this.isParent = true;
this.edittype = type
},
submitParent() {
if (this.addMsg.Name == '' || this.addMsg.Name == null) {
this.$message.success({
type: 'negative',
message: `请填写标签分组名称`,
position: 'top'
})
this.isalladopt = false
return
}
if (this.addMsg.Sort == '' || this.addMsg.Sort == null) {
this.$message.success({
type: 'negative',
message: `请填写标签分组序号`,
position: 'top'
})
this.isalladopt = false
return
}
let msg
if (this.edittype == 1) {
msg = {
Id: this.addMsg.Id,
Name: this.addMsg.Name,
Sort: this.addMsg.Sort,
}
} else {
msg = {
Name: this.addMsg.Name,
Sort: this.addMsg.Sort,
Id: this.addMsg.Id,
ParentId: this.addMsg.ParentId,
}
}
this.setlable(msg)
}}
}
</script>
<style lang="scss">
.customerlabel {
width: 100%;
height: calc(100vh - 80px);
.customer-category-content {
min-height: 100%;
max-height: 100%;
border-right: 1px solid #d7d7d7;
display: flex;
flex-direction: column;
overflow: hidden;
padding-right: 15px;
}
.header-box {
padding: 21px 0 21px 25px;
display: flex;
justify-content: space-between;
align-items: center;
}
.category-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 60px;
padding: 0 26px;
border-radius: 5px;
background-color: #f5f5fa;
}
.category-list {
flex: 1;
overflow: auto;
ul {
margin: 0;
padding: 0;
list-style: none outside none;
.category-item {
display: flex;
justify-content: space-between;
align-items: center;
height: 60px;
border-radius: 5px;
padding: 0 26px;
position: relative;
}
}
}
.w90 {
width: 90px;
text-align: center;
}
.category-list::-webkit-scrollbar {
display: none;
}
.icons {
font-size: 12px;
margin-right: 5px;
}
.Selectstyle {
background: #edf2fd;
color: #3470ff;
}
}
</style>
\ No newline at end of file
<template>
<div class="customerPhase page-body">
<div style="display: flex;align-items: center;justify-content: space-between;">
<div style="display: flex;align-items: center;">
<q-btn
color="accent"
style="margin-bottom: 10px;"
class="q-mr-md"
label="新增分配规则 "
@click="Addallocation('/editor/editlaqun')"
size="sm"
></q-btn>
<span style="margin-left: 10px;color: #3470ff;cursor: pointer;" @click="icon = true">查看拉群分配流程</span>
</div>
<div style="display: flex;align-items: center;">
<span
style="font-size: 14px; color: #858598;margin-right: 10px;"
>若客户满足多条规则,会优先按照编号顺序执行规则</span>
<q-btn
color="accent"
style="margin-bottom: 10px;"
class="q-mr-md"
label="新增分配规则 "
@click="Addallocation('/editor/editlaqun')"
size="sm"
></q-btn>
</div>
</div>
<q-dialog v-model="icon">
<q-card style="max-width: 1300px;">
<q-card-section class="row items-center q-pb-none">
<div class="text-h6">拉群分配客户流程 <span style="color: #858598;font-size: 14px;">(销售分配更公平,客服拉群更便捷)</span></div>
<q-space />
<q-btn icon="close" flat round dense v-close-popup />
</q-card-section>
<div style="display: flex;align-items: flex-start;justify-content: space-around;padding: 0 40px">
<div class="text-wrapper" v-for="(x,y) in textList " :key='y'>{{x.Name}}</div>
</div>
<div class="flow-line">
<div class="flow-line-slide1" style="width: 110px;"></div>
<div class="flow-line-center" style="width: 220px;"></div>
<div class="flow-line-center" style="width: 220px;"></div>
<div class="flow-line-center" style="width: 220px;"></div>
<div class="flow-line-center" style="width: 220px;"></div>
<div class="flow-line-slide2" style="width: 110px;">
<div class="right-arrow"></div>
</div>
</div>
<div class="flow-content-wrapper" style="padding: 0 40px">
<div class="flow-content">
<div class="arrow"></div>
<div>
<li class='setting-text'> 选择可拉群的SDR </li>
<li class='setting-text'> 选择适用的客户 </li>
<li class='setting-text'> 选择可分配的销售 </li>
<li class='setting-text'> 顺序分配或随机分配 </li>
</div>
</div>
<div class="flow-content">
<div class="arrow"></div>
<img class="flow-image" src="../../../../assets/images/customer/flow1.a3a1048d.png" alt="">
</div>
<div class="flow-content">
<div class="arrow"></div>
<img class="flow-image" src="../../../../assets/images/customer/flow2.61729a6c.png" alt="">
</div>
<div class="flow-content">
<div class="arrow"></div>
<img class="flow-image" src="../../../../assets/images/customer/flow3.85769e1f.png" alt="">
</div>
<div class="flow-content">
<div class="arrow"></div>
<div class="setting-text">自定义销售身份</div>
<div class="setting-text">负责人 or 协作人</div>
</div>
</div>
</q-card>
</q-dialog>
</div>
</template>
<script lang="ts">
import {
defineComponent,
ref,
// onMounted
} from 'vue'
// import { useQuasar } from 'quasar'
import router from '@/router/index'
export default defineComponent({
setup() {
const textList = ref < Array < {Name:string} >> ([
{Name:' 设置分配规则 '},
{Name:' SDR(售前客服) 在客户详情点击“更多” '},
{Name:' 点击“拉群分配客户” '},
{Name:' 建群成功,系统自动分配销售 '},
{Name:' 根据设置自动指定负责人 '},
])
const Addallocation = (url: string) => {//新增分配规则
console.log('新增匹配规则')
router.push({
path: url,
query: {
}
})
}
// onMounted(() => {
// })
return {
Addallocation,
icon:ref(false),
textList
}
}
})
</script>
<style lang="scss">
.text-wrapper{
width: 200px;
font-size: 14px;
color: #333;
font-weight: 500;
line-height: 20px;
text-align: center;
margin: 20px 6px 0;
}
.flow-line{
margin-top: 20px;
display: flex;
justify-content: center;
.flow-line-slide1{
height: 30px;
border-top: 1px dashed #3470ff;
border-right: 1px dashed #3470ff;
}
.flow-line-center{
border-top: 1px dashed #3470ff;
border-right: 1px dashed #3470ff;
height: 30px;
}
.flow-line-slide2{
position: relative;
height: 30px;
border-top: 1px dashed #3470ff;
.right-arrow{
position: absolute;
top: -7px;
right: -9px;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
border-left: 9px solid #3470ff;
}
}
}
.flow-content-wrapper{
height: 430px;
display: flex;
justify-content: space-around;
.flow-content{
position: relative;
width: 220px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.arrow{
position: absolute;
top: 0;
margin: 0 auto;
height: 0;
width: 0;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 9px solid #3470ff;
}
.setting-text{
color: #333;
border-radius: 20px;
height: 40px;
line-height: 40px;
background: #f9f9f9;
padding: 0 10px;
margin: 10px 0;
text-align: center;
list-style: none outside none;
}
.flow-image{
width: 190px;
height: 400px;
}
}
}
</style>
\ No newline at end of file
<template>
<div class="q-pa-md">
<q-page padding class="page" :style-fn="myTweak(640)">
<q-tabs
v-model="tab"
dense
class="text-grey"
align="left"
active-color="primary"
indicator-color="primary"
narrow-indicator
>
<q-tab name="model" label="业务模式" />
<q-tab name="field" label="客户字段" />
<q-tab name="label" label="标签" />
<q-tab name="clue" label="线索分配规则" />
<q-tab name="laqun" label="拉群分配客人" />
<q-tab name="phase" label="客户阶段" />
<q-tab name="operation" label="客户操作" />
<q-tab name="lib" label="客户库-公海" />
</q-tabs>
<div class="container fit">
<businessModel v-if="tab == 'model'"></businessModel>
<customer-Field v-if="tab == 'field'"></customer-Field>
<clue-rule v-if="tab == 'clue'"></clue-rule>
<laqundistribution v-if="tab == 'laqun'"></laqundistribution>
<customer-phase v-if="tab == 'phase'"></customer-phase>
<customerlabel v-if="tab == 'label'"></customerlabel>
<CustomerOperation v-if="tab == 'operation'"></CustomerOperation>
<CustomerLibrary v-if="tab == 'lib'"></CustomerLibrary>
</div>
</q-page>
</div>
</template>
<script lang="ts">
import businessModel from './components/businessModel.vue'
import customerField from './components/customerField.vue'
import clueRule from './components/clueRule.vue'
import laqundistribution from './components/laqundistribution.vue'//拉群分配客人
import customerPhase from './components/customerPhase.vue'
import customerlabel from './components/customerlabel.vue'
import CustomerOperation from "./components/CustomerOperation.vue"
import CustomerLibrary from "./components/CustomerLibrary.vue"
import router from '@/router/index'
import { useMeta } from 'quasar'
import {
defineComponent,
ref
} from 'vue'
export default defineComponent({
components: {
businessModel,
customerField,
clueRule,
customerPhase,
customerlabel,
CustomerOperation,
CustomerLibrary,
laqundistribution
},
setup() {
useMeta({title: '客户设置'})
let tab = ref<string>('field')
if (router.currentRoute.value.query && router.currentRoute.value.query.type) {
tab.value = router.currentRoute.value.query.type as string
}
let myTweak= (offset)=> {
return { minHeight: offset ? `calc(100vh - ${offset}px)` : '100vh' }
}
return {
tab,
myTweak
}
}
})
</script>
<style lang="scss" scoped>
.page{
background: #FFF;
box-sizing: border-box;
border-radius: 10px;
}
.container{
box-sizing: border-box;
overflow: scroll;
}
</style>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment