mirror of
https://gitee.com/fit2cloud-feizhiyun/MeterSphere.git
synced 2024-12-01 03:28:59 +08:00
feat(脑图): 脑图拖拽拦截&空白节点保存&部分 bug 修复
This commit is contained in:
parent
7ee89429c7
commit
99328a50e5
@ -94,9 +94,11 @@
|
||||
const stepTag = t('ms.minders.stepDesc');
|
||||
const textTag = t('ms.minders.textDesc');
|
||||
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 caseChildTags = [prerequisiteTag, stepTag, textTag, remarkTag];
|
||||
const caseOffspringTags = [...caseChildTags, stepTag, stepExpectTag, textTag, remarkTag];
|
||||
const importJson = ref<MinderJson>({
|
||||
root: {} as MinderJsonNode,
|
||||
template: 'default',
|
||||
@ -110,6 +112,7 @@
|
||||
updateCaseList: [],
|
||||
updateModuleList: [],
|
||||
deleteResourceList: [],
|
||||
additionalNodeList: [],
|
||||
});
|
||||
const templateId = ref('');
|
||||
|
||||
@ -153,6 +156,7 @@
|
||||
id: 'NONE',
|
||||
text: t('ms.minders.allModule'),
|
||||
resource: [moduleTag],
|
||||
disabled: true,
|
||||
},
|
||||
};
|
||||
window.minder.importJson(importJson.value);
|
||||
@ -217,31 +221,31 @@
|
||||
let remarkNode: MinderJsonNode | undefined; // 备注
|
||||
const stepNodes: MinderJsonNode[] = []; // 步骤描述
|
||||
node.children?.forEach((item) => {
|
||||
if (item.data.resource?.includes(textTag)) {
|
||||
if (item.data?.resource?.includes(textTag)) {
|
||||
textStep = item;
|
||||
} else if (item.data.resource?.includes(stepTag)) {
|
||||
} else if (item.data?.resource?.includes(stepTag)) {
|
||||
stepNodes.push(item);
|
||||
} else if (item.data.resource?.includes(prerequisiteTag)) {
|
||||
} else if (item.data?.resource?.includes(prerequisiteTag)) {
|
||||
prerequisiteNode = item;
|
||||
} else if (item.data.resource?.includes(remarkTag)) {
|
||||
} else if (item.data?.resource?.includes(remarkTag)) {
|
||||
remarkNode = item;
|
||||
}
|
||||
});
|
||||
const steps: FeatureCaseMinderStepItem[] = stepNodes.map((child, i) => {
|
||||
return {
|
||||
id: child.data.id,
|
||||
id: child.data?.id || getGenerateId(),
|
||||
num: i,
|
||||
desc: child.data.text,
|
||||
result: child.children?.[0].data.text || '',
|
||||
desc: child.data?.text || '',
|
||||
result: child.children?.[0].data?.text || '',
|
||||
};
|
||||
});
|
||||
return {
|
||||
prerequisite: prerequisiteNode?.data.text || '',
|
||||
prerequisite: prerequisiteNode?.data?.text || '',
|
||||
caseEditType: steps.length > 0 ? 'STEP' : ('TEXT' as FeatureCaseMinderEditType),
|
||||
steps: JSON.stringify(steps),
|
||||
textDescription: textStep?.data.text || '',
|
||||
expectedResult: textStep?.children?.[0]?.data.text || '',
|
||||
description: remarkNode?.data.text || '',
|
||||
textDescription: textStep?.data?.text || '',
|
||||
expectedResult: textStep?.children?.[0]?.data?.text || '',
|
||||
description: remarkNode?.data?.text || '',
|
||||
};
|
||||
}
|
||||
|
||||
@ -250,15 +254,14 @@
|
||||
* @param node 节点
|
||||
* @param parent 父节点
|
||||
*/
|
||||
function getNodeMoveInfo(node: MinderJsonNode, parent?: MinderJsonNode): { moveMode: MoveMode; targetId?: string } {
|
||||
const nodeIndex = parent?.children?.findIndex((e) => e.data.id === node.data.id);
|
||||
const moveMode = nodeIndex === 0 ? 'BEFORE' : 'AFTER';
|
||||
function getNodeMoveInfo(nodeIndex: number, parent?: MinderJsonNode): { moveMode: MoveMode; targetId?: string } {
|
||||
const moveMode = nodeIndex === 0 ? 'BEFORE' : 'AFTER'; // 除了第一个以外,其他都是在目标节点后面插入
|
||||
return {
|
||||
moveMode,
|
||||
targetId:
|
||||
moveMode === 'BEFORE'
|
||||
? parent?.children?.[1]?.data.id
|
||||
: parent?.children?.[(nodeIndex || parent.children.length - 1) - 1]?.data.id,
|
||||
? parent?.children?.[1]?.data?.id
|
||||
: parent?.children?.[(nodeIndex || parent.children.length - 1) - 1]?.data?.id,
|
||||
};
|
||||
}
|
||||
|
||||
@ -267,17 +270,19 @@
|
||||
*/
|
||||
function makeMinderParams(): FeatureCaseMinderUpdateParams {
|
||||
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.resource?.includes(moduleTag)) {
|
||||
// 处理模块节点
|
||||
tempMinderParams.value.updateModuleList.push({
|
||||
id: node.data.id,
|
||||
name: node.data.text,
|
||||
parentId: parent?.data.id || 'NONE',
|
||||
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)) {
|
||||
// 处理用例节点
|
||||
const caseNodeInfo = getCaseNodeInfo(node as MinderJsonNode);
|
||||
const caseBaseInfo = baseInfoRef.value?.makeParams();
|
||||
tempMinderParams.value.updateCaseList.push({
|
||||
@ -288,10 +293,19 @@
|
||||
tags: caseBaseInfo?.tags || [],
|
||||
customFields: caseBaseInfo?.customFields || [],
|
||||
name: caseBaseInfo?.name || node.data.text,
|
||||
...getNodeMoveInfo(node as MinderJsonNode, parent as MinderJsonNode),
|
||||
...getNodeMoveInfo(nodeIndex, parent as MinderJsonNode),
|
||||
...caseNodeInfo,
|
||||
});
|
||||
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;
|
||||
@ -316,7 +330,7 @@
|
||||
* 已选中节点的可替换标签判断
|
||||
* @param node 选中节点
|
||||
*/
|
||||
function replaceableTags(node: MinderJsonNode, nodes: MinderJsonNode[]) {
|
||||
function replaceableTags(nodes: MinderJsonNode[]) {
|
||||
if (nodes.length > 1) {
|
||||
// 选中的节点大于 1 时
|
||||
if (nodes.some((e) => (e.data?.resource || []).length > 0)) {
|
||||
@ -328,10 +342,11 @@
|
||||
return [moduleTag];
|
||||
}
|
||||
}
|
||||
const node = nodes[0];
|
||||
if (
|
||||
Object.keys(node.data || {}).length === 0 ||
|
||||
node.data?.id === 'root' ||
|
||||
(node.parent?.data.resource || []).length === 0
|
||||
(node.parent?.data?.resource || []).length === 0
|
||||
) {
|
||||
// 没有数据的节点、默认模块节点、父节点为文本节点的节点不可替换标签
|
||||
return [];
|
||||
@ -357,18 +372,21 @@
|
||||
// 选中节点无标签,且父节点为用例节点,可替换用例下级标签
|
||||
return caseChildTags;
|
||||
}
|
||||
if (
|
||||
(!node.data?.resource || node.data.resource.length === 0) &&
|
||||
(!node.parent?.data?.resource ||
|
||||
node.parent?.data?.resource.length === 0 ||
|
||||
node.parent?.data?.resource?.some((e) => topTags.includes(e)))
|
||||
) {
|
||||
// 如果选中节点子级含有用例节点或模块节点,则不可将选中节点标记为用例
|
||||
return node.children &&
|
||||
(node.children.some((e) => e.data?.resource?.includes(caseTag)) ||
|
||||
node.children.some((e) => e.data?.resource?.includes(moduleTag)))
|
||||
? topTags.filter((e) => e !== caseTag)
|
||||
: topTags;
|
||||
if ((!node.data?.resource || node.data.resource.length === 0) && node.parent?.data?.resource?.includes(moduleTag)) {
|
||||
// 选中节点是文本节点、选中节点的父节点是模块节点
|
||||
if (
|
||||
(node.children &&
|
||||
(node.children.some((e) => e.data?.resource?.includes(caseTag)) ||
|
||||
node.children.some((e) => e.data?.resource?.includes(moduleTag)))) ||
|
||||
node.parent?.data?.id === 'NONE'
|
||||
) {
|
||||
// 如果选中节点子级含有用例节点或模块节点,或者选中节点的父节点是根节点 NONE,只能将节点标记为模块节点
|
||||
return [moduleTag];
|
||||
}
|
||||
if (!node.children || node.children.length === 0) {
|
||||
// 如果选中节点无子级,可标记为用例节点或模块节点
|
||||
return topTags;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
@ -383,7 +401,22 @@
|
||||
window.minder.execCommand(command, node);
|
||||
nextTick(() => {
|
||||
const newNode: MinderJsonNode = window.minder.getSelectedNode();
|
||||
if (!newNode.data) {
|
||||
newNode.data = {
|
||||
id: getGenerateId(),
|
||||
text: '',
|
||||
};
|
||||
}
|
||||
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,
|
||||
data: {
|
||||
id: getGenerateId(),
|
||||
text: t('ms.minders.stepDesc'),
|
||||
resource: [t('ms.minders.stepDesc')],
|
||||
text: stepTag,
|
||||
resource: [stepTag],
|
||||
isNew: true,
|
||||
},
|
||||
children: [],
|
||||
@ -447,8 +480,8 @@
|
||||
parent: child,
|
||||
data: {
|
||||
id: getGenerateId(),
|
||||
text: t('ms.minders.stepExpect'),
|
||||
resource: [t('ms.minders.stepExpect')],
|
||||
text: stepExpectTag,
|
||||
resource: [stepExpectTag],
|
||||
isNew: true,
|
||||
},
|
||||
};
|
||||
@ -468,8 +501,8 @@
|
||||
parent: node,
|
||||
data: {
|
||||
id: getGenerateId(),
|
||||
text: t('ms.minders.stepExpect'),
|
||||
resource: [t('ms.minders.stepExpect')],
|
||||
text: stepExpectTag,
|
||||
resource: [stepExpectTag],
|
||||
isNew: true,
|
||||
},
|
||||
children: [],
|
||||
@ -587,14 +620,22 @@
|
||||
if (tag === moduleTag && node.data) {
|
||||
// 排除是从用例节点切换到模块节点的数据
|
||||
tempMinderParams.value.updateCaseList = tempMinderParams.value.updateCaseList.filter(
|
||||
(e) => e.id !== node.data.id
|
||||
(e) => e.id !== node.data?.id
|
||||
);
|
||||
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(
|
||||
(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);
|
||||
@ -792,9 +833,12 @@
|
||||
node.expand();
|
||||
node.renderTree();
|
||||
window.minder.layout();
|
||||
window.minder.execCommand('camera', node, 600);
|
||||
if (node.data) {
|
||||
node.data.isLoaded = true;
|
||||
}
|
||||
// 加载完用例数据后,更新当前importJson数据
|
||||
importJson.value = window.minder.exportJson();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
@ -812,25 +856,33 @@
|
||||
* @param event 脑图事件对象
|
||||
*/
|
||||
function handleAction(event: MinderCustomEvent) {
|
||||
const { node, name } = event;
|
||||
if (node) {
|
||||
const { nodes, name } = event;
|
||||
if (nodes && nodes.length > 0) {
|
||||
switch (name) {
|
||||
case MinderEventName.DELETE_NODE:
|
||||
tempMinderParams.value.deleteResourceList.push({
|
||||
id: node.data.id,
|
||||
type: node.data?.resource?.[0] || moduleTag,
|
||||
// TODO:循环优化
|
||||
nodes.forEach((node) => {
|
||||
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;
|
||||
default:
|
||||
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) {
|
||||
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>
|
||||
|
@ -4,6 +4,7 @@ export default {
|
||||
'ms.minders.stepDesc': 'Step Description',
|
||||
'ms.minders.stepExpect': 'Expected Result',
|
||||
'ms.minders.textDesc': 'Text Description',
|
||||
'ms.minders.remark': 'Remark Information',
|
||||
'ms.minders.caseName': 'Test Case Name',
|
||||
'ms.minders.caseNameNotNull': 'Test Case Name cannot be empty',
|
||||
'ms.minders.commentTotal': '{num} Comments in Total',
|
||||
|
@ -4,6 +4,7 @@ export default {
|
||||
'ms.minders.stepDesc': '步骤描述',
|
||||
'ms.minders.stepExpect': '预期结果',
|
||||
'ms.minders.textDesc': '文本描述',
|
||||
'ms.minders.remark': '备注信息',
|
||||
'ms.minders.caseName': '用例名称',
|
||||
'ms.minders.caseNameNotNull': '用例名称不能为空',
|
||||
'ms.minders.commentTotal': '共 {num} 评论',
|
||||
|
@ -44,7 +44,8 @@ export default function useEventListener(listener: UseEventListenerProps) {
|
||||
// console.log('dragFinish', minder.history);
|
||||
// });
|
||||
|
||||
minder.on('beforeExecCommand', (e: any) => {
|
||||
// 监听脑图执行命令前(可通过e.stopPropagation拦截命令执行)
|
||||
minder.on('beforeExecCommand', (e: MinderEvent) => {
|
||||
if (listener.handleBeforeExecCommand) {
|
||||
listener.handleBeforeExecCommand(e);
|
||||
}
|
||||
|
@ -84,7 +84,7 @@
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useMinderStore from '@/store/modules/components/minder-editor';
|
||||
import { findNodePathByKey } from '@/utils';
|
||||
import { findNodePathByKey, getGenerateId } from '@/utils';
|
||||
|
||||
import { MinderEventName } from '@/enums/minderEnum';
|
||||
|
||||
@ -94,6 +94,7 @@
|
||||
mainEditorProps,
|
||||
MinderJson,
|
||||
MinderJsonNode,
|
||||
MinderJsonNodeData,
|
||||
priorityProps,
|
||||
tagProps,
|
||||
} from '../props';
|
||||
@ -214,14 +215,6 @@
|
||||
init();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.importJson,
|
||||
(val) => {
|
||||
innerImportJson.value = val;
|
||||
window.minder.importJson(val);
|
||||
}
|
||||
);
|
||||
|
||||
const menuVisible = ref(false);
|
||||
const menuPopupOffset = ref([0, 0]);
|
||||
|
||||
@ -229,12 +222,17 @@
|
||||
* 切换脑图展示的节点层级
|
||||
* @param node 切换的节点
|
||||
*/
|
||||
function switchNode(node: MinderJsonNode) {
|
||||
innerImportJson.value = cloneDeep(findNodePathByKey([props.importJson.root], node.data?.id, 'data', 'id'));
|
||||
function switchNode(node: MinderJsonNode | MinderJsonNodeData) {
|
||||
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';
|
||||
window.minder.importJson(innerImportJson.value);
|
||||
window.minder.execCommand('template', Object.keys(window.kityminder.Minder.getTemplateList())[minderStore.mold]);
|
||||
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, node);
|
||||
setTimeout(() => {
|
||||
window.minder.execCommand('camera', window.minder.getRoot(), 600);
|
||||
}, 100); // TODO:暂未知渲染时机,临时延迟解决
|
||||
}
|
||||
|
||||
watch(
|
||||
@ -248,8 +246,8 @@
|
||||
];
|
||||
menuVisible.value = true;
|
||||
}
|
||||
if (minderStore.event.name === MinderEventName.ENTER_NODE && minderStore.event.node) {
|
||||
switchNode(minderStore.event.node);
|
||||
if (minderStore.event.name === MinderEventName.ENTER_NODE && minderStore.event.nodes) {
|
||||
switchNode(minderStore.event.nodes[0]);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -268,6 +266,12 @@
|
||||
window.minder.execCommand(command);
|
||||
nextTick(() => {
|
||||
const newNode: MinderJsonNode = window.minder.getSelectedNode();
|
||||
if (!newNode.data) {
|
||||
newNode.data = {
|
||||
id: getGenerateId(),
|
||||
text: '',
|
||||
};
|
||||
}
|
||||
newNode.data.isNew = true; // 新建的节点标记为新建
|
||||
});
|
||||
}
|
||||
@ -278,49 +282,52 @@
|
||||
* @param val 选择的菜单项
|
||||
*/
|
||||
function handleMinderMenuSelect(val: string | number | Record<string, any> | undefined) {
|
||||
const selectedNode = window.minder.getSelectedNode();
|
||||
switch (val) {
|
||||
case 'expand':
|
||||
if (selectedNode.data.expandState === 'collapse') {
|
||||
window.minder.execCommand('Expand');
|
||||
} else {
|
||||
window.minder.execCommand('Collapse');
|
||||
}
|
||||
minderStore.dispatchEvent(MinderEventName.EXPAND, undefined, undefined, selectedNode);
|
||||
break;
|
||||
case 'insetParent':
|
||||
execInsertCommand('AppendParentNode');
|
||||
minderStore.dispatchEvent(MinderEventName.INSERT_PARENT, undefined, undefined, selectedNode);
|
||||
break;
|
||||
case 'insetSon':
|
||||
execInsertCommand('AppendChildNode');
|
||||
minderStore.dispatchEvent(MinderEventName.INSERT_CHILD, undefined, undefined, selectedNode);
|
||||
break;
|
||||
case 'insetBrother':
|
||||
execInsertCommand('AppendSiblingNode');
|
||||
minderStore.dispatchEvent(MinderEventName.INSERT_SIBLING, undefined, undefined, selectedNode);
|
||||
break;
|
||||
case 'copy':
|
||||
window.minder.execCommand('Copy');
|
||||
minderStore.dispatchEvent(MinderEventName.COPY_NODE, undefined, undefined, selectedNode);
|
||||
break;
|
||||
case 'cut':
|
||||
window.minder.execCommand('Cut');
|
||||
minderStore.dispatchEvent(MinderEventName.CUT_NODE, undefined, undefined, selectedNode);
|
||||
break;
|
||||
case 'paste':
|
||||
window.minder.execCommand('Paste');
|
||||
minderStore.dispatchEvent(MinderEventName.PASTE_NODE, undefined, undefined, selectedNode);
|
||||
break;
|
||||
case 'delete':
|
||||
window.minder.execCommand('RemoveNode');
|
||||
minderStore.dispatchEvent(MinderEventName.DELETE_NODE, undefined, undefined, selectedNode);
|
||||
break;
|
||||
case 'enterNode':
|
||||
switchNode(selectedNode.data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
const selectedNodes: MinderJsonNode[] = window.minder.getSelectedNodes();
|
||||
if (selectedNodes.length > 0) {
|
||||
switch (val) {
|
||||
case 'expand':
|
||||
if (selectedNodes.some((node) => node.data?.expandState === 'collapse')) {
|
||||
window.minder.execCommand('Expand');
|
||||
} else {
|
||||
window.minder.execCommand('Collapse');
|
||||
}
|
||||
minderStore.dispatchEvent(MinderEventName.EXPAND, undefined, undefined, selectedNodes);
|
||||
break;
|
||||
case 'insetParent':
|
||||
execInsertCommand('AppendParentNode');
|
||||
minderStore.dispatchEvent(MinderEventName.INSERT_PARENT, undefined, undefined, selectedNodes);
|
||||
break;
|
||||
case 'insetSon':
|
||||
execInsertCommand('AppendChildNode');
|
||||
minderStore.dispatchEvent(MinderEventName.INSERT_CHILD, undefined, undefined, selectedNodes);
|
||||
break;
|
||||
case 'insetBrother':
|
||||
execInsertCommand('AppendSiblingNode');
|
||||
minderStore.dispatchEvent(MinderEventName.INSERT_SIBLING, undefined, undefined, selectedNodes);
|
||||
break;
|
||||
case 'copy':
|
||||
window.minder.execCommand('Copy');
|
||||
minderStore.dispatchEvent(MinderEventName.COPY_NODE, undefined, undefined, selectedNodes);
|
||||
break;
|
||||
case 'cut':
|
||||
window.minder.execCommand('Cut');
|
||||
minderStore.dispatchEvent(MinderEventName.CUT_NODE, undefined, undefined, selectedNodes);
|
||||
break;
|
||||
case 'paste':
|
||||
window.minder.execCommand('Paste');
|
||||
minderStore.dispatchEvent(MinderEventName.PASTE_NODE, undefined, undefined, selectedNodes);
|
||||
break;
|
||||
case 'delete':
|
||||
window.minder.execCommand('RemoveNode');
|
||||
minderStore.dispatchEvent(MinderEventName.DELETE_NODE, undefined, undefined, selectedNodes);
|
||||
break;
|
||||
case 'enterNode':
|
||||
switchNode(selectedNodes[0]);
|
||||
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, [selectedNodes[0]]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
import { MinderEventName } from '@/enums/minderEnum';
|
||||
|
||||
import { delProps } from '../../props';
|
||||
import { delProps, MinderJsonNode } from '../../props';
|
||||
import { isDeleteDisableNode } from '../../script/tool/utils';
|
||||
|
||||
const minderStore = useMinderStore();
|
||||
@ -59,19 +59,19 @@
|
||||
if (removeNodeDisabled.value || !minder.queryCommandState || !minder.execCommand) {
|
||||
return;
|
||||
}
|
||||
const node = minder.getSelectedNode();
|
||||
const nodes: MinderJsonNode[] = minder.getSelectedNodes();
|
||||
let position: MinderNodePosition | undefined;
|
||||
if (node) {
|
||||
if (nodes.length > 0) {
|
||||
if (props.delConfirm) {
|
||||
props.delConfirm(node);
|
||||
props.delConfirm(nodes);
|
||||
return;
|
||||
}
|
||||
const box = node.getRenderBox();
|
||||
const box = nodes[0].getRenderBox();
|
||||
position = {
|
||||
x: box.cx,
|
||||
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();
|
||||
}
|
||||
|
@ -47,11 +47,10 @@
|
||||
minder.on('selectionchange', () => {
|
||||
commandDisabled.value = isDisable();
|
||||
const nodes: MinderJsonNode[] = window.minder.getSelectedNodes();
|
||||
const node: MinderJsonNode = minder.getSelectedNode();
|
||||
if (commandDisabled.value) {
|
||||
tagList.value = [];
|
||||
} else if (props.replaceableTags) {
|
||||
tagList.value = props.replaceableTags(node, nodes);
|
||||
tagList.value = props.replaceableTags(nodes);
|
||||
} else {
|
||||
tagList.value = [];
|
||||
}
|
||||
@ -70,8 +69,8 @@
|
||||
return;
|
||||
}
|
||||
if (props.tagEditCheck) {
|
||||
const node: MinderJsonNode = minder.getSelectedNode();
|
||||
if (!props.tagEditCheck(node, resourceName)) {
|
||||
const nodes: MinderJsonNode[] = minder.getSelectedNodes();
|
||||
if (!props.tagEditCheck(nodes, resourceName)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -100,13 +99,12 @@
|
||||
}
|
||||
window.minder.execCommand('resource', origin);
|
||||
const nodes: MinderJsonNode[] = window.minder.getSelectedNodes();
|
||||
const node: MinderJsonNode = minder.getSelectedNode();
|
||||
minderStore.dispatchEvent(MinderEventName.SET_TAG, undefined, undefined, node);
|
||||
minderStore.dispatchEvent(MinderEventName.SET_TAG, undefined, undefined, nodes);
|
||||
if (props.replaceableTags) {
|
||||
tagList.value = props.replaceableTags(node, nodes);
|
||||
tagList.value = props.replaceableTags(nodes);
|
||||
}
|
||||
if (props.afterTagEdit) {
|
||||
props.afterTagEdit(node, resourceName);
|
||||
props.afterTagEdit(nodes, resourceName);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -6,19 +6,6 @@ import type { MoveMode } from '@/models/common';
|
||||
|
||||
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 {
|
||||
icon: string;
|
||||
tooltip: string;
|
||||
@ -35,10 +22,11 @@ export interface MinderJsonNodeData {
|
||||
changed?: boolean; // 脑图节点是否发生过变化
|
||||
moveMode?: MoveMode; // 移动方式(节点移动或新增时需要)
|
||||
targetId?: string; // 目标节点 id(节点移动或新增时需要)
|
||||
disabled?: boolean; // 是否禁用
|
||||
[key: string]: any;
|
||||
}
|
||||
export interface MinderJsonNode {
|
||||
data: MinderJsonNodeData;
|
||||
data?: MinderJsonNodeData;
|
||||
parent?: MinderJsonNode;
|
||||
children?: MinderJsonNode[];
|
||||
[key: string]: any; // minder 内置字段
|
||||
@ -49,6 +37,20 @@ export interface MinderJson {
|
||||
template: string;
|
||||
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 = {
|
||||
importJson: {
|
||||
@ -122,10 +124,10 @@ export const tagProps = {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
replaceableTags: Function as PropType<(node: MinderJsonNode, nodes: MinderJsonNode[]) => string[]>,
|
||||
replaceableTags: Function as PropType<(nodes: MinderJsonNode[]) => string[]>,
|
||||
tagDisableCheck: Function,
|
||||
tagEditCheck: Function as PropType<(node: MinderJsonNode, tag: string) => boolean>,
|
||||
afterTagEdit: Function as PropType<(node: MinderJsonNode, tag: string) => void>,
|
||||
tagEditCheck: Function as PropType<(nodes: MinderJsonNode[], tag: string) => boolean>,
|
||||
afterTagEdit: Function as PropType<(nodes: MinderJsonNode[], tag: string) => void>,
|
||||
};
|
||||
|
||||
export const insertProps = {
|
||||
|
@ -1,41 +1,41 @@
|
||||
import type { MinderJsonNode } from '../../props';
|
||||
|
||||
export function isDisableNode(minder: any) {
|
||||
let node;
|
||||
let node: MinderJsonNode;
|
||||
if (minder && minder.getSelectedNode) {
|
||||
node = minder.getSelectedNode();
|
||||
}
|
||||
if (node && node.data.disable === true) {
|
||||
return true;
|
||||
if (node && node.data?.disabled === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isDeleteDisableNode(minder: any) {
|
||||
let node;
|
||||
let node: MinderJsonNode;
|
||||
if (minder && minder.getSelectedNode) {
|
||||
node = minder.getSelectedNode();
|
||||
}
|
||||
if (node && node.data.disable === true && !node.data.allowDelete) {
|
||||
return true;
|
||||
if (node && node.data?.disabled === true && !node.data.allowDelete) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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 false;
|
||||
}
|
||||
|
||||
export function isTagEnable(minder: any) {
|
||||
let node;
|
||||
let node: MinderJsonNode;
|
||||
if (minder && minder.getSelectedNode) {
|
||||
node = minder.getSelectedNode();
|
||||
}
|
||||
if (isTagEnableNode(node)) {
|
||||
return true;
|
||||
if (isTagEnableNode(node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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);
|
||||
if (node.children) {
|
||||
node.children.forEach((child: any) => {
|
||||
node.children.forEach((child: MinderJsonNode) => {
|
||||
markDelNode(child, deleteChild);
|
||||
});
|
||||
}
|
||||
@ -58,9 +58,9 @@ function markDelNode(node: MinderJsonNode, deleteChild: any) {
|
||||
// 在父节点记录删除的节点
|
||||
export function markDeleteNode(minder: any) {
|
||||
if (minder) {
|
||||
const nodes = minder.getSelectedNodes();
|
||||
const nodes: MinderJsonNode[] = minder.getSelectedNodes();
|
||||
nodes.forEach((node: MinderJsonNode) => {
|
||||
if (node && node.parent) {
|
||||
if (node.parent?.data) {
|
||||
const pData = node.parent.data;
|
||||
if (!pData.deleteChild) {
|
||||
pData.deleteChild = [];
|
||||
@ -101,7 +101,7 @@ export function setPriorityView(priorityStartWithZero: boolean, priorityPrefix:
|
||||
* 将节点及其子节点id置为null,changed 标记为true
|
||||
* @param node
|
||||
*/
|
||||
export function resetNodes(nodes: any) {
|
||||
export function resetNodes(nodes: MinderJsonNode[]) {
|
||||
if (nodes) {
|
||||
nodes.forEach((item: any) => {
|
||||
if (item.data) {
|
||||
@ -115,7 +115,7 @@ export function resetNodes(nodes: any) {
|
||||
}
|
||||
|
||||
export function isDisableForNode(node: MinderJsonNode) {
|
||||
if (node && node.data.disable === true) {
|
||||
if (node && node.data?.disabled === true) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -382,7 +382,15 @@ export interface FeatureCaseMinderUpdateModuleItem {
|
||||
moveMode?: MoveMode;
|
||||
targetId?: string;
|
||||
}
|
||||
|
||||
// 脑图新增/修改的文本节点集合
|
||||
export interface FeatureCaseMinderUpdateTextNodeItem {
|
||||
id: string;
|
||||
name: string;
|
||||
parentId: string;
|
||||
type: FeatureCaseMinderActionType;
|
||||
moveMode?: MoveMode;
|
||||
targetId?: string;
|
||||
}
|
||||
export interface CustomField {
|
||||
fieldId: string;
|
||||
value: string;
|
||||
@ -419,4 +427,5 @@ export interface FeatureCaseMinderUpdateParams {
|
||||
updateCaseList: FeatureCaseMinderUpdateCaseItem[];
|
||||
updateModuleList: FeatureCaseMinderUpdateModuleItem[];
|
||||
deleteResourceList: FeatureCaseMinderDeleteResourceItem[];
|
||||
additionalNodeList: FeatureCaseMinderUpdateTextNodeItem[];
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ const useMinderStore = defineStore('minder', {
|
||||
y: 0,
|
||||
},
|
||||
nodeDom: undefined,
|
||||
node: undefined,
|
||||
nodes: undefined,
|
||||
},
|
||||
mold: 0,
|
||||
}),
|
||||
@ -27,15 +27,20 @@ const useMinderStore = defineStore('minder', {
|
||||
* @param name 事件名称
|
||||
* @param position 触发事件的节点/鼠标位置
|
||||
* @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 = {
|
||||
name,
|
||||
timestamp: Date.now(),
|
||||
nodePosition: position,
|
||||
nodeDom,
|
||||
node,
|
||||
nodes,
|
||||
};
|
||||
},
|
||||
setMold(val: number) {
|
||||
|
@ -12,7 +12,7 @@ export interface MinderCustomEvent {
|
||||
timestamp: number;
|
||||
nodePosition?: MinderNodePosition;
|
||||
nodeDom?: HTMLElement;
|
||||
node?: MinderJsonNode;
|
||||
nodes?: MinderJsonNode[];
|
||||
}
|
||||
|
||||
export interface MinderState {
|
||||
|
@ -281,7 +281,7 @@ export function mapTree<T>(
|
||||
*/
|
||||
export function filterTree<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',
|
||||
parentNode: TreeNode<T> | null = null
|
||||
): TreeNode<T>[] {
|
||||
@ -292,7 +292,7 @@ export function filterTree<T>(
|
||||
for (let i = 0; i < tree.length; i++) {
|
||||
const node = (tree as TreeNode<T>[])[i];
|
||||
// 如果节点满足过滤条件,则保留该节点,并递归过滤子节点
|
||||
if (filterFn(node, parentNode)) {
|
||||
if (filterFn(node, i, parentNode)) {
|
||||
const newNode = cloneDeep(node);
|
||||
if (node[customChildrenKey] && node[customChildrenKey].length > 0) {
|
||||
// 递归过滤子节点,并将过滤后的子节点添加到当前节点中
|
||||
|
@ -855,6 +855,7 @@
|
||||
|
||||
/** 环境管理-环境组 end */
|
||||
|
||||
const defaultLineData = ref(cloneDeep(props.defaultParamItem));
|
||||
/**
|
||||
* 当表格输入框变化时,给参数表格添加一行数据行
|
||||
* @param val 输入值
|
||||
@ -875,7 +876,7 @@
|
||||
const nextLine = {
|
||||
id,
|
||||
enable: true, // 是否勾选
|
||||
...cloneDeep(props.defaultParamItem), // 深拷贝,避免有嵌套引用类型,数据隔离
|
||||
...cloneDeep(defaultLineData.value), // 深拷贝,避免有嵌套引用类型,数据隔离
|
||||
} as any;
|
||||
selectColumnKeys.forEach((key) => {
|
||||
// 如果是更改了下拉框导致添加新的一列,需要将更改后的下拉框的值应用到下一行(产品为了方便统一输入参数类型)
|
||||
@ -894,6 +895,7 @@
|
||||
}
|
||||
});
|
||||
paramsData.value.push(nextLine);
|
||||
defaultLineData.value = cloneDeep(nextLine);
|
||||
}
|
||||
emitChange('addTableLine', isInit);
|
||||
handleMustContainColChange(true);
|
||||
@ -910,7 +912,7 @@
|
||||
// 批量添加过来的数据最后一行会是 undefined
|
||||
hasNoIdItem = true;
|
||||
return {
|
||||
...cloneDeep(props.defaultParamItem),
|
||||
...cloneDeep(defaultLineData.value),
|
||||
id: getGenerateId(),
|
||||
};
|
||||
}
|
||||
@ -927,7 +929,7 @@
|
||||
if (
|
||||
(!props.disabledExceptParam || !props.disabledParamValue) &&
|
||||
hasNoIdItem &&
|
||||
!filterKeyValParams(arr, props.defaultParamItem, !props.selectable).lastDataIsDefault &&
|
||||
!filterKeyValParams(arr, defaultLineData.value, !props.selectable).lastDataIsDefault &&
|
||||
!props.isTreeTable
|
||||
) {
|
||||
addTableLine(arr.length - 1, false, true);
|
||||
@ -939,7 +941,7 @@
|
||||
{
|
||||
id, // 默认给时间戳 id,若 props.defaultParamItem 有 id,则覆盖
|
||||
enable: true, // 是否勾选
|
||||
...cloneDeep(props.defaultParamItem),
|
||||
...cloneDeep(defaultLineData.value),
|
||||
},
|
||||
] as any[];
|
||||
emitChange('watch props.params', true);
|
||||
|
Loading…
Reference in New Issue
Block a user