vxe-gantt
Version:
A vue based gantt component
430 lines (429 loc) • 24.3 kB
JavaScript
import { h, inject, 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';
const { getIcon, renderEmptyElement } = VxeUI;
const sourceType = 'gantt';
const viewType = 'chart';
export default defineVxeComponent({
name: 'VxeGanttViewChart',
setup() {
const $xeGantt = inject('$xeGantt', {});
const $xeGanttView = inject('$xeGanttView', {});
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();
const refTaskWrapperElem = ref();
const refChartBeforeWrapperElem = ref();
const refChartAfterWrapperElem = ref();
const refNowLineElem = ref();
const renderTaskBar = ($xeTable, row, rowid, rowIndex, $rowIndex, _rowIndex, rowChildren, isExpandTree) => {
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 = {};
const vpStyle = {
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 = [];
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 = {};
if (showTooltip) {
ctOns.onMouseover = (evnt) => {
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) => {
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 = [];
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 = {};
const childVpStyle = {
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) {
evnt.stopPropagation();
$xeGantt.handleTaskBarClickEvent(evnt, childCtParams);
},
onDblclick(evnt) {
evnt.stopPropagation();
$xeGantt.handleTaskBarDblclickEvent(evnt, childCtParams);
},
onMousedown(evnt) {
evnt.stopPropagation();
if ($xeGantt.handleTaskBarMousedownEvent) {
$xeGantt.handleTaskBarMousedownEvent(evnt, childCtParams);
}
}
}, [
taskBarSlot
? h('div', Object.assign({ key: 'cbc', class: 'vxe-gantt-view--chart-subview-custom-bar-content-wrapper' }, ctOns), $xeGantt.callSlot(taskBarSlot, childCtParams))
: h('div', Object.assign({ 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', Object.assign({ 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', Object.assign({ 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', Object.assign({ 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) {
$xeGantt.handleTaskBarClickEvent(evnt, barParams);
},
onDblclick(evnt) {
$xeGantt.handleTaskBarDblclickEvent(evnt, barParams);
},
onMousedown(evnt) {
if ($xeGantt.handleTaskBarMousedownEvent) {
$xeGantt.handleTaskBarMousedownEvent(evnt, barParams);
}
}
}, cbVNs)
]);
};
const renderTaskRows = ($xeTable, tableData) => {
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 = [];
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 = [];
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;
}
});