324 lines
8.5 KiB
Vue
324 lines
8.5 KiB
Vue
<template>
|
||
<div ref="techyBar" class="techy-bar"></div>
|
||
</template>
|
||
|
||
<script>
|
||
import echarts from 'echarts' // echarts theme
|
||
import resize from '@/views/OperationalOverview/components/mixins/resize'
|
||
import * as BottomPic from './bottom.png'
|
||
|
||
export default {
|
||
name: 'TechyBar',
|
||
props: {
|
||
datainfo: {
|
||
type: Array,
|
||
default: () => [
|
||
{
|
||
name: '产线A',
|
||
list: [100, 102, 104, 105, 100, 117]
|
||
},
|
||
{
|
||
name: '产线B',
|
||
list: [110, 92, 124, 85, 100, 120]
|
||
}
|
||
]
|
||
},
|
||
unitName: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
extraSpaceBetweenZero: {
|
||
type: Number,
|
||
default: 25
|
||
}
|
||
},
|
||
mixins: [resize],
|
||
data() {
|
||
const color_gradients = [
|
||
/** 蓝 */
|
||
{
|
||
direction: 'to top',
|
||
from: '#49B2FF', // * 0.6 是底部颜色和顶部颜色
|
||
to: '#49B2FF00',
|
||
topAndBottom: '#49B2FF9F'
|
||
},
|
||
/** 绿 */
|
||
{
|
||
direction: 'to top',
|
||
from: '#49FBD6', // * 0.6 是底部颜色和顶部颜色
|
||
to: '#49FBD600',
|
||
topAndBottom: '#49FBD69F'
|
||
}
|
||
]
|
||
|
||
let result = []
|
||
|
||
this.datainfo.map((pl, index) => {
|
||
let topCircle = {
|
||
__position: 'top',
|
||
name: pl.name,
|
||
barGap: '10%',
|
||
barCategoryGap: '48%',
|
||
type: 'pictorialBar',
|
||
symbol: 'circle',
|
||
symbolPosition: 'end',
|
||
symbolOffset: ['25%', '-50%'],
|
||
// symbolSize: ['100%', 6],
|
||
symbolSize: ['200%', 6],
|
||
data: pl.list,
|
||
z: 10,
|
||
itemStyle: {
|
||
// color: color_gradients[index].topAndBottom
|
||
color: {
|
||
type: 'linear',
|
||
x: 0,
|
||
y: 1,
|
||
x2: 0,
|
||
y2: 0,
|
||
colorStops: [
|
||
{
|
||
offset: 0,
|
||
color: color_gradients[index].topAndBottom
|
||
},
|
||
{
|
||
offset: 1,
|
||
color: color_gradients[index].to
|
||
}
|
||
],
|
||
global: false // 缺省为 false
|
||
}
|
||
}
|
||
}
|
||
let bottomCircle = {
|
||
__position: 'top',
|
||
name: pl.name,
|
||
barGap: '10%',
|
||
barCategoryGap: '48%',
|
||
type: 'pictorialBar',
|
||
symbol: 'circle',
|
||
symbolOffset: ['-25%', '50%'],
|
||
symbolSize: ['200%', 6],
|
||
data: pl.list,
|
||
z: 10,
|
||
itemStyle: {
|
||
color: color_gradients[index].from
|
||
}
|
||
}
|
||
|
||
let mainBar = {
|
||
__position: 'main',
|
||
type: 'bar',
|
||
name: pl.name,
|
||
// barWidth: 20, // 不需要设置 barWidth
|
||
barGap: '10%',
|
||
barCategoryGap: '48%',
|
||
data: pl.list,
|
||
z: 0,
|
||
itemStyle: {
|
||
color: {
|
||
type: 'linear',
|
||
x: 0,
|
||
y: 1,
|
||
x2: 0,
|
||
y2: 0,
|
||
colorStops: [
|
||
{
|
||
offset: 0,
|
||
color: color_gradients[index].from
|
||
},
|
||
{
|
||
offset: 0.15,
|
||
color: color_gradients[index].topAndBottom
|
||
},
|
||
{
|
||
offset: 1,
|
||
color: color_gradients[index].to
|
||
}
|
||
],
|
||
global: false // 缺省为 false
|
||
}
|
||
},
|
||
label: {
|
||
show: true,
|
||
position: 'top',
|
||
color: '#fff8',
|
||
fontSize: 10,
|
||
offset: [0, 6]
|
||
}
|
||
}
|
||
|
||
result.push(topCircle)
|
||
result.push(mainBar)
|
||
result.push(bottomCircle)
|
||
})
|
||
|
||
return {
|
||
BottomPic,
|
||
width: 0,
|
||
chart: null,
|
||
option: {
|
||
grid: {
|
||
left: '10%',
|
||
top: 72,
|
||
bottom: 28
|
||
},
|
||
tooltip: {
|
||
show: true,
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'shadow',
|
||
shadowStyle: {
|
||
color: 'rgba(255,255,255,0.1)'
|
||
}
|
||
},
|
||
padding: 10,
|
||
backgroundColor: 'rgba(13, 29, 53, 0.8)',
|
||
extraCssText: 'width: 128px !important; height: auto !important;',
|
||
formatter: params => {
|
||
const [, a, , , b] = params
|
||
return `<div style="display: flex; flex-direction: column; gap: 2px; align-items: flex-start; ">
|
||
<h2 style="font-size: calc(14px * var(--beilv)); margin: 0 0 4px; font-weight: normal; color: white;">${
|
||
a.name
|
||
}</h2>
|
||
<div style="display: flex; align-items: center; gap: 8px;">
|
||
<span style="width: 12px; height: 12px; background: linear-gradient(to top, ${
|
||
a.color.colorStops[0].color
|
||
}, transparent);"></span>
|
||
<span style="font-size: calc(10px * var(--beilv));">${a.seriesName}: ${a.data}</span>
|
||
</div>
|
||
<div style="display: flex; align-items: center; gap: 8px;">
|
||
<span style="width: 12px; height: 12px; background: linear-gradient(to top, ${
|
||
b.color.colorStops[0].color
|
||
}, transparent);"></span>
|
||
<span style="font-size: calc(10px * var(--beilv));">${b.seriesName}: ${b.data}</span>
|
||
</div>
|
||
</div>`
|
||
}
|
||
},
|
||
xAxis: {
|
||
type: 'category',
|
||
axisTick: {
|
||
show: false
|
||
},
|
||
axisLine: {
|
||
onZero: false,
|
||
show: false,
|
||
lineStyle: {}
|
||
},
|
||
axisLabel: {
|
||
color: '#fff8',
|
||
fontSize: 12
|
||
},
|
||
data: ['脏污', '破片', '崩边', '崩孔', '划擦伤', '其他']
|
||
// axisPointer: {
|
||
// show: true,
|
||
// type: 'shadow',
|
||
// label: {
|
||
// show: true,
|
||
// formatter: params => {
|
||
// let info_arr = params.seriesData.filter(item => item.seriesType === 'bar')
|
||
// let v = [`<${info_arr[0].name}>详细数据: \n`]
|
||
// info_arr.map(item => { v.push(`${item.seriesName}: ${item.data}`) })
|
||
// return v.join('\n')
|
||
// }
|
||
// }
|
||
// }
|
||
},
|
||
yAxis: {
|
||
type: 'value',
|
||
// min: -25,
|
||
min: this.extraSpaceBetweenZero * -1,
|
||
name: this.unitName + ' ',
|
||
nameTextStyle: {
|
||
color: '#fff8',
|
||
fontSize: 12,
|
||
verticalAlign: 'top',
|
||
align: 'right'
|
||
},
|
||
nameGap: 20,
|
||
axisLine: {
|
||
show: true,
|
||
lineStyle: {
|
||
color: '#31A2FF'
|
||
}
|
||
},
|
||
axisLabel: {
|
||
color: '#fff8',
|
||
fontSize: 12,
|
||
/** y轴不从0开始,也可以用 xAxis 向下 offset 的方式模拟 **/
|
||
formatter: function(value, index) {
|
||
if (value < 0) {
|
||
return ''
|
||
}
|
||
return value
|
||
}
|
||
},
|
||
axisTick: { show: false },
|
||
splitLine: {
|
||
lineStyle: {
|
||
color: '#569acd',
|
||
type: 'dotted',
|
||
opacity: 0.2
|
||
}
|
||
}
|
||
},
|
||
|
||
series: result,
|
||
|
||
graphic: [
|
||
{
|
||
type: 'image',
|
||
left: 0,
|
||
bottom: 0,
|
||
scaleX: 0.8,
|
||
style: {
|
||
image: 'image url',
|
||
height: 0,
|
||
width: 0
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
},
|
||
mounted() {
|
||
window.addEventListener('resize', this.refreshOption)
|
||
if (!this.chart) this.chart = echarts.init(this.$refs.techyBar)
|
||
|
||
this.$nextTick(() => {
|
||
this.updateOption(this.option)
|
||
this.chart.setOption(this.option)
|
||
})
|
||
},
|
||
methods: {
|
||
updateOption(option) {
|
||
// console.log('option', this.BottomPic.default)
|
||
let width = this.$refs.techyBar.querySelector('div').clientWidth
|
||
let height = this.$refs.techyBar.querySelector('div').clientHeight
|
||
option.graphic[0].style.width = width * 0.85
|
||
option.graphic[0].style.height = height * 0.25
|
||
option.graphic[0].style.image = this.BottomPic.default
|
||
option.graphic[0].left = '10%'
|
||
option.graphic[0].bottom = 28
|
||
},
|
||
refreshOption() {
|
||
this.updateOption(this.option)
|
||
this.chart.setOption(this.option)
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.techy-bar {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.techy-bar >>> div {
|
||
width: 100% !important;
|
||
height: 100% !important;
|
||
}
|
||
</style>
|