//父组件
<div class="lifecycle-content" >
<Ganttchart />
</div>
.lifecycle-content {
position: relative;
width:100%;
height: calc(100% - 40px);
}
.lifecycle-content > div {
position: absolute;
top: 0;
bottom: 0;
}
//子组件
<template>
<div ref="chartPanel" class="chart-container" />
</template>
<script setup>
import { onMounted, ref } from 'vue'
import * as echarts from 'echarts';
import { getLifecycle } from "/@/api/lifecycle";
const chartPanel = ref(null)
const getDate = () => {
getLifecycle({ page: 1, row: 10, sceneId: '27113A942A7E4ABABBA16D4EDAAB0EAE' }).then((res) => {
let data = res.data[0].productDTO.organismStatusList
// 提取所需的字段lifeCycleTaskLists
let totalLength = 0;
for (let i = 0; i < data.length; i++) {
totalLength += data[i].lifeCycleTaskLists.length;
}
console.log(totalLength);
let currentTextValue = totalLength;
let result = data.map(item => {
return item.lifeCycleTaskLists.map(task => {
let text = currentTextValue - 1;
currentTextValue--;
return [
text,
task.actualStartTime,
task.planEndedTime,
task.actualEndedTime,
task.taskName
];
});
});
result = result.flat();
rowData.flight.data = result;
// 提取所需的字段
let resulttitle = data.map(item => {
return item.lifeCycleTaskLists.map(task => {
return [
task.organismName,
task.taskName
];
});
});
resulttitle = resulttitle.flat();
rowData.parkingApron.data = resulttitle;
console.log('------', rowData);
initChart();
}).catch((err) => {
console.log(err);
});
}
const HEIGHT_RATIO = 0.5;
const DIM_CATEGORY_INDEX = 0;
const DIM_TIME_ARRIVAL = 1;
const DIM_TIME_DEPARTURE = 2;
const ACTUAL_TIME_ARRIVAL = 3;
const _cartesianXBounds = [];
const _cartesianYBounds = [];
const rowData = {
flight: {
data: [
// [11, '2024-5-1', '2024-5-16', '2024-5-17', '', '播种'],
// [10, '2024-1-1', '2024-1-8', '2024-1-9', '播种'],
// [9, '2024-1-1', '2024-1-5', '2024-1-9', '播种'],
// [8, '2024-1-1', '2024-1-4', '2024-1-9', '播种'],
// [7, '2024-1-1', '2024-1-3', '2024-1-9', '播种'],
// [6, '2024-1-1', '2024-1-31', '2024-1-24', '铺地膜'],
// [5, '2024-2-1', '2024-4-30', '2024-4-30', '铺滴灌带'],
// [4, '2024-5-1', '2024-5-31', '2024-6-30', '起垄'],
// [3, '2024-6-1', '2024-6-30', '2024-7-15', '施肥'],
// [2, '2024-7-1', '2024-7-15', '2024-7-23', 'Go live'],
// [1, '2024-7-16', '2024-7-31', '2024-8-31', 'Training'],
// [0, '2024-8-1', '2024-8-31', '', 'Payment'],
],
dimensions: ['Parking Apron Index', '开始时间', '结束时间', '实际结束时间'],
},
parkingApron: {
dimensions: ['Name', 'Type'],
data: [
// ['发芽期', '播种'],
// ['发芽期', '铺地膜'],
// ['幼苗期', '铺滴灌带'],
// ['幼苗期', '起垄'],
// ['结果期', '施肥'],
// ['Task06', 'Go live'],
// ['Task07', 'Training'],
// ['Task08', 'Payment'],
// ['Task9', 'Payment'],
// ['Task10', 'Payment'],
// ['Task11', 'Payment'],
// ['Task12', 'Payment'],
],
},
}
const initChart = () => {
const myChart = echarts.init(chartPanel.value, null, { width: 'auto', height: 'auto' });
function clipRectByRect(params, rect) {
return echarts.graphic.clipRectByRect(rect, {
x: params.coordSys.x,
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height,
})
}
function renderGanttItem(params, api) {
const categoryIndex = api.value(DIM_CATEGORY_INDEX);
const timeStart = api.coord([api.value(DIM_TIME_ARRIVAL), categoryIndex]);
const timeEnd = api.coord([api.value(DIM_TIME_DEPARTURE), categoryIndex]);
const actualTimeArrival = api.coord([api.value(ACTUAL_TIME_ARRIVAL), categoryIndex]);
const { coordSys } = params;
_cartesianXBounds[0] = coordSys.x;
_cartesianXBounds[1] = coordSys.x + coordSys.width;
_cartesianYBounds[0] = coordSys.y;
_cartesianYBounds[1] = coordSys.y + coordSys.height;
const barLength1 = timeEnd[0] - timeStart[0];
const barLength2 = timeEnd[0] - actualTimeArrival[0];
const barLength3 = actualTimeArrival[0] - timeEnd[0];
const barHeight = api.size([0, 1])[1] * HEIGHT_RATIO;
const rectNormal = clipRectByRect(params, {
x: timeStart[0],
y: timeStart[1] - barHeight,
width: barLength1,
height: barHeight,
});
const rectBefore = clipRectByRect(params, {
x: actualTimeArrival[0],
y: actualTimeArrival[1] - barHeight,
width: barLength2,
height: barHeight,
});
const rectAfter = clipRectByRect(params, {
x: timeEnd[0],
y: timeEnd[1] - barHeight,
width: barLength3,
height: barHeight,
});
return {
type: 'group',
children: [
{
type: 'rect',
ignore: !rectNormal,
shape: rectNormal,
style: api.style({ fill: '#fac758' }), // 黄色区域-开始时间到结束时间
},
{
type: 'rect',
ignore: !rectBefore,
shape: rectBefore,
style: api.style({ fill: '#3ba272' }), // 绿色区域 - 提前结束
},
{
type: 'rect',
ignore: !rectAfter,
shape: rectAfter,
style: api.style({ fill: '#ee6666' }), // 红色区域-延迟
},
],
};
}
function renderAxisLabelItem(params, api) {
const y = api.coord([0, api.value(0)])[1];
if (y < params.coordSys + 5) {
return;
}
// eslint-disable-next-line consistent-return
return {
type: 'group',
position: [0, y],
children: [
{
type: 'path', // task区域
shape: {
d: 'M0,0 L0,-20 L30,-20 C42,-20 38,-1 50,-1 L70,-1 L70,0 Z',
x: 0,
y: -20,
width: 80,
height: 20,
layout: 'cover',
},
style: {
fill: '#368c6c',
},
},
{
type: 'text', // task字体
style: {
x: 24,
y: -3,
size: 1,
text: api.value(1),
textVerticalAlign: 'bottom',
textAlign: 'center',
textFill: '#fff',
},
},
{
type: 'text', // 项目名称
style: {
x: 50,
y: -2,
textVerticalAlign: 'bottom',
textAlign: 'left',
text: api.value(2),
textFill: '#fff', //--------------------------字体
},
},
],
};
}
function makeOption() {
return {
tooltip: {
trigger: 'item',
show: true,
axisPointer: {
type: 'line',
},
formatter(params) {
if (params && params.length > 0) {
return '';
}
const { data, value } = params;
const end = value[2] ? new Date(value[2]).getTime() : 0;
const actual = value[3] ? new Date(value[3]).getTime() : 0;
let color = '#ddb30b';
if (actual - end > 0) {
color = '#ee6666';
} else if (actual - end < 0) {
color = '#3ba272';
}
const marker = `<span style="display:inline-block;margin-right:4px;border-radius:10px;width:10px;height:10px;background-color:${color};"></span>`;
return `${data[4]} <br/>${marker}开始时间:${value[1]}<br/>${marker}结束时间:${value[2]}<br/>${marker}实际结束时间:${value[3]}`;
},
},
animation: false,
grid: {
show: true,
top: 50,
bottom: 0,
left: 110,
right: 20,
borderWidth: 0,
},
dataZoom: [
{
type: 'slider',
xAxisIndex: 0,
filterMode: 'weakFilter',
height: 20,
bottom: 0,
start: 7,
end: 0,
handleIcon:
'path://M10.7,11.9H9.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
handleSize: '70%',
showDetail: false,
},
{
type: 'inside',
id: 'insideX',
xAxisIndex: 0,
filterMode: 'weakFilter',
start: 0,
end: 100,
zoomOnMouseWheel: false,
moveOnMouseMove: true,
},
{
type: 'slider',
yAxisIndex: 0,
zoomLock: true,
width: 10,
right: 10,
top: 40,
bottom: 10,
start: 70,
end: 100,
handleSize: 0,
showDetail: false,
},
{
type: 'inside',
id: 'insideY',
yAxisIndex: 0,
start: 95,
end: 100,
zoomOnMouseWheel: false,
moveOnMouseMove: true,
moveOnMouseWheel: true,
},
],
xAxis: {
type: 'time',
position: 'top',
splitLine: {
alignWithLabel: true,
show: true,
lineStyle: {
color: ['#E9EDFF'],
},
},
axisLine: {
show: false,
},
axisTick: {
show: false,
lineStyle: {
color: '#929ABA',
},
},
axisLabel: {
color: '#FFFFFF',
inside: false,
align: 'center',
formatter: (param) => {
const formatDate = new Date(param);
return `${formatDate.getFullYear()}-${formatDate.getMonth() + 1
}-${formatDate.getDate()}`;
},
},
axisPointer: {
show: true,
label: {
backgroundColor: '#004f53',
margin: -20,
},
lineStyle: {
color: '#9fbfcd',
type: 'solid',
width: 1.5,
},
},
},
yAxis: {
axisTick: { show: false },
splitLine: { show: false },
axisLine: { show: false },
axisLabel: { show: false },
min: 0,
axisPointer: {
show: false,
},
max: rowData.parkingApron.data.length,
},
legend: {
show: true,
data: [ //---------------------------------
{
name: '提前完成',
itemStyle: { color: '#3ba272' },
textStyle: { color: '000' },
},
{
name: '逾期完成',
itemStyle: { color: '#ee6666' },
textStyle: { color: '000' },
},
{
name: '按时完成',
itemStyle: { color: '#fac758' },
textStyle: { color: '000' },
},
],
},
series: [
{
id: 'flightData1',
type: 'custom',
name: '提前完成',
renderItem: renderGanttItem,
dimensions: rowData.flight.dimensions,
encode: {
x: [DIM_TIME_ARRIVAL, DIM_TIME_DEPARTURE, ACTUAL_TIME_ARRIVAL],
y: DIM_CATEGORY_INDEX,
tooltip: [DIM_TIME_ARRIVAL, DIM_TIME_DEPARTURE, ACTUAL_TIME_ARRIVAL],
},
data: rowData.flight.data,
},
{
id: 'flightData2',
type: 'custom',
name: '逾期完成',
renderItem: renderGanttItem,
dimensions: rowData.flight.dimensions,
encode: {
x: [DIM_TIME_ARRIVAL, DIM_TIME_DEPARTURE, ACTUAL_TIME_ARRIVAL],
y: DIM_CATEGORY_INDEX,
tooltip: [DIM_TIME_ARRIVAL, DIM_TIME_DEPARTURE, ACTUAL_TIME_ARRIVAL],
},
data: rowData.flight.data,
},
{
id: 'flightData3',
type: 'custom',
name: '按时完成',
renderItem: renderGanttItem,
dimensions: rowData.flight.dimensions,
encode: {
x: [DIM_TIME_ARRIVAL, DIM_TIME_DEPARTURE, ACTUAL_TIME_ARRIVAL],
y: DIM_CATEGORY_INDEX,
tooltip: [DIM_TIME_ARRIVAL, DIM_TIME_DEPARTURE, ACTUAL_TIME_ARRIVAL],
},
data: rowData.flight.data,
},
{
type: 'custom',
renderItem: renderAxisLabelItem,
dimensions: rowData.parkingApron.dimensions,
encode: {
x: -1,
y: 0,
},
data: rowData.parkingApron.data.map((item, index) => {
return [rowData.parkingApron.data.length - 1 - index].concat(item);
}),
},
],
};
}
const option = makeOption()
myChart.setOption(option);
window.addEventListener('resize', () => {
myChart.resize();
});
};
onMounted(() => {
getDate()
});
</script>
<style scoped>
.chart-container {
width:100%;
overflow-y: scroll;
scrollbar-width: none;
}
</style>