Files
mesfront/src/views/mes/board/workshop/index.vue
2026-03-26 11:12:48 +08:00

1097 lines
33 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>