前端新增停机快捷录入,标签打印能力
This commit is contained in:
@@ -83,4 +83,15 @@ export function getStoppageSummary(data) {
|
||||
data,
|
||||
url: '/api/stoppage/calc'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询停机类型
|
||||
* @returns
|
||||
*/
|
||||
export function listStoppageTypes() {
|
||||
return l2Request({
|
||||
method: 'get',
|
||||
url: '/api/stoppage/types'
|
||||
})
|
||||
}
|
||||
@@ -32,6 +32,8 @@
|
||||
<span v-if="currentRow.exitMatId" style="margin-left: 10px; border: 1px solid #d4d4d4; border-radius: 2px; padding: 5px;">
|
||||
<el-button size="mini" type="text" icon="el-icon-view"
|
||||
@click.stop="handlePrint(currentRow)">Print</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-printer"
|
||||
@click.stop="handlePrintTag(currentRow)">Print Tag</el-button>
|
||||
<!-- 打印 -->
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click.stop="handleEdit(currentRow)">Edit</el-button>
|
||||
<!-- 编辑 -->
|
||||
@@ -155,6 +157,17 @@
|
||||
<!-- 质保书(单卷) -->
|
||||
<quality-certificate :detail="formData"></quality-certificate>
|
||||
</el-dialog>
|
||||
|
||||
<!-- Print Tag Dialog -->
|
||||
<el-dialog title="Print Tag" :visible.sync="printTagDialogVisible" width="520px" :close-on-click-modal="false" append-to-body>
|
||||
<div ref="printTagRef" class="material-label-container">
|
||||
<MaterialTag v-if="printTagContent" :content="printTagContent" :paper-width-mm="100" :paper-height-mm="80" />
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="printTagDialogVisible = false">Close</el-button>
|
||||
<el-button type="primary" @click="doPrintTag" :disabled="!printTagContent">Print</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -166,6 +179,7 @@ import PdoDataCorrection from './components/DataCorrection.vue'
|
||||
import PdoLabelPrint from './components/LabelPrint.vue'
|
||||
import PdoSummary from './components/PdoSummary.vue'
|
||||
import QualityCertificate from './components/QualityCertificate.vue'
|
||||
import MaterialTag from '../plan/components/MaterialTag.vue'
|
||||
|
||||
export default {
|
||||
name: 'PdoManagement',
|
||||
@@ -175,7 +189,8 @@ export default {
|
||||
PdoDataCorrection, // 注册数据修正组件
|
||||
PdoLabelPrint, // 注册标签打印组件
|
||||
PdoSummary,
|
||||
QualityCertificate // 注册质保书组件
|
||||
QualityCertificate, // 注册质保书组件
|
||||
MaterialTag
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -184,6 +199,8 @@ export default {
|
||||
total: 0,
|
||||
printOpen: false,
|
||||
certificateVisible: false, // 质保书对话框显示状态 Quality certificate dialog visibility state
|
||||
printTagDialogVisible: false,
|
||||
printTagContent: null,
|
||||
tableData: [],
|
||||
tableLoading: false,
|
||||
btnLoading: false,
|
||||
@@ -287,6 +304,75 @@ export default {
|
||||
this.formData = JSON.parse(JSON.stringify(row));
|
||||
this.printOpen = true
|
||||
},
|
||||
handlePrintTag(row) {
|
||||
if (!row) return
|
||||
// 将 PDO 字段映射到 MaterialTag 期望字段
|
||||
this.printTagContent = {
|
||||
planid: row.planId || row.planid || '',
|
||||
coilid: row.exitMatId || row.coilid || '',
|
||||
steelGrade: row.steelGrade || '',
|
||||
status: row.status || '',
|
||||
entryThick: row.entryThick,
|
||||
entryWidth: row.entryWidth,
|
||||
entryLength: row.entryLength,
|
||||
entryWeight: row.entryWeight,
|
||||
exitThick: row.exitThickness,
|
||||
exitWidth: row.exitWidth,
|
||||
exitLength: row.exitLength,
|
||||
exitWeight: row.exitNetWeight,
|
||||
yieldPoint: row.yieldPoint,
|
||||
orderNo: row.orderNo || ''
|
||||
}
|
||||
this.printTagDialogVisible = true
|
||||
},
|
||||
doPrintTag() {
|
||||
this.$nextTick(() => {
|
||||
this.printElementOnly(this.$refs.printTagRef)
|
||||
})
|
||||
},
|
||||
printElementOnly(el) {
|
||||
if (!el) return
|
||||
|
||||
const printWindow = window.open('', '_blank', 'noopener,noreferrer')
|
||||
if (!printWindow) {
|
||||
this.$message && this.$message.error('Failed to open print window')
|
||||
return
|
||||
}
|
||||
|
||||
const styles = Array.from(document.querySelectorAll('link[rel="stylesheet"], style'))
|
||||
.map(node => node.outerHTML)
|
||||
.join('\n')
|
||||
|
||||
const html = '<!doctype html>' +
|
||||
'<html>' +
|
||||
'<head>' +
|
||||
'<meta charset="utf-8">' +
|
||||
'<meta name="viewport" content="width=device-width, initial-scale=1">' +
|
||||
'<title>Print</title>' +
|
||||
styles +
|
||||
'<style>' +
|
||||
' @page { size: 100mm 80mm; margin: 0; }' +
|
||||
' html, body { margin: 0; padding: 0; }' +
|
||||
' body { background: #fff; }' +
|
||||
'</style>' +
|
||||
'</head>' +
|
||||
'<body>' +
|
||||
'<div class="material-label-container">' + el.innerHTML + '</div>' +
|
||||
'<script>' +
|
||||
' window.onload = function() {' +
|
||||
' setTimeout(function() {' +
|
||||
' window.print();' +
|
||||
' window.close();' +
|
||||
' }, 50);' +
|
||||
' };' +
|
||||
'<\\/script>' +
|
||||
'</body>' +
|
||||
'</html>'
|
||||
|
||||
printWindow.document.open()
|
||||
printWindow.document.write(html)
|
||||
printWindow.document.close()
|
||||
},
|
||||
// 处理质保书(单卷) Handle quality certificate (single coil)
|
||||
handleQualityCertificate(row) {
|
||||
// 深拷贝行数据 Deep copy row data
|
||||
|
||||
269
src/views/l2/plan/components/MaterialTag.vue
Normal file
269
src/views/l2/plan/components/MaterialTag.vue
Normal file
@@ -0,0 +1,269 @@
|
||||
<template>
|
||||
<div class="label-container" :style="{ '--print-scale': printScale }">
|
||||
<div class="tag-header">
|
||||
<img class="tag-logo" :src="logoImg" />
|
||||
<span class="tag-title">Galvanizing Line MATERIAL TAG</span>
|
||||
</div>
|
||||
|
||||
<table class="material-label-table">
|
||||
<tr>
|
||||
<td class="label-cell">Plan ID</td>
|
||||
<td class="value-cell">{{ content.planid || '-' }}</td>
|
||||
<td class="label-cell">Coil ID</td>
|
||||
<td class="value-cell">{{ content.coilid || '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label-cell">Steel Grade</td>
|
||||
<td class="value-cell">{{ content.steelGrade || '-' }}</td>
|
||||
<td class="label-cell">Status</td>
|
||||
<td class="value-cell">{{ formatStatus(content.status) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label-cell">Entry Thickness</td>
|
||||
<td class="value-cell">{{ formatMm(content.entryThick) }}</td>
|
||||
<td class="label-cell">Entry Width</td>
|
||||
<td class="value-cell">{{ formatMm(content.entryWidth) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label-cell">Entry Weight</td>
|
||||
<td class="value-cell">{{ formatT(content.entryWeight) }}</td>
|
||||
<td class="label-cell">Entry Length</td>
|
||||
<td class="value-cell">{{ formatMm(content.entryLength) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label-cell">Exit Thickness</td>
|
||||
<td class="value-cell">{{ formatMm(content.exitThick) }}</td>
|
||||
<td class="label-cell">Exit Width</td>
|
||||
<td class="value-cell">{{ formatMm(content.exitWidth) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label-cell">Exit Net Weight</td>
|
||||
<td class="value-cell">{{ formatT(content.exitWeight) }}</td>
|
||||
<td class="label-cell">Exit Length</td>
|
||||
<td class="value-cell">{{ formatMm(content.exitLength) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label-cell">Yield Point</td>
|
||||
<td class="value-cell">{{ content.yieldPoint || '-' }}</td>
|
||||
<td class="label-cell">Order No.</td>
|
||||
<td class="value-cell">{{ content.orderNo || '-' }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import logoImg from '@/assets/logo/logo.png'
|
||||
|
||||
export default {
|
||||
name: 'MaterialTag',
|
||||
props: {
|
||||
content: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
paperWidthMm: {
|
||||
type: Number,
|
||||
default: 100
|
||||
},
|
||||
paperHeightMm: {
|
||||
type: Number,
|
||||
default: 80
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
logoImg,
|
||||
printScale: 1,
|
||||
printMediaQuery: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.printMediaQuery = window.matchMedia('print')
|
||||
this.printMediaQuery.addListener(this.handlePrintMediaChange)
|
||||
|
||||
window.addEventListener('beforeprint', this.handleBeforePrint)
|
||||
window.addEventListener('afterprint', this.handleAfterPrint)
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.calculatePrintScale()
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.printMediaQuery) {
|
||||
this.printMediaQuery.removeListener(this.handlePrintMediaChange)
|
||||
}
|
||||
window.removeEventListener('beforeprint', this.handleBeforePrint)
|
||||
window.removeEventListener('afterprint', this.handleAfterPrint)
|
||||
},
|
||||
methods: {
|
||||
formatStatus(status) {
|
||||
const map = {
|
||||
PRODUCING: 'Producing',
|
||||
ONLINE: 'Online',
|
||||
READY: 'Ready',
|
||||
NEW: 'New',
|
||||
PRODUCT: 'Completed'
|
||||
}
|
||||
return map[status] || status || '-'
|
||||
},
|
||||
formatMm(val) {
|
||||
if (val === null || val === undefined || val === '') return '-'
|
||||
return `${val} mm`
|
||||
},
|
||||
formatT(val) {
|
||||
if (val === null || val === undefined || val === '') return '-'
|
||||
return `${val} t`
|
||||
},
|
||||
handlePrintMediaChange(mq) {
|
||||
if (mq.matches) {
|
||||
this.calculatePrintScale()
|
||||
} else {
|
||||
this.resetPrintScale()
|
||||
}
|
||||
},
|
||||
handleBeforePrint() {
|
||||
setTimeout(() => {
|
||||
this.calculatePrintScale()
|
||||
}, 100)
|
||||
},
|
||||
handleAfterPrint() {
|
||||
this.resetPrintScale()
|
||||
},
|
||||
calculatePrintScale() {
|
||||
this.$nextTick(() => {
|
||||
const container = this.$el
|
||||
if (!container) return
|
||||
|
||||
const dpi = 96
|
||||
const mmToPx = dpi / 25.4
|
||||
|
||||
const paperWidthMm = this.paperWidthMm || 100
|
||||
const paperHeightMm = this.paperHeightMm || 80
|
||||
const marginMm = 2
|
||||
|
||||
const paperWidthPx = paperWidthMm * mmToPx
|
||||
const paperHeightPx = paperHeightMm * mmToPx
|
||||
const marginPx = marginMm * mmToPx
|
||||
|
||||
const rect = container.getBoundingClientRect()
|
||||
let contentWidth = rect.width
|
||||
let contentHeight = rect.height
|
||||
|
||||
if (contentWidth === 0 || contentHeight === 0) {
|
||||
contentWidth = container.scrollWidth || contentWidth
|
||||
contentHeight = container.scrollHeight || contentHeight
|
||||
}
|
||||
|
||||
const availableWidth = paperWidthPx - marginPx * 2
|
||||
const availableHeight = paperHeightPx - marginPx * 2
|
||||
|
||||
const scaleX = contentWidth > 0 ? availableWidth / contentWidth : 1
|
||||
const scaleY = contentHeight > 0 ? availableHeight / contentHeight : 1
|
||||
|
||||
this.printScale = Math.min(scaleX, scaleY, 1)
|
||||
|
||||
container.style.setProperty('--print-scale', this.printScale)
|
||||
container.style.setProperty('--paper-width', `${paperWidthMm}mm`)
|
||||
container.style.setProperty('--paper-height', `${paperHeightMm}mm`)
|
||||
})
|
||||
},
|
||||
resetPrintScale() {
|
||||
this.printScale = 1
|
||||
if (this.$el) {
|
||||
this.$el.style.setProperty('--print-scale', 1)
|
||||
this.$el.style.removeProperty('--paper-width')
|
||||
this.$el.style.removeProperty('--paper-height')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.label-container {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 8px;
|
||||
background: #fff;
|
||||
border: 1px solid #333;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.tag-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 4px 0 8px;
|
||||
}
|
||||
|
||||
.tag-logo {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.tag-title {
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.material-label-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.material-label-table td {
|
||||
border: 1px solid #333;
|
||||
padding: 6px 6px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.label-cell {
|
||||
width: 25%;
|
||||
background: #f5f5f5;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.value-cell {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
@media print {
|
||||
@page {
|
||||
size: 100mm 80mm;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-print-color-adjust: exact !important;
|
||||
print-color-adjust: exact !important;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
body > *:not(.material-label-container) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.material-label-container {
|
||||
transform: scale(var(--print-scale, 1)) !important;
|
||||
transform-origin: top left !important;
|
||||
max-width: var(--paper-width, 100mm) !important;
|
||||
max-height: var(--paper-height, 80mm) !important;
|
||||
overflow: hidden !important;
|
||||
|
||||
page-break-inside: avoid !important;
|
||||
break-inside: avoid !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -61,6 +61,9 @@
|
||||
<el-button size="mini" type="text" @click.stop="handleDelete(plan)" icon="el-icon-delete">
|
||||
Delete <!-- 删除 -->
|
||||
</el-button>
|
||||
<el-button size="mini" type="text" @click.stop="handlePrintTag(plan)" icon="el-icon-printer">
|
||||
Print Tag
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
@@ -128,6 +131,7 @@
|
||||
<div class="plan-actions">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click.stop="handleUpdate(plan)" />
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click.stop="handleDelete(plan)" />
|
||||
<el-button size="mini" type="text" icon="el-icon-printer" @click.stop="handlePrintTag(plan)" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="pendingPlans.length === 0" class="empty-text">
|
||||
@@ -229,6 +233,17 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- Print Tag Dialog -->
|
||||
<el-dialog title="Print Tag" :visible.sync="printTagDialogVisible" width="520px" :close-on-click-modal="false" append-to-body>
|
||||
<div ref="printTagRef" class="material-label-container">
|
||||
<MaterialTag v-if="printTagContent" :content="printTagContent" :paper-width-mm="100" :paper-height-mm="80" />
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="printTagDialogVisible = false">Close</el-button>
|
||||
<el-button type="primary" @click="doPrintTag" :disabled="!printTagContent">Print</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 编辑/新增弹窗 Edit/Add Dialog -->
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="80%" :close-on-click-modal="false"
|
||||
append-to-body>
|
||||
@@ -366,6 +381,7 @@ import SetupForm from './components/setupForm.vue'
|
||||
import SetupPane from './components/setupPane.vue'
|
||||
import PlanRecommendPanel from './components/PlanRecommendPanel.vue'
|
||||
import ProcessRecommendPanel from './components/ProcessRecommendPanel.vue'
|
||||
import MaterialTag from './components/MaterialTag.vue'
|
||||
|
||||
// 标准日期格式化方法(优化时间处理逻辑,适配接口日期格式)
|
||||
function parseTime(time, format = "{yyyy}-{mm}-{dd} {hh}:{ii}:{ss}") {
|
||||
@@ -397,7 +413,7 @@ function parseTime(time, format = "{yyyy}-{mm}-{dd} {hh}:{ii}:{ss}") {
|
||||
};
|
||||
|
||||
// 补零格式化(确保月/日/时/分/秒为两位数)
|
||||
return format.replace(/{([ymdhisa])+}/g, (result, key) => {
|
||||
return format.replace(/{([ymdhisa])+}/g, (_result, key) => {
|
||||
const value = formatObj[key];
|
||||
return value.toString().length < 2 ? "0" + value : value;
|
||||
});
|
||||
@@ -409,7 +425,8 @@ export default {
|
||||
SetupForm,
|
||||
SetupPane,
|
||||
PlanRecommendPanel,
|
||||
ProcessRecommendPanel
|
||||
ProcessRecommendPanel,
|
||||
MaterialTag
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -434,6 +451,8 @@ export default {
|
||||
planList: [],
|
||||
loading: false,
|
||||
btnLoading: false,
|
||||
printTagDialogVisible: false,
|
||||
printTagContent: null,
|
||||
// 弹窗状态(新增/编辑弹窗显隐+标题) Dialog state (add/edit dialog visibility + title)
|
||||
dialogVisible: false,
|
||||
dialogTitle: "Add Plan", // 新增计划
|
||||
@@ -647,6 +666,59 @@ export default {
|
||||
if (!this.$refs.setupFormRef) return
|
||||
this.$refs.setupFormRef.applyAll()
|
||||
},
|
||||
handlePrintTag(plan) {
|
||||
if (!plan) return
|
||||
this.printTagContent = { ...plan }
|
||||
this.printTagDialogVisible = true
|
||||
},
|
||||
doPrintTag() {
|
||||
this.$nextTick(() => {
|
||||
this.printElementOnly(this.$refs.printTagRef)
|
||||
})
|
||||
},
|
||||
printElementOnly(el) {
|
||||
if (!el) return
|
||||
|
||||
const printWindow = window.open('', '_blank', 'noopener,noreferrer')
|
||||
if (!printWindow) {
|
||||
this.$message && this.$message.error('Failed to open print window')
|
||||
return
|
||||
}
|
||||
|
||||
const styles = Array.from(document.querySelectorAll('link[rel="stylesheet"], style'))
|
||||
.map(node => node.outerHTML)
|
||||
.join('\n')
|
||||
|
||||
const html = '<!doctype html>' +
|
||||
'<html>' +
|
||||
'<head>' +
|
||||
'<meta charset="utf-8">' +
|
||||
'<meta name="viewport" content="width=device-width, initial-scale=1">' +
|
||||
'<title>Print</title>' +
|
||||
styles +
|
||||
'<style>' +
|
||||
' @page { size: 100mm 80mm; margin: 0; }' +
|
||||
' html, body { margin: 0; padding: 0; }' +
|
||||
' body { background: #fff; }' +
|
||||
'</style>' +
|
||||
'</head>' +
|
||||
'<body>' +
|
||||
'<div class="material-label-container">' + el.innerHTML + '</div>' +
|
||||
'<script>' +
|
||||
' window.onload = function() {' +
|
||||
' setTimeout(function() {' +
|
||||
' window.print();' +
|
||||
' window.close();' +
|
||||
' }, 50);' +
|
||||
' };' +
|
||||
'<\\/script>' +
|
||||
'</body>' +
|
||||
'</html>'
|
||||
|
||||
printWindow.document.open()
|
||||
printWindow.document.write(html)
|
||||
printWindow.document.close()
|
||||
},
|
||||
// 获取物料位置映射
|
||||
fetchMatMapList() {
|
||||
getTrackMatPosition().then(res => {
|
||||
|
||||
@@ -149,32 +149,32 @@ export default {
|
||||
{ prop: 'steelGrade', label: 'Steel Grade', required: true, minWidth: 65 }, // 钢种
|
||||
{ prop: 'thick', label: 'Thickness', required: true, minWidth: 65 }, // 厚度
|
||||
{ prop: 'yieldStren', label: 'Yield Point', required: true, minWidth: 90 }, // 屈服强度
|
||||
{ prop: 'value1', label: 'Pay-off Reel Tension', minWidth: 120 }, // 开卷机张力
|
||||
{ prop: 'value2', label: 'Entry Loop Tension', minWidth: 120 }, // 入口活套张力
|
||||
{ prop: 'value3', label: 'Cleaning Section Tension', minWidth: 120 }, // 清洗段张力
|
||||
{ prop: 'value4', label: 'Passivation Section Tension', minWidth: 120 }, // 钝化段张力
|
||||
{ prop: 'value5', label: 'Exit Loop Tension', minWidth: 120 }, // 出口活套张力
|
||||
{ prop: 'value6', label: 'Take-up Reel Tension', minWidth: 120 } // 卷取机张力
|
||||
{ prop: 'value1', label: 'Pay-off Reel Tension (kg)', minWidth: 120 }, // 开卷机张力
|
||||
{ prop: 'value2', label: 'Entry Loop Tension (kg)', minWidth: 120 }, // 入口活套张力
|
||||
{ prop: 'value3', label: 'Cleaning Section Tension (kg)', minWidth: 120 }, // 清洗段张力
|
||||
{ prop: 'value4', label: 'Passivation Section Tension (kg)', minWidth: 120 }, // 钝化段张力
|
||||
{ prop: 'value5', label: 'Exit Loop Tension (kg)', minWidth: 120 }, // 出口活套张力
|
||||
{ prop: 'value6', label: 'Take-up Reel Tension (kg)', minWidth: 120 } // 卷取机张力
|
||||
],
|
||||
schemaLeveler: [
|
||||
{ prop: 'steelGrade', label: 'Steel Grade', required: true, minWidth: 65 }, // 钢种
|
||||
{ prop: 'thick', label: 'Thickness', required: true, minWidth: 65 }, // 厚度
|
||||
{ prop: 'yieldStren', label: 'Yield Point', required: true, minWidth: 90 }, // 屈服强度
|
||||
{ prop: 'value1', label: 'Leveler Entry Tension', minWidth: 120 }, // 平整机入口张力
|
||||
{ prop: 'value2', label: 'Leveler Exit Tension', minWidth: 120 } // 平整机出口张力
|
||||
{ prop: 'value1', label: 'Leveler Entry Tension (kg)', minWidth: 120 }, // 平整机入口张力
|
||||
{ prop: 'value2', label: 'Leveler Exit Tension (kg)', minWidth: 120 } // 平整机出口张力
|
||||
],
|
||||
schemaStraightener: [
|
||||
{ prop: 'steelGrade', label: 'Steel Grade', required: true, minWidth: 65 }, // 钢种
|
||||
{ prop: 'thick', label: 'Thickness', required: true, minWidth: 65 }, // 厚度
|
||||
{ prop: 'yieldStren', label: 'Yield Point', required: true, minWidth: 90 }, // 屈服强度
|
||||
{ prop: 'value1', label: 'Straightener Exit Tension', minWidth: 120 } // 矫直机出口张力
|
||||
{ prop: 'value1', label: 'Straightener Exit Tension (kg)', minWidth: 120 } // 矫直机出口张力
|
||||
],
|
||||
schemaAnnealingFurnace: [
|
||||
{ prop: 'steelGrade', label: 'Steel Grade', required: true, minWidth: 65 }, // 钢种
|
||||
{ prop: 'thick', label: 'Thickness', required: true, minWidth: 65 }, // 厚度
|
||||
{ prop: 'yieldStren', label: 'Yield Point', required: true, minWidth: 90 }, // 屈服强度
|
||||
{ prop: 'value1', label: 'Furnace Zone Tension', minWidth: 120 }, // 炉区张力
|
||||
{ prop: 'value2', label: 'Cooling Tower Tension', minWidth: 120 } // 冷却塔张力
|
||||
{ prop: 'value1', label: 'Furnace Zone Tension (kg)', minWidth: 120 }, // 炉区张力
|
||||
{ prop: 'value2', label: 'Cooling Tower Tension (kg)', minWidth: 120 } // 冷却塔张力
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -54,6 +54,27 @@
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="param-groups-row">
|
||||
<div class="quick-reason-row" @click.stop>
|
||||
<span class="quick-reason-label">Stop Type:</span>
|
||||
<el-select
|
||||
v-model="item.stopType"
|
||||
size="mini"
|
||||
placeholder="Quick select stop type"
|
||||
filterable
|
||||
clearable
|
||||
:disabled="item.reasonSaving"
|
||||
:loading="item.reasonSaving"
|
||||
@change="val => handleQuickReasonChange(item, val)"
|
||||
@click.native.stop
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in quickStopTypeOptions"
|
||||
:key="opt"
|
||||
:label="opt"
|
||||
:value="opt"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<!-- Basic Info / 基本信息 -->
|
||||
<div class="param-group">
|
||||
<div class="group-title">Basic Info</div>
|
||||
@@ -230,14 +251,12 @@
|
||||
<!-- 停机类型 -->
|
||||
<el-select v-model="formData.stopType" placeholder="Please select stop type" clearable>
|
||||
<!-- 请选择停机类型 -->
|
||||
<el-option label="Planned Stop" value="计划停机"></el-option>
|
||||
<!-- 计划停机 -->
|
||||
<el-option label="Fault Stop" value="故障停机"></el-option>
|
||||
<!-- 故障停机 -->
|
||||
<el-option label="Maintenance Stop" value="维护停机"></el-option>
|
||||
<!-- 维护停机 -->
|
||||
<el-option label="Other" value="其他"></el-option>
|
||||
<!-- 其他 -->
|
||||
<el-option
|
||||
v-for="opt in stopTypeOptions"
|
||||
:key="opt.stopType"
|
||||
:label="opt.remark"
|
||||
:value="opt.stopType"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
@@ -258,7 +277,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listStoppage, updateStoppage, deleteStoppage } from '@/api/l2/stop'; // 导入接口
|
||||
import { listStoppage, updateStoppage, deleteStoppage, listStoppageTypes } from '@/api/l2/stop'; // 导入接口
|
||||
|
||||
export default {
|
||||
name: 'StoppageManagement',
|
||||
@@ -267,8 +286,8 @@ export default {
|
||||
// Query form data / 查询表单数据
|
||||
queryForm: {
|
||||
// Only keep YYYY-MM-dd format / 只保留年月日YYYY-mm-dd
|
||||
startDate: '2023-08-13',
|
||||
endDate: '2025-08-20'
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
},
|
||||
// Table data / 表格数据
|
||||
tableData: [],
|
||||
@@ -313,14 +332,40 @@ export default {
|
||||
// Save button loading state / 保存按钮加载状态
|
||||
saveLoading: false,
|
||||
// Current row data / 当前操作的行数据
|
||||
currentRow: {}
|
||||
currentRow: {},
|
||||
// Stop types from backend / 后端停机类型
|
||||
stopTypeOptions: [],
|
||||
// Quick stop type options (from backend) / 快捷停机类型(来自后端)
|
||||
quickStopTypeOptions: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
const now = new Date();
|
||||
const yyyy = now.getFullYear();
|
||||
const mm = String(now.getMonth() + 1).padStart(2, '0');
|
||||
const dd = String(now.getDate()).padStart(2, '0');
|
||||
this.queryForm.startDate = `${yyyy}-${mm}-01`;
|
||||
this.queryForm.endDate = `${yyyy}-${mm}-${dd}`;
|
||||
|
||||
// Query once on page load / 页面加载时默认查询一次
|
||||
this.getStoppageList();
|
||||
this.getStopTypeOptions();
|
||||
},
|
||||
methods: {
|
||||
getStopTypeOptions() {
|
||||
listStoppageTypes()
|
||||
.then(res => {
|
||||
const list = (res && res.data) ? res.data : [];
|
||||
this.stopTypeOptions = Array.isArray(list) ? list : [];
|
||||
this.quickStopTypeOptions = this.stopTypeOptions.map(x => x.remark).filter(Boolean);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to fetch stop types:', err);
|
||||
this.stopTypeOptions = [];
|
||||
this.quickStopTypeOptions = [];
|
||||
});
|
||||
},
|
||||
|
||||
// Get stoppage record list / 获取停机记录列表
|
||||
getStoppageList() {
|
||||
this.tableLoading = true;
|
||||
@@ -330,7 +375,9 @@ export default {
|
||||
this.btnLoading = false;
|
||||
this.tableData = response.data.map(item => ({
|
||||
...item,
|
||||
deleteLoading: false
|
||||
deleteLoading: false,
|
||||
reasonSaving: false,
|
||||
_lastStopType: item.stopType
|
||||
}));
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -437,6 +484,32 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
handleQuickReasonChange(row, val) {
|
||||
if (!row || !row.stopid) return;
|
||||
const newVal = (val === undefined || val === null) ? '' : val;
|
||||
|
||||
const payload = { ...row, stopType: newVal };
|
||||
row.reasonSaving = true;
|
||||
|
||||
updateStoppage(payload)
|
||||
.then(res => {
|
||||
row.reasonSaving = false;
|
||||
if (res && res.code === 200) {
|
||||
row._lastStopType = newVal;
|
||||
this.$message.success('Saved');
|
||||
} else {
|
||||
row.stopType = row._lastStopType;
|
||||
this.$message.error((res && res.msg) || 'Save failed');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
row.reasonSaving = false;
|
||||
row.stopType = row._lastStopType;
|
||||
console.error('Quick stopType save failed:', err);
|
||||
this.$message.error('Save failed, please retry');
|
||||
});
|
||||
},
|
||||
|
||||
// Save / 保存
|
||||
handleSave() {
|
||||
this.$refs.formData.validate(valid => {
|
||||
@@ -572,6 +645,7 @@ export default {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
|
||||
.param-group {
|
||||
flex: 1;
|
||||
@@ -642,6 +716,26 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.quick-reason-row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding-bottom: 6px;
|
||||
margin-bottom: 6px;
|
||||
border-bottom: 1px dashed #e0e0e0;
|
||||
|
||||
.quick-reason-label {
|
||||
color: #777;
|
||||
font-size: 11px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
::v-deep .el-select {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
Reference in New Issue
Block a user