Переглянути джерело

feat: CRM/数据统计/客户总量分析

dhb52 1 рік тому
батько
коміт
441b91381f

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

@@ -0,0 +1,24 @@
+import request from '@/config/axios'
+
+export interface StatisticsCustomerRespVO {
+  count: number
+  category: string
+}
+
+// 客户总量分析 API
+export const StatisticsCustomerApi = {
+  // 客户总量(新建)
+  getTotalCustomerCount: (params: any) => {
+    return request.get({
+      url: '/crm/statistics-customer/get-total-customer-count',
+      params
+    })
+  },
+  // 客户总量(成交)
+  getDealTotalCustomerCount: (params: any) => {
+    return request.get({
+      url: '/crm/statistics-customer/get-deal-total-customer-count',
+      params
+    })
+  },
+}

+ 113 - 0
src/views/crm/statistics/customer/components/TotalCustomerCount.vue

@@ -0,0 +1,113 @@
+<!-- 客户总量分析 -->
+<template>
+  <!-- 柱状图 -->
+  <el-card shadow="never">
+    <el-skeleton :loading="loading" animated>
+      <Echart :height="500" :options="echartsOption" />
+    </el-skeleton>
+  </el-card>
+
+  <!-- 统计列表 -->
+  <el-card shadow="never" class="mt-16px">
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="序号" align="center" type="index" width="80" />
+      <el-table-column label="日期" align="center" prop="category" min-width="200" />
+      <el-table-column label="新增客户数" align="center" prop="customerCount" min-width="200" />
+      <el-table-column label="成交客户数" align="center" prop="dealCustomerCount" min-width="200" />
+    </el-table>
+  </el-card>
+</template>
+<script setup lang="ts">
+import { StatisticsCustomerApi, StatisticsCustomerRespVO } from '@/api/crm/statistics/customer'
+import { EChartsOption } from 'echarts'
+import { clone } from 'lodash-es'
+
+defineOptions({ name: 'TotalCustomerCount' })
+const props = defineProps<{ queryParams: any }>() // 搜索参数
+
+const loading = ref(false) // 加载中
+const list = ref<StatisticsCustomerRespVO[]>([]) // 列表的数据
+
+/** 柱状图配置:纵向 */
+const echartsOption = reactive<EChartsOption>({
+  grid: {
+    left: 20,
+    right: 20,
+    bottom: 20,
+    containLabel: true
+  },
+  legend: { },
+  series: [
+    {
+      name: '新增客户数',
+      type: 'bar',
+      data: []
+    },
+    {
+      name: '成交客户数',
+      type: 'bar',
+      data: []
+    },
+  ],
+  toolbox: {
+    feature: {
+      dataZoom: {
+        xAxisIndex: false // 数据区域缩放:Y 轴不缩放
+      },
+      brush: {
+        type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
+      },
+      saveAsImage: { show: true, name: '客户总量分析' } // 保存为图片
+    }
+  },
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
+  },
+  yAxis: {
+    type: 'value',
+    name: '数量(个)'
+  },
+  xAxis: {
+    type: 'category',
+    name: '日期',
+    data: []
+  }
+}) as EChartsOption
+
+/** 获取统计数据 */
+const loadData = async () => {
+  // 1. 加载统计数据
+  loading.value = true
+  const customerCount = await StatisticsCustomerApi.getTotalCustomerCount(props.queryParams)
+  const dealCustomerCount = await StatisticsCustomerApi.getDealTotalCustomerCount(props.queryParams)
+  // 2.1 更新 Echarts 数据
+  if (echartsOption.xAxis && echartsOption.xAxis['data']) {
+    echartsOption.xAxis['data'] = clone(customerCount.map(s => s['category']))
+  }
+  if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) {
+    echartsOption.series[0]['data'] = clone(customerCount.map(s => s['count']))
+  }
+  if (echartsOption.series && echartsOption.series[1] && echartsOption.series[1]['data']) {
+    echartsOption.series[1]['data'] = clone(dealCustomerCount.map(s => s['count']))
+  }
+  // 2.2 更新列表数据
+  const tableData = customerCount.map((item, index) => {
+    return {
+      category: item['category'],
+      customerCount: item['count'],
+      dealCustomerCount: dealCustomerCount[index]['count'],
+    }
+  })
+  list.value = tableData
+  loading.value = false
+}
+defineExpose({ loadData })
+
+/** 初始化 */
+onMounted(() => {
+  loadData()
+})
+</script>

+ 125 - 0
src/views/crm/statistics/customer/index.vue

@@ -0,0 +1,125 @@
+<!-- 数据统计 - 员工客户分析 -->
+<template>
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item label="时间范围" prop="orderDate">
+        <el-date-picker
+          v-model="queryParams.times"
+          :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"
+          :data="deptList"
+          :props="defaultProps"
+          check-strictly
+          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-option
+            v-for="(user, index) in userListByDeptId"
+            :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-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 排行数据 -->
+  <el-col>
+    <el-tabs v-model="activeTab">
+      <!-- 客户总量分析 -->
+      <el-tab-pane label="客户总量分析" name="totalCustomerCount" lazy>
+        <TotalCustomerCount :query-params="queryParams" ref="totalCustomerCountRef" />
+      </el-tab-pane>
+    </el-tabs>
+  </el-col>
+</template>
+<script lang="ts" setup>
+import * as DeptApi from '@/api/system/dept'
+import * as UserApi from '@/api/system/user'
+import { useUserStore } from '@/store/modules/user'
+import { beginOfDay, defaultShortcuts, endOfDay, formatDate } from '@/utils/formatTime'
+import { defaultProps, handleTree } from '@/utils/tree'
+import TotalCustomerCount from './components/TotalCustomerCount.vue'
+
+defineOptions({ name: 'CrmStatisticsCustomer' })
+
+const queryParams = reactive({
+  deptId: useUserStore().getUser.deptId,
+  userId: undefined,
+  times: [
+    // 默认显示最近一周的数据
+    formatDate(beginOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7))),
+    formatDate(endOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24)))
+  ]
+})
+
+const queryFormRef = ref() // 搜索的表单
+const deptList = ref<Tree[]>([]) // 部门树形结构
+const userList = ref<UserApi.UserVO[]>([]) // 全量用户清单
+// 根据选择的部门筛选员工清单
+const userListByDeptId = computed(() =>
+  queryParams.deptId ? userList.value.filter((u) => u.deptId === queryParams.deptId) : []
+)
+
+// 活跃标签
+const activeTab = ref('totalCustomerCount')
+// 1.客户总量分析
+const totalCustomerCountRef = ref()
+// 2.客户跟进次数分析
+// 3.客户跟进方式分析
+// 4.客户转化率分析
+// 5.公海客户分析
+// 6.成交周期分析
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  switch (activeTab.value) {
+    case 'totalCustomerCount':
+      totalCustomerCountRef.value?.loadData?.()
+      break
+  }
+}
+
+// 当 activeTab 改变时,刷新当前活动的 tab
+watch(activeTab, () => {
+  handleQuery()
+})
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+// 加载部门树
+onMounted(async () => {
+  deptList.value = handleTree(await DeptApi.getSimpleDeptList())
+  userList.value = handleTree(await UserApi.getSimpleUserList())
+})
+</script>