Merge remote-tracking branch 'origin/0.8.X' into 0.8.X

This commit is contained in:
2026-07-01 15:43:40 +08:00
5 changed files with 1125 additions and 740 deletions

View File

@@ -80,7 +80,7 @@
<!-- 基本信息 -->
<div class="pf-tb">
<!-- 第1行表头5按纸质单样式 -->
<div class="pf-tr" style="grid-template-columns:23fr 24fr 26fr 7fr 20fr">
<div class="pf-tr" style="grid-template-columns:23fr 24fr 25fr 8fr 20fr">
<div class="pf-td pf-lbl">请购部门</div>
<div class="pf-td pf-lbl"> </div>
<div class="pf-td pf-lbl"> </div>
@@ -88,7 +88,7 @@
<div class="pf-td pf-lbl">请购量</div>
</div>
<!-- 第2行输入区域 -->
<div class="pf-tr" style="grid-template-columns:23fr 24fr 26fr 7fr 20fr">
<div class="pf-tr" style="grid-template-columns:23fr 24fr 25fr 8fr 20fr">
<div class="pf-td pf-val"><el-form-item prop="reqDept"><el-input v-model="form.reqDept" size="mini" /></el-form-item></div>
<div class="pf-td pf-val"><el-form-item prop="itemName"><el-input v-model="form.itemName" size="mini" /></el-form-item></div>
<div class="pf-td pf-val"><el-form-item prop="specification"><el-input v-model="form.specification" size="mini" /></el-form-item></div>
@@ -149,7 +149,7 @@
<!-- 采购处理 -->
<div class="pf-tb pf-pt">
<!-- 品检条件 + 前期采购记录 -->
<div class="pf-tr" style="grid-template-columns:8fr 42fr 17fr 33fr">
<div class="pf-tr" style="grid-template-columns:10fr 40fr 17fr 33fr">
<div class="pf-td pf-lbl">品检条件</div>
<div class="pf-td pf-val pf-chk-group" style="flex-wrap:wrap">
<el-checkbox-group v-model="inspectionChecks" size="mini">
@@ -166,15 +166,15 @@
<div class="pf-td pf-val"><el-form-item prop="prevPurchaseRecord"><el-input v-model="form.prevPurchaseRecord" size="mini" /></el-form-item></div>
</div>
<!-- 采购拟办 + 付款条件 -->
<div class="pf-tr" style="grid-template-columns:10fr 40fr 8fr 42fr">
<div class="pf-tr" style="grid-template-columns:10fr 40fr 17fr 33fr">
<div class="pf-td pf-lbl">采购拟办</div>
<div class="pf-td pf-val"><el-form-item prop="purchaseProposal"><el-input v-model="form.purchaseProposal" size="mini" /></el-form-item></div>
<div class="pf-td pf-lbl">付款条件</div>
<div class="pf-td pf-val"><el-form-item prop="paymentTerms"><el-input v-model="form.paymentTerms" size="mini" /></el-form-item></div>
</div>
<!-- 采购金额(人民币) -->
<div class="pf-tr" style="grid-template-columns:18fr 82fr">
<div class="pf-td pf-lbl">采购金额(人民币)</div>
<div class="pf-tr" style="grid-template-columns:10fr 90fr">
<div class="pf-td pf-lbl" style="white-space:normal">采购金额(人民币)</div>
<div class="pf-td pf-val"><el-form-item prop="totalAmount"><el-input v-model="form.totalAmount" size="mini" /></el-form-item></div>
</div>
</div>
@@ -219,7 +219,7 @@
</div>
<!-- 状态 + 备注 -->
<div class="pf-tb">
<div class="pf-tr" style="grid-template-columns:8fr 20fr 8fr 64fr">
<div class="pf-tr" style="grid-template-columns:20fr 30fr 8fr 42fr">
<div class="pf-td pf-lbl">状态</div>
<div class="pf-td pf-val">
<el-select v-model="form.formStatus" size="mini" style="width:100%">
@@ -247,7 +247,7 @@
<div class="pr-view">
<div class="pf-tb">
<!-- 第1行表头 -->
<div class="pf-tr" style="grid-template-columns:23fr 24fr 26fr 7fr 20fr">
<div class="pf-tr" style="grid-template-columns:23fr 24fr 25fr 8fr 20fr">
<div class="pf-td pf-lbl">请购部门</div>
<div class="pf-td pf-lbl"> </div>
<div class="pf-td pf-lbl"> </div>
@@ -255,7 +255,7 @@
<div class="pf-td pf-lbl">请购量</div>
</div>
<!-- 第2行 -->
<div class="pf-tr" style="grid-template-columns:23fr 24fr 26fr 7fr 20fr">
<div class="pf-tr" style="grid-template-columns:23fr 24fr 25fr 8fr 20fr">
<div class="pf-td pf-val pf-val-read">{{ viewForm.reqDept || '—' }}</div>
<div class="pf-td pf-val pf-val-read">{{ viewForm.itemName || '—' }}</div>
<div class="pf-td pf-val pf-val-read">{{ viewForm.specification || '—' }}</div>
@@ -293,20 +293,20 @@
<!-- 采购处理 -->
<div class="pf-tb pf-pt">
<div class="pf-tr" style="grid-template-columns:8fr 42fr 17fr 33fr">
<div class="pf-tr" style="grid-template-columns:10fr 40fr 17fr 33fr">
<div class="pf-td pf-lbl">品检条件</div>
<div class="pf-td pf-val pf-val-read" style="text-align:left">{{ formatInsp(viewForm.inspectionCondition, viewForm.trialDays) }}</div>
<div class="pf-td pf-lbl">前期采购记录</div>
<div class="pf-td pf-val pf-val-read">{{ viewForm.prevPurchaseRecord || '—' }}</div>
</div>
<div class="pf-tr" style="grid-template-columns:10fr 40fr 8fr 42fr">
<div class="pf-tr" style="grid-template-columns:10fr 40fr 17fr 33fr">
<div class="pf-td pf-lbl">采购拟办</div>
<div class="pf-td pf-val pf-val-read">{{ viewForm.purchaseProposal || '—' }}</div>
<div class="pf-td pf-lbl">付款条件</div>
<div class="pf-td pf-val pf-val-read">{{ viewForm.paymentTerms || '—' }}</div>
</div>
<div class="pf-tr" style="grid-template-columns:18fr 82fr">
<div class="pf-td pf-lbl">采购金额(人民币)</div>
<div class="pf-tr" style="grid-template-columns:10fr 90fr">
<div class="pf-td pf-lbl" style="white-space:normal">采购金额(人民币)</div>
<div class="pf-td pf-val pf-val-read">{{ viewForm.totalAmount || '—' }}</div>
</div>
</div>
@@ -348,7 +348,7 @@
<div class="pf-td pf-lbl">置于</div>
<div class="pf-td pf-val pf-val-read">{{ viewForm.unloadOther || '—' }}</div>
</div>
<div class="pf-tr" style="grid-template-columns:8fr 20fr 8fr 64fr">
<div class="pf-tr" style="grid-template-columns:20fr 30fr 8fr 42fr">
<div class="pf-td pf-lbl">状态</div>
<div class="pf-td pf-val pf-val-read">
<span class="pv-status" :class="'s' + viewForm.formStatus">{{ statusText(viewForm.formStatus) }}</span>
@@ -423,10 +423,10 @@
<tr>
<td class="pp-lbl">用途类别</td>
<td colspan="2" class="pp-val" style="text-align:left;font-size:8.5pt">
<span :class="ppCheck(printData.category === '原料物料')"></span>原料物料
<span :class="ppCheck(printData.category === '设备增添')"></span>设备增添
<span :class="ppCheck(printData.category === '工程劳务')"></span>工程劳务
<span :class="ppCheck(printData.category === '其它用途')"></span>其它用途
<span>{{ printData.category === '原料物料' ? '☑' : '□' }}</span>原料物料
<span>{{ printData.category === '设备增添' ? '☑' : '□' }}</span>设备增添
<span>{{ printData.category === '工程劳务' ? '☑' : '□' }}</span>工程劳务
<span>{{ printData.category === '其它用途' ? '☑' : '□' }}</span>其它用途
</td>
<td class="pp-lbl">需求日期</td>
<td class="pp-val">{{ printData.requiredDate || '' }}</td>
@@ -838,10 +838,6 @@ export default {
const keywords = { 1: '一般', 2: '品保', 3: '工程', 4: '试用' }
return val.includes(keywords[code])
},
// PDF 复选框样式
ppCheck(cond) {
return cond ? 'pp-chk pp-on' : 'pp-chk'
}
}
}
</script>
@@ -1124,11 +1120,4 @@ $sub: #909399;
padding: 5px 4px;
}
/* 复选框 */
.pp-chk {
font-family: '宋体', SimSun, serif;
}
.pp-chk.pp-on {
font-weight: 700;
}
</style>

View File

@@ -87,6 +87,7 @@ export default {
{ label: '调制度', key: 'temperGrade' },
{ label: '镀层种类', key: 'coatingType' },
{ label: '钢卷表面处理', key: 'coilSurfaceTreatment' },
{ label: '创建时间', key: 'createTime' },
{ label: '备注', key: 'remark', span: this.column > 2 ? this.column - 2 : 1 }
]
},
@@ -101,9 +102,13 @@ export default {
}))
}
}
let value = this.coilInfo[item.key]
if (item.key === 'createTime' && value) {
value = `${value}${this.getDaysAgo(value)}`
}
return {
...item,
value: this.coilInfo[item.key]
value
}
})
@@ -119,6 +124,18 @@ export default {
return fields
}
},
methods: {
getDaysAgo(dateStr) {
if (!dateStr) return ''
const date = new Date(dateStr)
const now = new Date()
const diffTime = now.getTime() - date.getTime()
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24))
if (diffDays === 0) return '今天'
if (diffDays === 1) return '昨天'
return `${diffDays}天前`
}
}
}
</script>

View File

@@ -1,723 +1,30 @@
<template>
<div class="app-container">
<el-form v-show="showSearch" ref="queryForm" :model="queryParams" size="small" :inline="true" label-width="68px">
<el-form-item label="告警类型" prop="warningType">
<el-radio-group v-model="queryParams.warningType" @change="handleQuery">
<el-radio-button label="LENGTH">长度告警</el-radio-button>
<el-radio-button label="THICKNESS">厚度告警</el-radio-button>
</el-radio-group>
</el-form-item>
<!-- <el-form-item label="告警级别" prop="warningLevel">
<el-select v-model="queryParams.warningLevel" placeholder="请选择告警级别" clearable @keyup.enter.native="handleQuery">
<el-option label="警告" value="WARNING" />
<el-option label="错误" value="ERROR" />
<el-option label="严重错误" value="CRITICAL" />
</el-select>
</el-form-item> -->
<el-form-item label="告警状态" prop="warningStatus">
<el-select
v-model="queryParams.warningStatus"
placeholder="请选择告警状态"
clearable
@change="handleQuery"
>
<el-option label="未处理" value="0" />
<el-option label="已处理" value="1" />
<el-option label="已忽略" value="2" />
</el-select>
</el-form-item>
<el-form-item label="处理人" prop="handleBy">
<el-input v-model="queryParams.handleBy" placeholder="请输入处理人" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="只看今天" prop="onlyToday">
<el-switch v-model="queryParams.onlyToday" @change="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
<!-- <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button> -->
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-edit"
size="mini"
:disabled="multiple"
@click="handleBatchProcess"
>批量处理</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-edit"
size="mini"
:disabled="multiple"
@click="handleBatchIgnore"
>批量忽略</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-finished"
size="mini"
@click="handleBatchProcessHistory"
>处理历史告警</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
@click="handleBatchIgnoreHistory"
>忽略历史告警</el-button>
</el-col>
</el-row>
<el-row :gutter="10" class="mb8" style="margin-bottom: 16px;">
<el-col :span="24">
<el-radio-group v-model="queryParams.actionType" @change="handleQuery" size="mini">
<el-radio-button :key="all" :label="''">全部</el-radio-button>
<el-radio-button v-for="item in dict.type.action_type" :key="item.value" :label="item.value">{{ item.label }}</el-radio-button>
</el-radio-group>
</el-col>
</el-row>
<el-table v-loading="loading" :data="materialWarningList" :row-class-name="getRowClassName" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" :selectable="checkSelectable" />
<!-- <el-table-column label="钢卷ID" align="center" prop="coilId" /> -->
<el-table-column label="告警类型" align="center" prop="warningType" width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.warningType === 'LENGTH' ? 'warning' : scope.row.warningType === 'THICKNESS' ? '' : 'success'" size="small" effect="plain">
{{ scope.row.warningType === 'LENGTH' ? '长度' : scope.row.warningType === 'THICKNESS' ? '厚度' : '宽度' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="工序" align="center" prop="actionType">
<template slot-scope="scope">
<dict-tag :options="dict.type.action_type" :value="scope.row.actionType" />
</template>
</el-table-column>
<el-table-column label="入场卷号" align="center" prop="coilVo.enterCoilNo" />
<el-table-column label="当前卷号" align="center" prop="coilVo.currentCoilNo" />
<el-table-column label="品质" align="center" prop="coilVo.qualityStatus" />
<el-table-column label="发生时间" align="center" prop="createTime" width="180">
<!-- 如果是今天的标红显示 -->
<template slot-scope="scope">
<span v-if="isToday(scope.row.createTime)" style="color: red;">{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
<span v-else>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="推论值" align="center" prop="theoreticalVal" />
<el-table-column label="实际值" align="center" prop="actualVal" />
<el-table-column label="允许偏差" align="center" prop="allowDeviation">
<template slot-scope="scope">
<span v-if="scope.row.warningType === 'LENGTH'">{{ parseFloat((scope.row.allowDeviation * 100).toFixed(2)) }}%</span>
<span v-else>{{ scope.row.allowDeviation }}</span>
</template>
</el-table-column>
<el-table-column label="实际偏差" align="center" prop="deviationValue" />
<el-table-column label="告警说明" align="center" prop="warningMsg" show-overflow-tooltip />
<el-table-column label="告警状态" align="center" prop="warningStatus">
<template slot-scope="scope">
<div class="status-cell">
<el-tag
:type="scope.row.warningStatus == 0 ? 'danger' : scope.row.warningStatus == 1 ? 'success' : 'info'"
size="small"
effect="plain"
>
{{ scope.row.warningStatus == 0 ? '未处理' : scope.row.warningStatus == 1 ? '已处理' : '已忽略' }}
</el-tag>
</div>
</template>
</el-table-column>
<!-- <el-table-column label="备注" align="center" prop="remark" /> -->
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleHandle(scope.row)">处理</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleIgnore(scope.row)">忽略</el-button>
<el-button size="mini" type="text" icon="el-icon-view" @click="showDetail(scope.row)">详情</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 告警详情弹窗 -->
<el-dialog title="告警详情" :visible.sync="detailDialogVisible" width="1400px" append-to-body>
<div v-if="detailData.warningId" v-loading="detailLoading">
<el-divider content-position="left">钢卷信息</el-divider>
<CoilInfoRender :coilInfo="detailData.coilVo" />
<el-divider content-position="left">告警信息</el-divider>
<el-descriptions :column="2" border size="small">
<el-descriptions-item label="告警类型">
<el-tag :type="detailData.warningType === 'LENGTH' ? 'warning' : detailData.warningType === 'THICKNESS' ? '' : 'success'" size="small" effect="plain">
{{ detailData.warningType === 'LENGTH' ? '长度' : detailData.warningType === 'THICKNESS' ? '厚度' : '宽度' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="工序">
<dict-tag :options="dict.type.action_type" :value="detailData.actionType" />
</el-descriptions-item>
<el-descriptions-item label="告警状态">
<el-tag
:type="detailData.warningStatus == 0 ? 'danger' : detailData.warningStatus == 1 ? 'success' : 'info'"
size="small"
effect="plain"
>
{{ detailData.warningStatus == 0 ? '未处理' : detailData.warningStatus == 1 ? '已处理' : '已忽略' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="发生时间">
{{ parseTime(detailData.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}
</el-descriptions-item>
<el-descriptions-item label="推论值">{{ detailData.theoreticalVal }}</el-descriptions-item>
<el-descriptions-item label="实际值">{{ detailData.actualVal }}</el-descriptions-item>
<el-descriptions-item label="允许偏差">{{ detailData.allowDeviation }}</el-descriptions-item>
<el-descriptions-item label="实际偏差">{{ detailData.deviationValue }}</el-descriptions-item>
<el-descriptions-item label="告警说明" :span="2">{{ detailData.warningMsg }}</el-descriptions-item>
</el-descriptions>
<el-divider v-if="detailData.warningStatus != 0" content-position="left">处理信息</el-divider>
<div v-if="detailData.warningStatus != 0" class="handle-detail">
<div class="handle-remark-body">"{{ detailData.handleRemark }}"</div>
<div class="handle-meta-row">
<span>{{ detailData.handleBy }}</span>
<span class="handle-meta-sep">·</span>
<span>{{ parseTime(detailData.handleTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</div>
</div>
</div>
</el-dialog>
<!-- 处理备注弹窗 -->
<el-dialog title="填写处理备注" :visible.sync="handleRemarkDialogVisible" width="500px" append-to-body>
<el-form ref="handleRemarkForm" :model="handleRemarkForm" :rules="handleRemarkRules" label-width="80px">
<el-form-item label="处理备注" prop="handleRemark">
<el-input v-model="handleRemarkForm.handleRemark" type="textarea" placeholder="请输入处理备注" :rows="4" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="handleRemarkDialogVisible = false"> </el-button>
<el-button type="primary" @click="confirmHandleRemark"> </el-button>
</div>
</el-dialog>
<div>
<el-tabs v-model="activeTab" type="border-card">
<el-tab-pane label="维度告警" name="size">
<MaterialSizeWarning />
</el-tab-pane>
<el-tab-pane label="超期库存" name="time">
<MaterialTimeWarning />
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import { listMaterialWarning, getMaterialWarning, delMaterialWarning, addMaterialWarning, updateMaterialWarning, batchHandleMaterial, batchHandleHistory } from '@/api/wms/materialWarning'
import MaterialSizeWarning from './size.vue'
import MaterialTimeWarning from './time.vue'
export default {
name: 'MaterialWarning',
name: 'MaterialWarningIndex',
components: {
MaterialSizeWarning,
MaterialTimeWarning,
},
data() {
return {
// 按钮loading
buttonLoading: false,
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 钢卷告警表格数据
materialWarningList: [],
// 弹出层标题
title: '',
// 是否显示弹出层
open: false,
// 详情弹窗
detailDialogVisible: false,
detailLoading: false,
detailData: {},
// 处理备注弹窗
handleRemarkDialogVisible: false,
handleRemarkMode: 'single',
handleRemarkTarget: null,
handleRemarkForm: {
handleRemark: ''
},
handleRemarkRules: {
handleRemark: [
{ required: true, message: '处理备注不能为空', trigger: 'blur' }
]
},
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
coilId: undefined,
warningType: 'LENGTH',
theoreticalVal: undefined,
actualVal: undefined,
allowDeviation: undefined,
deviationValue: undefined,
deviationRate: undefined,
warningLevel: undefined,
warningMsg: undefined,
warningStatus: undefined,
handleBy: undefined,
handleTime: undefined,
handleRemark: undefined,
onlyToday: true,
createStartTime: undefined,
createEndTime: undefined
},
// 表单参数
form: {},
// 表单校验
rules: {
warningId: [
{ required: true, message: '告警ID不能为空', trigger: 'blur' }
],
coilId: [
{ required: true, message: '钢卷ID不能为空', trigger: 'blur' }
],
warningType: [
{ required: true, message: '告警类型(LENGTH=长度, THICKNESS=厚度, WIDTH=宽度)不能为空', trigger: 'change' }
],
theoreticalVal: [
{ required: true, message: '理论值不能为空', trigger: 'blur' }
],
actualVal: [
{ required: true, message: '实测值不能为空', trigger: 'blur' }
],
allowDeviation: [
{ required: true, message: '允许偏差(数值/百分比)不能为空', trigger: 'blur' }
],
deviationValue: [
{ required: true, message: '实际偏差值不能为空', trigger: 'blur' }
],
deviationRate: [
{ required: true, message: '偏差率(%)不能为空', trigger: 'blur' }
],
warningLevel: [
{ required: true, message: '告警级别(WARNING/ERROR/CRITICAL)不能为空', trigger: 'blur' }
],
warningMsg: [
{ required: true, message: '告警说明不能为空', trigger: 'blur' }
],
warningStatus: [
{ required: true, message: '告警状态(0=未处理,1=已处理,2=已忽略)不能为空', trigger: 'change' }
],
handleBy: [
{ required: true, message: '处理人不能为空', trigger: 'blur' }
],
handleTime: [
{ required: true, message: '处理时间不能为空', trigger: 'blur' }
],
handleRemark: [
{ required: true, message: '处理备注不能为空', trigger: 'blur' }
],
delFlag: [
{ required: true, message: '删除标志不能为空', trigger: 'blur' }
],
createTime: [
{ required: true, message: '创建时间不能为空', trigger: 'blur' }
],
updateTime: [
{ required: true, message: '更新时间不能为空', trigger: 'blur' }
],
remark: [
{ required: true, message: '备注不能为空', trigger: 'blur' }
]
}
activeTab: 'size',
}
},
dicts: ['action_type'],
created() {
this.getList()
},
methods: {
/** 查询钢卷告警列表 */
getList() {
this.loading = true
if (this.queryParams.onlyToday) {
const today = new Date()
const y = today.getFullYear()
const m = String(today.getMonth() + 1).padStart(2, '0')
const d = String(today.getDate()).padStart(2, '0')
this.queryParams.createStartTime = `${y}-${m}-${d} 00:00:00`
this.queryParams.createEndTime = `${y}-${m}-${d} 23:59:59`
} else {
this.queryParams.createStartTime = undefined
this.queryParams.createEndTime = undefined
}
listMaterialWarning(this.queryParams).then(response => {
this.materialWarningList = response.rows
this.total = response.total
this.loading = false
})
},
/** 忽略按钮操作 */
handleIgnore(row) {
this.$modal.confirm('确认忽略该告警?').then(() => {
this.loading = true
updateMaterialWarning({ ...row, warningStatus: 2, handleBy: this.$store.state.user.name, handleTime: this.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}') }).then(response => {
this.loading = false
this.$modal.msgSuccess('忽略成功')
this.getList()
})
}).catch(() => {})
},
/** 行样式区分长度告警和厚度告警 */
getRowClassName({ row }) {
if (row.warningType === 'LENGTH') return 'warning-row-length'
if (row.warningType === 'THICKNESS') return 'warning-row-thickness'
return ''
},
/** 判断是否是今天的 */
isToday(date) {
const today = new Date()
const year = today.getFullYear()
const month = today.getMonth() + 1
const day = today.getDate()
const dateObj = new Date(date)
const year2 = dateObj.getFullYear()
const month2 = dateObj.getMonth() + 1
const day2 = dateObj.getDate()
return year === year2 && month === month2 && day === day2
},
/** 处理按钮操作 */
handleHandle(row) {
this.handleRemarkMode = 'single'
this.handleRemarkTarget = row
this.handleRemarkForm.handleRemark = ''
this.handleRemarkDialogVisible = true
this.$nextTick(() => {
this.$refs.handleRemarkForm && this.$refs.handleRemarkForm.clearValidate()
})
},
/** 批量处理按钮操作 */
handleBatchProcess() {
const unprocessedIds = this.ids.filter(id => {
const row = this.materialWarningList.find(r => r.warningId === id)
return row && row.warningStatus == 0
})
if (unprocessedIds.length === 0) {
this.$modal.msgWarning('选中的记录中没有未处理的告警')
return
}
this.handleRemarkMode = 'batch'
this.handleRemarkTarget = unprocessedIds
this.handleRemarkForm.handleRemark = ''
this.handleRemarkDialogVisible = true
this.$nextTick(() => {
this.$refs.handleRemarkForm && this.$refs.handleRemarkForm.clearValidate()
})
},
/** 批量忽略按钮操作 */
handleBatchIgnore() {
const unprocessedIds = this.ids.filter(id => {
const row = this.materialWarningList.find(r => r.warningId === id)
return row && row.warningStatus == 0
})
if (unprocessedIds.length === 0) {
this.$modal.msgWarning('选中的记录中没有未处理的告警')
return
}
this.$modal.confirm('确认将选中的 ' + unprocessedIds.length + ' 条告警标记为已忽略?').then(() => {
this.loading = true
return batchHandleMaterial({ warningIds: unprocessedIds, warningStatus: 2 })
}).then(() => {
this.loading = false
this.$modal.msgSuccess('批量忽略成功')
this.getList()
}).catch(() => {
}).finally(() => {
this.loading = false
})
},
checkSelectable(row) {
return row.warningStatus == 0
},
/** 处理历史告警按钮操作(批量标记今天以前的所有记录为已处理) */
handleBatchProcessHistory() {
this.handleRemarkMode = 'history'
this.handleRemarkTarget = null
this.handleRemarkForm.handleRemark = ''
this.handleRemarkDialogVisible = true
this.$nextTick(() => {
this.$refs.handleRemarkForm && this.$refs.handleRemarkForm.clearValidate()
})
},
/** 忽略历史告警按钮操作(批量标记今天以前的所有记录为已忽略) */
handleBatchIgnoreHistory() {
this.$modal.confirm('确认将今天以前的所有告警标记为已忽略?').then(() => {
this.loading = true
return batchHandleHistory({ warningStatus: 2 })
}).then(() => {
this.loading = false
this.$modal.msgSuccess('忽略历史告警成功')
this.getList()
}).catch(() => {
}).finally(() => {
this.loading = false
})
},
// 取消按钮
cancel() {
this.open = false
this.reset()
},
// 表单重置
reset() {
this.form = {
warningId: undefined,
coilId: undefined,
warningType: undefined,
theoreticalVal: undefined,
actualVal: undefined,
allowDeviation: undefined,
deviationValue: undefined,
deviationRate: undefined,
warningLevel: undefined,
warningMsg: undefined,
warningStatus: undefined,
handleBy: undefined,
handleTime: undefined,
handleRemark: undefined,
delFlag: undefined,
createTime: undefined,
createBy: undefined,
updateTime: undefined,
updateBy: undefined,
remark: undefined
}
this.resetForm('form')
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm')
this.handleQuery()
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.warningId)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
this.open = true
this.title = '添加钢卷告警'
},
/** 修改按钮操作 */
handleUpdate(row) {
this.loading = true
this.reset()
const warningId = row.warningId || this.ids
getMaterialWarning(warningId).then(response => {
this.loading = false
this.form = response.data
this.open = true
this.title = '修改钢卷告警'
})
},
/** 提交按钮 */
submitForm() {
this.$refs['form'].validate(valid => {
if (valid) {
this.buttonLoading = true
if (this.form.warningId != null) {
updateMaterialWarning(this.form).then(response => {
this.$modal.msgSuccess('修改成功')
this.open = false
this.getList()
}).finally(() => {
this.buttonLoading = false
})
} else {
addMaterialWarning(this.form).then(response => {
this.$modal.msgSuccess('新增成功')
this.open = false
this.getList()
}).finally(() => {
this.buttonLoading = false
})
}
}
})
},
/** 详情按钮操作 */
showDetail(row) {
this.detailDialogVisible = true
// this.detailLoading = true
this.detailData = row;
// getMaterialWarning(row.warningId).then(response => {
// this.detailData = response.data
// this.detailLoading = false
// }).catch(() => {
// this.detailLoading = false
// })
},
/** 确认填写处理备注 */
confirmHandleRemark() {
this.$refs.handleRemarkForm.validate(valid => {
if (!valid) return
this.loading = true
const handleBy = this.$store.state.user.name
const handleTime = this.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')
const handleRemark = this.handleRemarkForm.handleRemark
if (this.handleRemarkMode === 'single') {
const row = this.handleRemarkTarget
updateMaterialWarning({
...row,
warningStatus: 1,
handleBy,
handleTime,
handleRemark
}).then(() => {
this.loading = false
this.handleRemarkDialogVisible = false
this.$modal.msgSuccess('处理成功')
this.getList()
}).catch(() => {
this.loading = false
})
} else if (this.handleRemarkMode === 'batch') {
batchHandleMaterial({
warningIds: this.handleRemarkTarget,
warningStatus: 1,
handleBy,
handleTime,
handleRemark
}).then(() => {
this.loading = false
this.handleRemarkDialogVisible = false
this.$modal.msgSuccess('批量处理成功')
this.getList()
}).catch(() => {
this.loading = false
})
} else if (this.handleRemarkMode === 'history') {
batchHandleHistory({
warningStatus: 1,
handleBy,
handleTime,
handleRemark
}).then(() => {
this.loading = false
this.handleRemarkDialogVisible = false
this.$modal.msgSuccess('处理历史告警成功')
this.getList()
}).catch(() => {
this.loading = false
})
}
})
},
/** 删除按钮操作 */
handleDelete(row) {
const warningIds = row.warningId || this.ids
this.$modal.confirm('是否确认删除钢卷告警编号为"' + warningIds + '"的数据项?').then(() => {
this.loading = true
return delMaterialWarning(warningIds)
}).then(() => {
this.loading = false
this.getList()
this.$modal.msgSuccess('删除成功')
}).catch(() => {
}).finally(() => {
this.loading = false
})
},
/** 导出按钮操作 */
handleExport() {
this.download('wms/materialWarning/export', {
...this.queryParams
}, `materialWarning_${new Date().getTime()}.xlsx`)
}
}
}
</script>
<style scoped>
.el-table .warning-row-length {
background-color: #fdf6ec;
}
.el-table .warning-row-thickness {
background-color: #ecf5ff;
}
.status-cell {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
padding: 4px 0;
}
.status-detail {
width: 100%;
text-align: left;
}
.status-remark {
font-size: 13px;
color: #303133;
line-height: 1.5;
margin-bottom: 4px;
font-weight: 500;
word-break: break-all;
}
.status-meta {
font-size: 11px;
color: #909399;
letter-spacing: 0.3px;
}
.handle-detail {
padding: 12px 0;
}
.handle-remark-body {
font-size: 15px;
color: #303133;
line-height: 1.8;
margin-bottom: 12px;
padding: 12px 16px;
background: #fafafa;
border-radius: 0 6px 6px 0;
font-style: italic;
letter-spacing: 0.5px;
}
.handle-meta-row {
font-size: 13px;
color: #909399;
display: flex;
align-items: center;
gap: 8px;
justify-content: flex-start;
letter-spacing: 0.4px;
}
.handle-meta-sep {
color: #c0c4cc;
font-weight: bold;
}
</style>

View File

@@ -0,0 +1,716 @@
<template>
<div>
<el-form v-show="showSearch" ref="queryForm" :model="queryParams" size="small" :inline="true" label-width="68px">
<el-form-item label="告警类型" prop="warningType">
<el-radio-group v-model="queryParams.warningType" @change="handleQuery">
<el-radio-button label="LENGTH">长度告警</el-radio-button>
<el-radio-button label="THICKNESS">厚度告警</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="告警状态" prop="warningStatus">
<el-select
v-model="queryParams.warningStatus"
placeholder="请选择告警状态"
clearable
@change="handleQuery"
>
<el-option label="未处理" value="0" />
<el-option label="已处理" value="1" />
<el-option label="已忽略" value="2" />
</el-select>
</el-form-item>
<el-form-item label="处理人" prop="handleBy">
<el-input v-model="queryParams.handleBy" placeholder="请输入处理人" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="只看今天" prop="onlyToday">
<el-switch v-model="queryParams.onlyToday" @change="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
<!-- <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button> -->
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-edit"
size="mini"
:disabled="multiple"
@click="handleBatchProcess"
>批量处理</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-edit"
size="mini"
:disabled="multiple"
@click="handleBatchIgnore"
>批量忽略</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-finished"
size="mini"
@click="handleBatchProcessHistory"
>处理历史告警</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
@click="handleBatchIgnoreHistory"
>忽略历史告警</el-button>
</el-col>
</el-row>
<el-row :gutter="10" class="mb8" style="margin-bottom: 16px;">
<el-col :span="24">
<el-radio-group v-model="queryParams.actionType" @change="handleQuery" size="mini">
<el-radio-button :key="all" :label="''">全部</el-radio-button>
<el-radio-button v-for="item in dict.type.action_type" :key="item.value" :label="item.value">{{ item.label }}</el-radio-button>
</el-radio-group>
</el-col>
</el-row>
<el-table v-loading="loading" :data="materialWarningList" :row-class-name="getRowClassName" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" :selectable="checkSelectable" />
<!-- <el-table-column label="钢卷ID" align="center" prop="coilId" /> -->
<el-table-column label="告警类型" align="center" prop="warningType" width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.warningType === 'LENGTH' ? 'warning' : scope.row.warningType === 'THICKNESS' ? '' : 'success'" size="small" effect="plain">
{{ scope.row.warningType === 'LENGTH' ? '长度' : scope.row.warningType === 'THICKNESS' ? '厚度' : '宽度' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="工序" align="center" prop="actionType">
<template slot-scope="scope">
<dict-tag :options="dict.type.action_type" :value="scope.row.actionType" />
</template>
</el-table-column>
<el-table-column label="入场卷号" align="center" prop="coilVo.enterCoilNo" />
<el-table-column label="当前卷号" align="center" prop="coilVo.currentCoilNo" />
<el-table-column label="品质" align="center" prop="coilVo.qualityStatus" />
<el-table-column label="发生时间" align="center" prop="createTime" width="180">
<!-- 如果是今天的标红显示 -->
<template slot-scope="scope">
<span v-if="isToday(scope.row.createTime)" style="color: red;">{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
<span v-else>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="推论值" align="center" prop="theoreticalVal" />
<el-table-column label="实际值" align="center" prop="actualVal" />
<el-table-column label="允许偏差" align="center" prop="allowDeviation">
<template slot-scope="scope">
<span v-if="scope.row.warningType === 'LENGTH'">{{ parseFloat((scope.row.allowDeviation * 100).toFixed(2)) }}%</span>
<span v-else>{{ scope.row.allowDeviation }}</span>
</template>
</el-table-column>
<el-table-column label="实际偏差" align="center" prop="deviationValue" />
<el-table-column label="告警说明" align="center" prop="warningMsg" show-overflow-tooltip />
<el-table-column label="告警状态" align="center" prop="warningStatus">
<template slot-scope="scope">
<div class="status-cell">
<el-tag
:type="scope.row.warningStatus == 0 ? 'danger' : scope.row.warningStatus == 1 ? 'success' : 'info'"
size="small"
effect="plain"
>
{{ scope.row.warningStatus == 0 ? '未处理' : scope.row.warningStatus == 1 ? '已处理' : '已忽略' }}
</el-tag>
</div>
</template>
</el-table-column>
<!-- <el-table-column label="备注" align="center" prop="remark" /> -->
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleHandle(scope.row)">处理</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleIgnore(scope.row)">忽略</el-button>
<el-button size="mini" type="text" icon="el-icon-view" @click="showDetail(scope.row)">详情</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 告警详情弹窗 -->
<el-dialog title="告警详情" :visible.sync="detailDialogVisible" width="1400px" append-to-body>
<div v-if="detailData.warningId" v-loading="detailLoading">
<el-divider content-position="left">钢卷信息</el-divider>
<CoilInfoRender :coilInfo="detailData.coilVo" />
<el-divider content-position="left">告警信息</el-divider>
<el-descriptions :column="2" border size="small">
<el-descriptions-item label="告警类型">
<el-tag :type="detailData.warningType === 'LENGTH' ? 'warning' : detailData.warningType === 'THICKNESS' ? '' : 'success'" size="small" effect="plain">
{{ detailData.warningType === 'LENGTH' ? '长度' : detailData.warningType === 'THICKNESS' ? '厚度' : '宽度' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="工序">
<dict-tag :options="dict.type.action_type" :value="detailData.actionType" />
</el-descriptions-item>
<el-descriptions-item label="告警状态">
<el-tag
:type="detailData.warningStatus == 0 ? 'danger' : detailData.warningStatus == 1 ? 'success' : 'info'"
size="small"
effect="plain"
>
{{ detailData.warningStatus == 0 ? '未处理' : detailData.warningStatus == 1 ? '已处理' : '已忽略' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="发生时间">
{{ parseTime(detailData.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}
</el-descriptions-item>
<el-descriptions-item label="推论值">{{ detailData.theoreticalVal }}</el-descriptions-item>
<el-descriptions-item label="实际值">{{ detailData.actualVal }}</el-descriptions-item>
<el-descriptions-item label="允许偏差">{{ detailData.allowDeviation }}</el-descriptions-item>
<el-descriptions-item label="实际偏差">{{ detailData.deviationValue }}</el-descriptions-item>
<el-descriptions-item label="告警说明" :span="2">{{ detailData.warningMsg }}</el-descriptions-item>
</el-descriptions>
<el-divider v-if="detailData.warningStatus != 0" content-position="left">处理信息</el-divider>
<div v-if="detailData.warningStatus != 0" class="handle-detail">
<div class="handle-remark-body">"{{ detailData.handleRemark }}"</div>
<div class="handle-meta-row">
<span>{{ detailData.handleBy }}</span>
<span class="handle-meta-sep">·</span>
<span>{{ parseTime(detailData.handleTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</div>
</div>
</div>
</el-dialog>
<!-- 处理备注弹窗 -->
<el-dialog title="填写处理备注" :visible.sync="handleRemarkDialogVisible" width="500px" append-to-body>
<el-form ref="handleRemarkForm" :model="handleRemarkForm" :rules="handleRemarkRules" label-width="80px">
<el-form-item label="处理备注" prop="handleRemark">
<el-input v-model="handleRemarkForm.handleRemark" type="textarea" placeholder="请输入处理备注" :rows="4" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="handleRemarkDialogVisible = false"> </el-button>
<el-button type="primary" @click="confirmHandleRemark"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listMaterialWarning, getMaterialWarning, delMaterialWarning, addMaterialWarning, updateMaterialWarning, batchHandleMaterial, batchHandleHistory } from '@/api/wms/materialWarning'
export default {
name: 'MaterialSizeWarning',
data() {
return {
// 按钮loading
buttonLoading: false,
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 钢卷告警表格数据
materialWarningList: [],
// 弹出层标题
title: '',
// 是否显示弹出层
open: false,
// 详情弹窗
detailDialogVisible: false,
detailLoading: false,
detailData: {},
// 处理备注弹窗
handleRemarkDialogVisible: false,
handleRemarkMode: 'single',
handleRemarkTarget: null,
handleRemarkForm: {
handleRemark: ''
},
handleRemarkRules: {
handleRemark: [
{ required: true, message: '处理备注不能为空', trigger: 'blur' }
]
},
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
coilId: undefined,
warningType: 'LENGTH',
theoreticalVal: undefined,
actualVal: undefined,
allowDeviation: undefined,
deviationValue: undefined,
deviationRate: undefined,
warningLevel: undefined,
warningMsg: undefined,
warningStatus: undefined,
handleBy: undefined,
handleTime: undefined,
handleRemark: undefined,
onlyToday: true,
createStartTime: undefined,
createEndTime: undefined
},
// 表单参数
form: {},
// 表单校验
rules: {
warningId: [
{ required: true, message: '告警ID不能为空', trigger: 'blur' }
],
coilId: [
{ required: true, message: '钢卷ID不能为空', trigger: 'blur' }
],
warningType: [
{ required: true, message: '告警类型(LENGTH=长度, THICKNESS=厚度, WIDTH=宽度)不能为空', trigger: 'change' }
],
theoreticalVal: [
{ required: true, message: '理论值不能为空', trigger: 'blur' }
],
actualVal: [
{ required: true, message: '实测值不能为空', trigger: 'blur' }
],
allowDeviation: [
{ required: true, message: '允许偏差(数值/百分比)不能为空', trigger: 'blur' }
],
deviationValue: [
{ required: true, message: '实际偏差值不能为空', trigger: 'blur' }
],
deviationRate: [
{ required: true, message: '偏差率(%)不能为空', trigger: 'blur' }
],
warningLevel: [
{ required: true, message: '告警级别(WARNING/ERROR/CRITICAL)不能为空', trigger: 'blur' }
],
warningMsg: [
{ required: true, message: '告警说明不能为空', trigger: 'blur' }
],
warningStatus: [
{ required: true, message: '告警状态(0=未处理,1=已处理,2=已忽略)不能为空', trigger: 'change' }
],
handleBy: [
{ required: true, message: '处理人不能为空', trigger: 'blur' }
],
handleTime: [
{ required: true, message: '处理时间不能为空', trigger: 'blur' }
],
handleRemark: [
{ required: true, message: '处理备注不能为空', trigger: 'blur' }
],
delFlag: [
{ required: true, message: '删除标志不能为空', trigger: 'blur' }
],
createTime: [
{ required: true, message: '创建时间不能为空', trigger: 'blur' }
],
updateTime: [
{ required: true, message: '更新时间不能为空', trigger: 'blur' }
],
remark: [
{ required: true, message: '备注不能为空', trigger: 'blur' }
]
}
}
},
dicts: ['action_type'],
created() {
this.getList()
},
methods: {
/** 查询钢卷告警列表 */
getList() {
this.loading = true
if (this.queryParams.onlyToday) {
const today = new Date()
const y = today.getFullYear()
const m = String(today.getMonth() + 1).padStart(2, '0')
const d = String(today.getDate()).padStart(2, '0')
this.queryParams.createStartTime = `${y}-${m}-${d} 00:00:00`
this.queryParams.createEndTime = `${y}-${m}-${d} 23:59:59`
} else {
this.queryParams.createStartTime = undefined
this.queryParams.createEndTime = undefined
}
listMaterialWarning(this.queryParams).then(response => {
this.materialWarningList = response.rows
this.total = response.total
this.loading = false
})
},
/** 忽略按钮操作 */
handleIgnore(row) {
this.$modal.confirm('确认忽略该告警?').then(() => {
this.loading = true
updateMaterialWarning({ ...row, warningStatus: 2, handleBy: this.$store.state.user.name, handleTime: this.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}') }).then(response => {
this.loading = false
this.$modal.msgSuccess('忽略成功')
this.getList()
})
}).catch(() => {})
},
/** 行样式区分长度告警和厚度告警 */
getRowClassName({ row }) {
if (row.warningType === 'LENGTH') return 'warning-row-length'
if (row.warningType === 'THICKNESS') return 'warning-row-thickness'
return ''
},
/** 判断是否是今天的 */
isToday(date) {
const today = new Date()
const year = today.getFullYear()
const month = today.getMonth() + 1
const day = today.getDate()
const dateObj = new Date(date)
const year2 = dateObj.getFullYear()
const month2 = dateObj.getMonth() + 1
const day2 = dateObj.getDate()
return year === year2 && month === month2 && day === day2
},
/** 处理按钮操作 */
handleHandle(row) {
this.handleRemarkMode = 'single'
this.handleRemarkTarget = row
this.handleRemarkForm.handleRemark = ''
this.handleRemarkDialogVisible = true
this.$nextTick(() => {
this.$refs.handleRemarkForm && this.$refs.handleRemarkForm.clearValidate()
})
},
/** 批量处理按钮操作 */
handleBatchProcess() {
const unprocessedIds = this.ids.filter(id => {
const row = this.materialWarningList.find(r => r.warningId === id)
return row && row.warningStatus == 0
})
if (unprocessedIds.length === 0) {
this.$modal.msgWarning('选中的记录中没有未处理的告警')
return
}
this.handleRemarkMode = 'batch'
this.handleRemarkTarget = unprocessedIds
this.handleRemarkForm.handleRemark = ''
this.handleRemarkDialogVisible = true
this.$nextTick(() => {
this.$refs.handleRemarkForm && this.$refs.handleRemarkForm.clearValidate()
})
},
/** 批量忽略按钮操作 */
handleBatchIgnore() {
const unprocessedIds = this.ids.filter(id => {
const row = this.materialWarningList.find(r => r.warningId === id)
return row && row.warningStatus == 0
})
if (unprocessedIds.length === 0) {
this.$modal.msgWarning('选中的记录中没有未处理的告警')
return
}
this.$modal.confirm('确认将选中的 ' + unprocessedIds.length + ' 条告警标记为已忽略?').then(() => {
this.loading = true
return batchHandleMaterial({ warningIds: unprocessedIds, warningStatus: 2 })
}).then(() => {
this.loading = false
this.$modal.msgSuccess('批量忽略成功')
this.getList()
}).catch(() => {
}).finally(() => {
this.loading = false
})
},
checkSelectable(row) {
return row.warningStatus == 0
},
/** 处理历史告警按钮操作(批量标记今天以前的所有记录为已处理) */
handleBatchProcessHistory() {
this.handleRemarkMode = 'history'
this.handleRemarkTarget = null
this.handleRemarkForm.handleRemark = ''
this.handleRemarkDialogVisible = true
this.$nextTick(() => {
this.$refs.handleRemarkForm && this.$refs.handleRemarkForm.clearValidate()
})
},
/** 忽略历史告警按钮操作(批量标记今天以前的所有记录为已忽略) */
handleBatchIgnoreHistory() {
this.$modal.confirm('确认将今天以前的所有告警标记为已忽略?').then(() => {
this.loading = true
return batchHandleHistory({ warningStatus: 2 })
}).then(() => {
this.loading = false
this.$modal.msgSuccess('忽略历史告警成功')
this.getList()
}).catch(() => {
}).finally(() => {
this.loading = false
})
},
// 取消按钮
cancel() {
this.open = false
this.reset()
},
// 表单重置
reset() {
this.form = {
warningId: undefined,
coilId: undefined,
warningType: undefined,
theoreticalVal: undefined,
actualVal: undefined,
allowDeviation: undefined,
deviationValue: undefined,
deviationRate: undefined,
warningLevel: undefined,
warningMsg: undefined,
warningStatus: undefined,
handleBy: undefined,
handleTime: undefined,
handleRemark: undefined,
delFlag: undefined,
createTime: undefined,
createBy: undefined,
updateTime: undefined,
updateBy: undefined,
remark: undefined
}
this.resetForm('form')
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm')
this.handleQuery()
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.warningId)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
this.open = true
this.title = '添加钢卷告警'
},
/** 修改按钮操作 */
handleUpdate(row) {
this.loading = true
this.reset()
const warningId = row.warningId || this.ids
getMaterialWarning(warningId).then(response => {
this.loading = false
this.form = response.data
this.open = true
this.title = '修改钢卷告警'
})
},
/** 提交按钮 */
submitForm() {
this.$refs['form'].validate(valid => {
if (valid) {
this.buttonLoading = true
if (this.form.warningId != null) {
updateMaterialWarning(this.form).then(response => {
this.$modal.msgSuccess('修改成功')
this.open = false
this.getList()
}).finally(() => {
this.buttonLoading = false
})
} else {
addMaterialWarning(this.form).then(response => {
this.$modal.msgSuccess('新增成功')
this.open = false
this.getList()
}).finally(() => {
this.buttonLoading = false
})
}
}
})
},
/** 详情按钮操作 */
showDetail(row) {
this.detailDialogVisible = true
// this.detailLoading = true
this.detailData = row;
// getMaterialWarning(row.warningId).then(response => {
// this.detailData = response.data
// this.detailLoading = false
// }).catch(() => {
// this.detailLoading = false
// })
},
/** 确认填写处理备注 */
confirmHandleRemark() {
this.$refs.handleRemarkForm.validate(valid => {
if (!valid) return
this.loading = true
const handleBy = this.$store.state.user.name
const handleTime = this.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')
const handleRemark = this.handleRemarkForm.handleRemark
if (this.handleRemarkMode === 'single') {
const row = this.handleRemarkTarget
updateMaterialWarning({
...row,
warningStatus: 1,
handleBy,
handleTime,
handleRemark
}).then(() => {
this.loading = false
this.handleRemarkDialogVisible = false
this.$modal.msgSuccess('处理成功')
this.getList()
}).catch(() => {
this.loading = false
})
} else if (this.handleRemarkMode === 'batch') {
batchHandleMaterial({
warningIds: this.handleRemarkTarget,
warningStatus: 1,
handleBy,
handleTime,
handleRemark
}).then(() => {
this.loading = false
this.handleRemarkDialogVisible = false
this.$modal.msgSuccess('批量处理成功')
this.getList()
}).catch(() => {
this.loading = false
})
} else if (this.handleRemarkMode === 'history') {
batchHandleHistory({
warningStatus: 1,
handleBy,
handleTime,
handleRemark
}).then(() => {
this.loading = false
this.handleRemarkDialogVisible = false
this.$modal.msgSuccess('处理历史告警成功')
this.getList()
}).catch(() => {
this.loading = false
})
}
})
},
/** 删除按钮操作 */
handleDelete(row) {
const warningIds = row.warningId || this.ids
this.$modal.confirm('是否确认删除钢卷告警编号为"' + warningIds + '"的数据项?').then(() => {
this.loading = true
return delMaterialWarning(warningIds)
}).then(() => {
this.loading = false
this.getList()
this.$modal.msgSuccess('删除成功')
}).catch(() => {
}).finally(() => {
this.loading = false
})
},
/** 导出按钮操作 */
handleExport() {
this.download('wms/materialWarning/export', {
...this.queryParams
}, `materialWarning_${new Date().getTime()}.xlsx`)
}
}
}
</script>
<style scoped>
.el-table .warning-row-length {
background-color: #fdf6ec;
}
.el-table .warning-row-thickness {
background-color: #ecf5ff;
}
.status-cell {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
padding: 4px 0;
}
.status-detail {
width: 100%;
text-align: left;
}
.status-remark {
font-size: 13px;
color: #303133;
line-height: 1.5;
margin-bottom: 4px;
font-weight: 500;
word-break: break-all;
}
.status-meta {
font-size: 11px;
color: #909399;
letter-spacing: 0.3px;
}
.handle-detail {
padding: 12px 0;
}
.handle-remark-body {
font-size: 15px;
color: #303133;
line-height: 1.8;
margin-bottom: 12px;
padding: 12px 16px;
background: #fafafa;
border-radius: 0 6px 6px 0;
font-style: italic;
letter-spacing: 0.5px;
}
.handle-meta-row {
font-size: 13px;
color: #909399;
display: flex;
align-items: center;
gap: 8px;
justify-content: flex-start;
letter-spacing: 0.4px;
}
.handle-meta-sep {
color: #c0c4cc;
font-weight: bold;
}
</style>

View File

@@ -0,0 +1,356 @@
<template>
<div>
<el-form ref="queryForm" :model="queryParams" size="small" :inline="true" label-width="60px">
<el-form-item label="物料类型" prop="materialType">
<el-select v-model="queryParams.materialType" placeholder="请选择物料类型" clearable @change="handleMaterialTypeChange">
<el-option label="成品" value="成品"></el-option>
<el-option label="原料" value="原料"></el-option>
</el-select>
</el-form-item>
<template v-if="queryParams.itemType">
<el-form-item label="产品名称" prop="itemName">
<muti-select v-model="queryParams.itemName" :options="dict.type.coil_itemname" placeholder="请选择物料" clearable />
</el-form-item>
<el-form-item label="规格" prop="itemSpecification">
<memo-input v-model="queryParams.itemSpecification" storageKey="coilSpec" placeholder="请选择规格" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="材质" prop="itemMaterial">
<muti-select v-model="queryParams.itemMaterial" :options="dict.type.coil_material" placeholder="请选择材质" clearable />
</el-form-item>
<el-form-item label="表面处理">
<el-input v-model="queryParams.itemSurfaceTreatmentDesc" placeholder="请输入表面处理" clearable size="small" />
</el-form-item>
</template>
<el-form-item label="逻辑库位" prop="warehouseId">
<warehouse-select v-model="queryParams.warehouseId" placeholder="请选择逻辑库位"
style="display: inline-block;" clearable />
</el-form-item>
<el-form-item label="入场卷号" prop="enterCoilNo">
<el-input v-model="queryParams.enterCoilNo" placeholder="请输入入场钢卷号" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="当前卷号" prop="currentCoilNo">
<el-input v-model="queryParams.currentCoilNo" placeholder="请输入当前钢卷号" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="厂家" prop="itemManufacturer">
<muti-select v-model="queryParams.itemManufacturer" :options="dict.type.coil_manufacturer" placeholder="请选择厂家" clearable />
</el-form-item>
<el-form-item label="包装" prop="packagingRequirement">
<el-select v-model="queryParams.packagingRequirement" placeholder="请选择包装" clearable style="width: 100%">
<el-option label="裸包" value="裸包" />
<el-option label="普包" value="普包" />
<el-option label="简包" value="简包" />
</el-select>
</el-form-item>
<el-form-item label="品质">
<muti-select v-model="queryParams.qualityStatusCsv" :options="dict.type.coil_quality_status" placeholder="请选择品质" clearable />
</el-form-item>
<el-form-item label="业务员" prop="saleName">
<el-input v-model="queryParams.saleName" placeholder="请输入业务员" clearable size="small" />
</el-form-item>
<el-form-item label="合同号" prop="contractNo">
<el-input v-model="queryParams.contractNo" placeholder="请输入合同号" clearable size="small" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="queryParams.remark" placeholder="请输入备注" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="超期天数">
<el-input-number v-model="overdueDays" :min="1" :max="3650" size="small" @change="handleQuery" />
<span style="margin-left: 6px; color: #909399; font-size: 12px;">创建超过此天数的钢卷</span>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="24">
<span style="color: #909399; font-size: 13px;">
查询条件创建时间早于 <b>{{ queryParams.byCreateTimeEnd }}</b>且当前仍在库
</span>
</el-col>
</el-row>
<KLPTable v-loading="loading" :data="coilList" :height="'calc(100vh - 320px)'" border>
<el-table-column label="入场钢卷号" align="center" prop="enterCoilNo" min-width="120">
<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" min-width="120">
<template slot-scope="scope">
<current-coil-no :current-coil-no="scope.row.currentCoilNo"></current-coil-no>
</template>
</el-table-column>
<el-table-column label="净重" width="80" align="center" prop="netWeight" />
<el-table-column label="逻辑库位" width="120" align="center" prop="warehouseName" />
<el-table-column label="实际库区" width="100" align="center" prop="actualWarehouseName" />
<el-table-column label="规格" prop="specification" width="100" />
<el-table-column label="物料" prop="itemName" width="100" />
<el-table-column label="材质" prop="material" width="100" />
<el-table-column label="厂家" prop="manufacturer" width="100" />
<el-table-column label="品质" prop="qualityStatus" width="80">
<template slot-scope="scope">
<dict-tag v-if="scope.row.qualityStatus" :options="dict.type.coil_quality_status"
:value="scope.row.qualityStatus"></dict-tag>
<span v-else>暂未判级</span>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="160" />
<el-table-column label="已存放天数" align="center" width="100">
<template slot-scope="scope">
<el-tag :type="getOverdueTagType(scope.row.createTime)" size="small">
{{ calcOverdueDays(scope.row.createTime) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="业务员" align="center" prop="saleName" width="80" />
<el-table-column label="合同号" align="center" prop="contractNo" width="120" />
<el-table-column label="备注" prop="remark" show-overflow-tooltip min-width="120" />
<el-table-column prop="action" label="操作" align="center" width="120" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreviewLabel(scope.row)">
预览标签
</el-button>
<el-button size="mini" type="text" icon="el-icon-printer" @click="handlePrintLabel(scope.row)">
打印标签
</el-button>
</template>
</el-table-column>
</KLPTable>
<div v-show="total > 0" style="display: flex; justify-content: space-between; align-items: center; padding-top: 10px;">
<span>
总净重{{ statistics.total_net_weight || 0 }}t &nbsp;|&nbsp; {{ total }}
</span>
<pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="getList" />
</div>
<!-- 标签预览弹窗 -->
<el-dialog title="标签预览" :visible.sync="labelRender.visible" append-to-body>
<label-render :content="labelRender.data" :labelType="labelRender.type" />
</el-dialog>
<label-render ref="labelRender" v-show="false" :content="labelRender.data" :labelType="labelRender.type" />
</div>
</template>
<script>
import { listMaterialCoil } from "@/api/wms/coil";
import { getCoilStatisticsList } from "@/api/wms/coil";
import WarehouseSelect from "@/components/KLPService/WarehouseSelect";
import ProductInfo from "@/components/KLPService/Renderer/ProductInfo";
import RawMaterialInfo from "@/components/KLPService/Renderer/RawMaterialInfo";
import CoilNo from "@/components/KLPService/Renderer/CoilNo.vue";
import MutiSelect from "@/components/MutiSelect";
import MemoInput from "@/components/MemoInput";
import ActualWarehouseSelect from "@/components/KLPService/ActualWarehouseSelect";
import LabelRender from '../panels/LabelRender/index.vue'
import { getCoilTagPrintType } from '@/views/wms/coil/js/coilPrint';
function formatDateTime(d) {
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return `${y}-${m}-${day} 00:00:00`;
}
export default {
name: "MaterialCoilTimeWarning",
components: {
WarehouseSelect,
ProductInfo,
RawMaterialInfo,
CoilNo,
MutiSelect,
MemoInput,
ActualWarehouseSelect,
LabelRender,
},
dicts: ['coil_itemname', 'coil_material', 'coil_manufacturer', 'coil_quality_status'],
data() {
const twentyDaysAgo = new Date();
twentyDaysAgo.setDate(twentyDaysAgo.getDate() - 20);
return {
loading: false,
total: 0,
coilList: [],
statistics: {},
overdueDays: 20,
queryParams: {
pageNum: 1,
pageSize: 50,
dataType: 1,
status: 0,
materialType: '成品',
itemType: 'product',
selectType: 'product',
byCreateTimeStart: '1970-01-01 00:00:00',
byCreateTimeEnd: formatDateTime(twentyDaysAgo),
warehouseId: undefined,
itemName: undefined,
enterCoilNo: undefined,
currentCoilNo: undefined,
itemSpecification: undefined,
itemMaterial: undefined,
itemManufacturer: undefined,
itemSurfaceTreatmentDesc: undefined,
trimmingRequirement: undefined,
packagingRequirement: undefined,
qualityStatusCsv: undefined,
actualWarehouseId: undefined,
saleName: undefined,
contractNo: undefined,
remark: undefined,
},
labelRender: {
visible: false,
data: {},
type: '2'
},
};
},
created() {
this.getList();
},
methods: {
/** 重新计算截止日期并查询 */
getList() {
this.loading = true;
const endDate = new Date();
endDate.setDate(endDate.getDate() - this.overdueDays);
this.queryParams.byCreateTimeEnd = formatDateTime(endDate);
this.queryParams.selectType = this.queryParams.itemType;
const { orderBy, materialType, itemType, ...statisticQuery } = this.queryParams;
listMaterialCoil(this.queryParams).then(response => {
this.coilList = response.rows;
this.total = response.total;
this.loading = false;
}).catch(() => {
this.loading = false;
});
getCoilStatisticsList(statisticQuery).then(res => {
this.statistics = res.data || {};
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
handleMaterialTypeChange(value) {
if (value === '原料') {
this.queryParams.itemType = 'raw_material';
this.queryParams.selectType = 'raw_material';
} else if (value === '成品') {
this.queryParams.itemType = 'product';
this.queryParams.selectType = 'product';
} else {
this.queryParams.itemType = undefined;
this.queryParams.selectType = undefined;
}
this.handleQuery();
},
resetQuery() {
this.queryParams = {
pageNum: 1,
pageSize: 50,
dataType: 1,
status: 0,
materialType: '成品',
itemType: 'product',
selectType: 'product',
byCreateTimeStart: '1970-01-01 00:00:00',
byCreateTimeEnd: '',
warehouseId: undefined,
itemName: undefined,
enterCoilNo: undefined,
currentCoilNo: undefined,
itemSpecification: undefined,
itemMaterial: undefined,
itemManufacturer: undefined,
itemSurfaceTreatmentDesc: undefined,
trimmingRequirement: undefined,
packagingRequirement: undefined,
qualityStatusCsv: undefined,
actualWarehouseId: undefined,
saleName: undefined,
contractNo: undefined,
remark: undefined,
orderBy: true,
};
this.overdueDays = 20;
this.handleQuery();
},
/** 计算已存放天数 */
calcOverdueDays(createTime) {
if (!createTime) return '-';
const created = new Date(createTime);
const now = new Date();
return Math.floor((now - created) / (1000 * 60 * 60 * 24));
},
getOverdueTagType(createTime) {
const days = this.calcOverdueDays(createTime);
if (days >= 60) return 'danger';
if (days >= 30) return 'warning';
return '';
},
/** 预览标签 */
handlePreviewLabel(row) {
this.labelRender.data = row;
this.labelRender.type = getCoilTagPrintType(row);
this.labelRender.visible = true;
},
/** 打印标签 */
handlePrintLabel(row) {
this.labelRender.data = row;
this.labelRender.type = getCoilTagPrintType(row);
this.$nextTick(() => {
const el = this.$refs.labelRender && this.$refs.labelRender.$el;
if (el) {
el.style.display = 'block';
window.print();
el.style.display = 'none';
}
});
},
handleExport() {
this.download('wms/materialCoil/export', {
...this.queryParams
}, `超期库存_${new Date().getTime()}.xlsx`)
},
}
};
</script>