UNPKG

vxe-gantt

Version:
500 lines (461 loc) 21.5 kB
import { h, inject, VNode, ref, Ref, onMounted, onUnmounted } from 'vue' import { defineVxeComponent } from '../../ui/src/comp' import { VxeUI } from '@vxe-ui/core' import XEUtils from 'xe-utils' import { getCellRestHeight, hasMilestoneTask, getTaskType, hasSubviewTask } from './util' import { getStringValue, isEnableConf, hasEnableConf } from '../../ui/src/utils' import type { VxeComponentStyleType } from 'vxe-pc-ui' import type { VxeTableConstructor, VxeTableMethods, VxeTablePrivateMethods } from 'vxe-table' import type { VxeGanttViewConstructor, VxeGanttViewPrivateMethods, VxeGanttConstructor, VxeGanttPrivateMethods } from '../../../types' const { getIcon, renderEmptyElement } = VxeUI const sourceType = 'gantt' const viewType = 'chart' export default defineVxeComponent({ name: 'VxeGanttViewChart', setup () { const $xeGantt = inject('$xeGantt', {} as (VxeGanttConstructor & VxeGanttPrivateMethods)) const $xeGanttView = inject('$xeGanttView', {} as VxeGanttViewConstructor & VxeGanttViewPrivateMethods) const { props: ganttProps, context: ganttContext, reactData: ganttReactData, internalData: ganttInternalData } = $xeGantt const { reactData: ganttViewReactData, internalData: ganttViewInternalData } = $xeGanttView const { computeProgressField, computeTitleField, computeTypeField, computeTaskBarOpts, computeScaleUnit, computeTaskLinkOpts, computeTaskBarMilestoneOpts, computeTaskBarSubviewOpts } = $xeGantt.getComputeMaps() const { computeNowLineLeft } = $xeGanttView.getComputeMaps() const refElem = ref<HTMLDivElement>() const refTaskWrapperElem = ref() as Ref<HTMLDivElement> const refChartBeforeWrapperElem = ref() as Ref<HTMLDivElement> const refChartAfterWrapperElem = ref() as Ref<HTMLDivElement> const refNowLineElem = ref() as Ref<HTMLDivElement> const renderTaskBar = ($xeTable: VxeTableConstructor & VxeTableMethods & VxeTablePrivateMethods, row: any, rowid: string, rowIndex: number, $rowIndex: number, _rowIndex: number, rowChildren: any[], isExpandTree: boolean) => { const tableReactData = $xeTable.reactData const { resizeHeightFlag, pendingRowFlag } = tableReactData const tableInternalData = $xeTable.internalData const { fullAllDataRowIdData, pendingRowMaps } = tableInternalData const { computeCellOpts, computeRowOpts, computeDefaultRowHeight } = $xeTable.getComputeMaps() const cellOpts = computeCellOpts.value const rowOpts = computeRowOpts.value const defaultRowHeight = computeDefaultRowHeight.value const { computeTreeOpts } = $xeTable.getComputeMaps() const treeOpts = computeTreeOpts.value const childrenField = treeOpts.children || treeOpts.childrenField const ganttSlots = $xeGantt.context.slots const taskBarSlot = ganttSlots.taskBar || ganttSlots['task-bar'] const taskBarOverviewSlot = ganttSlots.taskBarOverview || ganttSlots['task-bar-overview'] const { treeConfig, taskBarMilestoneConfig, taskBarSubviewConfig } = ganttProps const { activeLink, activeBarRowid } = ganttReactData const titleField = computeTitleField.value const progressField = computeProgressField.value const typeField = computeTypeField.value const taskBarOpts = computeTaskBarOpts.value const taskBarMilestoneOpts = computeTaskBarMilestoneOpts.value const taskBarSubviewOpts = computeTaskBarSubviewOpts.value const { showOverview, barStyle: subBarStyle } = taskBarSubviewOpts const scaleUnit = computeScaleUnit.value const barParams = { $gantt: $xeGantt, row, scaleType: scaleUnit } const { showProgress, showContent, contentMethod, barStyle, moveable, showTooltip } = taskBarOpts const isBarRowStyle = XEUtils.isFunction(barStyle) const barStyObj = (barStyle ? (isBarRowStyle ? barStyle(barParams) || undefined : barStyle) : {}) || {} const { round } = barStyObj const rowRest = fullAllDataRowIdData[rowid] || {} const cellHeight = resizeHeightFlag ? getCellRestHeight(rowRest, cellOpts, rowOpts, defaultRowHeight) : 0 let title = getStringValue(XEUtils.get(row, titleField)) const progressValue = showProgress ? Math.min(100, Math.max(0, XEUtils.toNumber(XEUtils.get(row, progressField)))) : 0 const renderTaskType = getTaskType(XEUtils.get(row, typeField)) const vbStyle: VxeComponentStyleType = {} const vpStyle: VxeComponentStyleType = { width: `${progressValue || 0}%` } if (isBarRowStyle) { const { bgColor, completedBgColor } = barStyObj if (bgColor) { vbStyle.backgroundColor = bgColor } if (completedBgColor) { vpStyle.backgroundColor = completedBgColor } } const ctParams = { $gantt: $xeGantt, source: sourceType, type: viewType, scaleType: scaleUnit, row, $rowIndex, rowIndex, _rowIndex } let cbVNs: VNode[] = [] if ($xeGantt.renderGanttTaskBarContent) { cbVNs = $xeGantt.renderGanttTaskBarContent(ctParams, { $gantt: $xeGantt, $table: $xeTable, rowid }) } else { const isMilestone = !!(hasEnableConf(taskBarMilestoneConfig, taskBarMilestoneOpts) && hasMilestoneTask(renderTaskType)) const isSubview = !!(hasEnableConf(taskBarSubviewConfig, taskBarSubviewOpts) && hasSubviewTask(renderTaskType)) if (contentMethod) { title = getStringValue(contentMethod({ row, title, scaleType: scaleUnit })) } const ctOns: { onMouseover?: any onMouseleave?: any } = {} if (showTooltip) { ctOns.onMouseover = (evnt: MouseEvent) => { const { dragBarRow } = ganttInternalData const ttParams = Object.assign({ $event: evnt }, ctParams) if (!dragBarRow) { $xeGantt.triggerTaskBarTooltipEvent(evnt, ttParams) } $xeGantt.dispatchEvent('task-bar-mouseenter', ttParams, evnt) } ctOns.onMouseleave = (evnt: MouseEvent) => { const { dragBarRow } = ganttInternalData const ttParams = Object.assign({ $event: evnt }, ctParams) if (!dragBarRow) { $xeGantt.handleTaskBarTooltipLeaveEvent(evnt, ttParams) } $xeGantt.dispatchEvent('task-bar-mouseleave', ttParams, evnt) } } if (isSubview && treeConfig && rowChildren && rowChildren.length) { if (isExpandTree) { if (showOverview) { cbVNs.push( h('div', { key: 'vcso', class: 'vxe-gantt-view--chart-subview-wrapper is--overview' }, [ h('div', { key: rowid, rowid: rowid, class: ['vxe-gantt-view--chart-subview-row', { 'is--progress': showProgress, 'is--round': round, 'is--move': moveable }] }, [ h('div', { rowid: rowid, class: [taskBarOverviewSlot ? 'vxe-gantt-view--chart-subview-custom-bar' : 'vxe-gantt-view--chart-subview-bar', `is--${renderTaskType}`] }, [ taskBarOverviewSlot ? h('div', { key: 'cbc', class: 'vxe-gantt-view--chart-subview-custom-bar-content-wrapper' }, $xeGantt.callSlot(taskBarOverviewSlot, barParams)) : h('div', { class: 'vxe-gantt-view--chart-subview-bar-content-wrapper' }, [ showContent ? h('div', { class: 'vxe-gantt-view--chart-content' }, title) : renderEmptyElement($xeGantt) ]) ]) ]) ]) ) } } else { const cbcVNs: VNode[] = [] XEUtils.eachTree(rowChildren, childRow => { const childBarParams = { $gantt: $xeGantt, row: childRow, scaleType: scaleUnit } const childBarStyObj = (barStyle ? (isBarRowStyle ? barStyle(childBarParams) : barStyle) : {}) || {} const { round } = childBarStyObj const childRowid = $xeTable.getRowid(childRow) let childTitle = getStringValue(XEUtils.get(childRow, titleField)) const childProgressValue = showProgress ? Math.min(100, Math.max(0, XEUtils.toNumber(XEUtils.get(childRow, progressField)))) : 0 const childRenderTaskType = getTaskType(XEUtils.get(childRow, typeField)) const isChildSubview = !!(hasEnableConf(taskBarSubviewConfig, taskBarSubviewOpts) && hasSubviewTask(childRenderTaskType)) if (isChildSubview) { return } const childVbStyle: VxeComponentStyleType = {} const childVpStyle: VxeComponentStyleType = { width: `${childProgressValue || 0}%` } if (isBarRowStyle) { const { bgColor, completedBgColor } = childBarStyObj if (bgColor) { childVbStyle.backgroundColor = bgColor } if (completedBgColor) { childVpStyle.backgroundColor = completedBgColor } } const childCtParams = XEUtils.assign({}, ctParams, { row: childRow, rowIndex: $xeTable.getRowIndex(childRow), $rowIndex: $xeTable.getVMRowIndex(childRow), _rowIndex: $xeTable.getVTRowIndex(childRow) }) if (contentMethod) { childTitle = getStringValue(contentMethod({ row: childRow, title: childTitle, scaleType: scaleUnit })) } cbcVNs.push( h('div', { key: childRowid, rowid: childRowid, class: ['vxe-gantt-view--chart-subview-row', `is--${childRenderTaskType}`, { 'is--progress': showProgress, 'is--round': round, 'is--move': moveable, 'row--pending': !!pendingRowFlag && !!pendingRowMaps[childRowid] }] }, [ h('div', { rowid: childRowid, class: [taskBarSlot ? 'vxe-gantt-view--chart-subview-custom-bar' : 'vxe-gantt-view--chart-subview-bar', `is--${childRenderTaskType}`], style: subBarStyle ? (XEUtils.isFunction(subBarStyle) ? subBarStyle(childCtParams) : subBarStyle) : undefined, onClick (evnt: MouseEvent) { evnt.stopPropagation() $xeGantt.handleTaskBarClickEvent(evnt, childCtParams) }, onDblclick (evnt: MouseEvent) { evnt.stopPropagation() $xeGantt.handleTaskBarDblclickEvent(evnt, childCtParams) }, onMousedown (evnt: MouseEvent) { evnt.stopPropagation() if ($xeGantt.handleTaskBarMousedownEvent) { $xeGantt.handleTaskBarMousedownEvent(evnt, childCtParams) } } }, [ taskBarSlot ? h('div', { key: 'cbc', class: 'vxe-gantt-view--chart-subview-custom-bar-content-wrapper', ...ctOns }, $xeGantt.callSlot(taskBarSlot, childCtParams)) : h('div', { class: 'vxe-gantt-view--chart-subview-bar-content-wrapper', ...ctOns }, [ showProgress ? h('div', { class: 'vxe-gantt-view--chart-progress', style: childVpStyle }) : renderEmptyElement($xeGantt), showContent ? h('div', { class: 'vxe-gantt-view--chart-content' }, childTitle) : renderEmptyElement($xeGantt) ]) ]) ]) ) }, { children: childrenField }) cbVNs.push( h('div', { key: 'vcsc', class: 'vxe-gantt-view--chart-subview-wrapper is--inline' }, cbcVNs) ) } } else { if (taskBarSlot) { cbVNs.push( h('div', { key: 'cbc', class: 'vxe-gantt-view--chart-custom-bar-content-wrapper', ...ctOns }, $xeGantt.callSlot(taskBarSlot, barParams)) ) } else if (isMilestone) { const { icon, iconStatus, iconStyle } = taskBarMilestoneOpts const tbmParams = { $gantt: $xeGantt, row } cbVNs.push( h('div', { key: 'vcm', class: 'vxe-gantt-view--chart-milestone-wrapper', ...ctOns }, [ h('div', { class: ['vxe-gantt-view--chart-milestone-icon', iconStatus ? `theme--${XEUtils.isFunction(iconStatus) ? iconStatus(tbmParams) : iconStatus}` : ''], style: iconStyle ? Object.assign({}, XEUtils.isFunction(iconStyle) ? iconStyle(tbmParams) : iconStyle) : undefined }, [ h('i', { class: (icon ? (XEUtils.isFunction(icon) ? icon(tbmParams) : icon) : '') || getIcon().GANTT_VIEW_TASK_MILESTONE }) ]), showContent ? h('div', { class: 'vxe-gantt-view--chart-milestone-content' }, title) : renderEmptyElement($xeGantt) ]) ) } else { cbVNs.push( h('div', { key: 'vbc', class: 'vxe-gantt-view--chart-bar-content-wrapper', ...ctOns }, [ showProgress ? h('div', { class: 'vxe-gantt-view--chart-progress', style: vpStyle }) : renderEmptyElement($xeGantt), showContent ? h('div', { class: 'vxe-gantt-view--chart-content' }, title) : renderEmptyElement($xeGantt) ]) ) } } } return h('div', { key: rowid, rowid, class: ['vxe-gantt-view--chart-row', `is--${renderTaskType}`, { 'is--progress': showProgress, 'row--pending': !!pendingRowFlag && !!pendingRowMaps[rowid], 'is--round': round, 'is--move': moveable }], style: { height: `${cellHeight}px` }, onDragstart (evnt) { if (ganttInternalData.dragBarRow) { evnt.preventDefault() } }, onContextmenu (evnt) { $xeGantt.handleTaskBarContextmenuEvent(evnt, ctParams) } }, [ h('div', { class: [taskBarSlot ? 'vxe-gantt-view--chart-custom-bar' : 'vxe-gantt-view--chart-bar', `is--${renderTaskType}`, { 'is--active': activeBarRowid === rowid, 'active--link': activeLink && (rowid === `${activeLink.from}` || rowid === `${activeLink.to}`) }], style: vbStyle, rowid, onClick (evnt: MouseEvent) { $xeGantt.handleTaskBarClickEvent(evnt, barParams) }, onDblclick (evnt: MouseEvent) { $xeGantt.handleTaskBarDblclickEvent(evnt, barParams) }, onMousedown (evnt: MouseEvent) { if ($xeGantt.handleTaskBarMousedownEvent) { $xeGantt.handleTaskBarMousedownEvent(evnt, barParams) } } }, cbVNs) ]) } const renderTaskRows = ($xeTable: VxeTableConstructor & VxeTableMethods & VxeTablePrivateMethods, tableData: any[]) => { const tableProps = $xeTable.props const { treeConfig } = tableProps const tableReactData = $xeTable.reactData const { treeExpandedFlag } = tableReactData const tableInternalData = $xeTable.internalData const { fullAllDataRowIdData, treeExpandedMaps } = tableInternalData const { computeTreeOpts } = $xeTable.getComputeMaps() const treeOpts = computeTreeOpts.value const { transform } = treeOpts const childrenField = treeOpts.children || treeOpts.childrenField const { scrollYLoad } = ganttViewReactData const trVNs: VNode[] = [] tableData.forEach((row, $rowIndex) => { const rowid = $xeTable.getRowid(row) const rowRest = fullAllDataRowIdData[rowid] || {} let rowIndex = $rowIndex let _rowIndex = -1 if (rowRest) { rowIndex = rowRest.index _rowIndex = rowRest._index } let isExpandTree = false let rowChildren: any[] = [] if (treeConfig) { rowChildren = row[childrenField] isExpandTree = !!treeExpandedFlag && rowChildren && rowChildren.length > 0 && !!treeExpandedMaps[rowid] } trVNs.push(renderTaskBar($xeTable, row, rowid, rowIndex, $rowIndex, _rowIndex, rowChildren, isExpandTree)) // 如果是树形表格 if (treeConfig && isExpandTree && !scrollYLoad && !transform) { trVNs.push(...renderTaskRows($xeTable, rowChildren)) } }) return trVNs } const renderVN = () => { const $xeTable = ganttViewInternalData.xeTable const { slots: ganttSlots } = ganttContext const { dragLinkFromStore } = ganttReactData const { tableData } = ganttViewReactData const taskLinkOpts = computeTaskLinkOpts.value const taskBarOpts = computeTaskBarOpts.value const nowLineLeft = computeNowLineLeft.value const { isCurrent, isHover } = taskLinkOpts const { linkCreatable } = taskBarOpts const taskNowLineSlot = ganttSlots.taskNowLine || ganttSlots['task-now-line'] return h('div', { ref: refElem, class: ['vxe-gantt-view--chart-wrapper', { 'is--cl-drag': dragLinkFromStore.rowid }] }, [ nowLineLeft > 0 ? h('div', { ref: refNowLineElem, class: 'vxe-gantt-view--chart-now-line', style: { left: nowLineLeft + 'px' } }, taskNowLineSlot ? taskNowLineSlot({}) : []) : renderEmptyElement($xeGantt), $xeGantt.renderGanttTaskChartBefores ? h('div', { ref: refChartBeforeWrapperElem, class: ['vxe-gantt-view--chart-before-wrapper', { 'link--current': isCurrent, 'link--hover': isHover }] }, $xeTable && isEnableConf(taskLinkOpts) ? $xeGantt.renderGanttTaskChartBefores() : []) : renderEmptyElement($xeGantt), h('div', { ref: refTaskWrapperElem, class: ['vxe-gantt-view--chart-task-wrapper', { 'link--current': isCurrent, 'link--create': linkCreatable }] }, $xeTable ? renderTaskRows($xeTable, tableData) : []), $xeGantt.renderGanttTaskChartAfters ? h('div', { ref: refChartAfterWrapperElem, class: 'vxe-gantt-view--chart-after-wrapper' }, $xeTable && isEnableConf(taskLinkOpts) ? $xeGantt.renderGanttTaskChartAfters() : []) : renderEmptyElement($xeGantt) ]) } onMounted(() => { const { elemStore } = ganttViewInternalData const prefix = 'main-chart-' elemStore[`${prefix}now-line`] = refNowLineElem elemStore[`${prefix}task-wrapper`] = refTaskWrapperElem elemStore[`${prefix}before-wrapper`] = refChartBeforeWrapperElem elemStore[`${prefix}after-wrapper`] = refChartAfterWrapperElem }) onUnmounted(() => { const { elemStore } = ganttViewInternalData const prefix = 'main-chart-' elemStore[`${prefix}now-line`] = null elemStore[`${prefix}task-wrapper`] = null elemStore[`${prefix}before-wrapper`] = null elemStore[`${prefix}after-wrapper`] = null }) return renderVN } })