element-ui、vue2
<template>
<div class="change_record">
<div class="title-box">
<span class="title">设备状态变更情况</span>
<div>
<span class="show-style">显示</span>
<el-radio-group v-model="showKey" @change="changeRadio" style="margin-right: 24px;">
<el-radio label="sn">SN</el-radio>
<el-radio label="eid">EID</el-radio>
<el-radio label="iccid">ICCID</el-radio>
</el-radio-group>
<el-button plain class="down-excel" @click="exportExcel">{{$t('common.导出记录')}}</el-button>
</div>
</div>
<div class="change-box">
<div class="change-content">
<el-row class="change-item" v-for="(item, index) in tableData" :key="index">
<div class="timeline" :id="'timeline_'+index">{{item.eid}}</div>
<div class="explain">
<span class="huibiao" style="background-color: #DF001F;"></span>
<span>{{item.offline_format}}</span>
<span class="huibiao" style="background-color: #FFDD00;"></span>
<span>{{item.unstable_format}}</span>
<span class="huibiao" style="background-color: #AAE252;"></span>
<span>{{item.online_format}}</span>
<span class="change-count">变化:{{item.state_change_time}}次</span>
</div>
</el-row>
</div>
<WhiteSpace heightProps="20px" />
<el-pagination
class="page"
layout="prev, pager, next, sizes, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
:total="amount">
</el-pagination>
<WhiteSpace heightProps="12px" />
</div>
</div>
</template>
<script>
import XLSX from "xlsx";
import lodash from 'lodash';
import moment from 'moment';
import * as echarts from 'echarts';
export default {
components: {},
data() {
return {
showKey: 'eid',
currentPage: 1,
pageSize: 10,
tableData: [],
types: [{
name: '在线',
value: 'online',
color: '#73D500',
}, {
name: '不稳定',
value: 'unstable',
color: '#FFDD00',
}, {
name: '离线',
value: 'offline',
color: '#DF001F',
}, {
name: '未知',
value: 'unknown',
color: '#DF001F',
}, {
name: '中控电量低',
value: 'noPower',
color: '#FFDD00',
}],
option: {
tooltip: {
position: function(point, params, dom, rect, size) {
if (params.value[7]) {
return ['10%', '-240%'];
} else {
return ['10%', '10%'];
}
},
formatter: function(params) {
let str = `<p>
<span>${params.marker} ${params.name}</span>
<p>UCT ${params.value[4]} 至 ${params.value[5]}</p>
<span>时长:${params.value[6]}</span>
</p>`;
return str;
}
},
title: {
text: '',
left: 'start',
// right: 'center',
top: 'center',
bottom: 'center',
},
grid: {
left: 0,
right: 0,
},
xAxis: {
type: 'time',
scale: true,
// position: 'top',
// type: 'time',
// splitNumber: 10,
// boundaryGap: ["0", "100%"],
// splitLine: {
// show: false
// },
// axisLine: {
// show: true
// }
},
yAxis: {
data: null,
type: 'category',
// offset: 100,
// axisLine: {
// show: false
// },
// splitLine: {
// show: true,
// },
},
series: [{
type: 'custom',
itemStyle: {
opacity: 0.8
},
encode: {
x: [1, 2],
y: 0
},
data: null,
renderItem: function(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] * 0.3;
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,
width: params.coordSys.width,
height: params.coordSys.height
});
return rectShape && {
type: 'rect',
shape: rectShape,
style: api.style()
};
}
}]
},
};
},
props: {
dataSource: {
type: Array,
default () {
return []
}
},
amount: {
type: Number,
default: 100,
},
controlPrams: {
type: Object,
default: () => {
return {};
},
},
},
computed: {},
watch: {
dataSource(newval) {
this.dataFormat(newval)
},
},
filters: {
timeformat(val) {
if (!val) return '';
return moment(val).local().format('YYYY.MM.DD HH:mm:ss');
},
},
methods: {
exportExcel() {
function sheet2blob(sheet, sheetName) {
sheetName = sheetName || 'sheet1';
var workbook = {
SheetNames: [sheetName],
Sheets: {}
};
workbook.Sheets[sheetName] = sheet;
var wopts = {
bookType: 'xlsx',
bookSST: false,
type: 'binary'
};
var wbout = XLSX.write(workbook, wopts);
var blob = new Blob([s2ab(wbout)], {
type: "application/octet-stream"
});
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
return blob;
}
function openDownloadDialog(url, saveName) {
if (typeof url == 'object' && url instanceof Blob) {
url = URL.createObjectURL(url);
}
var aLink = document.createElement('a');
aLink.href = url;
aLink.download = saveName || '';
var event;
if (window.MouseEvent) event = new MouseEvent('click');
else {
event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
}
aLink.dispatchEvent(event);
}
const aoa = [];
const sheetTitle = [
this.showKey.toUpperCase(),
'离线',
'不稳定',
'在线',
'变化',
'开始时间',
'结束时间',
];
aoa.push(sheetTitle);
let selectTime = this.controlPrams.selectTime;
let begin = 'UTC ' + moment(selectTime[0]).utc().format('YYYY.MM.DD HH:mm:ss');
let end = 'UTC ' + moment(selectTime[1]).utc().format('YYYY.MM.DD HH:mm:ss');
for (let i in this.tableData) {
let item = []
item.push(this.tableData[i][this.showKey]);
item.push(this.tableData[i].offline_format);
item.push(this.tableData[i].unstable_format);
item.push(this.tableData[i].online_format);
item.push(this.tableData[i].state_change_time);
item.push(begin);
item.push(end);
aoa.push(item);
}
const sheet = XLSX.utils.aoa_to_sheet(aoa);
sheet["!cols"] = [{
wch: 26,
}, {
wch: 26,
}, {
wch: 26,
}, {
wch: 26,
}, {
wch: 9,
}, {
wch: 23,
}, {
wch: 23,
}];
openDownloadDialog(sheet2blob(sheet), '设备状态变更情况.xlsx');
},
handleSizeChange(val) {
this.pageSize = val;
this.$emit('record-change', {
pageSize: this.pageSize,
currentPage: this.currentPage,
showKey: this.showKey,
})
},
handleCurrentChange(val) {
this.currentPage = val;
this.$emit('record-change', {
pageSize: this.pageSize,
currentPage: this.currentPage,
showKey: this.showKey,
})
},
changeRadio(val) {
this.showKey = val;
this.$emit('record-change', {
pageSize: this.pageSize,
currentPage: this.currentPage,
showKey: this.showKey,
})
},
formatPercentageNew(num, total) {
let rate = (num / total * 100).toFixed(2) + '%';
return rate;
},
formatTime2DurationNew(count) {
let time = '';
let second = count % 60;
let minute = parseInt(parseInt(count) / 60) % 60;
time = second + "秒";
if (minute >= 1) {
time = minute + "分" + second + "秒";
}
let hour = parseInt(parseInt(count / 60) / 60) % 24;
if (hour >= 1) {
time = hour + "小时" + minute + "分" + second + "秒";
}
var day = parseInt(parseInt(parseInt(count / 60) / 60) / 24);
if (day >= 1) {
time = day + "天" + hour + "小时" + minute + "分" + second + "秒";
}
return time;
},
dataFormat(data) {
if (data && data.length > 0) {
let len = data.length;
data.map((item, index) => {
let {
offline_duration = 0,
online_duration = 0,
unstable_duration = 0
} = item;
let total_duration = offline_duration + online_duration + unstable_duration;
let offline_duration_format = this.formatTime2DurationNew(offline_duration);
let online_duration_format = this.formatTime2DurationNew(online_duration);
let unstable_duration_format = this.formatTime2DurationNew(unstable_duration);
let offline_duration_rate = this.formatPercentageNew(offline_duration, total_duration);
let online_duration_rate = this.formatPercentageNew(online_duration, total_duration);
let unstable_duration_rate = this.formatPercentageNew(unstable_duration, total_duration);
item.offline_format = `离线(${offline_duration_format},${offline_duration_rate})`;
item.online_format = `在线(${online_duration_format},${online_duration_rate})`;
item.unstable_format = `不稳定(${unstable_duration_format},${unstable_duration_rate})`;
setTimeout(() => {
let flag = (len - index) <= 3;
this.initChart(item, index, flag);
}, 500)
})
this.tableData = data;
}
},
initChart(item, index, flag) {
let state = item.state || [];
let data = [];
for (let i = 0; i < state.length; i++) {
let tmp = state[i];
let typeItem = lodash.find(this.types, {
'value': tmp.state
})
let begin = moment(tmp.start_time).valueOf();
let end = moment(tmp.end_time).valueOf();
let diff = (end - begin) / 1000;
let duration = this.formatTime2DurationNew(diff) || '0s';
let begin_format = moment(tmp.start_time).utc().format('YYYY.MM.DD HH:mm:ss');
let end_format = moment(tmp.end_time).utc().format('YYYY.MM.DD HH:mm:ss');
data.push({
name: tmp[this.showKey],
value: [i, begin, end, tmp.state_duration, begin_format, end_format, duration, flag],
itemStyle: {
normal: {
color: typeItem.color
}
}
});
}
// 修改option
this.option.series[0].data = data;
this.option.yAxis.data = [item[this.showKey]];
this.option.title.text = item[this.showKey];
// 获取图表id
let chartDom = document.getElementById(`timeline_${index}`);
chartDom.style.height = 26 + 'px';
chartDom.style.width = '800px';
// 初始化图表
let myChart = echarts.init(chartDom);
myChart.resize();
myChart.clear();
myChart.setOption(this.option);
},
},
created() {},
mounted() {}
}
</script>
<style lang="scss" scoped>
.change_record {
width: 100%;
box-sizing: border-box;
border-radius: 4px;
box-shadow: 0px 8px 16px rgba(55, 70, 95, 0.070171);
background-color: #FFFFFF;
.title-box {
padding: 0 36px;
width: 100%;
height: 66px;
background-color: #FAFBFB;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
.title {
font-weight: 500;
font-size: 12px;
color: #828D99;
}
.show-style {
margin-right: 12px;
font-weight: 600;
font-size: 12px;
color: #828D99;
}
.down-excel {
padding: 6px 10px;
font-weight: 400;
font-size: 14px;
color: #304156;
}
}
.change-box {
width: 100%;
.change-content {
width: 100%;
overflow-x: scroll;
// overflow-y: hidden;
overflow-y: visible;
border: 1px solid #F0F1F2;
.change-item {
display: flex;
flex-direction: row;
}
.change-item:first-child {
.timeline,
.explain {
border-top: 0px;
}
}
.timeline {
min-width: 800px;
width: 800px;
height: 26px;
// background: green;
border-top: 1px solid #F0F1F2;
border-right: 1px solid #F0F1F2;
box-sizing: border-box;
}
.explain {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
font-weight: 400;
font-size: 12px;
color: #304156;
white-space: nowrap;
border-top: 1px solid #F0F1F2;
box-sizing: border-box;
.huibiao {
margin: 0 4px 0 6px;
width: 6px;
height: 12px;
}
.change-count {
margin: 0 12px 0 8px;
}
}
}
.page {
text-align: center;
}
}
}
</style>