UNPKG

vxe-gantt

Version:
2,204 lines • 109 kB
import { h, ref, reactive, nextTick, inject, onBeforeUnmount, provide, computed, onMounted, onUnmounted } from 'vue';
import { defineVxeComponent } from '../../ui/src/comp';
import { setScrollTop, setScrollLeft, removeClass, addClass, hasClass } from '../../ui/src/dom';
import { VxeUI } from '@vxe-ui/core';
import { getRefElem, getTaskBarLeft, getTaskBarWidth, hasMilestoneTask, getTaskType, hasSubviewTask } from './util';
import { VxeGanttDependencyType } from './static';
import XEUtils from 'xe-utils';
import GanttViewHeaderComponent from './gantt-header';
import GanttViewBodyComponent from './gantt-body';
import GanttViewFooterComponent from './gantt-footer';
const { globalEvents } = VxeUI;
const { FinishToStart, StartToFinish, StartToStart, FinishToFinish } = VxeGanttDependencyType;
const sourceType = 'gantt';
const secondMs = 1000;
const minuteMs = 1000 * 60;
const hourMs = 1000 * 60 * 60;
const dayMs = hourMs * 24;
function getTaskLinkKey(from, to, type) {
    return from + '_' + to + '_' + (type || 0);
}
function createInternalData() {
    return {
        xeTable: null,
        visibleColumn: [],
        startMaps: {},
        endMaps: {},
        chartMaps: {},
        todayDateMaps: {},
        elemStore: {},
        // 存放横向 X 虚拟滚动相关的信息
        scrollXStore: {
            preloadSize: 0,
            offsetSize: 0,
            visibleSize: 0,
            visibleStartIndex: 0,
            visibleEndIndex: 0,
            startIndex: 0,
            endIndex: 0
        },
        // 最后滚动位置
        lastScrollTop: 0,
        lastScrollLeft: 0
    };
}
function createReactData() {
    return {
        // 是否启用了横向 X 可视渲染方式加载
        scrollXLoad: false,
        // 是否启用了纵向 Y 可视渲染方式加载
        scrollYLoad: false,
        // 是否存在纵向滚动条
        overflowY: true,
        // 是否存在横向滚动条
        overflowX: true,
        // 纵向滚动条的宽度
        scrollbarWidth: 0,
        // 横向滚动条的高度
        scrollbarHeight: 0,
        // 最后滚动时间戳
        lastScrollTime: 0,
        lazScrollLoading: false,
        scrollVMLoading: false,
        scrollYHeight: 0,
        scrollYTop: 0,
        isScrollYBig: false,
        scrollXLeft: 0,
        scrollXWidth: 0,
        isScrollXBig: false,
        minViewDate: null,
        maxViewDate: null,
        tableData: [],
        tableColumn: [],
        headerGroups: [],
        viewCellWidth: 40
    };
}
const maxYHeight = 5e6;
// const maxXWidth = 5e6
export default defineVxeComponent({
    name: 'VxeGanttView',
    setup(props, context) {
        const xID = XEUtils.uniqueId();
        const $xeGantt = inject('$xeGantt', {});
        const ganttProps = $xeGantt.props;
        const ganttReactData = $xeGantt.reactData;
        const ganttInternalData = $xeGantt.internalData;
        const { computeDateFormat, computeTaskBarOpts, computeTaskViewOpts, computeStartField, computeEndField, computeTypeField, computeScrollbarOpts, computeScrollbarXToTop, computeScrollbarYToLeft, computeScaleUnit, computeWeekScale, computeMinScale, computeTaskNowLineOpts, computeScaleStep } = $xeGantt.getComputeMaps();
        const refElem = ref();
        const refScrollXVirtualElem = ref();
        const refScrollYVirtualElem = ref();
        const refScrollXHandleElem = ref();
        const refScrollXLeftCornerElem = ref();
        const refScrollXRightCornerElem = ref();
        const refScrollYHandleElem = ref();
        const refScrollYTopCornerElem = ref();
        const refScrollXWrapperElem = ref();
        const refScrollYWrapperElem = ref();
        const refScrollYBottomCornerElem = ref();
        const refScrollXSpaceElem = ref();
        const refScrollYSpaceElem = ref();
        const refColInfoElem = ref();
        const reactData = reactive(createReactData());
        const internalData = createInternalData();
        const refMaps = {
            refElem,
            refScrollXHandleElem,
            refScrollYHandleElem
        };
        const computeScaleDateList = computed(() => {
            const { minViewDate, maxViewDate } = reactData;
            const taskViewOpts = computeTaskViewOpts.value;
            const minScale = computeMinScale.value;
            const scaleStep = computeScaleStep.value;
            const { gridding } = taskViewOpts;
            const dateList = [];
            if (!minScale || !minViewDate || !maxViewDate) {
                return dateList;
            }
            const { type, startDay } = minScale;
            let leftSize = -(ganttReactData.currLeftSpacing + XEUtils.toNumber(gridding ? gridding.leftSpacing || 0 : 0));
            let rightSize = ganttReactData.currRightSpacing + XEUtils.toNumber(gridding ? gridding.rightSpacing || 0 : 0);
            if (scaleStep > 0) {
                leftSize *= scaleStep;
                rightSize *= scaleStep;
            }
            switch (type) {
                case 'year': {
                    let currDate = XEUtils.getWhatYear(minViewDate, leftSize, 'first');
                    const endDate = XEUtils.getWhatYear(maxViewDate, rightSize, 'first');
                    while (currDate <= endDate) {
                        const itemDate = currDate;
                        dateList.push(itemDate);
                        currDate = XEUtils.getWhatYear(currDate, scaleStep);
                    }
                    break;
                }
                case 'quarter': {
                    let currDate = XEUtils.getWhatQuarter(minViewDate, leftSize, 'first');
                    const endDate = XEUtils.getWhatQuarter(maxViewDate, rightSize, 'first');
                    while (currDate <= endDate) {
                        const itemDate = currDate;
                        dateList.push(itemDate);
                        currDate = XEUtils.getWhatQuarter(currDate, scaleStep);
                    }
                    break;
                }
                case 'month': {
                    let currDate = XEUtils.getWhatMonth(minViewDate, leftSize, 'first');
                    const endDate = XEUtils.getWhatMonth(maxViewDate, rightSize, 'first');
                    while (currDate <= endDate) {
                        const itemDate = currDate;
                        dateList.push(itemDate);
                        currDate = XEUtils.getWhatMonth(currDate, scaleStep);
                    }
                    break;
                }
                case 'week': {
                    let currDate = XEUtils.getWhatWeek(minViewDate, leftSize, startDay, startDay);
                    const endDate = XEUtils.getWhatWeek(maxViewDate, rightSize, startDay, startDay);
                    while (currDate <= endDate) {
                        const itemDate = currDate;
                        dateList.push(itemDate);
                        currDate = XEUtils.getWhatWeek(currDate, scaleStep);
                    }
                    break;
                }
                case 'day':
                case 'date': {
                    let currDate = XEUtils.getWhatDay(minViewDate, leftSize, 'first');
                    const endDate = XEUtils.getWhatDay(maxViewDate, rightSize, 'first');
                    while (currDate <= endDate) {
                        const itemDate = currDate;
                        dateList.push(itemDate);
                        currDate = XEUtils.getWhatDay(currDate, scaleStep);
                    }
                    break;
                }
                case 'hour': {
                    let currDate = XEUtils.getWhatHours(minViewDate, leftSize, 'first');
                    const endDate = XEUtils.getWhatHours(maxViewDate, rightSize, 'first');
                    while (currDate <= endDate) {
                        const itemDate = currDate;
                        dateList.push(itemDate);
                        currDate = XEUtils.getWhatHours(currDate, scaleStep);
                    }
                    break;
                }
                case 'minute': {
                    let currDate = XEUtils.getWhatMinutes(minViewDate, leftSize, 'first');
                    const endDate = XEUtils.getWhatMinutes(maxViewDate, rightSize, 'first');
                    while (currDate <= endDate) {
                        const itemDate = currDate;
                        dateList.push(itemDate);
                        currDate = XEUtils.getWhatMinutes(currDate, scaleStep);
                    }
                    break;
                }
                case 'second': {
                    let currDate = XEUtils.getWhatSeconds(minViewDate, leftSize, 'first');
                    const endDate = XEUtils.getWhatSeconds(maxViewDate, rightSize, 'first');
                    while (currDate <= endDate) {
                        const itemDate = currDate;
                        dateList.push(itemDate);
                        currDate = XEUtils.getWhatSeconds(currDate, scaleStep);
                    }
                    break;
                }
            }
            return dateList;
        });
        const computeNowLineLeft = computed(() => {
            const { minViewDate, maxViewDate, viewCellWidth } = reactData;
            const { visibleColumn, todayDateMaps } = internalData;
            const minScale = computeMinScale.value;
            const taskViewOpts = computeTaskViewOpts.value;
            const taskNowLineOpts = computeTaskNowLineOpts.value;
            const { showNowLine } = taskViewOpts;
            const { mode } = taskNowLineOpts;
            const { nowTime } = ganttReactData;
            // 此刻线
            let nlLeft = 0;
            if (showNowLine && minScale && minViewDate && maxViewDate && nowTime >= minViewDate.getTime() && nowTime <= maxViewDate.getTime()) {
                const todayValue = todayDateMaps[minScale.type];
                const todayDate = XEUtils.toStringDate(todayValue);
                let currCol = null;
                let nextCol = null;
                for (let i = 0; i < visibleColumn.length; i++) {
                    const column = visibleColumn[i];
                    const { startDateObj, endDateObj } = column;
                    if (startDateObj.date <= todayDate && endDateObj.date >= todayDate) {
                        currCol = column;
                        nlLeft = i * viewCellWidth;
                        nextCol = visibleColumn[i + 1];
                        break;
                    }
                }
                if (mode === 'progress') {
                    if (currCol && nextCol) {
                        const currTime = currCol.dateObj.date.getTime();
                        const offsetTime = nowTime - currTime;
                        const nowProgress = Math.max(0, Math.min(1, offsetTime / (nextCol.dateObj.date.getTime() - currTime)));
                        nlLeft += nowProgress * viewCellWidth;
                    }
                }
                else if (mode === 'center') {
                    nlLeft += viewCellWidth / 2;
                }
                else if (mode === 'end') {
                    nlLeft += viewCellWidth - 1;
                }
            }
            return nlLeft;
        });
        const computeMaps = {
            computeScaleDateList,
            computeNowLineLeft
        };
        const $xeGanttView = {
            xID,
            props,
            context,
            reactData,
            internalData,
            getRefMaps: () => refMaps,
            getComputeMaps: () => computeMaps
        };
        const parseStringDate = (dateValue) => {
            const dateFormat = computeDateFormat.value;
            return XEUtils.toStringDate(dateValue, dateFormat);
        };
        const updateTodayData = () => {
            const { taskScaleList } = ganttReactData;
            const minScale = computeMinScale.value;
            if (minScale) {
                const weekScale = taskScaleList.find(item => item.type === 'week');
                const isMinWeek = minScale.type === 'week';
                const itemDate = new Date();
                let [yyyy, M, MM, dd, HH, mm, ss] = XEUtils.toDateString(itemDate, 'yyyy-M-MM-dd-HH-mm-ss').split('-');
                const e = itemDate.getDay();
                const E = e + 1;
                const q = Math.ceil((itemDate.getMonth() + 1) / 3);
                const W = `${XEUtils.getYearWeek(itemDate, weekScale ? weekScale.startDay : undefined)}`;
                if (isMinWeek && checkWeekOfsetYear(W, M)) {
                    yyyy = `${Number(yyyy) + 1}`;
                    M = '1';
                    MM = '0' + M;
                }
                ganttReactData.nowTime = itemDate.getTime();
                internalData.todayDateMaps = {
                    year: yyyy,
                    quarter: `${yyyy}_q${q}`,
                    month: `${yyyy}_${MM}`,
                    week: `${yyyy}_W${W}`,
                    day: `${yyyy}_${MM}_${dd}_E${E}`,
                    date: `${yyyy}_${MM}_${dd}`,
                    hour: `${yyyy}_${MM}_${dd}_${HH}`,
                    minute: `${yyyy}_${MM}_${dd}_${HH}_${mm}`,
                    second: `${yyyy}_${MM}_${dd}_${HH}_${mm}_${ss}`
                };
            }
        };
        const handleColumnHeader = () => {
            const { taskScaleList } = ganttReactData;
            const scaleUnit = computeScaleUnit.value;
            const minScale = computeMinScale.value;
            const weekScale = computeWeekScale.value;
            const scaleDateList = computeScaleDateList.value;
            const scaleStep = computeScaleStep.value;
            const weekStartDay = weekScale ? weekScale.startDay : undefined;
            const fullCols = [];
            const groupCols = [];
            if (minScale && scaleUnit && scaleDateList.length) {
                const renderListMaps = {
                    year: [],
                    quarter: [],
                    month: [],
                    week: [],
                    day: [],
                    date: [],
                    hour: [],
                    minute: [],
                    second: []
                };
                const tempTypeMaps = {
                    year: {},
                    quarter: {},
                    month: {},
                    week: {},
                    day: {},
                    date: {},
                    hour: {},
                    minute: {},
                    second: {}
                };
                const isMinWeek = minScale.type === 'week';
                const handleData = (type, colMaps, minCol) => {
                    if (minScale.type === type) {
                        return;
                    }
                    const currCol = colMaps[type];
                    const currKey = `${currCol.field}`;
                    let currGpCol = tempTypeMaps[type][currKey];
                    if (!currGpCol) {
                        currGpCol = currCol;
                        tempTypeMaps[type][currKey] = currGpCol;
                        renderListMaps[type].push(currGpCol);
                    }
                    if (currGpCol) {
                        if (!currGpCol.children) {
                            currGpCol.children = [];
                        }
                        currGpCol.children.push(minCol);
                    }
                };
                const createDateObj = (date) => {
                    let [yy, yyyy, M, MM, d, dd, H, HH, m, mm, s, ss] = XEUtils.toDateString(date, 'yy-yyyy-M-MM-d-dd-H-HH-m-mm-s-ss').split('-');
                    const e = date.getDay();
                    const E = e + 1;
                    const q = Math.ceil((date.getMonth() + 1) / 3);
                    const W = `${XEUtils.getYearWeek(date, weekScale ? weekScale.startDay : undefined)}`;
                    const WW = XEUtils.padStart(W, 2, '0');
                    if (isMinWeek && checkWeekOfsetYear(W, M)) {
                        yyyy = `${Number(yyyy) + 1}`;
                        M = '1';
                        MM = '0' + M;
                    }
                    return { date, yy, yyyy, M, MM, d, dd, H, HH, m, mm, s, ss, q, W, WW, E, e };
                };
                for (let i = 0; i < scaleDateList.length; i++) {
                    const itemDate = scaleDateList[i];
                    const dateObj = createDateObj(itemDate);
                    const { yyyy, MM, dd, HH, mm, ss, q, W, E } = dateObj;
                    const startDateObj = dateObj;
                    const colMaps = {
                        year: {
                            field: yyyy,
                            title: yyyy,
                            dateObj,
                            startDateObj,
                            endDateObj: scaleStep > 1 ? createDateObj(XEUtils.getWhatYear(itemDate, scaleStep - 1)) : startDateObj
                        },
                        quarter: {
                            field: `${yyyy}_q${q}`,
                            title: `${q}`,
                            dateObj,
                            startDateObj,
                            endDateObj: scaleStep > 1 ? createDateObj(XEUtils.getWhatQuarter(itemDate, scaleStep - 1)) : startDateObj
                        },
                        month: {
                            field: `${yyyy}_${MM}`,
                            title: MM,
                            dateObj,
                            startDateObj,
                            endDateObj: scaleStep > 1 ? createDateObj(XEUtils.getWhatMonth(itemDate, scaleStep - 1)) : startDateObj
                        },
                        week: {
                            field: `${yyyy}_W${W}`,
                            title: `${W}`,
                            dateObj,
                            startDateObj,
                            endDateObj: scaleStep > 1 ? createDateObj(XEUtils.getWhatWeek(itemDate, scaleStep - 1, weekStartDay, weekStartDay)) : startDateObj
                        },
                        day: {
                            field: `${yyyy}_${MM}_${dd}_E${E}`,
                            title: `${E}`,
                            dateObj,
                            startDateObj,
                            endDateObj: scaleStep > 1 ? createDateObj(XEUtils.getWhatDay(itemDate, scaleStep - 1)) : startDateObj
                        },
                        date: {
                            field: `${yyyy}_${MM}_${dd}`,
                            title: dd,
                            dateObj,
                            startDateObj,
                            endDateObj: scaleStep > 1 ? createDateObj(XEUtils.getWhatDay(itemDate, scaleStep - 1)) : startDateObj
                        },
                        hour: {
                            field: `${yyyy}_${MM}_${dd}_${HH}`,
                            title: HH,
                            dateObj,
                            startDateObj,
                            endDateObj: scaleStep > 1 ? createDateObj(XEUtils.getWhatHours(itemDate, scaleStep - 1)) : startDateObj
                        },
                        minute: {
                            field: `${yyyy}_${MM}_${dd}_${HH}_${mm}`,
                            title: mm,
                            dateObj,
                            startDateObj,
                            endDateObj: scaleStep > 1 ? createDateObj(XEUtils.getWhatMinutes(itemDate, scaleStep - 1)) : startDateObj
                        },
                        second: {
                            field: `${yyyy}_${MM}_${dd}_${HH}_${mm}_${ss}`,
                            title: ss,
                            dateObj,
                            startDateObj,
                            endDateObj: scaleStep > 1 ? createDateObj(XEUtils.getWhatSeconds(itemDate, scaleStep - 1)) : startDateObj
                        }
                    };
                    const minCol = colMaps[minScale.type];
                    if (minScale.level < 19) {
                        handleData('year', colMaps, minCol);
                    }
                    if (minScale.level < 17) {
                        handleData('quarter', colMaps, minCol);
                    }
                    if (minScale.level < 15) {
                        handleData('month', colMaps, minCol);
                    }
                    if (minScale.level < 13) {
                        handleData('week', colMaps, minCol);
                    }
                    if (minScale.level < 11) {
                        handleData('day', colMaps, minCol);
                    }
                    if (minScale.level < 9) {
                        handleData('date', colMaps, minCol);
                    }
                    if (minScale.level < 7) {
                        handleData('hour', colMaps, minCol);
                    }
                    if (minScale.level < 5) {
                        handleData('minute', colMaps, minCol);
                    }
                    if (minScale.level < 3) {
                        handleData('second', colMaps, minCol);
                    }
                    fullCols.push(minCol);
                }
                taskScaleList.forEach(scaleItem => {
                    if (scaleItem.type === minScale.type) {
                        groupCols.push({
                            scaleItem,
                            columns: fullCols
                        });
                        return;
                    }
                    const list = renderListMaps[scaleItem.type] || [];
                    if (list) {
                        list.forEach(item => {
                            item.childCount = item.children ? item.children.length : 0;
                            item.children = undefined;
                        });
                    }
                    groupCols.push({
                        scaleItem,
                        columns: list
                    });
                });
            }
            return {
                fullCols,
                groupCols
            };
        };
        /**
         * 判断周的年份是否跨年
         */
        const checkWeekOfsetYear = (W, M) => {
            return `${W}` === '1' && `${M}` === '12';
        };
        /**
         * 周维度,由于年份和第几周是冲突的行为,所以需要特殊处理,判断是否跨年,例如
         * '2024-12-31' 'yyyy-MM-dd W' >> '2024-12-31 1'
         * '2025-01-01' 'yyyy-MM-dd W' >> '2025-01-01 1'
         */
        const parseWeekObj = (date, firstDay) => {
            const currDate = XEUtils.toStringDate(date);
            let yyyy = currDate.getFullYear();
            const month = currDate.getMonth();
            const weekNum = XEUtils.getYearWeek(currDate, firstDay);
            if (checkWeekOfsetYear(weekNum, month + 1)) {
                yyyy++;
            }
            return {
                yyyy,
                W: weekNum
            };
        };
        const createChartRender = (fullCols) => {
            const minScale = computeMinScale.value;
            const scaleStep = computeScaleStep.value;
            const scaleUnit = computeScaleUnit.value;
            const weekScale = computeWeekScale.value;
            const dateFormat = computeDateFormat.value;
            if (minScale) {
                const currRatio = scaleStep > 1 ? 1 / scaleStep : 0;
                switch (scaleUnit) {
                    case 'year': {
                        const showActualProgress = /M|d|H|m|s|S/.test(dateFormat);
                        const renderFormat = 'yyyy';
                        const indexMaps = {};
                        fullCols.forEach(({ dateObj }, i) => {
                            const currStr = XEUtils.toDateString(dateObj.date, 'yyyy');
                            indexMaps[currStr] = i;
                            if (currRatio) {
                                let currStep = scaleStep - 1;
                                while (currStep) {
                                    const currStr = XEUtils.toDateString(XEUtils.getWhatYear(dateObj.date, scaleStep - currStep), renderFormat);
                                    indexMaps[currStr] = i + ((scaleStep - currStep) * currRatio);
                                    currStep--;
                                }
                            }
                        });
                        return (startValue, endValue) => {
                            const startValDate = parseStringDate(startValue);
                            const endValDate = parseStringDate(endValue);
                            const startStr = XEUtils.toDateString(startValDate, renderFormat);
                            const startDate = showActualProgress ? startValDate : XEUtils.getWhatYear(startValDate, 0, 'first');
                            const startFirstDate = XEUtils.getWhatYear(startValDate, 0, 'first');
                            const endStr = XEUtils.toDateString(endValDate, renderFormat);
                            const endDate = showActualProgress ? endValDate : XEUtils.getWhatYear(endValDate, 0, 'last');
                            const endFirstDate = XEUtils.getWhatYear(endDate, 0, 'first');
                            // 当前是年维度,当指定解析格式精确到天时,显示实际进度
                            let startSubtractSize = 0;
                            let endSubtractSize = 1;
                            if (showActualProgress) {
                                // 按年的天数比计算
                                const syDaySize = XEUtils.getDayOfYear(startDate, 0);
                                startSubtractSize = (startDate.getTime() - startFirstDate.getTime()) / dayMs / syDaySize;
                                const eyDaySize = XEUtils.getDayOfYear(endDate, 0);
                                endSubtractSize = (endDate.getTime() - endFirstDate.getTime()) / dayMs / eyDaySize;
                                if (currRatio) {
                                    startSubtractSize *= currRatio;
                                    endSubtractSize *= currRatio;
                                }
                            }
                            const offsetLeftSize = (indexMaps[startStr] || 0) + startSubtractSize;
                            return {
                                offsetLeftSize,
                                offsetWidthSize: (indexMaps[endStr] || 0) - offsetLeftSize + endSubtractSize
                            };
                        };
                    }
                    case 'quarter': {
                        const showActualProgress = /M|d|H|m|ss|S/.test(dateFormat);
                        const renderFormat = 'yyyy-q';
                        const indexMaps = {};
                        fullCols.forEach(({ dateObj }, i) => {
                            const currStr = XEUtils.toDateString(dateObj.date, renderFormat);
                            indexMaps[currStr] = i;
                            if (currRatio) {
                                let currStep = scaleStep - 1;
                                while (currStep) {
                                    const currStr = XEUtils.toDateString(XEUtils.getWhatQuarter(dateObj.date, scaleStep - currStep), renderFormat);
                                    indexMaps[currStr] = i + ((scaleStep - currStep) * currRatio);
                                    currStep--;
                                }
                            }
                        });
                        return (startValue, endValue) => {
                            const startValDate = parseStringDate(startValue);
                            const endValDate = parseStringDate(endValue);
                            const startStr = XEUtils.toDateString(startValDate, renderFormat);
                            const startDate = showActualProgress ? startValDate : XEUtils.getWhatQuarter(startValDate, 0, 'first');
                            const startFirstDate = XEUtils.getWhatQuarter(startValDate, 0, 'first');
                            const endStr = XEUtils.toDateString(endValDate, renderFormat);
                            const endDate = showActualProgress ? endValDate : XEUtils.getWhatQuarter(endValDate, 0, 'last');
                            const endFirstDate = XEUtils.getWhatQuarter(endDate, 0, 'first');
                            // 当前是季度维度,当指定解析格式精确到天时,显示实际进度
                            let startSubtractSize = 0;
                            let endSubtractSize = 1;
                            if (showActualProgress) {
                                // 按季度天数比计算
                                const sqDaySize = XEUtils.getDayOfQuarter(startDate, 0);
                                startSubtractSize = (startDate.getTime() - startFirstDate.getTime()) / dayMs / sqDaySize;
                                const eqDaySize = XEUtils.getDayOfQuarter(endDate, 0);
                                endSubtractSize = (endDate.getTime() - endFirstDate.getTime()) / dayMs / eqDaySize;
                                if (currRatio) {
                                    startSubtractSize *= currRatio;
                                    endSubtractSize *= currRatio;
                                }
                            }
                            const offsetLeftSize = (indexMaps[startStr] || 0) + startSubtractSize;
                            return {
                                offsetLeftSize,
                                offsetWidthSize: (indexMaps[endStr] || 0) - offsetLeftSize + endSubtractSize
                            };
                        };
                    }
                    case 'month': {
                        const renderFormat = 'yyyy-MM';
                        const showActualProgress = /d|H|m|ss|S/.test(dateFormat);
                        const indexMaps = {};
                        fullCols.forEach(({ dateObj }, i) => {
                            const currStr = XEUtils.toDateString(dateObj.date, renderFormat);
                            indexMaps[currStr] = i;
                            if (currRatio) {
                                let currStep = scaleStep - 1;
                                while (currStep) {
                                    const currStr = XEUtils.toDateString(XEUtils.getWhatMonth(dateObj.date, scaleStep - currStep), renderFormat);
                                    indexMaps[currStr] = i + ((scaleStep - currStep) * currRatio);
                                    currStep--;
                                }
                            }
                        });
                        return (startValue, endValue) => {
                            const startValDate = parseStringDate(startValue);
                            const endValDate = parseStringDate(endValue);
                            const startStr = XEUtils.toDateString(startValDate, renderFormat);
                            const startDate = showActualProgress ? startValDate : XEUtils.getWhatMonth(startValDate, 0, 'first');
                            const startFirstDate = XEUtils.getWhatMonth(startValDate, 0, 'first');
                            const endStr = XEUtils.toDateString(endValDate, renderFormat);
                            const endDate = showActualProgress ? endValDate : XEUtils.getWhatMonth(endValDate, 0, 'last');
                            const endFirstDate = XEUtils.getWhatMonth(endDate, 0, 'first');
                            // 当前是月维度,当指定解析格式精确到天时,显示实际进度
                            let startSubtractSize = 0;
                            let endSubtractSize = 1;
                            if (showActualProgress) {
                                // 按月天数比计算
                                const smDaySize = XEUtils.getDayOfMonth(startDate, 0);
                                startSubtractSize = (startDate.getTime() - startFirstDate.getTime()) / dayMs / smDaySize;
                                const emDaySize = XEUtils.getDayOfMonth(endDate, 0);
                                endSubtractSize = (endDate.getTime() - endFirstDate.getTime()) / dayMs / emDaySize;
                                if (currRatio) {
                                    startSubtractSize *= currRatio;
                                    endSubtractSize *= currRatio;
                                }
                            }
                            const offsetLeftSize = (indexMaps[startStr] || 0) + startSubtractSize;
                            return {
                                offsetLeftSize,
                                offsetWidthSize: (indexMaps[endStr] || 0) - offsetLeftSize + endSubtractSize
                            };
                        };
                    }
                    case 'week': {
                        const weekStartDay = weekScale ? weekScale.startDay : undefined;
                        const showActualProgress = /d|H|m|s|S/.test(dateFormat);
                        const indexMaps = {};
                        fullCols.forEach(({ dateObj }, i) => {
                            const currStr = `${dateObj.yyyy}-${dateObj.W}`;
                            indexMaps[currStr] = i;
                            if (currRatio) {
                                let currStep = scaleStep - 1;
                                while (currStep) {
                                    const offsetDate = XEUtils.getWhatWeek(dateObj.date, scaleStep - currStep, weekStartDay, weekStartDay);
                                    let [yyyy, M] = XEUtils.toDateString(offsetDate, 'yyyy-M-MM-dd-HH-mm-ss').split('-');
                                    const W = `${XEUtils.getYearWeek(offsetDate, weekScale ? weekScale.startDay : undefined)}`;
                                    if (checkWeekOfsetYear(W, M)) {
                                        yyyy = `${Number(yyyy) + 1}`;
                                    }
                                    const currStr = `${yyyy}-${W}`;
                                    indexMaps[currStr] = i + ((scaleStep - currStep) * currRatio);
                                    currStep--;
                                }
                            }
                        });
                        return (startValue, endValue) => {
                            const startValDate = parseStringDate(startValue);
                            const endValDate = parseStringDate(endValue);
                            const startDate = showActualProgress ? startValDate : XEUtils.getWhatWeek(startValDate, 0, 'first', weekStartDay);
                            const startWeekObj = parseWeekObj(startDate, weekStartDay);
                            const startStr = `${startWeekObj.yyyy}-${startWeekObj.W}`;
                            const startFirstDate = XEUtils.getWhatWeek(startDate, 0, 'first', weekStartDay);
                            const endDate = showActualProgress ? endValDate : XEUtils.getWhatWeek(endValDate, 0, 'first', weekStartDay);
                            const endWeekObj = parseWeekObj(endDate, weekStartDay);
                            const endStr = `${endWeekObj.yyyy}-${endWeekObj.W}`;
                            const endFirstDate = XEUtils.getWhatWeek(endDate, 0, 'first', weekStartDay);
                            // 当前是周维度,当指定解析格式精确到天时,显示实际进度
                            let startSubtractSize = 0;
                            let endSubtractSize = 1;
                            if (showActualProgress) {
                                // 按周天数比计算
                                startSubtractSize = (startDate.getTime() - startFirstDate.getTime()) / dayMs / 7;
                                endSubtractSize = (endDate.getTime() - endFirstDate.getTime()) / dayMs / 7;
                                if (currRatio) {
                                    startSubtractSize *= currRatio;
                                    endSubtractSize *= currRatio;
                                }
                            }
                            const offsetLeftSize = (indexMaps[startStr] || 0) + startSubtractSize;
                            return {
                                offsetLeftSize,
                                offsetWidthSize: (indexMaps[endStr] || 0) - offsetLeftSize + endSubtractSize
                            };
                        };
                    }
                    case 'day':
                    case 'date': {
                        const renderFormat = 'yyyy-MM-dd';
                        const showActualProgress = /H|m|s|S/.test(dateFormat);
                        const indexMaps = {};
                        fullCols.forEach(({ dateObj }, i) => {
                            const currStr = XEUtils.toDateString(dateObj.date, renderFormat);
                            indexMaps[currStr] = i;
                            if (currRatio) {
                                let currStep = scaleStep - 1;
                                while (currStep) {
                                    const currStr = XEUtils.toDateString(XEUtils.getWhatDay(dateObj.date, scaleStep - currStep), renderFormat);
                                    indexMaps[currStr] = i + ((scaleStep - currStep) * currRatio);
                                    currStep--;
                                }
                            }
                        });
                        return (startValue, endValue) => {
                            const startValDate = parseStringDate(startValue);
                            const endValDate = parseStringDate(endValue);
                            const startStr = XEUtils.toDateString(startValDate, renderFormat);
                            const startDate = showActualProgress ? startValDate : XEUtils.getWhatDay(startValDate, 0, 'first');
                            const startFirstDate = XEUtils.getWhatDay(startDate, 0, 'first');
                            const endStr = XEUtils.toDateString(endValDate, renderFormat);
                            const endDate = showActualProgress ? endValDate : XEUtils.getWhatDay(endValDate, 0, 'last');
                            const endFirstDate = XEUtils.getWhatDay(endDate, 0, 'first');
                            // 当前是天维度,当指定解析格式精确到时分秒豪秒时,显示实际进度
                            let startSubtractSize = 0;
                            let endSubtractSize = 1;
                            if (showActualProgress) {
                                startSubtractSize = (startDate.getTime() - startFirstDate.getTime()) / dayMs;
                                endSubtractSize = (endDate.getTime() - endFirstDate.getTime()) / dayMs;
                                if (currRatio) {
                                    startSubtractSize *= currRatio;
                                    endSubtractSize *= currRatio;
                                }
                            }
                            const offsetLeftSize = (indexMaps[startStr] || 0) + startSubtractSize;
                            return {
                                offsetLeftSize,
                                offsetWidthSize: (indexMaps[endStr] || 0) - offsetLeftSize + endSubtractSize
                            };
                        };
                    }
                    case 'hour': {
                        const renderFormat = 'yyyy-MM-dd HH';
                        const showActualProgress = /m|s|S/.test(dateFormat);
                        const indexMaps = {};
                        fullCols.forEach(({ dateObj }, i) => {
                            const currStr = XEUtils.toDateString(dateObj.date, renderFormat);
                            indexMaps[currStr] = i;
                            if (currRatio) {
                                let currStep = scaleStep - 1;
                                while (currStep) {
                                    const currStr = XEUtils.toDateString(XEUtils.getWhatHours(dateObj.date, scaleStep - currStep), renderFormat);
                                    indexMaps[currStr] = i + ((scaleStep - currStep) * currRatio);
                                    currStep--;
                                }
                            }
                        });
                        return (startValue, endValue) => {
                            const startValDate = parseStringDate(startValue);
                            const endValDate = parseStringDate(endValue);
                            const startStr = XEUtils.toDateString(startValDate, renderFormat);
                            const startDate = showActualProgress ? startValDate : XEUtils.getWhatHours(startValDate, 0, 'first');
                            const startFirstDate = XEUtils.getWhatHours(startDate, 0, 'first');
                            const endStr = XEUtils.toDateString(endValDate, renderFormat);
                            const endDate = showActualProgress ? endValDate : XEUtils.getWhatHours(endValDate, 0, 'last');
                            const endFirstDate = XEUtils.getWhatHours(endDate, 0, 'first');
                            // 当前是小时维度,当指定解析格式精确到分秒豪秒时,显示实际进度
                            let startSubtractSize = 0;
                            let endSubtractSize = 1;
                            if (showActualProgress) {
                                startSubtractSize = (startDate.getTime() - startFirstDate.getTime()) / hourMs;
                                endSubtractSize = (endDate.getTime() - endFirstDate.getTime()) / hourMs;
                                if (currRatio) {
                                    startSubtractSize *= currRatio;
                                    endSubtractSize *= currRatio;
                                }
                            }
                            const offsetLeftSize = (indexMaps[startStr] || 0) + startSubtractSize;
                            return {
                                offsetLeftSize,
                                offsetWidthSize: (indexMaps[endStr] || 0) - offsetLeftSize + endSubtractSize
                            };
                        };
                    }
                    case 'minute': {
                        const renderFormat = 'yyyy-MM-dd HH:mm';
                        const showActualProgress = /s|S/.test(dateFormat);
                        const indexMaps = {};
                        fullCols.forEach(({ dateObj }, i) => {
                            const currStr = XEUtils.toDateString(dateObj.date, renderFormat);
                            indexMaps[currStr] = i;
                            if (currRatio) {
                                let currStep = scaleStep - 1;
                                while (currStep) {
                                    const currStr = XEUtils.toDateString(XEUtils.getWhatMinutes(dateObj.date, scaleStep - currStep), renderFormat);
                                    indexMaps[currStr] = i + ((scaleStep - currStep) * currRatio);
                                    currStep--;
                                }
                            }
                        });
                        return (startValue, endValue) => {
                            const startValDate = parseStringDate(startValue);
                            const endValDate = parseStringDate(endValue);
                            const startStr = XEUtils.toDateString(startValDate, renderFormat);
                            const startDate = showActualProgress ? startValDate : XEUtils.getWhatMinutes(startValDate, 0, 'first');
                            const startFirstDate = XEUtils.getWhatMinutes(startDate, 0, 'first');
                            const endStr = XEUtils.toDateString(endValDate, renderFormat);
                            const endDate = showActualProgress ? endValDate : XEUtils.getWhatMinutes(endValDate, 0, 'last');
                            const endFirstDate = XEUtils.getWhatMinutes(endDate, 0, 'first');
                            // 当前是分钟维度,当指定解析格式精确到秒豪秒时,显示实际进度
                            let startSubtractSize = 0;
                            let endSubtractSize = 1;
                            if (showActualProgress) {
                                startSubtractSize = (startDate.getTime() - startFirstDate.getTime()) / minuteMs;
                                endSubtractSize = (endDate.getTime() - endFirstDate.getTime()) / minuteMs;
                                if (currRatio) {
                                    startSubtractSize *= currRatio;
                                    endSubtractSize *= currRatio;
                                }
                            }
                            const offsetLeftSize = (indexMaps[startStr] || 0) + startSubtractSize;
                            return {
                                offsetLeftSize,
                                offsetWidthSize: (indexMaps[endStr] || 0) - offsetLeftSize + endSubtractSize
                            };
                        };
                    }
                    case 'second': {
                        const renderFormat = 'yyyy-MM-dd HH:mm:ss';
                        const showActualProgress = /S/.test(dateFormat);
                        const indexMaps = {};
                        fullCols.forEach(({ dateObj }, i) => {
                            const currStr = XEUtils.toDateString(dateObj.date, renderFormat);
                            indexMaps[currStr] = i;
                            if (currRatio) {
                                let currStep = scaleStep - 1;
                                while (currStep) {
                                    const currStr = XEUtils.toDateString(XEUtils.getWhatSeconds(dateObj.date, scaleStep - currStep), renderFormat);
                                    indexMaps[currStr] = i + ((scaleStep - currStep) * currRatio);
                                    currStep--;
                                }
                            }
                        });
                        return (startValue, endValue) => {
                            const startValDate = parseStringDate(startValue);
                            const endValDate = parseStringDate(endValue);
                            const startStr = XEUtils.toDateString(startValDate, renderFormat);
                            const startDate = showActualProgress ? startValDate : XEUtils.getWhatSeconds(startValDate, 0, 'first');
                            const startFirstDate = XEUtils.getWhatSeconds(startDate, 0, 'first');
                            const endStr = XEUtils.toDateString(endValDate, renderFormat);
                            const endDate = showActualProgress ? endValDate : XEUtils.getWhatSeconds(endValDate, 0, 'last');
                            const endFirstDate = XEUtils.getWhatSeconds(endDate, 0, 'first');
                            // 当前是秒维度,当指定解析格式精确到豪秒时,显示实际进度
                            let startSubtractSize = 0;
                            let endSubtractSize = 1;
                            if (showActualProgress) {
                                startSubtractSize = (startDate.getTime() - startFirstDate.getTime()) / secondMs;
                                endSubtractSize = (endDate.getTime() - endFirstDate.getTime()) / secondMs;
                                if (currRatio) {
                                    startSubtractSize *= currRatio;
                                    endSubtractSize *= currRatio;
                                }
                            }
                            const offsetLeftSize = (indexMaps[startStr] || 0) + startSubtractSize;
                            return {
                                offsetLeftSize,
                                offsetWidthSize: (indexMaps[endStr] || 0) - offsetLeftSize + endSubtractSize
                            };
                        };
                    }
                }
            }
            return () => {
                return {
                    offsetLeftSize: 0,
                    offsetWidthSize: 0
                };
            };
        };
        const handleParseColumn = () => {
            const ganttProps = $xeGantt.props;
            const { treeConfig } = ganttProps;
            const { minViewDate, maxViewDate } = reactData;
            const { fullCols, groupCols } = handleColumnHeader();
            if (minViewDate && maxViewDate && fullCols.length) {
                const $xeTable = internalData.xeTable;
                if ($xeTable) {
                    const startField = computeStartField.value;
                    const endField = computeEndField.value;
                    const typeField = computeTypeField.value;
                    const { computeAggregateOpts, computeTreeOpts } = $xeTable.getComputeMaps();
                    const tableReactData = $xeTable.reactData;
                    const { isRowGroupStatus } = tableReactData;
                    const tableInternalData = $xeTable.internalData;
                    const { afterFullData, afterTreeFullData, afterGroupFullData } = tableInternalData;
                    const aggregateOpts = computeAggregateOpts.value;
                    const treeOpts = computeTreeOpts.value;
                    const { transform } = treeOpts;
                    const childrenField = treeOpts.children || treeOpts.childrenField;
                    const ctMaps = {};
                    const renderFn = createChartRender(fullCols);
                    const handleParseRender = (row) => {
                        const rowid = $xeTable.getRowid(row);
                        let startValue = XEUtils.get(row, startField);
                        let endValue = XEUtils.get(row, endField);
                        const renderTaskType = getTaskType(XEUtils.get(row, typeField));
                        const isMilestone = hasMilestoneTask(renderTaskType);
                        const isSubview = hasSubviewTask(renderTaskType);
                        if (isMilestone) {
                            if (!startValue) {
                                startValue = endValue;
                            }
                            endValue = startValue;
                        }
                        if (isSubview) {
                            ctMaps[rowid] = {
                                row,
                                rowid,
                                oLeftSize: 0,
                                oWidthSize: 0
                            };
                        }
                        else if (startValue && endValue) {
                            const { offsetLeftSize, offsetWidthSize } = renderFn(startValue, endValue);
                            ctMaps[rowid] = {
                                row,
                                rowid,
                                oLeftSize: offsetLeftSize,
                                oWidthSize: offsetWidthSize
                            };
                        }
                    };
                    if (isRowGroupStatus) {
                        // 行分组
                        const mapChildrenField = aggregateOpts.mapChildrenField;
                        if (mapChildrenField) {
                            XEUtils.eachTree(afterGroupFullData, handleParseRender, { children: mapChildrenField });
                        }
                    }
                    else if (treeConfig) {
                        // 树结构
                        XEUtils.eachTree(afterTreeFullData, handleParseRender, { children: transform ? treeOpts.mapChildrenField : childrenField });
                    }
                    else {
                        afterFullData.forEach(handleParseRender);
                    }
                    internalData.chartMaps = ctMaps;
                }
            }
            internalData.visibleColumn = fullCols;
            reactData.headerGroups = groupCols;
            updateTodayData();
            updateScrollXStatus();
            handleTableColumn();
        };
        const handleUpdateData = () => {
            const { linkList } = ganttReactData;
            const { treeConfig } = ganttProps;
            const { scrollXStore } = internalData;
            const $xeTable = internalData.xeTable;
            const sdMaps = {};
            const edMaps = {};
            let minDate = null;
            let maxDate = null;
            const allList = [];
            if ($xeTable) {
                const startField = computeStartField.value;
                const endField = computeEndField.value;
                const typeField = computeTypeField.value;
                const { computeAggregateOpts, computeTreeOpts } = $xeTable.getComputeMaps();
                const tableReactData = $xeTable.reactData;
                const { isRowGroupStatus } = tableReactData;
                const tableInternalData = $xeTable.internalData;
                const { afterFullData, afterTreeFullData, afterGroupFullData } = tableInternalData;
                const aggregateOpts = computeAggregateOpts.value;
                const treeOpts = computeTreeOpts.value;
                const { transform } = treeOpts;
                const childrenField = treeOpts.children || treeOpts.childrenField;
                const handleMinMaxData = (row) => {
                    let startValue = XEUtils.get(row, startField);
                    let endValue = XEUtils.get(row, endField);
                    const typeValue = XEUtils.get(row, typeField);
                    const isMilestone = hasMilestoneTask(typeValue);
                    if (!startValue) {
                        startValue = endValue;
                    }
                    if (isMilestone || !endValue) {
                        endValue = startValue;
                    }
                    if (startValue) {
                        const startDate = parseStringDate(startValue);
                        if (!minDate || minDate.getTime() > startDate.getTime()) {
                            minDate = startDate;
                        }
                    }
                    if (endValue) {
                        const endDate = parseStringDate(endValue);
                        if (!maxDate || maxDate.getTime() < endDate.getTime()) {
                            maxDate = endDate;
                        }
                    }
                    allList.push(row);
                };
                if (isRowGroupStatus) {
                    // 行分组
                    const mapChildrenField = aggregateOpts.mapChildrenField;
                    if (mapChildrenField) {
                        XEUtils.eachTree(afterGroupFullData, handleMinMaxData, { children: mapChildrenField });
                    }
                }
                else if (treeConfig) {
                    // 树结构
                    XEUtils.eachTree(afterTreeFullData, handleMinMaxData, { children: transform ? treeOpts.mapChildrenField : childrenField });
                }
                else {
                    afterFullData.forEach(handleMinMaxData);
                }
            }
            scrollXStore.startIndex = 0;
            scrollXStore.endIndex = Math.max(1, scrollXStore.visibleSize);
            reactData.minViewDate = minDate;
            reactData.maxViewDate = maxDate;
            internalData.startMaps = sdMaps;
            internalData.endMaps = edMaps;
            handleParseColumn();
            handleCriticalPath(allList, linkList);
        };
        /**
         * 正向:后置 ES 的下连接线
         */
        function forwardCriticalPathLowerBound(linkType, predES, predEF, duration) {
            switch (linkType) {
                case FinishToStart:
                    return predEF;
                case StartToFinish:
                    return predES - duration;
                case StartToStart:
                    return predES;
                case FinishToFinish:
                    return predEF - duration;
                default:
                    return 0;
            }
        }
        /**
         * 反向:前置 LF 的上连接线
         */
        function backwardCriticalPathUpperBound(linkType, succLS, succLF, duration) {
            switch (linkType) {
                case FinishToStart:
                    return succLS;
                case StartToFinish:
                    return succLF + duration;
                case StartToStart:
                    return succLS + duration;
                case FinishToFinish:
                    return succLF;
                default:
                    return -1;
            }
        }
        /**
         * 关键边:两端都关键时,约束是否连接
         */
        function isCriticalPathLinkTight(linkType, fromES, fromEF, toES, toEF, tolerance) {
            switch (linkType) {
                case FinishToStart:
                    return Math.abs(fromEF - toES) < tolerance;
                case StartToFinish:
                    return Math.abs(fromES - toEF) < tolerance;
                case StartToStart:
                    return Math.abs(fromES - toES) < tolerance;
                case FinishToFinish:
                    return Math.abs(fromEF - toEF) < tolerance;
                default:
                    return false;
            }
        }
        function handleCriticalPath(data, links) {
            const taskBarOpts = computeTaskBarOpts.value;
            const $xeTable = internalData.xeTable;
            const criticalRowList = [];
            const criticalRowMaps = new Map();
            const criticalLinkKeyMaps = new Map();
            ganttInternalData.criticalRowList = criticalRowList;
            ganttInternalData.criticalRowMaps = criticalRowMaps;
            ganttInternalData.criticalLinkKeyMaps = criticalLinkKeyMaps;
            if (!taskBarOpts.showCriticalPath || !links.length || !data.length || !$xeTable) {
                if (ganttReactData.criticalRowIds.length || ganttReactData.criticalLinks.length) {
                    ganttReactData.criticalPathFlag++;
                }
                ganttReactData.criticalLinks = [];
                ganttReactData.criticalRowIds = [];
                return;
            }
            const { chartMaps } = internalData;
            const tolerance = 1e-6;
            const nodeMap = new Map();
            for (const row of data) {
                const rowid = $xeTable.getRowid(row);
                const chartRest = chartMaps[rowid];
                nodeMap.set(rowid, {
                    row,
                    rowid: rowid,
                    duration: chartRest ? chartRest.oWidthSize : 0,
                    incoming: [],
                    outgoing: [],
                    indeg: 0,
                    ES: 0,
                    EF: 0,
                    LS: 0,
                    LF: 0
                });
            }
            // 边界
            const linkList = [];
            for (const link of links) {
                const fromKey = '' + link.from;
                const toKey = '' + link.to;
                if (!nodeMap.has(fromKey) || !nodeMap.has(toKey) || fromKey === toKey) {
                    continue;
                }
                const lkItemitem = {
                    from: link.from,
                    to: link.to,
                    type: XEUtils.toNumber(link.type) || 0,
                    raw: link
                };
                linkList.push(lkItemitem);
                const toObjItem = nodeMap.get('' + link.to);
                const fromObjItem = nodeMap.get('' + link.from);
                if (fromObjItem) {
                    fromObjItem.outgoing.push(lkItemitem);
                }
                if (toObjItem) {
                    toObjItem.incoming.push(lkItemitem);
                    toObjItem.indeg++;
                }
            }
            // 拓扑排序 + 环检测
            const queueList = [];
            nodeMap.forEach(nodeitem => {
                if (nodeitem.indeg === 0) {
                    queueList.push(nodeitem);
                }
            });
            const orderList = [];
            for (let head = 0; head < queueList.length; head++) {
                const node = queueList[head];
                orderList.push(node);
                for (const lkItemitem of node.outgoing) {
                    const toObjItem = nodeMap.get('' + lkItemitem.to);
                    if (toObjItem) {
                        const nextDeg = toObjItem.indeg - 1;
                        toObjItem.indeg = nextDeg;
                        if (nextDeg === 0) {
                            queueList.push(nodeMap.get('' + lkItemitem.to));
                        }
                    }
                }
            }
            if (orderList.length !== nodeMap.size) {
                // 异常数据,依赖图中存在环
                if (ganttReactData.criticalRowIds.length || ganttReactData.criticalLinks.length) {
                    ganttReactData.criticalPathFlag++;
                }
                ganttReactData.criticalLinks = [];
                ganttReactData.criticalRowIds = [];
                return;
            }
            // 正向:ES / EF
            let criticalDuration = 0;
            for (const node of orderList) {
                let earliestStart = 0;
                for (const lkItem of node.incoming) {
                    const fromObjItem = nodeMap.get('' + lkItem.from);
                    if (fromObjItem) {
                        const need = forwardCriticalPathLowerBound(lkItem.type, fromObjItem.ES, fromObjItem.EF, node.duration);
                        if (need > earliestStart) {
                            earliestStart = need;
                        }
                    }
                }
                node.ES = earliestStart;
                node.EF = earliestStart + node.duration;
                if (node.EF > criticalDuration) {
                    criticalDuration = node.EF;
                }
            }
            // 反向:LS / LF
            for (let i = orderList.length - 1; i >= 0; i--) {
                const node = orderList[i];
                let latestFinish = criticalDuration;
                for (const lkItem of node.outgoing) {
                    const toObjItem = nodeMap.get('' + lkItem.to);
                    if (toObjItem) {
                        const need = backwardCriticalPathUpperBound(lkItem.type, toObjItem.LS, toObjItem.LF, node.duration);
                        if (need < latestFinish) {
                            latestFinish = need;
                        }
                    }
                }
                node.LF = latestFinish;
                node.LS = latestFinish - node.duration;
            }
            // 关键任务
            const criticalSet = new Set();
            const criticalRowIds = [];
            for (const nodeItem of orderList) {
                if (Math.abs(nodeItem.LS - nodeItem.ES) < tolerance) {
                    criticalSet.add(nodeItem.rowid);
                    criticalRowIds.push(nodeItem.rowid);
                    criticalRowList.push(nodeItem.row);
                    criticalRowMaps.set(nodeItem.rowid, nodeItem.row);
                }
            }
            // 关键连接线
            const criticalLinks = [];
            for (const lkItem of linkList) {
                const toObjItem = nodeMap.get('' + lkItem.to);
                const fromObjItem = nodeMap.get('' + lkItem.from);
                if (!toObjItem || !fromObjItem) {
                    continue;
                }
                // 两端必须都是关键任务
                if (!criticalSet.has(fromObjItem.rowid) || !criticalSet.has(toObjItem.rowid)) {
                    continue;
                }
                // 约束必须是连接的
                if (isCriticalPathLinkTight(lkItem.type, fromObjItem.ES, fromObjItem.EF, toObjItem.ES, toObjItem.EF, tolerance)) {
                    criticalLinks.push(lkItem.raw);
                    criticalLinkKeyMaps.set(getTaskLinkKey(lkItem.from, lkItem.to, lkItem.type), lkItem.raw);
                }
            }
            ganttReactData.criticalLinks = criticalLinks;
            ganttReactData.criticalRowIds = criticalRowIds;
            ganttReactData.criticalPathFlag++;
        }
        const calcScrollbar = () => {
            const { scrollXWidth, scrollYHeight } = reactData;
            const { elemStore } = internalData;
            const scrollbarOpts = computeScrollbarOpts.value;
            const bodyWrapperElem = getRefElem(elemStore['main-body-wrapper']);
            const xHandleEl = refScrollXHandleElem.value;
            const yHandleEl = refScrollYHandleElem.value;
            let overflowY = false;
            let overflowX = false;
            if (bodyWrapperElem) {
                overflowY = scrollYHeight > bodyWrapperElem.clientHeight;
                if (yHandleEl) {
                    const scrollbarWidth = scrollbarOpts.width || (yHandleEl.offsetWidth - yHandleEl.clientWidth) || 14;
                    reactData.scrollbarWidth = scrollbarWidth;
                    ganttReactData.scrollbarWidth = scrollbarWidth;
                }
                reactData.overflowY = overflowY;
                ganttReactData.overflowY = overflowY;
                overflowX = scrollXWidth > bodyWrapperElem.clientWidth;
                if (xHandleEl) {
                    const scrollbarHeight = scrollbarOpts.height || (xHandleEl.offsetHeight - xHandleEl.clientHeight) || 14;
                    reactData.scrollbarHeight = scrollbarHeight;
                    ganttReactData.scrollbarHeight = scrollbarHeight;
                }
                reactData.overflowX = overflowX;
                ganttReactData.overflowX = overflowX;
            }
        };
        const handleSubTaskMinMaxSize = ($xeTable, list) => {
            const { chartMaps } = internalData;
            const { computeTreeOpts } = $xeTable.getComputeMaps();
            const treeOpts = computeTreeOpts.value;
            const childrenField = treeOpts.children || treeOpts.childrenField;
            const typeField = computeTypeField.value;
            let minChildLeftSize = 0;
            let maxChildLeftSize = 0;
            XEUtils.eachTree(list, childRow => {
                const childRowid = $xeTable.getRowid(childRow);
                const renderTaskType = XEUtils.get(childRow, typeField);
                if (hasSubviewTask(renderTaskType)) {
                    return;
                }
                const childChartRest = childRowid ? chartMaps[childRowid] : null;
                if (childChartRest) {
                    maxChildLeftSize = Math.max(maxChildLeftSize, childChartRest.oLeftSize + childChartRest.oWidthSize);
                    minChildLeftSize = minChildLeftSize ? Math.min(minChildLeftSize, childChartRest.oLeftSize) : childChartRest.oLeftSize;
                }
            }, { children: childrenField });
            return {
                minSize: minChildLeftSize,
                maxSize: maxChildLeftSize
            };
        };
        const updateTaskChartStyle = () => {
            const { dragBarRow } = ganttInternalData;
            const { viewCellWidth } = reactData;
            const { elemStore, chartMaps } = internalData;
            const $xeTable = internalData.xeTable;
            const chartWrapper = getRefElem(elemStore['main-chart-task-wrapper']);
            if (chartWrapper && $xeTable) {
                const { computeTreeOpts } = $xeTable.getComputeMaps();
                const treeOpts = computeTreeOpts.value;
                const childrenField = treeOpts.children || treeOpts.childrenField;
                XEUtils.arrayEach(chartWrapper.children, (rowEl) => {
                    const barEl = rowEl.children[0];
                    if (!barEl) {
                        return;
                    }
                    const rowid = rowEl.getAttribute('rowid');
                    if (dragBarRow && $xeTable.getRowid(dragBarRow) === rowid) {
                        return;
                    }
                    const chartRest = rowid ? chartMaps[rowid] : null;
                    const row = chartRest ? chartRest.row : null;
                    // 子任务视图
                    if (hasClass(barEl, 'is--subview')) {
                        const childWrapperEl = barEl.firstElementChild;
                        if (childWrapperEl) {
                            // 行内展示
                            if (hasClass(childWrapperEl, 'is--inline')) {
                                XEUtils.arrayEach(childWrapperEl.children, (childRowEl) => {
                                    const childBarEl = childRowEl.children[0];
                                    const childRowid = childBarEl.getAttribute('rowid') || '';
                                    const childChartRest = childRowid ? chartMaps[childRowid] : null;
                                    if (childChartRest) {
                                        const childRow = childChartRest.row;
                                        // 如果是子视图
                                        if (hasClass(childBarEl, 'is--subview')) {
                                            const subChildren = childRow[childrenField];
                                            const { minSize: minChildLeftSize, maxSize: maxChildLeftSize } = handleSubTaskMinMaxSize($xeTable, subChildren);
                                            childBarEl.style.left = `${viewCellWidth * minChildLeftSize}px`;
                                            childBarEl.style.width = `${viewCellWidth * (maxChildLeftSize - minChildLeftSize)}px`;
                                        }
                                        else {
                                            childBarEl.style.left = `${getTaskBarLeft(childChartRest, viewCellWidth)}px`;
                                            if (!hasClass(childBarEl, 'is--milestone')) {
                                                // 里程碑不需要宽度
                                                childBarEl.style.width = `${getTaskBarWidth(childChartRest, viewCellWidth)}px`;
                                            }
                                        }
                                    }
                                });
                            }
                            else {
                                // 如果展开子任务
                                const childRowEl = childWrapperEl.children[0];
                                const childBarEl = childRowEl ? childRowEl.children[0] : null;
                                if (childBarEl) {
                                    const rowChildren = row ? row[childrenField] : [];
                                    const { minSize: minChildLeftSize, maxSize: maxChildLeftSize } = handleSubTaskMinMaxSize($xeTable, rowChildren);
                                    childBarEl.style.left = `${viewCellWidth * minChildLeftSize}px`;
                                    childBarEl.style.width = `${viewCellWidth * (maxChildLeftSize - minChildLeftSize)}px`;
                                }
                            }
                        }
                    }
                    else {
                        barEl.style.left = `${getTaskBarLeft(chartRest, viewCellWidth)}px`;
                        // 里程碑不需要宽度
                        if (!hasClass(barEl, 'is--milestone')) {
                            barEl.style.width = `${getTaskBarWidth(chartRest, viewCellWidth)}px`;
                        }
                    }
                });
            }
            return nextTick();
        };
        const updateStyle = () => {
            const { showLeftView } = ganttReactData;
            const { scrollbarWidth, scrollbarHeight, headerGroups, tableColumn } = reactData;
            const { elemStore, visibleColumn } = internalData;
            const $xeTable = internalData.xeTable;
            const el = refElem.value;
            if (!el) {
                return;
            }
            if (!$xeGantt) {
                return;
            }
            const scrollbarOpts = computeScrollbarOpts.value;
            const scrollbarXToTop = computeScrollbarXToTop.value;
            const scrollbarYToLeft = computeScrollbarYToLeft.value;
            const xLeftCornerEl = refScrollXLeftCornerElem.value;
            const xRightCornerEl = refScrollXRightCornerElem.value;
            const scrollXVirtualEl = refScrollXVirtualElem.value;
            let osbWidth = scrollbarWidth;
            const osbHeight = scrollbarHeight;
            let tbHeight = 0;
            let tHeaderHeight = 0;
            let tFooterHeight = 0;
            if ($xeTable) {
                const tableInternalData = $xeTable.internalData;
                tbHeight = tableInternalData.tBodyHeight;
                tHeaderHeight = tableInternalData.tHeaderHeight;
                tFooterHeight = tableInternalData.tFooterHeight;
            }
            let yScrollbarVisible = 'visible';
            if ((scrollbarYToLeft && showLeftView) || (scrollbarOpts.y && scrollbarOpts.y.visible === false)) {
                osbWidth = 0;
                yScrollbarVisible = 'hidden';
            }
            const headerScrollElem = getRefElem(elemStore['main-header-scroll']);
            if (headerScrollElem) {
                headerScrollElem.style.height = `${tHeaderHeight}px`;
                headerScrollElem.style.setProperty('--vxe-ui-gantt-view-cell-height', `${tHeaderHeight / headerGroups.length}px`);
            }
            const bodyScrollElem = getRefElem(elemStore['main-body-scroll']);
            if (bodyScrollElem) {
                bodyScrollElem.style.height = `${tbHeight}px`;
            }
            const footerScrollElem = getRefElem(elemStore['main-footer-scroll']);
            if (footerScrollElem) {
                footerScrollElem.style.height = `${tFooterHeight}px`;
            }
            if (scrollXVirtualEl) {
                scrollXVirtualEl.style.height = `${osbHeight}px`;
                scrollXVirtualEl.style.visibility = 'visible';
            }
            const xWrapperEl = refScrollXWrapperElem.value;
            if (xWrapperEl) {
                xWrapperEl.style.left = scrollbarXToTop ? `${osbWidth}px` : '';
                xWrapperEl.style.width = `${el.clientWidth - osbWidth}px`;
            }
            if (xLeftCornerEl) {
                xLeftCornerEl.style.width = scrollbarXToTop ? `${osbWidth}px` : '';
                xLeftCornerEl.style.display = scrollbarXToTop ? (osbHeight ? 'block' : '') : '';
            }
            if (xRightCornerEl) {
                xRightCornerEl.style.width = scrollbarXToTop ? '' : `${osbWidth}px`;
                xRightCornerEl.style.display = scrollbarXToTop ? '' : (osbHeight ? 'block' : '');
            }
            const scrollYVirtualEl = refScrollYVirtualElem.value;
            if (scrollYVirtualEl) {
                scrollYVirtualEl.style.width = `${osbWidth}px`;
                scrollYVirtualEl.style.height = `${tbHeight + tHeaderHeight + tFooterHeight}px`;
                scrollYVirtualEl.style.visibility = yScrollbarVisible;
            }
            const yTopCornerEl = refScrollYTopCornerElem.value;
            if (yTopCornerEl) {
                yTopCornerEl.style.height = `${tHeaderHeight}px`;
                yTopCornerEl.style.display = tHeaderHeight ? 'block' : '';
            }
            const yWrapperEl = refScrollYWrapperElem.value;
            if (yWrapperEl) {
                yWrapperEl.style.height = `${tbHeight}px`;
                yWrapperEl.style.top = `${tHeaderHeight}px`;
            }
            const yBottomCornerEl = refScrollYBottomCornerElem.value;
            if (yBottomCornerEl) {
                yBottomCornerEl.style.height = `${tFooterHeight}px`;
                yBottomCornerEl.style.top = `${tHeaderHeight + tbHeight}px`;
                yBottomCornerEl.style.display = tFooterHeight ? 'block' : '';
            }
            const colInfoElem = refColInfoElem.value;
            let viewCellWidth = 40;
            if (colInfoElem) {
                viewCellWidth = colInfoElem.clientWidth || 40;
            }
            let viewTableWidth = viewCellWidth;
            if (visibleColumn.length) {
                viewTableWidth = Math.max(0, viewCellWidth * visibleColumn.length);
                if (bodyScrollElem) {
                    const viewWidth = bodyScrollElem.clientWidth;
                    const remainWidth = viewWidth - viewTableWidth;
                    if (remainWidth > 0) {
                        viewCellWidth += Math.max(0, remainWidth / visibleColumn.length);
                        viewTableWidth = viewWidth;
                    }
                }
            }
            reactData.viewCellWidth = viewCellWidth;
            const headerTableElem = getRefElem(elemStore['main-header-table']);
            const bodyTableElem = getRefElem(elemStore['main-body-table']);
            const vmTableWidth = viewCellWidth * tableColumn.length;
            if (headerTableElem) {
                headerTableElem.style.width = `${viewTableWidth}px`;
            }
            if (bodyTableElem) {
                bodyTableElem.style.width = `${vmTableWidth}px`;
            }
            reactData.scrollXWidth = viewTableWidth;
            return Promise.all([
                updateTaskChartStyle(),
                $xeGantt.handleUpTaskLinkStyle ? $xeGantt.handleUpTaskLinkStyle($xeGanttView) : null
            ]);
        };
        const handleRecalculateStyle = () => {
            const el = refElem.value;
            internalData.rceRunTime = Date.now();
            if (!el || !el.clientWidth) {
                return nextTick();
            }
            if (!$xeGantt) {
                return nextTick();
            }
            calcScrollbar();
            updateStyle();
            return computeScrollLoad();
        };
        const handleLazyRecalculate = () => {
            return new Promise(resolve => {
                const { rceTimeout, rceRunTime } = internalData;
                const $xeTable = internalData.xeTable;
                let refreshDelay = 30;
                if ($xeTable) {
                    const { computeResizeOpts } = $xeTable.getComputeMaps();
                    const resizeOpts = computeResizeOpts.value;
                    refreshDelay = resizeOpts.refreshDelay || refreshDelay;
                }
                if (rceTimeout) {
                    clearTimeout(rceTimeout);
                    if (rceRunTime && rceRunTime + (refreshDelay - 5) < Date.now()) {
                        resolve(handleRecalculateStyle());
                    }
                    else {
                        nextTick(() => {
                            resolve();
                        });
                    }
                }
                else {
                    resolve(handleRecalculateStyle());
                }
                internalData.rceTimeout = setTimeout(() => {
                    internalData.rceTimeout = undefined;
                    handleRecalculateStyle();
                }, refreshDelay);
            });
        };
        const computeScrollLoad = () => {
            return nextTick().then(() => {
                const { scrollXLoad } = reactData;
                const { scrollXStore } = internalData;
                // 计算 X 逻辑
                if (scrollXLoad) {
                    const { toVisibleIndex: toXVisibleIndex, visibleSize: visibleXSize } = handleVirtualXVisible();
                    const offsetXSize = 2;
                    scrollXStore.preloadSize = 1;
                    scrollXStore.offsetSize = offsetXSize;
                    scrollXStore.visibleSize = visibleXSize;
                    scrollXStore.endIndex = Math.max(scrollXStore.startIndex + scrollXStore.visibleSize + offsetXSize, scrollXStore.endIndex);
                    scrollXStore.visibleStartIndex = Math.max(scrollXStore.startIndex, toXVisibleIndex);
                    scrollXStore.visibleEndIndex = Math.min(scrollXStore.endIndex, toXVisibleIndex + visibleXSize);
                    updateScrollXData().then(() => {
                        loadScrollXData();
                    });
                }
                else {
                    updateScrollXSpace();
                }
            });
        };
        const handleVirtualXVisible = () => {
            const { viewCellWidth } = reactData;
            const { elemStore } = internalData;
            const bodyScrollElem = getRefElem(elemStore['main-body-scroll']);
            if (bodyScrollElem) {
                const clientWidth = bodyScrollElem.clientWidth;
                const scrollLeft = bodyScrollElem.scrollLeft;
                const toVisibleIndex = Math.floor(scrollLeft / viewCellWidth) - 1;
                const visibleSize = Math.ceil(clientWidth / viewCellWidth) + 1;
                return { toVisibleIndex: Math.max(0, toVisibleIndex), visibleSize: Math.max(1, visibleSize) };
            }
            return { toVisibleIndex: 0, visibleSize: 6 };
        };
        const loadScrollXData = () => {
            const { isScrollXBig } = reactData;
            const { scrollXStore } = internalData;
            const { preloadSize, startIndex, endIndex, offsetSize } = scrollXStore;
            const { toVisibleIndex, visibleSize } = handleVirtualXVisible();
            const offsetItem = {
                startIndex: Math.max(0, isScrollXBig ? toVisibleIndex - 1 : toVisibleIndex - 1 - offsetSize - preloadSize),
                endIndex: isScrollXBig ? toVisibleIndex + visibleSize : toVisibleIndex + visibleSize + offsetSize + preloadSize
            };
            scrollXStore.visibleStartIndex = toVisibleIndex - 1;
            scrollXStore.visibleEndIndex = toVisibleIndex + visibleSize + 1;
            const { startIndex: offsetStartIndex, endIndex: offsetEndIndex } = offsetItem;
            if (toVisibleIndex <= startIndex || toVisibleIndex >= endIndex - visibleSize - 1) {
                if (startIndex !== offsetStartIndex || endIndex !== offsetEndIndex) {
                    scrollXStore.startIndex = offsetStartIndex;
                    scrollXStore.endIndex = offsetEndIndex;
                    updateScrollXData();
                }
            }
        };
        const updateScrollXData = () => {
            handleTableColumn();
            updateScrollXSpace();
            return nextTick();
        };
        const updateScrollXStatus = () => {
            const scrollXLoad = true;
            reactData.scrollXLoad = scrollXLoad;
            return scrollXLoad;
        };
        const handleTableColumn = () => {
            const { scrollXLoad } = reactData;
            const { visibleColumn, scrollXStore } = internalData;
            const tableColumn = scrollXLoad ? visibleColumn.slice(scrollXStore.startIndex, scrollXStore.endIndex) : visibleColumn.slice(0);
            reactData.tableColumn = tableColumn;
        };
        const updateScrollXSpace = () => {
            const { scrollXLoad, scrollXWidth, viewCellWidth } = reactData;
            const { elemStore, scrollXStore } = internalData;
            const bodyTableElem = getRefElem(elemStore['main-body-table']);
            // const headerTableElem = getRefElem(elemStore['main-header-table'])
            // const footerTableElem = getRefElem(elemStore['main-footer-table'])
            const { startIndex } = scrollXStore;
            let xSpaceLeft = 0;
            if (scrollXLoad) {
                xSpaceLeft = Math.max(0, startIndex * viewCellWidth);
            }
            // if (headerTableElem) {
            //   headerTableElem.style.transform = `translate(${xSpaceLeft}px, 0px)`
            // }
            if (bodyTableElem) {
                bodyTableElem.style.transform = `translate(${xSpaceLeft}px, ${reactData.scrollYTop || 0}px)`;
            }
            // if (footerTableElem) {
            //   footerTableElem.style.transform = `translate(${xSpaceLeft}px, 0px)`
            // }
            const ySpaceWidth = scrollXWidth;
            const layoutList = ['header', 'body', 'footer'];
            layoutList.forEach(layout => {
                const xSpaceElem = getRefElem(elemStore[`main-${layout}-xSpace`]);
                if (xSpaceElem) {
                    xSpaceElem.style.width = scrollXLoad ? `${ySpaceWidth}px` : '';
                }
            });
            const scrollXSpaceEl = refScrollXSpaceElem.value;
            if (scrollXSpaceEl) {
                scrollXSpaceEl.style.width = `${ySpaceWidth}px`;
            }
            const beforeWrapper = getRefElem(elemStore['main-chart-before-wrapper']);
            const beforeSvgElem = beforeWrapper ? beforeWrapper.firstElementChild : null;
            if (beforeSvgElem) {
                beforeSvgElem.style.width = `${ySpaceWidth}px`;
            }
            const afterWrapper = getRefElem(elemStore['main-chart-after-wrapper']);
            const afterSvgElem = afterWrapper ? afterWrapper.firstElementChild : null;
            if (afterSvgElem) {
                afterSvgElem.style.width = `${ySpaceWidth}px`;
            }
            reactData.scrollXLeft = xSpaceLeft;
            reactData.scrollXWidth = ySpaceWidth;
            calcScrollbar();
            return nextTick();
        };
        const triggerScrollXEvent = () => {
            loadScrollXData();
        };
        const updateScrollYSpace = () => {
            const { scrollYLoad, overflowY } = reactData;
            const { elemStore } = internalData;
            const $xeTable = internalData.xeTable;
            const bodyScrollElem = getRefElem(elemStore['main-body-scroll']);
            const bodyTableElem = getRefElem(elemStore['main-body-table']);
            let ySpaceTop = 0;
            let scrollYHeight = 0;
            let isScrollYBig = false;
            if ($xeTable) {
                const tableReactData = $xeTable.reactData;
                ySpaceTop = tableReactData.scrollYTop;
                scrollYHeight = tableReactData.scrollYHeight;
                isScrollYBig = tableReactData.isScrollYBig;
            }
            let ySpaceHeight = scrollYHeight;
            let scrollYTop = ySpaceTop;
            let clientHeight = 0;
            if (bodyScrollElem) {
                clientHeight = bodyScrollElem.clientHeight;
            }
            if (isScrollYBig) {
                // 触底
                if (bodyScrollElem && bodyTableElem && bodyScrollElem.scrollTop + clientHeight >= maxYHeight) {
                    scrollYTop = maxYHeight - bodyTableElem.clientHeight;
                }
                else {
                    scrollYTop = (maxYHeight - clientHeight) * (ySpaceTop / (scrollYHeight - clientHeight));
                }
                ySpaceHeight = maxYHeight;
            }
            if (!(scrollYLoad && overflowY)) {
                scrollYTop = 0;
            }
            const bodyChartWrapperElem = getRefElem(elemStore['main-chart-task-wrapper']);
            if (bodyTableElem) {
                bodyTableElem.style.transform = `translate(${reactData.scrollXLeft || 0}px, ${scrollYTop}px)`;
            }
            if (bodyChartWrapperElem) {
                bodyChartWrapperElem.style.transform = `translateY(${scrollYTop}px)`;
            }
            const bodyYSpaceElem = getRefElem(elemStore['main-body-ySpace']);
            if (bodyYSpaceElem) {
                bodyYSpaceElem.style.height = ySpaceHeight ? `${ySpaceHeight}px` : '';
            }
            const scrollYSpaceEl = refScrollYSpaceElem.value;
            if (scrollYSpaceEl) {
                scrollYSpaceEl.style.height = ySpaceHeight ? `${ySpaceHeight}px` : '';
            }
            const beforeWrapper = getRefElem(elemStore['main-chart-before-wrapper']);
            const beforeSvgElem = beforeWrapper ? beforeWrapper.firstElementChild : null;
            if (beforeSvgElem) {
                beforeSvgElem.style.height = ySpaceHeight ? `${ySpaceHeight}px` : '';
            }
            const afterWrapper = getRefElem(elemStore['main-chart-after-wrapper']);
            const afterSvgElem = afterWrapper ? afterWrapper.firstElementChild : null;
            if (afterSvgElem) {
                afterSvgElem.style.height = ySpaceHeight ? `${ySpaceHeight}px` : '';
            }
            const nowLineElem = getRefElem(elemStore['main-chart-now-line']);
            if (nowLineElem) {
                nowLineElem.style.height = ySpaceHeight ? `${ySpaceHeight}px` : '';
            }
            reactData.scrollYTop = scrollYTop;
            reactData.scrollYHeight = scrollYHeight;
            reactData.isScrollYBig = isScrollYBig;
            calcScrollbar();
            return nextTick().then(() => {
                updateStyle();
            });
        };
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        const checkLastSyncScroll = (isRollX, isRollY) => {
            const { lcsTimeout } = internalData;
            reactData.lazScrollLoading = true;
            if (lcsTimeout) {
                clearTimeout(lcsTimeout);
            }
            internalData.lcsTimeout = setTimeout(() => {
                internalData.lcsRunTime = Date.now();
                internalData.lcsTimeout = undefined;
                internalData.intoRunScroll = false;
                internalData.inVirtualScroll = false;
                internalData.inWheelScroll = false;
                internalData.inHeaderScroll = false;
                internalData.inBodyScroll = false;
                internalData.inFooterScroll = false;
                reactData.lazScrollLoading = false;
            }, 200);
        };
        const handleScrollData = (isRollY, isRollX, scrollTop, scrollLeft) => {
            if (isRollX) {
                internalData.lastScrollLeft = scrollLeft;
            }
            if (isRollY) {
                internalData.lastScrollTop = scrollTop;
            }
            reactData.lastScrollTime = Date.now();
            checkLastSyncScroll(isRollX, isRollY);
        };
        const handleScrollEvent = (evnt, isRollY, isRollX, scrollTop, scrollLeft) => {
            const $xeTable = internalData.xeTable;
            const { lastScrollLeft, lastScrollTop } = internalData;
            const xHandleEl = refScrollXHandleElem.value;
            const yHandleEl = refScrollYHandleElem.value;
            if (!xHandleEl || !yHandleEl) {
                return;
            }
            if (!$xeTable) {
                return;
            }
            const { computeScrollXThreshold, computeScrollYThreshold } = $xeTable.getComputeMaps();
            const bodyHeight = yHandleEl.clientHeight;
            const bodyWidth = xHandleEl.clientWidth;
            const scrollHeight = yHandleEl.scrollHeight;
            const scrollWidth = xHandleEl.scrollWidth;
            let isTop = false;
            let isBottom = false;
            let isLeft = false;
            let isRight = false;
            let direction = '';
            let isTopBoundary = false;
            let isBottomBoundary = false;
            let isLeftBoundary = false;
            let isRightBoundary = false;
            if (isRollX) {
                const xThreshold = computeScrollXThreshold.value;
                isLeft = scrollLeft <= 0;
                if (!isLeft) {
                    isRight = scrollLeft + bodyWidth >= scrollWidth - 1;
                }
                if (scrollLeft > lastScrollLeft) {
                    direction = 'right';
                    if (scrollLeft + bodyWidth >= scrollWidth - xThreshold) {
                        isRightBoundary = true;
                    }
                }
                else {
                    direction = 'left';
                    if (scrollLeft <= xThreshold) {
                        isLeftBoundary = true;
                    }
                }
            }
            if (isRollY) {
                const yThreshold = computeScrollYThreshold.value;
                isTop = scrollTop <= 0;
                if (!isTop) {
                    isBottom = scrollTop + bodyHeight >= scrollHeight - 1;
                }
                if (scrollTop > lastScrollTop) {
                    direction = 'bottom';
                    if (scrollTop + bodyHeight >= scrollHeight - yThreshold) {
                        isBottomBoundary = true;
                    }
                }
                else {
                    direction = 'top';
                    if (scrollTop <= yThreshold) {
                        isTopBoundary = true;
                    }
                }
            }
            handleScrollData(isRollY, isRollX, scrollTop, scrollLeft);
            const evntParams = {
                source: sourceType,
                scrollTop,
                scrollLeft,
                bodyHeight,
                bodyWidth,
                scrollHeight,
                scrollWidth,
                isX: isRollX,
                isY: isRollY,
                isTop,
                isBottom,
                isLeft,
                isRight,
                direction
            };
            if (isBottomBoundary || isTopBoundary || isRightBoundary || isLeftBoundary) {
                $xeGantt.dispatchEvent('scroll-boundary', evntParams, evnt);
            }
            $xeGantt.dispatchEvent('scroll', evntParams, evnt);
        };
        const ganttViewMethods = {
            refreshData() {
                handleUpdateData();
                handleRecalculateStyle();
                return nextTick().then(() => {
                    const $xeTable = internalData.xeTable;
                    handleRecalculateStyle();
                    if ($xeTable) {
                        return $xeTable.recalculate();
                    }
                });
            },
            updateViewData(force) {
                const $xeTable = internalData.xeTable;
                if ($xeTable) {
                    const tableReactData = $xeTable.reactData;
                    const { tableData } = tableReactData;
                    reactData.tableData = tableData;
                    if (force) {
                        ganttReactData.currLeftSpacing = 0;
                        ganttReactData.currRightSpacing = 0;
                        handleUpdateData();
                    }
                    handleRecalculateStyle();
                }
                return nextTick();
            },
            connectUpdate({ $table }) {
                if ($table) {
                    internalData.xeTable = $table;
                }
                return nextTick();
            }
        };
        /**
         * 同步表格滚动
         */
        const syncTableScrollTop = (scrollTop) => {
            const $xeTable = internalData.xeTable;
            if ($xeTable) {
                const tableInternalData = $xeTable.internalData;
                const { elemStore: tableElemStore } = tableInternalData;
                const tableBodyScrollElem = getRefElem(tableElemStore['main-body-scroll']);
                if (tableBodyScrollElem) {
                    tableBodyScrollElem.scrollTop = scrollTop;
                }
            }
        };
        const ganttViewPrivateMethods = {
            handleUpdateStyle: updateStyle,
            handleLazyRecalculate,
            handleUpdateCurrentRow(row) {
                const $xeTable = internalData.xeTable;
                const el = refElem.value;
                if ($xeTable && el) {
                    if (row) {
                        const tableProps = $xeTable.props;
                        const { computeRowOpts } = $xeTable.getComputeMaps();
                        const rowOpts = computeRowOpts.value;
                        if (rowOpts.isCurrent || tableProps.highlightCurrentRow) {
                            XEUtils.arrayEach(el.querySelectorAll(`.vxe-gantt-view--body-row[rowid="${$xeTable.getRowid(row)}"]`), elem => addClass(elem, 'row--current'));
                        }
                    }
                    else {
                        XEUtils.arrayEach(el.querySelectorAll('.vxe-gantt-view--body-row.row--current'), elem => removeClass(elem, 'row--current'));
                    }
                }
            },
            handleUpdateHoverRow(row) {
                const $xeTable = internalData.xeTable;
                const el = refElem.value;
                if ($xeTable && el) {
                    if (row) {
                        XEUtils.arrayEach(el.querySelectorAll(`.vxe-gantt-view--body-row[rowid="${$xeTable.getRowid(row)}"]`), elem => addClass(elem, 'row--hover'));
                    }
                    else {
                        XEUtils.arrayEach(el.querySelectorAll('.vxe-gantt-view--body-row.row--hover'), elem => removeClass(elem, 'row--hover'));
                    }
                }
            },
            triggerHeaderScrollEvent(evnt) {
                const { elemStore, inVirtualScroll, inBodyScroll, inFooterScroll } = internalData;
                if (inVirtualScroll) {
                    return;
                }
                if (inBodyScroll || inFooterScroll) {
                    return;
                }
                const wrapperEl = evnt.currentTarget;
                const bodyScrollElem = getRefElem(elemStore['main-body-scroll']);
                const xHandleEl = refScrollXHandleElem.value;
                if (bodyScrollElem && wrapperEl) {
                    const isRollX = true;
                    const isRollY = false;
                    const currLeftNum = wrapperEl.scrollLeft;
                    internalData.inHeaderScroll = true;
                    setScrollLeft(xHandleEl, currLeftNum);
                    setScrollLeft(bodyScrollElem, currLeftNum);
                    handleScrollEvent(evnt, isRollY, isRollX, wrapperEl.scrollTop, currLeftNum);
                }
            },
            triggerBodyScrollEvent(evnt) {
                const { scrollXLoad } = reactData;
                const { elemStore, inVirtualScroll, inHeaderScroll, inFooterScroll, lastScrollLeft, lastScrollTop } = internalData;
                if (inVirtualScroll) {
                    return;
                }
                if (inHeaderScroll || inFooterScroll) {
                    return;
                }
                const wrapperEl = evnt.currentTarget;
                const headerScrollElem = getRefElem(elemStore['main-header-scroll']);
                const xHandleEl = refScrollXHandleElem.value;
                const yHandleEl = refScrollYHandleElem.value;
                const scrollLeft = wrapperEl.scrollLeft;
                const scrollTop = wrapperEl.scrollTop;
                const isRollX = scrollLeft !== lastScrollLeft;
                const isRollY = scrollTop !== lastScrollTop;
                internalData.inBodyScroll = true;
                internalData.scrollRenderType = '';
                if (isRollY) {
                    setScrollTop(yHandleEl, scrollTop);
                    syncTableScrollTop(scrollTop);
                }
                if (isRollX) {
                    internalData.inBodyScroll = true;
                    setScrollLeft(xHandleEl, scrollLeft);
                    setScrollLeft(headerScrollElem, scrollLeft);
                    if (scrollXLoad) {
                        triggerScrollXEvent();
                    }
                }
                if (isRollY) {
                    handleScrollData(isRollY, isRollX, wrapperEl.scrollTop, scrollLeft);
                }
                if (isRollX) {
                    handleScrollEvent(evnt, isRollY, isRollX, wrapperEl.scrollTop, scrollLeft);
                }
            },
            // triggerFooterScrollEvent (evnt) {
            //   const { inVirtualScroll, inHeaderScroll, inBodyScroll } = internalData
            //   if (inVirtualScroll) {
            //     return
            //   }
            //   if (inHeaderScroll || inBodyScroll) {
            //     return
            //   }
            //   const wrapperEl = evnt.currentTarget as HTMLDivElement
            //   if (wrapperEl) {
            //     const isRollX = true
            //     const isRollY = false
            //     const currLeftNum = wrapperEl.scrollLeft
            //     handleScrollEvent(evnt, isRollY, isRollX, wrapperEl.scrollTop, currLeftNum)
            //   }
            // },
            triggerVirtualScrollXEvent(evnt) {
                const { scrollXLoad } = reactData;
                const { elemStore, inHeaderScroll, inBodyScroll } = internalData;
                if (inHeaderScroll || inBodyScroll) {
                    return;
                }
                const wrapperEl = evnt.currentTarget;
                const headerScrollElem = getRefElem(elemStore['main-header-scroll']);
                const bodyScrollElem = getRefElem(elemStore['main-body-scroll']);
                if (wrapperEl) {
                    const isRollY = false;
                    const isRollX = true;
                    const currLeftNum = wrapperEl.scrollLeft;
                    internalData.inVirtualScroll = true;
                    setScrollLeft(headerScrollElem, currLeftNum);
                    setScrollLeft(bodyScrollElem, currLeftNum);
                    if (scrollXLoad) {
                        triggerScrollXEvent();
                    }
                    handleScrollEvent(evnt, isRollY, isRollX, wrapperEl.scrollTop, currLeftNum);
                }
            },
            triggerVirtualScrollYEvent(evnt) {
                const { elemStore, inHeaderScroll, inBodyScroll } = internalData;
                if (inHeaderScroll || inBodyScroll) {
                    return;
                }
                const wrapperEl = evnt.currentTarget;
                const bodyScrollElem = getRefElem(elemStore['main-body-scroll']);
                if (wrapperEl) {
                    const isRollY = true;
                    const isRollX = false;
                    const currTopNum = wrapperEl.scrollTop;
                    internalData.inVirtualScroll = true;
                    setScrollTop(bodyScrollElem, currTopNum);
                    syncTableScrollTop(currTopNum);
                    handleScrollData(isRollY, isRollX, currTopNum, wrapperEl.scrollLeft);
                }
            },
            handleUpdateSXSpace() {
                return updateScrollXSpace();
            },
            handleUpdateSYSpace() {
                return updateScrollYSpace();
            },
            handleUpdateSYStatus(sYLoad) {
                reactData.scrollYLoad = sYLoad;
            }
        };
        const handleGlobalResizeEvent = () => {
            handleLazyRecalculate();
        };
        Object.assign($xeGanttView, ganttViewMethods, ganttViewPrivateMethods);
        const renderScrollX = () => {
            return h('div', {
                key: 'vsx',
                ref: refScrollXVirtualElem,
                class: 'vxe-gantt-view--scroll-x-virtual'
            }, [
                h('div', {
                    ref: refScrollXLeftCornerElem,
                    class: 'vxe-gantt-view--scroll-x-left-corner'
                }),
                h('div', {
                    ref: refScrollXWrapperElem,
                    class: 'vxe-gantt-view--scroll-x-wrapper'
                }, [
                    h('div', {
                        ref: refScrollXHandleElem,
                        class: 'vxe-gantt-view--scroll-x-handle',
                        onScroll: $xeGanttView.triggerVirtualScrollXEvent
                    }, [
                        h('div', {
                            ref: refScrollXSpaceElem,
                            class: 'vxe-gantt-view--scroll-x-space'
                        })
                    ]),
                    h('div', {
                        class: 'vxe-gantt-view--scroll-x-handle-appearance'
                    })
                ]),
                h('div', {
                    ref: refScrollXRightCornerElem,
                    class: 'vxe-gantt-view--scroll-x-right-corner'
                })
            ]);
        };
        const renderScrollY = () => {
            return h('div', {
                ref: refScrollYVirtualElem,
                class: 'vxe-gantt-view--scroll-y-virtual'
            }, [
                h('div', {
                    ref: refScrollYTopCornerElem,
                    class: 'vxe-gantt-view--scroll-y-top-corner'
                }),
                h('div', {
                    ref: refScrollYWrapperElem,
                    class: 'vxe-gantt-view--scroll-y-wrapper'
                }, [
                    h('div', {
                        ref: refScrollYHandleElem,
                        class: 'vxe-gantt-view--scroll-y-handle',
                        onScroll: $xeGanttView.triggerVirtualScrollYEvent
                    }, [
                        h('div', {
                            ref: refScrollYSpaceElem,
                            class: 'vxe-gantt-view--scroll-y-space'
                        })
                    ]),
                    h('div', {
                        class: 'vxe-gantt-view--scroll-y-handle-appearance'
                    })
                ]),
                h('div', {
                    ref: refScrollYBottomCornerElem,
                    class: 'vxe-gantt-view--scroll-y-bottom-corner'
                })
            ]);
        };
        const renderViewport = () => {
            return h('div', {
                class: 'vxe-gantt-view--viewport-wrapper'
            }, [
                h(GanttViewHeaderComponent),
                h(GanttViewBodyComponent),
                h(GanttViewFooterComponent)
            ]);
        };
        const renderBody = () => {
            const scrollbarYToLeft = computeScrollbarYToLeft.value;
            return h('div', {
                class: 'vxe-gantt-view--layout-wrapper'
            }, scrollbarYToLeft
                ? [
                    renderScrollY(),
                    renderViewport()
                ]
                : [
                    renderViewport(),
                    renderScrollY()
                ]);
        };
        const renderVN = () => {
            const { overflowX, overflowY, scrollXLoad, scrollYLoad } = reactData;
            const scrollbarXToTop = computeScrollbarXToTop.value;
            return h('div', {
                ref: refElem,
                class: ['vxe-gantt-view', {
                        'is--scroll-y': overflowY,
                        'is--scroll-x': overflowX,
                        'is--virtual-x': scrollXLoad,
                        'is--virtual-y': scrollYLoad
                    }]
            }, [
                h('div', {
                    class: 'vxe-gantt-view--render-wrapper'
                }, scrollbarXToTop
                    ? [
                        renderScrollX(),
                        renderBody()
                    ]
                    : [
                        renderBody(),
                        renderScrollX()
                    ]),
                h('div', {
                    class: 'vxe-gantt-view--render-vars'
                }, [
                    h('div', {
                        ref: refColInfoElem,
                        class: 'vxe-gantt-view--column-info'
                    })
                ])
            ]);
        };
        onMounted(() => {
            globalEvents.on($xeGanttView, 'resize', handleGlobalResizeEvent);
        });
        onBeforeUnmount(() => {
            XEUtils.assign(reactData, createReactData());
            XEUtils.assign(internalData, createInternalData());
        });
        onUnmounted(() => {
            globalEvents.off($xeGanttView, 'keydown');
        });
        $xeGanttView.renderVN = renderVN;
        provide('$xeGanttView', $xeGanttView);
        return $xeGanttView;
    },
    render() {
        return this.renderVN();
    }
});