BusinessSummary.vue 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <!-- 客户总量统计 -->
  2. <template>
  3. <!-- Echarts图 -->
  4. <el-card shadow="never">
  5. <el-skeleton :loading="loading" animated>
  6. <Echart :height="500" :options="echartsOption" />
  7. </el-skeleton>
  8. </el-card>
  9. <!-- 统计列表 -->
  10. <el-card class="mt-16px" shadow="never">
  11. <el-table v-loading="loading" :data="list">
  12. <el-table-column align="center" fixed="left" label="序号" type="index" width="80" />
  13. <el-table-column align="center" fixed="left" label="商机名称" prop="name" width="160">
  14. <template #default="scope">
  15. <el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
  16. {{ scope.row.name }}
  17. </el-link>
  18. </template>
  19. </el-table-column>
  20. <el-table-column align="center" fixed="left" label="客户名称" prop="customerName" width="120">
  21. <template #default="scope">
  22. <el-link
  23. :underline="false"
  24. type="primary"
  25. @click="openCustomerDetail(scope.row.customerId)"
  26. >
  27. {{ scope.row.customerName }}
  28. </el-link>
  29. </template>
  30. </el-table-column>
  31. <el-table-column
  32. :formatter="erpPriceTableColumnFormatter"
  33. align="center"
  34. label="商机金额(元)"
  35. prop="totalPrice"
  36. width="140"
  37. />
  38. <el-table-column
  39. :formatter="dateFormatter"
  40. align="center"
  41. label="预计成交日期"
  42. prop="dealTime"
  43. width="180px"
  44. />
  45. <el-table-column align="center" label="备注" prop="remark" width="200" />
  46. <el-table-column
  47. :formatter="dateFormatter"
  48. align="center"
  49. label="下次联系时间"
  50. prop="contactNextTime"
  51. width="180px"
  52. />
  53. <el-table-column align="center" label="负责人" prop="ownerUserName" width="100px" />
  54. <el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
  55. <el-table-column
  56. :formatter="dateFormatter"
  57. align="center"
  58. label="最后跟进时间"
  59. prop="contactLastTime"
  60. width="180px"
  61. />
  62. <el-table-column
  63. :formatter="dateFormatter"
  64. align="center"
  65. label="更新时间"
  66. prop="updateTime"
  67. width="180px"
  68. />
  69. <el-table-column
  70. :formatter="dateFormatter"
  71. align="center"
  72. label="创建时间"
  73. prop="createTime"
  74. width="180px"
  75. />
  76. <el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
  77. <el-table-column
  78. align="center"
  79. fixed="right"
  80. label="商机状态组"
  81. prop="statusTypeName"
  82. width="140"
  83. />
  84. <el-table-column
  85. align="center"
  86. fixed="right"
  87. label="商机阶段"
  88. prop="statusName"
  89. width="120"
  90. />
  91. </el-table>
  92. <!-- 分页 -->
  93. <Pagination
  94. v-model:limit="queryParams0.pageSize"
  95. v-model:page="queryParams0.pageNo"
  96. :total="total"
  97. @pagination="getList"
  98. />
  99. </el-card>
  100. </template>
  101. <script lang="ts" setup>
  102. import {
  103. CrmStatisticsBusinessSummaryByDateRespVO,
  104. StatisticFunnelApi
  105. } from '@/api/crm/statistics/funnel'
  106. import { EChartsOption } from 'echarts'
  107. import { erpPriceTableColumnFormatter } from '@/utils'
  108. import { dateFormatter } from '@/utils/formatTime'
  109. defineOptions({ name: 'BusinessSummary' })
  110. const props = defineProps<{ queryParams: any }>() // 搜索参数
  111. const queryParams0 = reactive({
  112. pageNo: 1,
  113. pageSize: 10
  114. })
  115. const loading = ref(false) // 加载中
  116. const list = ref([]) // 列表的数据
  117. const total = ref(0)
  118. /** 将传进来的值赋值给 queryParams0 */
  119. watch(
  120. () => props.queryParams,
  121. (data) => {
  122. if (!data) {
  123. return
  124. }
  125. const newObj = { ...queryParams0, ...data }
  126. Object.assign(queryParams0, newObj)
  127. },
  128. {
  129. immediate: true
  130. }
  131. )
  132. /** 柱状图配置:纵向 */
  133. const echartsOption = reactive<EChartsOption>({
  134. grid: {
  135. left: 30,
  136. right: 30, // 让 X 轴右侧显示完整
  137. bottom: 20,
  138. containLabel: true
  139. },
  140. legend: {},
  141. series: [
  142. {
  143. name: '新增商机数量',
  144. type: 'bar',
  145. yAxisIndex: 0,
  146. data: []
  147. },
  148. {
  149. name: '新增商机金额',
  150. type: 'bar',
  151. yAxisIndex: 1,
  152. data: []
  153. }
  154. ],
  155. toolbox: {
  156. feature: {
  157. dataZoom: {
  158. xAxisIndex: false // 数据区域缩放:Y 轴不缩放
  159. },
  160. brush: {
  161. type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
  162. },
  163. saveAsImage: { show: true, name: '新增商机分析' } // 保存为图片
  164. }
  165. },
  166. tooltip: {
  167. trigger: 'axis',
  168. axisPointer: {
  169. type: 'shadow'
  170. }
  171. },
  172. yAxis: [
  173. {
  174. type: 'value',
  175. name: '新增商机数量',
  176. min: 0,
  177. minInterval: 1 // 显示整数刻度
  178. },
  179. {
  180. type: 'value',
  181. name: '新增商机金额',
  182. min: 0,
  183. minInterval: 1, // 显示整数刻度
  184. splitLine: {
  185. lineStyle: {
  186. type: 'dotted', // 右侧网格线虚化, 减少混乱
  187. opacity: 0.7
  188. }
  189. }
  190. }
  191. ],
  192. xAxis: {
  193. type: 'category',
  194. name: '日期',
  195. data: []
  196. }
  197. }) as EChartsOption
  198. /** 获取数据并填充图表 */
  199. const fetchAndFill = async () => {
  200. // 1. 加载统计数据
  201. const businessSummaryByDate = await StatisticFunnelApi.getBusinessSummaryByDate(props.queryParams)
  202. // 2.1 更新 Echarts 数据
  203. if (echartsOption.xAxis && echartsOption.xAxis['data']) {
  204. echartsOption.xAxis['data'] = businessSummaryByDate.map(
  205. (s: CrmStatisticsBusinessSummaryByDateRespVO) => s.time
  206. )
  207. }
  208. if (echartsOption.series && echartsOption.series[0] && echartsOption.series[0]['data']) {
  209. echartsOption.series[0]['data'] = businessSummaryByDate.map(
  210. (s: CrmStatisticsBusinessSummaryByDateRespVO) => s.businessCreateCount
  211. )
  212. }
  213. if (echartsOption.series && echartsOption.series[1] && echartsOption.series[1]['data']) {
  214. echartsOption.series[1]['data'] = businessSummaryByDate.map(
  215. (s: CrmStatisticsBusinessSummaryByDateRespVO) => s.totalPrice
  216. )
  217. }
  218. // 2.2 更新列表数据
  219. await getList()
  220. }
  221. /** 获取商机列表 */
  222. const getList = async () => {
  223. const data = await StatisticFunnelApi.getBusinessPageByDate(props.queryParams)
  224. list.value = data.list
  225. total.value = data.total
  226. }
  227. /** 打开客户详情 */
  228. const { push } = useRouter()
  229. const openDetail = (id: number) => {
  230. push({ name: 'CrmBusinessDetail', params: { id } })
  231. }
  232. /** 打开客户详情 */
  233. const openCustomerDetail = (id: number) => {
  234. push({ name: 'CrmCustomerDetail', params: { id } })
  235. }
  236. /** 获取统计数据 */
  237. const loadData = async () => {
  238. loading.value = true
  239. try {
  240. await fetchAndFill()
  241. } finally {
  242. loading.value = false
  243. }
  244. }
  245. defineExpose({ loadData })
  246. /** 初始化 */
  247. onMounted(() => {
  248. loadData()
  249. })
  250. </script>