feat(脑图): 脑图未保存提示

This commit is contained in:
baiqi 2024-06-11 11:12:26 +08:00 committed by 刘瑞斌
parent 5be3cf217d
commit 1db33454a0
8 changed files with 63 additions and 18 deletions

View File

@ -198,7 +198,7 @@
} }
} }
watchEffect(() => { onMounted(() => {
initCaseTree(); initCaseTree();
}); });

View File

@ -9,4 +9,5 @@ export default {
'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',
'ms.minders.text': 'Text', 'ms.minders.text': 'Text',
'ms.minders.leaveUnsavedTip': 'The mind map has unsaved changes, are you sure you want to exit?',
}; };

View File

@ -9,4 +9,5 @@ export default {
'ms.minders.caseNameNotNull': '用例名称不能为空', 'ms.minders.caseNameNotNull': '用例名称不能为空',
'ms.minders.commentTotal': '共 {num} 评论', 'ms.minders.commentTotal': '共 {num} 评论',
'ms.minders.text': '文本', 'ms.minders.text': '文本',
'ms.minders.leaveUnsavedTip': '脑图有未保存的更改,确认离开吗?',
}; };

View File

@ -27,6 +27,7 @@
import minderHeader from './header.vue'; import minderHeader from './header.vue';
import Navigator from './navigator.vue'; import Navigator from './navigator.vue';
import useLeaveUnSaveTip from '@/hooks/useLeaveUnSaveTip';
import useMinderStore from '@/store/modules/components/minder-editor'; import useMinderStore from '@/store/modules/components/minder-editor';
import { findNodePathByKey, replaceNodeInTree } from '@/utils'; import { findNodePathByKey, replaceNodeInTree } from '@/utils';
@ -63,6 +64,11 @@
}>(); }>();
const minderStore = useMinderStore(); const minderStore = useMinderStore();
const { setIsSave } = useLeaveUnSaveTip({
leaveContent: 'ms.minders.leaveUnsavedTip',
leaveTitle: 'common.unSaveLeaveTitle',
tipType: 'warning',
});
const mec: Ref<HTMLDivElement | null> = ref(null); const mec: Ref<HTMLDivElement | null> = ref(null);
const importJson = defineModel<MinderJson>('importJson', { const importJson = defineModel<MinderJson>('importJson', {
required: true, required: true,
@ -72,7 +78,6 @@
template: 'default', template: 'default',
treePath: [], treePath: [],
}); });
const minderUnsaved = ref(false);
function handlePriorityButton() { function handlePriorityButton() {
const { priorityPrefix } = props; const { priorityPrefix } = props;
@ -148,7 +153,7 @@
'zoomOut', 'zoomOut',
]); ]);
if (selectNodes && !notChangeCommands.has(env.commandName.toLocaleLowerCase())) { if (selectNodes && !notChangeCommands.has(env.commandName.toLocaleLowerCase())) {
minderUnsaved.value = true; minderStore.setMinderUnsaved(true);
minderStore.dispatchEvent(MinderEventName.MINDER_CHANGED); minderStore.dispatchEvent(MinderEventName.MINDER_CHANGED);
selectNodes.forEach((node: MinderJsonNode) => { selectNodes.forEach((node: MinderJsonNode) => {
markChangeNode(node); markChangeNode(node);
@ -179,7 +184,7 @@
* @param node 切换的节点 * @param node 切换的节点
*/ */
function switchNode(node: MinderJsonNode | MinderJsonNodeData) { function switchNode(node: MinderJsonNode | MinderJsonNodeData) {
if (minderUnsaved.value) { if (minderStore.minderUnsaved) {
// //
replaceNodeInTree( replaceNodeInTree(
[importJson.value.root], [importJson.value.root],
@ -232,10 +237,17 @@
data = window.minder.exportJson(); data = window.minder.exportJson();
} }
emit('save', data, () => { emit('save', data, () => {
minderUnsaved.value = false; minderStore.setMinderUnsaved(false);
menuVisible.value = false; menuVisible.value = false;
}); });
} }
watch(
() => minderStore.getMinderUnsaved,
(val) => {
setIsSave(!val);
}
);
</script> </script>
<style lang="less"> <style lang="less">

View File

@ -13,7 +13,7 @@ export interface LeaveProps {
const leaveProps: LeaveProps = { const leaveProps: LeaveProps = {
leaveTitle: 'common.unSaveLeaveTitle', leaveTitle: 'common.unSaveLeaveTitle',
leaveContent: 'common.unSaveLeaveContent', leaveContent: 'common.unSaveLeaveContent',
tipType: 'error', tipType: 'warning',
}; };
// 离开页面确认提示 // 离开页面确认提示

View File

@ -22,7 +22,13 @@ const useMinderStore = defineStore('minder', {
}, },
mold: 0, mold: 0,
clipboard: [], clipboard: [],
minderUnsaved: false,
}), }),
getters: {
getMinderUnsaved(): boolean {
return this.minderUnsaved;
},
},
actions: { actions: {
/** /**
* *
@ -56,6 +62,9 @@ const useMinderStore = defineStore('minder', {
setClipboard(nodes?: MinderJsonNode[]) { setClipboard(nodes?: MinderJsonNode[]) {
this.clipboard = nodes || []; this.clipboard = nodes || [];
}, },
setMinderUnsaved(val: boolean) {
this.minderUnsaved = val;
},
}, },
}); });

View File

@ -28,4 +28,5 @@ export interface MinderState {
event: MinderCustomEvent; event: MinderCustomEvent;
mold: number; mold: number;
clipboard: MinderJsonNode[]; // 剪切板 clipboard: MinderJsonNode[]; // 剪切板
minderUnsaved: boolean; // 脑图是否有未保存的内容
} }

View File

@ -20,7 +20,7 @@
<a-radio value="list" class="show-type-icon !m-[2px]"> <a-radio value="list" class="show-type-icon !m-[2px]">
<MsIcon :size="14" type="icon-icon_view-list_outlined" /> <MsIcon :size="14" type="icon-icon_view-list_outlined" />
</a-radio> </a-radio>
<a-radio value="xMind" class="show-type-icon !m-[2px]"> <a-radio value="minder" class="show-type-icon !m-[2px]">
<MsIcon :size="14" type="icon-icon_mindnote_outlined" /> <MsIcon :size="14" type="icon-icon_mindnote_outlined" />
</a-radio> </a-radio>
</a-radio-group> </a-radio-group>
@ -191,11 +191,17 @@
</template> </template>
</a-popover> </a-popover>
<div class="flex items-center gap-[12px]"> <div class="flex items-center gap-[12px]">
<a-radio-group v-model:model-value="showType" type="button" size="small" class="list-show-type"> <a-radio-group
v-model:model-value="showType"
type="button"
size="small"
class="list-show-type"
@change="handleShowTypeChange"
>
<a-radio value="list" class="show-type-icon !m-[2px]"> <a-radio value="list" class="show-type-icon !m-[2px]">
<MsIcon :size="14" type="icon-icon_view-list_outlined" /> <MsIcon :size="14" type="icon-icon_view-list_outlined" />
</a-radio> </a-radio>
<a-radio value="xMind" class="show-type-icon !m-[2px]"> <a-radio value="minder" class="show-type-icon !m-[2px]">
<MsIcon :size="14" type="icon-icon_mindnote_outlined" /> <MsIcon :size="14" type="icon-icon_mindnote_outlined" />
</a-radio> </a-radio>
</a-radio-group> </a-radio-group>
@ -203,7 +209,7 @@
</div> </div>
<div class="mt-[16px] h-[calc(100%-32px)] border-t border-[var(--color-text-n8)]"> <div class="mt-[16px] h-[calc(100%-32px)] border-t border-[var(--color-text-n8)]">
<!-- 脑图开始 --> <!-- 脑图开始 -->
<MsMinder <MsFeatureCaseMinder
minder-type="FeatureCase" minder-type="FeatureCase"
:module-id="props.activeFolder" :module-id="props.activeFolder"
:modules-count="props.modulesCount" :modules-count="props.modulesCount"
@ -313,10 +319,9 @@
import useTable from '@/components/pure/ms-table/useTable'; import useTable from '@/components/pure/ms-table/useTable';
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue'; import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import { ActionsItem } from '@/components/pure/ms-table-more-action/types'; import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue'; import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
import ExecuteStatusTag from '@/components/business/ms-case-associate/executeResult.vue'; import ExecuteStatusTag from '@/components/business/ms-case-associate/executeResult.vue';
import MsMinder from '@/components/business/ms-minders/index.vue'; import MsFeatureCaseMinder from '@/components/business/ms-minders/featureCaseMinder/index.vue';
import BatchEditModal from './batchEditModal.vue'; import BatchEditModal from './batchEditModal.vue';
import CaseDetailDrawer from './caseDetailDrawer.vue'; import CaseDetailDrawer from './caseDetailDrawer.vue';
import FeatureCaseTree from './caseTree.vue'; import FeatureCaseTree from './caseTree.vue';
@ -343,6 +348,7 @@
import useModal from '@/hooks/useModal'; import useModal from '@/hooks/useModal';
import { useAppStore, useTableStore } from '@/store'; import { useAppStore, useTableStore } from '@/store';
import useFeatureCaseStore from '@/store/modules/case/featureCase'; import useFeatureCaseStore from '@/store/modules/case/featureCase';
import useMinderStore from '@/store/modules/components/minder-editor';
import { characterLimit, findNodeByKey, findNodePathByKey, mapTree } from '@/utils'; import { characterLimit, findNodeByKey, findNodePathByKey, mapTree } from '@/utils';
import { hasAnyPermission } from '@/utils/permission'; import { hasAnyPermission } from '@/utils/permission';
@ -382,18 +388,33 @@
(e: 'import', type: 'Excel' | 'Xmind'): void; (e: 'import', type: 'Excel' | 'Xmind'): void;
}>(); }>();
const minderStore = useMinderStore();
const keyword = ref<string>(''); const keyword = ref<string>('');
const filterRowCount = ref(0); const filterRowCount = ref(0);
const groupKeyword = ref<string>(''); const groupKeyword = ref<string>('');
const showType = ref<string>('list'); const showType = ref<string>('list');
const versionOptions = ref([ function handleShowTypeChange(val: string | number | boolean) {
{ if (minderStore.minderUnsaved && val === 'list') {
id: '1001', showType.value = 'minder';
name: 'v_1.0', openModal({
}, type: 'warning',
]); title: t('common.tip'),
content: t('ms.minders.leaveUnsavedTip'),
okText: t('common.confirm'),
cancelText: t('common.cancel'),
okButtonProps: {
status: 'normal',
},
onBeforeOk: async () => {
showType.value = 'list';
},
hideCancel: false,
});
}
}
const caseTreeData = computed(() => { const caseTreeData = computed(() => {
return mapTree<ModuleTreeNode>(featureCaseStore.caseTree, (e) => { return mapTree<ModuleTreeNode>(featureCaseStore.caseTree, (e) => {