二级修改
This commit is contained in:
@@ -17,6 +17,50 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</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">
|
<div v-loading="loading" class="card-grid-container">
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
@@ -60,10 +104,19 @@
|
|||||||
type="primary"
|
type="primary"
|
||||||
size="mini"
|
size="mini"
|
||||||
icon="el-icon-s-promotion"
|
icon="el-icon-s-promotion"
|
||||||
@click="handleSend(setup)"
|
@click="handleSendCurrent(setup)"
|
||||||
:loading="setup.sending"
|
:loading="setup.sending"
|
||||||
>
|
>
|
||||||
下发
|
当前计划下发
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
size="mini"
|
||||||
|
icon="el-icon-right"
|
||||||
|
@click="handleSendNext(setup)"
|
||||||
|
:loading="setup.sendingNext"
|
||||||
|
>
|
||||||
|
下一计划下发
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -103,6 +156,7 @@
|
|||||||
import { listSetup } from '@/api/business/setup'
|
import { listSetup } from '@/api/business/setup'
|
||||||
import { createSendJob, executeSendJob } from '@/api/l2/sendJob'
|
import { createSendJob, executeSendJob } from '@/api/l2/sendJob'
|
||||||
import { getLastSuccess } from '@/api/l2/sendTemplate'
|
import { getLastSuccess } from '@/api/l2/sendTemplate'
|
||||||
|
import { listPlan } from '@/api/l2/plan'
|
||||||
|
|
||||||
// 传动字段定义(中文界面,贴合工业场景)
|
// 传动字段定义(中文界面,贴合工业场景)
|
||||||
const DRIVE_FIELDS = [
|
const DRIVE_FIELDS = [
|
||||||
@@ -165,6 +219,8 @@ export default {
|
|||||||
loading: false,
|
loading: false,
|
||||||
lastSuccess: null,
|
lastSuccess: null,
|
||||||
setups: [],
|
setups: [],
|
||||||
|
planQueueLoading: false,
|
||||||
|
planQueue: [],
|
||||||
driveFields: DRIVE_FIELDS,
|
driveFields: DRIVE_FIELDS,
|
||||||
driveAddress: { ...DRIVE_ADDRESS }
|
driveAddress: { ...DRIVE_ADDRESS }
|
||||||
}
|
}
|
||||||
@@ -172,8 +228,53 @@ export default {
|
|||||||
created() {
|
created() {
|
||||||
this.reload()
|
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: {
|
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() {
|
async reload() {
|
||||||
|
// 同时加载计划队列和设置
|
||||||
|
await Promise.all([
|
||||||
|
this.getPlanQueue(),
|
||||||
|
this.loadSetups()
|
||||||
|
])
|
||||||
|
},
|
||||||
|
|
||||||
|
async loadSetups() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
// 1. 获取传动模块上次成功下发数据
|
// 1. 获取传动模块上次成功下发数据
|
||||||
@@ -204,7 +305,8 @@ export default {
|
|||||||
return {
|
return {
|
||||||
...s,
|
...s,
|
||||||
params,
|
params,
|
||||||
sending: false
|
sending: false,
|
||||||
|
sendingNext: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -242,7 +344,7 @@ export default {
|
|||||||
return new Date(t).toLocaleString()
|
return new Date(t).toLocaleString()
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSend(setup) {
|
handleSendCurrent(setup) {
|
||||||
this.$confirm(
|
this.$confirm(
|
||||||
`确认要下发【${setup.coilid || '-'}】钢卷的传动参数吗?`,
|
`确认要下发【${setup.coilid || '-'}】钢卷的传动参数吗?`,
|
||||||
'提示',
|
'提示',
|
||||||
@@ -254,29 +356,53 @@ export default {
|
|||||||
).then(() => this.doSend(setup)).catch(() => {})
|
).then(() => this.doSend(setup)).catch(() => {})
|
||||||
},
|
},
|
||||||
|
|
||||||
async doSend(setup) {
|
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
|
setup.sending = true
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const items = this.driveFields.map(f => ({
|
const items = this.driveFields.map(f => ({
|
||||||
paramCode: f.key,
|
paramCode: f.key,
|
||||||
address: this.driveAddress[f.key],
|
address: this.driveAddress[f.key],
|
||||||
valueRaw: String(setup.params[f.key] || ''),
|
valueRaw: String(setup.params[f.key] || ''),
|
||||||
setTime: new Date()
|
setTime: new Date()
|
||||||
})).filter(it => !!it.address) // 过滤无OPC地址的项
|
})).filter(it => !!it.address)
|
||||||
|
|
||||||
if (!items.length) {
|
if (!items.length) {
|
||||||
this.$message.warning('OPC地址未配置,无可下发内容')
|
this.$message.warning('OPC地址未配置,无可下发内容')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bizKey = isNextPlan ? plan.coilid : setup.coilid
|
||||||
const dto = {
|
const dto = {
|
||||||
deviceName: 'CGL_LINE_1',
|
deviceName: 'CGL_LINE_1',
|
||||||
bizKey: setup.coilid,
|
bizKey: bizKey,
|
||||||
groups: [
|
groups: [
|
||||||
{
|
{
|
||||||
groupNo: 1,
|
groupNo: 1,
|
||||||
groupType: 'DRIVE',
|
groupType: 'DRIVE',
|
||||||
groupName: `传动参数_${setup.coilid || ''}`,
|
groupName: `传动参数_${bizKey || ''}`,
|
||||||
items
|
items
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -294,15 +420,77 @@ export default {
|
|||||||
console.error(e)
|
console.error(e)
|
||||||
this.$message.error(e.message || '下发失败')
|
this.$message.error(e.message || '下发失败')
|
||||||
} finally {
|
} finally {
|
||||||
|
if (isNextPlan) {
|
||||||
|
setup.sendingNext = false
|
||||||
|
} else {
|
||||||
setup.sending = false
|
setup.sending = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
doSendNextPlan(setup, plan) {
|
||||||
|
this.doSend(setup, plan)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.toolbar { margin-bottom: 20px; display:flex; flex-wrap:wrap; gap:8px; align-items:center; }
|
.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-grid-container { min-height: 300px; }
|
||||||
.card-col { margin-bottom: 20px; }
|
.card-col { margin-bottom: 20px; }
|
||||||
.parameter-card .card-header { display:flex; justify-content:space-between; align-items:center; }
|
.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-row { margin-bottom: 4px; }
|
||||||
.card-title { font-weight: 600; font-size: 16px; }
|
.card-title { font-weight: 600; font-size: 16px; }
|
||||||
.card-subtitle { font-size: 12px; color: #909399; display: flex; gap: 12px; }
|
.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; }
|
.last-send-time { font-size: 12px; color:#909399; margin-right:16px; }
|
||||||
.empty-data { margin-top: 20px; }
|
.empty-data { margin-top: 20px; }
|
||||||
</style>
|
</style>
|
||||||
@@ -177,7 +177,7 @@ export default {
|
|||||||
templateItems() {
|
templateItems() {
|
||||||
if (!this.template || !Array.isArray(this.template.items)) return []
|
if (!this.template || !Array.isArray(this.template.items)) return []
|
||||||
return [...this.template.items]
|
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))
|
.sort((a, b) => (a.itemNo || 0) - (b.itemNo || 0))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user