✨ feat: L2过程跟踪UI压缩
This commit is contained in:
@@ -1,16 +1,173 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="track-container">
|
<div>
|
||||||
<!-- Set Values Floating Panel / 设定值悬浮窗 -->
|
<!-- Set Values Floating Panel / 设定值悬浮窗 -->
|
||||||
<FloatingPanel ref="setValuesPanel" title="Set Values" width="720px" storageKey="TRACK_SET_VALUES"> <!-- 设定值 -->
|
<FloatingPanel ref="setValuesPanel" title="Set Values" width="720px" storageKey="TRACK_SET_VALUES"> <!-- 设定值 -->
|
||||||
<LatestSetValues :driveData="setupValue.drive" :furnaceData="setupValue.furnace" />
|
<LatestSetValues :driveData="setupValue.drive" :furnaceData="setupValue.furnace" />
|
||||||
</FloatingPanel>
|
</FloatingPanel>
|
||||||
|
|
||||||
<FloatingPanel ref="furCurrentPanel" title="Furnace Real-time Parameters" width="400px" storageKey="TRACK_FUR_CURRENT"> <!-- 炉火实时参数 -->
|
<FloatingPanel ref="furCurrentPanel" title="Furnace Real-time Parameters" width="400px"
|
||||||
|
storageKey="TRACK_FUR_CURRENT"> <!-- 炉火实时参数 -->
|
||||||
<FurCurrent :driveData="realtimeData.furnace" />
|
<FurCurrent :driveData="realtimeData.furnace" />
|
||||||
</FloatingPanel>
|
</FloatingPanel>
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="6" v-loading="isLoading">
|
||||||
<!-- 左侧:设备列表 -->
|
<!-- 左侧:设备列表 -->
|
||||||
<el-col :span="16">
|
<el-col :span="16">
|
||||||
|
<div v-if="!isLoading" class="device-layout">
|
||||||
|
<!-- Entry Section / 入口段区域 -->
|
||||||
|
<div class="section-area entry-area">
|
||||||
|
<div class="section-header">
|
||||||
|
Entry Section <!-- 入口段 -->
|
||||||
|
<span class="section-info" v-if="positionData.entrySpeed">Speed: {{ positionData.entrySpeed.toFixed(1) }}
|
||||||
|
m/min</span> <!-- 速度 -->
|
||||||
|
</div>
|
||||||
|
<div class="section-summary" v-if="entrySectionMetrics.length">
|
||||||
|
<div class="summary-item" v-for="item in entrySectionMetrics" :key="item.label">
|
||||||
|
<span class="summary-label">{{ item.label }}:</span>
|
||||||
|
<span class="summary-value">{{ item.value }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="device-grid">
|
||||||
|
<div v-for="device in entryDevicesList" :key="device.positionNameEn" class="device-card" :class="{
|
||||||
|
active: selectedCard && selectedCard.positionNameEn === device.positionNameEn,
|
||||||
|
'has-data': hasMatId(device.positionNameEn)
|
||||||
|
}" @click="selectDevice(device.positionNameEn)">
|
||||||
|
<div class="device-name">{{ device.positionNameCn }}</div>
|
||||||
|
|
||||||
|
<div class="device-status">
|
||||||
|
<div class="device-code">{{ device.positionNameEn }}</div>
|
||||||
|
<span class="coil-id"
|
||||||
|
:class="{ 'status-working': isDeviceWorking(device.positionNameEn), 'status-idle': !isDeviceWorking(device.positionNameEn) }">
|
||||||
|
{{ getDeviceStatus(device.positionNameEn) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Furnace Section / 熔炉段区域 -->
|
||||||
|
<div class="section-area furnace-area">
|
||||||
|
<div class="section-header">
|
||||||
|
Furnace Section <!-- 熔炉段 -->
|
||||||
|
<span class="section-info" v-if="positionData.technologySpeed">Speed: {{
|
||||||
|
positionData.technologySpeed.toFixed(1) }} m/min</span> <!-- 速度 -->
|
||||||
|
<el-button type="text" size="small" @click="openFurCurrentPanel">
|
||||||
|
<i class="el-icon-paperclip"></i>
|
||||||
|
More <!-- 更多 -->
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="section-summary" v-if="furnaceSectionMetrics.length">
|
||||||
|
<div class="summary-item" v-for="item in furnaceSectionMetrics" :key="item.label">
|
||||||
|
<span class="summary-label">{{ item.label }}:</span>
|
||||||
|
<span class="summary-value">{{ item.value }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="device-grid">
|
||||||
|
<div v-for="device in furnaceDevicesList" :key="device.positionNameEn" class="device-card" :class="{
|
||||||
|
active: selectedCard && selectedCard.positionNameEn === device.positionNameEn,
|
||||||
|
'has-data': hasMatId(device.positionNameEn)
|
||||||
|
}" @click="selectDevice(device.positionNameEn)">
|
||||||
|
<div class="device-name">{{ device.positionNameCn }}</div>
|
||||||
|
|
||||||
|
<div class="device-status">
|
||||||
|
<div class="device-code">{{ device.positionNameEn }}</div>
|
||||||
|
<span class="coil-id"
|
||||||
|
:class="{ 'status-working': isDeviceWorking(device.positionNameEn), 'status-idle': !isDeviceWorking(device.positionNameEn) }">
|
||||||
|
{{ getDeviceStatus(device.positionNameEn) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Coating Section / 涂层段区域 -->
|
||||||
|
<div class="section-area coat-area">
|
||||||
|
<div class="section-header">
|
||||||
|
Coating Section <!-- 涂层段 -->
|
||||||
|
<span class="section-info" v-if="positionData.coatSpeed">Speed: {{ positionData.coatSpeed.toFixed(1) }}
|
||||||
|
m/min</span> <!-- 速度 -->
|
||||||
|
</div>
|
||||||
|
<div class="section-summary" v-if="coatSectionMetrics.length">
|
||||||
|
<div class="summary-item" v-for="item in coatSectionMetrics" :key="item.label">
|
||||||
|
<span class="summary-label">{{ item.label }}:</span>
|
||||||
|
<span class="summary-value">{{ item.value }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="device-grid">
|
||||||
|
<div v-for="device in coatDevicesList" :key="device.positionNameEn" class="device-card" :class="{
|
||||||
|
active: selectedCard && selectedCard.positionNameEn === device.positionNameEn,
|
||||||
|
'has-data': hasMatId(device.positionNameEn)
|
||||||
|
}" @click="selectDevice(device.positionNameEn)">
|
||||||
|
<div class="device-name">{{ device.positionNameCn }}</div>
|
||||||
|
|
||||||
|
<div class="device-status">
|
||||||
|
<div class="device-code">{{ device.positionNameEn }}</div>
|
||||||
|
<span class="coil-id"
|
||||||
|
:class="{ 'status-working': isDeviceWorking(device.positionNameEn), 'status-idle': !isDeviceWorking(device.positionNameEn) }">
|
||||||
|
{{ getDeviceStatus(device.positionNameEn) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Exit Section / 出口段区域 -->
|
||||||
|
<div class="section-area exit-area">
|
||||||
|
<div class="section-header">
|
||||||
|
Exit Section <!-- 出口段 -->
|
||||||
|
<span class="section-info" v-if="positionData.exitSpeed">Speed: {{ positionData.exitSpeed.toFixed(1) }}
|
||||||
|
m/min</span> <!-- 速度 -->
|
||||||
|
</div>
|
||||||
|
<div class="section-summary" v-if="exitSectionMetrics.length">
|
||||||
|
<div class="summary-item" v-for="item in exitSectionMetrics" :key="item.label">
|
||||||
|
<span class="summary-label">{{ item.label }}:</span>
|
||||||
|
<span class="summary-value">{{ item.value }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="device-grid">
|
||||||
|
<div v-for="device in exitDevicesList" :key="device.positionNameEn" class="device-card" :class="{
|
||||||
|
active: selectedCard && selectedCard.positionNameEn === device.positionNameEn,
|
||||||
|
'has-data': hasMatId(device.positionNameEn)
|
||||||
|
}" @click="selectDevice(device.positionNameEn)">
|
||||||
|
<div class="device-name">{{ device.positionNameCn }}</div>
|
||||||
|
|
||||||
|
<div class="device-status">
|
||||||
|
<div class="device-code">{{ device.positionNameEn }}</div>
|
||||||
|
<span class="coil-id"
|
||||||
|
:class="{ 'status-working': isDeviceWorking(device.positionNameEn), 'status-idle': !isDeviceWorking(device.positionNameEn) }">
|
||||||
|
{{ getDeviceStatus(device.positionNameEn) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Other Section / 其他段区域 -->
|
||||||
|
<!-- <div class="section-area" v-if="exitOtherDevicesList.length">
|
||||||
|
<div class="section-header">
|
||||||
|
Other Section
|
||||||
|
</div>
|
||||||
|
<div class="device-grid other-exit-grid">
|
||||||
|
<div v-for="device in exitOtherDevicesList" :key="device.positionNameEn" class="device-card" :class="{
|
||||||
|
active: selectedCard && selectedCard.positionNameEn === device.positionNameEn,
|
||||||
|
'has-data': hasMatId(device.positionNameEn)
|
||||||
|
}" @click="selectDevice(device.positionNameEn)">
|
||||||
|
<div class="device-name">{{ device.positionNameCn }}</div>
|
||||||
|
|
||||||
|
<div class="device-status">
|
||||||
|
<div class="device-code">{{ device.positionNameEn }}</div>
|
||||||
|
<span class="coil-id"
|
||||||
|
:class="{ 'status-working': isDeviceWorking(device.positionNameEn), 'status-idle': !isDeviceWorking(device.positionNameEn) }">
|
||||||
|
{{ getDeviceStatus(device.positionNameEn) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<!-- 右侧:信息面板 -->
|
||||||
|
<el-col :span="8">
|
||||||
<!-- Loading Status / 加载状态 -->
|
<!-- Loading Status / 加载状态 -->
|
||||||
<div v-if="isLoading" class="loading-container">
|
<div v-if="isLoading" class="loading-container">
|
||||||
<el-icon class="is-loading"><i class="el-icon-loading"></i></el-icon>
|
<el-icon class="is-loading"><i class="el-icon-loading"></i></el-icon>
|
||||||
@@ -23,9 +180,9 @@
|
|||||||
<div class="ws-status-left">
|
<div class="ws-status-left">
|
||||||
<!-- Set Values Floating Window Trigger / 设定值悬浮窗触发按钮 -->
|
<!-- Set Values Floating Window Trigger / 设定值悬浮窗触发按钮 -->
|
||||||
<el-tooltip content="Set Values" placement="top"> <!-- 设定值 -->
|
<el-tooltip content="Set Values" placement="top"> <!-- 设定值 -->
|
||||||
<el-button type="text" class="ws-open-btn" @click="openSetValuesPanel">
|
<!-- <el-button type="text" class="ws-open-btn" @click="openSetValuesPanel"> -->
|
||||||
<i class="el-icon-setting"></i>
|
<i class="el-icon-setting" @click="openSetValuesPanel"></i>
|
||||||
</el-button>
|
<!-- </el-button> -->
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip content="Measurement Data" placement="top"> <!-- 测量数据 -->
|
<el-tooltip content="Measurement Data" placement="top"> <!-- 测量数据 -->
|
||||||
<el-badge :is-dot="true" :type="socketStatus.measure ? 'success' : 'danger'">
|
<el-badge :is-dot="true" :type="socketStatus.measure ? 'success' : 'danger'">
|
||||||
@@ -56,153 +213,12 @@
|
|||||||
|
|
||||||
<!-- Right: quick links -->
|
<!-- Right: quick links -->
|
||||||
<div class="ws-status-right">
|
<div class="ws-status-right">
|
||||||
<el-button type="primary" size="small" @click="$router.push('/furnace')">Furnace Settings</el-button> <!-- 炉火设置 -->
|
<el-button type="primary" size="small" @click="$router.push('/furnace')">Furnace Settings</el-button>
|
||||||
<el-button type="primary" size="small" @click="$router.push('/drive')">Drive Settings</el-button> <!-- 传动设置 -->
|
<!-- 炉火设置 -->
|
||||||
|
<el-button type="primary" size="small" @click="$router.push('/drive')">Drive Settings</el-button>
|
||||||
|
<!-- 传动设置 -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="!isLoading" class="device-layout">
|
|
||||||
<!-- Entry Section / 入口段区域 -->
|
|
||||||
<div class="section-area entry-area">
|
|
||||||
<div class="section-header">
|
|
||||||
Entry Section <!-- 入口段 -->
|
|
||||||
<span class="section-info" v-if="positionData.entrySpeed">Speed: {{ positionData.entrySpeed.toFixed(1) }} m/min</span> <!-- 速度 -->
|
|
||||||
</div>
|
|
||||||
<div class="section-summary" v-if="entrySectionMetrics.length">
|
|
||||||
<div class="summary-item" v-for="item in entrySectionMetrics" :key="item.label">
|
|
||||||
<span class="summary-label">{{ item.label }}:</span>
|
|
||||||
<span class="summary-value">{{ item.value }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="device-grid">
|
|
||||||
<div v-for="device in entryDevicesList" :key="device.positionNameEn" class="device-card" :class="{
|
|
||||||
active: selectedCard && selectedCard.positionNameEn === device.positionNameEn,
|
|
||||||
'has-data': hasMatId(device.positionNameEn)
|
|
||||||
}" @click="selectDevice(device.positionNameEn)">
|
|
||||||
<div class="device-name">{{ device.positionNameCn }}</div>
|
|
||||||
<div class="device-code">{{ device.positionNameEn }}</div>
|
|
||||||
<div class="device-status">
|
|
||||||
<span class="coil-id" :class="{ 'status-working': isDeviceWorking(device.positionNameEn), 'status-idle': !isDeviceWorking(device.positionNameEn) }">
|
|
||||||
{{ getDeviceStatus(device.positionNameEn) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Furnace Section / 熔炉段区域 -->
|
|
||||||
<div class="section-area furnace-area">
|
|
||||||
<div class="section-header">
|
|
||||||
Furnace Section <!-- 熔炉段 -->
|
|
||||||
<span class="section-info" v-if="positionData.technologySpeed">Speed: {{ positionData.technologySpeed.toFixed(1) }} m/min</span> <!-- 速度 -->
|
|
||||||
<el-button type="text" size="small" @click="openFurCurrentPanel">
|
|
||||||
<i class="el-icon-paperclip"></i>
|
|
||||||
More <!-- 更多 -->
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
<div class="section-summary" v-if="furnaceSectionMetrics.length">
|
|
||||||
<div class="summary-item" v-for="item in furnaceSectionMetrics" :key="item.label">
|
|
||||||
<span class="summary-label">{{ item.label }}:</span>
|
|
||||||
<span class="summary-value">{{ item.value }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="device-grid">
|
|
||||||
<div v-for="device in furnaceDevicesList" :key="device.positionNameEn" class="device-card" :class="{
|
|
||||||
active: selectedCard && selectedCard.positionNameEn === device.positionNameEn,
|
|
||||||
'has-data': hasMatId(device.positionNameEn)
|
|
||||||
}" @click="selectDevice(device.positionNameEn)">
|
|
||||||
<div class="device-name">{{ device.positionNameCn }}</div>
|
|
||||||
<div class="device-code">{{ device.positionNameEn }}</div>
|
|
||||||
<div class="device-status">
|
|
||||||
<span class="coil-id" :class="{ 'status-working': isDeviceWorking(device.positionNameEn), 'status-idle': !isDeviceWorking(device.positionNameEn) }">
|
|
||||||
{{ getDeviceStatus(device.positionNameEn) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Coating Section / 涂层段区域 -->
|
|
||||||
<div class="section-area coat-area">
|
|
||||||
<div class="section-header">
|
|
||||||
Coating Section <!-- 涂层段 -->
|
|
||||||
<span class="section-info" v-if="positionData.coatSpeed">Speed: {{ positionData.coatSpeed.toFixed(1) }} m/min</span> <!-- 速度 -->
|
|
||||||
</div>
|
|
||||||
<div class="section-summary" v-if="coatSectionMetrics.length">
|
|
||||||
<div class="summary-item" v-for="item in coatSectionMetrics" :key="item.label">
|
|
||||||
<span class="summary-label">{{ item.label }}:</span>
|
|
||||||
<span class="summary-value">{{ item.value }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="device-grid">
|
|
||||||
<div v-for="device in coatDevicesList" :key="device.positionNameEn" class="device-card" :class="{
|
|
||||||
active: selectedCard && selectedCard.positionNameEn === device.positionNameEn,
|
|
||||||
'has-data': hasMatId(device.positionNameEn)
|
|
||||||
}" @click="selectDevice(device.positionNameEn)">
|
|
||||||
<div class="device-name">{{ device.positionNameCn }}</div>
|
|
||||||
<div class="device-code">{{ device.positionNameEn }}</div>
|
|
||||||
<div class="device-status">
|
|
||||||
<span class="coil-id" :class="{ 'status-working': isDeviceWorking(device.positionNameEn), 'status-idle': !isDeviceWorking(device.positionNameEn) }">
|
|
||||||
{{ getDeviceStatus(device.positionNameEn) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Exit Section / 出口段区域 -->
|
|
||||||
<div class="section-area exit-area">
|
|
||||||
<div class="section-header">
|
|
||||||
Exit Section <!-- 出口段 -->
|
|
||||||
<span class="section-info" v-if="positionData.exitSpeed">Speed: {{ positionData.exitSpeed.toFixed(1) }} m/min</span> <!-- 速度 -->
|
|
||||||
</div>
|
|
||||||
<div class="section-summary" v-if="exitSectionMetrics.length">
|
|
||||||
<div class="summary-item" v-for="item in exitSectionMetrics" :key="item.label">
|
|
||||||
<span class="summary-label">{{ item.label }}:</span>
|
|
||||||
<span class="summary-value">{{ item.value }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="device-grid">
|
|
||||||
<div v-for="device in exitDevicesList" :key="device.positionNameEn" class="device-card" :class="{
|
|
||||||
active: selectedCard && selectedCard.positionNameEn === device.positionNameEn,
|
|
||||||
'has-data': hasMatId(device.positionNameEn)
|
|
||||||
}" @click="selectDevice(device.positionNameEn)">
|
|
||||||
<div class="device-name">{{ device.positionNameCn }}</div>
|
|
||||||
<div class="device-code">{{ device.positionNameEn }}</div>
|
|
||||||
<div class="device-status">
|
|
||||||
<span class="coil-id" :class="{ 'status-working': isDeviceWorking(device.positionNameEn), 'status-idle': !isDeviceWorking(device.positionNameEn) }">
|
|
||||||
{{ getDeviceStatus(device.positionNameEn) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Other Section / 其他段区域 -->
|
|
||||||
<div class="section-area" v-if="exitOtherDevicesList.length">
|
|
||||||
<div class="section-header">
|
|
||||||
Other Section <!-- 其他段 -->
|
|
||||||
</div>
|
|
||||||
<div class="device-grid other-exit-grid">
|
|
||||||
<div v-for="device in exitOtherDevicesList" :key="device.positionNameEn" class="device-card" :class="{
|
|
||||||
active: selectedCard && selectedCard.positionNameEn === device.positionNameEn,
|
|
||||||
'has-data': hasMatId(device.positionNameEn)
|
|
||||||
}" @click="selectDevice(device.positionNameEn)">
|
|
||||||
<div class="device-name">{{ device.positionNameCn }}</div>
|
|
||||||
<div class="device-code">{{ device.positionNameEn }}</div>
|
|
||||||
<div class="device-status">
|
|
||||||
<span class="coil-id" :class="{ 'status-working': isDeviceWorking(device.positionNameEn), 'status-idle': !isDeviceWorking(device.positionNameEn) }">
|
|
||||||
{{ getDeviceStatus(device.positionNameEn) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
|
|
||||||
<!-- 右侧:信息面板 -->
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="info-panels">
|
<div class="info-panels">
|
||||||
<!-- Production Plan List / 生产计划列表 -->
|
<!-- Production Plan List / 生产计划列表 -->
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
@@ -210,22 +226,18 @@
|
|||||||
<i class="el-icon-s-order"></i> Production Plan <!-- 生产计划 -->
|
<i class="el-icon-s-order"></i> Production Plan <!-- 生产计划 -->
|
||||||
</div>
|
</div>
|
||||||
<div class="plan-list-vertical">
|
<div class="plan-list-vertical">
|
||||||
<el-empty v-if="planQueue.length === 0" description="No production plan" :image-size="80" /> <!-- 暂无生产计划 -->
|
<el-empty v-if="planQueue.length === 0" description="No production plan" :image-size="80" />
|
||||||
<div
|
<!-- 暂无生产计划 -->
|
||||||
v-else
|
<div v-else v-for="(plan, index) in planQueue" :key="plan.id || plan.planid || index"
|
||||||
v-for="(plan, index) in planQueue"
|
class="plan-item-vertical" :class="getPlanItemClass(plan)" @click="selectPlan(plan)">
|
||||||
:key="plan.id || plan.planid || index"
|
|
||||||
class="plan-item-vertical"
|
|
||||||
:class="getPlanItemClass(plan)"
|
|
||||||
@click="selectPlan(plan)"
|
|
||||||
>
|
|
||||||
<div class="plan-item-top">
|
<div class="plan-item-top">
|
||||||
<div class="plan-order-dot" :class="getPlanOrderClass(plan.status)">{{ index + 1 }}</div>
|
<div class="plan-order-dot" :class="getPlanOrderClass(plan.status)">{{ index + 1 }}</div>
|
||||||
<div class="plan-title-text">
|
<div class="plan-title-text">
|
||||||
<div class="plan-id">Plan ID: {{ plan.planid || '-' }}</div> <!-- 计划号 -->
|
<div class="plan-id">Plan ID: {{ plan.planid || '-' }}</div> <!-- 计划号 -->
|
||||||
<div class="plan-coil">Coil ID: {{ plan.coilid || '-' }}</div> <!-- 钢卷号 -->
|
<div class="plan-coil">Coil ID: {{ plan.coilid || '-' }}</div> <!-- 钢卷号 -->
|
||||||
</div>
|
</div>
|
||||||
<el-tag :type="getPlanStatusTagType(plan.status)" size="mini">{{ getPlanStatusText(plan.status) }}</el-tag>
|
<el-tag :type="getPlanStatusTagType(plan.status)" size="mini">{{ getPlanStatusText(plan.status)
|
||||||
|
}}</el-tag>
|
||||||
</div>
|
</div>
|
||||||
<div class="plan-item-bottom">
|
<div class="plan-item-bottom">
|
||||||
<span>Steel Grade: {{ plan.steelGrade || '-' }}</span> <!-- 钢种 -->
|
<span>Steel Grade: {{ plan.steelGrade || '-' }}</span> <!-- 钢种 -->
|
||||||
@@ -256,15 +268,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-descriptions :column="1" border size="small">
|
<el-descriptions :column="1" border size="small">
|
||||||
<el-descriptions-item label="Plan ID">{{ selectedPlan.planid || '-' }}</el-descriptions-item> <!-- 计划号 -->
|
<el-descriptions-item label="Plan ID">{{ selectedPlan.planid || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="Coil ID">{{ selectedPlan.coilid || '-' }}</el-descriptions-item> <!-- 钢卷号 -->
|
<!-- 计划号 -->
|
||||||
<el-descriptions-item label="Sequence No">{{ selectedPlan.seqid || '-' }}</el-descriptions-item> <!-- 顺序号 -->
|
<el-descriptions-item label="Coil ID">{{ selectedPlan.coilid || '-' }}</el-descriptions-item>
|
||||||
|
<!-- 钢卷号 -->
|
||||||
|
<el-descriptions-item label="Sequence No">{{ selectedPlan.seqid || '-' }}</el-descriptions-item>
|
||||||
|
<!-- 顺序号 -->
|
||||||
<el-descriptions-item label="Status"> <!-- 状态 -->
|
<el-descriptions-item label="Status"> <!-- 状态 -->
|
||||||
<el-tag :type="getPlanStatusTagType(selectedPlan.status)" size="small" effect="dark">
|
<el-tag :type="getPlanStatusTagType(selectedPlan.status)" size="small" effect="dark">
|
||||||
{{ getPlanStatusText(selectedPlan.status) }}
|
{{ getPlanStatusText(selectedPlan.status) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="Steel Grade">{{ selectedPlan.steelGrade || '-' }}</el-descriptions-item> <!-- 钢种 -->
|
<el-descriptions-item label="Steel Grade">{{ selectedPlan.steelGrade || '-' }}</el-descriptions-item>
|
||||||
|
<!-- 钢种 -->
|
||||||
<el-descriptions-item label="Entry Thickness"> <!-- 入口厚度 -->
|
<el-descriptions-item label="Entry Thickness"> <!-- 入口厚度 -->
|
||||||
{{ selectedPlan.entryThick ? selectedPlan.entryThick + ' mm' : '-' }}
|
{{ selectedPlan.entryThick ? selectedPlan.entryThick + ' mm' : '-' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
@@ -277,13 +293,17 @@
|
|||||||
<el-descriptions-item label="Entry Length"> <!-- 入口长度 -->
|
<el-descriptions-item label="Entry Length"> <!-- 入口长度 -->
|
||||||
{{ selectedPlan.entryLength ? selectedPlan.entryLength + ' mm' : '-' }}
|
{{ selectedPlan.entryLength ? selectedPlan.entryLength + ' mm' : '-' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="Order No">{{ selectedPlan.orderNo || '-' }}</el-descriptions-item> <!-- 订单号 -->
|
<el-descriptions-item label="Order No">{{ selectedPlan.orderNo || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="Unit Code">{{ selectedPlan.unitCode || '-' }}</el-descriptions-item> <!-- 机组号 -->
|
<!-- 订单号 -->
|
||||||
<el-descriptions-item label="Plan Type">{{ selectedPlan.planType || '-' }}</el-descriptions-item> <!-- 计划类型 -->
|
<el-descriptions-item label="Unit Code">{{ selectedPlan.unitCode || '-' }}</el-descriptions-item>
|
||||||
|
<!-- 机组号 -->
|
||||||
|
<el-descriptions-item label="Plan Type">{{ selectedPlan.planType || '-' }}</el-descriptions-item>
|
||||||
|
<!-- 计划类型 -->
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
|
|
||||||
<!-- Time Information / 时间信息 -->
|
<!-- Time Information / 时间信息 -->
|
||||||
<div class="plan-time-info" v-if="selectedPlan.onlineDate || selectedPlan.startDate || selectedPlan.endDate">
|
<div class="plan-time-info"
|
||||||
|
v-if="selectedPlan.onlineDate || selectedPlan.startDate || selectedPlan.endDate">
|
||||||
<div class="info-subtitle">Time Information</div> <!-- 时间信息 -->
|
<div class="info-subtitle">Time Information</div> <!-- 时间信息 -->
|
||||||
<el-descriptions :column="1" border size="small">
|
<el-descriptions :column="1" border size="small">
|
||||||
<el-descriptions-item label="Online Time" v-if="selectedPlan.onlineDate"> <!-- 上线时间 -->
|
<el-descriptions-item label="Online Time" v-if="selectedPlan.onlineDate"> <!-- 上线时间 -->
|
||||||
@@ -305,10 +325,7 @@
|
|||||||
<div class="panel-title">
|
<div class="panel-title">
|
||||||
<i class="el-icon-bell"></i> Recent Operation
|
<i class="el-icon-bell"></i> Recent Operation
|
||||||
<!-- 最近操作 -->
|
<!-- 最近操作 -->
|
||||||
<el-tag
|
<el-tag :type="getOperationTagType(signalData.operation)" size="mini" effect="dark">
|
||||||
:type="getOperationTagType(signalData.operation)"
|
|
||||||
size="mini"
|
|
||||||
effect="dark">
|
|
||||||
{{ getOperationConfig(signalData.operation).icon }} {{ getOperationText(signalData.operation) }}
|
{{ getOperationConfig(signalData.operation).icon }} {{ getOperationText(signalData.operation) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</div>
|
</div>
|
||||||
@@ -341,13 +358,16 @@
|
|||||||
<div class="panel">
|
<div class="panel">
|
||||||
<div class="panel-title">Operation Buttons</div> <!-- 操作按钮 -->
|
<div class="panel-title">Operation Buttons</div> <!-- 操作按钮 -->
|
||||||
<div class="btn-list">
|
<div class="btn-list">
|
||||||
<el-button class="action-btn" size="small" @click="handleOperate(selectedCard, 'ONLINE')">Coil Online</el-button> <!-- 钢卷上线 -->
|
<el-button class="action-btn" size="small" @click="handleOperate(selectedCard, 'ONLINE')">Coil
|
||||||
<el-button class="action-btn" size="small" @click="handleOperate(selectedCard, 'UNLOAD')">Manual Unload</el-button> <!-- 手动卸卷 -->
|
Online</el-button> <!-- 钢卷上线 -->
|
||||||
<el-button class="action-btn" size="small"
|
<el-button class="action-btn" size="small" @click="handleOperate(selectedCard, 'UNLOAD')">Manual
|
||||||
@click="handleOperate(selectedCard, 'ALL_RETURN')">Full Return</el-button> <!-- 整卷回退 -->
|
Unload</el-button> <!-- 手动卸卷 -->
|
||||||
<el-button class="action-btn" size="small"
|
<el-button class="action-btn" size="small" @click="handleOperate(selectedCard, 'ALL_RETURN')">Full
|
||||||
@click="handleOperate(selectedCard, 'HALF_RETURN')">Half Return</el-button> <!-- 半卷回退 -->
|
Return</el-button> <!-- 整卷回退 -->
|
||||||
<el-button class="action-btn" size="small" @click="handleOperate(selectedCard, 'BLOCK')">Unload & Block</el-button> <!-- 卸卷并封闭 -->
|
<el-button class="action-btn" size="small" @click="handleOperate(selectedCard, 'HALF_RETURN')">Half
|
||||||
|
Return</el-button> <!-- 半卷回退 -->
|
||||||
|
<el-button class="action-btn" size="small" @click="handleOperate(selectedCard, 'BLOCK')">Unload &
|
||||||
|
Block</el-button> <!-- 卸卷并封闭 -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Device Basic Info / 设备基本信息 -->
|
<!-- Device Basic Info / 设备基本信息 -->
|
||||||
@@ -384,44 +404,22 @@
|
|||||||
<!-- Adjustment Tool: Select two positions with two dropdowns / 调整工具,选择两个位置,两个下拉选,分别双向绑定 -->
|
<!-- Adjustment Tool: Select two positions with two dropdowns / 调整工具,选择两个位置,两个下拉选,分别双向绑定 -->
|
||||||
<el-form :model="adjustForm" ref="adjustForm" label-width="120px" class="adjust-form">
|
<el-form :model="adjustForm" ref="adjustForm" label-width="120px" class="adjust-form">
|
||||||
<el-form-item label="Current Position" prop="current"> <!-- 当前位置 -->
|
<el-form-item label="Current Position" prop="current"> <!-- 当前位置 -->
|
||||||
<el-select
|
<el-select v-model="adjustForm.current" placeholder="Please select current position"
|
||||||
v-model="adjustForm.current"
|
class="adjust-select" filterable> <!-- 请选择当前位置 -->
|
||||||
placeholder="Please select current position"
|
<el-option v-for="item in matMapList" :key="item.positionNameEn" :label="item.positionNameCn"
|
||||||
class="adjust-select"
|
:value="item.positionNameEn" class="adjust-option"></el-option>
|
||||||
filterable
|
|
||||||
> <!-- 请选择当前位置 -->
|
|
||||||
<el-option
|
|
||||||
v-for="item in matMapList"
|
|
||||||
:key="item.positionNameEn"
|
|
||||||
:label="item.positionNameCn"
|
|
||||||
:value="item.positionNameEn"
|
|
||||||
class="adjust-option"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="Target Position" prop="target"> <!-- 目标位置 -->
|
<el-form-item label="Target Position" prop="target"> <!-- 目标位置 -->
|
||||||
<el-select
|
<el-select v-model="adjustForm.target" placeholder="Please select target position" class="adjust-select"
|
||||||
v-model="adjustForm.target"
|
filterable> <!-- 请选择目标位置 -->
|
||||||
placeholder="Please select target position"
|
<el-option v-for="item in matMapList" :key="item.positionNameEn" :label="item.positionNameCn"
|
||||||
class="adjust-select"
|
:value="item.positionNameEn" class="adjust-option"></el-option>
|
||||||
filterable
|
|
||||||
> <!-- 请选择目标位置 -->
|
|
||||||
<el-option
|
|
||||||
v-for="item in matMapList"
|
|
||||||
:key="item.positionNameEn"
|
|
||||||
:label="item.positionNameCn"
|
|
||||||
:value="item.positionNameEn"
|
|
||||||
class="adjust-option"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<el-button
|
<el-button type="primary" :disabled="!adjustForm.current || !adjustForm.target" @click="handleConfirmAdjust"
|
||||||
type="primary"
|
class="adjust-confirm-btn">Confirm Adjustment</el-button> <!-- 确认调整 -->
|
||||||
:disabled="!adjustForm.current || !adjustForm.target"
|
|
||||||
@click="handleConfirmAdjust"
|
|
||||||
class="adjust-confirm-btn"
|
|
||||||
>Confirm Adjustment</el-button> <!-- 确认调整 -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -429,25 +427,17 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<!-- Calculation Setup Result Dialog / 计算结果对话框 -->
|
<!-- Calculation Setup Result Dialog / 计算结果对话框 -->
|
||||||
<el-dialog
|
<el-dialog title="Calculation Result" :visible.sync="showCalcResultDialog" width="80%" v-if="calcSetupResult">
|
||||||
title="Calculation Result"
|
|
||||||
:visible.sync="showCalcResultDialog"
|
|
||||||
width="80%"
|
|
||||||
v-if="calcSetupResult"
|
|
||||||
>
|
|
||||||
<div class="calc-result-header" v-if="calcSetupResult">
|
<div class="calc-result-header" v-if="calcSetupResult">
|
||||||
<el-tag :type="calcSetupResult && calcSetupResult.flag ? 'success' : 'danger'">
|
<el-tag :type="calcSetupResult && calcSetupResult.flag ? 'success' : 'danger'">
|
||||||
{{ calcSetupResult && calcSetupResult.flag ? 'Calculation Successful' : 'Calculation Failed' }} <!-- 计算成功 / 计算失败 -->
|
{{ calcSetupResult && calcSetupResult.flag ? 'Calculation Successful' : 'Calculation Failed' }}
|
||||||
|
<!-- 计算成功 / 计算失败 -->
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<span>Key: {{ calcSetupResult && calcSetupResult.key ? calcSetupResult.key : '-' }}</span>
|
<span>Key: {{ calcSetupResult && calcSetupResult.key ? calcSetupResult.key : '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<el-table
|
<el-table
|
||||||
v-if="calcSetupResult && calcSetupResult.flag && calcSetupResult.lists && calcSetupResult.lists.length > 0"
|
v-if="calcSetupResult && calcSetupResult.flag && calcSetupResult.lists && calcSetupResult.lists.length > 0"
|
||||||
:data="calcSetupResult.lists"
|
:data="calcSetupResult.lists" border stripe max-height="500">
|
||||||
border
|
|
||||||
stripe
|
|
||||||
max-height="500"
|
|
||||||
>
|
|
||||||
<el-table-column prop="passno" label="Pass No" width="80" fixed></el-table-column> <!-- 道次号 -->
|
<el-table-column prop="passno" label="Pass No" width="80" fixed></el-table-column> <!-- 道次号 -->
|
||||||
<el-table-column prop="entryThick" label="Entry Thickness (mm)" width="120"></el-table-column> <!-- 入口厚度 -->
|
<el-table-column prop="entryThick" label="Entry Thickness (mm)" width="120"></el-table-column> <!-- 入口厚度 -->
|
||||||
<el-table-column prop="exitThick" label="Exit Thickness (mm)" width="120"></el-table-column> <!-- 出口厚度 -->
|
<el-table-column prop="exitThick" label="Exit Thickness (mm)" width="120"></el-table-column> <!-- 出口厚度 -->
|
||||||
@@ -487,10 +477,12 @@
|
|||||||
<!-- Return Related Fields / 回退相关字段 -->
|
<!-- Return Related Fields / 回退相关字段 -->
|
||||||
<template v-if="['ALL_RETURN', 'HALF_RETURN'].includes(operateMatForm.operation)">
|
<template v-if="['ALL_RETURN', 'HALF_RETURN'].includes(operateMatForm.operation)">
|
||||||
<el-form-item label="Return Coil ID" prop="returnMatId"> <!-- 回退钢卷号 -->
|
<el-form-item label="Return Coil ID" prop="returnMatId"> <!-- 回退钢卷号 -->
|
||||||
<el-input v-model="operateMatForm.returnMatId" placeholder="Please enter Return Coil ID"></el-input> <!-- 请输入回退卷号 -->
|
<el-input v-model="operateMatForm.returnMatId" placeholder="Please enter Return Coil ID"></el-input>
|
||||||
|
<!-- 请输入回退卷号 -->
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="Return Weight" prop="returnWeight"> <!-- 回退重量 -->
|
<el-form-item label="Return Weight" prop="returnWeight"> <!-- 回退重量 -->
|
||||||
<el-input v-model="operateMatForm.returnWeight" placeholder="Please enter Return Weight"></el-input> <!-- 请输入回退重量 -->
|
<el-input v-model="operateMatForm.returnWeight" placeholder="Please enter Return Weight"></el-input>
|
||||||
|
<!-- 请输入回退重量 -->
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="Return Remark" prop="returnRemark"> <!-- 回退备注 -->
|
<el-form-item label="Return Remark" prop="returnRemark"> <!-- 回退备注 -->
|
||||||
<el-input v-model="operateMatForm.returnRemark" rows="3"></el-input>
|
<el-input v-model="operateMatForm.returnRemark" rows="3"></el-input>
|
||||||
@@ -500,7 +492,8 @@
|
|||||||
<!-- Output Length Fields / 产出长度字段 -->
|
<!-- Output Length Fields / 产出长度字段 -->
|
||||||
<template v-if="['PRODUCING', 'PRODUCT'].includes(operateMatForm.operation)">
|
<template v-if="['PRODUCING', 'PRODUCT'].includes(operateMatForm.operation)">
|
||||||
<el-form-item label="Output Coil Length" prop="coilLength"> <!-- 产出钢卷长度 -->
|
<el-form-item label="Output Coil Length" prop="coilLength"> <!-- 产出钢卷长度 -->
|
||||||
<el-input v-model="operateMatForm.coilLength" type="number" placeholder="Please enter Output Coil Length"></el-input> <!-- 请输入产出钢卷长度 -->
|
<el-input v-model="operateMatForm.coilLength" type="number"
|
||||||
|
placeholder="Please enter Output Coil Length"></el-input> <!-- 请输入产出钢卷长度 -->
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
</el-form>
|
</el-form>
|
||||||
@@ -541,15 +534,17 @@ const DEVICE_META = {
|
|||||||
TOWER: { sectionType: 'PROCESS', sourceType: 'COAT', paramFields: ['scsExitStripTemp'] },
|
TOWER: { sectionType: 'PROCESS', sourceType: 'COAT', paramFields: ['scsExitStripTemp'] },
|
||||||
TM: { sectionType: 'PROCESS', sourceType: 'COAT', paramFields: ['tensionBr5Tm', 'stripSpeedTmExit'] },
|
TM: { sectionType: 'PROCESS', sourceType: 'COAT', paramFields: ['tensionBr5Tm', 'stripSpeedTmExit'] },
|
||||||
TL: { sectionType: 'PROCESS', sourceType: 'COAT', paramFields: ['tlElongation', 'tensionTlBr7'] },
|
TL: { sectionType: 'PROCESS', sourceType: 'COAT', paramFields: ['tlElongation', 'tensionTlBr7'] },
|
||||||
COAT: { sectionType: 'PROCESS', sourceType: 'COAT', paramFields: [
|
COAT: {
|
||||||
'avrCoatingWeightTop','stdCoatingWeightTop','maxCoatingWeightTop','minCoatingWeightTop',
|
sectionType: 'PROCESS', sourceType: 'COAT', paramFields: [
|
||||||
'avrCoatingWeightBottom','stdCoatingWeightBottom','maxCoatingWeightBottom','minCoatingWeightBottom',
|
'avrCoatingWeightTop', 'stdCoatingWeightTop', 'maxCoatingWeightTop', 'minCoatingWeightTop',
|
||||||
'airKnifePressure','airKnifeFlow','airKnifeGap','stripSpeedTmExit','tensionBr8Tm',
|
'avrCoatingWeightBottom', 'stdCoatingWeightBottom', 'maxCoatingWeightBottom', 'minCoatingWeightBottom',
|
||||||
'tensionTmBr9','tensionBr8Br9','tmMask','tmElongation','rollForceOperator','rollForceDrive',
|
'airKnifePressure', 'airKnifeFlow', 'airKnifeGap', 'stripSpeedTmExit', 'tensionBr8Tm',
|
||||||
'motorTorque','bendingForce','antiCrimpingRollMesh','billyRollMesh',
|
'tensionTmBr9', 'tensionBr8Br9', 'tmMask', 'tmElongation', 'rollForceOperator', 'rollForceDrive',
|
||||||
'tensionTlBr10Br11','tensionBr9toBr10Br11','tlFlag','tlElongation','levelingUnit1Mesh','levelingUnit2Mesh',
|
'motorTorque', 'bendingForce', 'antiCrimpingRollMesh', 'billyRollMesh',
|
||||||
'antiCrossBowUnitMesh','tensionBr10Br11toBr12','stripSpeedAfp','stripTempAfp'
|
'tensionTlBr10Br11', 'tensionBr9toBr10Br11', 'tlFlag', 'tlElongation', 'levelingUnit1Mesh', 'levelingUnit2Mesh',
|
||||||
] },
|
'antiCrossBowUnitMesh', 'tensionBr10Br11toBr12', 'stripSpeedAfp', 'stripTempAfp'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
CXL1: { sectionType: 'EXIT', sourceType: 'EXIT', paramFields: ['cxlLength', 'cxlCapacity', 'tensionCxl'] },
|
CXL1: { sectionType: 'EXIT', sourceType: 'EXIT', paramFields: ['cxlLength', 'cxlCapacity', 'tensionCxl'] },
|
||||||
CXL2: { sectionType: 'EXIT', sourceType: 'EXIT', paramFields: ['cxlLength', 'cxlCapacity', 'tensionCxl'] },
|
CXL2: { sectionType: 'EXIT', sourceType: 'EXIT', paramFields: ['cxlLength', 'cxlCapacity', 'tensionCxl'] },
|
||||||
@@ -896,7 +891,8 @@ export default {
|
|||||||
const exitDevices = this.getDevicesBySection('EXIT')
|
const exitDevices = this.getDevicesBySection('EXIT')
|
||||||
const looper = this.matMapList.find(d => ['CXL1', 'CXL2'].includes(d.positionNameEn))
|
const looper = this.matMapList.find(d => ['CXL1', 'CXL2'].includes(d.positionNameEn))
|
||||||
|
|
||||||
const base = exitDevices.filter(d => !['INS', 'EXC', 'WEIGHT', 'CXL1', 'CXL2'].includes(d.positionNameEn))
|
const base = exitDevices
|
||||||
|
// .filter(d => !['INS', 'EXC', 'WEIGHT', 'CXL1', 'CXL2'].includes(d.positionNameEn))
|
||||||
if (!looper) return base
|
if (!looper) return base
|
||||||
const tr = this.matMapList.find(d => d.positionNameEn === 'TR')
|
const tr = this.matMapList.find(d => d.positionNameEn === 'TR')
|
||||||
if (!tr) return base.concat([{ ...looper, positionNameEn: 'CXL', positionNameCn: 'Exit Looper' }])
|
if (!tr) return base.concat([{ ...looper, positionNameEn: 'CXL', positionNameCn: 'Exit Looper' }])
|
||||||
@@ -1179,7 +1175,7 @@ export default {
|
|||||||
|
|
||||||
processSignalData(data) {
|
processSignalData(data) {
|
||||||
// 先记录是否为连续生产中信号 Record if it's a continuous producing signal
|
// 先记录是否为连续生产中信号 Record if it's a continuous producing signal
|
||||||
const repeatProducing =this.lastSignalOperation === 'PRODUCING' && data.operation === 'PRODUCING'
|
const repeatProducing = this.lastSignalOperation === 'PRODUCING' && data.operation === 'PRODUCING'
|
||||||
|
|
||||||
this.signalData = data
|
this.signalData = data
|
||||||
const operationText = this.getOperationText(data.operation)
|
const operationText = this.getOperationText(data.operation)
|
||||||
@@ -1187,13 +1183,13 @@ export default {
|
|||||||
const config = this.getOperationConfig(data.operation)
|
const config = this.getOperationConfig(data.operation)
|
||||||
|
|
||||||
// 检测到上线、生产中、生产完成等关键操作时,刷新生产计划队列 Refresh plan queue when detecting key operations
|
// 检测到上线、生产中、生产完成等关键操作时,刷新生产计划队列 Refresh plan queue when detecting key operations
|
||||||
if (['ONLINE', 'PRODUCING', 'PRODUCT'].includes(data.operation) ) {
|
if (['ONLINE', 'PRODUCING', 'PRODUCT'].includes(data.operation)) {
|
||||||
console.log(`Detected ${operationText} operation, refreshing plan queue...`) // 检测到${operationText}操作,刷新生产计划队列...
|
console.log(`Detected ${operationText} operation, refreshing plan queue...`) // 检测到${operationText}操作,刷新生产计划队列...
|
||||||
this.scheduleRefreshPlanQueue()
|
this.scheduleRefreshPlanQueue()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 右上角通知(所有操作都显示) Top-right notification (display for all operations)
|
// 右上角通知(所有操作都显示) Top-right notification (display for all operations)
|
||||||
if(!repeatProducing){
|
if (!repeatProducing) {
|
||||||
this.$notify({
|
this.$notify({
|
||||||
title: `${config.icon} ${config.title}`,
|
title: `${config.icon} ${config.title}`,
|
||||||
message: `${autoFlagText} ${operationText}\nCoil ID: ${data.entryMatId}\nPlan ID: ${data.planId || '-'}`, // 钢卷号 / 计划ID
|
message: `${autoFlagText} ${operationText}\nCoil ID: ${data.entryMatId}\nPlan ID: ${data.planId || '-'}`, // 钢卷号 / 计划ID
|
||||||
@@ -1856,17 +1852,16 @@ export default {
|
|||||||
.device-layout {
|
.device-layout {
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 15px;
|
height: calc(100vh - 84px);
|
||||||
height: calc(100vh - 60px);
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 分区区域 */
|
/* 分区区域 */
|
||||||
.section-area {
|
.section-area {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 10px;
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 12px;
|
padding: 6px;
|
||||||
border: 1px solid #e8e8e8;
|
border: 1px solid #e8e8e8;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1874,8 +1869,8 @@ export default {
|
|||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #333;
|
color: #333;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 6px;
|
||||||
padding-bottom: 8px;
|
padding-bottom: 6px;
|
||||||
border-bottom: 2px solid #ddd;
|
border-bottom: 2px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1883,18 +1878,17 @@ export default {
|
|||||||
.device-grid {
|
.device-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||||||
gap: 12px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 设备卡片 */
|
/* 设备卡片 */
|
||||||
.device-card {
|
.device-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 2px solid #e0e0e0;
|
border: 2px solid #e0e0e0;
|
||||||
border-radius: 6px;
|
padding: 6px;
|
||||||
padding: 12px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
min-height: 120px;
|
/* min-height: 100px; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-card:hover {
|
.device-card:hover {
|
||||||
@@ -1933,12 +1927,16 @@ export default {
|
|||||||
.device-code {
|
.device-code {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: #999;
|
color: #999;
|
||||||
margin-bottom: 6px;
|
/* margin-bottom: 6px; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-status {
|
.device-status {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
|
display: flex;
|
||||||
|
/* justify-content: space-between; */
|
||||||
|
gap: 6px;
|
||||||
|
align-items: center;
|
||||||
border-top: 1px solid #f0f0f0;
|
border-top: 1px solid #f0f0f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1962,7 +1960,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mini-data {
|
.mini-data {
|
||||||
font-size: 11px;
|
font-size: 10px;
|
||||||
color: #666;
|
color: #666;
|
||||||
margin-bottom: 3px;
|
margin-bottom: 3px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1980,8 +1978,8 @@ export default {
|
|||||||
|
|
||||||
.section-summary {
|
.section-summary {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||||||
gap: 10px;
|
gap: 6px;
|
||||||
margin: 8px 0 14px;
|
margin: 8px 0 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1990,7 +1988,7 @@ export default {
|
|||||||
border: 1px solid #e0e6ed;
|
border: 1px solid #e0e6ed;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
font-size: 13px;
|
font-size: 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -2008,14 +2006,37 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 段落色系 */
|
/* 段落色系 */
|
||||||
.entry-area .section-header { border-bottom-color: #409eff; }
|
.entry-area .section-header {
|
||||||
.entry-area .summary-value { color: #409eff; }
|
border-bottom-color: #409eff;
|
||||||
.furnace-area .section-header { border-bottom-color: #e6a23c; }
|
}
|
||||||
.furnace-area .summary-value { color: #e6a23c; }
|
|
||||||
.coat-area .section-header { border-bottom-color: #67c23a; }
|
.entry-area .summary-value {
|
||||||
.coat-area .summary-value { color: #67c23a; }
|
color: #409eff;
|
||||||
.exit-area .section-header { border-bottom-color: #909399; }
|
}
|
||||||
.exit-area .summary-value { color: #909399; }
|
|
||||||
|
.furnace-area .section-header {
|
||||||
|
border-bottom-color: #e6a23c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.furnace-area .summary-value {
|
||||||
|
color: #e6a23c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coat-area .section-header {
|
||||||
|
border-bottom-color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coat-area .summary-value {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exit-area .section-header {
|
||||||
|
border-bottom-color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exit-area .summary-value {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
@@ -2132,8 +2153,13 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes rotating {
|
@keyframes rotating {
|
||||||
from { transform: rotate(0deg); }
|
from {
|
||||||
to { transform: rotate(360deg); }
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 生产计划队列面板 */
|
/* 生产计划队列面板 */
|
||||||
@@ -2283,10 +2309,22 @@ export default {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plan-order-dot.order-producing { background: #67c23a; animation: blink-green-dot 1.2s infinite; }
|
.plan-order-dot.order-producing {
|
||||||
.plan-order-dot.order-online { background: #e6a23c; }
|
background: #67c23a;
|
||||||
.plan-order-dot.order-ready { background: #409eff; }
|
animation: blink-green-dot 1.2s infinite;
|
||||||
.plan-order-dot.order-new { background: #909399; }
|
}
|
||||||
|
|
||||||
|
.plan-order-dot.order-online {
|
||||||
|
background: #e6a23c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-order-dot.order-ready {
|
||||||
|
background: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-order-dot.order-new {
|
||||||
|
background: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
.plan-title-text {
|
.plan-title-text {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -2297,8 +2335,13 @@ export default {
|
|||||||
color: #303133;
|
color: #303133;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plan-id { font-weight: 600; }
|
.plan-id {
|
||||||
.plan-coil { color: #606266; }
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-coil {
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
.plan-item-bottom {
|
.plan-item-bottom {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -2368,10 +2411,13 @@ export default {
|
|||||||
|
|
||||||
/* 绿色闪烁动画 */
|
/* 绿色闪烁动画 */
|
||||||
@keyframes pulse-green {
|
@keyframes pulse-green {
|
||||||
0%, 100% {
|
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
border-color: #67c23a;
|
border-color: #67c23a;
|
||||||
box-shadow: 0 0 0 0 rgba(103, 194, 58, 0.4);
|
box-shadow: 0 0 0 0 rgba(103, 194, 58, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
border-color: #85ce61;
|
border-color: #85ce61;
|
||||||
box-shadow: 0 0 0 4px rgba(103, 194, 58, 0.6), 0 0 15px rgba(103, 194, 58, 0.4);
|
box-shadow: 0 0 0 4px rgba(103, 194, 58, 0.6), 0 0 15px rgba(103, 194, 58, 0.4);
|
||||||
@@ -2411,10 +2457,13 @@ export default {
|
|||||||
|
|
||||||
/* 绿色点闪烁动画 */
|
/* 绿色点闪烁动画 */
|
||||||
@keyframes blink-green-dot {
|
@keyframes blink-green-dot {
|
||||||
0%, 100% {
|
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
background: #67c23a;
|
background: #67c23a;
|
||||||
box-shadow: 0 0 5px rgba(103, 194, 58, 0.5);
|
box-shadow: 0 0 5px rgba(103, 194, 58, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
background: #85ce61;
|
background: #85ce61;
|
||||||
box-shadow: 0 0 10px rgba(103, 194, 58, 0.8);
|
box-shadow: 0 0 10px rgba(103, 194, 58, 0.8);
|
||||||
|
|||||||
Reference in New Issue
Block a user