Files
klp-oa/klp-ui/src/views/wms/coil/box2.vue
2025-11-25 01:13:26 +08:00

508 lines
17 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="statistics-container" v-loading="loading">
<!-- 按照物料类型盘点 -->
<!-- 统计方式选择 -->
<el-form inline>
<MaterialSelect :itemType.sync="queryParams.itemType" :itemId.sync="queryParams.itemId" @change="getList" />
<el-form-item label="逻辑库位">
<WarehouseSelect v-model="queryParams.warehouseId" @change="handleWarehouseChange" />
</el-form-item>
<!-- 真实库区 -->
<el-form-item label="真实库区">
<ActualWarehouseSelect
v-model="queryParams.actualWarehouseId"
@change="handleActualWarehouseChange"
:width="220"
/>
</el-form-item>
</el-form>
<!-- el-descriptions 汇总数据 -->
<!-- 汇总数据展示 -->
<div class="summary-container" v-if="list.length > 0">
<h3>数据汇总</h3>
<el-descriptions :column="3" border class="summary-descriptions">
<el-descriptions-item label="总卷数">{{ summaryData.totalCoilCount }} </el-descriptions-item>
<el-descriptions-item label="成品卷数">{{ summaryData.productCoilCount }} </el-descriptions-item>
<el-descriptions-item label="原料卷数">{{ summaryData.rawMaterialCoilCount }} </el-descriptions-item>
<el-descriptions-item label="总重(t)">{{ summaryData.totalNetWeight }} / {{ summaryData.totalGrossWeight }}</el-descriptions-item>
<el-descriptions-item label="成品总重(t)">{{ summaryData.productTotalNetWeight }} / {{ summaryData.productTotalGrossWeight }}</el-descriptions-item>
<el-descriptions-item label="原料总重(t)">{{ summaryData.rawMaterialTotalNetWeight }} / {{ summaryData.rawMaterialTotalGrossWeight }}</el-descriptions-item>
</el-descriptions>
</div>
<!-- 汇总总卷数成品卷数原料卷数总毛重(t)总净重(t)成品总毛重(t)成品总净重(t)原料总毛重(t)原料总净重(t) -->
<!-- 数据表格 -->
<div class="table-container">
<el-table max-height="800" :data="list" border stripe style="width: 100%; margin-top: 20px"
@row-click="handleTableRowClick" row-class-name="table-row-hover">
<!-- 物料ID/名称列仅物料统计时有效 -->
<el-table-column label="物料信息" align="center" min-width="250">
<template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType === 'product'" :product="scope.row.product">
<template #default="{ product }">
{{ product.productName || '未知' }}[{{ product.specification || '无规格' }}] - (材质{{ product.material || '无材质' }})
</template>
</ProductInfo>
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row.rawMaterial">
<template #default="{ material }">
{{ material.rawMaterialName || '未知' }}[{{ material.specification || '无规格' }}] - (材质{{ material.material || '无材质' }})
</template>
</RawMaterialInfo>
</template>
</el-table-column>
<!-- 通用列 -->
<el-table-column prop="coilCount" label="卷数" align="center" min-width="80"></el-table-column>
<el-table-column prop="totalGrossWeight" label="总毛重(t)" align="center" min-width="120"></el-table-column>
<el-table-column prop="totalNetWeight" label="总净重(t)" align="center" min-width="120"></el-table-column>
<!-- 物料类型列仅物料统计时有效 -->
<el-table-column prop="itemType" label="物料类型" align="center" min-width="120"
:formatter="formatItemType"></el-table-column>
</el-table>
<!-- 点击图表项或者表格行后弹出弹窗, 数据详情钻取获取汇总的数据详情如果当前是物料统计钻取将itemType和itemId作为条件查询list
如果当前是仓库统计将仓库id作为筛选条件 -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="80%" :close-on-click-modal="false">
<div class="drill-down-content">
<!-- 钻取条件显示 -->
<div class="drill-down-header">
<el-tag size="large" v-if="drillDownParams.itemType">
{{ drillDownParams.itemType === 'product' ? '成品' : '原材料' }}
</el-tag>
<el-tag size="large" v-if="drillDownParams.itemName" type="primary">
{{ drillDownParams.itemName }}
</el-tag>
<el-tag size="large" v-if="drillDownParams.warehouseName" type="success">
{{ drillDownParams.warehouseName }}
</el-tag>
</div>
<!-- 加载状态 -->
<el-skeleton :loading="drillDownLoading" animated>
<template #template>
<div class="demo-skeleton">
<el-skeleton-item variant="p" style="width: 80%" />
<el-skeleton-item variant="text" style="width: 30%" />
<el-skeleton-item variant="text" style="width: 60%" />
</div>
</template>
<!-- 钻取表格 -->
<el-table max-height="400" v-loading="drillDownLoading" :data="drillDownList" border stripe
style="width: 100%" v-if="!drillDownLoading">
<el-table-column prop="warehouseName" label="仓库名称" align="center" min-width="150"></el-table-column>
<el-table-column prop="currentCoilNo" label="当前卷号" align="center" min-width="120"></el-table-column>
<el-table-column prop="enterCoilNo" label="入场卷号" align="center" min-width="180"></el-table-column>
<el-table-column label="物料类型" align="center" prop="itemType">
<template slot-scope="scope">
{{ scope.row.itemType == 'product' ? '成品' : '原料' }}
</template>
</el-table-column>
<el-table-column label="产品类型" align="center" min-width="250">
<template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType === 'product'" :product="scope.row.product">
<template #default="{ product }">
{{ product.itemName || '未知' }}({{ product.itemCode || '无编码' }})
</template>
</ProductInfo>
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row.rawMaterial">
<template #default="{ material }">
{{ material.itemName || '未知' }}({{ material.itemCode || '无编码' }})
</template>
</RawMaterialInfo>
</template>
</el-table-column>
<el-table-column label="更新时间" align="center" prop="updateTime" />
<el-table-column prop="grossWeight" label="毛重(t)" align="center" min-width="100"></el-table-column>
<el-table-column prop="netWeight" label="净重(t)" align="center" min-width="100"></el-table-column>
</el-table>
</el-skeleton>
<!-- 分页 -->
<div class="pagination-container" v-if="!drillDownLoading && drillDownList.length > 0">
<el-pagination background :current-page.sync="drillDownQueryParams.pageNum"
:page-size.sync="drillDownQueryParams.pageSize" :page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="drillDownTotal"
@size-change="handleDrillDownSizeChange" @current-change="handleDrillDownCurrentChange"></el-pagination>
</div>
</div>
</el-dialog>
</div>
</div>
</template>
<script>
import { getMaterialCoilDistributionByType, distributionByActualItemType } from "@/api/wms/coil";
import { listMaterialCoil } from "@/api/wms/coil";
import MaterialSelect from "@/components/KLPService/MaterialSelect";
import RawMaterialInfo from "@/components/KLPService/Renderer/RawMaterialInfo";
import ProductInfo from "@/components/KLPService/Renderer/ProductInfo";
import WarehouseSelect from "@/components/KLPService/WarehouseSelect";
import ActualWarehouseSelect from "@/components/KLPService/ActualWarehouseSelect";
export default {
components: {
MaterialSelect,
RawMaterialInfo,
ProductInfo,
WarehouseSelect,
ActualWarehouseSelect,
},
data() {
return {
data() {
return {
// 原有变量保持不变...
summaryData: {
totalCoilCount: 0, // 总卷数
productCoilCount: 0, // 成品卷数
rawMaterialCoilCount: 0, // 原料卷数
totalGrossWeight: 0, // 总毛重(t)
totalNetWeight: 0, // 总净重(t)
productTotalGrossWeight: 0, // 成品总毛重(t)
productTotalNetWeight: 0, // 成品总净重(t)
rawMaterialTotalGrossWeight: 0, // 原料总毛重(t)
rawMaterialTotalNetWeight: 0 // 原料总净重(t)
}
}
},
queryParams: {
statType: '1',
itemType: undefined,
itemId: undefined,
warehouseId: undefined,
actualWarehouseId: undefined,
},
loading: false,
list: [],
// 科技风配色方案(蓝色系为主,体现科技感)
techColors: [
'#165DFF', // 主蓝色
'#36CFC9', // 青绿色
'#4080FF', // 中蓝色
'#0FC6C2', // 深青色
'#6AA1FF', // 浅蓝色
'#2BBEBA', // 浅青色
'#8CAAFF', // 极浅蓝色
'#08A8A5' // 暗青色
],
// 图表实例
treeChart: null,
barChart: null,
pieChart: null,
// 钻取相关数据
dialogVisible: false,
dialogTitle: '',
drillDownLoading: false,
drillDownList: [],
drillDownTotal: 0,
drillDownParams: {
itemType: null,
itemId: null,
itemName: '',
warehouseId: null,
warehouseName: '',
dataType: 1
},
drillDownQueryParams: {
pageNum: 1,
pageSize: 20
}
}
},
mounted() {
// this.initCharts();
this.getList();
},
beforeDestroy() {
// 销毁图表实例
if (this.treeChart) this.treeChart.dispose();
if (this.barChart) this.barChart.dispose();
if (this.pieChart) this.pieChart.dispose();
window.removeEventListener('resize', this.handleResize);
},
methods: {
// 计算汇总数据
calculateSummary() {
// 初始化汇总数据为0
const summary = {
totalCoilCount: 0,
productCoilCount: 0,
rawMaterialCoilCount: 0,
totalGrossWeight: 0,
totalNetWeight: 0,
productTotalGrossWeight: 0,
productTotalNetWeight: 0,
rawMaterialTotalGrossWeight: 0,
rawMaterialTotalNetWeight: 0
};
// 遍历列表数据累加计算
this.list.forEach(item => {
// 卷数统计
summary.totalCoilCount += item.coilCount || 0;
if (item.itemType === 'product') {
summary.productCoilCount += item.coilCount || 0;
} else if (item.itemType === 'raw_material') {
summary.rawMaterialCoilCount += item.coilCount || 0;
}
// 重量统计保留2位小数
const grossWeight = Number(item.totalGrossWeight) || 0;
const netWeight = Number(item.totalNetWeight) || 0;
summary.totalGrossWeight += grossWeight;
summary.totalNetWeight += netWeight;
if (item.itemType === 'product') {
summary.productTotalGrossWeight += grossWeight;
summary.productTotalNetWeight += netWeight;
} else if (item.itemType === 'raw_material') {
summary.rawMaterialTotalGrossWeight += grossWeight;
summary.rawMaterialTotalNetWeight += netWeight;
}
});
// 格式化重量数据为2位小数
Object.keys(summary).forEach(key => {
if (key.includes('Weight')) {
summary[key] = Number(summary[key].toFixed(2));
}
});
this.summaryData = summary;
},
// 处理逻辑库位选择变化
handleWarehouseChange() {
this.queryParams.actualWarehouseId = null; // 清空真实库区
this.queryParams.statType = '1'; // 切换为逻辑库位统计
this.getList();
},
// 处理真实库区选择变化
handleActualWarehouseChange() {
this.queryParams.warehouseId = null; // 清空逻辑库区
this.queryParams.statType = '2'; // 切换为真实库位统计
this.getList();
},
getList() {
this.loading = true;
if (this.queryParams.statType === '1') {
getMaterialCoilDistributionByType(this.queryParams).then(res => {
this.list = res.data || [];
this.calculateSummary(); // 新增:计算汇总数据
this.loading = false;
})
} else if (this.queryParams.statType === '2') {
distributionByActualItemType(this.queryParams).then(res => {
this.list = res.data || [];
this.calculateSummary(); // 新增:计算汇总数据
this.loading = false;
})
}
},
// 获取科技风颜色(循环使用配色方案)
getTechColor(index) {
return this.techColors[index % this.techColors.length];
},
// 生成物料名称(简化版,实际可根据组件返回值优化)
getMaterialName(item) {
if (item.itemType === 'product') {
return `成品(${item.itemId.slice(-6)})`; // 取ID后6位简化显示
} else if (item.itemType === 'raw_material') {
return `原材料(${item.itemId.slice(-6)})`;
}
return '未知物料';
},
// 格式化物料类型
formatItemType(row) {
const typeMap = {
'raw_material': '原材料',
'product': '成品'
};
return typeMap[row.itemType] || '未知';
},
// 处理表格行点击
handleTableRowClick(row) {
// 构建点击数据对象
const clickData = {};
if (this.queryParams.statType === '1') {
// 物料统计
clickData.itemType = row.itemType;
clickData.itemId = row.itemId;
clickData.name = this.getMaterialName(row);
} else {
// 仓库统计
clickData.warehouseId = row.warehouseId;
clickData.warehouseName = row.warehouseName;
}
// 调用图表点击处理函数
this.handleChartItemClick(clickData);
},
// 获取钻取数据列表
getDrillDownList() {
this.drillDownLoading = true;
// 构建查询参数
const params = {
...this.drillDownQueryParams,
itemType: this.drillDownParams.itemType,
itemId: this.drillDownParams.itemId,
warehouseId: this.drillDownParams.warehouseId
};
// 调用API获取数据
listMaterialCoil(params).then(res => {
this.drillDownList = res.rows || [];
this.drillDownTotal = res.total || 0;
this.drillDownLoading = false;
}).catch(error => {
console.error('获取钻取数据失败:', error);
this.drillDownLoading = false;
this.$message.error('获取明细数据失败');
});
},
// 处理钻取分页大小变化
handleDrillDownSizeChange(val) {
this.drillDownQueryParams.pageSize = val;
this.getDrillDownList();
},
// 处理钻取当前页变化
handleDrillDownCurrentChange(val) {
this.drillDownQueryParams.pageNum = val;
this.getDrillDownList();
},
// 格式化卷材状态
formatCoilStatus(status) {
const statusMap = {
'IN_STOCK': '在库',
'OUT_STOCK': '出库',
'TRANSFERING': '转移中',
'MAINTAINING': '维护中'
};
return statusMap[status] || '未知';
},
// 获取卷材状态标签类型
getCoilStatusTagType(status) {
const typeMap = {
'IN_STOCK': 'success',
'OUT_STOCK': 'info',
'TRANSFERING': 'warning',
'MAINTAINING': 'danger'
};
return typeMap[status] || 'default';
}
}
}
</script>
<style scoped>
.statistics-container {
padding: 20px;
background-color: #fafafa;
}
.charts-container {
display: flex;
flex-wrap: wrap;
gap: 20px;
margin-top: 20px;
}
.chart-item {
flex: 1;
min-width: 300px;
background: #fff;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.chart-box {
width: 100%;
height: 300px;
}
.table-container {
margin-top: 20px;
background: #fff;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
h3 {
margin: 0 0 15px 0;
font-size: 16px;
color: #1D2129;
font-weight: 500;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
/* 表格行悬停效果 */
.table-row-hover:hover {
background-color: #f5f7fa;
cursor: pointer;
}
/* 钻取弹窗样式 */
.drill-down-content {
padding: 10px 0;
}
.drill-down-header {
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #f0f0f0;
}
.drill-down-header .el-tag {
margin-right: 10px;
margin-bottom: 10px;
}
.pagination-container {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
/* 骨架屏样式 */
.demo-skeleton {
padding: 20px 0;
}
/* 汇总区域样式 */
.summary-container {
background: #fff;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
margin-top: 20px;
}
.summary-descriptions {
margin-top: 10px;
}
.el-descriptions-item__content {
font-weight: 500;
color: #165DFF;
/* 科技蓝强调汇总数据 */
}
</style>