Files
klp-oa/klp-ui/src/views/wms/report/zinc/year.vue
砂糖 352e09d704 feat(wms): 新增锌和酸连轧报表页面并更新仓库配置
新增锌和酸连轧的日报、班组报和年报页面
在zha.vue和zinc.vue中添加新的仓库配置项
移除部分注释代码并优化查询逻辑
2026-02-12 09:58:15 +08:00

402 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="app-container" v-loading="loading">
<el-row>
<el-form label-width="80px" inline>
<el-form-item label="日期" prop="date">
<el-date-picker style="width: 200px;" v-model="queryParams.date" type="year" value-format="yyyy"
placeholder="选择日期" @change="handleDateChange"></el-date-picker>
</el-form-item>
<el-form-item label="入场钢卷号" prop="enterCoilNo">
<el-input style="width: 200px; display: inline-block;" v-model="queryParams.enterCoilNo"
placeholder="请输入入场钢卷号" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="当前钢卷号" prop="currentCoilNo">
<el-input style="width: 200px;" v-model="queryParams.currentCoilNo" placeholder="请输入当前钢卷号" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="逻辑库位" prop="warehouseIds">
<el-select v-model="warehouseIds" collapse-tags multiple placeholder="请选择逻辑库位" style="width: 200px;">
<el-option v-for="item in warehouseOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="产品名称" prop="itemName">
<el-input style="width: 200px;" v-model="queryParams.itemName" placeholder="请输入产品名称" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="规格" prop="itemSpecification">
<memo-input style="width: 200px;" v-model="queryParams.itemSpecification" storageKey="coilSpec"
placeholder="请选择规格" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="材质" prop="itemMaterial">
<muti-select style="width: 200px;" v-model="queryParams.itemMaterial" :options="dict.type.coil_material"
placeholder="请选择材质" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="厂家" prop="itemManufacturer">
<muti-select style="width: 200px;" v-model="queryParams.itemManufacturer"
:options="dict.type.coil_manufacturer" placeholder="请选择厂家" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">查询</el-button> <!-- 统一改为handleQuery -->
<el-button type="primary" @click="exportData">导出产出钢卷</el-button>
<el-button type="primary" @click="exportLossData">导出消耗钢卷</el-button>
</el-form-item>
</el-form>
</el-row>
<el-descriptions title="统计信息" :column="3" border>
<el-descriptions-item label="产出数量">{{ summary.outCount }}</el-descriptions-item>
<el-descriptions-item label="产出总重">{{ summary.outTotalWeight }}t</el-descriptions-item>
<el-descriptions-item label="产出均重">{{ summary.outAvgWeight }}t</el-descriptions-item>
<el-descriptions-item label="消耗数量">{{ summary.lossCount }}</el-descriptions-item>
<el-descriptions-item label="消耗总重">{{ summary.lossTotalWeight }}t</el-descriptions-item>
<el-descriptions-item label="消耗均重">{{ summary.lossAvgWeight }}t</el-descriptions-item>
<el-descriptions-item label="合计数量">{{ summary.totalCount }}</el-descriptions-item>
<el-descriptions-item label="合计总重">{{ summary.totalWeight }}t</el-descriptions-item>
<el-descriptions-item label="合计均重">{{ summary.totalAvgWeight }}t</el-descriptions-item>
<!-- 成品率 -->
<el-descriptions-item label="成品率">{{ summary.passRate }}</el-descriptions-item>
<el-descriptions-item label="损耗率">{{ summary.lossRate }}</el-descriptions-item>
<!-- 异常率 -->
<el-descriptions-item label="异常率">{{ summary.abRate }}</el-descriptions-item>
</el-descriptions>
<el-descriptions title="明细信息" :column="3" border>
</el-descriptions>
<el-tabs v-model="activeTab">
<el-tab-pane label="损耗钢卷" name="loss">
<el-table :data="lossList" border height="calc(100vh - 320px)">
<el-table-column label="入场钢卷号" align="center" prop="enterCoilNo">
<template slot-scope="scope">
<coil-no :coil-no="scope.row.enterCoilNo"></coil-no>
</template>
</el-table-column>
<el-table-column label="当前钢卷号" align="center" prop="currentCoilNo">
<template slot-scope="scope">
<coil-no :coil-no="scope.row.currentCoilNo"></coil-no>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" />
<el-table-column label="逻辑库位" align="center" prop="warehouseName" />
<!-- <el-table-column label="实际库区" align="center" prop="actualWarehouseName" /> -->
<el-table-column label="产品类型" align="center" width="250">
<template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType == 'product'" :product="scope.row.product" />
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row.rawMaterial" />
</template>
</el-table-column>
<el-table-column label="重量 (吨)" align="center" prop="netWeight" />
<el-table-column label="长度 (米)" align="center" prop="length" />
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
<el-table-column label="更新人" align="center" prop="updateByName" />
<el-table-column label="更新时间" align="center" prop="updateTime" />
</el-table>
</el-tab-pane>
<el-tab-pane label="产出钢卷" name="output">
<el-table :data="list" border height="calc(100vh - 320px)">
<el-table-column label="入场钢卷号" align="center" prop="enterCoilNo">
<template slot-scope="scope">
<coil-no :coil-no="scope.row.enterCoilNo"></coil-no>
</template>
</el-table-column>
<el-table-column label="当前钢卷号" align="center" prop="currentCoilNo">
<template slot-scope="scope">
<coil-no :coil-no="scope.row.currentCoilNo"></coil-no>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" />
<el-table-column label="逻辑库位" align="center" prop="warehouseName" />
<el-table-column label="实际库区" align="center" prop="actualWarehouseName" />
<el-table-column label="产品类型" align="center" width="250">
<template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType == 'product'" :product="scope.row.product" />
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row.rawMaterial" />
</template>
</el-table-column>
<el-table-column label="重量 (吨)" align="center" prop="netWeight" />
<el-table-column label="长度 (米)" align="center" prop="length" />
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
<el-table-column label="出库状态" align="center" prop="status">
<!-- 0在库1已出库 -->
<template slot-scope="scope">
{{ scope.row.status === 0 ? '在库' : '已出库' }}
</template>
</el-table-column>
<el-table-column label="更新人" align="center" prop="updateByName" />
<el-table-column label="更新时间" align="center" prop="updateTime" />
</el-table>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import { listCoilWithIds } from "@/api/wms/coil";
import {
listPendingAction,
} from '@/api/wms/pendingAction';
import ProductInfo from "@/components/KLPService/Renderer/ProductInfo";
import RawMaterialInfo from "@/components/KLPService/Renderer/RawMaterialInfo";
import CoilNo from "@/components/KLPService/Renderer/CoilNo.vue";
import MemoInput from "@/components/MemoInput";
import MutiSelect from "@/components/MutiSelect";
import WarehouseSelect from "@/components/KLPService/WarehouseSelect";
export default {
components: {
ProductInfo,
RawMaterialInfo,
CoilNo,
MemoInput,
MutiSelect,
WarehouseSelect,
},
dicts: ['product_coil_status', 'coil_material', 'coil_itemname', 'coil_manufacturer'],
data() {
// 工具函数:个位数补零
const addZero = (num) => num.toString().padStart(2, '0')
// 获取当前日期(默认选中当天)
const now = new Date()
const currentDate = `${now.getFullYear()}-${addZero(now.getMonth() + 1)}-${addZero(now.getDate())}`
/**
* 生成指定年份的时间范围字符串
* @param {string} dateStr - 必须为 yyyy 格式(如 "2026"
* @returns {object} 包含start当年第一天和end当年最后一天的时间对象
* @throws {Error} 输入格式错误时抛出异常
*/
const getDayTimeRange = (dateStr) => {
// 校验输入格式必须是4位数字的yyyy格式
const yearPattern = /^\d{4}$/;
if (!yearPattern.test(dateStr)) {
throw new Error('输入格式错误,请传入 yyyy 格式的字符串(如 "2026"');
}
// 拼接当年第一天和最后一天的完整日期+时间
return {
start: `${dateStr}-01-01 00:00:00`, // 年-01-01 0点
end: `${dateStr}-12-31 23:59:59` // 年-12-31 23点59分59秒
};
};
const { start, end } = getDayTimeRange(currentDate)
return {
activeTab: 'loss',
list: [],
lossList: [],
queryParams: {
pageNum: 1,
pageSize: 9999,
date: currentDate, // 绑定日期选择器的默认值(当天)
byCreateTimeStart: start, // 默认当天0点
byCreateTimeEnd: end, // 默认当天23:59:59
selectType: 'product',
enterCoilNo: '',
currentCoilNo: '',
warehouseId: '',
itemName: '', // 修正原代码的productName为itemName和表单绑定一致
itemSpecification: '',
itemMaterial: '',
itemManufacturer: '',
},
loading: false,
warehouseIds: [
'1988150323162836993',
'1988150487185289217',
'2019583656787259393',
'2019583325311414274',
'2019583429955104769',
'2019583137616310273',
],
warehouseOptions: [
{ value: '1988150323162836993', label: '镀锌成品库' },
{ value: '1988150487185289217', label: '镀锌纵剪分条原料库' },
{ label: '技术部', value: '2019583656787259393' },
{ label: '小钢卷库', value: '2019583325311414274' },
{ label: '废品库', value: '2019583429955104769' },
{ label: '退货库', value: '2019583137616310273' },
],
warehouseQueryMap: {
'1988150323162836993': {
selectType: 'product',
// createBy: 'suanzhakuguan',
warehouseId: '1988150323162836993'
},
'1988150487185289217': {
selectType: 'raw_material',
// createBy: 'suanzhakuguan',
warehouseId: '1988150487185289217'
},
'2019583656787259393': {
selectType: 'product',
// createBy: 'suanzhakuguan',
warehouseId: '2019583656787259393'
},
'20195833325311414274': {
selectType: 'product',
// createBy: 'suanzhakuguan',
warehouseId: '20195833325311414274'
},
'2019583429955104769': {
selectType: 'product',
// createBy: 'suanzhakuguan',
warehouseId: '2019583429955104769'
},
'2019583137616310273': {
selectType: 'product',
// createBy: 'suanzhakuguan',
warehouseId: '2019583137616310273'
},
},
getDayTimeRange // 挂载时间范围生成函数
}
},
computed: {
summary() {
// 总钢卷数量、总重、均重
const outCount = this.list.length
const outTotalWeight = this.list.reduce((acc, cur) => acc + (parseFloat(cur.netWeight) || 0), 0) // 增加容错
const outAvgWeight = outCount > 0 ? (outTotalWeight / outCount)?.toFixed(2) : 0
// 损失钢卷数量、总重、均重
const lossCount = this.lossList.length
const lossTotalWeight = this.lossList.reduce((acc, cur) => acc + (parseFloat(cur.netWeight) || 0), 0) // 增加容错
const lossAvgWeight = lossCount > 0 ? (lossTotalWeight / lossCount)?.toFixed(2) : 0
// 合计数量、总重、均重
const totalCount = outCount + lossCount
const totalWeight = parseFloat((outTotalWeight + lossTotalWeight).toFixed(2))
const totalAvgWeight = totalCount > 0 ? (totalWeight / totalCount)?.toFixed(2) : 0
// 成品比率
const passRate = outCount > 0 ? (outCount / lossCount) : 0
// 损失比率
const lossRate = 1 - passRate
// 异常率,成品在warehouseId在'2019583656787259393',
// '2019583325311414274',
// '2019583429955104769',
// '2019583137616310273',这四个库中的占比
const abRate = this.list.filter(item => {
return item.warehouseId == '2019583656787259393' || item.warehouseId == '2019583325311414274' || item.warehouseId == '2019583429955104769' || item.warehouseId == '2019583137616310273'
}).length / totalCount
return {
outCount,
outTotalWeight: outTotalWeight.toFixed(2),
outAvgWeight,
lossCount,
lossTotalWeight: lossTotalWeight.toFixed(2),
lossAvgWeight,
totalCount,
totalWeight: totalWeight.toFixed(2),
totalAvgWeight,
passRate: passRate?.toFixed(2) * 100 + '%',
lossRate: lossRate?.toFixed(2) * 100 + '%',
abRate: (abRate?.toFixed(2) * 100) || 0 + '%',
}
}
},
methods: {
// 日期变更处理:更新开始/结束时间
handleDateChange(date) {
if (!date) return
const { start, end } = this.getDayTimeRange(date)
this.queryParams.byCreateTimeStart = start
this.queryParams.byCreateTimeEnd = end
// 日期变更后自动查询
this.handleQuery()
},
// 统一查询入口(兼容回车和按钮点击)
handleQuery() {
this.getList()
},
// 核心查询逻辑
getList() {
this.loading = true
Promise.all(
this.warehouseIds.map(warehouseId => {
const params = this.warehouseQueryMap[warehouseId] || {} // 增加容错
return listCoilWithIds({
...this.queryParams,
pageSize: 9999,
pageNum: 1,
...params
})
}),
).then((resList) => {
const list = resList.flatMap(res => res?.rows || []) // 增加容错
// 按照createTime 降序排序
this.list = list.sort(
(a, b) => new Date(b.createTime) - new Date(a.createTime)
)
this.loading = false
this.getLossList()
}).catch(err => { // 增加错误处理
console.error('查询失败:', err)
this.loading = false
})
},
getLossList() {
this.loading = true
listPendingAction({
actionType: 501, // 镀锌工序
pageSize: 999,
pageNum: 1,
startTime: this.queryParams.byCreateTimeStart,
endTime: this.queryParams.byCreateTimeEnd,
}).then(res => {
const actions = res.rows
const coilIds = actions.map(item => item.coilId).join(',')
console.log(coilIds)
if (!coilIds) {
this.$message({
message: '暂无数据',
type: 'warning',
})
this.list = []
this.loading = false
return
}
listCoilWithIds({
...this.queryParams,
byCreateTimeStart: undefined,
byCreateTimeEnd: undefined,
coilIds: coilIds,
}).then(res => {
this.lossList = res.rows
this.loading = false
})
})
},
// 导出
exportData() {
if (this.list.length === 0) {
this.$message.warning('暂无数据可导出')
return
}
this.download('wms/materialCoil/export', {
coilIds: this.list.map(item => item.coilId).join(',')
}, `materialCoil_${this.queryParams.date}_${new Date().getTime()}.xlsx`)
},
exportLossData() {
if (this.lossList.length === 0) {
this.$message.warning('暂无数据可导出')
return
}
this.download('wms/materialCoil/export', {
coilIds: this.lossList.map(item => item.coilId).join(',')
}, `materialCoil_${this.queryParams.date}_${new Date().getTime()}.xlsx`)
},
},
mounted() {
this.getList()
}
}
</script>
<style scoped></style>