二级修改
This commit is contained in:
@@ -17,6 +17,50 @@
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 计划队列 -->
|
||||
<div v-loading="planQueueLoading" class="plan-queue-section">
|
||||
<div class="section-header">
|
||||
<i class="el-icon-s-order"></i>
|
||||
<span>生产队列</span>
|
||||
<el-badge :value="sortedPlanQueue.length" class="tab-badge" style="margin-left: 10px;" />
|
||||
</div>
|
||||
<div class="plan-list">
|
||||
<div
|
||||
v-for="plan in sortedPlanQueue"
|
||||
:key="plan.id"
|
||||
class="plan-item"
|
||||
:class="{
|
||||
'plan-item-producing': plan.status === 'PRODUCING',
|
||||
'plan-item-ready': plan.status === 'READY',
|
||||
'plan-item-new': plan.status === 'NEW'
|
||||
}"
|
||||
>
|
||||
<div class="plan-status">
|
||||
<span class="status-dot" :class="{
|
||||
'status-producing': plan.status === 'PRODUCING',
|
||||
'status-ready': plan.status === 'READY',
|
||||
'status-new': plan.status === 'NEW'
|
||||
}"></span>
|
||||
<el-tag
|
||||
:type="plan.status === 'PRODUCING' ? 'success' :
|
||||
plan.status === 'READY' ? 'primary' : 'info'"
|
||||
size="mini"
|
||||
>
|
||||
{{ plan.status === 'PRODUCING' ? '生产中' :
|
||||
plan.status === 'READY' ? '就绪' : '新建' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="plan-content">
|
||||
<span class="plan-no">{{ plan.planid }}</span>
|
||||
<span class="coil-no">{{ plan.coilid }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="sortedPlanQueue.length === 0 && !planQueueLoading" class="empty-text">
|
||||
暂无生产计划
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片列表 -->
|
||||
<div v-loading="loading" class="card-grid-container">
|
||||
<el-row :gutter="20">
|
||||
@@ -60,10 +104,19 @@
|
||||
type="primary"
|
||||
size="mini"
|
||||
icon="el-icon-s-promotion"
|
||||
@click="handleSend(setup)"
|
||||
@click="handleSendCurrent(setup)"
|
||||
:loading="setup.sending"
|
||||
>
|
||||
下发
|
||||
当前计划下发
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
size="mini"
|
||||
icon="el-icon-right"
|
||||
@click="handleSendNext(setup)"
|
||||
:loading="setup.sendingNext"
|
||||
>
|
||||
下一计划下发
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -103,6 +156,7 @@
|
||||
import { listSetup } from '@/api/business/setup'
|
||||
import { createSendJob, executeSendJob } from '@/api/l2/sendJob'
|
||||
import { getLastSuccess } from '@/api/l2/sendTemplate'
|
||||
import { listPlan } from '@/api/l2/plan'
|
||||
|
||||
// 传动字段定义(中文界面,贴合工业场景)
|
||||
const DRIVE_FIELDS = [
|
||||
@@ -165,6 +219,8 @@ export default {
|
||||
loading: false,
|
||||
lastSuccess: null,
|
||||
setups: [],
|
||||
planQueueLoading: false,
|
||||
planQueue: [],
|
||||
driveFields: DRIVE_FIELDS,
|
||||
driveAddress: { ...DRIVE_ADDRESS }
|
||||
}
|
||||
@@ -172,8 +228,53 @@ export default {
|
||||
created() {
|
||||
this.reload()
|
||||
},
|
||||
computed: {
|
||||
// 生产队列(不含已完成),按优先级排序:生产中 > 就绪 > 新建
|
||||
sortedPlanQueue() {
|
||||
const statusPriority = { PRODUCING: 1, READY: 2, NEW: 3 }
|
||||
return (this.planQueue || []).slice().sort((a, b) => {
|
||||
const pa = statusPriority[a.status] || 999
|
||||
const pb = statusPriority[b.status] || 999
|
||||
return pa - pb
|
||||
})
|
||||
},
|
||||
// 下一计划:队列中第一个非生产中计划(READY/NEW);若没有则取队列第一个
|
||||
nextPlan() {
|
||||
const list = this.sortedPlanQueue
|
||||
if (!list.length) return null
|
||||
const next = list.find(p => p.status !== 'PRODUCING')
|
||||
return next || list[0]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取生产计划队列
|
||||
async getPlanQueue() {
|
||||
this.planQueueLoading = true
|
||||
try {
|
||||
// 查询状态为 PRODUCING, READY, NEW 的计划
|
||||
const res = await listPlan({
|
||||
status: 'PRODUCING,READY,NEW',
|
||||
pageSize: 100, // 获取足够多的计划
|
||||
pageNum: 1
|
||||
})
|
||||
this.planQueue = res.data || []
|
||||
} catch (e) {
|
||||
console.error('获取计划队列失败:', e)
|
||||
this.$message.error('获取计划队列失败')
|
||||
} finally {
|
||||
this.planQueueLoading = false
|
||||
}
|
||||
},
|
||||
|
||||
async reload() {
|
||||
// 同时加载计划队列和设置
|
||||
await Promise.all([
|
||||
this.getPlanQueue(),
|
||||
this.loadSetups()
|
||||
])
|
||||
},
|
||||
|
||||
async loadSetups() {
|
||||
this.loading = true
|
||||
try {
|
||||
// 1. 获取传动模块上次成功下发数据
|
||||
@@ -204,7 +305,8 @@ export default {
|
||||
return {
|
||||
...s,
|
||||
params,
|
||||
sending: false
|
||||
sending: false,
|
||||
sendingNext: false
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
@@ -242,7 +344,7 @@ export default {
|
||||
return new Date(t).toLocaleString()
|
||||
},
|
||||
|
||||
handleSend(setup) {
|
||||
handleSendCurrent(setup) {
|
||||
this.$confirm(
|
||||
`确认要下发【${setup.coilid || '-'}】钢卷的传动参数吗?`,
|
||||
'提示',
|
||||
@@ -254,29 +356,53 @@ export default {
|
||||
).then(() => this.doSend(setup)).catch(() => {})
|
||||
},
|
||||
|
||||
async doSend(setup) {
|
||||
setup.sending = true
|
||||
handleSendNext(setup) {
|
||||
if (!this.nextPlan) {
|
||||
this.$message.warning('暂无下一计划')
|
||||
return
|
||||
}
|
||||
const plan = this.nextPlan
|
||||
this.$confirm(
|
||||
`确认要按下一计划【${plan.coilid || '-'}】下发传动参数吗?`,
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => this.doSendNextPlan(setup, plan)).catch(() => {})
|
||||
},
|
||||
|
||||
async doSend(setup, plan) {
|
||||
const isNextPlan = !!plan
|
||||
if (isNextPlan) {
|
||||
setup.sendingNext = true
|
||||
} else {
|
||||
setup.sending = true
|
||||
}
|
||||
|
||||
try {
|
||||
const items = this.driveFields.map(f => ({
|
||||
paramCode: f.key,
|
||||
address: this.driveAddress[f.key],
|
||||
valueRaw: String(setup.params[f.key] || ''),
|
||||
setTime: new Date()
|
||||
})).filter(it => !!it.address) // 过滤无OPC地址的项
|
||||
})).filter(it => !!it.address)
|
||||
|
||||
if (!items.length) {
|
||||
this.$message.warning('OPC地址未配置,无可下发内容')
|
||||
return
|
||||
}
|
||||
|
||||
const bizKey = isNextPlan ? plan.coilid : setup.coilid
|
||||
const dto = {
|
||||
deviceName: 'CGL_LINE_1',
|
||||
bizKey: setup.coilid,
|
||||
bizKey: bizKey,
|
||||
groups: [
|
||||
{
|
||||
groupNo: 1,
|
||||
groupType: 'DRIVE',
|
||||
groupName: `传动参数_${setup.coilid || ''}`,
|
||||
groupName: `传动参数_${bizKey || ''}`,
|
||||
items
|
||||
}
|
||||
]
|
||||
@@ -294,8 +420,16 @@ export default {
|
||||
console.error(e)
|
||||
this.$message.error(e.message || '下发失败')
|
||||
} finally {
|
||||
setup.sending = false
|
||||
if (isNextPlan) {
|
||||
setup.sendingNext = false
|
||||
} else {
|
||||
setup.sending = false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
doSendNextPlan(setup, plan) {
|
||||
this.doSend(setup, plan)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -303,6 +437,60 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
.toolbar { margin-bottom: 20px; display:flex; flex-wrap:wrap; gap:8px; align-items:center; }
|
||||
|
||||
.plan-queue-section {
|
||||
margin-bottom: 20px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 15px;
|
||||
background: #f5f7fa;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.plan-list {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.plan-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-left: 3px solid #409eff;
|
||||
border-radius: 4px;
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.plan-item-producing { border-left-color: #67c23a; }
|
||||
.plan-item-ready { border-left-color: #409eff; }
|
||||
.plan-item-new { border-left-color: #909399; }
|
||||
|
||||
.plan-status { display: flex; flex-direction: column; align-items: center; gap: 4px; }
|
||||
.plan-content { display: flex; flex-direction: column; gap: 2px; }
|
||||
.plan-no { font-weight: 600; color: #409eff; }
|
||||
.coil-no { font-size: 12px; color: #606266; }
|
||||
|
||||
.status-dot { width: 10px; height: 10px; border-radius: 50%; }
|
||||
.status-producing { background: #67c23a; box-shadow: 0 0 6px rgba(103, 194, 58, 0.6); }
|
||||
.status-ready { background: #409eff; box-shadow: 0 0 6px rgba(64, 158, 255, 0.5); }
|
||||
.status-new { background: #909399; box-shadow: 0 0 6px rgba(144, 147, 153, 0.4); }
|
||||
|
||||
.empty-text { padding: 10px; color: #909399; }
|
||||
|
||||
.card-grid-container { min-height: 300px; }
|
||||
.card-col { margin-bottom: 20px; }
|
||||
.parameter-card .card-header { display:flex; justify-content:space-between; align-items:center; }
|
||||
@@ -310,7 +498,7 @@ export default {
|
||||
.card-title-row { margin-bottom: 4px; }
|
||||
.card-title { font-weight: 600; font-size: 16px; }
|
||||
.card-subtitle { font-size: 12px; color: #909399; display: flex; gap: 12px; }
|
||||
.header-right { flex-shrink: 0; margin-left: 16px; }
|
||||
.header-right { flex-shrink: 0; margin-left: 16px; display: flex; gap: 8px; align-items: center; }
|
||||
.last-send-time { font-size: 12px; color:#909399; margin-right:16px; }
|
||||
.empty-data { margin-top: 20px; }
|
||||
</style>
|
||||
@@ -177,7 +177,7 @@ export default {
|
||||
templateItems() {
|
||||
if (!this.template || !Array.isArray(this.template.items)) return []
|
||||
return [...this.template.items]
|
||||
.filter(i => i.enabled === undefined || i.enabled === 1)
|
||||
.filter(i => i.enabled === undefined || i.enabled === 1 || i.enabled === null)
|
||||
.sort((a, b) => (a.itemNo || 0) - (b.itemNo || 0))
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user