Files
mesfront/src/views/mes/board/workshop/index.vue

1097 lines
33 KiB
Vue
Raw Normal View History

2026-03-26 11:12:48 +08:00
<template>
<div class="workshop-board">
<div class="board-header">
<div class="header-cell">
<div class="header-label">看板编号</div>
<div class="header-value">HY-CJKB-001</div>
</div>
<div class="header-cell">
<div class="header-label">管理责任人</div>
<div class="header-value">张主管</div>
</div>
<div class="header-cell">
<div class="header-label">更新时间</div>
<div class="header-value">{{ updateTime }}</div>
</div>
</div>
<div class="board-body">
<section class="board-main">
<div class="board-stats">
<div class="stats-title">设备列表</div>
<div class="stats-item">设备总数 <span class="stats-value">{{ summary.total }}</span></div>
<div class="stats-item">在线数量 <span class="stats-value">{{ summary.online }}</span></div>
<div class="stats-item">运行数量 <span class="stats-value">{{ summary.running }}</span></div>
<div class="stats-item">停止数量 <span class="stats-value">{{ summary.stop }}</span></div>
<div class="stats-item">开机率 <span class="stats-value">{{ summary.startRate }}</span></div>
</div>
<div class="machine-grid">
<article
v-for="item in orderedMachineList"
:key="item.id"
class="machine-card"
@click="openDetail(item)"
>
<span
v-if="item.id === 5 || item.id === 6"
class="fold-mark fold-mark--card"
aria-hidden="true"
/>
<div class="machine-card__title">
<span class="status-dot" :class="item.statusClass" />
<span>{{ item.statusText }}</span>
<span class="machine-no">{{ item.name }}</span>
<span>{{ item.model }}</span>
<span>{{ item.mode }}模式</span>
</div>
<div class="machine-sections">
<section class="machine-panel machine-panel--primary">
<div class="machine-fields machine-fields--primary">
<div class="machine-field machine-field--accent">
<span class="machine-field__label">今日<br/>开机时间</span>
<span class="machine-field__value">{{ item.runtime }}</span>
</div>
<div class="machine-field machine-field--accent">
<span class="machine-field__label">今日<br/>计件数</span>
<span class="machine-field__value">{{ item.output }}</span>
</div>
<div class="machine-field machine-field--accent">
<span class="machine-field__label">任务<br/>进度</span>
<span class="machine-field__value">{{ item.progress }}</span>
</div>
</div>
</section>
<section class="machine-panel machine-panel--secondary">
<div class="machine-fields machine-fields--secondary">
<div class="machine-field">
<span class="machine-field__label">工单日期</span>
<span class="machine-field__value">{{ item.workDate }}</span>
</div>
<div class="machine-field">
<span class="machine-field__label">批次号</span>
<span class="machine-field__value">{{ item.batchNo }}</span>
</div>
<div class="machine-field">
<span class="machine-field__label">计划产量</span>
<span class="machine-field__value">{{ item.planQty }}</span>
</div>
<div class="machine-field">
<span class="machine-field__label">型号</span>
<span class="machine-field__value">{{ item.productModel }}</span>
</div>
<div class="machine-field">
<span class="machine-field__label">颜色</span>
<span class="machine-field__value">{{ item.color }}</span>
</div>
<div class="machine-field">
<span class="machine-field__label">备注</span>
<span class="machine-field__value">{{ item.remark }}</span>
</div>
</div>
</section>
</div>
</article>
</div>
</section>
<aside class="board-side">
<section class="side-panel fault-panel">
<span class="fold-mark fold-mark--panel" aria-hidden="true" />
<div class="side-panel__title">今日故障记录</div>
<table class="side-table">
<colgroup>
<col style="width: 16%">
<col style="width: 17%">
<col style="width: 27%">
<col style="width: 14%">
<col style="width: 26%">
</colgroup>
<thead>
<tr>
<th>设备<br/>编号</th>
<th>故障<br/>时间</th>
<th>故障<br/>描述</th>
<th>责任<br/></th>
<th>预计<br/>恢复时间</th>
</tr>
</thead>
<tbody>
<tr v-for="item in faultList" :key="item.deviceNo + item.faultTime">
<td>{{ item.deviceNo }}</td>
<td>{{ item.faultTime }}</td>
<td>{{ item.faultDesc }}</td>
<td>{{ item.owner }}</td>
<td>{{ item.recoverTime }}</td>
</tr>
<tr v-for="index in Math.max(0, faultTableRows - faultList.length)" :key="'fault-empty-' + index" class="is-empty">
<td>&nbsp;</td>
<td />
<td />
<td />
<td />
</tr>
</tbody>
</table>
<div class="side-panel__time">更新时间 {{ updateTime }}</div>
</section>
<section class="side-panel plan-panel">
<span class="fold-mark fold-mark--panel" aria-hidden="true" />
<div class="side-panel__title">设备维护计划</div>
<table class="side-table">
<colgroup>
<col style="width: 18%">
<col style="width: 30%">
<col style="width: 32%">
<col style="width: 20%">
</colgroup>
<thead>
<tr>
<th>设备<br/>编号</th>
<th>维护<br/>项目</th>
<th>计划<br/>时间</th>
<th>维护<br/>状态</th>
</tr>
</thead>
<tbody>
<tr v-for="item in maintenanceList" :key="item.deviceNo + item.planTime">
<td>{{ item.deviceNo }}</td>
<td>{{ item.project }}</td>
<td>{{ item.planTime }}</td>
<td>{{ item.status }}</td>
</tr>
<tr v-for="index in Math.max(0, planTableRows - maintenanceList.length)" :key="'plan-empty-' + index" class="is-empty">
<td>&nbsp;</td>
<td />
<td />
<td />
</tr>
</tbody>
</table>
<div class="side-panel__time">更新时间 {{ updateTime }}</div>
</section>
</aside>
</div>
<el-dialog
:visible.sync="detailVisible"
width="88%"
top="4vh"
custom-class="board-dialog"
:append-to-body="true"
>
<template slot="title">
<div class="dialog-title">
<span>{{ activeMachine.name }} 详细信息</span>
<span class="dialog-subtitle">实时数据 / 设定参数 / 生产参数</span>
</div>
</template>
<div class="dialog-body">
<section class="dialog-section">
<div class="section-head">
<span>实时数据定时发送</span>
<span>更新时间{{ updateTime }}</span>
</div>
<div class="detail-table-wrap">
<table class="detail-table">
<thead>
<tr>
<th>点位名称</th>
<th>实时数据</th>
<th>更新时间</th>
<th>设定值</th>
<th>设定时间</th>
<th>工单给定值</th>
<th>工单时间</th>
<th>历史曲线</th>
</tr>
</thead>
<tbody>
<tr v-for="item in detailRealtime" :key="item.name">
<td>{{ item.name }}</td>
<td>{{ item.current }}</td>
<td>{{ item.currentTime }}</td>
<td>{{ item.target }}</td>
<td>{{ item.targetTime }}</td>
<td>{{ item.orderValue }}</td>
<td>{{ item.orderTime }}</td>
<td><span class="curve-tag">一图三曲线</span></td>
</tr>
</tbody>
</table>
</div>
</section>
<section class="dialog-grid">
<div class="dialog-section">
<div class="section-head">
<span>全部设定参数变化上报</span>
</div>
<div class="detail-table-wrap small">
<table class="detail-table">
<thead>
<tr>
<th>点位名称</th>
<th>设定数据</th>
<th>设定时间</th>
<th>工单给定值</th>
<th>工单时间</th>
<th>历史曲线</th>
</tr>
</thead>
<tbody>
<tr v-for="item in detailSetting" :key="item.name">
<td>{{ item.name }}</td>
<td>{{ item.setting }}</td>
<td>{{ item.settingTime }}</td>
<td>{{ item.orderValue }}</td>
<td>{{ item.orderTime }}</td>
<td><span class="curve-tag">趋势</span></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="dialog-section">
<div class="section-head">
<span>生产参数一模一次</span>
</div>
<div class="detail-table-wrap small">
<table class="detail-table">
<thead>
<tr>
<th>点位名称</th>
<th>数据值</th>
<th>采集时间</th>
<th>历史曲线</th>
</tr>
</thead>
<tbody>
<tr v-for="item in detailProduction" :key="item.name">
<td>{{ item.name }}</td>
<td>{{ item.value }}</td>
<td>{{ item.collectTime }}</td>
<td><span class="curve-tag">趋势</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
</div>
</el-dialog>
</div>
</template>
<script>
function createMachine(id, name, model, statusText, statusClass, mode, runtime, output, workDate, batchNo, planQty, progress, productModel, color, remark, nextModel) {
return {
id,
name,
model,
statusText,
statusClass,
mode,
runtime,
output,
workDate,
batchNo,
planQty,
progress,
productModel,
color,
remark,
nextModel
}
}
export default {
name: 'WorkshopBoard',
computed: {
orderedMachineList() {
return [...this.machineList].sort((a, b) => a.id - b.id)
}
},
data() {
return {
detailVisible: false,
updateTime: '',
clockTimer: null,
previousTitle: '',
activeMachine: {},
faultTableRows: 8,
planTableRows: 8,
summary: {
total: 17,
online: 15,
running: 15,
stop: 2,
startRate: '88.2%'
},
faultList: [
{ deviceNo: '8#', faultTime: '09:25', faultDesc: '模温异常', owner: '李工', recoverTime: '10:10' },
{ deviceNo: '16#', faultTime: '11:40', faultDesc: '液压压力波动', owner: '王工', recoverTime: '13:00' },
{ deviceNo: '5#', faultTime: '13:18', faultDesc: '送料传感器告警', owner: '赵工', recoverTime: '13:45' }
],
maintenanceList: [
{ deviceNo: '2#', project: '保养润滑系统', planTime: '2026-03-23 16:00', status: '待执行' },
{ deviceNo: '11#', project: '校验温控模块', planTime: '2026-03-23 18:00', status: '待执行' },
{ deviceNo: '14#', project: '更换过滤组件', planTime: '2026-03-24 09:00', status: '已排期' }
],
machineList: [
createMachine(6, '6#', 'MA2500/1000G', '状态', 'is-running', '运行', '13小时', '600个', '2026.3.16-3.18', '25110914', '5000个', '30%', '23W 中间盖', '7037C 灰', '重点订单', '159BB'),
createMachine(12, '12#', 'MA1600/540G', '状态', 'is-running', '运行', '8小时', '305个', '2026.3.23-3.25', '25110953', '2800个', '27%', '中盖件', '灰色', '正常生产', 'J20R'),
createMachine(5, '5#', 'MA2500 2S/1000', '状态', 'is-running', '运行', '6小时', '210个', '2026.3.22-3.24', '25110937', '3600个', '19%', '卡扣件', '7035 灰', '待维修', '32QR'),
createMachine(11, '11#', 'MA3600/2250G', '状态', 'is-running', '运行', '15小时', '720个', '2026.3.23-3.25', '25110950', '6500个', '33%', '大型壳体', '深蓝', '计划保养', 'LT9X'),
createMachine(17, '17#', 'MA1600/540G', '状态', 'is-running', '运行', '12小时', '510个', '2026.3.23-3.25', '25110961', '3600个', '53%', '面框件', '冷白', '正常生产', 'AA17'),
createMachine(4, '4#', 'MA2500/1000G', '状态', 'is-running', '运行', '10小时', '488个', '2026.3.21-3.22', '25110931', '4500个', '61%', '28W 外壳', '米白', '正常生产', '26WA'),
createMachine(10, '10#', 'MA2500/1000G', '状态', 'is-running', '运行', '13小时', '610个', '2026.3.23-3.24', '25110946', '5100个', '44%', '挡板件', '黑色', '正常生产', 'M22A'),
createMachine(16, '16#', '570', '状态', 'is-running', '运行', '5小时', '190个', '2026.3.23-3.25', '25110959', '2400个', '18%', '小型卡件', '浅灰', '液压检修', 'Q30E'),
createMachine(3, '3#', 'MA2500/1000G', '状态', 'is-running', '运行', '8小时', '360个', '2026.3.20-3.22', '25110925', '3000个', '58%', '17W 支架', '银色', '等待换模', '90KT'),
createMachine(9, '9#', 'MA2500/1000G', '状态', 'is-running', '运行', '14小时', '680个', '2026.3.23-3.24', '25110945', '5400个', '52%', '导流板', '银灰', '正常生产', 'C88L'),
createMachine(15, '15#', 'MA1600/540G', '状态', 'is-running', '运行', '11小时', '460个', '2026.3.23-3.25', '25110957', '3500个', '41%', '转接件', '深灰', '正常生产', 'UV06'),
createMachine(2, '2#', 'MA2500/1000G', '状态', 'is-running', '运行', '11小时', '520个', '2026.3.18-3.20', '25110918', '4200个', '42%', '26W 面板', '黑色', '夜班优先', '188AX'),
createMachine(8, '8#', 'MA2500/1000G', '状态', 'is-running', '运行', '9小时', '430个', '2026.3.22-3.24', '25110941', '4600个', '36%', '封边件', '白色', '温控波动', 'B71S'),
createMachine(14, '14#', 'MA1600/540G', '状态', 'is-running', '运行', '10小时', '395个', '2026.3.23-3.25', '25110955', '3200个', '38%', '扣合件', '亮黑', '正常生产', 'R81N'),
createMachine(1, '1#', 'MA2500/1000G', '状态', 'is-running', '运行', '13小时', '600个', '2026.3.16-3.18', '25110914', '5000个', '30%', '23W 中间盖', '7037C 灰', '重点订单', '159BB'),
createMachine(7, '7#', 'MA2500/1000G', '状态', 'is-running', '运行', '4小时', '120个', '2026.3.22-3.24', '25110940', '3000个', '12%', '结构件', '深灰', '人工调试', 'X09M'),
createMachine(13, '13#', 'MA1600/540G', '状态', 'is-running', '运行', '7小时', '280个', '2026.3.23-3.25', '25110954', '2600个', '31%', '面罩件', '暖白', '待料', 'PK11')
],
detailRealtime: [
{ name: '温度一段A15', current: '186℃', currentTime: '14:20:15', target: '185℃', targetTime: '14:05:00', orderValue: '188℃', orderTime: '08:30:00' },
{ name: '温度二段A16', current: '192℃', currentTime: '14:20:15', target: '190℃', targetTime: '14:05:00', orderValue: '192℃', orderTime: '08:30:00' },
{ name: '射胶压力A243', current: '12.3MPa', currentTime: '14:20:10', target: '12.0MPa', targetTime: '14:00:00', orderValue: '12.5MPa', orderTime: '08:30:00' }
],
detailSetting: [
{ name: '开模行程A060', setting: '320mm', settingTime: '13:15:00', orderValue: '325mm', orderTime: '08:30:00' },
{ name: '锁模压力A061', setting: '86%', settingTime: '13:15:00', orderValue: '85%', orderTime: '08:30:00' },
{ name: '保压时间A062', setting: '4.5s', settingTime: '13:15:00', orderValue: '4.8s', orderTime: '08:30:00' }
],
detailProduction: [
{ name: '上模循环时A37', value: '28.6s', collectTime: '14:20:11' },
{ name: '一模产出数', value: '2', collectTime: '14:20:11' },
{ name: '累计良品数', value: '598', collectTime: '14:20:11' }
]
}
},
created() {
this.refreshClock()
this.clockTimer = setInterval(this.refreshClock, 1000)
this.activeMachine = this.machineList[0]
},
mounted() {
this.applyDisplayTitle()
this.tryEnterFullscreen()
},
beforeDestroy() {
if (this.clockTimer) {
clearInterval(this.clockTimer)
this.clockTimer = null
}
this.restoreTitle()
},
methods: {
applyDisplayTitle() {
const mode = this.$route && this.$route.query ? this.$route.query.displayMode : ''
if (mode !== 'fullscreen') {
return
}
this.previousTitle = document.title
document.title = ''
},
restoreTitle() {
if (this.previousTitle !== '') {
document.title = this.previousTitle
}
},
async tryEnterFullscreen() {
const mode = this.$route && this.$route.query ? this.$route.query.displayMode : ''
if (mode !== 'fullscreen') {
return
}
const element = document.documentElement
try {
if (element.requestFullscreen) {
await element.requestFullscreen()
} else if (element.webkitRequestFullscreen) {
await element.webkitRequestFullscreen()
} else if (element.msRequestFullscreen) {
await element.msRequestFullscreen()
}
} catch (error) {
// 全屏能力受浏览器安全策略限制,失败时保留铺满窗口的展示模式。
}
if (window.outerWidth < window.screen.availWidth || window.outerHeight < window.screen.availHeight) {
try {
window.moveTo(0, 0)
window.resizeTo(window.screen.availWidth, window.screen.availHeight)
} catch (error) {
// 某些浏览器会阻止脚本调整窗口尺寸,忽略即可。
}
}
},
refreshClock() {
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0')
const date = String(now.getDate()).padStart(2, '0')
const hours = String(now.getHours()).padStart(2, '0')
const minutes = String(now.getMinutes()).padStart(2, '0')
const seconds = String(now.getSeconds()).padStart(2, '0')
this.updateTime = `${year}-${month}-${date} ${hours}:${minutes}:${seconds}`
},
openDetail(machine) {
this.activeMachine = machine
this.detailVisible = true
}
}
}
</script>
<style lang="scss" scoped>
.workshop-board {
position: relative;
min-height: 100vh;
height: 100vh;
padding: 8px;
background:
radial-gradient(circle at top left, rgba(60, 160, 255, 0.12), transparent 22%),
radial-gradient(circle at top right, rgba(0, 214, 170, 0.07), transparent 18%),
linear-gradient(180deg, #050a12 0%, #0a111a 48%, #060d15 100%);
color: #e8edf5;
overflow: hidden;
font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
}
.workshop-board::before {
content: '';
position: absolute;
top: 0;
left: -30%;
width: 30%;
height: 2px;
background: linear-gradient(90deg, transparent, rgba(111, 223, 255, 0.9), transparent);
box-shadow: 0 0 12px rgba(83, 207, 255, 0.45);
animation: topScanner 7s linear infinite;
pointer-events: none;
z-index: 3;
}
.board-header {
position: relative;
display: grid;
grid-template-columns: repeat(3, 1fr);
border: 1px solid #2d6e97;
background:
linear-gradient(180deg, rgba(11, 24, 38, 0.97), rgba(8, 18, 30, 0.98)),
linear-gradient(90deg, rgba(91, 217, 255, 0.06), transparent 36%, rgba(41, 99, 181, 0.06));
box-shadow:
inset 0 0 0 1px rgba(124, 208, 255, 0.06),
0 0 18px rgba(37, 125, 191, 0.18);
}
.board-header::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(90deg, transparent, rgba(92, 208, 255, 0.08), transparent);
pointer-events: none;
}
.fold-mark {
position: absolute;
pointer-events: none;
z-index: 4;
opacity: 1;
}
.fold-mark::before,
.fold-mark::after {
content: '';
position: absolute;
}
.header-cell {
min-height: 42px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 10px;
padding: 0 10px;
white-space: nowrap;
}
.header-label {
flex: 0 0 auto;
font-size: 14px;
font-weight: 700;
color: #dff6ff;
text-shadow: 0 0 10px rgba(84, 194, 255, 0.18);
}
.header-value {
min-width: 0;
font-size: 13px;
color: #7fd4ff;
font-family: "DIN Alternate", "Bahnschrift", "Consolas", monospace;
letter-spacing: 1px;
text-shadow: 0 0 10px rgba(76, 196, 255, 0.22);
overflow: hidden;
text-overflow: ellipsis;
}
.board-body {
display: grid;
grid-template-columns: minmax(0, 1fr) 360px;
gap: 0;
height: calc(100vh - 58px);
margin-top: 6px;
background: linear-gradient(180deg, rgba(6, 13, 22, 0.99), rgba(4, 10, 17, 0.99));
}
.board-main {
position: relative;
border: 0;
padding: 0 6px 6px;
overflow: hidden;
background:
linear-gradient(180deg, rgba(8, 16, 26, 0.97), rgba(6, 13, 22, 0.98)),
linear-gradient(90deg, rgba(70, 183, 255, 0.03) 1px, transparent 1px),
linear-gradient(rgba(70, 183, 255, 0.03) 1px, transparent 1px);
background-size: auto, 22px 22px, 22px 22px;
box-shadow: none;
}
.board-main::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(82, 208, 255, 0.5), transparent);
pointer-events: none;
}
.board-stats {
display: grid;
grid-template-columns: 1.4fr repeat(5, 1fr);
align-items: center;
min-height: 34px;
font-size: 15px;
font-weight: 700;
column-gap: 12px;
color: #d8f1ff;
text-shadow: 0 0 10px rgba(56, 150, 255, 0.12);
}
.stats-title,
.stats-item {
white-space: nowrap;
}
.stats-value {
display: inline-block;
min-width: 28px;
margin-left: 2px;
padding: 0 4px;
border: 1px solid rgba(85, 191, 255, 0.28);
border-radius: 2px;
background: linear-gradient(180deg, rgba(20, 69, 107, 0.55), rgba(10, 35, 58, 0.5));
color: #8ee5ff;
font-family: "DIN Alternate", "Bahnschrift", "Consolas", monospace;
letter-spacing: 1px;
text-shadow: 0 0 8px rgba(91, 218, 255, 0.28);
box-shadow: inset 0 0 8px rgba(70, 173, 255, 0.08);
text-align: center;
}
.machine-grid {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 6px 8px;
align-content: start;
height: calc(100% - 36px);
overflow: auto;
padding-right: 2px;
}
.machine-card {
position: relative;
border: 1px solid rgba(117, 205, 255, 0.42);
border-radius: 16px;
background:
linear-gradient(180deg, rgba(12, 37, 59, 0.98), rgba(6, 17, 30, 0.99)),
linear-gradient(135deg, rgba(121, 220, 255, 0.05), transparent 55%);
padding: 8px 8px 10px;
cursor: pointer;
box-shadow:
inset 0 1px 0 rgba(204, 240, 255, 0.08),
0 10px 24px rgba(5, 17, 30, 0.28),
0 0 0 1px rgba(67, 150, 201, 0.14);
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.machine-card::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba(154, 232, 255, 0.12), transparent 34%);
pointer-events: none;
}
.machine-card::after {
content: '';
position: absolute;
inset: 1px;
border-radius: 15px;
border: 1px solid rgba(135, 219, 255, 0.1);
pointer-events: none;
}
.fold-mark--card {
right: 1px;
bottom: 1px;
width: 66px;
height: 48px;
}
.fold-mark--card::before {
right: 0;
bottom: 0;
width: 0;
height: 0;
border-bottom: 22px solid rgba(216, 0, 11, 0.52);
border-left: 22px solid transparent;
border-bottom-right-radius: 14px;
filter: drop-shadow(0 0 12px rgba(216, 0, 11, 0.62));
}
.fold-mark--card::after {
display: none;
}
.machine-card:hover {
box-shadow:
inset 0 1px 0 rgba(204, 240, 255, 0.1),
0 16px 28px rgba(5, 17, 30, 0.34),
0 0 0 1px rgba(93, 190, 244, 0.22);
border-color: rgba(131, 216, 255, 0.58);
transform: translateY(-2px);
}
.machine-card__title {
position: relative;
display: flex;
align-items: center;
gap: 4px;
min-height: 24px;
font-size: 12px;
font-weight: 700;
white-space: nowrap;
overflow: hidden;
color: #dff7ff;
text-shadow: 0 0 6px rgba(102, 215, 255, 0.12);
}
.machine-no {
margin-left: 2px;
color: #8ce9ff;
font-size: 14px;
}
.machine-sections {
display: grid;
gap: 6px;
margin-top: 6px;
}
.machine-panel {
position: relative;
padding: 0;
border-radius: 0;
border: 0;
background: transparent;
box-shadow: none;
}
.machine-panel--primary {
background: transparent;
}
.machine-fields {
display: grid;
gap: 5px;
}
.machine-fields--primary {
grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr) minmax(0, 0.82fr);
}
.machine-fields--secondary {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.machine-field {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
gap: 3px;
min-height: 38px;
padding: 5px 7px;
border: 1px solid rgba(104, 159, 193, 0.52);
border-radius: 8px;
background: linear-gradient(180deg, rgba(6, 22, 35, 0.86), rgba(4, 15, 26, 0.92));
font-size: 10px;
line-height: 1.2;
}
.machine-field__label {
width: 100%;
color: #89c8ec;
font-size: 9px;
line-height: 1.15;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.machine-field__value {
width: 100%;
min-width: 0;
color: #f4fbff;
font-size: 12px;
font-weight: 700;
font-family: "DIN Alternate", "Bahnschrift", "Consolas", monospace;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-shadow: 0 0 10px rgba(128, 225, 255, 0.16);
}
.machine-field--accent {
border-color: rgba(84, 220, 255, 0.5);
background: linear-gradient(180deg, rgba(12, 78, 102, 0.68), rgba(5, 35, 54, 0.94));
}
.machine-field--accent .machine-field__label {
color: #b8f2ff;
}
.machine-field--accent .machine-field__value {
color: #fff3a6;
font-size: 13px;
text-shadow: 0 0 12px rgba(255, 218, 109, 0.22);
}
.status-dot {
width: 10px;
height: 10px;
flex: 0 0 10px;
border-radius: 50%;
border: 1px solid #52d88d;
background: radial-gradient(circle at 35% 35%, #7df3a6, #1fb36e 70%);
box-shadow: 0 0 6px rgba(63, 228, 132, 0.45);
animation: statusPulseGreen 2.4s ease-in-out infinite;
}
.status-dot.is-stop {
background: radial-gradient(circle at 35% 35%, #ffe18d, #d0a33d 72%);
border-color: #e6bf66;
box-shadow: 0 0 6px rgba(240, 191, 83, 0.35);
animation-name: statusPulseYellow;
}
.status-dot.is-standby {
background: radial-gradient(circle at 35% 35%, #8fd2ff, #3b86da 72%);
border-color: #7db4f0;
box-shadow: 0 0 6px rgba(90, 176, 255, 0.35);
animation-name: statusPulseBlue;
}
.status-dot.is-fault {
background: radial-gradient(circle at 35% 35%, #ff9f9f, #ce5b5b 72%);
border-color: #e48c8c;
box-shadow: 0 0 6px rgba(232, 106, 106, 0.35);
animation-name: statusPulseRed;
}
.status-dot.is-offline {
background: radial-gradient(circle at 35% 35%, #b9c1cc, #74808e 72%);
border-color: #aab2bd;
box-shadow: 0 0 5px rgba(149, 160, 175, 0.25);
animation-name: statusPulseGray;
}
.side-table,
.detail-table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
.side-table th,
.side-table td,
.detail-table th,
.detail-table td {
border: 1px solid rgba(104, 159, 193, 0.8);
padding: 1px 4px;
line-height: 1.08;
word-break: break-all;
}
.board-side {
display: grid;
grid-template-rows: 1fr 1fr;
gap: 10px;
padding-left: 10px;
background: linear-gradient(180deg, rgba(6, 13, 22, 0.99), rgba(4, 10, 17, 0.99));
}
.side-panel {
position: relative;
padding: 8px 8px 6px;
display: grid;
grid-template-rows: auto auto 1fr auto;
background:
linear-gradient(180deg, rgba(10, 21, 34, 0.97), rgba(7, 15, 25, 0.98)),
linear-gradient(90deg, rgba(104, 211, 255, 0.025) 1px, transparent 1px),
linear-gradient(rgba(104, 211, 255, 0.02) 1px, transparent 1px);
background-size: auto, 18px 18px, 18px 18px;
border: 0;
box-shadow: none;
}
.fold-mark--panel {
right: 4px;
bottom: 2px;
width: 84px;
height: 62px;
}
.fold-mark--panel::before {
right: 0;
bottom: 0;
width: 0;
height: 0;
border-bottom: 32px solid rgba(216, 0, 11, 0.44);
border-left: 32px solid transparent;
filter: drop-shadow(0 0 14px rgba(216, 0, 11, 0.52));
}
.fold-mark--panel::after {
display: none;
}
.side-panel__title {
font-size: 17px;
font-weight: 700;
margin-bottom: 8px;
color: #dff7ff;
text-shadow: 0 0 8px rgba(88, 207, 255, 0.12);
}
.side-table th,
.side-table td {
font-size: 12px;
text-align: center;
height: 28px;
color: #cae5f6;
white-space: nowrap;
word-break: normal;
overflow: hidden;
text-overflow: ellipsis;
}
.side-table tbody tr.is-empty td {
color: rgba(202, 229, 246, 0.28);
}
.side-table th {
color: #dff7ff;
background: rgba(36, 93, 129, 0.28);
}
.side-panel__time {
align-self: end;
font-size: 14px;
font-weight: 700;
padding-top: 6px;
color: #82cfff;
font-family: "DIN Alternate", "Bahnschrift", "Consolas", monospace;
letter-spacing: 1px;
text-shadow: 0 0 10px rgba(76, 196, 255, 0.18);
}
.fault-panel .side-table {
margin-bottom: 8px;
}
.plan-panel .side-table {
margin-top: 8px;
}
.dialog-title {
display: flex;
align-items: baseline;
gap: 12px;
font-weight: 700;
}
.dialog-subtitle {
color: #666666;
font-size: 12px;
font-weight: 400;
}
.dialog-body {
display: grid;
gap: 16px;
}
.dialog-section {
border: 1px solid #dcdfe6;
border-radius: 8px;
padding: 16px;
background: #ffffff;
}
.section-head {
display: flex;
justify-content: space-between;
gap: 12px;
margin-bottom: 12px;
font-size: 14px;
font-weight: 700;
}
.dialog-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
}
.detail-table-wrap {
overflow: auto;
}
.detail-table th,
.detail-table td {
border: 1px solid #dcdfe6;
padding: 8px 10px;
font-size: 12px;
text-align: left;
}
.curve-tag {
color: #409eff;
}
@keyframes statusPulseGreen {
0%, 100% {
box-shadow: 0 0 4px rgba(63, 228, 132, 0.2);
transform: scale(1);
}
50% {
box-shadow: 0 0 8px rgba(63, 228, 132, 0.48);
transform: scale(1.06);
}
}
@keyframes topScanner {
0% {
left: -30%;
opacity: 0;
}
8% {
opacity: 1;
}
50% {
opacity: 1;
}
100% {
left: 100%;
opacity: 0;
}
}
@keyframes statusPulseYellow {
0%, 100% {
box-shadow: 0 0 4px rgba(240, 191, 83, 0.18);
transform: scale(1);
}
50% {
box-shadow: 0 0 8px rgba(240, 191, 83, 0.42);
transform: scale(1.06);
}
}
@keyframes statusPulseBlue {
0%, 100% {
box-shadow: 0 0 4px rgba(90, 176, 255, 0.18);
transform: scale(1);
}
50% {
box-shadow: 0 0 8px rgba(90, 176, 255, 0.42);
transform: scale(1.06);
}
}
@keyframes statusPulseRed {
0%, 100% {
box-shadow: 0 0 4px rgba(232, 106, 106, 0.18);
transform: scale(1);
}
50% {
box-shadow: 0 0 8px rgba(232, 106, 106, 0.42);
transform: scale(1.06);
}
}
@keyframes statusPulseGray {
0%, 100% {
box-shadow: 0 0 3px rgba(149, 160, 175, 0.15);
transform: scale(1);
}
50% {
box-shadow: 0 0 6px rgba(149, 160, 175, 0.3);
transform: scale(1.05);
}
}
::v-deep .board-dialog {
border-radius: 12px;
}
@media (max-width: 1600px) {
.board-body {
grid-template-columns: minmax(0, 1fr) 330px;
}
.board-stats {
grid-template-columns: 1.2fr repeat(5, 1fr);
font-size: 13px;
}
.machine-card__title {
font-size: 11px;
}
.machine-no {
font-size: 13px;
}
}
</style>