Files
klp-oa/klp-ui/src/views/da/oee/index.vue
2026-03-20 13:36:42 +08:00

835 lines
28 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="oee-report-page">
<!-- 查询条件概览去掉大标题只保留区间提示 -->
<el-card class="oee-header-card" shadow="never">
<div class="oee-header">
<div class="oee-title-block">
<div class="oee-subtitle">
查询区间
<span v-if="queryRange && queryRange.length === 2">
{{ queryRange[0] }} ~ {{ queryRange[1] }}
</span>
</div>
</div>
<div class="oee-query-bar">
<el-select v-model="lineType" size="small" style="width: 120px">
<el-option label="酸轧线" value="acid" />
<el-option label="镀锌一线" value="galvanize1" />
</el-select>
<el-date-picker
v-model="queryRange"
type="daterange"
unlink-panels
size="small"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
:picker-options="pickerOptions"
/>
<el-button
type="primary"
size="small"
icon="el-icon-search"
@click="handleSearch"
>
查询
</el-button>
<el-button
size="small"
icon="el-icon-refresh"
@click="handleReset"
>
重置
</el-button>
<el-button
size="small"
icon="el-icon-document"
@click="handleExportWord"
>
导出 Word
</el-button>
</div>
</div>
</el-card>
<el-alert
v-if="lineType === 'galvanize1'"
title="镀锌二级数据未完全使用,故而停机时间可能有错误"
type="warning"
:closable="false"
class="oee-top-warning"
show-icon
/>
<el-row :gutter="16" class="oee-main-row">
<!-- 左侧报表主体Word 风格 -->
<el-col :span="18">
<el-card class="oee-word-card" shadow="never">
<!-- KPI 一览报表头部表格 -->
<div class="oee-section-title">KPI 总览酸轧线 SY</div>
<div class="oee-help-text">
展示所选期间酸轧线整体 OEE A/P/Q 等关键指标用于快速判断本期综合表现好坏
</div>
<el-table
:data="[kpi]"
border
size="mini"
class="oee-kpi-table"
v-loading="loading.summary"
>
<el-table-column prop="oee" label="OEE (%)" align="center">
<template slot-scope="scope">
<span>{{ formatPercent(scope.row.oee) }}</span>
</template>
</el-table-column>
<el-table-column prop="availability" label="时间稼动率 A (%)" align="center">
<template slot-scope="scope">
<span>{{ formatPercent(scope.row.availability) }}</span>
</template>
</el-table-column>
<el-table-column prop="performanceTon" label="性能稼动率 P_ton (%)" align="center">
<template slot-scope="scope">
<span>{{ formatPercent(scope.row.performanceTon) }}</span>
</template>
</el-table-column>
<el-table-column prop="quality" label="良品率(%)" align="center">
<template slot-scope="scope">
<span>{{ formatPercent(scope.row.quality) }}</span>
</template>
</el-table-column>
<el-table-column prop="qualifiedRate" label="合格品率(%)" align="center">
<template slot-scope="scope">
<span>{{ formatPercent(scope.row.qualifiedRate) }}</span>
</template>
</el-table-column>
<el-table-column prop="defectRate" label="次品率 (%)" align="center">
<template slot-scope="scope">
<span>{{ formatPercent(scope.row.defectRate) }}</span>
</template>
</el-table-column>
<el-table-column prop="pendingRate" label="待判级率 (%)" align="center">
<template slot-scope="scope">
<span>{{ formatPercent(scope.row.pendingRate) }}</span>
</template>
</el-table-column>
<el-table-column prop="loadingTimeMin" label="负荷时间 (min)" align="center" />
<el-table-column prop="downtimeMin" label="停机时间 (min)" align="center" />
<el-table-column prop="runTimeMin" label="运转时间 (min)" align="center" />
<el-table-column prop="totalOutputTon" label="总产量 (吨)" align="center">
<template slot-scope="scope">
<span>{{ formatNumber(scope.row.totalOutputTon) }}</span>
</template>
</el-table-column>
<el-table-column prop="totalOutputCoil" label="总产量 (卷)" align="center" />
<el-table-column prop="goodOutputTon" label="良品(吨)" align="center">
<template slot-scope="scope">
<span>{{ formatNumber(scope.row.goodOutputTon) }}</span>
</template>
</el-table-column>
<el-table-column prop="abOutputTon" label="合格(吨)" align="center">
<template slot-scope="scope">
<span>{{ formatNumber(scope.row.abOutputTon) }}</span>
</template>
</el-table-column>
<el-table-column prop="defectOutputTon" label="次品(吨)" align="center">
<template slot-scope="scope">
<span>{{ formatNumber(scope.row.defectOutputTon) }}</span>
</template>
</el-table-column>
<el-table-column prop="pendingOutputTon" label="待判(吨)" align="center">
<template slot-scope="scope">
<span>{{ formatNumber(scope.row.pendingOutputTon) }}</span>
</template>
</el-table-column>
</el-table>
<!-- 日明细趋势表格风格方便导出 Word -->
<div class="oee-section-title">日明细用于趋势分析</div>
<div class="oee-help-text">
按天拆分 A/P/Q 及产量等指标用于观察本月趋势波动点以及与重大事件的对应关系
</div>
<el-table
:data="summaryList"
border
size="mini"
class="oee-daily-table"
v-loading="loading.summary"
>
<el-table-column prop="statDate" label="日期" />
<el-table-column prop="oee" label="OEE (%)" >
<template slot-scope="scope">
{{ formatPercent(scope.row.oee) }}
</template>
</el-table-column>
<el-table-column prop="availability" label="A (%)" >
<template slot-scope="scope">
{{ formatPercent(scope.row.availability) }}
</template>
</el-table-column>
<el-table-column prop="performanceTon" label="P_ton (%)">
<template slot-scope="scope">
{{ formatPercent(scope.row.performanceTon) }}
</template>
</el-table-column>
<el-table-column prop="quality" label="良品率 (%)">
<template slot-scope="scope">
{{ formatPercent(scope.row.quality) }}
</template>
</el-table-column>
<el-table-column prop="qualifiedRate" label="合格品率 (%)">
<template slot-scope="scope">
{{ formatPercent(scope.row.qualifiedRate) }}
</template>
</el-table-column>
<el-table-column prop="defectRate" label="次品率 (%)">
<template slot-scope="scope">
{{ formatPercent(scope.row.defectRate) }}
</template>
</el-table-column>
<el-table-column prop="pendingRate" label="待判级率 (%)">
<template slot-scope="scope">
{{ formatPercent(scope.row.pendingRate) }}
</template>
</el-table-column>
<el-table-column prop="loadingTimeMin" label="负荷 (min)"/>
<el-table-column prop="downtimeMin" label="停机 (min)"/>
<el-table-column prop="runTimeMin" label="运转 (min)"/>
<el-table-column prop="totalOutputTon" label="总产量 (吨)">
<template slot-scope="scope">
{{ formatNumber(scope.row.totalOutputTon) }}
</template>
</el-table-column>
<el-table-column prop="totalOutputCoil" label="总产量 (卷)" />
<el-table-column prop="goodOutputTon" label="良品(吨)">
<template slot-scope="scope">
{{ formatNumber(scope.row.goodOutputTon) }}
</template>
</el-table-column>
<el-table-column prop="abOutputTon" label="合格(吨)">
<template slot-scope="scope">
{{ formatNumber(scope.row.abOutputTon) }}
</template>
</el-table-column>
<el-table-column prop="defectOutputTon" label="次品(吨)">
<template slot-scope="scope">
{{ formatNumber(scope.row.defectOutputTon) }}
</template>
</el-table-column>
<el-table-column prop="pendingOutputTon" label="待判(吨)">
<template slot-scope="scope">
{{ formatNumber(scope.row.pendingOutputTon) }}
</template>
</el-table-column>
</el-table>
<!-- OEE/A/P/Q 趋势图 -->
<oee-trend-chart :data="summaryList" />
<!-- 理论节拍统计口径 -->
<div class="oee-section-title">理论节拍统计口径</div>
<div class="oee-help-text">
基于历史优良日统计得到的理论节拍中位数用于作为性能稼动率计算的稳定标尺
</div>
<div
v-if="idealCycle && idealCycle.dailyComparePoints && idealCycle.dailyComparePoints.length"
class="oee-reg-section"
>
<div class="reg-chart-block">
<div class="reg-chart-title">日粒度理论耗时 vs 实际运转时间</div>
<div class="oee-help-text">
按天对比理想应耗时间实际运转时间可一眼看出哪几天效率偏低存在明显损失空间
</div>
<div ref="regDailyCompareChart" class="reg-chart"></div>
</div>
</div>
<!-- 7 大损失 -->
<div class="oee-section-title">7 大损失汇总 stop_type 分类</div>
<div class="oee-help-text">
将所有停机事件按 stop_type 归类统计时间占比和次数用于确定先从哪几类损失下手改善
</div>
<el-table
:data="loss7List"
border
size="mini"
class="oee-loss7-table"
v-loading="loading.loss7"
>
<el-table-column prop="lossCategoryName" label="损失类别" min-width="160" />
<el-table-column prop="lossTimeMin" label="损失时间 (min)" width="140" />
<el-table-column prop="lossTimeRate" label="占比 (%)" width="110">
<template slot-scope="scope">
{{ formatPercent(scope.row.lossTimeRate) }}
</template>
</el-table-column>
<el-table-column prop="count" label="次数" width="90" />
<el-table-column prop="avgDurationMin" label="平均时长 (min)" width="140">
<template slot-scope="scope">
{{ formatNumber(scope.row.avgDurationMin) }}
</template>
</el-table-column>
</el-table>
<!-- 7 大损失帕累托图 -->
<oee-loss-pareto :data="loss7List" />
<!-- 停机事件明细 -->
<div class="oee-section-title">停机/损失事件明细</div>
<div class="oee-help-text">
罗列每一条停机/损失事件包含时间段区域机组和备注方便班组和工艺人员对照现场记录进行原因分析
</div>
<el-table
:data="eventList"
border
size="mini"
class="oee-events-table"
v-loading="loading.events"
>
<el-table-column prop="startDate" label="开始时间" min-width="150">
<template slot-scope="scope">
{{ formatDateTime(scope.row.startDate) }}
</template>
</el-table-column>
<el-table-column prop="endDate" label="结束时间" min-width="150">
<template slot-scope="scope">
{{ formatDateTime(scope.row.endDate) }}
</template>
</el-table-column>
<el-table-column prop="duration" label="时长 (s)" width="100" />
<el-table-column prop="durationMin" label="时长 (min)" width="110">
<template slot-scope="scope">
{{ Math.max(1, Math.floor((scope.row.duration || 0) / 60)) }}
</template>
</el-table-column>
<el-table-column prop="stopType" label="停机类型 (stop_type)" min-width="160" />
<el-table-column prop="area" label="区域" width="100" />
<el-table-column prop="unit" label="机组" width="100" />
<el-table-column prop="shift" label="班次" width="80" />
<el-table-column prop="crew" label="班组" width="80" />
<el-table-column prop="remark" label="备注" min-width="180" show-overflow-tooltip />
</el-table>
<!-- 停机 TopN 条形图 -->
<oee-stoppage-top :data="eventList" :top-n="10" />
</el-card>
</el-col>
<!-- 右侧公式与口径说明支撑材料 -->
<el-col :span="6">
<el-card class="oee-formula-card" shadow="never">
<div slot="header" class="clearfix">
<span>公式与口径说明</span>
</div>
<div class="formula-block">
<div class="formula-title">OEE 总公式</div>
<div class="formula-eq">OEE = A × P × Q</div>
<ul class="formula-list">
<li>A时间稼动率 = (负荷时间 停机时间) / 负荷时间</li>
<li>P性能稼动率吨维度 = (理论节拍 × 产量吨) / 实际运转时间</li>
<li>Q良品率 = A系列吨 / 总产量吨</li>
</ul>
</div>
<div class="formula-block">
<div class="formula-title">关键字段定义</div>
<ul class="formula-list">
<li><b>负荷时间</b>计划生产时间扣除计划停机后的时间</li>
<li><b>停机时间</b>所有停机/中断 stop_type 汇总的总时长</li>
<li><b>实际运转时间</b>负荷时间 停机时间</li>
<li><b>理论节拍</b>使用整体酸轧库中位数学习得出</li>
<li><b>A系列</b>良品<b>B系列</b>合格品<b>C/D系列</b>次品<b>O系列</b>待判级</li>
<li><b>合格品率(AB)</b> = (A+B) / 总量<b>次品率(CD)</b> = CD / 总量<b>待判级率(O)</b> = O / 总量</li>
</ul>
</div>
<div class="formula-block" v-if="idealCycle">
<div class="formula-title">当前理论节拍统计口径</div>
<el-descriptions :column="1" size="small" class="reg-desc">
<el-descriptions-item label="理论节拍 (min/吨)">
{{ formatNumber(idealCycle.idealCycleTimeMinPerTon) }}
</el-descriptions-item>
<el-descriptions-item label="生产节拍中位数 (min/吨)">
{{ formatNumber(idealCycle.medianCycleTimeMinPerTon) }}
</el-descriptions-item>
<el-descriptions-item label="样本天数">
{{ idealCycle.sampleDays || 0 }}
</el-descriptions-item>
<el-descriptions-item label="吨数下限 (吨)">
{{ formatNumber(idealCycle.minWeightTon) }}
</el-descriptions-item>
<el-descriptions-item label="停机占比上限">
{{
idealCycle.maxDowntimeRate != null
? (Number(idealCycle.maxDowntimeRate) * 100).toFixed(0) + '%'
: '-'
}}
</el-descriptions-item>
</el-descriptions>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import {
exportOeeWord,
fetchOeeSummary,
fetchOeeLoss7,
fetchOeeEvents,
fetchOeeIdealCycle
} from '@/api/da/oee'
import * as echarts from 'echarts'
import OeeTrendChart from './components/OeeTrendChart.vue'
import OeeLossPareto from './components/OeeLossPareto.vue'
import OeeStoppageTop from './components/OeeStoppageTop.vue'
export default {
name: 'DaOeeIndex',
components: {
OeeTrendChart,
OeeLossPareto,
OeeStoppageTop
},
data() {
const today = new Date()
const firstDay = new Date(today.getFullYear(), today.getMonth(), 1)
return {
lineType: 'acid',
queryRange: [
this.formatDate(firstDay),
this.formatDate(today)
],
pickerOptions: {
disabledDate() {
// 不限制选择范围,保留扩展空间
return false
}
},
loading: {
summary: false,
loss7: false,
events: false,
idealCycle: false,
export: false
},
summaryList: [],
loss7List: [],
eventList: [],
idealCycle: null,
regDailyCompareChart: null
}
},
computed: {
kpi() {
const list = Array.isArray(this.summaryList) ? this.summaryList : []
if (list.length === 0) {
return {
oee: 0,
availability: 0,
performanceTon: 0,
quality: 0,
qualifiedRate: 0,
defectRate: 0,
pendingRate: 0,
loadingTimeMin: 0,
downtimeMin: 0,
runTimeMin: 0,
totalOutputTon: 0,
totalOutputCoil: 0,
goodOutputTon: 0,
abOutputTon: 0,
defectOutputTon: 0,
pendingOutputTon: 0
}
}
let sumLoading = 0
let sumDowntime = 0
let sumRun = 0
let sumTotalTon = 0
let sumGoodTon = 0
let sumAbTon = 0
let sumDefectTon = 0
let sumPendingTon = 0
let sumCoil = 0
let sumOee = 0
let sumA = 0
let sumP = 0
let sumQ = 0
let sumQualifiedRate = 0
let sumDefectRate = 0
let sumPendingRate = 0
list.forEach(row => {
sumLoading += row.loadingTimeMin || 0
sumDowntime += row.downtimeMin || 0
sumRun += row.runTimeMin || 0
sumTotalTon += Number(row.totalOutputTon || 0)
sumGoodTon += Number(row.goodOutputTon || 0)
sumAbTon += Number(row.abOutputTon || 0)
sumDefectTon += Number(row.defectOutputTon || 0)
sumPendingTon += Number(row.pendingOutputTon || 0)
sumCoil += row.totalOutputCoil || 0
sumOee += Number(row.oee || 0)
sumA += Number(row.availability || 0)
sumP += Number(row.performanceTon || 0)
sumQ += Number(row.quality || 0)
sumQualifiedRate += Number(row.qualifiedRate || 0)
sumDefectRate += Number(row.defectRate || 0)
sumPendingRate += Number(row.pendingRate || 0)
})
const n = list.length
return {
oee: n ? sumOee / n : 0,
availability: n ? sumA / n : 0,
performanceTon: n ? sumP / n : 0,
quality: n ? sumQ / n : 0,
qualifiedRate: n ? sumQualifiedRate / n : 0,
defectRate: n ? sumDefectRate / n : 0,
pendingRate: n ? sumPendingRate / n : 0,
loadingTimeMin: sumLoading,
downtimeMin: sumDowntime,
runTimeMin: sumRun,
totalOutputTon: sumTotalTon,
totalOutputCoil: sumCoil,
goodOutputTon: sumGoodTon,
abOutputTon: sumAbTon,
defectOutputTon: sumDefectTon,
pendingOutputTon: sumPendingTon
}
}
},
created() {
this.loadAll()
},
mounted() {
window.addEventListener('resize', this.handleResize)
},
beforeDestroy() {
window.removeEventListener('resize', this.handleResize)
if (this.regDailyCompareChart) {
this.regDailyCompareChart.dispose()
this.regDailyCompareChart = null
}
},
methods: {
formatDate(d) {
const y = d.getFullYear()
const m = (`0${d.getMonth() + 1}`).slice(-2)
const day = (`0${d.getDate()}`).slice(-2)
return `${y}-${m}-${day}`
},
formatPercent(value) {
if (value === null || value === undefined || isNaN(value)) {
return '-'
}
const num = Number(value)
return num.toFixed(1)
},
formatNumber(value) {
if (value === null || value === undefined || value === '') {
return '-'
}
const num = Number(value)
if (isNaN(num)) return '-'
return num.toFixed(2)
},
formatDateTime(val) {
if (!val) return ''
// 后端可能返回字符串或时间戳,这里做容错
if (typeof val === 'string') return val
try {
const d = new Date(val)
const y = d.getFullYear()
const m = (`0${d.getMonth() + 1}`).slice(-2)
const day = (`0${d.getDate()}`).slice(-2)
const hh = (`0${d.getHours()}`).slice(-2)
const mm = (`0${d.getMinutes()}`).slice(-2)
const ss = (`0${d.getSeconds()}`).slice(-2)
return `${y}-${m}-${day} ${hh}:${mm}:${ss}`
} catch (e) {
return ''
}
},
buildQuery() {
const [start, end] = this.queryRange || []
return {
lineType: this.lineType,
startDate: start,
endDate: end
}
},
async loadAll() {
await Promise.all([
this.loadSummary(),
this.loadLoss7(),
this.loadEvents(),
this.loadIdealCycle()
])
},
async loadSummary() {
this.loading.summary = true
try {
const res = await fetchOeeSummary(this.buildQuery())
// 兼容后端直接返回数组或 TableDataInfo { rows, total } 两种结构
this.summaryList = res.data
} catch (e) {
this.$message.error('加载酸轧日汇总失败')
} finally {
this.loading.summary = false
}
},
async loadLoss7() {
this.loading.loss7 = true
try {
const res = await fetchOeeLoss7(this.buildQuery())
// 兼容后端直接返回数组或 TableDataInfo { rows, total } 两种结构
this.loss7List = res.data
} catch (e) {
this.$message.error('加载酸轧 7 大损失失败')
} finally {
this.loading.loss7 = false
}
},
async loadEvents() {
this.loading.events = true
try {
const res = await fetchOeeEvents(this.buildQuery())
// 后端 TableDataInfo 结构:{ rows, total }
this.eventList = (res && Array.isArray(res.rows)) ? res.rows : []
} catch (e) {
this.$message.error('加载酸轧停机事件失败')
} finally {
this.loading.events = false
}
},
async loadIdealCycle() {
this.loading.idealCycle = true
try {
const res = await fetchOeeIdealCycle(this.buildQuery())
this.idealCycle = res && res.data ? res.data : null
this.$nextTick(() => {
this.renderIdealCycleChart()
})
} catch (e) {
this.$message.error('加载理论节拍失败')
} finally {
this.loading.idealCycle = false
}
},
handleSearch() {
if (!this.queryRange || this.queryRange.length !== 2) {
this.$message.warning('请选择查询日期范围')
return
}
this.loadAll()
},
handleReset() {
const today = new Date()
const firstDay = new Date(today.getFullYear(), today.getMonth(), 1)
this.queryRange = [this.formatDate(firstDay), this.formatDate(today)]
this.loadAll()
},
async handleExportWord() {
this.loading.export = true
try {
const res = await exportOeeWord(this.buildQuery())
const blob = new Blob([res], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
})
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `酸轧线OEE报表_${this.queryRange[0]}_${this.queryRange[1]}.docx`
a.click()
window.URL.revokeObjectURL(url)
} catch (e) {
this.$message.error('导出 Word 失败')
} finally {
this.loading.export = false
}
},
renderIdealCycleChart() {
if (!this.idealCycle) return
const dailyPoints = Array.isArray(this.idealCycle.dailyComparePoints)
? this.idealCycle.dailyComparePoints
: []
if (this.$refs.regDailyCompareChart && dailyPoints.length) {
if (!this.regDailyCompareChart) {
this.regDailyCompareChart = echarts.init(this.$refs.regDailyCompareChart)
}
const categories = dailyPoints.map(d => d.statDate)
const actualRun = dailyPoints.map(d => Number(d.actualRunTimeMin || 0))
const theoretical = dailyPoints.map(d => Number(d.theoreticalTimeMin || 0))
const compareOption = {
tooltip: { trigger: 'axis' },
grid: { left: 40, right: 20, top: 30, bottom: 40 },
legend: { top: 0, left: 'center' },
xAxis: {
type: 'category',
data: categories,
axisLabel: { fontSize: 10 }
},
yAxis: {
type: 'value',
name: '时间 (min)',
nameLocation: 'middle',
nameGap: 35
},
series: [
{
name: '实际运转时间',
type: 'line',
data: actualRun,
smooth: true,
symbolSize: 4,
itemStyle: { color: '#409EFF' }
},
{
name: '理论耗时',
type: 'line',
data: theoretical,
smooth: true,
symbolSize: 4,
itemStyle: { color: '#E6A23C' }
}
]
}
this.regDailyCompareChart.setOption(compareOption)
}
},
handleResize() {
this.regDailyCompareChart && this.regDailyCompareChart.resize()
}
}
}
</script>
<style scoped>
.oee-report-page {
padding: 16px;
background: #f5f7fa;
}
.oee-header-card {
margin-bottom: 12px;
}
.oee-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.oee-title-block {
display: flex;
flex-direction: column;
}
.oee-title {
font-size: 20px;
font-weight: 600;
color: #303133;
}
.oee-subtitle {
margin-top: 4px;
font-size: 13px;
color: #606266;
}
.oee-query-bar {
display: flex;
align-items: center;
gap: 8px;
}
.oee-top-warning {
margin-bottom: 8px;
}
.oee-main-row {
margin-top: 8px;
}
.oee-word-card {
min-height: 600px;
background: #ffffff;
border-radius: 4px;
font-size: 12px;
line-height: 1.5;
overflow-x: hidden;
}
.oee-section-title {
margin: 12px 0 6px;
font-weight: 600;
color: #303133;
}
.oee-help-text {
margin: 0 0 6px;
font-size: 12px;
color: #909399;
}
.oee-kpi-table,
.oee-daily-table,
.oee-loss7-table,
.oee-events-table {
font-size: 12px;
}
.oee-formula-card {
background: #ffffff;
}
.formula-block {
margin-bottom: 16px;
}
.formula-title {
font-weight: 600;
margin-bottom: 6px;
color: #303133;
}
.formula-eq {
font-family: 'Times New Roman', serif;
font-style: italic;
margin-bottom: 6px;
}
.formula-list {
padding-left: 16px;
margin: 0;
}
.formula-list li {
margin-bottom: 4px;
}
.reg-desc {
font-size: 12px;
}
.reg-chart {
width: 100%;
height: 260px;
border: 1px solid #ebeef5;
border-radius: 4px;
box-sizing: border-box;
overflow: hidden;
}
.reg-chart canvas {
max-width: 100% !important;
}
</style>