|
@@ -1,105 +1,164 @@
|
|
|
<template>
|
|
|
<div class="tenant">
|
|
|
- <div class="content">
|
|
|
- <byTable
|
|
|
- :source="sourceList.data"
|
|
|
- :pagination="sourceList.pagination"
|
|
|
- :config="config"
|
|
|
- :loading="loading"
|
|
|
- :selectConfig="selectConfig"
|
|
|
- highlight-current-row
|
|
|
- :action-list="[
|
|
|
- {
|
|
|
- text: '添加客户',
|
|
|
- action: () => openModal(),
|
|
|
- },
|
|
|
- ]"
|
|
|
- @get-list="getList">
|
|
|
- <template #isTop="{ item }">
|
|
|
- <div>
|
|
|
- <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/isTop.png'" @click="deleteTop(item)" v-if="item.isTop === 1" />
|
|
|
- <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/noTop.png'" @click="addTop(item)" v-else />
|
|
|
+ <div style="padding: 20px; background: #fff; margin-bottom: 20px">
|
|
|
+ <el-button type="primary" style="margin-left: 10px" @click="openModal()">添加客户</el-button>
|
|
|
+ </div>
|
|
|
+ <div style="padding: 20px 20px 0 20px; background: #fff; margin-bottom: 20px">
|
|
|
+ <div style="display: flex">
|
|
|
+ <div style="font-size: 14px; cursor: pointer" class="by-dropdown">
|
|
|
+ <div class="by-dropdown-title">
|
|
|
+ <span>{{ dictValueLabel(sourceList.paginationTwo.statisticsType, statisticsType) }}</span>
|
|
|
+ <el-icon style="margin-left: 5px; font-size: 16px"><CaretBottom /></el-icon>
|
|
|
+ </div>
|
|
|
+ <ul class="by-dropdown-lists">
|
|
|
+ <li
|
|
|
+ v-for="item in statisticsType"
|
|
|
+ :key="item.value"
|
|
|
+ @click="searchItemSelect(item.value)"
|
|
|
+ style="display: flex; align-items: center; justify-content: center">
|
|
|
+ {{ item.label }}
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div style="display: flex; width: 100%; margin: 10px 0 0 10px; flex-wrap: wrap">
|
|
|
+ <div style="padding: 20px; border-radius: 10px; width: 200px; background-color: #d1caff59; margin: 0 20px 20px 0">
|
|
|
+ <div style="margin-bottom: 10px; display: flex">
|
|
|
+ <div style="width: 8px; height: 8px; background-color: #5bacff; border-radius: 50px; margin-top: 6px"></div>
|
|
|
+ <span style="padding-left: 8px">合计</span>
|
|
|
+ </div>
|
|
|
+ <div style="color: black; font-size: 20px; font-weight: 700">{{ statisticalData.countAmount }}</div>
|
|
|
+ </div>
|
|
|
+ <template v-if="sourceList.paginationTwo.statisticsType === 1">
|
|
|
+ <div
|
|
|
+ style="padding: 20px; border-radius: 10px; width: 200px; background-color: #a2d8ff70; margin: 0 20px 20px 0"
|
|
|
+ v-for="(item, index) in customerSource"
|
|
|
+ :key="index">
|
|
|
+ <div style="margin-bottom: 10px; display: flex">
|
|
|
+ <div style="width: 8px; height: 8px; background-color: #5bacff; border-radius: 50px; margin-top: 6px"></div>
|
|
|
+ <span style="padding-left: 8px">{{ item.label }}</span>
|
|
|
+ </div>
|
|
|
+ <div style="color: black; font-size: 20px; font-weight: 700">{{ getNum(item.value) }}</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
- <template #address="{ item }">
|
|
|
- <span>{{ item.countryName }}</span>
|
|
|
- <span v-if="item.provinceName"> ,{{ item.provinceName }}</span>
|
|
|
- <span v-if="item.cityName"> ,{{ item.cityName }}</span>
|
|
|
- </template>
|
|
|
- <template #name="{ item }">
|
|
|
- <div style="cursor: pointer; color: #409eff" @click="handleClickName(item)">
|
|
|
- {{ item.name }}
|
|
|
+ <template v-else-if="sourceList.paginationTwo.statisticsType === 2">
|
|
|
+ <div
|
|
|
+ style="padding: 20px; border-radius: 10px; width: 200px; background-color: #a2d8ff70; margin: 0 20px 20px 0"
|
|
|
+ v-for="(item, index) in customerStatus"
|
|
|
+ :key="index">
|
|
|
+ <div style="margin-bottom: 10px; display: flex">
|
|
|
+ <div style="width: 8px; height: 8px; background-color: #5bacff; border-radius: 50px; margin-top: 6px"></div>
|
|
|
+ <span style="padding-left: 8px">{{ item.label }}</span>
|
|
|
+ </div>
|
|
|
+ <div style="color: black; font-size: 20px; font-weight: 700">{{ getNum(item.value) }}</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
- <template #tags="{ item }">
|
|
|
- <div style="width: 100%">
|
|
|
- <el-tag style="margin-right: 8px" type="success" v-for="(tag, index) in item.tag" closable :key="index" @close="tagClose(tag, item)">
|
|
|
- {{ dictValueLabel(tag, customerTag) }}
|
|
|
- </el-tag>
|
|
|
- <template v-if="item.tag.length !== customerTag.length">
|
|
|
- <el-select
|
|
|
- v-if="item.addTagShow"
|
|
|
- v-model="addTag"
|
|
|
- style="width: 100%"
|
|
|
- @change="
|
|
|
- (val) => {
|
|
|
- return changeTag(val, item);
|
|
|
- }
|
|
|
- ">
|
|
|
- <el-option v-for="tag in customerTag" :key="tag.value" :label="tag.label" :value="tag.value" :disabled="judgeTagSelect(item.tag, tag.value)" />
|
|
|
- </el-select>
|
|
|
- <el-tag style="cursor: pointer" type="success" @click="showSelect(item)" v-else> + </el-tag>
|
|
|
- </template>
|
|
|
+ <template v-else-if="sourceList.paginationTwo.statisticsType === 3">
|
|
|
+ <div
|
|
|
+ style="padding: 20px; border-radius: 10px; width: 200px; background-color: #a2d8ff70; margin: 0 20px 20px 0"
|
|
|
+ v-for="(item, index) in userList"
|
|
|
+ :key="index">
|
|
|
+ <div style="margin-bottom: 10px; display: flex">
|
|
|
+ <div style="width: 8px; height: 8px; background-color: #5bacff; border-radius: 50px; margin-top: 6px"></div>
|
|
|
+ <span style="padding-left: 8px">{{ item.label }}</span>
|
|
|
+ </div>
|
|
|
+ <div style="color: black; font-size: 20px; font-weight: 700">{{ getNum(item.value) }}</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
- <template #follow="{ item }">
|
|
|
- <div :class="'getWidth' + item.id" style="width: 100%">
|
|
|
- <div style="width: 100%; display: flex">
|
|
|
- <template v-if="item.customerFollowRecordsList && item.customerFollowRecordsList.length > 0">
|
|
|
- <div
|
|
|
- :style="
|
|
|
- index > 2
|
|
|
- ? 'line-height: 32px; margin-right: 8px; padding: 0 8px; background-color: #eeeeee; border-radius: 4px; cursor: pointer; display: none'
|
|
|
- : 'line-height: 32px; margin-right: 8px; padding: 0 8px; background-color: #eeeeee; border-radius: 4px; cursor: pointer'
|
|
|
- "
|
|
|
- v-for="(record, index) in item.customerFollowRecordsList"
|
|
|
- :key="record.id">
|
|
|
- <el-popover placement="bottom" :width="300" trigger="hover" @show="recordShow(record)">
|
|
|
- <template #reference>
|
|
|
- <div>
|
|
|
- <span v-if="record.date">{{ record.date.substr(0, 10) }}</span>
|
|
|
- <el-icon style="margin-left: 8px; transform: translateY(2px)" @click="deleteFollow(record)"><DeleteFilled /></el-icon>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template #default>
|
|
|
- <div style="width: 100%">
|
|
|
- <div style="color: #909399; margin: 8px 0">跟进时间: {{ record.date }}</div>
|
|
|
- <div style="word-wrap: break-word; margin: 8px 0" v-html="getStyle(record.content)" v-if="record.content"></div>
|
|
|
- <div v-else>跟进记录:</div>
|
|
|
- <div style="margin: 8px 0; display: flex" v-if="record.fileList && record.fileList.length > 0">
|
|
|
- <div style="width: 36px">附件:</div>
|
|
|
- <div style="width: calc(100% - 36px)">
|
|
|
- <div v-for="(file, index) in record.fileList" :key="index">
|
|
|
- <a style="color: #409eff; cursor: pointer" @click="openFile(file.fileUrl)">{{ file.fileName }}</a>
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <byTable
|
|
|
+ :source="sourceList.data"
|
|
|
+ :pagination="sourceList.pagination"
|
|
|
+ :config="config"
|
|
|
+ :loading="loading"
|
|
|
+ :selectConfig="selectConfig"
|
|
|
+ highlight-current-row
|
|
|
+ @get-list="getList">
|
|
|
+ <template #isTop="{ item }">
|
|
|
+ <div>
|
|
|
+ <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/isTop.png'" @click="deleteTop(item)" v-if="item.isTop === 1" />
|
|
|
+ <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/noTop.png'" @click="addTop(item)" v-else />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template #address="{ item }">
|
|
|
+ <span>{{ item.countryName }}</span>
|
|
|
+ <span v-if="item.provinceName"> ,{{ item.provinceName }}</span>
|
|
|
+ <span v-if="item.cityName"> ,{{ item.cityName }}</span>
|
|
|
+ </template>
|
|
|
+ <template #name="{ item }">
|
|
|
+ <div style="cursor: pointer; color: #409eff" @click="handleClickName(item)">
|
|
|
+ {{ item.name }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template #tags="{ item }">
|
|
|
+ <div style="width: 100%">
|
|
|
+ <el-tag style="margin-right: 8px" type="success" v-for="(tag, index) in item.tag" closable :key="index" @close="tagClose(tag, item)">
|
|
|
+ {{ dictValueLabel(tag, customerTag) }}
|
|
|
+ </el-tag>
|
|
|
+ <template v-if="item.tag.length !== customerTag.length">
|
|
|
+ <el-select
|
|
|
+ v-if="item.addTagShow"
|
|
|
+ v-model="addTag"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="
|
|
|
+ (val) => {
|
|
|
+ return changeTag(val, item);
|
|
|
+ }
|
|
|
+ ">
|
|
|
+ <el-option v-for="tag in customerTag" :key="tag.value" :label="tag.label" :value="tag.value" :disabled="judgeTagSelect(item.tag, tag.value)" />
|
|
|
+ </el-select>
|
|
|
+ <el-tag style="cursor: pointer" type="success" @click="showSelect(item)" v-else> + </el-tag>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template #follow="{ item }">
|
|
|
+ <div :class="'getWidth' + item.id" style="width: 100%">
|
|
|
+ <div style="width: 100%; display: flex">
|
|
|
+ <template v-if="item.customerFollowRecordsList && item.customerFollowRecordsList.length > 0">
|
|
|
+ <div
|
|
|
+ :style="
|
|
|
+ index > 2
|
|
|
+ ? 'line-height: 32px; margin-right: 8px; padding: 0 8px; background-color: #eeeeee; border-radius: 4px; cursor: pointer; display: none'
|
|
|
+ : 'line-height: 32px; margin-right: 8px; padding: 0 8px; background-color: #eeeeee; border-radius: 4px; cursor: pointer'
|
|
|
+ "
|
|
|
+ v-for="(record, index) in item.customerFollowRecordsList"
|
|
|
+ :key="record.id">
|
|
|
+ <el-popover placement="bottom" :width="300" trigger="hover" @show="recordShow(record)">
|
|
|
+ <template #reference>
|
|
|
+ <div>
|
|
|
+ <span v-if="record.date">{{ record.date.substr(0, 10) }}</span>
|
|
|
+ <el-icon style="margin-left: 8px; transform: translateY(2px)" @click="deleteFollow(record)"><DeleteFilled /></el-icon>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template #default>
|
|
|
+ <div style="width: 100%">
|
|
|
+ <div style="color: #909399; margin: 8px 0">跟进时间: {{ record.date }}</div>
|
|
|
+ <div style="word-wrap: break-word; margin: 8px 0" v-html="getStyle(record.content)" v-if="record.content"></div>
|
|
|
+ <div v-else>跟进记录:</div>
|
|
|
+ <div style="margin: 8px 0; display: flex" v-if="record.fileList && record.fileList.length > 0">
|
|
|
+ <div style="width: 36px">附件:</div>
|
|
|
+ <div style="width: calc(100% - 36px)">
|
|
|
+ <div v-for="(file, index) in record.fileList" :key="index">
|
|
|
+ <a style="color: #409eff; cursor: pointer" @click="openFile(file.fileUrl)">{{ file.fileName }}</a>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </template>
|
|
|
- </el-popover>
|
|
|
- </div>
|
|
|
- <div
|
|
|
- style="line-height: 32px; margin-right: 8px; padding: 0 8px; background-color: #eeeeee; border-radius: 4px; cursor: pointer"
|
|
|
- @click="clickMore(item)"
|
|
|
- v-if="item.customerFollowRecordsList.length >= 3">
|
|
|
- 更多
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-popover>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ style="line-height: 32px; margin-right: 8px; padding: 0 8px; background-color: #eeeeee; border-radius: 4px; cursor: pointer"
|
|
|
+ @click="clickMore(item)"
|
|
|
+ v-if="item.customerFollowRecordsList.length >= 3">
|
|
|
+ 更多
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
</div>
|
|
|
- </template>
|
|
|
- </byTable>
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </byTable>
|
|
|
|
|
|
<el-dialog :title="modalType == 'add' ? '新增' : '编辑'" v-if="dialogVisible" v-model="dialogVisible" width="800" v-loading="loadingOperation">
|
|
|
<byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
|
|
@@ -307,6 +366,20 @@ const contactType = ref([]);
|
|
|
const userList = ref([]);
|
|
|
const fileList = ref([]);
|
|
|
const uploadData = ref({});
|
|
|
+const statisticsType = ref([
|
|
|
+ {
|
|
|
+ label: "客户来源统计",
|
|
|
+ value: 1,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: "客户类型统计",
|
|
|
+ value: 2,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: "业务员统计",
|
|
|
+ value: 3,
|
|
|
+ },
|
|
|
+]);
|
|
|
const sourceList = ref({
|
|
|
data: [],
|
|
|
pagination: {
|
|
@@ -317,6 +390,10 @@ const sourceList = ref({
|
|
|
source: "",
|
|
|
type: "",
|
|
|
},
|
|
|
+ paginationTwo: {
|
|
|
+ statisticsType: 1,
|
|
|
+ type: null,
|
|
|
+ },
|
|
|
});
|
|
|
const selectConfig = computed(() => {
|
|
|
return [
|
|
@@ -498,6 +575,7 @@ const config = computed(() => {
|
|
|
type: "success",
|
|
|
});
|
|
|
getList();
|
|
|
+ obtainStatisticalData();
|
|
|
});
|
|
|
});
|
|
|
},
|
|
@@ -574,6 +652,7 @@ const formConfig = computed(() => {
|
|
|
prop: "userId",
|
|
|
itemWidth: 100,
|
|
|
data: userList.value,
|
|
|
+ clearable: true,
|
|
|
},
|
|
|
{
|
|
|
type: "select",
|
|
@@ -611,6 +690,7 @@ const formConfigAllocation = computed(() => {
|
|
|
prop: "userId",
|
|
|
itemWidth: 100,
|
|
|
data: userList.value,
|
|
|
+ clearable: true,
|
|
|
},
|
|
|
];
|
|
|
});
|
|
@@ -743,6 +823,7 @@ const submitAllocation = () => {
|
|
|
});
|
|
|
openAllocation.value = false;
|
|
|
getList();
|
|
|
+ obtainStatisticalData();
|
|
|
});
|
|
|
});
|
|
|
};
|
|
@@ -788,6 +869,7 @@ const submitForm = () => {
|
|
|
dialogVisible.value = false;
|
|
|
submitLoading.value = false;
|
|
|
getList();
|
|
|
+ obtainStatisticalData();
|
|
|
},
|
|
|
(err) => {
|
|
|
console.log(err);
|
|
@@ -1059,6 +1141,41 @@ const addTop = (item) => {
|
|
|
item.isTop = 1;
|
|
|
});
|
|
|
};
|
|
|
+const searchItemSelect = (val) => {
|
|
|
+ sourceList.value.paginationTwo.statisticsType = val;
|
|
|
+ obtainStatisticalData();
|
|
|
+};
|
|
|
+const statisticalData = ref({
|
|
|
+ countAmount: 0,
|
|
|
+ customerList: [],
|
|
|
+});
|
|
|
+const obtainStatisticalData = () => {
|
|
|
+ proxy.post("/customer/sourceStatistics", sourceList.value.paginationTwo).then((res) => {
|
|
|
+ statisticalData.value = res;
|
|
|
+ });
|
|
|
+};
|
|
|
+obtainStatisticalData();
|
|
|
+const getNum = (val) => {
|
|
|
+ let num = 0;
|
|
|
+ if (statisticalData.value.customerList && statisticalData.value.customerList.length > 0) {
|
|
|
+ statisticalData.value.customerList.map((item) => {
|
|
|
+ if (sourceList.value.paginationTwo.statisticsType === 1) {
|
|
|
+ if (item.source === val) {
|
|
|
+ num = item.count;
|
|
|
+ }
|
|
|
+ } else if (sourceList.value.paginationTwo.statisticsType === 2) {
|
|
|
+ if (item.status === val) {
|
|
|
+ num = item.count;
|
|
|
+ }
|
|
|
+ } else if (sourceList.value.paginationTwo.statisticsType === 3) {
|
|
|
+ if (item.userId === val) {
|
|
|
+ num = item.count;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return num;
|
|
|
+};
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
@@ -1072,4 +1189,69 @@ const addTop = (item) => {
|
|
|
width: 0px;
|
|
|
}
|
|
|
}
|
|
|
+.by-dropdown {
|
|
|
+ position: relative;
|
|
|
+ text-align: left;
|
|
|
+ height: 32px;
|
|
|
+ z-index: 1010;
|
|
|
+ padding: 0 10px;
|
|
|
+ transition: all 0.5s ease;
|
|
|
+ cursor: pointer;
|
|
|
+ line-height: 32px;
|
|
|
+ .by-dropdown-title {
|
|
|
+ font-size: 14px;
|
|
|
+ background-color: #fff;
|
|
|
+ }
|
|
|
+ ul {
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ top: 32px;
|
|
|
+ padding: 0;
|
|
|
+ margin: 0;
|
|
|
+ z-index: 1200;
|
|
|
+ display: none;
|
|
|
+ white-space: nowrap;
|
|
|
+ background-color: #fff;
|
|
|
+ li {
|
|
|
+ list-style: none;
|
|
|
+ z-index: 1200;
|
|
|
+ font-size: 12px;
|
|
|
+ height: 30px;
|
|
|
+ padding: 0 10px;
|
|
|
+ }
|
|
|
+ li:hover {
|
|
|
+ background-color: #eff6ff;
|
|
|
+ color: #0084ff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+.by-dropdown::before {
|
|
|
+ display: block;
|
|
|
+ width: 1px;
|
|
|
+ content: " ";
|
|
|
+ position: absolute;
|
|
|
+ height: 14px;
|
|
|
+ top: 8px;
|
|
|
+ background-color: #ddd;
|
|
|
+ right: 0;
|
|
|
+ z-index: 1011;
|
|
|
+}
|
|
|
+.by-dropdown:hover {
|
|
|
+ background: #ffffff;
|
|
|
+ border-radius: 2px 2px 2px 2px;
|
|
|
+ opacity: 1;
|
|
|
+ ul {
|
|
|
+ background: #ffffff;
|
|
|
+ box-shadow: 0px 2px 16px 1px rgba(0, 0, 0, 0.06);
|
|
|
+ border-radius: 2px 2px 2px 2px;
|
|
|
+ opacity: 1;
|
|
|
+ display: block;
|
|
|
+ text-align: left;
|
|
|
+ }
|
|
|
+}
|
|
|
+.by-dropdown-lists {
|
|
|
+ max-height: 50vh;
|
|
|
+ overflow-y: auto;
|
|
|
+ line-height: 1;
|
|
|
+}
|
|
|
</style>
|