Explorar el Código

CRM: 新增客户行业、来源、级别统计、区域数据统计

puhui999 hace 1 año
padre
commit
cce1fadc33

+ 58 - 0
src/api/crm/statistics/customer.ts

@@ -49,6 +49,36 @@ export interface CrmStatisticsCustomerDealCycleByDateRespVO {
   customerDealCycle: number
 }
 
+export interface CrmStatisticCustomerBaseRespVO {
+  customerCount: number
+  dealCount: number
+  dealPortion: number
+}
+
+export interface CrmStatisticCustomerIndustryRespVO extends CrmStatisticCustomerBaseRespVO {
+  industryId: number
+  industryName: string
+  industryPortion: number
+}
+
+export interface CrmStatisticCustomerSourceRespVO extends CrmStatisticCustomerBaseRespVO {
+  source: number
+  sourceName: string
+  sourcePortion: number
+}
+
+export interface CrmStatisticCustomerLevelRespVO extends CrmStatisticCustomerBaseRespVO {
+  level: number
+  levelName: string
+  levelPortion: number
+}
+
+export interface CrmStatisticCustomerAreaRespVO extends CrmStatisticCustomerBaseRespVO {
+  areaId: number
+  areaName: string
+  areaPortion: number
+}
+
 export interface CrmStatisticsCustomerDealCycleByUserRespVO {
   ownerUserName: string
   customerDealCycle: number
@@ -112,5 +142,33 @@ export const StatisticsCustomerApi = {
       url: '/crm/statistics-customer/get-customer-deal-cycle-by-user',
       params
     })
+  },
+  // 6.1 获取客户行业统计数据
+  getCustomerIndustry: (params: any) => {
+    return request.get({
+      url: '/crm/statistics-customer/get-customer-industry-summary',
+      params
+    })
+  },
+  // 6.1 获取客户来源统计数据
+  getCustomerSource: (params: any) => {
+    return request.get({
+      url: '/crm/statistics-customer/get-customer-source-summary',
+      params
+    })
+  },
+  // 6.1 获取客户行业统计数据
+  getCustomerLevel: (params: any) => {
+    return request.get({
+      url: '/crm/statistics-customer/get-customer-level-summary',
+      params
+    })
+  },
+  // 6.1 获取客户行业统计数据
+  getCustomerArea: (params: any) => {
+    return request.get({
+      url: '/crm/statistics-customer/get-customer-area-summary',
+      params
+    })
   }
 }

+ 1 - 1
src/api/mall/statistics/member.ts

@@ -5,7 +5,7 @@ import { formatDate } from '@/utils/formatTime'
 
 /** 会员分析 Request VO */
 export interface MemberAnalyseReqVO {
-  times: [dayjs.ConfigType, dayjs.ConfigType]
+  times: dayjs.ConfigType[]
 }
 
 /** 会员分析 Response VO */

+ 150 - 0
src/views/crm/statistics/customer/components/CustomerAddress.vue

@@ -0,0 +1,150 @@
+<!-- 客户城市分布 -->
+<template>
+  <!-- Echarts图 -->
+  <el-card shadow="never">
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-skeleton :loading="loading" animated>
+          <Echart :height="500" :options="echartsOption" />
+        </el-skeleton>
+      </el-col>
+      <el-col :span="12">
+        <el-skeleton :loading="loading" animated>
+          <Echart :height="500" :options="echartsOption2" />
+        </el-skeleton>
+      </el-col>
+    </el-row>
+  </el-card>
+</template>
+<script lang="ts" setup>
+import { EChartsOption } from 'echarts'
+import china from '@/assets/map/json/china.json'
+import echarts from '@/plugins/echarts'
+import {
+  CrmStatisticCustomerAreaRespVO,
+  StatisticsCustomerApi
+} from '@/api/crm/statistics/customer'
+
+defineOptions({ name: 'CustomerAddress' })
+const props = defineProps<{ queryParams: any }>() // 搜索参数
+
+// 注册地图
+echarts?.registerMap('china', china as any)
+
+const loading = ref(false) // 加载中
+const areaStatisticsList = ref<CrmStatisticCustomerAreaRespVO[]>([]) // 列表的数据
+
+/** 地图配置 */
+const echartsOption = reactive<EChartsOption>({
+  title: {
+    text: '全部客户',
+    left: 'center'
+  },
+  tooltip: {
+    trigger: 'item',
+    showDelay: 0,
+    transitionDuration: 0.2
+  },
+  visualMap: {
+    text: ['高', '低'],
+    realtime: false,
+    calculable: true,
+    top: 'middle',
+    inRange: {
+      color: ['#fff', '#3b82f6']
+    }
+  },
+  series: [
+    {
+      name: '客户地域分布',
+      type: 'map',
+      map: 'china',
+      roam: false,
+      selectedMode: false,
+      data: []
+    }
+  ]
+}) as EChartsOption
+
+/** 地图配置 */
+const echartsOption2 = reactive<EChartsOption>({
+  title: {
+    text: '成交客户',
+    left: 'center'
+  },
+  tooltip: {
+    trigger: 'item',
+    showDelay: 0,
+    transitionDuration: 0.2
+  },
+  visualMap: {
+    text: ['高', '低'],
+    realtime: false,
+    calculable: true,
+    top: 'middle',
+    inRange: {
+      color: ['#fff', '#3b82f6']
+    }
+  },
+  series: [
+    {
+      name: '客户地域分布',
+      type: 'map',
+      map: 'china',
+      roam: false,
+      selectedMode: false,
+      data: []
+    }
+  ]
+}) as EChartsOption
+
+/** 获取统计数据 */
+const loadData = async () => {
+  // 1. 加载统计数据
+  loading.value = true
+  const areaList = await StatisticsCustomerApi.getCustomerArea(props.queryParams)
+  areaStatisticsList.value = areaList.map((item: CrmStatisticCustomerAreaRespVO) => {
+    return {
+      ...item,
+      areaName: item.areaName
+        .replace('维吾尔自治区', '')
+        .replace('壮族自治区', '')
+        .replace('回族自治区', '')
+        .replace('自治区', '')
+        .replace('省', '')
+    }
+  })
+  builderLeftMap()
+  builderRightMap()
+  loading.value = false
+}
+defineExpose({ loadData })
+
+const builderLeftMap = () => {
+  let min = 0
+  let max = 0
+  echartsOption.series![0].data = areaStatisticsList.value.map((item) => {
+    min = Math.min(min, item.customerCount || 0)
+    max = Math.max(max, item.customerCount || 0)
+    return { ...item, name: item.areaName, value: item.customerCount || 0 }
+  })
+  echartsOption.visualMap!['min'] = min
+  echartsOption.visualMap!['max'] = max
+}
+
+const builderRightMap = () => {
+  let min = 0
+  let max = 0
+  echartsOption2.series![0].data = areaStatisticsList.value.map((item) => {
+    min = Math.min(min, item.dealCount || 0)
+    max = Math.max(max, item.dealCount || 0)
+    return { ...item, name: item.areaName, value: item.dealCount || 0 }
+  })
+  echartsOption2.visualMap!['min'] = min
+  echartsOption2.visualMap!['max'] = max
+}
+/** 初始化 */
+onMounted(() => {
+  loadData()
+})
+</script>

+ 171 - 0
src/views/crm/statistics/customer/components/CustomerIndustry.vue

@@ -0,0 +1,171 @@
+<!-- 客户行业分析 -->
+<template>
+  <!-- Echarts图 -->
+  <el-card shadow="never">
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-skeleton :loading="loading" animated>
+          <Echart :height="500" :options="echartsOption" />
+        </el-skeleton>
+      </el-col>
+      <el-col :span="12">
+        <el-skeleton :loading="loading" animated>
+          <Echart :height="500" :options="echartsOption2" />
+        </el-skeleton>
+      </el-col>
+    </el-row>
+  </el-card>
+
+  <!-- 统计列表 -->
+  <el-card class="mt-16px" shadow="never">
+    <el-table v-loading="loading" :data="list">
+      <el-table-column align="center" label="序号" type="index" width="80" />
+      <el-table-column align="center" label="客户行业" min-width="200" prop="industryName" />
+      <el-table-column align="center" label="客户个数" min-width="200" prop="customerCount" />
+      <el-table-column align="center" label="成交个数" min-width="200" prop="dealCount" />
+      <el-table-column align="center" label="行业占比(%)" min-width="200" prop="industryPortion" />
+      <el-table-column align="center" label="成交占比(%)" min-width="200" prop="dealPortion" />
+    </el-table>
+  </el-card>
+</template>
+<script lang="ts" setup>
+import {
+  CrmStatisticCustomerIndustryRespVO,
+  StatisticsCustomerApi
+} from '@/api/crm/statistics/customer'
+import { EChartsOption } from 'echarts'
+
+defineOptions({ name: 'CustomerIndustry' })
+const props = defineProps<{ queryParams: any }>() // 搜索参数
+
+const loading = ref(false) // 加载中
+const list = ref<CrmStatisticCustomerIndustryRespVO[]>([]) // 列表的数据
+
+/** 饼图配置 */
+const echartsOption = reactive<EChartsOption>({
+  title: {
+    text: '全部客户',
+    left: 'center'
+  },
+  tooltip: {
+    trigger: 'item'
+  },
+  legend: {
+    orient: 'vertical',
+    left: 'left'
+  },
+  toolbox: {
+    feature: {
+      saveAsImage: { show: true, name: '全部客户' } // 保存为图片
+    }
+  },
+  series: [
+    {
+      name: '全部客户',
+      type: 'pie',
+      radius: ['40%', '70%'],
+      avoidLabelOverlap: false,
+      itemStyle: {
+        borderRadius: 10,
+        borderColor: '#fff',
+        borderWidth: 2
+      },
+      label: {
+        show: false,
+        position: 'center'
+      },
+      emphasis: {
+        label: {
+          show: true,
+          fontSize: 40,
+          fontWeight: 'bold'
+        }
+      },
+      labelLine: {
+        show: false
+      },
+      data: []
+    }
+  ]
+}) as EChartsOption
+/** 饼图配置 */
+const echartsOption2 = reactive<EChartsOption>({
+  title: {
+    text: '成交客户',
+    left: 'center'
+  },
+  tooltip: {
+    trigger: 'item'
+  },
+  legend: {
+    orient: 'vertical',
+    left: 'left'
+  },
+  toolbox: {
+    feature: {
+      saveAsImage: { show: true, name: '成交客户' } // 保存为图片
+    }
+  },
+  series: [
+    {
+      name: '成交客户',
+      type: 'pie',
+      radius: ['40%', '70%'],
+      avoidLabelOverlap: false,
+      itemStyle: {
+        borderRadius: 10,
+        borderColor: '#fff',
+        borderWidth: 2
+      },
+      label: {
+        show: false,
+        position: 'center'
+      },
+      emphasis: {
+        label: {
+          show: true,
+          fontSize: 40,
+          fontWeight: 'bold'
+        }
+      },
+      labelLine: {
+        show: false
+      },
+      data: []
+    }
+  ]
+}) as EChartsOption
+
+/** 获取统计数据 */
+const loadData = async () => {
+  // 1. 加载统计数据
+  loading.value = true
+  const industryList = await StatisticsCustomerApi.getCustomerIndustry(props.queryParams)
+  // 2.1 更新 Echarts 数据
+  if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) {
+    echartsOption.series[0]['data'] = industryList.map((r: CrmStatisticCustomerIndustryRespVO) => {
+      return {
+        name: r.industryName,
+        value: r.customerCount
+      }
+    })
+  }
+  // 2.2 更新 Echarts2 数据
+  if (echartsOption2.series && echartsOption2.series[0] && echartsOption2.series[0]['data']) {
+    echartsOption2.series[0]['data'] = industryList.map((r: CrmStatisticCustomerIndustryRespVO) => {
+      return {
+        name: r.industryName,
+        value: r.dealCount
+      }
+    })
+  }
+  list.value = industryList
+  loading.value = false
+}
+defineExpose({ loadData })
+
+/** 初始化 */
+onMounted(() => {
+  loadData()
+})
+</script>

+ 171 - 0
src/views/crm/statistics/customer/components/CustomerLevel.vue

@@ -0,0 +1,171 @@
+<!-- 客户来源分析 -->
+<template>
+  <!-- Echarts图 -->
+  <el-card shadow="never">
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-skeleton :loading="loading" animated>
+          <Echart :height="500" :options="echartsOption" />
+        </el-skeleton>
+      </el-col>
+      <el-col :span="12">
+        <el-skeleton :loading="loading" animated>
+          <Echart :height="500" :options="echartsOption2" />
+        </el-skeleton>
+      </el-col>
+    </el-row>
+  </el-card>
+
+  <!-- 统计列表 -->
+  <el-card class="mt-16px" shadow="never">
+    <el-table v-loading="loading" :data="list">
+      <el-table-column align="center" label="序号" type="index" width="80" />
+      <el-table-column align="center" label="客户来源" min-width="200" prop="levelName" />
+      <el-table-column align="center" label="客户个数" min-width="200" prop="customerCount" />
+      <el-table-column align="center" label="成交个数" min-width="200" prop="dealCount" />
+      <el-table-column align="center" label="级别占比(%)" min-width="200" prop="levelPortion" />
+      <el-table-column align="center" label="成交占比(%)" min-width="200" prop="dealPortion" />
+    </el-table>
+  </el-card>
+</template>
+<script lang="ts" setup>
+import {
+  CrmStatisticCustomerLevelRespVO,
+  StatisticsCustomerApi
+} from '@/api/crm/statistics/customer'
+import { EChartsOption } from 'echarts'
+
+defineOptions({ name: 'CustomerSource' })
+const props = defineProps<{ queryParams: any }>() // 搜索参数
+
+const loading = ref(false) // 加载中
+const list = ref<CrmStatisticCustomerLevelRespVO[]>([]) // 列表的数据
+
+/** 饼图配置 */
+const echartsOption = reactive<EChartsOption>({
+  title: {
+    text: '全部客户',
+    left: 'center'
+  },
+  tooltip: {
+    trigger: 'item'
+  },
+  legend: {
+    orient: 'vertical',
+    left: 'left'
+  },
+  toolbox: {
+    feature: {
+      saveAsImage: { show: true, name: '全部客户' } // 保存为图片
+    }
+  },
+  series: [
+    {
+      name: '全部客户',
+      type: 'pie',
+      radius: ['40%', '70%'],
+      avoidLabelOverlap: false,
+      itemStyle: {
+        borderRadius: 10,
+        borderColor: '#fff',
+        borderWidth: 2
+      },
+      label: {
+        show: false,
+        position: 'center'
+      },
+      emphasis: {
+        label: {
+          show: true,
+          fontSize: 40,
+          fontWeight: 'bold'
+        }
+      },
+      labelLine: {
+        show: false
+      },
+      data: []
+    }
+  ]
+}) as EChartsOption
+/** 饼图配置 */
+const echartsOption2 = reactive<EChartsOption>({
+  title: {
+    text: '成交客户',
+    left: 'center'
+  },
+  tooltip: {
+    trigger: 'item'
+  },
+  legend: {
+    orient: 'vertical',
+    left: 'left'
+  },
+  toolbox: {
+    feature: {
+      saveAsImage: { show: true, name: '成交客户' } // 保存为图片
+    }
+  },
+  series: [
+    {
+      name: '成交客户',
+      type: 'pie',
+      radius: ['40%', '70%'],
+      avoidLabelOverlap: false,
+      itemStyle: {
+        borderRadius: 10,
+        borderColor: '#fff',
+        borderWidth: 2
+      },
+      label: {
+        show: false,
+        position: 'center'
+      },
+      emphasis: {
+        label: {
+          show: true,
+          fontSize: 40,
+          fontWeight: 'bold'
+        }
+      },
+      labelLine: {
+        show: false
+      },
+      data: []
+    }
+  ]
+}) as EChartsOption
+
+/** 获取统计数据 */
+const loadData = async () => {
+  // 1. 加载统计数据
+  loading.value = true
+  const levelList = await StatisticsCustomerApi.getCustomerLevel(props.queryParams)
+  // 2.1 更新 Echarts 数据
+  if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) {
+    echartsOption.series[0]['data'] = levelList.map((r: CrmStatisticCustomerLevelRespVO) => {
+      return {
+        name: r.levelName,
+        value: r.customerCount
+      }
+    })
+  }
+  // 2.2 更新 Echarts2 数据
+  if (echartsOption2.series && echartsOption2.series[0] && echartsOption2.series[0]['data']) {
+    echartsOption2.series[0]['data'] = levelList.map((r: CrmStatisticCustomerLevelRespVO) => {
+      return {
+        name: r.levelName,
+        value: r.dealCount
+      }
+    })
+  }
+  list.value = levelList
+  loading.value = false
+}
+defineExpose({ loadData })
+
+/** 初始化 */
+onMounted(() => {
+  loadData()
+})
+</script>

+ 171 - 0
src/views/crm/statistics/customer/components/CustomerSource.vue

@@ -0,0 +1,171 @@
+<!-- 客户来源分析 -->
+<template>
+  <!-- Echarts图 -->
+  <el-card shadow="never">
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-skeleton :loading="loading" animated>
+          <Echart :height="500" :options="echartsOption" />
+        </el-skeleton>
+      </el-col>
+      <el-col :span="12">
+        <el-skeleton :loading="loading" animated>
+          <Echart :height="500" :options="echartsOption2" />
+        </el-skeleton>
+      </el-col>
+    </el-row>
+  </el-card>
+
+  <!-- 统计列表 -->
+  <el-card class="mt-16px" shadow="never">
+    <el-table v-loading="loading" :data="list">
+      <el-table-column align="center" label="序号" type="index" width="80" />
+      <el-table-column align="center" label="客户来源" min-width="200" prop="sourceName" />
+      <el-table-column align="center" label="客户个数" min-width="200" prop="customerCount" />
+      <el-table-column align="center" label="成交个数" min-width="200" prop="dealCount" />
+      <el-table-column align="center" label="来源占比(%)" min-width="200" prop="sourcePortion" />
+      <el-table-column align="center" label="成交占比(%)" min-width="200" prop="dealPortion" />
+    </el-table>
+  </el-card>
+</template>
+<script lang="ts" setup>
+import {
+  CrmStatisticCustomerSourceRespVO,
+  StatisticsCustomerApi
+} from '@/api/crm/statistics/customer'
+import { EChartsOption } from 'echarts'
+
+defineOptions({ name: 'CustomerSource' })
+const props = defineProps<{ queryParams: any }>() // 搜索参数
+
+const loading = ref(false) // 加载中
+const list = ref<CrmStatisticCustomerSourceRespVO[]>([]) // 列表的数据
+
+/** 饼图配置 */
+const echartsOption = reactive<EChartsOption>({
+  title: {
+    text: '全部客户',
+    left: 'center'
+  },
+  tooltip: {
+    trigger: 'item'
+  },
+  legend: {
+    orient: 'vertical',
+    left: 'left'
+  },
+  toolbox: {
+    feature: {
+      saveAsImage: { show: true, name: '全部客户' } // 保存为图片
+    }
+  },
+  series: [
+    {
+      name: '全部客户',
+      type: 'pie',
+      radius: ['40%', '70%'],
+      avoidLabelOverlap: false,
+      itemStyle: {
+        borderRadius: 10,
+        borderColor: '#fff',
+        borderWidth: 2
+      },
+      label: {
+        show: false,
+        position: 'center'
+      },
+      emphasis: {
+        label: {
+          show: true,
+          fontSize: 40,
+          fontWeight: 'bold'
+        }
+      },
+      labelLine: {
+        show: false
+      },
+      data: []
+    }
+  ]
+}) as EChartsOption
+/** 饼图配置 */
+const echartsOption2 = reactive<EChartsOption>({
+  title: {
+    text: '成交客户',
+    left: 'center'
+  },
+  tooltip: {
+    trigger: 'item'
+  },
+  legend: {
+    orient: 'vertical',
+    left: 'left'
+  },
+  toolbox: {
+    feature: {
+      saveAsImage: { show: true, name: '成交客户' } // 保存为图片
+    }
+  },
+  series: [
+    {
+      name: '成交客户',
+      type: 'pie',
+      radius: ['40%', '70%'],
+      avoidLabelOverlap: false,
+      itemStyle: {
+        borderRadius: 10,
+        borderColor: '#fff',
+        borderWidth: 2
+      },
+      label: {
+        show: false,
+        position: 'center'
+      },
+      emphasis: {
+        label: {
+          show: true,
+          fontSize: 40,
+          fontWeight: 'bold'
+        }
+      },
+      labelLine: {
+        show: false
+      },
+      data: []
+    }
+  ]
+}) as EChartsOption
+
+/** 获取统计数据 */
+const loadData = async () => {
+  // 1. 加载统计数据
+  loading.value = true
+  const sourceList = await StatisticsCustomerApi.getCustomerSource(props.queryParams)
+  // 2.1 更新 Echarts 数据
+  if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) {
+    echartsOption.series[0]['data'] = sourceList.map((r: CrmStatisticCustomerSourceRespVO) => {
+      return {
+        name: r.sourceName,
+        value: r.customerCount
+      }
+    })
+  }
+  // 2.2 更新 Echarts2 数据
+  if (echartsOption2.series && echartsOption2.series[0] && echartsOption2.series[0]['data']) {
+    echartsOption2.series[0]['data'] = sourceList.map((r: CrmStatisticCustomerSourceRespVO) => {
+      return {
+        name: r.sourceName,
+        value: r.dealCount
+      }
+    })
+  }
+  list.value = sourceList
+  loading.value = false
+}
+defineExpose({ loadData })
+
+/** 初始化 */
+onMounted(() => {
+  loadData()
+})
+</script>

+ 63 - 19
src/views/crm/statistics/customer/index.vue

@@ -3,49 +3,55 @@
   <ContentWrap>
     <!-- 搜索工作栏 -->
     <el-form
-      class="-mb-15px"
-      :model="queryParams"
       ref="queryFormRef"
       :inline="true"
+      :model="queryParams"
+      class="-mb-15px"
       label-width="68px"
     >
       <el-form-item label="时间范围" prop="orderDate">
         <el-date-picker
           v-model="queryParams.times"
+          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
           :shortcuts="defaultShortcuts"
           class="!w-240px"
           end-placeholder="结束日期"
           start-placeholder="开始日期"
           type="daterange"
           value-format="YYYY-MM-DD HH:mm:ss"
-          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
         />
       </el-form-item>
       <el-form-item label="归属部门" prop="deptId">
         <el-tree-select
           v-model="queryParams.deptId"
-          class="!w-240px"
           :data="deptList"
           :props="defaultProps"
           check-strictly
+          class="!w-240px"
           node-key="id"
           placeholder="请选择归属部门"
           @change="queryParams.userId = undefined"
         />
       </el-form-item>
       <el-form-item label="员工" prop="userId">
-        <el-select v-model="queryParams.userId" class="!w-240px" placeholder="员工" clearable>
+        <el-select v-model="queryParams.userId" class="!w-240px" clearable placeholder="员工">
           <el-option
             v-for="(user, index) in userListByDeptId"
+            :key="index"
             :label="user.nickname"
             :value="user.id"
-            :key="index"
           />
         </el-select>
       </el-form-item>
       <el-form-item>
-        <el-button @click="handleQuery"> <Icon icon="ep:search" class="mr-5px" /> 搜索 </el-button>
-        <el-button @click="resetQuery"> <Icon icon="ep:refresh" class="mr-5px" /> 重置 </el-button>
+        <el-button @click="handleQuery">
+          <Icon class="mr-5px" icon="ep:search" />
+          搜索
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon class="mr-5px" icon="ep:refresh" />
+          重置
+        </el-button>
       </el-form-item>
     </el-form>
   </ContentWrap>
@@ -54,24 +60,40 @@
   <el-col>
     <el-tabs v-model="activeTab">
       <!-- 客户总量分析 -->
-      <el-tab-pane label="客户总量分析" name="customerSummary" lazy>
-        <CustomerSummary :query-params="queryParams" ref="customerSummaryRef" />
+      <el-tab-pane label="客户总量分析" lazy name="customerSummary">
+        <CustomerSummary ref="customerSummaryRef" :query-params="queryParams" />
       </el-tab-pane>
       <!-- 客户跟进次数分析 -->
-      <el-tab-pane label="客户跟进次数分析" name="followupSummary" lazy>
-        <CustomerFollowupSummary :query-params="queryParams" ref="followupSummaryRef" />
+      <el-tab-pane label="客户跟进次数分析" lazy name="followupSummary">
+        <CustomerFollowupSummary ref="followupSummaryRef" :query-params="queryParams" />
       </el-tab-pane>
       <!-- 客户跟进方式分析 -->
-      <el-tab-pane label="客户跟进方式分析" name="followupType" lazy>
-        <CustomerFollowupType :query-params="queryParams" ref="followupTypeRef" />
+      <el-tab-pane label="客户跟进方式分析" lazy name="followupType">
+        <CustomerFollowupType ref="followupTypeRef" :query-params="queryParams" />
       </el-tab-pane>
       <!-- 客户转化率分析 -->
-      <el-tab-pane label="客户转化率分析" name="conversionStat" lazy>
-        <CustomerConversionStat :query-params="queryParams" ref="conversionStatRef" />
+      <el-tab-pane label="客户转化率分析" lazy name="conversionStat">
+        <CustomerConversionStat ref="conversionStatRef" :query-params="queryParams" />
       </el-tab-pane>
       <!-- 成交周期分析 -->
-      <el-tab-pane label="成交周期分析" name="dealCycle" lazy>
-        <CustomerDealCycle :query-params="queryParams" ref="dealCycleRef" />
+      <el-tab-pane label="成交周期分析" lazy name="dealCycle">
+        <CustomerDealCycle ref="dealCycleRef" :query-params="queryParams" />
+      </el-tab-pane>
+      <!-- 城市分布分析 -->
+      <el-tab-pane label="城市分布分析" lazy name="addressRef">
+        <CustomerAddress ref="addressRef" :query-params="queryParams" />
+      </el-tab-pane>
+      <!-- 客户级别分析 -->
+      <el-tab-pane label="客户级别分析" lazy name="levelRef">
+        <CustomerLevel ref="levelRef" :query-params="queryParams" />
+      </el-tab-pane>
+      <!-- 客户来源分析 -->
+      <el-tab-pane label="客户来源分析" lazy name="sourceRef">
+        <CustomerSource ref="sourceRef" :query-params="queryParams" />
+      </el-tab-pane>
+      <!-- 客户行业分析 -->
+      <el-tab-pane label="客户行业分析" lazy name="industryRef">
+        <CustomerIndustry ref="industryRef" :query-params="queryParams" />
       </el-tab-pane>
     </el-tabs>
   </el-col>
@@ -88,6 +110,10 @@ import CustomerFollowupSummary from './components/CustomerFollowupSummary.vue'
 import CustomerFollowupType from './components/CustomerFollowupType.vue'
 import CustomerConversionStat from './components/CustomerConversionStat.vue'
 import CustomerDealCycle from './components/CustomerDealCycle.vue'
+import CustomerAddress from './components/CustomerAddress.vue'
+import CustomerIndustry from './components/CustomerIndustry.vue'
+import CustomerSource from './components/CustomerSource.vue'
+import CustomerLevel from './components/CustomerLevel.vue'
 
 defineOptions({ name: 'CrmStatisticsCustomer' })
 
@@ -125,7 +151,13 @@ const conversionStatRef = ref()
 // 缺 crm_owner_record 表
 // 6.成交周期分析
 const dealCycleRef = ref()
-
+const addressRef = ref()
+// 客户级别
+const levelRef = ref()
+// 客户来源
+const sourceRef = ref()
+// 客户行业
+const industryRef = ref()
 /** 搜索按钮操作 */
 const handleQuery = () => {
   switch (activeTab.value) {
@@ -144,6 +176,18 @@ const handleQuery = () => {
     case 'dealCycle':
       dealCycleRef.value?.loadData?.()
       break
+    case 'addressRef':
+      addressRef.value?.loadData?.()
+      break
+    case 'levelRef':
+      levelRef.value?.loadData?.()
+      break
+    case 'sourceRef':
+      sourceRef.value?.loadData?.()
+      break
+    case 'industryRef':
+      industryRef.value?.loadData?.()
+      break
   }
 }