UNPKG

vxe-gantt

Version:
1,042 lines (958 loc) 36.4 kB
import { h, ref, reactive, nextTick, inject, watch, provide, onMounted, onUnmounted } from 'vue' import { defineVxeComponent } from '../../ui/src/comp' import { setScrollTop, setScrollLeft, removeClass, addClass } from '../../ui/src/dom' import { VxeUI } from '@vxe-ui/core' import { getRefElem } from './util' import XEUtils from 'xe-utils' import GanttViewHeaderComponent from './gantt-header' import GanttViewBodyComponent from './gantt-body' import GanttViewFooterComponent from './gantt-footer' import type { VxeGanttViewConstructor, GanttViewReactData, GanttViewPrivateRef, VxeGanttDefines, VxeGanttViewPrivateMethods, GanttViewInternalData, VxeGanttViewMethods, GanttViewPrivateComputed, VxeGanttConstructor, VxeGanttPrivateMethods } from '../../../types' const { globalEvents } = VxeUI function createInternalData (): GanttViewInternalData { return { xeTable: null, startMaps: {}, endMaps: {}, chartMaps: {}, elemStore: {}, // 存放横向 X 虚拟滚动相关的信息 scrollXStore: { preloadSize: 0, offsetSize: 0, visibleSize: 0, visibleStartIndex: 0, visibleEndIndex: 0, startIndex: 0, endIndex: 0 }, // 存放纵向 Y 虚拟滚动相关信息 scrollYStore: { preloadSize: 0, offsetSize: 0, visibleSize: 0, visibleStartIndex: 0, visibleEndIndex: 0, startIndex: 0, endIndex: 0 } } } const maxYHeight = 5e6 // const maxXWidth = 5e6 export default defineVxeComponent({ name: 'VxeGanttView', setup (props, context) { const xID = XEUtils.uniqueId() const $xeGantt = inject('$xeGantt', {} as (VxeGanttConstructor & VxeGanttPrivateMethods)) const { computeTaskOpts, computeStartField, computeEndField, computeScrollbarOpts, computeScrollbarXToTop, computeScrollbarYToLeft } = $xeGantt.getComputeMaps() const refElem = ref<HTMLDivElement>() const refScrollXVirtualElem = ref<HTMLDivElement>() const refScrollYVirtualElem = ref<HTMLDivElement>() const refScrollXHandleElem = ref<HTMLDivElement>() const refScrollXLeftCornerElem = ref<HTMLDivElement>() const refScrollXRightCornerElem = ref<HTMLDivElement>() const refScrollYHandleElem = ref<HTMLDivElement>() const refScrollYTopCornerElem = ref<HTMLDivElement>() const refScrollXWrapperElem = ref<HTMLDivElement>() const refScrollYWrapperElem = ref<HTMLDivElement>() const refScrollYBottomCornerElem = ref<HTMLDivElement>() const refScrollXSpaceElem = ref<HTMLDivElement>() const refScrollYSpaceElem = ref<HTMLDivElement>() const refColInfoElem = ref<HTMLDivElement>() const reactData = reactive<GanttViewReactData>({ // 是否启用了横向 X 可视渲染方式加载 scrollXLoad: false, // 是否启用了纵向 Y 可视渲染方式加载 scrollYLoad: false, // 是否存在纵向滚动条 overflowY: true, // 是否存在横向滚动条 overflowX: true, // 纵向滚动条的宽度 scrollbarWidth: 0, // 横向滚动条的高度 scrollbarHeight: 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: 20 }) const internalData = createInternalData() const refMaps: GanttViewPrivateRef = { refElem } const computeMaps: GanttViewPrivateComputed = { } const $xeGanttView = { xID, props, context, reactData, internalData, getRefMaps: () => refMaps, getComputeMaps: () => computeMaps } as unknown as VxeGanttViewConstructor & VxeGanttViewPrivateMethods const parseStringDate = (dateValue: any) => { const taskOpts = computeTaskOpts.value const { dateFormat } = taskOpts return XEUtils.toStringDate(dateValue, dateFormat || null) } const handleParseColumn = () => { const ganttProps = $xeGantt.props const ganttReactData = $xeGantt.reactData const { treeConfig } = ganttProps const { taskScaleList } = ganttReactData const { minViewDate, maxViewDate } = reactData const minScale = XEUtils.last(taskScaleList) const fullCols: VxeGanttDefines.ViewColumn[] = [] const groupCols: VxeGanttDefines.HeaderColumn[] = [] if (minScale && minViewDate && maxViewDate) { const minSType = minScale.type const weekScale = taskScaleList.find(item => item.type === 'week') let gapTime = 1000 * 60 * 60 * 24 switch (minScale.type) { case 'hour': gapTime = 1000 * 60 * 60 break case 'minute': gapTime = 1000 * 60 break case 'second': gapTime = 1000 break default: { break } } const currTime = minViewDate.getTime() const diffDayNum = maxViewDate.getTime() - minViewDate.getTime() const countSize = Math.max(5, Math.floor(diffDayNum / gapTime) + 1) switch (minScale.type) { case 'day': case 'date': if (diffDayNum > (1000 * 60 * 60 * 24 * 366 * 3)) { reactData.tableColumn = [] reactData.headerGroups = [] return } break case 'hour': if (diffDayNum > (1000 * 60 * 60 * 24 * 31 * 3)) { reactData.tableColumn = [] reactData.headerGroups = [] return } break case 'minute': if (diffDayNum > (1000 * 60 * 60 * 24 * 3)) { reactData.tableColumn = [] reactData.headerGroups = [] return } break case 'second': if (diffDayNum > (1000 * 60 * 60 * 3)) { reactData.tableColumn = [] reactData.headerGroups = [] return } break } const renderListMaps: Record<VxeGanttDefines.ColumnScaleType, VxeGanttDefines.ViewColumn[]> = { year: [], quarter: [], month: [], week: [], day: [], date: [], hour: [], minute: [], second: [] } const tempTypeMaps: Record<VxeGanttDefines.ColumnScaleType, Record<string, VxeGanttDefines.ViewColumn>> = { year: {}, quarter: {}, month: {}, week: {}, day: {}, date: {}, hour: {}, minute: {}, second: {} } const handleData = (type: VxeGanttDefines.ColumnScaleType, colMaps: Record<VxeGanttDefines.ColumnScaleType, VxeGanttDefines.ViewColumn>, minCol: VxeGanttDefines.ViewColumn) => { if (minSType === 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) } } for (let i = 0; i < countSize; i++) { const itemDate = new Date(currTime + (i * gapTime)) const [yyyy, MM, dd, HH, mm, ss] = XEUtils.toDateString(itemDate, 'yyyy-M-d-H-m-s').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) const dateObj: VxeGanttDefines.ScaleDateObj = { yy: yyyy, M: MM, d: dd, H: HH, m: mm, s: ss, q, W, E, e } const colMaps: Record<VxeGanttDefines.ColumnScaleType, VxeGanttDefines.ViewColumn> = { year: { field: yyyy, title: yyyy, params: dateObj }, quarter: { field: `${yyyy}_q${q}`, title: q, params: dateObj }, month: { field: `${yyyy}_${MM}`, title: MM, params: dateObj }, week: { field: `${yyyy}_W${W}`, title: W, params: dateObj }, day: { field: `${yyyy}_${MM}_${dd}_E${E}`, title: E, params: dateObj }, date: { field: `${yyyy}_${MM}_${dd}`, title: dd, params: dateObj }, hour: { field: `${yyyy}_${MM}_${dd}_${HH}`, title: HH, params: dateObj }, minute: { field: `${yyyy}_${MM}_${dd}_${HH}_${mm}`, title: mm, params: dateObj }, second: { field: `${yyyy}_${MM}_${dd}_${HH}_${mm}_${ss}`, title: ss, params: dateObj } } const minCol = colMaps[minSType] if (minScale.level < 19) { handleData('year', colMaps, minCol) } if (minScale.level < 17) { handleData('quarter', colMaps, minCol) } if (minScale.level < 14) { handleData('month', colMaps, minCol) } if (minScale.level < 13) { handleData('week', colMaps, minCol) } if (minScale.level < 11) { handleData('day', colMaps, minCol) } if (minScale.level < 12) { handleData('date', colMaps, minCol) } if (minScale.level < 7) { handleData('hour', colMaps, minCol) } if (minScale.level < 5) { handleData('minute', colMaps, minCol) } fullCols.push(minCol) } taskScaleList.forEach(scaleItem => { if (scaleItem.type === minSType) { 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 }) }) const $xeTable = internalData.xeTable if ($xeTable) { const startField = computeStartField.value const endField = computeEndField.value const { computeTreeOpts } = $xeTable.getComputeMaps() const tableInternalData = $xeTable.internalData const { afterFullData, afterTreeFullData } = tableInternalData const treeOpts = computeTreeOpts.value const { transform } = treeOpts const childrenField = treeOpts.children || treeOpts.childrenField const ctMaps: Record<string, VxeGanttDefines.RowCacheItem> = {} const handleParseRender = (row: any) => { const rowid = $xeTable.getRowid(row) const startValue = XEUtils.get(row, startField) const endValue = XEUtils.get(row, endField) if (startValue && endValue) { const startDate = parseStringDate(startValue) const endDate = parseStringDate(endValue) const oLeftSize = Math.floor((startDate.getTime() - minViewDate.getTime()) / gapTime) const oWidthSize = Math.floor((endDate.getTime() - startDate.getTime()) / gapTime) + 1 ctMaps[rowid] = { row, rowid, oLeftSize, oWidthSize } } } if (treeConfig) { XEUtils.eachTree(afterTreeFullData, handleParseRender, { children: transform ? treeOpts.mapChildrenField : childrenField }) } else { afterFullData.forEach(handleParseRender) } internalData.chartMaps = ctMaps } } reactData.tableColumn = fullCols reactData.headerGroups = groupCols } const handleUpdateData = () => { const ganttProps = $xeGantt.props const { treeConfig } = ganttProps const $xeTable = internalData.xeTable const sdMaps: Record<string, any> = {} const edMaps: Record<string, any> = {} let minDate: Date | null = null let maxDate: Date | null = null if ($xeTable) { const startField = computeStartField.value const endField = computeEndField.value const { computeTreeOpts } = $xeTable.getComputeMaps() const tableInternalData = $xeTable.internalData const { afterFullData, afterTreeFullData } = tableInternalData const treeOpts = computeTreeOpts.value const { transform } = treeOpts const childrenField = treeOpts.children || treeOpts.childrenField const handleMinMaxData = (row: any) => { const startValue = XEUtils.get(row, startField) const endValue = XEUtils.get(row, endField) if (startValue && endValue) { const startDate = parseStringDate(startValue) if (!minDate || minDate.getTime() > startDate.getTime()) { minDate = startDate } const endDate = parseStringDate(endValue) if (!maxDate || maxDate.getTime() < endDate.getTime()) { maxDate = endDate } } } if (treeConfig) { XEUtils.eachTree(afterTreeFullData, handleMinMaxData, { children: transform ? treeOpts.mapChildrenField : childrenField }) } else { afterFullData.forEach(handleMinMaxData) } } reactData.minViewDate = minDate reactData.maxViewDate = maxDate internalData.startMaps = sdMaps internalData.endMaps = edMaps handleParseColumn() } 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) { reactData.scrollbarWidth = scrollbarOpts.width || (yHandleEl.offsetWidth - yHandleEl.clientWidth) || 14 } reactData.overflowY = overflowY overflowX = scrollXWidth > bodyWrapperElem.clientWidth if (xHandleEl) { reactData.scrollbarHeight = scrollbarOpts.height || (xHandleEl.offsetHeight - xHandleEl.clientHeight) || 14 } reactData.overflowX = overflowX } } const updateChart = () => { const { viewCellWidth } = reactData const { elemStore, chartMaps } = internalData const chartWrapper = getRefElem(elemStore['main-chart-wrapper']) if (chartWrapper) { XEUtils.arrayEach(chartWrapper.children, (rowEl) => { const barEl = rowEl.children[0] as HTMLDivElement if (!barEl) { return } const rowid = rowEl.getAttribute('rowid') const rowRest = rowid ? chartMaps[rowid] : null if (rowRest) { barEl.style.left = `${viewCellWidth * rowRest.oLeftSize}px` barEl.style.width = `${viewCellWidth * rowRest.oWidthSize}px` } }) } return nextTick() } const updateStyle = () => { const { scrollbarWidth, scrollbarHeight, tableColumn, headerGroups } = reactData const { elemStore } = internalData const $xeTable = internalData.xeTable const el = refElem.value if (!el || !el.clientHeight) { 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 || (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 if (colInfoElem) { reactData.viewCellWidth = colInfoElem.clientWidth || 40 } let viewTableWidth = reactData.viewCellWidth * tableColumn.length if (bodyScrollElem) { const viewWidth = bodyScrollElem.clientWidth const remainWidth = viewWidth - viewTableWidth if (remainWidth > 0) { reactData.viewCellWidth += Math.floor(remainWidth / tableColumn.length) viewTableWidth = viewWidth } } const headerTableElem = getRefElem(elemStore['main-header-table']) const bodyTableElem = getRefElem(elemStore['main-body-table']) if (headerTableElem) { headerTableElem.style.width = `${viewTableWidth}px` } if (bodyTableElem) { bodyTableElem.style.width = `${viewTableWidth}px` } reactData.scrollXWidth = viewTableWidth return updateChart() } const handleLazyRecalculate = () => { calcScrollbar() updateStyle() updateChart() return nextTick() } // const updateScrollXSpace = () => { // } const updateScrollYSpace = () => { 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 } const bodyChartWrapperElem = getRefElem(elemStore['main-chart-wrapper']) if (bodyTableElem) { bodyTableElem.style.transform = `translate(${reactData.scrollXLeft || 0}px, ${scrollYTop}px)` } if (bodyChartWrapperElem) { bodyChartWrapperElem.style.transform = `translate(${reactData.scrollXLeft || 0}px, ${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` : '' } 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: boolean, isRollY: boolean) => { 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) } // eslint-disable-next-line @typescript-eslint/no-unused-vars const handleScrollEvent = (evnt: Event, isRollY: boolean, isRollX: boolean, scrollTop: number, scrollLeft: number) => { checkLastSyncScroll(isRollX, isRollY) } const ganttViewMethods: VxeGanttViewMethods = { refreshData () { handleUpdateData() return handleLazyRecalculate() }, updateViewData () { const $xeTable = internalData.xeTable if ($xeTable) { const tableReactData = $xeTable.reactData const { tableData } = tableReactData reactData.tableData = tableData } return nextTick() }, connectUpdate ({ $table }) { if ($table) { internalData.xeTable = $table } return nextTick() } } /** * 同步表格滚动 */ const syncTableScrollTop = (scrollTop: number) => { 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: VxeGanttViewPrivateMethods = { handleUpdateStyle: updateStyle, handleLazyRecalculate, handleUpdateCurrentRow (row) { const $xeTable = internalData.xeTable const el = refElem.value if ($xeTable && el) { if (row) { const tableProps = $xeTable.props const { highlightCurrentRow } = tableProps const { computeRowOpts } = $xeTable.getComputeMaps() const rowOpts = computeRowOpts.value if (rowOpts.isCurrent || 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 as HTMLDivElement 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 { elemStore, inVirtualScroll, inHeaderScroll, inFooterScroll } = internalData if (inVirtualScroll) { return } if (inHeaderScroll || inFooterScroll) { return } const wrapperEl = evnt.currentTarget as HTMLDivElement const headerScrollElem = getRefElem(elemStore['main-header-scroll']) const xHandleEl = refScrollXHandleElem.value const yHandleEl = refScrollYHandleElem.value if (headerScrollElem && wrapperEl) { const isRollX = true const isRollY = true const currLeftNum = wrapperEl.scrollLeft const currTopNum = wrapperEl.scrollTop internalData.inBodyScroll = true setScrollLeft(xHandleEl, currLeftNum) setScrollLeft(headerScrollElem, currLeftNum) setScrollTop(yHandleEl, currTopNum) syncTableScrollTop(currTopNum) handleScrollEvent(evnt, isRollY, isRollX, wrapperEl.scrollTop, currLeftNum) } }, 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 { elemStore, inHeaderScroll, inBodyScroll } = internalData if (inHeaderScroll || inBodyScroll) { return } const wrapperEl = evnt.currentTarget as HTMLDivElement 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) handleScrollEvent(evnt, isRollY, isRollX, wrapperEl.scrollTop, currLeftNum) } }, triggerVirtualScrollYEvent (evnt) { const { elemStore, inHeaderScroll, inBodyScroll } = internalData if (inHeaderScroll || inBodyScroll) { return } const wrapperEl = evnt.currentTarget as HTMLDivElement 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) handleScrollEvent(evnt, isRollY, isRollX, currTopNum, wrapperEl.scrollLeft) } }, handleUpdateSXSpace () { const { scrollXLoad, scrollXWidth } = reactData const { elemStore } = internalData const layoutList = ['header', 'body', 'footer'] layoutList.forEach(layout => { const xSpaceElem = getRefElem(elemStore[`main-${layout}-xSpace`]) if (xSpaceElem) { xSpaceElem.style.width = scrollXLoad ? `${scrollXWidth}px` : '' } }) const scrollXSpaceEl = refScrollXSpaceElem.value if (scrollXSpaceEl) { scrollXSpaceEl.style.width = `${scrollXWidth}px` } calcScrollbar() return nextTick() }, handleUpdateSYSpace: 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', { 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', { 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' }) ]) ]) } const tdFlag = ref(0) watch(() => reactData.tableData, () => { tdFlag.value++ }) watch(() => reactData.tableData.length, () => { tdFlag.value++ }) watch(tdFlag, () => { handleUpdateData() }) onMounted(() => { globalEvents.on($xeGanttView, 'resize', handleGlobalResizeEvent) }) onUnmounted(() => { globalEvents.off($xeGanttView, 'keydown') XEUtils.assign(internalData, createInternalData()) }) $xeGanttView.renderVN = renderVN provide('$xeGanttView', $xeGanttView) return $xeGanttView }, render () { return this.renderVN() } })