diff --git a/frontend/src/api/modules/case-management/featureCase.ts b/frontend/src/api/modules/case-management/featureCase.ts
index 1f2930d591..56992bc400 100644
--- a/frontend/src/api/modules/case-management/featureCase.ts
+++ b/frontend/src/api/modules/case-management/featureCase.ts
@@ -90,7 +90,7 @@ import type {
DeleteDependencyParams,
DemandItem,
DragCase,
- FeatureCaseMinder,
+ FeatureCaseMinderUpdateParams,
ImportExcelType,
ModulesTreeType,
OperationFile,
@@ -182,7 +182,7 @@ export function batchCopyToModules(data: BatchMoveOrCopyType) {
}
// 保存脑图
-export function saveCaseMinder(data: FeatureCaseMinder) {
+export function saveCaseMinder(data: FeatureCaseMinderUpdateParams) {
return MSR.post({ url: `${SaveCaseMinderUrl}`, data });
}
diff --git a/frontend/src/components/business/ms-minders/featureCaseMinder/basInfo.vue b/frontend/src/components/business/ms-minders/featureCaseMinder/basInfo.vue
index 55edf3fc23..e37409b049 100644
--- a/frontend/src/components/business/ms-minders/featureCaseMinder/basInfo.vue
+++ b/frontend/src/components/business/ms-minders/featureCaseMinder/basInfo.vue
@@ -63,6 +63,7 @@
loading: boolean;
}>();
const emit = defineEmits<{
+ (e: 'initTemplate', id: string): void;
(e: 'cancel'): void;
}>();
@@ -118,6 +119,7 @@
});
formRules.value = result.filter((e: any) => e);
baseInfoLoading.value = false;
+ emit('initTemplate', id);
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
@@ -129,6 +131,22 @@
});
const saveLoading = ref(false);
+
+ function makeParams() {
+ return {
+ ...baseInfoForm.value,
+ id: props.activeCase.id,
+ projectId: appStore.currentProjectId,
+ caseEditType: props.activeCase.caseEditType,
+ customFields: formItem.value.map((item: any) => {
+ return {
+ fieldId: item.field,
+ value: Array.isArray(item.value) ? JSON.stringify(item.value) : item.value,
+ };
+ }),
+ };
+ }
+
function handleSave() {
baseInfoFormRef.value?.validate((errors) => {
if (!errors) {
@@ -136,20 +154,8 @@
if (valid === true) {
try {
saveLoading.value = true;
- const data = {
- ...baseInfoForm.value,
- id: props.activeCase.id,
- projectId: appStore.currentProjectId,
- caseEditType: props.activeCase.caseEditType,
- customFields: formItem.value.map((item: any) => {
- return {
- fieldId: item.field,
- value: Array.isArray(item.value) ? JSON.stringify(item.value) : item.value,
- };
- }),
- };
await updateCaseRequest({
- request: data,
+ request: makeParams(),
fileList: [],
});
const selectedNode: MinderJsonNode = window.minder.getSelectedNode();
@@ -181,6 +187,10 @@
immediate: true,
}
);
+
+ defineExpose({
+ makeParams,
+ });
diff --git a/frontend/src/components/pure/ms-minder-editor/hooks/useEventListener.ts b/frontend/src/components/pure/ms-minder-editor/hooks/useEventListener.ts
new file mode 100644
index 0000000000..49d9f2420b
--- /dev/null
+++ b/frontend/src/components/pure/ms-minder-editor/hooks/useEventListener.ts
@@ -0,0 +1,46 @@
+import { debounce } from 'lodash-es';
+
+import useMinderStore from '@/store/modules/components/minder-editor/index';
+import type { MinderEvent } from '@/store/modules/components/minder-editor/types';
+
+import type { MinderJsonNode } from '../props';
+
+export interface UseEventListenerProps {
+ handleContentChange?: (node: MinderJsonNode) => void;
+ handleSelectionChange?: (node: MinderJsonNode) => void;
+ handleMinderEvent?: (event: MinderEvent) => void;
+}
+
+export default function useEventListener(listener: UseEventListenerProps) {
+ const { minder } = window;
+ const minderStore = useMinderStore();
+
+ // 监听脑图节点内容变化
+ minder.on('contentchange', () => {
+ const node: MinderJsonNode = minder.getSelectedNode();
+ if (listener.handleContentChange) {
+ listener.handleContentChange(node);
+ }
+ });
+
+ // 监听脑图选中节点变化
+ minder.on(
+ 'selectionchange',
+ debounce(() => {
+ const node: MinderJsonNode = minder.getSelectedNode();
+ if (listener.handleSelectionChange) {
+ listener.handleSelectionChange(node);
+ }
+ }, 300)
+ );
+
+ // 监听脑图自定义事件
+ watch(
+ () => minderStore.event.timestamp,
+ () => {
+ if (listener.handleMinderEvent) {
+ listener.handleMinderEvent(minderStore.event);
+ }
+ }
+ );
+}
diff --git a/frontend/src/components/pure/ms-minder-editor/hooks/useShortCut.ts b/frontend/src/components/pure/ms-minder-editor/hooks/useShortCut.ts
new file mode 100644
index 0000000000..a6e8008326
--- /dev/null
+++ b/frontend/src/components/pure/ms-minder-editor/hooks/useShortCut.ts
@@ -0,0 +1 @@
+export default function useShortCut() {}
diff --git a/frontend/src/components/pure/ms-minder-editor/main/mainEditor.vue b/frontend/src/components/pure/ms-minder-editor/main/mainEditor.vue
index 8b0b5d5e26..2b1c07ee2c 100644
--- a/frontend/src/components/pure/ms-minder-editor/main/mainEditor.vue
+++ b/frontend/src/components/pure/ms-minder-editor/main/mainEditor.vue
@@ -19,12 +19,6 @@
( / )
-
-
-
{{ t('minder.hotboxMenu.insetParent') }}
-
(Shift + Tab)
-
-
{{ t('minder.hotboxMenu.insetSon') }}
@@ -94,7 +88,15 @@
import { MinderEventName } from '@/enums/minderEnum';
- import { editMenuProps, insertProps, mainEditorProps, MinderJsonNode, priorityProps, tagProps } from '../props';
+ import {
+ editMenuProps,
+ insertProps,
+ mainEditorProps,
+ MinderJson,
+ MinderJsonNode,
+ priorityProps,
+ tagProps,
+ } from '../props';
import Editor from '../script/editor';
import { markChangeNode, markDeleteNode } from '../script/tool/utils';
import type { Ref } from 'vue';
@@ -102,11 +104,10 @@
const { t } = useI18n();
const props = defineProps({ ...editMenuProps, ...insertProps, ...mainEditorProps, ...tagProps, ...priorityProps });
- const emit = defineEmits({
- afterMount: () => ({}),
- save: (json) => json,
- enterNode: (data) => data,
- });
+ const emit = defineEmits<{
+ (e: 'afterMount'): void;
+ (e: 'save', json: MinderJson): void;
+ }>();
const minderStore = useMinderStore();
const mec: Ref
= ref(null);
@@ -192,7 +193,7 @@
'appendsiblingnode',
]);
if (selectNodes && !notChangeCommands.has(env.commandName.toLocaleLowerCase())) {
- selectNodes.forEach((node: any) => {
+ selectNodes.forEach((node: MinderJsonNode) => {
markChangeNode(node);
});
}
@@ -233,6 +234,7 @@
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);
}
watch(
@@ -280,27 +282,35 @@
} 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);
diff --git a/frontend/src/components/pure/ms-minder-editor/menu/edit/tagBox.vue b/frontend/src/components/pure/ms-minder-editor/menu/edit/tagBox.vue
index 936422865e..24b37702a9 100644
--- a/frontend/src/components/pure/ms-minder-editor/menu/edit/tagBox.vue
+++ b/frontend/src/components/pure/ms-minder-editor/menu/edit/tagBox.vue
@@ -15,10 +15,15 @@
diff --git a/frontend/src/components/pure/ms-minder-editor/props.ts b/frontend/src/components/pure/ms-minder-editor/props.ts
index a231e16c6c..0b9f3edc20 100644
--- a/frontend/src/components/pure/ms-minder-editor/props.ts
+++ b/frontend/src/components/pure/ms-minder-editor/props.ts
@@ -16,6 +16,8 @@ export interface MinderJsonNodeData {
expandState?: 'collapse' | 'expand';
priority?: number;
// 前端渲染字段
+ isNew?: boolean; // 是否脑图新增节点,需要在初始化脑图数据时标记已存在节点为 false 以区分是否新增节点
+ changed?: boolean; // 脑图节点是否发生过变化
[key: string]: any;
}
export interface MinderJsonNode {
diff --git a/frontend/src/components/pure/ms-minder-editor/script/tool/utils.ts b/frontend/src/components/pure/ms-minder-editor/script/tool/utils.ts
index 9877c3708c..6b225bc057 100644
--- a/frontend/src/components/pure/ms-minder-editor/script/tool/utils.ts
+++ b/frontend/src/components/pure/ms-minder-editor/script/tool/utils.ts
@@ -1,3 +1,5 @@
+import type { MinderJsonNode } from '../../props';
+
export function isDisableNode(minder: any) {
let node;
if (minder && minder.getSelectedNode) {
@@ -20,7 +22,7 @@ export function isDeleteDisableNode(minder: any) {
return false;
}
-export function isTagEnableNode(node: any) {
+export function isTagEnableNode(node: MinderJsonNode) {
if (node && (node.data.tagEnable === true || node.data.allowDisabledTag === true)) {
return true;
}
@@ -38,19 +40,13 @@ export function isTagEnable(minder: any) {
return false;
}
-export function markChangeNode(node: any) {
- if (node && node.data) {
- // 修改的该节点标记为 contextChanged
- node.data.contextChanged = true;
- while (node) {
- // 该路径上的节点都标记为 changed
- node.data.changed = true;
- node = node.parent;
- }
+export function markChangeNode(node: MinderJsonNode) {
+ if (node.data) {
+ node.data.changed = true;
}
}
-function markDelNode(node: any, deleteChild: any) {
+function markDelNode(node: MinderJsonNode, deleteChild: any) {
deleteChild.push(node.data);
if (node.children) {
node.children.forEach((child: any) => {
@@ -63,7 +59,7 @@ function markDelNode(node: any, deleteChild: any) {
export function markDeleteNode(minder: any) {
if (minder) {
const nodes = minder.getSelectedNodes();
- nodes.forEach((node: any) => {
+ nodes.forEach((node: MinderJsonNode) => {
if (node && node.parent) {
const pData = node.parent.data;
if (!pData.deleteChild) {
@@ -118,7 +114,7 @@ export function resetNodes(nodes: any) {
}
}
-export function isDisableForNode(node: any) {
+export function isDisableForNode(node: MinderJsonNode) {
if (node && node.data.disable === true) {
return true;
}
diff --git a/frontend/src/enums/minderEnum.ts b/frontend/src/enums/minderEnum.ts
index d7b955f318..4cfbeeab4b 100644
--- a/frontend/src/enums/minderEnum.ts
+++ b/frontend/src/enums/minderEnum.ts
@@ -2,6 +2,14 @@ export enum MinderEventName {
'DELETE_NODE' = 'DELETE_NODE', // 删除节点
'HOTBOX' = 'HOTBOX', // 热键菜单
'ENTER_NODE' = 'ENTER_NODE', // 进入节点
+ 'EXPAND' = 'EXPAND', // 展开节点
+ 'INSERT_PARENT' = 'INSERT_PARENT', // 插入父节点
+ 'INSERT_CHILD' = 'INSERT_CHILD', // 插入子节点
+ 'INSERT_SIBLING' = 'INSERT_SIBLING', // 插入同级节点
+ 'COPY_NODE' = 'COPY_NODE', // 复制节点
+ 'PASTE_NODE' = 'PASTE_NODE', // 粘贴节点
+ 'CUT_NODE' = 'CUT_NODE', // 剪切节点
+ 'SET_TAG' = 'SET_TAG', // 设置节点标签
}
export default {};
diff --git a/frontend/src/models/caseManagement/featureCase.ts b/frontend/src/models/caseManagement/featureCase.ts
index 427b0349e6..0b8d674015 100644
--- a/frontend/src/models/caseManagement/featureCase.ts
+++ b/frontend/src/models/caseManagement/featureCase.ts
@@ -365,7 +365,7 @@ export interface ContentTabsMap {
backupTabList: TabItemType[];
}
// 脑图删除的模块/用例的集合
-export interface FeatureCaseMinderDeleteResourceList {
+export interface FeatureCaseMinderDeleteResourceItem {
id: string;
type: string;
}
@@ -374,7 +374,7 @@ export type FeatureCaseMinderActionType = 'ADD' | 'UPDATE';
// 脑图用例编辑模式
export type FeatureCaseMinderEditType = 'STEP' | 'TEXT';
// 脑图新增/修改的模块集合(只记录操作的节点,节点下的子节点不需要记录)
-export interface FeatureCaseMinderUpdateModuleList {
+export interface FeatureCaseMinderUpdateModuleItem {
id: string;
name: string;
parentId: string;
@@ -395,7 +395,7 @@ export interface FeatureCaseMinderStepItem {
result?: string;
}
// 脑图新增/修改的用例对象集合
-export interface FeatureCaseMinderUpdateCaseList {
+export interface FeatureCaseMinderUpdateCaseItem {
id: string; // 用例id(新增的时候前端传UUid,更新的时候必填)
templateId: string; // 模板id
type: FeatureCaseMinderActionType;
@@ -403,7 +403,7 @@ export interface FeatureCaseMinderUpdateCaseList {
moduleId: string;
moveMode?: MoveMode; // 移动方式(节点移动或新增时需要)
targetId?: string;
- prerequisite: string;
+ prerequisite: string; // 前置条件
caseEditType: FeatureCaseMinderEditType;
steps: FeatureCaseMinderStepItem[];
textDescription: string; // 文本描述
@@ -413,10 +413,10 @@ export interface FeatureCaseMinderUpdateCaseList {
customFields: CustomField[];
}
// 脑图
-export interface FeatureCaseMinder {
+export interface FeatureCaseMinderUpdateParams {
projectId: string;
versionId?: string;
- updateCaseList: FeatureCaseMinderUpdateCaseList[];
- updateModuleList: FeatureCaseMinderUpdateModuleList[];
- deleteResourceList: FeatureCaseMinderDeleteResourceList[];
+ updateCaseList: FeatureCaseMinderUpdateCaseItem[];
+ updateModuleList: FeatureCaseMinderUpdateModuleItem[];
+ deleteResourceList: FeatureCaseMinderDeleteResourceItem[];
}
diff --git a/frontend/src/views/case-management/caseManagementFeature/components/caseTable.vue b/frontend/src/views/case-management/caseManagementFeature/components/caseTable.vue
index aa1109fe63..0bebb317ae 100644
--- a/frontend/src/views/case-management/caseManagementFeature/components/caseTable.vue
+++ b/frontend/src/views/case-management/caseManagementFeature/components/caseTable.vue
@@ -16,14 +16,14 @@
@refresh="fetchData()"
>
-
+