624 lines
21 KiB
Vue
624 lines
21 KiB
Vue
<template>
|
||
<el-dialog
|
||
title="产品质量报表预览"
|
||
:visible.sync="visible"
|
||
width="880px"
|
||
append-to-body
|
||
:close-on-click-modal="false"
|
||
@open="onOpen"
|
||
@close="onClose"
|
||
>
|
||
<!-- 工具栏 -->
|
||
<div class="rpt-toolbar">
|
||
<span class="rpt-coil-hint">{{ coilLabel }}</span>
|
||
<div class="rpt-toolbar-right">
|
||
<el-button
|
||
size="small"
|
||
type="primary"
|
||
icon="el-icon-download"
|
||
:loading="exporting"
|
||
@click="exportPdf"
|
||
>导出 PDF</el-button>
|
||
<el-button size="small" @click="visible = false">关闭</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 加载状态 -->
|
||
<div v-if="dataLoading" class="rpt-loading">
|
||
<i class="el-icon-loading" /> 正在加载实绩数据…
|
||
</div>
|
||
|
||
<!-- 报告主体 -->
|
||
<div v-show="!dataLoading" ref="reportContent" class="report-body">
|
||
<!-- ─── 页眉 ─── -->
|
||
<div class="rpt-header">
|
||
<div class="rpt-header-left">
|
||
<div class="company-line">中国五矿</div>
|
||
<div class="mcc-line">MCC 中冶赛迪信息</div>
|
||
</div>
|
||
<div class="rpt-header-title">产品质量报表</div>
|
||
<div class="rpt-header-right">
|
||
<div class="brand-name">科伦普</div>
|
||
<div class="brand-sub">KE LUN PU</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ─── 钢卷规格 ─── -->
|
||
<div class="rpt-section-bar">钢卷规格</div>
|
||
<table class="rpt-table spec-table">
|
||
<tbody>
|
||
<tr>
|
||
<td class="cell-label">钢卷号</td>
|
||
<td class="cell-val cell-coilid">{{ v('hot_coilid') || v('coilid') }}</td>
|
||
<td class="cell-label">来料厚度[mm]</td>
|
||
<td class="cell-val">{{ v('entry_thick') }}</td>
|
||
<td class="cell-label">来料宽度[mm]</td>
|
||
<td class="cell-val">{{ v('entry_width') }}</td>
|
||
<td class="cell-label">来料重量[t]</td>
|
||
<td class="cell-val">{{ v('entry_weight') }}</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="cell-label">钢种</td>
|
||
<td class="cell-val">{{ v('steel_grade') || v('steelgrade') || v('process_code') }}</td>
|
||
<td class="cell-label">成品厚度[mm]</td>
|
||
<td class="cell-val">{{ v('exit_thick') }}</td>
|
||
<td class="cell-label">成品宽度[mm]</td>
|
||
<td class="cell-val">{{ v('exit_width') }}</td>
|
||
<td class="cell-label">成品重量[t]</td>
|
||
<td class="cell-val">{{ v('exit_weight') || v('entry_weight') }}</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="cell-label">班组</td>
|
||
<td class="cell-val">{{ v('shift') || v('class_no') || '—' }}</td>
|
||
<td class="cell-label">偏差上限[mm]</td>
|
||
<td class="cell-val">{{ v('thick_upper') || v('up_tol') || '—' }}</td>
|
||
<td class="cell-label">压下率[%]</td>
|
||
<td class="cell-val">{{ reductionRate }}</td>
|
||
<td class="cell-label">成品长度[m]</td>
|
||
<td class="cell-val">{{ v('exit_length') }}</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="cell-label">下工序</td>
|
||
<td class="cell-val">{{ v('next_process') || v('next_proc') || '—' }}</td>
|
||
<td class="cell-label">偏差下限[mm]</td>
|
||
<td class="cell-val">{{ v('thick_lower') || v('dn_tol') || '—' }}</td>
|
||
<td class="cell-label">原料卷号</td>
|
||
<td class="cell-val" colspan="3">{{ v('coilid') }}</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="cell-label">生产时间</td>
|
||
<td class="cell-val" colspan="3">{{ v('prod_time') || v('start_time') || v('createtime') || '—' }}</td>
|
||
<td class="cell-label">生产时长</td>
|
||
<td class="cell-val" colspan="3">{{ productionDuration }}</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<!-- ─── 质量判定 ─── -->
|
||
<div class="rpt-section-bar">质量判定</div>
|
||
<table class="rpt-table quality-table">
|
||
<thead>
|
||
<tr>
|
||
<th>轧制总长[m]</th>
|
||
<th>头尾超差[m]</th>
|
||
<th>厚度合格率[%]</th>
|
||
<th>目标板形</th>
|
||
<th>板形合格率[%]</th>
|
||
<th>板形质量</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>{{ rolledLength }}</td>
|
||
<td>{{ v('head_tail_dev') || '—' }}</td>
|
||
<td>{{ thickPassRate }}</td>
|
||
<td>{{ v('target_shape') || '平直' }}</td>
|
||
<td>{{ v('shape_pass_rate') || '—' }}</td>
|
||
<td :class="qualityClass">{{ qualityLabel }}</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<!-- ─── 图表区 ─── -->
|
||
<template v-if="hasSeries">
|
||
<!-- 厚度偏差 -->
|
||
<template v-if="hasField('thick_dev') || hasField('act_thick')">
|
||
<div class="rpt-chart-title">
|
||
成品厚度偏差
|
||
<span class="rpt-chart-subtitle">产出厚度 - {{ v('exit_thick') }} [mm]</span>
|
||
</div>
|
||
<div ref="chartThick" class="rpt-chart" />
|
||
</template>
|
||
|
||
<!-- 速度趋势 -->
|
||
<template v-if="hasField('plspeed') || hasField('trimspeed') || hasField('millexitspeed')">
|
||
<div class="rpt-chart-title">速度趋势 [m/min]</div>
|
||
<div ref="chartSpeed" class="rpt-chart" />
|
||
</template>
|
||
|
||
<!-- 轧机出口速度 -->
|
||
<template v-if="hasField('millexitspeed') && (hasField('plspeed') || hasField('trimspeed'))">
|
||
<div class="rpt-chart-title">轧机出口速度 [m/min]</div>
|
||
<div ref="chartMillSpeed" class="rpt-chart" />
|
||
</template>
|
||
|
||
<!-- 张力趋势 -->
|
||
<template v-if="hasField('pltens') || hasField('enltens') || hasField('cxltens')">
|
||
<div class="rpt-chart-title">张力趋势 [N]</div>
|
||
<div ref="chartTension" class="rpt-chart" />
|
||
</template>
|
||
|
||
<!-- 额外字段图表 -->
|
||
<template v-for="grp in extraChartGroups">
|
||
<div :key="grp.title + '_t'" class="rpt-chart-title">{{ grp.title }}</div>
|
||
<div :key="grp.title + '_c'" :ref="'chartExtra_' + grp.refKey" class="rpt-chart" />
|
||
</template>
|
||
</template>
|
||
<div v-else class="rpt-no-data">暂无实绩曲线数据</div>
|
||
|
||
<!-- ─── 页脚 ─── -->
|
||
<div class="rpt-footer">生成时间: {{ nowStr }}</div>
|
||
</div>
|
||
</el-dialog>
|
||
</template>
|
||
|
||
<script>
|
||
import * as echarts from 'echarts'
|
||
import { getPlanWithSeg } from '@/api/l2/timing'
|
||
|
||
// Series fields consumed by named charts — remaining fields get auto-grouped
|
||
const NAMED_KEYS = new Set(['startpos', 'thick_dev', 'act_thick', 'plspeed', 'trimspeed', 'millexitspeed', 'pltens', 'enltens', 'cxltens'])
|
||
|
||
// Groups of extra series fields to chart together
|
||
const EXTRA_GROUPS = [
|
||
{ keys: ['bendf1', 'bendf2', 'bendf3', 'bendf4', 'bendf5'], title: '弯辊力 [kN]', unit: 'kN' },
|
||
{ keys: ['rollf1', 'rollf2', 'rollf3', 'rollf4', 'rollf5'], title: '轧制力 [kN]', unit: 'kN' },
|
||
{ keys: ['fwd_slip1', 'fwd_slip2', 'fwd_slip3', 'fwd_slip4', 'fwd_slip5'], title: '前滑值', unit: '' },
|
||
{ keys: ['temp_in', 'temp_out', 'cool_temp'], title: '温度 [°C]', unit: '°C' },
|
||
]
|
||
|
||
function lineOption(title, xData, series, yName) {
|
||
return {
|
||
title: { text: title, textStyle: { fontSize: 12, fontWeight: 'normal', color: '#303133' }, top: 4, left: 8 },
|
||
tooltip: { trigger: 'axis', confine: true },
|
||
legend: { top: 4, right: 8, textStyle: { fontSize: 10 } },
|
||
grid: { top: 40, bottom: 32, left: 10, right: 12, containLabel: true },
|
||
xAxis: {
|
||
type: 'category', data: xData, name: '带钢长度[m]',
|
||
nameTextStyle: { fontSize: 10 }, axisLabel: { fontSize: 9, interval: 'auto' }
|
||
},
|
||
yAxis: { type: 'value', name: yName, nameTextStyle: { fontSize: 10 }, axisLabel: { fontSize: 9 } },
|
||
series: series.map((s, i) => ({
|
||
name: s.name, type: 'line', smooth: false, symbol: 'none',
|
||
lineStyle: { width: 1.5 },
|
||
data: s.data
|
||
}))
|
||
}
|
||
}
|
||
|
||
function dualAxisOption(title, xData, devSeries, thickSeries, exitThick) {
|
||
return {
|
||
title: { text: title, textStyle: { fontSize: 12, fontWeight: 'normal', color: '#303133' }, top: 4, left: 8 },
|
||
tooltip: { trigger: 'axis', confine: true },
|
||
legend: { top: 4, right: 8, textStyle: { fontSize: 10 } },
|
||
grid: { top: 40, bottom: 32, left: 10, right: 12, containLabel: true },
|
||
xAxis: {
|
||
type: 'category', data: xData, name: '带钢长度[m]',
|
||
nameTextStyle: { fontSize: 10 }, axisLabel: { fontSize: 9, interval: 'auto' }
|
||
},
|
||
yAxis: [
|
||
{ type: 'value', name: 'μm', nameTextStyle: { fontSize: 10 }, axisLabel: { fontSize: 9 } },
|
||
{ type: 'value', name: 'mm', nameTextStyle: { fontSize: 10 }, axisLabel: { fontSize: 9 }, splitLine: { show: false } }
|
||
],
|
||
series: [
|
||
{ name: '厚度偏差', type: 'line', yAxisIndex: 0, smooth: false, symbol: 'none', lineStyle: { width: 1, color: '#5F7BA0' }, data: devSeries },
|
||
thickSeries && { name: '实际厚度', type: 'line', yAxisIndex: 1, smooth: false, symbol: 'none', lineStyle: { width: 1.5, color: '#e6a23c' }, data: thickSeries }
|
||
].filter(Boolean)
|
||
}
|
||
}
|
||
|
||
export default {
|
||
name: 'QualityReportDialog',
|
||
data() {
|
||
return {
|
||
visible: false,
|
||
dataLoading: false,
|
||
exporting: false,
|
||
plan: null,
|
||
series: null,
|
||
nowStr: '',
|
||
chartInsts: []
|
||
}
|
||
},
|
||
computed: {
|
||
coilLabel() {
|
||
const id = this.plan?.hot_coilid || this.plan?.coilid || ''
|
||
return id ? `钢卷号:${id}` : ''
|
||
},
|
||
hasSeries() {
|
||
return !!this.series && Object.keys(this.series).length > 0
|
||
},
|
||
reductionRate() {
|
||
const e = parseFloat(this.plan?.entry_thick)
|
||
const x = parseFloat(this.plan?.exit_thick)
|
||
if (!e || !x || e <= 0) return '—'
|
||
return ((e - x) / e * 100).toFixed(2)
|
||
},
|
||
rolledLength() {
|
||
if (this.plan?.exit_length) return this.plan.exit_length
|
||
if (this.series?.startpos?.length) {
|
||
const max = Math.max(...this.series.startpos.filter(v => v != null))
|
||
return max > 0 ? max.toFixed(1) : '—'
|
||
}
|
||
return '—'
|
||
},
|
||
thickPassRate() {
|
||
const r = this.plan?.thick_pass_rate || this.plan?.thick_passrate
|
||
return r != null ? r : '—'
|
||
},
|
||
qualityLabel() {
|
||
const r = this.plan?.quality || this.plan?.quality_result
|
||
if (r) return r
|
||
const tp = parseFloat(this.thickPassRate)
|
||
if (!isNaN(tp)) return tp >= 99 ? '合格' : '不合格'
|
||
return '—'
|
||
},
|
||
qualityClass() {
|
||
return this.qualityLabel === '合格' ? 'cell-ok' : this.qualityLabel === '不合格' ? 'cell-ng' : ''
|
||
},
|
||
productionDuration() {
|
||
const d = this.plan?.prod_duration || this.plan?.duration
|
||
if (!d) return '—'
|
||
const sec = parseInt(d)
|
||
if (isNaN(sec)) return d
|
||
const h = Math.floor(sec / 3600)
|
||
const m = Math.floor((sec % 3600) / 60)
|
||
const s = sec % 60
|
||
return `${String(h).padStart(2, '0')}时${String(m).padStart(2, '0')}分${String(s).padStart(2, '0')}秒`
|
||
},
|
||
extraChartGroups() {
|
||
if (!this.series) return []
|
||
const result = []
|
||
// first try the predefined groups
|
||
for (const grp of EXTRA_GROUPS) {
|
||
const presentKeys = grp.keys.filter(k => this.hasField(k))
|
||
if (presentKeys.length) {
|
||
result.push({ title: grp.title, unit: grp.unit, keys: presentKeys, refKey: grp.title.replace(/\s/g, '_') })
|
||
}
|
||
}
|
||
// then collect truly unknown keys
|
||
const knownInExtra = new Set(EXTRA_GROUPS.flatMap(g => g.keys))
|
||
const unknown = Object.keys(this.series).filter(k => !NAMED_KEYS.has(k) && !knownInExtra.has(k) && Array.isArray(this.series[k]) && this.series[k].length)
|
||
if (unknown.length) {
|
||
result.push({ title: '其他工艺参数', unit: '', keys: unknown, refKey: 'other' })
|
||
}
|
||
return result
|
||
}
|
||
},
|
||
methods: {
|
||
v(key) {
|
||
const val = this.plan?.[key]
|
||
return val != null && val !== '' ? val : '—'
|
||
},
|
||
hasField(key) {
|
||
return !!(this.series?.[key]?.length)
|
||
},
|
||
|
||
// Public: open with a row object
|
||
open(row) {
|
||
this.plan = row
|
||
this.series = null
|
||
this.visible = true
|
||
},
|
||
|
||
onOpen() {
|
||
this.nowStr = new Date().toLocaleString('zh-CN').replace(/\//g, '-')
|
||
this.$nextTick(() => {
|
||
if (this.series) {
|
||
this.renderAllCharts()
|
||
} else if (this.plan) {
|
||
this.fetchSeries()
|
||
}
|
||
})
|
||
},
|
||
|
||
onClose() {
|
||
this.disposeCharts()
|
||
},
|
||
|
||
async fetchSeries() {
|
||
const id = this.plan?.encoilid || this.plan?.coilid
|
||
if (!id) return
|
||
this.dataLoading = true
|
||
try {
|
||
const res = await getPlanWithSeg(id)
|
||
const plan = res?.data?.plan || res?.data?.firstRow || this.plan
|
||
const series = res?.data?.series || null
|
||
if (plan) this.plan = plan
|
||
this.series = series
|
||
this.$nextTick(() => this.renderAllCharts())
|
||
} finally {
|
||
this.dataLoading = false
|
||
}
|
||
},
|
||
|
||
disposeCharts() {
|
||
this.chartInsts.forEach(c => { try { c.dispose() } catch (_) {} })
|
||
this.chartInsts = []
|
||
},
|
||
|
||
pick(key) {
|
||
return (this.series?.[key] || []).map(v => v == null ? null : +parseFloat(v).toFixed(3))
|
||
},
|
||
|
||
xData() {
|
||
return (this.series?.startpos || []).map(v => v == null ? '' : (+parseFloat(v).toFixed(1)))
|
||
},
|
||
|
||
renderAllCharts() {
|
||
this.disposeCharts()
|
||
if (!this.series) return
|
||
const x = this.xData()
|
||
|
||
// 厚度偏差
|
||
if (this.$refs.chartThick) {
|
||
const devKey = this.hasField('thick_dev') ? 'thick_dev' : null
|
||
const actKey = this.hasField('act_thick') ? 'act_thick' : null
|
||
if (devKey || actKey) {
|
||
const devData = devKey ? this.pick(devKey).map(v => v == null ? null : v * 1000) : []
|
||
const actData = actKey ? this.pick(actKey) : null
|
||
const c = echarts.init(this.$refs.chartThick, null, { renderer: 'canvas' })
|
||
if (devKey) {
|
||
c.setOption(dualAxisOption('成品厚度偏差', x, devData, actData, this.plan?.exit_thick))
|
||
} else {
|
||
c.setOption(lineOption('实际厚度 [mm]', x, [{ name: '实际厚度', data: actData }], 'mm'))
|
||
}
|
||
this.chartInsts.push(c)
|
||
}
|
||
}
|
||
|
||
// 速度趋势
|
||
if (this.$refs.chartSpeed) {
|
||
const series = []
|
||
if (this.hasField('plspeed')) series.push({ name: '轧制速度 plspeed', data: this.pick('plspeed') })
|
||
if (this.hasField('trimspeed')) series.push({ name: '剪切速度 trimspeed', data: this.pick('trimspeed') })
|
||
if (!this.hasField('plspeed') && !this.hasField('trimspeed') && this.hasField('millexitspeed')) {
|
||
series.push({ name: '轧机出口速度', data: this.pick('millexitspeed') })
|
||
}
|
||
if (series.length) {
|
||
const c = echarts.init(this.$refs.chartSpeed, null, { renderer: 'canvas' })
|
||
c.setOption(lineOption('速度趋势', x, series, 'm/min'))
|
||
this.chartInsts.push(c)
|
||
}
|
||
}
|
||
|
||
// 轧机出口速度(单独显示)
|
||
if (this.$refs.chartMillSpeed && this.hasField('millexitspeed')) {
|
||
const c = echarts.init(this.$refs.chartMillSpeed, null, { renderer: 'canvas' })
|
||
c.setOption(lineOption('轧机出口速度', x, [{ name: 'millexitspeed', data: this.pick('millexitspeed') }], 'm/min'))
|
||
this.chartInsts.push(c)
|
||
}
|
||
|
||
// 张力趋势
|
||
if (this.$refs.chartTension) {
|
||
const series = []
|
||
if (this.hasField('pltens')) series.push({ name: '出口张力 pltens', data: this.pick('pltens') })
|
||
if (this.hasField('enltens')) series.push({ name: '入口张力 enltens', data: this.pick('enltens') })
|
||
if (this.hasField('cxltens')) series.push({ name: 'cxltens', data: this.pick('cxltens') })
|
||
if (series.length) {
|
||
const c = echarts.init(this.$refs.chartTension, null, { renderer: 'canvas' })
|
||
c.setOption(lineOption('张力趋势', x, series, 'N'))
|
||
this.chartInsts.push(c)
|
||
}
|
||
}
|
||
|
||
// 额外图表组
|
||
for (const grp of this.extraChartGroups) {
|
||
const refKey = 'chartExtra_' + grp.refKey
|
||
const refEl = this.$refs[refKey]
|
||
const el = Array.isArray(refEl) ? refEl[0] : refEl
|
||
if (!el) continue
|
||
const series = grp.keys.filter(k => this.hasField(k)).map(k => ({ name: k, data: this.pick(k) }))
|
||
if (series.length) {
|
||
const c = echarts.init(el, null, { renderer: 'canvas' })
|
||
c.setOption(lineOption(grp.title, x, series, grp.unit))
|
||
this.chartInsts.push(c)
|
||
}
|
||
}
|
||
},
|
||
|
||
async exportPdf() {
|
||
this.exporting = true
|
||
try {
|
||
const [html2canvas, { jsPDF }] = await Promise.all([
|
||
import('html2canvas').then(m => m.default),
|
||
import('jspdf')
|
||
])
|
||
|
||
const el = this.$refs.reportContent
|
||
const canvas = await html2canvas(el, {
|
||
scale: 2,
|
||
useCORS: true,
|
||
allowTaint: true,
|
||
backgroundColor: '#ffffff',
|
||
logging: false
|
||
})
|
||
|
||
const imgData = canvas.toDataURL('image/jpeg', 0.95)
|
||
const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' })
|
||
const pageW = pdf.internal.pageSize.getWidth() // 210
|
||
const pageH = pdf.internal.pageSize.getHeight() // 297
|
||
const margin = 8
|
||
const printW = pageW - margin * 2
|
||
const ratio = canvas.width / printW
|
||
const imgH = canvas.height / ratio
|
||
let remaining = imgH
|
||
let srcY = 0
|
||
|
||
while (remaining > 0) {
|
||
const sliceH = Math.min(pageH - margin * 2, remaining)
|
||
const slicePx = sliceH * ratio
|
||
const sliceCanvas = document.createElement('canvas')
|
||
sliceCanvas.width = canvas.width
|
||
sliceCanvas.height = slicePx
|
||
const ctx = sliceCanvas.getContext('2d')
|
||
ctx.drawImage(canvas, 0, srcY, canvas.width, slicePx, 0, 0, canvas.width, slicePx)
|
||
pdf.addImage(sliceCanvas.toDataURL('image/jpeg', 0.95), 'JPEG', margin, margin, printW, sliceH)
|
||
srcY += slicePx
|
||
remaining -= sliceH
|
||
if (remaining > 0) pdf.addPage()
|
||
}
|
||
|
||
const coilId = this.plan?.hot_coilid || this.plan?.coilid || 'report'
|
||
pdf.save(`质量报表_${coilId}.pdf`)
|
||
} catch (e) {
|
||
this.$message.error('PDF导出失败:' + e.message)
|
||
console.error(e)
|
||
} finally {
|
||
this.exporting = false
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* ── 工具栏 ── */
|
||
.rpt-toolbar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 0 10px;
|
||
border-bottom: 1px solid #f0f2f5;
|
||
margin-bottom: 10px;
|
||
}
|
||
.rpt-coil-hint { font-size: 13px; color: #606266; font-weight: 500; }
|
||
.rpt-toolbar-right { display: flex; gap: 8px; }
|
||
|
||
/* ── 加载 ── */
|
||
.rpt-loading {
|
||
padding: 40px 0;
|
||
text-align: center;
|
||
font-size: 14px;
|
||
color: #909399;
|
||
}
|
||
|
||
/* ── 报告主体 ── */
|
||
.report-body {
|
||
background: #fff;
|
||
padding: 20px 24px;
|
||
font-size: 12px;
|
||
color: #303133;
|
||
min-height: 300px;
|
||
}
|
||
|
||
/* 页眉 */
|
||
.rpt-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
border-bottom: 2px solid #2c5282;
|
||
padding-bottom: 10px;
|
||
margin-bottom: 14px;
|
||
}
|
||
.rpt-header-left { font-size: 11px; color: #555; line-height: 1.6; }
|
||
.company-line { font-weight: 600; font-size: 13px; }
|
||
.mcc-line { font-size: 10px; color: #888; }
|
||
.rpt-header-title { font-size: 22px; font-weight: 700; color: #1a365d; letter-spacing: 2px; }
|
||
.rpt-header-right { text-align: right; }
|
||
.brand-name { font-size: 16px; font-weight: 700; color: #2c5282; letter-spacing: 1px; }
|
||
.brand-sub { font-size: 9px; color: #888; letter-spacing: 2px; }
|
||
|
||
/* 节标题 */
|
||
.rpt-section-bar {
|
||
background: #2c5282;
|
||
color: #fff;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
padding: 4px 10px;
|
||
border-radius: 2px;
|
||
margin: 10px 0 6px;
|
||
letter-spacing: 1px;
|
||
}
|
||
|
||
/* 规格表 & 质量判定表 */
|
||
.rpt-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 11.5px;
|
||
margin-bottom: 4px;
|
||
}
|
||
.rpt-table td, .rpt-table th {
|
||
border: 1px solid #c9d4e0;
|
||
padding: 4px 8px;
|
||
text-align: left;
|
||
white-space: nowrap;
|
||
}
|
||
.rpt-table th {
|
||
background: #dce8f4;
|
||
color: #1a365d;
|
||
font-weight: 600;
|
||
text-align: center;
|
||
font-size: 11px;
|
||
}
|
||
.cell-label {
|
||
background: #eef3f9;
|
||
color: #4a6080;
|
||
font-weight: 500;
|
||
width: 110px;
|
||
}
|
||
.cell-val { color: #1a1a2e; font-weight: 600; }
|
||
.cell-coilid { font-size: 13px; color: #1a365d; letter-spacing: 0.5px; }
|
||
.cell-ok { color: #2e7d32; font-weight: 700; text-align: center; }
|
||
.cell-ng { color: #c62828; font-weight: 700; text-align: center; }
|
||
.quality-table td { text-align: center; }
|
||
|
||
/* 图表 */
|
||
.rpt-chart-title {
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
color: #2c5282;
|
||
margin: 14px 0 4px;
|
||
padding-left: 6px;
|
||
border-left: 3px solid #2c5282;
|
||
}
|
||
.rpt-chart-subtitle {
|
||
font-size: 11px;
|
||
font-weight: normal;
|
||
color: #888;
|
||
margin-left: 8px;
|
||
}
|
||
.rpt-chart {
|
||
width: 100%;
|
||
height: 180px;
|
||
border: 1px solid #e8edf2;
|
||
border-radius: 3px;
|
||
background: #fafbfc;
|
||
}
|
||
|
||
/* 无数据 */
|
||
.rpt-no-data {
|
||
padding: 28px 0;
|
||
text-align: center;
|
||
font-size: 13px;
|
||
color: #c0c4cc;
|
||
}
|
||
|
||
/* 页脚 */
|
||
.rpt-footer {
|
||
margin-top: 20px;
|
||
padding-top: 8px;
|
||
border-top: 1px solid #e2e8f0;
|
||
text-align: center;
|
||
font-size: 10px;
|
||
color: #888;
|
||
}
|
||
|
||
/* 覆盖 dialog padding */
|
||
::v-deep .el-dialog__body { padding: 12px 20px 20px; }
|
||
::v-deep .el-button--primary {
|
||
background: #2c5282 !important; border-color: #2c5282 !important;
|
||
}
|
||
::v-deep .el-button--primary:hover { background: #1a365d !important; border-color: #1a365d !important; }
|
||
</style>
|