36 分钟以前 46b2ae1c889e181ebe186de3758fc09804696029
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
<script lang="ts" setup>
import type { CrmReportApi } from '#/api/crm/report';
 
import { computed, onMounted, ref } from 'vue';
 
import { Page } from '@vben/common-ui';
 
import { Card, Col, DatePicker, Row, Statistic, Table } from 'ant-design-vue';
import dayjs from 'dayjs';
 
import {
  getBusinessAnalyze,
  getContractAnalyze,
  getCustomerAnalyze,
  getSalesFunnel,
  getSalesPerformance,
} from '#/api/crm/report';
 
defineOptions({ name: 'CrmReportDashboard' });
 
const loading = ref(false);
const dateRange = ref<[string, string] | null>(null);
 
const customerAnalyze = ref<CrmReportApi.CustomerAnalyze>();
const businessAnalyze = ref<CrmReportApi.BusinessAnalyze>();
const contractAnalyze = ref<CrmReportApi.ContractAnalyze>();
const salesFunnel = ref<CrmReportApi.SalesFunnel>();
const salesPerformance = ref<CrmReportApi.SalesPerformance>();
 
const startTime = computed(() =>
  dateRange.value?.[0] ? dayjs(dateRange.value[0]).format('YYYY-MM-DD') : undefined,
);
const endTime = computed(() =>
  dateRange.value?.[1] ? dayjs(dateRange.value[1]).format('YYYY-MM-DD') : undefined,
);
 
const performanceColumns = [
  { title: '姓名', dataIndex: 'userName', width: 120 },
  { title: '部门', dataIndex: 'deptName', width: 150 },
  { title: '合同数量', dataIndex: 'contractCount', width: 100 },
  { title: '合同金额', dataIndex: 'contractPrice', width: 120, formatter: 'formatAmount2' },
  { title: '回款金额', dataIndex: 'receivablePrice', width: 120, formatter: 'formatAmount2' },
  { title: '商机数量', dataIndex: 'businessCount', width: 100 },
  { title: '商机金额', dataIndex: 'businessPrice', width: 120, formatter: 'formatAmount2' },
];
 
async function loadData() {
  loading.value = true;
  try {
    const [customer, business, contract, funnel, performance] = await Promise.all([
      getCustomerAnalyze(startTime.value, endTime.value),
      getBusinessAnalyze(startTime.value, endTime.value),
      getContractAnalyze(startTime.value, endTime.value),
      getSalesFunnel(),
      getSalesPerformance(startTime.value, endTime.value),
    ]);
    customerAnalyze.value = customer;
    businessAnalyze.value = business;
    contractAnalyze.value = contract;
    salesFunnel.value = funnel;
    salesPerformance.value = performance;
  } finally {
    loading.value = false;
  }
}
 
function handleDateChange() {
  loadData();
}
 
onMounted(() => {
  loadData();
});
</script>
 
<template>
  <Page auto-content-height>
    <div class="p-4">
      <Card title="CRM 数据概览" class="mb-4">
        <div class="mb-4">
          <DatePicker.RangePicker
            v-model:value="dateRange"
            :placeholder="['开始日期', '结束日期']"
            format="YYYY-MM-DD"
            @change="handleDateChange"
          />
        </div>
      </Card>
 
      <Row :gutter="16" class="mb-4">
        <Col :span="6">
          <Card title="客户分析" :loading="loading">
            <Statistic title="总客户数" :value="customerAnalyze?.totalCount || 0" />
            <Statistic title="今日新增" :value="customerAnalyze?.todayCount || 0" class="mt-2" />
            <Statistic title="成交客户" :value="customerAnalyze?.dealCount || 0" class="mt-2" />
            <Statistic
              title="成交率"
              :value="customerAnalyze?.dealRate || 0"
              :precision="2"
              suffix="%"
              class="mt-2"
            />
          </Card>
        </Col>
        <Col :span="6">
          <Card title="商机分析" :loading="loading">
            <Statistic title="总商机数" :value="businessAnalyze?.totalCount || 0" />
            <Statistic title="商机金额" :value="businessAnalyze?.totalPrice || 0" :precision="2" class="mt-2" />
            <Statistic title="成功商机" :value="businessAnalyze?.successCount || 0" class="mt-2" />
            <Statistic
              title="成功率"
              :value="businessAnalyze?.successRate || 0"
              :precision="2"
              suffix="%"
              class="mt-2"
            />
          </Card>
        </Col>
        <Col :span="6">
          <Card title="合同分析" :loading="loading">
            <Statistic title="总合同数" :value="contractAnalyze?.totalCount || 0" />
            <Statistic title="合同金额" :value="contractAnalyze?.totalPrice || 0" :precision="2" class="mt-2" />
            <Statistic title="审批中" :value="contractAnalyze?.processCount || 0" class="mt-2" />
            <Statistic
              title="回款率"
              :value="contractAnalyze?.receivableRate || 0"
              :precision="2"
              suffix="%"
              class="mt-2"
            />
          </Card>
        </Col>
        <Col :span="6">
          <Card title="待处理" :loading="loading">
            <Statistic title="客户公海" :value="customerAnalyze?.poolCount || 0" />
            <Statistic title="锁定客户" :value="customerAnalyze?.lockCount || 0" class="mt-2" />
            <Statistic title="进行中商机" :value="businessAnalyze?.processingCount || 0" class="mt-2" />
            <Statistic title="失败商机" :value="businessAnalyze?.failCount || 0" class="mt-2" />
          </Card>
        </Col>
      </Row>
 
      <Row :gutter="16">
        <Col :span="12">
          <Card title="销售漏斗" :loading="loading">
            <Table
              :dataSource="salesFunnel?.stages || []"
              :columns="[
                { title: '阶段', dataIndex: 'name', width: 150 },
                { title: '数量', dataIndex: 'count', width: 100 },
                { title: '金额', dataIndex: 'price', width: 120 },
                { title: '转化率', dataIndex: 'conversionRate', width: 100, customRender: ({ text }) => `${text}%` },
              ]"
              :pagination="false"
              size="small"
              rowKey="name"
            />
          </Card>
        </Col>
        <Col :span="12">
          <Card title="销售业绩" :loading="loading">
            <Table
              :dataSource="salesPerformance?.items || []"
              :columns="performanceColumns"
              :pagination="false"
              size="small"
              rowKey="userId"
            />
          </Card>
        </Col>
      </Row>
    </div>
  </Page>
</template>