feat(wms): 新增合同挂接钢卷统计功能

- 在IWmsCoilContractRelService接口中添加queryContractCoilStatistics方法
- 在WmsCoilContractRelController控制器中添加统计查询和导出接口
- 在WmsCoilContractRelMapper中添加selectContractCoilStatistics查询方法
- 实现合同挂接钢卷统计的SQL查询逻辑,支持多条件筛选
- 创建ContractCoilStatisticsBo查询对象和ContractCoilStatisticsVo视图对象
- 开发前端统计页面,包含搜索筛选、统计卡片、图表展示和数据表格
- 集成ECharts实现合同状态分布饼图和销售员挂接钢卷柱状图
- 实现统计数据的Excel导出功能
- 添加响应式设计适配移动端显示
This commit is contained in:
2026-06-30 15:31:35 +08:00
parent 524f8f3333
commit 64d52f1ecf
9 changed files with 837 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
import request from '@/utils/request'
// 查询合同挂接情况统计
export function getContractCoilStatistics(query) {
return request({
url: '/wms/coilContractRel/statistics',
method: 'get',
params: query
})
}

View File

@@ -0,0 +1,613 @@
<template>
<div class="statistics-container" v-loading="loading">
<!-- 搜索筛选区 -->
<div class="filter-panel">
<el-form :model="queryParams" ref="queryForm" :inline="true" size="small">
<el-form-item label="合同号" prop="contractCode">
<el-input v-model="queryParams.contractCode" placeholder="请输入合同号" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="合同名称" prop="contractName">
<el-input v-model="queryParams.contractName" placeholder="请输入合同名称" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="订单编号" prop="orderCode">
<el-input v-model="queryParams.orderCode" placeholder="请输入订单编号" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="需方" prop="customer">
<el-input v-model="queryParams.customer" placeholder="请输入需方" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="销售员" prop="salesman">
<el-input v-model="queryParams.salesman" placeholder="请输入销售员" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="合同状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择" clearable style="width:120px">
<el-option label="草稿" :value="0" />
<el-option label="生效" :value="1" />
<el-option label="作废" :value="2" />
<el-option label="已完成" :value="3" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
<el-button type="warning" icon="el-icon-download" @click="handleExport">导出</el-button>
</el-form-item>
</el-form>
</div>
<!-- 四个统计卡片 -->
<el-row :gutter="16" class="stat-cards-row">
<el-col :xs="24" :sm="12" :md="6">
<div class="stat-card card-blue">
<div class="card-inner">
<div class="card-icon"><i class="el-icon-document"></i></div>
<div class="card-info">
<div class="card-value">{{ summary.totalContracts }}</div>
<div class="card-label">合同总数</div>
</div>
</div>
<div class="card-footer">
<span>全部合同</span>
<i class="el-icon-arrow-right"></i>
</div>
</div>
</el-col>
<el-col :xs="24" :sm="12" :md="6">
<div class="stat-card card-green">
<div class="card-inner">
<div class="card-icon"><i class="el-icon-link"></i></div>
<div class="card-info">
<div class="card-value">{{ summary.attachedContracts }}</div>
<div class="card-label">已挂接合同</div>
</div>
</div>
<div class="card-footer">
<span>占比 {{ attachmentRate }}%</span>
<i class="el-icon-arrow-right"></i>
</div>
</div>
</el-col>
<el-col :xs="24" :sm="12" :md="6">
<div class="stat-card card-orange">
<div class="card-inner">
<div class="card-icon"><i class="el-icon-warning-outline"></i></div>
<div class="card-info">
<div class="card-value">{{ summary.unattachedContracts }}</div>
<div class="card-label">未挂接合同</div>
</div>
</div>
<div class="card-footer">
<span>需关注</span>
<i class="el-icon-arrow-right"></i>
</div>
</div>
</el-col>
<el-col :xs="24" :sm="12" :md="6">
<div class="stat-card card-red">
<div class="card-inner">
<div class="card-icon"><i class="el-icon-s-data"></i></div>
<div class="card-info">
<div class="card-value">{{ summary.totalCoils }}</div>
<div class="card-label">挂接钢卷总数</div>
</div>
</div>
<div class="card-footer">
<span>总净重 {{ summary.totalWeight }}t</span>
<i class="el-icon-arrow-right"></i>
</div>
</div>
</el-col>
</el-row>
<!-- 图表区域 -->
<el-row :gutter="16" class="charts-row">
<el-col :xs="24" :md="12">
<div class="chart-card">
<div class="chart-header">
<span class="chart-title"><i class="el-icon-pie-chart"></i> 合同状态分布</span>
</div>
<div ref="statusPieChart" class="chart-box"></div>
</div>
</el-col>
<el-col :xs="24" :md="12">
<div class="chart-card">
<div class="chart-header">
<span class="chart-title"><i class="el-icon-s-marketing"></i> 销售员挂接钢卷TOP10</span>
</div>
<div ref="salesmanBarChart" class="chart-box"></div>
</div>
</el-col>
</el-row>
<el-row :gutter="16" class="charts-row">
<el-col :span="24">
<div class="chart-card">
<div class="chart-header">
<span class="chart-title"><i class="el-icon-data-line"></i> 合同挂接钢卷数排行 TOP10</span>
</div>
<div ref="topContractChart" class="chart-box chart-box-bar"></div>
</div>
</el-col>
</el-row>
<!-- 数据表格 -->
<div class="table-card">
<div class="table-header">
<span class="table-title"><i class="el-icon-tickets"></i> 详细数据</span>
<right-toolbar :showSearch.sync="showSearch" style="float:right"></right-toolbar>
</div>
<el-table v-loading="tableLoading" :data="tableData" border style="width: 100%"
:row-class-name="tableRowClassName">
<el-table-column type="index" label="序号" width="55" align="center" />
<el-table-column label="合同号" align="center" prop="contractCode" min-width="140" show-overflow-tooltip />
<el-table-column label="合同名称" align="center" prop="contractName" min-width="160" show-overflow-tooltip />
<el-table-column label="订单编号" align="center" prop="orderCode" min-width="140" show-overflow-tooltip />
<el-table-column label="需方" align="center" prop="customer" min-width="120" show-overflow-tooltip />
<el-table-column label="销售员" align="center" prop="salesman" width="100" />
<el-table-column label="合同状态" align="center" prop="status" width="90">
<template slot-scope="scope">
<el-tag v-if="scope.row.status === 0" type="info" size="small">草稿</el-tag>
<el-tag v-else-if="scope.row.status === 1" type="success" size="small">生效</el-tag>
<el-tag v-else-if="scope.row.status === 2" type="danger" size="small">作废</el-tag>
<el-tag v-else-if="scope.row.status === 3" type="warning" size="small">已完成</el-tag>
<span v-else>{{ scope.row.status }}</span>
</template>
</el-table-column>
<el-table-column label="签订时间" align="center" prop="signTime" width="110">
<template slot-scope="scope">
<span>{{ scope.row.signTime ? parseTime(scope.row.signTime, '{y}-{m}-{d}') : '-' }}</span>
</template>
</el-table-column>
<el-table-column label="订单总额" align="center" prop="orderAmount" width="120">
<template slot-scope="scope">
<span>{{ scope.row.orderAmount ? formatMoney(scope.row.orderAmount) : '-' }}</span>
</template>
</el-table-column>
<el-table-column label="挂接钢卷数" align="center" prop="coilCount" width="110" sortable>
<template slot-scope="scope">
<el-tag :type="scope.row.coilCount > 0 ? 'success' : 'info'" size="small">
{{ scope.row.coilCount }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="总净重(t)" align="center" width="120">
<template slot-scope="scope">
<span>{{ scope.row.totalNetWeight != null ? (Number(scope.row.totalNetWeight) / 1000).toFixed(2) : '-' }}</span>
</template>
</el-table-column>
<el-table-column label="总毛重(t)" align="center" width="120">
<template slot-scope="scope">
<span>{{ scope.row.totalGrossWeight != null ? (Number(scope.row.totalGrossWeight) / 1000).toFixed(2) : '-' }}</span>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="handlePageChange" />
</div>
</div>
</template>
<script>
import { getContractCoilStatistics } from "@/api/wms/contractCoilStatistics";
import * as echarts from 'echarts';
export default {
name: "ContractCoilStatistics",
data() {
return {
loading: false,
tableLoading: false,
showSearch: true,
total: 0,
tableData: [],
allData: [],
summary: {
totalContracts: 0,
attachedContracts: 0,
unattachedContracts: 0,
totalCoils: 0,
totalWeight: 0
},
queryParams: {
pageNum: 1,
pageSize: 20,
contractCode: undefined,
contractName: undefined,
orderCode: undefined,
customer: undefined,
salesman: undefined,
status: undefined
},
// ECharts 实例
statusPieChartInst: null,
salesmanBarChartInst: null,
topContractChartInst: null
};
},
computed: {
attachmentRate() {
if (this.summary.totalContracts === 0) return 0;
return ((this.summary.attachedContracts / this.summary.totalContracts) * 100).toFixed(1);
}
},
created() {
this.getList();
},
mounted() {
this.$nextTick(() => {
this.initCharts();
});
},
beforeDestroy() {
this.disposeCharts();
},
methods: {
/** 查询数据 */
getList() {
this.loading = true;
this.tableLoading = true;
getContractCoilStatistics(this.queryParams).then(response => {
const list = response.data || [];
this.allData = list;
this.total = list.length;
// 汇总
this.summary.totalContracts = list.length;
this.summary.attachedContracts = list.filter(item => item.coilCount > 0).length;
this.summary.unattachedContracts = list.filter(item => item.coilCount === 0).length;
this.summary.totalCoils = list.reduce((sum, item) => sum + (Number(item.coilCount) || 0), 0);
const totalNetKg = list.reduce((sum, item) => sum + (Number(item.totalNetWeight) || 0), 0);
this.summary.totalWeight = (totalNetKg / 1000).toFixed(2);
// 切片
this.sliceTableData();
this.loading = false;
this.tableLoading = false;
// 渲染图表
this.$nextTick(() => {
this.renderCharts();
});
}).catch(() => {
this.loading = false;
this.tableLoading = false;
});
},
sliceTableData() {
const { pageNum, pageSize } = this.queryParams;
const start = (pageNum - 1) * pageSize;
this.tableData = this.allData.slice(start, start + pageSize);
},
handlePageChange() {
this.sliceTableData();
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
handleExport() {
this.download('wms/coilContractRel/statistics/export', {
...this.queryParams
}, `合同挂接情况统计_${new Date().getTime()}.xlsx`)
},
formatMoney(val) {
if (!val) return '-';
return (val / 10000).toFixed(2) + '万';
},
tableRowClassName({ row }) {
if (row.coilCount === 0) return 'row-unattached';
if (row.coilCount >= 10) return 'row-high';
return '';
},
// ========== ECharts 图表 ==========
initCharts() {
if (this.$refs.statusPieChart) {
this.statusPieChartInst = echarts.init(this.$refs.statusPieChart);
}
if (this.$refs.salesmanBarChart) {
this.salesmanBarChartInst = echarts.init(this.$refs.salesmanBarChart);
}
if (this.$refs.topContractChart) {
this.topContractChartInst = echarts.init(this.$refs.topContractChart);
}
window.addEventListener('resize', this.resizeCharts);
this.renderCharts();
},
disposeCharts() {
window.removeEventListener('resize', this.resizeCharts);
if (this.statusPieChartInst) { this.statusPieChartInst.dispose(); }
if (this.salesmanBarChartInst) { this.salesmanBarChartInst.dispose(); }
if (this.topContractChartInst) { this.topContractChartInst.dispose(); }
},
resizeCharts() {
if (this.statusPieChartInst) this.statusPieChartInst.resize();
if (this.salesmanBarChartInst) this.salesmanBarChartInst.resize();
if (this.topContractChartInst) this.topContractChartInst.resize();
},
renderCharts() {
this.renderStatusPie();
this.renderSalesmanBar();
this.renderTopContractBar();
},
/** 合同状态分布饼图 */
renderStatusPie() {
if (!this.statusPieChartInst || !this.allData.length) return;
const statusMap = { 0: '草稿', 1: '生效', 2: '作废', 3: '已完成' };
const statusColorMap = { 0: '#909399', 1: '#67c23a', 2: '#f56c6c', 3: '#e6a23c' };
const countMap = {};
this.allData.forEach(item => {
const k = item.status != null ? item.status : -1;
countMap[k] = (countMap[k] || 0) + 1;
});
const data = Object.keys(countMap).map(k => ({
name: statusMap[k] || ('未知(' + k + ')'),
value: countMap[k],
itemStyle: { color: statusColorMap[k] || '#c0c4cc' }
}));
this.statusPieChartInst.setOption({
tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
legend: { bottom: 0, textStyle: { color: '#606266' } },
series: [{
type: 'pie',
radius: ['45%', '72%'],
center: ['50%', '48%'],
avoidLabelOverlap: false,
itemStyle: { borderRadius: 4, borderColor: '#fff', borderWidth: 2 },
label: { show: true, formatter: '{b}\n{d}%' },
emphasis: {
label: { fontSize: 16, fontWeight: 'bold' },
scaleSize: 8
},
data
}]
});
},
/** 销售员挂接钢卷 TOP10 横向柱状图 */
renderSalesmanBar() {
if (!this.salesmanBarChartInst || !this.allData.length) return;
// 按销售员聚合
const map = {};
this.allData.forEach(item => {
const name = item.salesman || '未知';
map[name] = (map[name] || 0) + (item.coilCount || 0);
});
let arr = Object.entries(map).map(([name, count]) => ({ name, count }));
arr.sort((a, b) => b.count - a.count);
arr = arr.slice(0, 10);
this.salesmanBarChartInst.setOption({
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
grid: { left: '3%', right: '10%', bottom: '3%', top: '5%', containLabel: true },
xAxis: { type: 'value', name: '钢卷数' },
yAxis: {
type: 'category',
data: arr.map(i => i.name).reverse(),
axisLabel: { width: 60, overflow: 'truncate' }
},
series: [{
type: 'bar',
data: arr.map(i => ({
value: i.count,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#6dd5ed' },
{ offset: 1, color: '#2193b0' }
])
}
})).reverse(),
barWidth: 16,
itemStyle: { borderRadius: [0, 8, 8, 0] },
label: { show: true, position: 'right', color: '#303133' }
}]
});
},
/** 合同挂接钢卷排行 TOP10 */
renderTopContractBar() {
if (!this.topContractChartInst || !this.allData.length) return;
let arr = this.allData
.filter(item => item.coilCount > 0)
.sort((a, b) => b.coilCount - a.coilCount)
.slice(0, 10);
const xData = arr.map(i => i.contractCode || i.orderCode || '未知');
const yData = arr.map(i => i.coilCount);
this.topContractChartInst.setOption({
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: function(params) {
const d = arr[params[0].dataIndex];
return `<b>${d.contractCode || d.orderCode}</b><br/>
合同名称: ${d.contractName || '-'}<br/>
需方: ${d.customer || '-'}<br/>
挂接钢卷: <b>${d.coilCount}</b> 卷<br/>
总净重: ${(Number(d.totalNetWeight) / 1000).toFixed(2)}t`;
}
},
grid: { left: '3%', right: '4%', bottom: '12%', top: '8%', containLabel: true },
xAxis: {
type: 'category',
data: xData,
axisLabel: { rotate: 30, fontSize: 11, width: 80, overflow: 'truncate' }
},
yAxis: { type: 'value', name: '钢卷数' },
series: [{
type: 'bar',
data: yData.map((v, idx) => ({
value: v,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#f093fb' },
{ offset: 1, color: '#f5576c' }
])
}
})),
barWidth: 28,
itemStyle: { borderRadius: [8, 8, 0, 0] },
label: { show: true, position: 'top', color: '#303133', fontWeight: 'bold' }
}]
});
}
}
};
</script>
<style scoped>
.statistics-container {
padding: 16px;
background: #f0f2f5;
min-height: 100%;
}
/* 筛选面板 */
.filter-panel {
background: #fff;
padding: 16px 20px 4px;
border-radius: 6px;
margin-bottom: 16px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
/* 统计卡片 */
.stat-cards-row {
margin-bottom: 16px;
}
.stat-card {
background: #fff;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}
.card-inner {
display: flex;
align-items: center;
padding: 20px 20px 12px;
}
.card-icon {
width: 54px;
height: 54px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16px;
flex-shrink: 0;
}
.card-icon i {
font-size: 26px;
color: #fff;
}
.card-blue .card-icon { background: linear-gradient(135deg, #409eff, #337ecc); }
.card-green .card-icon { background: linear-gradient(135deg, #67c23a, #529b2e); }
.card-orange .card-icon { background: linear-gradient(135deg, #e6a23c, #cf8b2b); }
.card-red .card-icon { background: linear-gradient(135deg, #f56c6c, #da4a4a); }
.card-info { flex: 1; min-width: 0; }
.card-value { font-size: 30px; font-weight: 700; color: #303133; line-height: 1.2; }
.card-label { font-size: 13px; color: #909399; margin-top: 4px; }
.card-footer {
padding: 8px 20px;
background: #fafafa;
font-size: 12px;
color: #909399;
display: flex;
justify-content: space-between;
align-items: center;
}
/* 图表卡片 */
.charts-row {
margin-bottom: 16px;
}
.chart-card {
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
padding: 16px 20px;
}
.chart-header {
padding-bottom: 12px;
border-bottom: 1px solid #ebeef5;
margin-bottom: 8px;
}
.chart-title {
font-size: 15px;
font-weight: 600;
color: #303133;
}
.chart-title i {
margin-right: 4px;
color: #409eff;
}
.chart-box {
width: 100%;
height: 320px;
}
.chart-box-bar {
height: 360px;
}
/* 表格卡片 */
.table-card {
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
padding: 16px 20px;
}
.table-header {
margin-bottom: 12px;
overflow: hidden;
}
.table-title {
font-size: 15px;
font-weight: 600;
color: #303133;
line-height: 32px;
}
.table-title i {
margin-right: 4px;
color: #409eff;
}
/* 表格行样式 */
::v-deep .el-table .row-unattached {
background-color: #fef0f0 !important;
}
::v-deep .el-table .row-high {
background-color: #f0f9eb !important;
}
::v-deep .el-table .row-unattached:hover > td {
background-color: #fde2e2 !important;
}
::v-deep .el-table .row-high:hover > td {
background-color: #e1f3d8 !important;
}
/* 响应式 */
@media (max-width: 768px) {
.chart-box { height: 260px; }
.chart-box-bar { height: 300px; }
}
</style>

View File

@@ -18,7 +18,9 @@ import com.klp.common.core.validate.EditGroup;
import com.klp.common.enums.BusinessType;
import com.klp.common.utils.poi.ExcelUtil;
import com.klp.domain.vo.WmsCoilContractRelVo;
import com.klp.domain.vo.ContractCoilStatisticsVo;
import com.klp.domain.bo.WmsCoilContractRelBo;
import com.klp.domain.bo.ContractCoilStatisticsBo;
import com.klp.service.IWmsCoilContractRelService;
import com.klp.common.core.page.TableDataInfo;
@@ -97,6 +99,25 @@ public class WmsCoilContractRelController extends BaseController {
return R.ok(updated);
}
/**
* 查询合同挂接情况统计
*/
@GetMapping("/statistics")
public R<List<ContractCoilStatisticsVo>> statistics(ContractCoilStatisticsBo bo) {
List<ContractCoilStatisticsVo> list = iWmsCoilContractRelService.queryContractCoilStatistics(bo);
return R.ok(list);
}
/**
* 导出合同挂接情况统计
*/
@Log(title = "合同挂接情况统计", businessType = BusinessType.EXPORT)
@PostMapping("/statistics/export")
public void statisticsExport(ContractCoilStatisticsBo bo, HttpServletResponse response) {
List<ContractCoilStatisticsVo> list = iWmsCoilContractRelService.queryContractCoilStatistics(bo);
ExcelUtil.exportExcel(list, "合同挂接情况统计", ContractCoilStatisticsVo.class, response);
}
/**
* 删除钢卷与合同关联关系
*

View File

@@ -0,0 +1,36 @@
package com.klp.domain.bo;
import com.klp.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 合同挂接情况统计查询对象
*
* @author klp
* @date 2026-06-30
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ContractCoilStatisticsBo extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 合同号 */
private String contractCode;
/** 合同名称 */
private String contractName;
/** 需方(客户) */
private String customer;
/** 销售员 */
private String salesman;
/** 合同状态 0=草稿 1=生效 2=作废 3=已完成 */
private Long status;
/** 订单编号 */
private String orderCode;
}

View File

@@ -0,0 +1,85 @@
package com.klp.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 合同挂接情况统计视图对象
* 基于 crm_order + wms_coil_contract_rel + wms_material_coil 三表联合统计
*
* @author klp
* @date 2026-06-30
*/
@Data
@ExcelIgnoreUnannotated
public class ContractCoilStatisticsVo {
private static final long serialVersionUID = 1L;
// ========== 合同信息(来自 crm_order==========
/** 订单ID */
@ExcelProperty(value = "订单ID")
private Long orderId;
/** 订单编号 */
@ExcelProperty(value = "订单编号")
private String orderCode;
/** 合同号 */
@ExcelProperty(value = "合同号")
private String contractCode;
/** 合同名称 */
@ExcelProperty(value = "合同名称")
private String contractName;
/** 需方(客户) */
@ExcelProperty(value = "需方")
private String customer;
/** 供方 */
@ExcelProperty(value = "供方")
private String supplier;
/** 销售员 */
@ExcelProperty(value = "销售员")
private String salesman;
/** 合同状态 0=草稿 1=生效 2=作废 3=已完成 */
@ExcelProperty(value = "合同状态")
private Long status;
/** 订单总额 */
@ExcelProperty(value = "订单总额")
private BigDecimal orderAmount;
/** 签订时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "签订时间")
private Date signTime;
/** 交货日期 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "交货日期")
private Date deliveryDate;
// ========== 统计字段 ==========
/** 挂接钢卷数量 */
@ExcelProperty(value = "挂接钢卷数")
private Long coilCount;
/** 钢卷总净重kg */
@ExcelProperty(value = "钢卷总净重(kg)")
private BigDecimal totalNetWeight;
/** 钢卷总毛重kg */
@ExcelProperty(value = "钢卷总毛重(kg)")
private BigDecimal totalGrossWeight;
}

View File

@@ -2,6 +2,8 @@ package com.klp.mapper;
import com.klp.domain.WmsCoilContractRel;
import com.klp.domain.vo.WmsCoilContractRelVo;
import com.klp.domain.vo.ContractCoilStatisticsVo;
import com.klp.domain.bo.ContractCoilStatisticsBo;
import com.klp.common.core.mapper.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
@@ -34,4 +36,9 @@ public interface WmsCoilContractRelMapper extends BaseMapperPlus<WmsCoilContract
* 根据订单ID查询合同信息业务员姓名 + 合同编号)
*/
java.util.Map<String, String> selectContractInfoByOrderId(@Param("orderId") Long orderId);
/**
* 查询合同挂接钢卷统计(按合同分组统计钢卷数量和重量)
*/
List<ContractCoilStatisticsVo> selectContractCoilStatistics(@Param("bo") ContractCoilStatisticsBo bo);
}

View File

@@ -2,7 +2,9 @@ package com.klp.service;
import com.klp.domain.WmsCoilContractRel;
import com.klp.domain.vo.WmsCoilContractRelVo;
import com.klp.domain.vo.ContractCoilStatisticsVo;
import com.klp.domain.bo.WmsCoilContractRelBo;
import com.klp.domain.bo.ContractCoilStatisticsBo;
import com.klp.common.core.page.TableDataInfo;
import com.klp.common.core.domain.PageQuery;
@@ -55,4 +57,12 @@ public interface IWmsCoilContractRelService {
* @return 实际更新的记录数
*/
int batchUpdateContractId(Long contractId, List<Long> coilIds);
/**
* 查询合同挂接钢卷统计
*
* @param bo 查询参数
* @return 统计列表
*/
List<ContractCoilStatisticsVo> queryContractCoilStatistics(ContractCoilStatisticsBo bo);
}

View File

@@ -11,7 +11,9 @@ import com.klp.common.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.klp.domain.bo.WmsCoilContractRelBo;
import com.klp.domain.bo.ContractCoilStatisticsBo;
import com.klp.domain.vo.WmsCoilContractRelVo;
import com.klp.domain.vo.ContractCoilStatisticsVo;
import com.klp.domain.WmsCoilContractRel;
import com.klp.domain.WmsMaterialCoil;
import com.klp.mapper.WmsCoilContractRelMapper;
@@ -195,4 +197,12 @@ public class WmsCoilContractRelServiceImpl implements IWmsCoilContractRelService
}
return updated;
}
/**
* 查询合同挂接钢卷统计
*/
@Override
public List<ContractCoilStatisticsVo> queryContractCoilStatistics(ContractCoilStatisticsBo bo) {
return baseMapper.selectContractCoilStatistics(bo);
}
}

View File

@@ -154,4 +154,49 @@
LIMIT 1
</select>
<!-- 合同挂接钢卷统计LEFT JOIN 合同 + 关联表 + 钢卷,按合同分组统计 -->
<select id="selectContractCoilStatistics" resultType="com.klp.domain.vo.ContractCoilStatisticsVo">
SELECT
co.order_id AS orderId,
co.order_code AS orderCode,
co.contract_code AS contractCode,
co.contract_name AS contractName,
co.customer,
co.supplier,
co.salesman,
co.status,
co.order_amount AS orderAmount,
co.sign_time AS signTime,
co.delivery_date AS deliveryDate,
COUNT(rel.coil_id) AS coilCount,
IFNULL(SUM(mc.net_weight), 0) AS totalNetWeight,
IFNULL(SUM(mc.gross_weight), 0) AS totalGrossWeight
FROM crm_order co
LEFT JOIN wms_coil_contract_rel rel ON co.order_id = rel.contract_id AND rel.del_flag = 0
LEFT JOIN wms_material_coil mc ON rel.coil_id = mc.coil_id AND mc.del_flag = 0
WHERE co.del_flag = 0
<if test="bo.contractCode != null and bo.contractCode != ''">
AND co.contract_code LIKE CONCAT('%', #{bo.contractCode}, '%')
</if>
<if test="bo.contractName != null and bo.contractName != ''">
AND co.contract_name LIKE CONCAT('%', #{bo.contractName}, '%')
</if>
<if test="bo.customer != null and bo.customer != ''">
AND co.customer LIKE CONCAT('%', #{bo.customer}, '%')
</if>
<if test="bo.salesman != null and bo.salesman != ''">
AND co.salesman LIKE CONCAT('%', #{bo.salesman}, '%')
</if>
<if test="bo.status != null">
AND co.status = #{bo.status}
</if>
<if test="bo.orderCode != null and bo.orderCode != ''">
AND co.order_code LIKE CONCAT('%', #{bo.orderCode}, '%')
</if>
GROUP BY co.order_id, co.order_code, co.contract_code, co.contract_name,
co.customer, co.supplier, co.salesman, co.status,
co.order_amount, co.sign_time, co.delivery_date
ORDER BY coilCount DESC, co.order_id ASC
</select>
</mapper>