新增写入功能,追踪弹窗
This commit is contained in:
@@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- Page Title / 页面标题 -->
|
||||
<h2 class="page-title">Drive Parameter Sending</h2>
|
||||
|
||||
<!-- Toolbar / 工具栏 -->
|
||||
<div class="toolbar">
|
||||
<el-button @click="reload" icon="el-icon-refresh" size="small" :loading="loading">
|
||||
@@ -37,7 +34,6 @@
|
||||
<span class="card-title">Steel Grade: {{ plan.steelGrade || '-' }}</span>
|
||||
|
||||
<div class="header-right">
|
||||
<!-- Last send time / 上次发送时间 -->
|
||||
<span v-if="lastSuccess && lastSuccess.lastSendTime" class="last-send-time">
|
||||
<i class="el-icon-time"></i>
|
||||
Last Sent: {{ formatTime(lastSuccess.lastSendTime) }}
|
||||
@@ -56,18 +52,18 @@
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<!-- Template-driven form / 按模板渲染可编辑表单 -->
|
||||
<!-- Plan-driven editable form / 按计划参数渲染可编辑表单(不使用模板) -->
|
||||
<el-form :model="plan.params" label-position="top" size="mini">
|
||||
<el-row :gutter="10">
|
||||
<el-col
|
||||
v-for="item in templateItems"
|
||||
:key="item.templateItemId || item.paramCode"
|
||||
v-for="item in driveFields"
|
||||
:key="item.key"
|
||||
:span="12"
|
||||
>
|
||||
<el-form-item :label="item.labelEn">
|
||||
<el-form-item :label="item.label">
|
||||
<el-input
|
||||
v-model="plan.params[item.paramCode]"
|
||||
:placeholder="getPlaceholder(item)"
|
||||
v-model="plan.params[item.key]"
|
||||
:placeholder="getPlaceholder(item.key)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -90,57 +86,89 @@
|
||||
import { listPlan } from '@/api/l2/plan'
|
||||
import { listSetup } from '@/api/business/setup'
|
||||
import { createSendJob, executeSendJob } from '@/api/l2/sendJob'
|
||||
import { getSendTemplate, getLastSuccess } from '@/api/l2/sendTemplate'
|
||||
import { getLastSuccess } from '@/api/l2/sendTemplate'
|
||||
|
||||
// Drive fields definition (English UI, Chinese comments) / 传动字段定义(英文界面,中文注释)
|
||||
// 说明:key 必须与 setupForm 字段一致(来自 plan/components/setupForm.vue)
|
||||
const DRIVE_FIELDS = [
|
||||
{ key: 'porTension', label: 'Pay-off Reel Tension' },
|
||||
{ key: 'celTension', label: 'Entry Loop Tension' },
|
||||
{ key: 'cleanTension', label: 'Cleaning Section Tension' },
|
||||
{ key: 'furTension', label: 'Furnace Zone Tension' },
|
||||
{ key: 'towerTension', label: 'Cooling Tower Tension' },
|
||||
{ key: 'tmNoneTension', label: 'TM No Tension' },
|
||||
{ key: 'tmEntryTension', label: 'TM Entry Tension' },
|
||||
{ key: 'tmExitTension', label: 'TM Exit Tension' },
|
||||
{ key: 'tlNoneTension', label: 'TL No Tension' },
|
||||
{ key: 'tlExitTension', label: 'TL Exit Tension' },
|
||||
{ key: 'coatTension', label: 'Post-treatment Tension' },
|
||||
{ key: 'cxlTension', label: 'Exit Loop Tension' },
|
||||
{ key: 'trTension', label: 'Take-up Reel Tension' },
|
||||
|
||||
{ key: 'tlElong', label: 'TL Elongation' },
|
||||
{ key: 'tlLvlMesh1', label: 'TL Leveling Roll Mesh 1' },
|
||||
{ key: 'tlLvlMesh2', label: 'TL Leveling Roll Mesh 2' },
|
||||
{ key: 'tlAcbMesh', label: 'TL Anti-crossbow Mesh' },
|
||||
|
||||
{ key: 'tmBendforce', label: 'TM Bending Force' },
|
||||
{ key: 'tmAcrMesh', label: 'TM Anti-crimping Roll Mesh' },
|
||||
{ key: 'tmBrMesh', label: 'TM Anti-tremor Roll Mesh' },
|
||||
{ key: 'tmRollforce', label: 'TM Roll Force' }
|
||||
]
|
||||
|
||||
// OPC address mapping (must align with back-end OpcMessageIdsManager.pdiSetupIds) / OPC点位映射(需与后端一致)
|
||||
// 中文注释:这里用“字段名->OPC地址”的方式直接组装发送items
|
||||
const DRIVE_ADDRESS = {
|
||||
porTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionPorBR1',
|
||||
celTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR3',
|
||||
cleanTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR1BR2',
|
||||
furTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionFur1',
|
||||
towerTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionFur2',
|
||||
tmNoneTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR5BR6',
|
||||
tmEntryTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR5TM',
|
||||
tmExitTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionTMBR6',
|
||||
tlNoneTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR6BR7',
|
||||
tlExitTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionTLBR7',
|
||||
coatTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR7BR8',
|
||||
cxlTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR8BR9',
|
||||
trTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR9TR',
|
||||
|
||||
tlElong: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.TLElongation',
|
||||
tlLvlMesh1: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.LevelingMesh1',
|
||||
tlLvlMesh2: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.LevelingMesh2',
|
||||
tlAcbMesh: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.AntiCrossBowUnitMesh',
|
||||
|
||||
tmBendforce: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.TMBendforce',
|
||||
tmAcrMesh: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.ACRMesh',
|
||||
tmBrMesh: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.BRMesh',
|
||||
tmRollforce: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.TMRollforce'
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'DriveSend',
|
||||
data() {
|
||||
return {
|
||||
loading: false, // Loading state / 加载状态
|
||||
template: null, // Template data / 模板数据
|
||||
lastSuccess: null, // Last success data / 上次成功记录
|
||||
plans: [] // Plans with editable params / 带可编辑参数的钢种卡片
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// Template items sorted by itemNo / 模板明细按序号排序
|
||||
templateItems() {
|
||||
if (!this.template || !Array.isArray(this.template.items)) return []
|
||||
return [...this.template.items]
|
||||
.filter(i => i.enabled === undefined || i.enabled === 1)
|
||||
.sort((a, b) => (a.itemNo || 0) - (b.itemNo || 0))
|
||||
loading: false,
|
||||
lastSuccess: null,
|
||||
plans: [],
|
||||
driveFields: DRIVE_FIELDS
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.reload()
|
||||
},
|
||||
methods: {
|
||||
// Reload all / 重新加载:模板 + 上次成功记录 + 计划卡片
|
||||
async reload() {
|
||||
this.loading = true
|
||||
try {
|
||||
// 1) Load template + last success in parallel / 并行加载模板与上次成功记录
|
||||
const [tplRes, lastRes] = await Promise.all([
|
||||
getSendTemplate('DRIVE_DEFAULT'),
|
||||
getLastSuccess('DRIVE')
|
||||
])
|
||||
// last success for DRIVE / 获取传动上次成功
|
||||
const lastRes = await getLastSuccess('DRIVE')
|
||||
this.lastSuccess = lastRes && lastRes.code === 200 ? lastRes.data : null
|
||||
|
||||
// 后端返回 AjaxResult:{code,msg,data}
|
||||
this.template = tplRes ? tplRes.data : null
|
||||
this.lastSuccess = lastRes ? lastRes.data : null
|
||||
|
||||
if (!this.template || !this.templateItems.length) {
|
||||
this.$message.error('DRIVE_DEFAULT template is empty')
|
||||
this.plans = []
|
||||
return
|
||||
}
|
||||
|
||||
// 2) Load plans / 再加载计划(卡片只展示钢种)
|
||||
// plans / 获取计划
|
||||
const planRes = await listPlan({ status: 'NEW,READY' })
|
||||
const planList = planRes.rows || []
|
||||
|
||||
// 3) For each plan fetch setup, and map to template paramCode
|
||||
// 每条计划拉取setup并映射到模板paramCode;优先级:setup值 > 上次成功值 > 模板默认值
|
||||
const tasks = planList.map(async (p) => {
|
||||
let setup = {}
|
||||
try {
|
||||
@@ -151,16 +179,15 @@ export default {
|
||||
}
|
||||
|
||||
const params = {}
|
||||
this.templateItems.forEach(item => {
|
||||
const fromSetup = setup[item.paramCode]
|
||||
const fromLast = this.lastSuccess && this.lastSuccess.values ? this.lastSuccess.values[item.paramCode] : undefined
|
||||
|
||||
this.driveFields.forEach(f => {
|
||||
const fromSetup = setup[f.key]
|
||||
const fromLast = this.lastSuccess?.values?.[f.key]
|
||||
if (fromSetup !== undefined && fromSetup !== null && String(fromSetup) !== '') {
|
||||
params[item.paramCode] = String(fromSetup)
|
||||
params[f.key] = String(fromSetup)
|
||||
} else if (fromLast !== undefined && fromLast !== null) {
|
||||
params[item.paramCode] = String(fromLast)
|
||||
params[f.key] = String(fromLast)
|
||||
} else {
|
||||
params[item.paramCode] = item.defaultValueRaw || ''
|
||||
params[f.key] = ''
|
||||
}
|
||||
})
|
||||
|
||||
@@ -181,45 +208,33 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// Apply last success values to all cards / 将上次成功值应用到所有卡片(覆盖当前输入)
|
||||
applyLastSuccessValues() {
|
||||
if (!this.lastSuccess || !this.lastSuccess.values) {
|
||||
this.$message.info('No last success data')
|
||||
return
|
||||
}
|
||||
|
||||
this.plans.forEach(plan => {
|
||||
this.templateItems.forEach(item => {
|
||||
const v = this.lastSuccess.values[item.paramCode]
|
||||
this.driveFields.forEach(f => {
|
||||
const v = this.lastSuccess.values[f.key]
|
||||
if (v !== undefined) {
|
||||
this.$set(plan.params, item.paramCode, String(v))
|
||||
this.$set(plan.params, f.key, String(v))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
this.$message.success('Last success values applied')
|
||||
},
|
||||
|
||||
// Placeholder shows last/default / 占位符显示上次值或默认值
|
||||
getPlaceholder(item) {
|
||||
const fromLast = this.lastSuccess && this.lastSuccess.values ? this.lastSuccess.values[item.paramCode] : undefined
|
||||
if (fromLast !== undefined) {
|
||||
return `Last: ${fromLast}`
|
||||
}
|
||||
if (item.defaultValueRaw) {
|
||||
return `Default: ${item.defaultValueRaw}`
|
||||
}
|
||||
getPlaceholder(key) {
|
||||
const v = this.lastSuccess?.values?.[key]
|
||||
if (v !== undefined) return `Last: ${v}`
|
||||
return 'Please enter'
|
||||
},
|
||||
|
||||
// Format time / 格式化时间
|
||||
formatTime(t) {
|
||||
if (!t) return ''
|
||||
const d = new Date(t)
|
||||
return d.toLocaleString()
|
||||
return new Date(t).toLocaleString()
|
||||
},
|
||||
|
||||
// Send / 发送
|
||||
handleSend(plan) {
|
||||
this.$confirm(
|
||||
`Confirm to send parameters for Steel Grade [${plan.steelGrade || '-'}]?`,
|
||||
@@ -233,28 +248,17 @@ export default {
|
||||
},
|
||||
|
||||
async doSend(plan) {
|
||||
if (!this.template) {
|
||||
this.$message.error('Template not loaded')
|
||||
return
|
||||
}
|
||||
|
||||
plan.sending = true
|
||||
try {
|
||||
// Build items from template / 按模板组装发送明细
|
||||
const items = this.templateItems.map(item => ({
|
||||
paramCode: item.paramCode,
|
||||
address: item.address,
|
||||
valueRaw: String(plan.params[item.paramCode] || ''),
|
||||
const items = this.driveFields.map(f => ({
|
||||
paramCode: f.key,
|
||||
address: DRIVE_ADDRESS[f.key],
|
||||
valueRaw: String(plan.params[f.key] || ''),
|
||||
setTime: new Date()
|
||||
})).filter(it => !!it.address)
|
||||
|
||||
if (!items.length) {
|
||||
this.$message.error('No valid OPC address')
|
||||
return
|
||||
}
|
||||
|
||||
const dto = {
|
||||
deviceName: this.template.deviceName,
|
||||
deviceName: 'CGL_LINE_1',
|
||||
groups: [
|
||||
{
|
||||
groupNo: 1,
|
||||
@@ -272,7 +276,6 @@ export default {
|
||||
await executeSendJob(jobId)
|
||||
this.$message.success('Send success')
|
||||
|
||||
// Refresh last success time / 发送成功后刷新上次成功记录与时间
|
||||
await this.reload()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
@@ -286,36 +289,13 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-title {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.toolbar {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.card-grid-container {
|
||||
min-height: 300px;
|
||||
}
|
||||
.card-col {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.parameter-card .card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.card-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.last-send-time {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-right: 16px;
|
||||
}
|
||||
.empty-data {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.page-title { margin-bottom: 20px; }
|
||||
.toolbar { margin-bottom: 20px; display:flex; flex-wrap:wrap; gap:8px; align-items:center; }
|
||||
.card-grid-container { min-height: 300px; }
|
||||
.card-col { margin-bottom: 20px; }
|
||||
.parameter-card .card-header { display:flex; justify-content:space-between; align-items:center; }
|
||||
.card-title { font-weight: 600; }
|
||||
.header-right { display:flex; align-items:center; }
|
||||
.last-send-time { font-size: 12px; color:#909399; margin-right:16px; }
|
||||
.empty-data { margin-top: 20px; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user