diff --git a/CHANGELOG.md b/CHANGELOG.md index c53c655eec8d1b1fddf034bcf3dac72bffd30e57..584d2c9401abea54304508b9eb6f870cfc27d553 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,15 @@ ## [Unreleased] +### Added + +- 新增甘特图滑块的链接线绘制,新增部件参数linkdatasourcetype(链接数据获取模式)、linkappdataentityname(链接数据集应用实体名称)、linkappdedatasetname(链接应用实体结果集名称)、linknodedataname(链接节点数据属性名称)、fromdataitemname(链接起始数据项属性名称)、todataitemname(链接结束数据项属性名称) +- 新增树节点数据拖拽状态属性 + ### Changed - 更新drtab流布局导航栏样式 +- 适配多数据部件刷新模式,每次刷新后更新甘特图选中节点的样式 ## Fixed diff --git a/src/control/gantt/gantt.scss b/src/control/gantt/gantt.scss index 3695f64dd2fe17f79c15db0ad2a18dfeb0b00b3f..900990c9eb4ee5e442ed3c1cbc5e40c1fbbb6858 100644 --- a/src/control/gantt/gantt.scss +++ b/src/control/gantt/gantt.scss @@ -33,6 +33,11 @@ // 警示色 --gantt-warning: var(--ibiz-color-warning); + // 链接线颜色 + + --gantt-color-linking: #{getCssVar(color, primary)}; + --gantt-link-path-hover: #{getCssVar(color, primary)}; + .xg-table-body .xg-table-row { cursor: pointer; @@ -161,6 +166,16 @@ border: none; box-shadow: getCssVar('shadow', 'elevated'); } + + @include e('link-path-popover') { + z-index: 300 !important; + width: auto; + min-width: 300px; + padding: 12px; + border: none; + box-shadow: getCssVar('shadow', 'elevated'); + } + .#{bem('tree-grid-ex-field-column','','ellipsis')} { .#{bem('tree-grid-ex-field-column-text-container')} { min-width: 0; @@ -177,5 +192,5 @@ .xg-view-toolbar-switch-action { // 文本 --gantt-text-switch-item: var(--ibiz-color-text-3); - --gantt-bg-hover-switch-item: var(--ibiz-color-bg-0);; + --gantt-bg-hover-switch-item: var(--ibiz-color-bg-0); } diff --git a/src/control/gantt/gantt.tsx b/src/control/gantt/gantt.tsx index c8ef52fef75cd6088cbb6c56dd824a226b59a866..195e18920b0deb465eef14fe450f9fbaffa6ee7f 100644 --- a/src/control/gantt/gantt.tsx +++ b/src/control/gantt/gantt.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable no-unused-vars */ /* eslint-disable no-restricted-syntax */ @@ -21,6 +22,7 @@ import { getCurrentInstance, ref, Ref, + nextTick, } from 'vue'; import { IDEGantt, @@ -99,6 +101,8 @@ export const GanttControl = defineComponent({ // 滑块模态 let overlay: null | IOverlayPopoverContainer = null; + // 滑块链接线模态 + let linkOverlay: null | IOverlayPopoverContainer = null; // 滑块移动 const sliderMove = ref(false); @@ -107,6 +111,7 @@ export const GanttControl = defineComponent({ const iBizIcon = resolveComponent('IBizIcon'); let forbidClick: boolean = false; + let forbidOperation: boolean = false; const selection: IGanttNodeData[] = []; @@ -209,6 +214,28 @@ export const GanttControl = defineComponent({ return ibiz.i18n.getLang().toLowerCase(); }); + // 监听选中数据,操作甘特来处理界面回显选中效果。 + watch( + [() => ganttRef.value, (): IGanttNodeData[] => c.state.selectedData], + ([table, newVal]) => { + if (forbidOperation || !table) return; + nextTick(() => { + if (c.state.singleSelect) { + // 单选,选中效果回显。 + if (newVal[0]) { + ganttRef.value?.setSelected(newVal[0]); + } else { + ganttRef.value?.setSelected(); + } + } else { + selection.length = 0; + selection.push(...newVal); + newVal.forEach(item => ganttRef.value?.setChecked(item, true)); + } + }); + }, + ); + /** * 查找对应节点的布局面板 * @@ -219,13 +246,38 @@ export const GanttControl = defineComponent({ let layoutPanel: IPanel | undefined; const nodeModel = c.getNodeModel(id); nodeModel?.controlRenders?.forEach(renderItem => { - if (renderItem.renderType === 'LAYOUTPANEL') { + if ( + renderItem.renderType === 'LAYOUTPANEL' && + (renderItem.id || '').split('_')[0] !== 'nodelinkrender' + ) { layoutPanel = renderItem.layoutPanel; } }); return layoutPanel; }; + /** + * 查找对应节点链接的布局面板 + * + * @param {string} id + * @return {*} {(IPanel | undefined)} + */ + const findNodeLinkLayoutPanel = (id: string): IPanel | undefined => { + let layoutPanel: IPanel | undefined; + const nodeModel = c.getNodeModel(id); + nodeModel?.controlRenders?.find(renderItem => { + if ( + renderItem.renderType === 'LAYOUTPANEL' && + (renderItem.id || '').split('_')[0] === 'nodelinkrender' + ) { + layoutPanel = renderItem.layoutPanel; + return true; + } + return false; + }); + return layoutPanel; + }; + /** * 多选时选中节点变更 * @@ -233,6 +285,7 @@ export const GanttControl = defineComponent({ * @param {IGanttNodeData} item 当前数据 */ const onCheck = (state: boolean, item: IGanttNodeData) => { + forbidOperation = true; if (state) { selection.push(item); } else { @@ -244,6 +297,9 @@ export const GanttControl = defineComponent({ } } c.setSelection(selection); + setTimeout(() => { + forbidOperation = false; + }, 200); }; /** @@ -257,11 +313,13 @@ export const GanttControl = defineComponent({ sliderMove.value = false; return; } + forbidOperation = true; c.onTreeNodeClick(nodeData, evt); forbidClick = true; setTimeout(() => { forbidClick = false; + forbidOperation = false; }, 200); }; @@ -802,6 +860,61 @@ export const GanttControl = defineComponent({ } }; + /** + * 打开链接 popover + * + * @param {IGanttNodeData} item + * @param {MouseEvent} evt + * @return {*} + */ + const openLinkPathPopover = async ( + row: IGanttNodeData, + panelData: IData, + evt: MouseEvent, + ): Promise => { + const panel = findNodeLinkLayoutPanel(row._nodeId); + if (linkOverlay || !panel) { + return; + } + const component = renderNodePanel(panel, panelData); + linkOverlay = ibiz.overlay.createPopover( + (modal: IModal): VNode => { + return h(component, { modal }); + }, + undefined, + { + width: 'auto', + height: 'auto', + noArrow: true, + autoClose: true, + modalClass: ns.e('link-path-popover'), + }, + ); + linkOverlay?.present(evt.currentTarget as HTMLElement); + + await linkOverlay.onWillDismiss(); + linkOverlay = null; + }; + + /** + * 处理点击链接线 + * + * @param {IData} [_link] + * @param {MouseEvent} [_event] + */ + const handleClickLink = (_link?: IData, _event?: MouseEvent): void => { + if (_link && _event) { + const curLink = c.state.links.find(_item => _item._uuid === _link.id); + if (curLink) { + openLinkPathPopover( + curLink._fromData as IGanttNodeData, + curLink._deData, + _event, + ); + } + } + }; + return { c, ns, @@ -825,6 +938,7 @@ export const GanttControl = defineComponent({ allowDrag, handleDrop, onHeaderDragend, + handleClickLink, }; }, render() { @@ -841,6 +955,7 @@ export const GanttControl = defineComponent({ ref='ganttRef' data-id='_id' data={this.data} + links={this.c.state.links} row-height={46} expand-all={false} headerDrag={true} @@ -849,9 +964,18 @@ export const GanttControl = defineComponent({ children='_children' leaf='_leaf' expand-key='_defaultExpand' + link-props={{ + fromKey: '_from', + toKey: '_to', + linkKey: '_uuid', + }} locale={this.locale} unit={this.c.state.unit} - draggable={{ level: 'all', draggable: true }} + draggable={{ + level: 'all', + draggable: false, + draggableStateKey: '_draggable', + }} allow-drop={this.allowDrop} allow-drag={this.allowDrag} onNodeDrop={this.handleDrop} @@ -863,6 +987,7 @@ export const GanttControl = defineComponent({ onRowChecked={this.onCheck} onHeaderDragend={this.onHeaderDragend} onMoveSlider={this.onSliderMove} + onClickLink={this.handleClickLink} primaryColor={this.ganttStyle.primaryColor} headerStyle={{ textColor: this.ganttStyle.textColor,