Procházet zdrojové kódy

CRM:优化合同金额排行、回款金额排行

YunaiV před 1 rokem
rodič
revize
4549f8ed3c

+ 25 - 0
src/api/crm/bi/rank.ts

@@ -0,0 +1,25 @@
+import request from '@/config/axios'
+
+export interface BiRankRespVO {
+  count: number
+  nickname: string
+  deptName: string
+}
+
+// 排行 API
+export const RankApi = {
+  // 获得合同排行榜
+  getContractPriceRank: (params: any) => {
+    return request.get({
+      url: '/crm/bi-rank/get-contract-price-rank',
+      params
+    })
+  },
+  // 获得回款排行榜
+  getReceivablePriceRank: (params: any) => {
+    return request.get({
+      url: '/crm/bi-rank/get-receivable-price-rank',
+      params
+    })
+  }
+}

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

@@ -1,38 +0,0 @@
-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
-  orderDate: Date[]
-  startTime: Date
-  endTime: Date
-}
-
-// 排行 API
-export const RankingStatisticsApi = {
-  // 获得合同排行榜
-  contractAmountRanking: (params: any) => {
-    return request.get({
-      url: '/crm/bi-ranking/contract-ranking',
-      params
-    })
-  },
-  // 获得回款排行榜
-  receivablesAmountRanking: (params: any) => {
-    return request.get({
-      url: '/crm/bi-ranking/receivables-ranking',
-      params
-    })
-  }
-}

+ 6 - 2
src/store/modules/user.ts

@@ -10,7 +10,9 @@ interface UserVO {
   id: number
   avatar: string
   nickname: string
+  deptId: number
 }
+
 interface UserInfoVO {
   permissions: string[]
   roles: string[]
@@ -26,7 +28,8 @@ export const useUserStore = defineStore('admin-user', {
     user: {
       id: 0,
       avatar: '',
-      nickname: ''
+      nickname: '',
+      deptId: 0
     }
   }),
   getters: {
@@ -73,7 +76,8 @@ export const useUserStore = defineStore('admin-user', {
       this.user = {
         id: 0,
         avatar: '',
-        nickname: ''
+        nickname: '',
+        deptId: 0
       }
     }
   }

+ 107 - 0
src/views/crm/bi/rank/ContractPriceRank.vue

@@ -0,0 +1,107 @@
+<!-- 合同金额排行 -->
+<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="nickname" min-width="200" />
+      <el-table-column label="部门" align="center" prop="deptName" min-width="200" />
+      <el-table-column
+        label="合同金额(元)"
+        align="center"
+        prop="count"
+        min-width="200"
+        :formatter="fenToYuanFormat"
+      />
+    </el-table>
+  </el-card>
+</template>
+<script setup lang="ts">
+import { RankApi, BiRankRespVO } from '@/api/crm/bi/rank'
+import { EChartsOption } from 'echarts'
+import { fenToYuanFormat } from '@/utils/formatter'
+import { fenToYuan } from '@/utils'
+import { clone } from 'unocss'
+
+defineOptions({ name: 'ContractPriceRank' })
+const props = defineProps<{ queryParams: any }>() // 搜索参数
+
+const loading = ref(false) // 加载中
+const list = ref<BiRankRespVO[]>([]) // 列表的数据
+
+/** 柱状图配置:横向 */
+const echartsOption = reactive<EChartsOption>({
+  dataset: {
+    dimensions: ['nickname', 'count'],
+    source: []
+  },
+  grid: {
+    left: 20,
+    right: 20,
+    bottom: 20,
+    containLabel: true
+  },
+  legend: {
+    top: 50
+  },
+  series: [
+    {
+      name: '合同金额排行',
+      type: 'bar'
+    }
+  ],
+  toolbox: {
+    feature: {
+      dataZoom: {
+        yAxisIndex: false // 数据区域缩放:Y 轴不缩放
+      },
+      brush: {
+        type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
+      },
+      saveAsImage: { show: true, name: '合同金额排行' } // 保存为图片
+    }
+  },
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    },
+    valueFormatter: fenToYuan
+  },
+  xAxis: {
+    type: 'value',
+    name: '合同金额(元)'
+  },
+  yAxis: {
+    type: 'category',
+    name: '签订人'
+  }
+}) as EChartsOption
+
+/** 获取合同金额排行 */
+const loadData = async () => {
+  // 1. 加载排行数据
+  loading.value = true
+  const rankingList = await RankApi.getContractPriceRank(props.queryParams)
+  // 2.1 更新 Echarts 数据
+  if (echartsOption.dataset && echartsOption.dataset['source']) {
+    echartsOption.dataset['source'] = clone(rankingList).reverse()
+  }
+  // 2.2 更新列表数据
+  list.value = rankingList
+  loading.value = false
+}
+defineExpose({ loadData })
+
+/** 初始化 */
+onMounted(() => {
+  loadData()
+})
+</script>

+ 108 - 0
src/views/crm/bi/rank/ReceivablePriceRank.vue

@@ -0,0 +1,108 @@
+<!-- 回款金额排行 -->
+<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="nickname" min-width="200" />
+      <el-table-column label="部门" align="center" prop="deptName" min-width="200" />
+      <el-table-column
+        label="回款金额(元)"
+        align="center"
+        prop="count"
+        min-width="200"
+        :formatter="fenToYuanFormat"
+      />
+    </el-table>
+  </el-card>
+</template>
+<script setup lang="ts">
+import { RankApi, BiRankRespVO } from '@/api/crm/bi/rank'
+import { EChartsOption } from 'echarts'
+import { fenToYuanFormat } from '@/utils/formatter'
+import { fenToYuan } from '@/utils'
+import { clone } from 'unocss'
+
+defineOptions({ name: 'ReceivablePriceRank' })
+const props = defineProps<{ queryParams: any }>() // 搜索参数
+
+const loading = ref(false) // 加载中
+const list = ref<BiRankRespVO[]>([]) // 列表的数据
+
+/** 柱状图配置:横向 */
+const echartsOption = reactive<EChartsOption>({
+  dataset: {
+    dimensions: ['nickname', 'count'],
+    source: []
+  },
+  grid: {
+    left: 20,
+    right: 20,
+    bottom: 20,
+    containLabel: true
+  },
+  legend: {
+    top: 50
+  },
+  series: [
+    {
+      name: '回款金额排行',
+      type: 'bar'
+    }
+  ],
+  toolbox: {
+    feature: {
+      dataZoom: {
+        yAxisIndex: false // 数据区域缩放:Y 轴不缩放
+      },
+      brush: {
+        type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
+      },
+      saveAsImage: { show: true, name: '回款金额排行' } // 保存为图片
+    }
+  },
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    },
+    valueFormatter: fenToYuan
+  },
+  xAxis: {
+    type: 'value',
+    name: '回款金额(元)'
+  },
+  yAxis: {
+    type: 'category',
+    name: '签订人',
+    nameGap: 30
+  }
+}) as EChartsOption
+
+/** 获取回款金额排行 */
+const loadData = async () => {
+  // 1. 加载排行数据
+  loading.value = true
+  const rankingList = await RankApi.getReceivablePriceRank(props.queryParams)
+  // 2.1 更新 Echarts 数据
+  if (echartsOption.dataset && echartsOption.dataset['source']) {
+    echartsOption.dataset['source'] = clone(rankingList).reverse()
+  }
+  // 2.2 更新列表数据
+  list.value = rankingList
+  loading.value = false
+}
+defineExpose({ loadData })
+
+/** 初始化 */
+onMounted(() => {
+  loadData()
+})
+</script>

+ 24 - 26
src/views/crm/bi/ranking/index.vue → src/views/crm/bi/rank/index.vue

@@ -1,3 +1,4 @@
+<!-- BI 排行版 -->
 <template>
   <ContentWrap>
     <!-- 搜索工作栏 -->
@@ -8,9 +9,9 @@
       :inline="true"
       label-width="68px"
     >
-      <el-form-item label="时间" prop="orderDate">
+      <el-form-item label="时间范围" prop="orderDate">
         <el-date-picker
-          v-model="queryParams.orderDate"
+          v-model="queryParams.times"
           :shortcuts="defaultShortcuts"
           class="!w-240px"
           end-placeholder="结束日期"
@@ -36,55 +37,52 @@
       </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 label="合同金额排行" name="contractPriceRank" lazy>
+        <ContractPriceRank :query-params="queryParams" ref="contractPriceRankRef" />
       </el-tab-pane>
       <!-- 回款金额排行 -->
-      <el-tab-pane label="回款金额排行" name="receivablesRanKing" lazy>
-        <RankingReceivablesStatistics
-          :queryParams="queryParams"
-          ref="rankingReceivablesStatisticsRef"
-        />
+      <el-tab-pane label="回款金额排行" name="receivablePriceRank" lazy>
+        <ReceivablePriceRank :query-params="queryParams" ref="receivablePriceRankRef" />
       </el-tab-pane>
     </el-tabs>
   </el-col>
 </template>
 <script lang="ts" setup>
-import RankingContractStatistics from './components/RankingContractStatistics.vue'
+import ContractPriceRank from './ContractPriceRank.vue'
+import ReceivablePriceRank from './ReceivablePriceRank.vue'
 import { defaultProps, handleTree } from '@/utils/tree'
 import * as DeptApi from '@/api/system/dept'
 import { beginOfDay, defaultShortcuts, endOfDay, formatDate } from '@/utils/formatTime'
-import RankingReceivablesStatistics from '@/views/crm/bi/ranking/components/RankingReceivablesStatistics.vue'
+import { useUserStore } from '@/store/modules/user'
 
-/** 排行榜 */
-defineOptions({ name: 'RankingStatistics' })
+defineOptions({ name: 'CrmBiRank' })
 
 const queryParams = reactive({
-  deptId: undefined,
-  //默认显示最近一周的数据
-  orderDate: [
+  deptId: useUserStore().getUser.deptId,
+  times: [
+    // 默认显示最近一周的数据
     formatDate(beginOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7))),
     formatDate(endOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24)))
-  ],
-  startTime: undefined,
-  endTime: undefined
+  ]
 })
 
 const queryFormRef = ref() // 搜索的表单
 const deptList = ref<Tree[]>([]) // 树形结构
-const activeTab = ref('contractAmountRanking')
-const rankingContractStatisticsRef = ref() // RankingContractStatistics组件的引用
-const rankingReceivablesStatisticsRef = ref() // RankingReceivablesStatistics组件的引用
+const activeTab = ref('contractPriceRank')
+const contractPriceRankRef = ref() // ContractPriceRank 组件的引用
+const receivablePriceRankRef = ref() // ReceivablePriceRank 组件的引用
 
 /** 搜索按钮操作 */
 const handleQuery = () => {
-  if (activeTab.value === 'contractAmountRanking') {
-    rankingContractStatisticsRef.value.reloadData()
-  } else if (activeTab.value === 'receivablesRanKing') {
-    rankingReceivablesStatisticsRef.value.reloadData()
+  if (activeTab.value === 'contractPriceRank') {
+    contractPriceRankRef.value.loadData()
+  } else if (activeTab.value === 'receivablePriceRank') {
+    receivablePriceRankRef.value.loadData()
   }
 }
 

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

@@ -1,143 +0,0 @@
-<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'
-import { beginOfDay, endOfDay, formatDate } from '@/utils/formatTime'
-
-/** 合同金额排行 */
-defineOptions({ name: 'RankingContractStatistics' })
-
-const params = reactive({
-  deptId: undefined,
-  startTime: undefined,
-  endTime: undefined
-})
-
-const trendLoading = ref(true) // 状态加载中
-const loading = ref(false) // 列表的加载中
-const list = ref<BiContractRanKingRespVO[]>([]) // 列表的数据
-const queryParams = 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
-  params.deptId = queryParams.queryParams.deptId
-  params.startTime = formatDate(beginOfDay(new Date(queryParams.queryParams.orderDate[0])))
-  params.endTime = formatDate(endOfDay(new Date(queryParams.queryParams.orderDate[1])))
-  const rankingList = await RankingStatisticsApi.contractAmountRanking(params)
-  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>

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

@@ -1,143 +0,0 @@
-<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, BiRankReqVO } from '@/api/crm/bi/ranking'
-import { EChartsOption } from 'echarts'
-import { beginOfDay, endOfDay, formatDate } from '@/utils/formatTime'
-
-/** 回款金额排行 */
-defineOptions({ name: 'RankingReceivablesStatistics' })
-
-const params = reactive({
-  deptId: undefined,
-  startTime: undefined,
-  endTime: undefined
-})
-
-const trendLoading = ref(true) // 状态加载中
-const loading = ref(false) // 列表的加载中
-const list = ref<BiReceivablesRanKingRespVO[]>([]) // 列表的数据
-const queryParams = 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 getRankingReceivablesStatistics = async () => {
-  trendLoading.value = true
-  loading.value = true
-  params.deptId = queryParams.queryParams.deptId
-  params.startTime = formatDate(beginOfDay(new Date(queryParams.queryParams.orderDate[0])))
-  params.endTime = formatDate(endOfDay(new Date(queryParams.queryParams.orderDate[1])))
-  const rankingList = await RankingStatisticsApi.receivablesAmountRanking(params)
-  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>