feat(脑图): 脑图拖拽拦截&空白节点保存&部分 bug 修复

This commit is contained in:
baiqi 2024-06-04 19:45:25 +08:00 committed by 刘瑞斌
parent 7ee89429c7
commit 99328a50e5
14 changed files with 361 additions and 182 deletions

View File

@ -94,9 +94,11 @@
const stepTag = t('ms.minders.stepDesc'); const stepTag = t('ms.minders.stepDesc');
const textTag = t('ms.minders.textDesc'); const textTag = t('ms.minders.textDesc');
const prerequisiteTag = t('ms.minders.precondition'); const prerequisiteTag = t('ms.minders.precondition');
const remarkTag = t('common.remark'); const stepExpectTag = t('ms.minders.stepExpect');
const remarkTag = t('ms.minders.remark');
const descTags = [stepTag, textTag]; const descTags = [stepTag, textTag];
const caseChildTags = [prerequisiteTag, stepTag, textTag, remarkTag]; const caseChildTags = [prerequisiteTag, stepTag, textTag, remarkTag];
const caseOffspringTags = [...caseChildTags, stepTag, stepExpectTag, textTag, remarkTag];
const importJson = ref<MinderJson>({ const importJson = ref<MinderJson>({
root: {} as MinderJsonNode, root: {} as MinderJsonNode,
template: 'default', template: 'default',
@ -110,6 +112,7 @@
updateCaseList: [], updateCaseList: [],
updateModuleList: [], updateModuleList: [],
deleteResourceList: [], deleteResourceList: [],
additionalNodeList: [],
}); });
const templateId = ref(''); const templateId = ref('');
@ -153,6 +156,7 @@
id: 'NONE', id: 'NONE',
text: t('ms.minders.allModule'), text: t('ms.minders.allModule'),
resource: [moduleTag], resource: [moduleTag],
disabled: true,
}, },
}; };
window.minder.importJson(importJson.value); window.minder.importJson(importJson.value);
@ -217,31 +221,31 @@
let remarkNode: MinderJsonNode | undefined; // let remarkNode: MinderJsonNode | undefined; //
const stepNodes: MinderJsonNode[] = []; // const stepNodes: MinderJsonNode[] = []; //
node.children?.forEach((item) => { node.children?.forEach((item) => {
if (item.data.resource?.includes(textTag)) { if (item.data?.resource?.includes(textTag)) {
textStep = item; textStep = item;
} else if (item.data.resource?.includes(stepTag)) { } else if (item.data?.resource?.includes(stepTag)) {
stepNodes.push(item); stepNodes.push(item);
} else if (item.data.resource?.includes(prerequisiteTag)) { } else if (item.data?.resource?.includes(prerequisiteTag)) {
prerequisiteNode = item; prerequisiteNode = item;
} else if (item.data.resource?.includes(remarkTag)) { } else if (item.data?.resource?.includes(remarkTag)) {
remarkNode = item; remarkNode = item;
} }
}); });
const steps: FeatureCaseMinderStepItem[] = stepNodes.map((child, i) => { const steps: FeatureCaseMinderStepItem[] = stepNodes.map((child, i) => {
return { return {
id: child.data.id, id: child.data?.id || getGenerateId(),
num: i, num: i,
desc: child.data.text, desc: child.data?.text || '',
result: child.children?.[0].data.text || '', result: child.children?.[0].data?.text || '',
}; };
}); });
return { return {
prerequisite: prerequisiteNode?.data.text || '', prerequisite: prerequisiteNode?.data?.text || '',
caseEditType: steps.length > 0 ? 'STEP' : ('TEXT' as FeatureCaseMinderEditType), caseEditType: steps.length > 0 ? 'STEP' : ('TEXT' as FeatureCaseMinderEditType),
steps: JSON.stringify(steps), steps: JSON.stringify(steps),
textDescription: textStep?.data.text || '', textDescription: textStep?.data?.text || '',
expectedResult: textStep?.children?.[0]?.data.text || '', expectedResult: textStep?.children?.[0]?.data?.text || '',
description: remarkNode?.data.text || '', description: remarkNode?.data?.text || '',
}; };
} }
@ -250,15 +254,14 @@
* @param node 节点 * @param node 节点
* @param parent 父节点 * @param parent 父节点
*/ */
function getNodeMoveInfo(node: MinderJsonNode, parent?: MinderJsonNode): { moveMode: MoveMode; targetId?: string } { function getNodeMoveInfo(nodeIndex: number, parent?: MinderJsonNode): { moveMode: MoveMode; targetId?: string } {
const nodeIndex = parent?.children?.findIndex((e) => e.data.id === node.data.id); const moveMode = nodeIndex === 0 ? 'BEFORE' : 'AFTER'; //
const moveMode = nodeIndex === 0 ? 'BEFORE' : 'AFTER';
return { return {
moveMode, moveMode,
targetId: targetId:
moveMode === 'BEFORE' moveMode === 'BEFORE'
? parent?.children?.[1]?.data.id ? parent?.children?.[1]?.data?.id
: parent?.children?.[(nodeIndex || parent.children.length - 1) - 1]?.data.id, : parent?.children?.[(nodeIndex || parent.children.length - 1) - 1]?.data?.id,
}; };
} }
@ -267,17 +270,19 @@
*/ */
function makeMinderParams(): FeatureCaseMinderUpdateParams { function makeMinderParams(): FeatureCaseMinderUpdateParams {
const fullJson: MinderJson = window.minder.exportJson(); const fullJson: MinderJson = window.minder.exportJson();
filterTree(fullJson.root.children, (node, parent) => { filterTree(fullJson.root.children, (node, nodeIndex, parent) => {
if (node.data.isNew !== false || node.data.changed === true) { if (node.data.isNew !== false || node.data.changed === true) {
if (node.data.resource?.includes(moduleTag)) { if (node.data.resource?.includes(moduleTag)) {
//
tempMinderParams.value.updateModuleList.push({ tempMinderParams.value.updateModuleList.push({
id: node.data.id, id: node.data.id,
name: node.data.text, name: node.data.text,
parentId: parent?.data.id || 'NONE', parentId: parent?.data.id || 'NONE',
type: node.data.isNew !== false ? 'ADD' : 'UPDATE', type: node.data.isNew !== false ? 'ADD' : 'UPDATE',
...getNodeMoveInfo(node as MinderJsonNode, parent as MinderJsonNode), ...getNodeMoveInfo(nodeIndex, parent as MinderJsonNode),
}); });
} else if (node.data.resource?.includes(caseTag)) { } else if (node.data.resource?.includes(caseTag)) {
//
const caseNodeInfo = getCaseNodeInfo(node as MinderJsonNode); const caseNodeInfo = getCaseNodeInfo(node as MinderJsonNode);
const caseBaseInfo = baseInfoRef.value?.makeParams(); const caseBaseInfo = baseInfoRef.value?.makeParams();
tempMinderParams.value.updateCaseList.push({ tempMinderParams.value.updateCaseList.push({
@ -288,10 +293,19 @@
tags: caseBaseInfo?.tags || [], tags: caseBaseInfo?.tags || [],
customFields: caseBaseInfo?.customFields || [], customFields: caseBaseInfo?.customFields || [],
name: caseBaseInfo?.name || node.data.text, name: caseBaseInfo?.name || node.data.text,
...getNodeMoveInfo(node as MinderJsonNode, parent as MinderJsonNode), ...getNodeMoveInfo(nodeIndex, parent as MinderJsonNode),
...caseNodeInfo, ...caseNodeInfo,
}); });
return false; // return false; //
} else if (!node.data.resource) {
//
tempMinderParams.value.additionalNodeList.push({
id: node.data.id,
parentId: parent?.data.id || 'NONE',
type: node.data.isNew !== false ? 'ADD' : 'UPDATE',
name: node.data.text,
...getNodeMoveInfo(nodeIndex, parent as MinderJsonNode),
});
} }
} }
return true; return true;
@ -316,7 +330,7 @@
* 已选中节点的可替换标签判断 * 已选中节点的可替换标签判断
* @param node 选中节点 * @param node 选中节点
*/ */
function replaceableTags(node: MinderJsonNode, nodes: MinderJsonNode[]) { function replaceableTags(nodes: MinderJsonNode[]) {
if (nodes.length > 1) { if (nodes.length > 1) {
// 1 // 1
if (nodes.some((e) => (e.data?.resource || []).length > 0)) { if (nodes.some((e) => (e.data?.resource || []).length > 0)) {
@ -328,10 +342,11 @@
return [moduleTag]; return [moduleTag];
} }
} }
const node = nodes[0];
if ( if (
Object.keys(node.data || {}).length === 0 || Object.keys(node.data || {}).length === 0 ||
node.data?.id === 'root' || node.data?.id === 'root' ||
(node.parent?.data.resource || []).length === 0 (node.parent?.data?.resource || []).length === 0
) { ) {
// //
return []; return [];
@ -357,18 +372,21 @@
// //
return caseChildTags; return caseChildTags;
} }
if ( if ((!node.data?.resource || node.data.resource.length === 0) && node.parent?.data?.resource?.includes(moduleTag)) {
(!node.data?.resource || node.data.resource.length === 0) && //
(!node.parent?.data?.resource || if (
node.parent?.data?.resource.length === 0 || (node.children &&
node.parent?.data?.resource?.some((e) => topTags.includes(e))) (node.children.some((e) => e.data?.resource?.includes(caseTag)) ||
) { node.children.some((e) => e.data?.resource?.includes(moduleTag)))) ||
// node.parent?.data?.id === 'NONE'
return node.children && ) {
(node.children.some((e) => e.data?.resource?.includes(caseTag)) || // NONE
node.children.some((e) => e.data?.resource?.includes(moduleTag))) return [moduleTag];
? topTags.filter((e) => e !== caseTag) }
: topTags; if (!node.children || node.children.length === 0) {
//
return topTags;
}
} }
return []; return [];
} }
@ -383,7 +401,22 @@
window.minder.execCommand(command, node); window.minder.execCommand(command, node);
nextTick(() => { nextTick(() => {
const newNode: MinderJsonNode = window.minder.getSelectedNode(); const newNode: MinderJsonNode = window.minder.getSelectedNode();
if (!newNode.data) {
newNode.data = {
id: getGenerateId(),
text: '',
};
}
newNode.data.isNew = true; // newNode.data.isNew = true; //
if (newNode.data?.resource?.some((e) => caseOffspringTags.includes(e))) {
//
if (newNode.parent?.data?.resource?.includes(caseTag)) {
newNode.parent.data.changed = true;
} else if (newNode.parent?.parent?.data?.resource?.includes(caseTag)) {
//
newNode.parent.parent.data.changed = true;
}
}
}); });
} }
} }
@ -437,8 +470,8 @@
parent: node, parent: node,
data: { data: {
id: getGenerateId(), id: getGenerateId(),
text: t('ms.minders.stepDesc'), text: stepTag,
resource: [t('ms.minders.stepDesc')], resource: [stepTag],
isNew: true, isNew: true,
}, },
children: [], children: [],
@ -447,8 +480,8 @@
parent: child, parent: child,
data: { data: {
id: getGenerateId(), id: getGenerateId(),
text: t('ms.minders.stepExpect'), text: stepExpectTag,
resource: [t('ms.minders.stepExpect')], resource: [stepExpectTag],
isNew: true, isNew: true,
}, },
}; };
@ -468,8 +501,8 @@
parent: node, parent: node,
data: { data: {
id: getGenerateId(), id: getGenerateId(),
text: t('ms.minders.stepExpect'), text: stepExpectTag,
resource: [t('ms.minders.stepExpect')], resource: [stepExpectTag],
isNew: true, isNew: true,
}, },
children: [], children: [],
@ -587,14 +620,22 @@
if (tag === moduleTag && node.data) { if (tag === moduleTag && node.data) {
// //
tempMinderParams.value.updateCaseList = tempMinderParams.value.updateCaseList.filter( tempMinderParams.value.updateCaseList = tempMinderParams.value.updateCaseList.filter(
(e) => e.id !== node.data.id (e) => e.id !== node.data?.id
); );
window.minder.execCommand('priority'); window.minder.execCommand('priority');
} else if (node.data.resource?.includes(caseTag)) { } else if (node.data?.resource?.includes(caseTag)) {
// //
tempMinderParams.value.updateModuleList = tempMinderParams.value.updateModuleList.filter( tempMinderParams.value.updateModuleList = tempMinderParams.value.updateModuleList.filter(
(e) => e.id !== node.data.id (e) => e.id !== node.data?.id
); );
} else if (node.data?.resource?.some((e) => caseOffspringTags.includes(e))) {
//
if (node.parent?.data?.resource?.includes(caseTag)) {
node.parent.data.changed = true;
} else if (node.parent?.parent?.data?.resource?.includes(caseTag)) {
//
node.parent.parent.data.changed = true;
}
} }
} }
const baseInfoLoading = ref(false); const baseInfoLoading = ref(false);
@ -792,9 +833,12 @@
node.expand(); node.expand();
node.renderTree(); node.renderTree();
window.minder.layout(); window.minder.layout();
window.minder.execCommand('camera', node, 600);
if (node.data) { if (node.data) {
node.data.isLoaded = true; node.data.isLoaded = true;
} }
// importJson
importJson.value = window.minder.exportJson();
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);
@ -812,25 +856,33 @@
* @param event 脑图事件对象 * @param event 脑图事件对象
*/ */
function handleAction(event: MinderCustomEvent) { function handleAction(event: MinderCustomEvent) {
const { node, name } = event; const { nodes, name } = event;
if (node) { if (nodes && nodes.length > 0) {
switch (name) { switch (name) {
case MinderEventName.DELETE_NODE: case MinderEventName.DELETE_NODE:
tempMinderParams.value.deleteResourceList.push({ // TODO:
id: node.data.id, nodes.forEach((node) => {
type: node.data?.resource?.[0] || moduleTag, tempMinderParams.value.deleteResourceList.push({
id: node.data?.id || getGenerateId(),
type: node.data?.resource?.[0] || moduleTag,
});
if (node.data?.resource?.includes(caseTag)) {
//
tempMinderParams.value.updateCaseList = tempMinderParams.value.updateCaseList.filter(
(e) => e.id !== node.data?.id
);
} else if (node.data?.resource?.includes(moduleTag)) {
//
tempMinderParams.value.updateModuleList = tempMinderParams.value.updateModuleList.filter(
(e) => e.id !== node.data?.id
);
} else if (!node.data?.resource) {
//
tempMinderParams.value.additionalNodeList = tempMinderParams.value.additionalNodeList.filter(
(e) => e.id !== node.data?.id
);
}
}); });
if (node.data?.resource?.includes(caseTag)) {
//
tempMinderParams.value.updateCaseList = tempMinderParams.value.updateCaseList.filter(
(e) => e.id !== node.data.id
);
} else if (node.data?.resource?.includes(moduleTag)) {
//
tempMinderParams.value.updateModuleList = tempMinderParams.value.updateModuleList.filter(
(e) => e.id !== node.data.id
);
}
break; break;
default: default:
break; break;
@ -838,10 +890,111 @@
} }
} }
/**
* 是否停止拖拽动作
* @param dragNode 拖动节点
* @param dropNode 目标节点
* @param mode 拖拽模式
*/
function stopDrag(
dragNodes: MinderJsonNode | MinderJsonNode[],
dropNode: MinderJsonNode,
mode: 'movetoparent' | 'arrange'
) {
if (!Array.isArray(dragNodes)) {
dragNodes = [dragNodes];
}
for (let i = 0; i < dragNodes.length; i++) {
const dragNode = (dragNodes as MinderJsonNode[])[i];
if (mode === 'movetoparent') {
//
if (dragNode.data?.resource?.includes(caseTag) && dropNode.data?.id === 'NONE') {
//
return true;
}
if (
(dragNode.data?.resource?.includes(moduleTag) || dragNode.data?.resource?.includes(caseTag)) &&
dropNode.data?.resource?.includes(moduleTag)
) {
//
if (dragNode.data) {
dragNode.data.changed = true;
}
return false;
}
if (!dragNode.data?.resource && (dropNode.data?.resource?.includes(moduleTag) || !dropNode.data?.resource)) {
//
if (dragNode.data) {
dragNode.data.changed = true;
}
return false;
}
if (
dragNode.data?.resource?.some((e) => caseChildTags.includes(e)) &&
dropNode.data?.resource?.includes(caseTag) &&
dragNode.parent?.data?.id === dropNode.data?.id
) {
//
if (dragNode.parent?.data) {
dragNode.parent.data.changed = true;
}
return false;
}
} else if (mode === 'arrange') {
//
if (
(dragNode.data?.resource?.includes(moduleTag) ||
dragNode.data?.resource?.includes(caseTag) ||
!dragNode.data?.resource) &&
(dropNode.data?.resource?.includes(moduleTag) ||
dropNode.data?.resource?.includes(caseTag) ||
!dropNode.data?.resource)
) {
if (dragNode.data) {
dragNode.data.changed = true;
}
//
return false;
}
if (dragNode.data?.resource?.includes(stepTag) && dropNode.data?.resource?.includes(stepTag)) {
if (dragNode.parent?.data) {
dragNode.parent.data.changed = true;
}
//
return false;
}
}
}
return true;
}
/**
* 脑图命令执行前拦截
* @param event 命令执行事件
*/
function handleBeforeExecCommand(event: MinderEvent) { function handleBeforeExecCommand(event: MinderEvent) {
if (event.commandName === 'movetoparent') { if (event.commandName === 'movetoparent') {
// TODO: //
event.stopPropagation(); if (stopDrag(event.commandArgs[0] as MinderJsonNode, event.commandArgs[1] as MinderJsonNode, 'movetoparent')) {
event.stopPropagation();
}
} else if (event.commandName === 'arrange') {
//
const dragNodes: MinderJsonNode[] = window.minder.getSelectedNodes();
let dropNode: MinderJsonNode;
if (dragNodes[0].parent?.children?.[event.commandArgs[0] as number]) {
//
dropNode = dragNodes[0].parent?.children?.[event.commandArgs[0] as number];
} else if (dragNodes[0].parent?.children?.[(event.commandArgs[0] as number) - 1]) {
//
dropNode = dragNodes[0].parent?.children?.[(event.commandArgs[0] as number) - 1];
} else {
//
dropNode = dragNodes[dragNodes.length - 1];
}
if (stopDrag(dragNodes, dropNode, 'arrange')) {
event.stopPropagation();
}
} }
} }
</script> </script>

View File

@ -4,6 +4,7 @@ export default {
'ms.minders.stepDesc': 'Step Description', 'ms.minders.stepDesc': 'Step Description',
'ms.minders.stepExpect': 'Expected Result', 'ms.minders.stepExpect': 'Expected Result',
'ms.minders.textDesc': 'Text Description', 'ms.minders.textDesc': 'Text Description',
'ms.minders.remark': 'Remark Information',
'ms.minders.caseName': 'Test Case Name', 'ms.minders.caseName': 'Test Case Name',
'ms.minders.caseNameNotNull': 'Test Case Name cannot be empty', 'ms.minders.caseNameNotNull': 'Test Case Name cannot be empty',
'ms.minders.commentTotal': '{num} Comments in Total', 'ms.minders.commentTotal': '{num} Comments in Total',

View File

@ -4,6 +4,7 @@ export default {
'ms.minders.stepDesc': '步骤描述', 'ms.minders.stepDesc': '步骤描述',
'ms.minders.stepExpect': '预期结果', 'ms.minders.stepExpect': '预期结果',
'ms.minders.textDesc': '文本描述', 'ms.minders.textDesc': '文本描述',
'ms.minders.remark': '备注信息',
'ms.minders.caseName': '用例名称', 'ms.minders.caseName': '用例名称',
'ms.minders.caseNameNotNull': '用例名称不能为空', 'ms.minders.caseNameNotNull': '用例名称不能为空',
'ms.minders.commentTotal': '共 {num} 评论', 'ms.minders.commentTotal': '共 {num} 评论',

View File

@ -44,7 +44,8 @@ export default function useEventListener(listener: UseEventListenerProps) {
// console.log('dragFinish', minder.history); // console.log('dragFinish', minder.history);
// }); // });
minder.on('beforeExecCommand', (e: any) => { // 监听脑图执行命令前可通过e.stopPropagation拦截命令执行
minder.on('beforeExecCommand', (e: MinderEvent) => {
if (listener.handleBeforeExecCommand) { if (listener.handleBeforeExecCommand) {
listener.handleBeforeExecCommand(e); listener.handleBeforeExecCommand(e);
} }

View File

@ -84,7 +84,7 @@
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useMinderStore from '@/store/modules/components/minder-editor'; import useMinderStore from '@/store/modules/components/minder-editor';
import { findNodePathByKey } from '@/utils'; import { findNodePathByKey, getGenerateId } from '@/utils';
import { MinderEventName } from '@/enums/minderEnum'; import { MinderEventName } from '@/enums/minderEnum';
@ -94,6 +94,7 @@
mainEditorProps, mainEditorProps,
MinderJson, MinderJson,
MinderJsonNode, MinderJsonNode,
MinderJsonNodeData,
priorityProps, priorityProps,
tagProps, tagProps,
} from '../props'; } from '../props';
@ -214,14 +215,6 @@
init(); init();
}); });
watch(
() => props.importJson,
(val) => {
innerImportJson.value = val;
window.minder.importJson(val);
}
);
const menuVisible = ref(false); const menuVisible = ref(false);
const menuPopupOffset = ref([0, 0]); const menuPopupOffset = ref([0, 0]);
@ -229,12 +222,17 @@
* 切换脑图展示的节点层级 * 切换脑图展示的节点层级
* @param node 切换的节点 * @param node 切换的节点
*/ */
function switchNode(node: MinderJsonNode) { function switchNode(node: MinderJsonNode | MinderJsonNodeData) {
innerImportJson.value = cloneDeep(findNodePathByKey([props.importJson.root], node.data?.id, 'data', 'id')); if (node.data) {
innerImportJson.value = cloneDeep(findNodePathByKey([props.importJson.root], node.data.id, 'data', 'id'));
} else {
innerImportJson.value = cloneDeep(findNodePathByKey([props.importJson.root], node.id, 'data', 'id'));
}
innerImportJson.value.data.expandState = 'expand'; innerImportJson.value.data.expandState = 'expand';
window.minder.importJson(innerImportJson.value); window.minder.importJson(innerImportJson.value);
window.minder.execCommand('template', Object.keys(window.kityminder.Minder.getTemplateList())[minderStore.mold]); setTimeout(() => {
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, node); window.minder.execCommand('camera', window.minder.getRoot(), 600);
}, 100); // TODO:
} }
watch( watch(
@ -248,8 +246,8 @@
]; ];
menuVisible.value = true; menuVisible.value = true;
} }
if (minderStore.event.name === MinderEventName.ENTER_NODE && minderStore.event.node) { if (minderStore.event.name === MinderEventName.ENTER_NODE && minderStore.event.nodes) {
switchNode(minderStore.event.node); switchNode(minderStore.event.nodes[0]);
} }
} }
); );
@ -268,6 +266,12 @@
window.minder.execCommand(command); window.minder.execCommand(command);
nextTick(() => { nextTick(() => {
const newNode: MinderJsonNode = window.minder.getSelectedNode(); const newNode: MinderJsonNode = window.minder.getSelectedNode();
if (!newNode.data) {
newNode.data = {
id: getGenerateId(),
text: '',
};
}
newNode.data.isNew = true; // newNode.data.isNew = true; //
}); });
} }
@ -278,49 +282,52 @@
* @param val 选择的菜单项 * @param val 选择的菜单项
*/ */
function handleMinderMenuSelect(val: string | number | Record<string, any> | undefined) { function handleMinderMenuSelect(val: string | number | Record<string, any> | undefined) {
const selectedNode = window.minder.getSelectedNode(); const selectedNodes: MinderJsonNode[] = window.minder.getSelectedNodes();
switch (val) { if (selectedNodes.length > 0) {
case 'expand': switch (val) {
if (selectedNode.data.expandState === 'collapse') { case 'expand':
window.minder.execCommand('Expand'); if (selectedNodes.some((node) => node.data?.expandState === 'collapse')) {
} else { window.minder.execCommand('Expand');
window.minder.execCommand('Collapse'); } else {
} window.minder.execCommand('Collapse');
minderStore.dispatchEvent(MinderEventName.EXPAND, undefined, undefined, selectedNode); }
break; minderStore.dispatchEvent(MinderEventName.EXPAND, undefined, undefined, selectedNodes);
case 'insetParent': break;
execInsertCommand('AppendParentNode'); case 'insetParent':
minderStore.dispatchEvent(MinderEventName.INSERT_PARENT, undefined, undefined, selectedNode); execInsertCommand('AppendParentNode');
break; minderStore.dispatchEvent(MinderEventName.INSERT_PARENT, undefined, undefined, selectedNodes);
case 'insetSon': break;
execInsertCommand('AppendChildNode'); case 'insetSon':
minderStore.dispatchEvent(MinderEventName.INSERT_CHILD, undefined, undefined, selectedNode); execInsertCommand('AppendChildNode');
break; minderStore.dispatchEvent(MinderEventName.INSERT_CHILD, undefined, undefined, selectedNodes);
case 'insetBrother': break;
execInsertCommand('AppendSiblingNode'); case 'insetBrother':
minderStore.dispatchEvent(MinderEventName.INSERT_SIBLING, undefined, undefined, selectedNode); execInsertCommand('AppendSiblingNode');
break; minderStore.dispatchEvent(MinderEventName.INSERT_SIBLING, undefined, undefined, selectedNodes);
case 'copy': break;
window.minder.execCommand('Copy'); case 'copy':
minderStore.dispatchEvent(MinderEventName.COPY_NODE, undefined, undefined, selectedNode); window.minder.execCommand('Copy');
break; minderStore.dispatchEvent(MinderEventName.COPY_NODE, undefined, undefined, selectedNodes);
case 'cut': break;
window.minder.execCommand('Cut'); case 'cut':
minderStore.dispatchEvent(MinderEventName.CUT_NODE, undefined, undefined, selectedNode); window.minder.execCommand('Cut');
break; minderStore.dispatchEvent(MinderEventName.CUT_NODE, undefined, undefined, selectedNodes);
case 'paste': break;
window.minder.execCommand('Paste'); case 'paste':
minderStore.dispatchEvent(MinderEventName.PASTE_NODE, undefined, undefined, selectedNode); window.minder.execCommand('Paste');
break; minderStore.dispatchEvent(MinderEventName.PASTE_NODE, undefined, undefined, selectedNodes);
case 'delete': break;
window.minder.execCommand('RemoveNode'); case 'delete':
minderStore.dispatchEvent(MinderEventName.DELETE_NODE, undefined, undefined, selectedNode); window.minder.execCommand('RemoveNode');
break; minderStore.dispatchEvent(MinderEventName.DELETE_NODE, undefined, undefined, selectedNodes);
case 'enterNode': break;
switchNode(selectedNode.data); case 'enterNode':
break; switchNode(selectedNodes[0]);
default: minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, [selectedNodes[0]]);
break; break;
default:
break;
}
} }
} }
</script> </script>

View File

@ -24,7 +24,7 @@
import { MinderEventName } from '@/enums/minderEnum'; import { MinderEventName } from '@/enums/minderEnum';
import { delProps } from '../../props'; import { delProps, MinderJsonNode } from '../../props';
import { isDeleteDisableNode } from '../../script/tool/utils'; import { isDeleteDisableNode } from '../../script/tool/utils';
const minderStore = useMinderStore(); const minderStore = useMinderStore();
@ -59,19 +59,19 @@
if (removeNodeDisabled.value || !minder.queryCommandState || !minder.execCommand) { if (removeNodeDisabled.value || !minder.queryCommandState || !minder.execCommand) {
return; return;
} }
const node = minder.getSelectedNode(); const nodes: MinderJsonNode[] = minder.getSelectedNodes();
let position: MinderNodePosition | undefined; let position: MinderNodePosition | undefined;
if (node) { if (nodes.length > 0) {
if (props.delConfirm) { if (props.delConfirm) {
props.delConfirm(node); props.delConfirm(nodes);
return; return;
} }
const box = node.getRenderBox(); const box = nodes[0].getRenderBox();
position = { position = {
x: box.cx, x: box.cx,
y: box.cy, y: box.cy,
}; };
minderStore.dispatchEvent(MinderEventName.DELETE_NODE, position, node.rc.node, node.data); minderStore.dispatchEvent(MinderEventName.DELETE_NODE, position, nodes[0].rc.node, nodes);
} }
minder.forceRemoveNode(); minder.forceRemoveNode();
} }

View File

@ -47,11 +47,10 @@
minder.on('selectionchange', () => { minder.on('selectionchange', () => {
commandDisabled.value = isDisable(); commandDisabled.value = isDisable();
const nodes: MinderJsonNode[] = window.minder.getSelectedNodes(); const nodes: MinderJsonNode[] = window.minder.getSelectedNodes();
const node: MinderJsonNode = minder.getSelectedNode();
if (commandDisabled.value) { if (commandDisabled.value) {
tagList.value = []; tagList.value = [];
} else if (props.replaceableTags) { } else if (props.replaceableTags) {
tagList.value = props.replaceableTags(node, nodes); tagList.value = props.replaceableTags(nodes);
} else { } else {
tagList.value = []; tagList.value = [];
} }
@ -70,8 +69,8 @@
return; return;
} }
if (props.tagEditCheck) { if (props.tagEditCheck) {
const node: MinderJsonNode = minder.getSelectedNode(); const nodes: MinderJsonNode[] = minder.getSelectedNodes();
if (!props.tagEditCheck(node, resourceName)) { if (!props.tagEditCheck(nodes, resourceName)) {
return; return;
} }
} }
@ -100,13 +99,12 @@
} }
window.minder.execCommand('resource', origin); window.minder.execCommand('resource', origin);
const nodes: MinderJsonNode[] = window.minder.getSelectedNodes(); const nodes: MinderJsonNode[] = window.minder.getSelectedNodes();
const node: MinderJsonNode = minder.getSelectedNode(); minderStore.dispatchEvent(MinderEventName.SET_TAG, undefined, undefined, nodes);
minderStore.dispatchEvent(MinderEventName.SET_TAG, undefined, undefined, node);
if (props.replaceableTags) { if (props.replaceableTags) {
tagList.value = props.replaceableTags(node, nodes); tagList.value = props.replaceableTags(nodes);
} }
if (props.afterTagEdit) { if (props.afterTagEdit) {
props.afterTagEdit(node, resourceName); props.afterTagEdit(nodes, resourceName);
} }
} }
</script> </script>

View File

@ -6,19 +6,6 @@ import type { MoveMode } from '@/models/common';
import type { PropType } from 'vue'; import type { PropType } from 'vue';
export interface MinderClass {
stopPropagation: () => void; // 阻止事件冒泡
stopPropagationImmediately: () => void; // 立即阻止事件冒泡
[key: string]: any; // TODO: 其他事件属性
}
// TODO:脑图事件类型补充
export interface MinderEvent extends MinderClass {
command: any;
commandArgs: Record<string, any>[];
commandName: string;
minder: any;
type: string;
}
export interface MinderIconButtonItem { export interface MinderIconButtonItem {
icon: string; icon: string;
tooltip: string; tooltip: string;
@ -35,10 +22,11 @@ export interface MinderJsonNodeData {
changed?: boolean; // 脑图节点是否发生过变化 changed?: boolean; // 脑图节点是否发生过变化
moveMode?: MoveMode; // 移动方式(节点移动或新增时需要) moveMode?: MoveMode; // 移动方式(节点移动或新增时需要)
targetId?: string; // 目标节点 id节点移动或新增时需要 targetId?: string; // 目标节点 id节点移动或新增时需要
disabled?: boolean; // 是否禁用
[key: string]: any; [key: string]: any;
} }
export interface MinderJsonNode { export interface MinderJsonNode {
data: MinderJsonNodeData; data?: MinderJsonNodeData;
parent?: MinderJsonNode; parent?: MinderJsonNode;
children?: MinderJsonNode[]; children?: MinderJsonNode[];
[key: string]: any; // minder 内置字段 [key: string]: any; // minder 内置字段
@ -49,6 +37,20 @@ export interface MinderJson {
template: string; template: string;
treePath: Record<string, MinderJsonNode>[]; treePath: Record<string, MinderJsonNode>[];
} }
// 脑图类
export interface MinderClass {
stopPropagation: () => void; // 阻止事件冒泡
stopPropagationImmediately: () => void; // 立即阻止事件冒泡
[key: string]: any; // TODO: 其他事件属性
}
// TODO:脑图事件类型补充
export interface MinderEvent extends MinderClass {
command: any;
commandArgs: ((Record<string, any> & MinderJsonNode) | (Record<string, any> & MinderJsonNode)[] | number)[];
commandName: string;
minder: any;
type: string;
}
export const mainEditorProps = { export const mainEditorProps = {
importJson: { importJson: {
@ -122,10 +124,10 @@ export const tagProps = {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
replaceableTags: Function as PropType<(node: MinderJsonNode, nodes: MinderJsonNode[]) => string[]>, replaceableTags: Function as PropType<(nodes: MinderJsonNode[]) => string[]>,
tagDisableCheck: Function, tagDisableCheck: Function,
tagEditCheck: Function as PropType<(node: MinderJsonNode, tag: string) => boolean>, tagEditCheck: Function as PropType<(nodes: MinderJsonNode[], tag: string) => boolean>,
afterTagEdit: Function as PropType<(node: MinderJsonNode, tag: string) => void>, afterTagEdit: Function as PropType<(nodes: MinderJsonNode[], tag: string) => void>,
}; };
export const insertProps = { export const insertProps = {

View File

@ -1,41 +1,41 @@
import type { MinderJsonNode } from '../../props'; import type { MinderJsonNode } from '../../props';
export function isDisableNode(minder: any) { export function isDisableNode(minder: any) {
let node; let node: MinderJsonNode;
if (minder && minder.getSelectedNode) { if (minder && minder.getSelectedNode) {
node = minder.getSelectedNode(); node = minder.getSelectedNode();
} if (node && node.data?.disabled === true) {
if (node && node.data.disable === true) { return true;
return true; }
} }
return false; return false;
} }
export function isDeleteDisableNode(minder: any) { export function isDeleteDisableNode(minder: any) {
let node; let node: MinderJsonNode;
if (minder && minder.getSelectedNode) { if (minder && minder.getSelectedNode) {
node = minder.getSelectedNode(); node = minder.getSelectedNode();
} if (node && node.data?.disabled === true && !node.data.allowDelete) {
if (node && node.data.disable === true && !node.data.allowDelete) { return true;
return true; }
} }
return false; return false;
} }
export function isTagEnableNode(node: MinderJsonNode) { export function isTagEnableNode(node: MinderJsonNode) {
if (node && (node.data.tagEnable === true || node.data.allowDisabledTag === true)) { if (node.data && (node.data.tagEnable === true || node.data.allowDisabledTag === true)) {
return true; return true;
} }
return false; return false;
} }
export function isTagEnable(minder: any) { export function isTagEnable(minder: any) {
let node; let node: MinderJsonNode;
if (minder && minder.getSelectedNode) { if (minder && minder.getSelectedNode) {
node = minder.getSelectedNode(); node = minder.getSelectedNode();
} if (isTagEnableNode(node)) {
if (isTagEnableNode(node)) { return true;
return true; }
} }
return false; return false;
} }
@ -46,10 +46,10 @@ export function markChangeNode(node: MinderJsonNode) {
} }
} }
function markDelNode(node: MinderJsonNode, deleteChild: any) { function markDelNode(node: MinderJsonNode, deleteChild: MinderJsonNode) {
deleteChild.push(node.data); deleteChild.push(node.data);
if (node.children) { if (node.children) {
node.children.forEach((child: any) => { node.children.forEach((child: MinderJsonNode) => {
markDelNode(child, deleteChild); markDelNode(child, deleteChild);
}); });
} }
@ -58,9 +58,9 @@ function markDelNode(node: MinderJsonNode, deleteChild: any) {
// 在父节点记录删除的节点 // 在父节点记录删除的节点
export function markDeleteNode(minder: any) { export function markDeleteNode(minder: any) {
if (minder) { if (minder) {
const nodes = minder.getSelectedNodes(); const nodes: MinderJsonNode[] = minder.getSelectedNodes();
nodes.forEach((node: MinderJsonNode) => { nodes.forEach((node: MinderJsonNode) => {
if (node && node.parent) { if (node.parent?.data) {
const pData = node.parent.data; const pData = node.parent.data;
if (!pData.deleteChild) { if (!pData.deleteChild) {
pData.deleteChild = []; pData.deleteChild = [];
@ -101,7 +101,7 @@ export function setPriorityView(priorityStartWithZero: boolean, priorityPrefix:
* id置为nullchanged true * id置为nullchanged true
* @param node * @param node
*/ */
export function resetNodes(nodes: any) { export function resetNodes(nodes: MinderJsonNode[]) {
if (nodes) { if (nodes) {
nodes.forEach((item: any) => { nodes.forEach((item: any) => {
if (item.data) { if (item.data) {
@ -115,7 +115,7 @@ export function resetNodes(nodes: any) {
} }
export function isDisableForNode(node: MinderJsonNode) { export function isDisableForNode(node: MinderJsonNode) {
if (node && node.data.disable === true) { if (node && node.data?.disabled === true) {
return true; return true;
} }
return false; return false;

View File

@ -382,7 +382,15 @@ export interface FeatureCaseMinderUpdateModuleItem {
moveMode?: MoveMode; moveMode?: MoveMode;
targetId?: string; targetId?: string;
} }
// 脑图新增/修改的文本节点集合
export interface FeatureCaseMinderUpdateTextNodeItem {
id: string;
name: string;
parentId: string;
type: FeatureCaseMinderActionType;
moveMode?: MoveMode;
targetId?: string;
}
export interface CustomField { export interface CustomField {
fieldId: string; fieldId: string;
value: string; value: string;
@ -419,4 +427,5 @@ export interface FeatureCaseMinderUpdateParams {
updateCaseList: FeatureCaseMinderUpdateCaseItem[]; updateCaseList: FeatureCaseMinderUpdateCaseItem[];
updateModuleList: FeatureCaseMinderUpdateModuleItem[]; updateModuleList: FeatureCaseMinderUpdateModuleItem[];
deleteResourceList: FeatureCaseMinderDeleteResourceItem[]; deleteResourceList: FeatureCaseMinderDeleteResourceItem[];
additionalNodeList: FeatureCaseMinderUpdateTextNodeItem[];
} }

View File

@ -17,7 +17,7 @@ const useMinderStore = defineStore('minder', {
y: 0, y: 0,
}, },
nodeDom: undefined, nodeDom: undefined,
node: undefined, nodes: undefined,
}, },
mold: 0, mold: 0,
}), }),
@ -27,15 +27,20 @@ const useMinderStore = defineStore('minder', {
* @param name * @param name
* @param position / * @param position /
* @param nodeDom DOM * @param nodeDom DOM
* @param node * @param nodes
*/ */
dispatchEvent(name: MinderEventName, position?: MinderNodePosition, nodeDom?: HTMLElement, node?: MinderJsonNode) { dispatchEvent(
name: MinderEventName,
position?: MinderNodePosition,
nodeDom?: HTMLElement,
nodes?: MinderJsonNode[]
) {
this.event = { this.event = {
name, name,
timestamp: Date.now(), timestamp: Date.now(),
nodePosition: position, nodePosition: position,
nodeDom, nodeDom,
node, nodes,
}; };
}, },
setMold(val: number) { setMold(val: number) {

View File

@ -12,7 +12,7 @@ export interface MinderCustomEvent {
timestamp: number; timestamp: number;
nodePosition?: MinderNodePosition; nodePosition?: MinderNodePosition;
nodeDom?: HTMLElement; nodeDom?: HTMLElement;
node?: MinderJsonNode; nodes?: MinderJsonNode[];
} }
export interface MinderState { export interface MinderState {

View File

@ -281,7 +281,7 @@ export function mapTree<T>(
*/ */
export function filterTree<T>( export function filterTree<T>(
tree: TreeNode<T> | TreeNode<T>[] | T | T[], tree: TreeNode<T> | TreeNode<T>[] | T | T[],
filterFn: (node: TreeNode<T>, parent?: TreeNode<T> | null) => boolean, filterFn: (node: TreeNode<T>, nodeIndex: number, parent?: TreeNode<T> | null) => boolean,
customChildrenKey = 'children', customChildrenKey = 'children',
parentNode: TreeNode<T> | null = null parentNode: TreeNode<T> | null = null
): TreeNode<T>[] { ): TreeNode<T>[] {
@ -292,7 +292,7 @@ export function filterTree<T>(
for (let i = 0; i < tree.length; i++) { for (let i = 0; i < tree.length; i++) {
const node = (tree as TreeNode<T>[])[i]; const node = (tree as TreeNode<T>[])[i];
// 如果节点满足过滤条件,则保留该节点,并递归过滤子节点 // 如果节点满足过滤条件,则保留该节点,并递归过滤子节点
if (filterFn(node, parentNode)) { if (filterFn(node, i, parentNode)) {
const newNode = cloneDeep(node); const newNode = cloneDeep(node);
if (node[customChildrenKey] && node[customChildrenKey].length > 0) { if (node[customChildrenKey] && node[customChildrenKey].length > 0) {
// 递归过滤子节点,并将过滤后的子节点添加到当前节点中 // 递归过滤子节点,并将过滤后的子节点添加到当前节点中

View File

@ -855,6 +855,7 @@
/** 环境管理-环境组 end */ /** 环境管理-环境组 end */
const defaultLineData = ref(cloneDeep(props.defaultParamItem));
/** /**
* 当表格输入框变化时给参数表格添加一行数据行 * 当表格输入框变化时给参数表格添加一行数据行
* @param val 输入值 * @param val 输入值
@ -875,7 +876,7 @@
const nextLine = { const nextLine = {
id, id,
enable: true, // enable: true, //
...cloneDeep(props.defaultParamItem), // ...cloneDeep(defaultLineData.value), //
} as any; } as any;
selectColumnKeys.forEach((key) => { selectColumnKeys.forEach((key) => {
// 便 // 便
@ -894,6 +895,7 @@
} }
}); });
paramsData.value.push(nextLine); paramsData.value.push(nextLine);
defaultLineData.value = cloneDeep(nextLine);
} }
emitChange('addTableLine', isInit); emitChange('addTableLine', isInit);
handleMustContainColChange(true); handleMustContainColChange(true);
@ -910,7 +912,7 @@
// undefined // undefined
hasNoIdItem = true; hasNoIdItem = true;
return { return {
...cloneDeep(props.defaultParamItem), ...cloneDeep(defaultLineData.value),
id: getGenerateId(), id: getGenerateId(),
}; };
} }
@ -927,7 +929,7 @@
if ( if (
(!props.disabledExceptParam || !props.disabledParamValue) && (!props.disabledExceptParam || !props.disabledParamValue) &&
hasNoIdItem && hasNoIdItem &&
!filterKeyValParams(arr, props.defaultParamItem, !props.selectable).lastDataIsDefault && !filterKeyValParams(arr, defaultLineData.value, !props.selectable).lastDataIsDefault &&
!props.isTreeTable !props.isTreeTable
) { ) {
addTableLine(arr.length - 1, false, true); addTableLine(arr.length - 1, false, true);
@ -939,7 +941,7 @@
{ {
id, // id props.defaultParamItem id id, // id props.defaultParamItem id
enable: true, // enable: true, //
...cloneDeep(props.defaultParamItem), ...cloneDeep(defaultLineData.value),
}, },
] as any[]; ] as any[];
emitChange('watch props.params', true); emitChange('watch props.params', true);