Selaa lähdekoodia

新增:商业智能-排行榜,合同金额排行和回款金额排行

anhaohao 1 vuosi sitten
vanhempi
commit
770e551900

+ 34 - 0
src/api/crm/bi/ranking.ts

@@ -0,0 +1,34 @@
+import request from '@/config/axios'
+
+export interface BiContractRanKingRespVO {
+  price: number
+  nickname: string
+  deptName: string
+}
+export interface BiReceivablesRanKingRespVO {
+  price: number
+  nickname: string
+  deptName: string
+}
+export interface BiRankReqVO {
+  deptId: number
+  type: string
+}
+
+// 排行 API
+export const RankingStatisticsApi = {
+  // 获得合同排行榜
+  contractAmountRanking: (params: any) => {
+    return request.get({
+      url: '/bi/ranking/contract-ranking',
+      params
+    })
+  },
+  // 获得回款排行榜
+  receivablesAmountRanking: (params: any) => {
+    return request.get({
+      url: '/bi/ranking/receivables-ranking',
+      params
+    })
+  }
+}

+ 4 - 1
src/utils/dict.ts

@@ -203,5 +203,8 @@ export enum DICT_TYPE {
   CRM_PRODUCT_STATUS = 'crm_product_status',
   CRM_PERMISSION_LEVEL = 'crm_permission_level', // CRM 数据权限的级别
   CRM_PRODUCT_UNIT = 'crm_product_unit', // 产品单位
-  CRM_FOLLOW_UP_TYPE = 'crm_follow_up_type' // 跟进方式
+  CRM_FOLLOW_UP_TYPE = 'crm_follow_up_type', // 跟进方式
+
+  // ========== BI - 商业智能模块 ==========
+  BI_ANALYZE_TYPE = 'bi_analyze_type' // 分析类型
 }

+ 133 - 0
src/views/crm/bi/ranking/components/RankingContractStatistics.vue

@@ -0,0 +1,133 @@
+<template>
+  <el-card shadow="never">
+    <!-- 柱状图 -->
+    <el-skeleton :loading="trendLoading" animated>
+      <Echart :height="500" :options="barChartOptions" />
+    </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="nickname" min-width="200" />
+      <el-table-column label="部门" align="center" prop="deptName" min-width="200" />
+      <el-table-column label="合同金额(元)" align="center" prop="price" min-width="200" />
+    </el-table>
+  </el-card>
+</template>
+<script setup lang="ts">
+import { RankingStatisticsApi, BiContractRanKingRespVO, BiRankReqVO } from '@/api/crm/bi/ranking'
+import { EChartsOption } from 'echarts'
+
+/** 合同金额排行 */
+defineOptions({ name: 'RankingContractStatistics' })
+
+const trendLoading = ref(true) // 状态加载中
+const loading = ref(false) // 列表的加载中
+const list = ref<BiContractRanKingRespVO[]>([]) // 列表的数据
+const params = defineProps<{ queryParams: BiRankReqVO }>() // 搜索参数
+
+/** 柱状图配置 横向 */
+const barChartOptions = reactive<EChartsOption>({
+  dataset: {
+    dimensions: ['name', 'value'],
+    source: []
+  },
+  grid: {
+    left: 20,
+    right: 20,
+    bottom: 20,
+    top: 80,
+    containLabel: true
+  },
+  legend: {
+    top: 50
+  },
+  series: [
+    {
+      name: '合同金额排行',
+      type: 'bar',
+      smooth: true,
+      itemStyle: { color: '#B37FEB' }
+    }
+  ],
+  toolbox: {
+    feature: {
+      // 数据区域缩放
+      dataZoom: {
+        yAxisIndex: false // Y轴不缩放
+      },
+      brush: {
+        type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
+      },
+      saveAsImage: { show: true, name: '合同金额排行' } // 保存为图片
+    }
+  },
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
+  },
+  xAxis: {
+    type: 'value',
+    name: '合同金额(元)',
+    nameGap: 30,
+    nameTextStyle: {
+      color: '#666',
+      fontSize: 14
+    }
+  },
+  yAxis: {
+    type: 'category',
+    name: '签订人',
+    nameGap: 30,
+    nameTextStyle: {
+      color: '#666',
+      fontSize: 14
+    },
+    axisLabel: {
+      formatter: (value: string) => {
+        return value
+      }
+    }
+  }
+}) as EChartsOption
+
+/** 获取合同金额排行 */
+const getRankingContractStatistics = async () => {
+  trendLoading.value = true
+  loading.value = true
+  const rankingList = await RankingStatisticsApi.contractAmountRanking(params.queryParams)
+  let source = rankingList.map((item: BiContractRanKingRespVO) => {
+    return {
+      name: item.nickname,
+      value: item.price
+    }
+  })
+  // 反转数据源
+  source = source.reverse()
+  // 更新 Echarts 数据
+  if (barChartOptions.dataset && barChartOptions.dataset['source']) {
+    barChartOptions.dataset['source'] = source
+  }
+  // 更新列表数据
+  list.value = rankingList
+  trendLoading.value = false
+  loading.value = false
+}
+
+/** 重新加载数据 */
+const reloadData = async () => {
+  await getRankingContractStatistics()
+}
+// 暴露 reloadData 函数
+defineExpose({
+  reloadData
+})
+
+onMounted(() => {
+  getRankingContractStatistics()
+})
+</script>
+<style scoped lang="scss"></style>

+ 133 - 0
src/views/crm/bi/ranking/components/RankingReceivablesStatistics.vue

@@ -0,0 +1,133 @@
+<template>
+  <el-card shadow="never">
+    <!-- 柱状图 -->
+    <el-skeleton :loading="trendLoading" animated>
+      <Echart :height="500" :options="barChartOptions" />
+    </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="nickname" min-width="200" />
+      <el-table-column label="部门" align="center" prop="deptName" min-width="200" />
+      <el-table-column label="合同金额(元)" align="center" prop="price" min-width="200" />
+    </el-table>
+  </el-card>
+</template>
+<script setup lang="ts">
+import { RankingStatisticsApi, BiReceivablesRanKingRespVO } from '@/api/crm/bi/ranking'
+import { EChartsOption } from 'echarts'
+
+/** 回款金额排行 */
+defineOptions({ name: 'RankingReceivablesStatistics' })
+
+const trendLoading = ref(true) // 状态加载中
+const loading = ref(false) // 列表的加载中
+const list = ref<BiReceivablesRanKingRespVO[]>([]) // 列表的数据
+const params = defineProps<{ queryParams: any }>() // 搜索参数
+
+/** 柱状图配置 横向 */
+const barChartOptions = reactive<EChartsOption>({
+  dataset: {
+    dimensions: ['name', 'value'],
+    source: []
+  },
+  grid: {
+    left: 20,
+    right: 20,
+    bottom: 20,
+    top: 80,
+    containLabel: true
+  },
+  legend: {
+    top: 50
+  },
+  series: [
+    {
+      name: '回款金额排行',
+      type: 'bar',
+      smooth: true,
+      itemStyle: { color: '#B37FEB' }
+    }
+  ],
+  toolbox: {
+    feature: {
+      // 数据区域缩放
+      dataZoom: {
+        yAxisIndex: false // Y轴不缩放
+      },
+      brush: {
+        type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
+      },
+      saveAsImage: { show: true, name: '回款金额排行' } // 保存为图片
+    }
+  },
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
+  },
+  xAxis: {
+    type: 'value',
+    name: '回款金额(元)',
+    nameGap: 30,
+    nameTextStyle: {
+      color: '#666',
+      fontSize: 14
+    }
+  },
+  yAxis: {
+    type: 'category',
+    name: '签订人',
+    nameGap: 30,
+    nameTextStyle: {
+      color: '#666',
+      fontSize: 14
+    },
+    axisLabel: {
+      formatter: (value: string) => {
+        return value
+      }
+    }
+  }
+}) as EChartsOption
+
+/** 获取回款金额排行 */
+const getRankingReceivablesStatistics = async () => {
+  trendLoading.value = true
+  loading.value = true
+  const rankingList = await RankingStatisticsApi.receivablesAmountRanking(params.queryParams)
+  let source = rankingList.map((item: BiReceivablesRanKingRespVO) => {
+    return {
+      name: item.nickname,
+      value: item.price
+    }
+  })
+  // 反转数据源
+  source = source.reverse()
+  // 更新 Echarts 数据
+  if (barChartOptions.dataset && barChartOptions.dataset['source']) {
+    barChartOptions.dataset['source'] = source
+  }
+  // 更新列表数据
+  list.value = rankingList
+  trendLoading.value = false
+  loading.value = false
+}
+
+/** 重新加载数据 */
+const reloadData = async () => {
+  await getRankingReceivablesStatistics()
+}
+// 暴露 reloadData 函数
+defineExpose({
+  reloadData
+})
+
+onMounted(() => {
+  getRankingReceivablesStatistics()
+})
+</script>
+<style scoped lang="scss"></style>

+ 91 - 0
src/views/crm/bi/ranking/index.vue

@@ -0,0 +1,91 @@
+<template>
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item label="类型" prop="type">
+        <el-select v-model="queryParams.type" placeholder="请选择类型" clearable class="!w-240px">
+          <el-option
+            v-for="dict in getIntDictOptions(DICT_TYPE.BI_ANALYZE_TYPE)"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </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="请选择归属部门"
+        />
+      </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="contractAmountRanking">
+        <!-- 合同金额排行 -->
+        <RankingContractStatistics :queryParams="queryParams" ref="rankingContractStatisticsRef" />
+      </el-tab-pane>
+      <el-tab-pane label="回款金额排行" name="receivablesRanKing" lazy>
+        <!-- 回款金额排行 -->
+        <RankingReceivablesStatistics
+          :queryParams="queryParams"
+          ref="rankingReceivablesStatisticsRef"
+        />
+      </el-tab-pane>
+    </el-tabs>
+  </el-col>
+</template>
+<script lang="ts" setup>
+import RankingContractStatistics from './components/RankingContractStatistics.vue'
+import { defaultProps, handleTree } from '@/utils/tree'
+import * as DeptApi from '@/api/system/dept'
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+
+/** 排行榜 */
+defineOptions({ name: 'RankingStatistics' })
+
+const queryParams = reactive({
+  type: 9, // 将 type 的初始值设置为 9 本年
+  deptId: null
+})
+const queryFormRef = ref() // 搜索的表单
+const deptList = ref<Tree[]>([]) // 树形结构
+const activeTab = ref('contractAmountRanking')
+const rankingContractStatisticsRef = ref() // RankingContractStatistics组件的引用
+const rankingReceivablesStatisticsRef = ref() // RankingReceivablesStatistics组件的引用
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  if (activeTab.value === 'contractAmountRanking') {
+    rankingContractStatisticsRef.value.reloadData()
+  } else if (activeTab.value === 'receivablesRanKing') {
+    rankingReceivablesStatisticsRef.value.reloadData()
+  }
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+// 加载部门树
+onMounted(async () => {
+  deptList.value = handleTree(await DeptApi.getSimpleDeptList())
+})
+</script>
+<style lang="scss" scoped></style>