Files
klp-oa/klp-ui/src/views/wms/report/abnormal.vue
砂糖 fa198181ee feat(wms): 新增报表透视表组件并优化操作状态显示
refactor: 替换select为tag显示操作状态
feat: 添加今天按钮到时间范围选择器
fix: 移除调试用的console.log
style: 调整按钮间距样式
feat: 新增厂家材质透视表和宽度厚度统计表
refactor: 优化导出功能去除orderBy参数
feat: 添加表面处理等查询条件
feat: 在coilTable中添加settings插槽
feat: 使用下拉菜单整合报表操作按钮
2026-05-05 14:52:24 +08:00

387 lines
16 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="时间">
<time-range-picker v-model="timeRangeParams" start-key="startTime" end-key="endTime"
:default-start-time="queryParams.startTime" :default-end-time="queryParams.endTime"
@quick-select="handleQuery" />
</el-form-item>
<el-form-item label="产线" prop="lineId">
<el-select style="width: 200px;" v-model="actionTypes" placeholder="请选择产线" clearable @change="handleQuery">
<el-option label="全部" value="" />
<el-option v-for="line in lineOptions" :key="line.value" :label="line.label" :value="line.value" />
</el-select>
</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="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="getList">查询</el-button> -->
<el-dropdown split-button type="primary" @click="getList" @command="handleCommand">
查询
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="exportData">导出产出钢卷</el-dropdown-item>
<el-dropdown-item command="exportLossData">导出消耗钢卷</el-dropdown-item>
<el-dropdown-item command="saveOutputReport">保存产出报表</el-dropdown-item>
<el-dropdown-item command="saveLossReport">保存消耗报表</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<!-- <el-button type="primary" @click="exportData">导出产出钢卷</el-button>
<el-button type="primary" @click="exportLossData">导出消耗钢卷</el-button>
<el-button type="primary" @click="saveOutputReport">保存产出报表</el-button>
<el-button type="primary" @click="saveLossReport">保存消耗报表</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.countDiff }}</el-descriptions-item>
<el-descriptions-item label="总重差值">{{ summary.weightDiff }}</el-descriptions-item>
<el-descriptions-item label="均重差值">{{ summary.avgWeightDiff }}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>
<!-- 已处理M统计信息 -->
<el-descriptions title="已处理M统计信息" :column="3" border>
<el-descriptions-item label="产出数量">{{ mSummary.outCount }}</el-descriptions-item>
<el-descriptions-item label="产出总重">{{ mSummary.outTotalWeight }}t</el-descriptions-item>
<el-descriptions-item label="产出均重">{{ mSummary.outAvgWeight }}t</el-descriptions-item>
<el-descriptions-item label="消耗数量">{{ mSummary.lossCount }}</el-descriptions-item>
<el-descriptions-item label="消耗总重">{{ mSummary.lossTotalWeight }}t</el-descriptions-item>
<el-descriptions-item label="消耗均重">{{ mSummary.lossAvgWeight }}t</el-descriptions-item>
<el-descriptions-item label="数量差值">{{ mSummary.countDiff }}</el-descriptions-item>
<el-descriptions-item label="总重差值">{{ mSummary.weightDiff }}</el-descriptions-item>
<el-descriptions-item label="均重差值">{{ mSummary.avgWeightDiff }}t</el-descriptions-item>
<!-- 成品率 -->
<el-descriptions-item label="成品率">{{ mSummary.passRate }}</el-descriptions-item>
<el-descriptions-item label="损耗率">{{ mSummary.lossRate }}</el-descriptions-item>
</el-descriptions>
<el-descriptions title="明细信息" :column="3" border>
</el-descriptions>
<el-tabs v-model="activeTab">
<el-tab-pane label="产出钢卷" name="output">
<coil-table :data="outList" :columns="outputColumns" :loading="loading" height="calc(100vh - 360px)">
<template slot="settings">
<el-button icon="el-icon-setting" @click="() => {settingVisible = true; activeColumnConfig = 'coil-report-output';}">列设置</el-button>
</template>
</coil-table>
</el-tab-pane>
<el-tab-pane label="投入钢卷" name="loss">
<coil-table :data="lossList" :columns="lossColumns" :loading="loading" height="calc(100vh - 360px)">
<template slot="settings">
<el-button icon="el-icon-setting" @click="() => {settingVisible = true; activeColumnConfig = 'coil-report-loss';}">列设置</el-button>
</template>
</coil-table>
</el-tab-pane>
</el-tabs>
<el-dialog title="列设置" :visible.sync="settingVisible" width="50%">
<columns-setting :reportType="activeColumnConfig"></columns-setting>
</el-dialog>
</div>
</template>
<script>
import { listCoilWithIds } from "@/api/wms/coil";
import {
listPendingAction,
} from '@/api/wms/pendingAction';
import MemoInput from "@/components/MemoInput";
import MutiSelect from "@/components/MutiSelect";
import ProductInfo from "@/components/KLPService/Renderer/ProductInfo";
import RawMaterialInfo from "@/components/KLPService/Renderer/RawMaterialInfo";
import CoilNo from "@/components/KLPService/Renderer/CoilNo.vue";
import { calcSummary, calcMSummary } from "@/views/wms/report/js/calc";
import CoilTable from "@/views/wms/report/components/coilTable";
import ColumnsSetting from "@/views/wms/report/components/setting/columns";
import TimeRangePicker from "@/views/wms/report/components/timeRangePicker.vue";
import { saveReportFile } from "@/views/wms/report/js/reportFile";
export default {
name: 'MergeTemplate',
components: {
MemoInput,
MutiSelect,
ProductInfo,
RawMaterialInfo,
CoilNo,
CoilTable,
ColumnsSetting,
TimeRangePicker
},
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)}`
/**
* 生成指定日期/月份的时间范围字符串
* @param {string} dateStr - 支持格式yyyy-MM月份 或 yyyy-MM-dd具体日期
* @returns {object} 包含start开始时间和end结束时间的对象
*/
const getDayTimeRange = (dateStr) => {
// 先校验输入格式是否合法
const monthPattern = /^\d{4}-\d{2}$/; // yyyy-MM 正则
const dayPattern = /^\d{4}-\d{2}-\d{2}$/; // yyyy-MM-dd 正则
if (!monthPattern.test(dateStr) && !dayPattern.test(dateStr)) {
throw new Error('输入格式错误,请传入 yyyy-MM 或 yyyy-MM-dd 格式的字符串');
}
let startDate, endDate;
if (monthPattern.test(dateStr)) {
// 处理 yyyy-MM 格式:获取本月第一天和最后一天
const [year, month] = dateStr.split('-').map(Number);
// 月份是0基的0=1月1=2月...所以要减1
// 第一天yyyy-MM-01
startDate = `${dateStr}-01`;
// 最后一天:通过 new Date(year, month, 0) 计算month是原始月份比如2代表2月传2则取3月0日=2月最后一天
const lastDayOfMonth = new Date(year, month, 0).getDate();
endDate = `${dateStr}-${lastDayOfMonth.toString().padStart(2, '0')}`;
} else {
// 处理 yyyy-MM-dd 格式:直接使用传入的日期
startDate = dateStr;
endDate = dateStr;
}
// 拼接时间部分
return {
start: `${startDate} 00:00:00`,
end: `${endDate} 23:59:59`
};
};
const { start, end } = getDayTimeRange(currentDate)
return {
lossList: [],
outList: [],
activeTab: 'output',
activeColumnConfig: 'coil-report-output',
settingVisible: false,
loading: false,
timeRangeParams: {
startTime: start,
endTime: end
},
queryParams: {
startTime: start,
endTime: end,
lineId: '',
enterCoilNo: '',
currentCoilNo: '',
warehouseId: '',
itemName: '',
itemSpecification: '',
itemMaterial: '',
itemManufacturer: '',
pageSize: 99999,
pageNum: 1,
},
lossColumns: [],
outputColumns: [],
actionTypes: '',
lineOptions: [
{ label: '酸轧线', value: '11,120,201,520' },
{ label: '镀锌线', value: '202,501,521' },
{ label: '双机架', value: '205,504,524' },
{ label: '镀铬线', value: '206,505,525' },
{ label: '拉矫线', value: '204,503,523' },
{ label: '脱脂线', value: '203,502,522' },
]
}
},
computed: {
summary() {
return calcSummary(this.outList, this.lossList)
},
mSummary() {
return calcMSummary(this.outList, this.lossList)
},
},
watch: {
timeRangeParams: {
handler(newVal) {
this.queryParams.startTime = newVal.startTime
this.queryParams.endTime = newVal.endTime
},
deep: true,
immediate: true
}
},
created() {
this.handleQuery()
this.loadColumns()
},
methods: {
handleQuery() {
this.getList()
},
// 保存产出报表
saveOutputReport() {
this.loading = true
saveReportFile(this.outList.map(item => item.coilId).join(','), {
reportParams: this.queryParams,
reportType: '产出报表,异常报表',
productionLine: this.productionLine,
}).then(res => {
this.$message({
message: '保存成功',
type: 'success',
})
}).catch(err => {
this.$message({
message: '保存失败',
type: 'error',
})
}).finally(() => {
this.loading = false
})
},
// 保存消耗报表
saveLossReport() {
this.loading = true
saveReportFile(this.lossList.map(item => item.coilId).join(','), {
reportParams: this.queryParams,
reportType: '消耗报表,异常报表',
productionLine: this.productionLine,
}).then(res => {
this.$message({
message: '保存成功',
type: 'success',
})
}).catch(err => {
this.$message({
message: '保存失败',
type: 'error',
})
}).finally(() => {
this.loading = false
})
},
async getList() {
this.loading = true;
const actions = await listPendingAction({ ...this.queryParams, actionTypes: this.actionTypes, actionStatus: 2 });
const outIds = actions.rows.map(item => item.processedCoilIds).join(',');
if (!outIds) {
this.outList = []
this.loading = false;
return;
}
const outRes = await listCoilWithIds({ ...this.queryParams, coilIds: outIds || '', startTime: '', endTime: '', minAbnormalCount: 1 });
this.outList = outRes.rows.map(item => {
// 计算宽度和厚度,将规格按照*分割,*前的是厚度,*后的是宽度
const [thickness, width] = item.specification?.split('*') || []
return {
...item,
computedThickness: parseFloat(thickness),
computedWidth: parseFloat(width),
}
});
const lossIds = this.outList.map(item => item.parentCoilId).join(',');
if (!lossIds) {
this.lossList = []
this.loading = false;
return;
}
const lossRes = await listCoilWithIds({ ...this.queryParams, coilIds: lossIds || '', startTime: '', endTime: '' });
this.lossList = lossRes.rows.map(item => {
// 计算宽度和厚度,将规格按照*分割,*前的是厚度,*后的是宽度
const [thickness, width] = item.specification?.split('*') || []
return {
...item,
computedThickness: parseFloat(thickness),
computedWidth: parseFloat(width),
}
});
this.loading = false;
},
// 导出产出钢卷
exportData() {
if (this.outList.length === 0) {
this.$message.warning('暂无数据可导出')
return
}
this.download('wms/materialCoil/export', {
coilIds: this.outList.map(item => item.coilId).join(',')
}, `materialCoil_${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_${new Date().getTime()}.xlsx`)
},
// 处理命令
handleCommand(command) {
if (command === 'exportData') {
this.exportData()
} else if (command === 'exportLossData') {
this.exportLossData()
} else if (command === 'saveOutputReport') {
this.saveOutputReport()
} else if (command === 'saveLossReport') {
this.saveLossReport()
}
},
// 加载列设置
loadColumns() {
this.lossColumns = JSON.parse(localStorage.getItem('preference-tableColumns-coil-report-loss') || '[]') || []
this.outputColumns = JSON.parse(localStorage.getItem('preference-tableColumns-coil-report-output') || '[]') || []
}
}
}
</script>
<style></style>