Files
klp-oa/klp-ui/src/views/crm/report/index.vue
砂糖 1263621e2d refactor(crm/report): 重构销售报表页面为组件化结构
将原销售报表页面拆分为多个独立组件,包括销售汇总卡片、销售员图表、行业图表、客户等级图表和订单明细组件
优化代码结构,提升可维护性和复用性
2025-12-29 17:21:32 +08:00

296 lines
8.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="crm-sales-report-page">
<!-- 时间筛选区域根页面保留统一管理查询参数 -->
<div class="date-filter-container">
<el-form :model="dateQuery" inline class="date-form">
<el-form-item label="统计时间" prop="dateRange">
<el-date-picker
v-model="dateQuery.dateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
clearable
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="queryAllData">查询</el-button>
<el-button icon="el-icon-refresh" @click="resetDateRange">重置</el-button>
</el-form-item>
</el-form>
</div>
<!-- 1. 销售汇总指标卡组件 -->
<SalesReportSummaryCard
:summary-data="summaryData"
:loading="summaryLoading"
:format-amount="formatAmount"
/>
<!-- 2. 三个ECharts图表组件布局由根页面控制 -->
<div class="echarts-container">
<el-row :gutter="20">
<!-- 销售员统计图表 -->
<el-col :span="8">
<SalesmanChart :salesman-stat-list="salesmanStatList" />
</el-col>
<!-- 客户等级统计图表 -->
<el-col :span="8">
<CustomerLevelChart
:customer-level-stat-list="customerLevelStatList"
:customer-level-dict="dict.type.customer_level"
/>
</el-col>
<!-- 行业统计图表 -->
<el-col :span="8">
<IndustryChart :industry-stat-list="industryStatList" />
</el-col>
</el-row>
</div>
<!-- 3. 订单明细组件 -->
<SalesReportOrderDetail
:order-detail-list="orderDetailList"
:page-params="pageParams"
:total="total"
:loading="orderLoading"
:format-amount="formatAmount"
:format-date="formatDate"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@export-order="exportOrderDetails"
@selection-change="handleSelectionChange"
/>
</div>
</template>
<script>
// 导入子组件
import SalesReportSummaryCard from "./SalesReportSummaryCard.vue";
import SalesmanChart from "./SalesmanChart.vue";
import CustomerLevelChart from "./CustomerLevelChart.vue";
import IndustryChart from "./IndustryChart.vue";
import SalesReportOrderDetail from "./SalesReportOrderDetail.vue";
// 导入API函数
import {
getSummary,
getOrderDetails,
getSalesmanStats,
getCustomerLevelStats,
getIndustryStats,
exportOrderDetails as apiExportOrderDetails
} from "@/api/crm/report";
export default {
name: "CrmSalesReport",
dicts: ['customer_level'],
components: {
SalesReportSummaryCard,
SalesmanChart,
CustomerLevelChart,
IndustryChart,
SalesReportOrderDetail
},
data() {
return {
// 时间查询参数
dateQuery: {
dateRange: []
},
// 分页参数
pageParams: {
pageNum: 1,
pageSize: 10
},
// 汇总数据
summaryData: {},
// 订单明细数据
orderDetailList: [],
// 总条数
total: 0,
// 图表数据(传递给对应图表组件)
salesmanStatList: [],
customerLevelStatList: [],
industryStatList: [],
// 加载状态
summaryLoading: false,
orderLoading: false,
// 表格勾选数据
multipleSelection: []
};
},
created() {
// 初始化默认时间
this.initDefaultDateRange();
// 页面加载查询所有数据
this.queryAllData();
},
methods: {
// 初始化默认时间范围:当月第一天到今天
initDefaultDateRange() {
const now = new Date();
const monthFirstDay = new Date(now.getFullYear(), now.getMonth(), 1);
const firstDayStr = this.formatToDateStr(monthFirstDay);
const todayStr = this.formatToDateStr(now);
this.dateQuery.dateRange = [firstDayStr, todayStr];
},
// 日期对象格式化为yyyy-MM-dd字符串
formatToDateStr(date) {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const day = date.getDate().toString().padStart(2, "0");
return `${year}-${month}-${day}`;
},
// 格式化金额
formatAmount(amount) {
if (!amount) {
return "0.00";
}
return Number(amount).toFixed(2);
},
// 格式化日期
formatDate(timestamp) {
if (!timestamp) return "-";
return timestamp;
},
// 重置日期范围
resetDateRange() {
this.initDefaultDateRange();
this.queryAllData();
},
// 统一查询所有数据
queryAllData() {
this.loadSummaryData();
this.loadChartData();
this.loadOrderDetailData();
},
// 组装公共查询参数
getCommonParams(needPage = false) {
const params = {};
if (this.dateQuery.dateRange && this.dateQuery.dateRange.length === 2) {
params.startTime = this.dateQuery.dateRange[0];
params.endTime = this.dateQuery.dateRange[1];
}
if (needPage) {
params.pageNum = this.pageParams.pageNum;
params.pageSize = this.pageParams.pageSize;
}
return params;
},
// 加载销售汇总数据
loadSummaryData() {
this.summaryLoading = true;
const params = this.getCommonParams();
getSummary(params)
.then((res) => {
if (res.code === 200) {
this.summaryData = res.data || {};
}
})
.finally(() => {
this.summaryLoading = false;
});
},
// 加载图表数据
loadChartData() {
const params = this.getCommonParams();
// 并行加载三个图表数据
Promise.all([
getSalesmanStats(params),
getCustomerLevelStats(params),
getIndustryStats(params)
])
.then(([salesmanRes, customerRes, industryRes]) => {
if (salesmanRes.code === 200) {
this.salesmanStatList = salesmanRes.data || [];
}
if (customerRes.code === 200) {
this.customerLevelStatList = customerRes.data || [];
}
if (industryRes.code === 200) {
this.industryStatList = industryRes.data || [];
}
});
},
// 加载订单明细数据
loadOrderDetailData() {
this.orderLoading = true;
const params = this.getCommonParams(true);
getOrderDetails(params)
.then((res) => {
if (res.code === 200) {
this.orderDetailList = res.rows || [];
this.total = res.total || 0;
}
})
.finally(() => {
this.orderLoading = false;
});
},
// 分页大小改变
handleSizeChange(val) {
this.pageParams.pageSize = val;
this.loadOrderDetailData();
},
// 当前页改变
handleCurrentChange(val) {
this.pageParams.pageNum = val;
this.loadOrderDetailData();
},
// 表格勾选事件
handleSelectionChange(val) {
this.multipleSelection = val;
},
// 导出订单明细
exportOrderDetails() {
const params = this.getCommonParams();
apiExportOrderDetails(params).then((res) => {
this.handleExportBlob(res, "销售报表订单明细.xlsx");
});
},
// 处理blob文件导出
handleExportBlob(res, fileName) {
const blob = new Blob([res], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
this.$message.success("导出成功!");
}
}
};
</script>
<style scoped>
.crm-sales-report-page {
padding: 20px;
background-color: #f5f7fa;
min-height: calc(100vh - 120px);
}
/* 时间筛选区域样式 */
.date-filter-container {
background-color: #fff;
padding: 20px;
border-radius: 4px;
margin-bottom: 20px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.04);
}
.date-form {
display: flex;
align-items: center;
}
/* 图表容器布局样式 */
.echarts-container {
margin-bottom: 20px;
}
</style>