前端新增停机快捷录入,标签打印能力

This commit is contained in:
2026-02-02 17:13:27 +08:00
parent 7741d19116
commit 4e058c344f
6 changed files with 559 additions and 27 deletions

View File

@@ -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'
})
}

View File

@@ -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

View 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>

View File

@@ -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 => {

View File

@@ -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 } // 冷却塔张力
]
}
},

View File

@@ -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;