import * as echarts from 'echarts' function getStartTime(timestamp) { return new Date(new Date(timestamp).toLocaleDateString()).getTime(); } function renderItem(params, api) { var categoryIndex = api.value(0); var start = api.coord([api.value(1), categoryIndex]); var end = api.coord([api.value(2), categoryIndex]); var height = api.size([0, 1])[1] * 2; var rectShape = echarts.graphic.clipRectByRect( { x: start[0], y: start[1] - height / 2, width: end[0] - start[0], height: height, }, { x: params.coordSys.x, y: params.coordSys.y - 16, width: params.coordSys.width, height: params.coordSys.height, } ); return ( rectShape && { type: 'rect', transition: ['shape'], shape: rectShape, style: api.style(), } ); } // unused function getXaxisRange(startTime) { return Array(24) .fill(startTime) .map((item, index) => { return new Date(item + index * 3600 * 1000) .toLocaleTimeString() .split(':') .slice(0, 2) .join(':'); }); } function getTodayStart(today) { const [y, m, d] = [ today.getFullYear(), today.getMonth(), today.getDate(), ]; // debugger; return new Date(y, m, d).getTime(); } /** 颜色配置 */ const types = [ { name: '运行', color: '#288AFF' }, { name: '故障', color: '#FC9C91' }, { name: '计划停机', color: '#FFDC94' }, { name: '空白', color: '#F2F4F9' }, ]; export default class GanttGraph { // tooltip - 基本是固定的 tooltip = { trigger: 'item', axisPointer: { type: 'none', }, formatter: (params) => { return `

${params.seriesName}

${params.name}

${new Date(params.value[1]).toLocaleString()} ~ ${new Date(params.value[2]).toLocaleString()}
` } } grid = [] xAxis = [] yAxis = [] series = [] constructor(el, startTime) { this.el = el; this.gridIndex = -1; this.currentGraphIndex = -2; this.startTime = new Date(startTime); // this.startTime = new Date(new Date('2023/10/8').toLocaleDateString()); console.log('<> Gantt Created', this.startTime); // this.grid.push(this.makeGrid()) // this.xAxis.push(...this.makeXaxis()) // this.yAxis.push(...this.makeYaxis("设备1")) // this.series.push(...this.makeSeries({ equipmentName: "设备1" })) // this.grid.push(this.makeGrid()) // this.xAxis.push(...this.makeXaxis()) // this.yAxis.push(...this.makeYaxis("设备2")) // this.series.push(...this.makeSeries({ equipmentName: "设备2" })) } // 构造一个新的 grid makeGrid() { this.gridIndex++; return { id: 'GRID_' + this.gridIndex, // top: 12 + 128 * this.gridIndex, top: 12 + 104 * this.gridIndex, right: 48, left: 88, height: 56 } } // 构造一个 xAxis makeXaxis() { const [id1, id2] = ['' + Math.random(), '' + Math.random()] return [ { id: id1, gridIndex: this.gridIndex, axisTick: { alignWithLabel: true, inside: true, }, type: 'time', min: getTodayStart(this.startTime), max: getStartTime(this.startTime.getTime() + 3600 * 24 * 1000), splitNumber: 10, axisLabel: { margin: 12, formatter: function (val) { return new Date(val) .toLocaleTimeString() .split(':') .slice(0, 2) .join(':'); }, }, boundaryGap: false, // data: getXaxisRange(getTodayStart(new Date())), }, { id: id2, gridIndex: this.gridIndex, axisLabel: { show: false }, axisLine: { show: false }, }, ] } // 构造一个 yAxis makeYaxis(equipmentName) { const [id1, id2] = ['' + Math.random(), '' + Math.random()] return [ // 主y轴 { id: id1, gridIndex: this.gridIndex, type: 'value', splitLine: { show: false }, name: equipmentName, nameLocation: 'center', nameGap: 14, nameRotate: 0, nameTextStyle: { fontSize: 16, }, axisLine: { show: true, lineStyle: {}, }, axisLabel: { show: false }, axisTick: { show: false }, }, // 辅y轴 { id: id2, gridIndex: this.gridIndex, type: 'value', splitLine: { show: false }, axisLabel: { show: false }, axisTick: { show: false }, }, ] } // 构造一个 series makeSeries({ equipmentName, arr }) { this.currentGraphIndex += 2; const bgStartTime = this.startTime.getTime(); const bgEndTime = bgStartTime + 3600 * 24 * 1000; return [ // 沉默的背景 { xAxisIndex: this.currentGraphIndex, yAxisIndex: this.currentGraphIndex, type: 'custom', renderItem: renderItem, silent: true, itemStyle: { opacity: 0.8, }, encode: { x: [1, 2], y: 0, }, data: [ { name: '无数据', value: [0, bgStartTime, bgEndTime, 0], tooltip: { show: false }, itemStyle: { color: '#ccc', opacity: 0.3, } }, ] }, { name: equipmentName, xAxisIndex: this.currentGraphIndex, yAxisIndex: this.currentGraphIndex, type: 'custom', renderItem: renderItem, itemStyle: { opacity: 0.8, }, encode: { x: [1, 2], y: 0, }, data: arr.map(item => ({ name: ['运行', '故障', '计划停机'][item.status], value: [0, item.startTime, item.startTime + item.duration * 60 * 1000, 0], itemStyle: { color: types[item.status].color, } })), }, ] } init(data) { if (!this.el) throw new Error('没有可供echarts初始化的容器') if (typeof this.el == 'string') { this.el = document.querySelector(this.el); } this.chart = echarts.init(this.el); this.handleProps(data); setTimeout(() => { // debugger; this.chart.setOption(this.option); }, 200); } update(data) { this.clear(); this.init(data); } resize() { this.chart.resize(); } get option() { return { tooltip: this.tooltip, grid: this.grid, xAxis: this.xAxis, yAxis: this.yAxis, series: this.series, } } // 每次 graphList 刷新都会重新渲染整个所有图表 // 可以改进的地方:添加一个 handleAdd() 方法,一次添加一个新的 handleProps(props) { // props 是父组件的 graphList console.log('props: ', props); props.forEach(eqArr => { this.grid.push(this.makeGrid()); this.xAxis.push(...this.makeXaxis()); this.yAxis.push(...this.makeYaxis(eqArr.key)); this.series.push(...this.makeSeries({ equipmentName: eqArr.key, arr: eqArr })) }); } // handleAdd handleAdd() { } clear() { this.grid = []; this.xAxis = []; this.yAxis = []; this.series = []; this.currentGraphIndex = -2; this.gridIndex = -1; this.chart.dispose(); } // print option print() { console.log(JSON.stringify(this.option, null, 2)); } }