vxe-gantt
Version:
A vue based gantt component
1,042 lines (958 loc) • 36.4 kB
text/typescript
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()
}
})