300 lines
10 KiB
Vue
300 lines
10 KiB
Vue
<template>
|
||
<div class="roll-view">
|
||
<div class="toolbar">
|
||
<el-button size="small" type="primary" :loading="loading" icon="el-icon-refresh" @click="loadData">刷新</el-button>
|
||
<el-button size="small" icon="el-icon-search" @click="handleCheck">检查</el-button>
|
||
<span v-if="lastRefresh" class="refresh-time">上次刷新:{{ lastRefresh }}</span>
|
||
</div>
|
||
|
||
<div class="table-wrap" v-loading="loading">
|
||
<table class="roll-table">
|
||
<thead>
|
||
<tr>
|
||
<th rowspan="2" class="th-stand">机架</th>
|
||
<th rowspan="2" class="th-pos">位置</th>
|
||
<th colspan="4" class="th-section th-standby">准备辊</th>
|
||
<th colspan="9" class="th-section th-online">在线辊</th>
|
||
<th colspan="4" class="th-section th-standard">换辊标准</th>
|
||
</tr>
|
||
<tr>
|
||
<th class="th-sub">轧辊号</th>
|
||
<th class="th-sub">外径</th>
|
||
<th class="th-sub">凸度</th>
|
||
<th class="th-sub">粗糙度</th>
|
||
<th class="th-sub">轧辊号</th>
|
||
<th class="th-sub">位置</th>
|
||
<th class="th-sub">类型</th>
|
||
<th class="th-sub">直径</th>
|
||
<th class="th-sub">凸度</th>
|
||
<th class="th-sub">粗糙度</th>
|
||
<th class="th-sub">长度</th>
|
||
<th class="th-sub">重量</th>
|
||
<th class="th-sub">安装时间</th>
|
||
<th class="th-sub">本次长度</th>
|
||
<th class="th-sub">累计长度</th>
|
||
<th class="th-sub">本次重量</th>
|
||
<th class="th-sub">累计重量</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<template v-for="(stand, si) in tableData">
|
||
<tr
|
||
v-for="(pos, pi) in stand.positions"
|
||
:key="stand.id + '-' + pi"
|
||
:class="rowCls(pos)"
|
||
>
|
||
<td v-if="pi === 0" :rowspan="stand.positions.length" class="td-stand">
|
||
{{ stand.name }}
|
||
</td>
|
||
<td class="td-pos">{{ pos.label }}</td>
|
||
|
||
<!-- 准备辊 -->
|
||
<td class="td-standby">{{ sv(pos.standby, 'rollid') }}</td>
|
||
<td class="td-standby">{{ nv(pos.standby, 'diameter') }}</td>
|
||
<td class="td-standby">{{ nv(pos.standby, 'crown') }}</td>
|
||
<td class="td-standby">{{ nv(pos.standby, 'rough') }}</td>
|
||
|
||
<!-- 在线辊 -->
|
||
<td class="td-online td-bold">{{ sv(pos.online, 'rollid') }}</td>
|
||
<td class="td-online">{{ dispPos(pos.online && pos.online.position) }}</td>
|
||
<td class="td-online">{{ dispType(pos.online && pos.online.type) }}</td>
|
||
<td class="td-online">{{ nv(pos.online, 'diameter') }}</td>
|
||
<td class="td-online">{{ nv(pos.online, 'crown') }}</td>
|
||
<td class="td-online">{{ nv(pos.online, 'rough') }}</td>
|
||
<td class="td-online">{{ iv(pos.online, 'rolled_length') }}</td>
|
||
<td class="td-online">{{ iv(pos.online, 'rolled_weight') }}</td>
|
||
<td class="td-online td-time">{{ dv(pos.online, 'instal_time') }}</td>
|
||
|
||
<!-- 换辊标准 -->
|
||
<td class="td-std">{{ iv(pos.online, 'rolled_length') }}</td>
|
||
<td class="td-std">{{ iv(pos.online, 'total_rolled_length') }}</td>
|
||
<td class="td-std">{{ iv(pos.online, 'rolled_weight') }}</td>
|
||
<td class="td-std">{{ iv(pos.online, 'total_rolled_weight') }}</td>
|
||
</tr>
|
||
</template>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { getRollData } from '@/api/l2/timing'
|
||
|
||
const STAND_NAMES = ['一机架', '二机架', '三机架', '四机架', '五机架', '六机架']
|
||
|
||
// 归一化:英文 → 中文,兼容已是中文的情况
|
||
const POS_NORM = {
|
||
TOP: '上', UPPER: '上', UP: '上', '上': '上',
|
||
BOTTOM: '下', LOWER: '下', DOWN: '下', '下': '下'
|
||
}
|
||
const TYPE_NORM = {
|
||
WORK: '工作辊', WORK_ROLL: '工作辊', WR: '工作辊', '工作辊': '工作辊',
|
||
BACKUP: '支撑辊', BUR: '支撑辊', SUPPORT: '支撑辊', BACK_UP: '支撑辊', '支撑辊': '支撑辊',
|
||
INTERMEDIATE: '中间辊', IMR: '中间辊', INTER: '中间辊', '中间辊': '中间辊'
|
||
}
|
||
|
||
function normPos(v) { return POS_NORM[String(v || '').toUpperCase()] || String(v || '').trim() }
|
||
function normType(v) { return TYPE_NORM[String(v || '').toUpperCase()] || String(v || '').trim() }
|
||
|
||
const POSITIONS = [
|
||
{ label: '上支撑辊', position: '上', type: '支撑辊' },
|
||
{ label: '上中间辊', position: '上', type: '中间辊' },
|
||
{ label: '上工作辊', position: '上', type: '工作辊' },
|
||
{ label: '下工作辊', position: '下', type: '工作辊' },
|
||
{ label: '下中间辊', position: '下', type: '中间辊' },
|
||
{ label: '下支撑辊', position: '下', type: '支撑辊' }
|
||
]
|
||
|
||
function fmtDateStr(val) {
|
||
if (!val) return '—'
|
||
const s = String(val)
|
||
const m = s.match(/(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2}):(\d{2})/)
|
||
if (m) return `${m[1]}-${m[2]}-${m[3]} ${m[4]}:${m[5]}:${m[6]}`
|
||
const d = new Date(val)
|
||
if (isNaN(d.getTime())) return s
|
||
const pad = n => String(n).padStart(2, '0')
|
||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
|
||
}
|
||
|
||
export default {
|
||
name: 'TimingRollPage',
|
||
data() {
|
||
return {
|
||
loading: false,
|
||
lastRefresh: '',
|
||
tableData: []
|
||
}
|
||
},
|
||
created() {
|
||
this.loadData()
|
||
},
|
||
methods: {
|
||
async loadData() {
|
||
this.loading = true
|
||
try {
|
||
const res = await getRollData()
|
||
const rows = res?.data?.rows || []
|
||
this.buildTableData(rows)
|
||
const now = new Date()
|
||
const pad = n => String(n).padStart(2, '0')
|
||
this.lastRefresh = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
|
||
} finally {
|
||
this.loading = false
|
||
}
|
||
},
|
||
buildTableData(rows) {
|
||
this.tableData = STAND_NAMES.map((name, idx) => {
|
||
const standId = idx + 1
|
||
const standRows = rows.filter(r => Number(r.standid) === standId)
|
||
const positions = POSITIONS.map(p => {
|
||
// 同位置下所有 ONLINE 辊,按安装时间升序排列
|
||
const matches = standRows
|
||
.filter(r =>
|
||
normPos(r.position) === p.position &&
|
||
normType(r.type) === p.type
|
||
)
|
||
.sort((a, b) => {
|
||
const ta = a.instal_time ? new Date(a.instal_time).getTime() : 0
|
||
const tb = b.instal_time ? new Date(b.instal_time).getTime() : 0
|
||
return ta - tb
|
||
})
|
||
// 安装最早的是当前在线辊,后装的是准备辊(等待换入)
|
||
const online = matches[0] || null
|
||
const standby = matches[1] || null
|
||
return { ...p, online, standby }
|
||
})
|
||
return { id: standId, name, positions }
|
||
})
|
||
},
|
||
rowCls(pos) {
|
||
return pos.online ? '' : 'row-empty'
|
||
},
|
||
// 字符串值
|
||
sv(row, key) {
|
||
if (!row) return '—'
|
||
const v = row[key]
|
||
return v != null && v !== '' ? String(v) : '—'
|
||
},
|
||
// 数值(保留两位小数)
|
||
nv(row, key) {
|
||
if (!row) return '—'
|
||
const v = row[key]
|
||
if (v == null || v === '') return '—'
|
||
const n = parseFloat(v)
|
||
return isNaN(n) ? String(v) : n.toFixed(2)
|
||
},
|
||
// 整数
|
||
iv(row, key) {
|
||
if (!row) return '—'
|
||
const v = row[key]
|
||
if (v == null || v === '') return '—'
|
||
const n = parseFloat(v)
|
||
return isNaN(n) ? String(v) : Math.round(n).toString()
|
||
},
|
||
// 日期
|
||
dv(row, key) {
|
||
return row ? fmtDateStr(row[key]) : '—'
|
||
},
|
||
dispPos(v) { return POS_NORM[String(v || '').toUpperCase()] || v || '—' },
|
||
dispType(v) { return TYPE_NORM[String(v || '').toUpperCase()] || v || '—' },
|
||
handleCheck() {
|
||
const missing = []
|
||
this.tableData.forEach(stand => {
|
||
stand.positions.forEach(pos => {
|
||
if (pos.online && !pos.standby) missing.push(`${stand.name} ${pos.label}`)
|
||
})
|
||
})
|
||
if (missing.length === 0) {
|
||
this.$message.success('检查通过,各机架备辊配置正常')
|
||
} else {
|
||
this.$alert(missing.join('\n'), '以下位置缺少备辊', { type: 'warning', confirmButtonText: '知道了' })
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.roll-view {
|
||
padding: 12px;
|
||
background: #fff;
|
||
height: 100%;
|
||
box-sizing: border-box;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.toolbar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-bottom: 10px;
|
||
flex-shrink: 0;
|
||
}
|
||
.refresh-time {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
margin-left: 6px;
|
||
}
|
||
.table-wrap {
|
||
flex: 1;
|
||
overflow: auto;
|
||
border: 1px solid #dcdfe6;
|
||
border-radius: 3px;
|
||
min-height: 0;
|
||
}
|
||
.roll-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 12px;
|
||
color: #303133;
|
||
white-space: nowrap;
|
||
}
|
||
.roll-table th,
|
||
.roll-table td {
|
||
border: 1px solid #dcdfe6;
|
||
padding: 5px 8px;
|
||
text-align: center;
|
||
}
|
||
.roll-table thead tr:first-child th {
|
||
background: #eef0f3;
|
||
font-weight: 600;
|
||
font-size: 13px;
|
||
}
|
||
.th-section { border-bottom: 2px solid #c0c4cc !important; }
|
||
.th-standby { background: #f5f7fa !important; }
|
||
.th-online { background: #ecf5ff !important; color: #1a5276; }
|
||
.th-standard { background: #fdf6ec !important; color: #7b5c1e; }
|
||
.th-stand, .th-pos { background: #eef0f3; font-weight: 600; }
|
||
.th-sub {
|
||
background: #fafafa;
|
||
font-weight: normal;
|
||
font-size: 11px;
|
||
color: #606266;
|
||
}
|
||
.td-stand {
|
||
font-weight: 600;
|
||
font-size: 13px;
|
||
background: #f5f7fa;
|
||
writing-mode: vertical-rl;
|
||
text-orientation: mixed;
|
||
letter-spacing: 2px;
|
||
width: 32px;
|
||
min-width: 32px;
|
||
}
|
||
.td-pos {
|
||
background: #fafafa;
|
||
font-weight: 500;
|
||
width: 62px;
|
||
text-align: left;
|
||
padding-left: 6px;
|
||
}
|
||
.td-bold { font-weight: 500; }
|
||
.td-standby { color: #606266; }
|
||
.td-online { color: #303133; }
|
||
.td-std { color: #606266; }
|
||
.td-time { font-size: 11px; color: #606266; }
|
||
.row-empty td { color: #c0c4cc; background: #fafafa; }
|
||
.roll-table tbody tr:hover td { background: #f0f7ff; }
|
||
</style>
|