feat(脑图): 标签&优先级&添加交互规则
This commit is contained in:
parent
318b7fc950
commit
3209249045
|
@ -467,7 +467,7 @@
|
|||
.arco-radio-button {
|
||||
@apply bg-transparent;
|
||||
|
||||
margin: 1px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.arco-radio-checked {
|
||||
@apply bg-white;
|
||||
|
|
|
@ -0,0 +1,313 @@
|
|||
<template>
|
||||
<MsMinderEditor
|
||||
:tags="tags"
|
||||
:import-json="props.importJson"
|
||||
:replaceable-tags="replaceableTags"
|
||||
:insert-node="insertNode"
|
||||
:priority-disable-check="priorityDisableCheck"
|
||||
:after-tag-edit="afterTagEdit"
|
||||
single-tag
|
||||
tag-enable
|
||||
sequence-enable
|
||||
@click="handleNodeClick"
|
||||
>
|
||||
</MsMinderEditor>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MsMinderEditor from '@/components/pure/ms-minder-editor/minderEditor.vue';
|
||||
import type { MinderJson, MinderJsonNode, MinderJsonNodeData } from '@/components/pure/ms-minder-editor/props';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { getGenerateId } from '@/utils';
|
||||
|
||||
const props = defineProps<{
|
||||
importJson: MinderJson;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const caseTag = t('common.case');
|
||||
const moduleTag = t('common.module');
|
||||
const topTags = [moduleTag, caseTag];
|
||||
const descTags = [t('ms.minders.stepDesc'), t('ms.minders.textDesc')];
|
||||
const tags = [...topTags, t('ms.minders.precondition'), ...descTags, t('ms.minders.stepExpect'), t('common.remark')];
|
||||
const visible = ref<boolean>(false);
|
||||
const nodeData = ref<any>({});
|
||||
|
||||
function handleNodeClick(data: any) {
|
||||
if (data.resource && data.resource.includes(caseTag)) {
|
||||
visible.value = true;
|
||||
nodeData.value = data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 已选中节点的可替换标签判断
|
||||
* @param node 选中节点
|
||||
*/
|
||||
function replaceableTags(node: MinderJsonNode) {
|
||||
if (node.data?.resource?.some((e) => topTags.includes(e))) {
|
||||
// 选中节点属于顶级节点,可替换为除自身外的顶级标签
|
||||
return !node.children || node.children.length === 0
|
||||
? topTags.filter((tag) => !node.data?.resource?.includes(tag))
|
||||
: [];
|
||||
}
|
||||
if (node.data?.resource?.some((e) => descTags.includes(e))) {
|
||||
// 选中节点属于描述节点,可替换为除自身外的描述标签
|
||||
return descTags.filter((tag) => !node.data?.resource?.includes(tag));
|
||||
}
|
||||
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;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function execInert(command: string, node?: MinderJsonNodeData) {
|
||||
if (window.minder.queryCommandState(command) !== -1) {
|
||||
window.minder.execCommand(command, node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入前置条件
|
||||
* @param node 目标节点
|
||||
* @param type 插入类型
|
||||
*/
|
||||
function inertPrecondition(node: MinderJsonNode, type: string) {
|
||||
const child: MinderJsonNode = {
|
||||
parent: node,
|
||||
data: {
|
||||
id: getGenerateId(),
|
||||
text: t('ms.minders.precondition'),
|
||||
resource: [t('ms.minders.precondition')],
|
||||
expandState: 'expand',
|
||||
},
|
||||
children: [],
|
||||
};
|
||||
const sibling = {
|
||||
parent: child,
|
||||
data: {
|
||||
id: getGenerateId(),
|
||||
text: '',
|
||||
resource: [],
|
||||
},
|
||||
};
|
||||
execInert(type, child.data);
|
||||
nextTick(() => {
|
||||
execInert('AppendChildNode', sibling.data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入备注
|
||||
* @param node 目标节点
|
||||
* @param type 插入类型
|
||||
*/
|
||||
function insetRemark(node: MinderJsonNode, type: string) {
|
||||
const child = {
|
||||
parent: node,
|
||||
data: {
|
||||
id: getGenerateId(),
|
||||
text: t('common.remark'),
|
||||
resource: [t('common.remark')],
|
||||
},
|
||||
children: [],
|
||||
};
|
||||
execInert(type, child.data);
|
||||
}
|
||||
|
||||
// function insertTextDesc(node: MinderJsonNode, type: string) {
|
||||
// const child = {
|
||||
// parent: node,
|
||||
// data: {
|
||||
// id: getGenerateId(),
|
||||
// text: t('ms.minders.textDesc'),
|
||||
// resource: [t('ms.minders.textDesc')],
|
||||
// },
|
||||
// children: [],
|
||||
// };
|
||||
// const sibling = {
|
||||
// parent: child,
|
||||
// data: {
|
||||
// id: getGenerateId(),
|
||||
// text: t('ms.minders.stepExpect'),
|
||||
// resource: [t('ms.minders.stepExpect')],
|
||||
// },
|
||||
// };
|
||||
// execInert(type, {
|
||||
// ...child,
|
||||
// children: [sibling],
|
||||
// });
|
||||
// }
|
||||
|
||||
/**
|
||||
* 插入步骤描述
|
||||
* @param node 目标节点
|
||||
* @param type 插入类型
|
||||
*/
|
||||
function insetStepDesc(node: MinderJsonNode, type: string) {
|
||||
const child = {
|
||||
parent: node,
|
||||
data: {
|
||||
id: getGenerateId(),
|
||||
text: t('ms.minders.stepDesc'),
|
||||
resource: [t('ms.minders.stepDesc')],
|
||||
},
|
||||
children: [],
|
||||
};
|
||||
const sibling = {
|
||||
parent: child,
|
||||
data: {
|
||||
id: getGenerateId(),
|
||||
text: t('ms.minders.stepExpect'),
|
||||
resource: [t('ms.minders.stepExpect')],
|
||||
},
|
||||
};
|
||||
execInert(type, child.data);
|
||||
nextTick(() => {
|
||||
execInert('AppendChildNode', sibling.data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入预期结果
|
||||
* @param node 目标节点
|
||||
* @param type 插入类型
|
||||
*/
|
||||
function insertExpect(node: MinderJsonNode, type: string) {
|
||||
const child = {
|
||||
parent: node,
|
||||
data: {
|
||||
id: getGenerateId(),
|
||||
text: t('ms.minders.stepExpect'),
|
||||
resource: [t('ms.minders.stepExpect')],
|
||||
},
|
||||
children: [],
|
||||
};
|
||||
execInert(type, child.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入节点
|
||||
* @param node 目标节点
|
||||
* @param type 插入类型
|
||||
*/
|
||||
function insertNode(node: MinderJsonNode, type: string) {
|
||||
switch (type) {
|
||||
case 'AppendChildNode':
|
||||
if (node.data?.resource?.includes(moduleTag)) {
|
||||
execInert('AppendChildNode');
|
||||
} else if (node.data?.resource?.includes(caseTag)) {
|
||||
// 给用例插入子节点
|
||||
if (!node.children || node.children.length === 0) {
|
||||
// 当前用例还没有子节点,默认添加一个前置条件
|
||||
inertPrecondition(node, type);
|
||||
} else if (node.children.length > 0) {
|
||||
// 当前用例有子节点
|
||||
let hasPreCondition = false;
|
||||
let hasTextDesc = false;
|
||||
let hasRemark = false;
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
const child = node.children[i];
|
||||
if (child.data?.resource?.includes(t('ms.minders.precondition'))) {
|
||||
hasPreCondition = true;
|
||||
} else if (child.data?.resource?.includes(t('ms.minders.textDesc'))) {
|
||||
hasTextDesc = true;
|
||||
} else if (child.data?.resource?.includes(t('common.remark'))) {
|
||||
hasRemark = true;
|
||||
}
|
||||
}
|
||||
if (!hasPreCondition) {
|
||||
// 没有前置条件,则默认添加一个前置条件
|
||||
inertPrecondition(node, type);
|
||||
} else if (!hasRemark) {
|
||||
// 没有备注,则默认添加一个备注
|
||||
insetRemark(node, type);
|
||||
} else if (!hasTextDesc) {
|
||||
// 没有文本描述,则默认添加一个步骤描述
|
||||
insetStepDesc(node, type);
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
(node.data?.resource?.includes(t('ms.minders.stepDesc')) ||
|
||||
node.data?.resource?.includes(t('ms.minders.textDesc'))) &&
|
||||
(!node.children || node.children.length === 0)
|
||||
) {
|
||||
// 当前节点是步骤描述或文本描述,且没有子节点,则默认添加一个预期结果
|
||||
insertExpect(node, 'AppendChildNode');
|
||||
} else if (node.data?.resource?.includes(t('ms.minders.precondition'))) {
|
||||
// 当前节点是前置条件,则默认添加一个文本节点
|
||||
execInert('AppendChildNode');
|
||||
}
|
||||
break;
|
||||
case 'AppendParentNode':
|
||||
execInert('AppendParentNode');
|
||||
break;
|
||||
case 'AppendSiblingNode':
|
||||
if (node.parent?.data?.resource?.includes(caseTag) && node.parent?.children) {
|
||||
// 当前节点的父节点是用例
|
||||
let hasPreCondition = false;
|
||||
let hasTextDesc = false;
|
||||
let hasRemark = false;
|
||||
for (let i = 0; i < node.parent.children.length; i++) {
|
||||
const sibling = node.parent.children[i];
|
||||
if (sibling.data?.resource?.includes(t('ms.minders.precondition'))) {
|
||||
hasPreCondition = true;
|
||||
} else if (sibling.data?.resource?.includes(t('common.remark'))) {
|
||||
hasRemark = true;
|
||||
} else if (sibling.data?.resource?.includes(t('ms.minders.textDesc'))) {
|
||||
hasTextDesc = true;
|
||||
}
|
||||
}
|
||||
if (!hasPreCondition) {
|
||||
// 没有前置条件,则默认添加一个前置条件
|
||||
inertPrecondition(node, type);
|
||||
} else if (!hasRemark) {
|
||||
// 没有备注,则默认添加一个备注
|
||||
insetRemark(node, type);
|
||||
} else if (!hasTextDesc) {
|
||||
// 没有文本描述,则默认添加一个步骤描述
|
||||
insetStepDesc(node, type);
|
||||
}
|
||||
} else if (node.parent?.data?.resource?.includes(moduleTag) || !node.parent?.data?.resource) {
|
||||
// 当前节点的父节点是模块或没有标签,则默认添加一个文本节点
|
||||
execInert('AppendSiblingNode');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function priorityDisableCheck(node: MinderJsonNode) {
|
||||
if (node.data?.resource?.includes(caseTag)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标签编辑后,如果将标签修改为模块,则删除已添加的优先级
|
||||
* @param node 选中节点
|
||||
* @param tag 更改后的标签
|
||||
*/
|
||||
function afterTagEdit(node: MinderJsonNode, tag: string) {
|
||||
if (tag === moduleTag && node.data) {
|
||||
window.minder.execCommand('priority');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<FeatureCaseMinder :import-json="props.importJson" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { MinderJson } from '@/components/pure/ms-minder-editor/props';
|
||||
import FeatureCaseMinder from '@/components/business/ms-minders/featureCaseMinder.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
minderType: 'FeatureCase';
|
||||
importJson: MinderJson;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
|
@ -0,0 +1,6 @@
|
|||
export default {
|
||||
'ms.minders.precondition': 'Precondition',
|
||||
'ms.minders.stepDesc': 'Step Description',
|
||||
'ms.minders.stepExpect': 'Expected Result',
|
||||
'ms.minders.textDesc': 'Text Description',
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
export default {
|
||||
'ms.minders.precondition': '前置条件',
|
||||
'ms.minders.stepDesc': '步骤描述',
|
||||
'ms.minders.stepExpect': '预期结果',
|
||||
'ms.minders.textDesc': '文本描述',
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<slot name="left"></slot>
|
||||
<div class="flex flex-row gap-[8px]">
|
||||
<div class="flex flex-row gap-[12px]">
|
||||
<a-input-search
|
||||
v-model:modelValue="innerKeyword"
|
||||
size="small"
|
||||
|
|
|
@ -1,34 +1,55 @@
|
|||
<template>
|
||||
<header>
|
||||
<div class="mind-tab-panel">
|
||||
<edit-menu
|
||||
:minder="minder"
|
||||
:move-enable="props.moveEnable"
|
||||
:move-confirm="props.moveConfirm"
|
||||
:sequence-enable="props.sequenceEnable"
|
||||
:tag-enable="props.tagEnable"
|
||||
:progress-enable="props.progressEnable"
|
||||
:priority-count="props.priorityCount"
|
||||
:priority-prefix="props.priorityPrefix"
|
||||
:tag-edit-check="props.tagEditCheck"
|
||||
:tag-disable-check="props.tagDisableCheck"
|
||||
:priority-disable-check="props.priorityDisableCheck"
|
||||
:priority-start-with-zero="props.priorityStartWithZero"
|
||||
:tags="props.tags"
|
||||
:distinct-tags="props.distinctTags"
|
||||
:del-confirm="props.delConfirm"
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
<!-- <div class="ms-minder-editor-header">
|
||||
<a-tooltip v-for="item of props.iconButtons" :key="item.eventTag" :content="t(item.tooltip)">
|
||||
<MsButton type="icon" class="ms-minder-editor-header-icon-button" @click="emit('click', item.eventTag)">
|
||||
<MsIcon :type="item.icon" class="text-[var(--color-text-4)]" />
|
||||
</MsButton>
|
||||
</a-tooltip>
|
||||
<a-divider v-if="props.iconButtons?.length" direction="vertical" :margin="8"></a-divider>
|
||||
<a-tooltip :content="isFullScreen ? t('common.offFullScreen') : t('common.fullScreen')">
|
||||
<MsButton v-if="isFullScreen" type="icon" class="ms-minder-editor-header-icon-button" @click="toggleFullScreen">
|
||||
<MsIcon type="icon-icon_off_screen" class="text-[var(--color-text-4)]" />
|
||||
</MsButton>
|
||||
<MsButton v-else type="icon" class="ms-minder-editor-header-icon-button" @click="toggleFullScreen">
|
||||
<MsIcon type="icon-icon_full_screen_one" class="text-[var(--color-text-4)]" />
|
||||
</MsButton>
|
||||
</a-tooltip>
|
||||
</div> -->
|
||||
<div class="mind-tab-panel">
|
||||
<editMenu
|
||||
:minder="minder"
|
||||
:move-enable="props.moveEnable"
|
||||
:move-confirm="props.moveConfirm"
|
||||
:sequence-enable="props.sequenceEnable"
|
||||
:tag-enable="props.tagEnable"
|
||||
:progress-enable="props.progressEnable"
|
||||
:priority-count="props.priorityCount"
|
||||
:priority-prefix="props.priorityPrefix"
|
||||
:tag-edit-check="props.tagEditCheck"
|
||||
:tag-disable-check="props.tagDisableCheck"
|
||||
:priority-disable-check="props.priorityDisableCheck"
|
||||
:priority-start-with-zero="props.priorityStartWithZero"
|
||||
:tags="props.tags"
|
||||
:distinct-tags="props.distinctTags"
|
||||
:del-confirm="props.delConfirm"
|
||||
:replaceable-tags="props.replaceableTags"
|
||||
:single-tag="props.singleTag"
|
||||
:insert-node="props.insertNode"
|
||||
:after-tag-edit="props.afterTagEdit"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="headerVue" setup>
|
||||
<script lang="ts" setup>
|
||||
// import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
// import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import editMenu from '../menu/edit/editMenu.vue';
|
||||
|
||||
import { delProps, editMenuProps, moleProps, priorityProps, tagProps, viewMenuProps } from '../props';
|
||||
import { delProps, editMenuProps, insertProps, moleProps, priorityProps, tagProps, viewMenuProps } from '../props';
|
||||
|
||||
const props = defineProps({
|
||||
...editMenuProps,
|
||||
...insertProps,
|
||||
...moleProps,
|
||||
...priorityProps,
|
||||
...tagProps,
|
||||
|
@ -36,6 +57,21 @@
|
|||
...viewMenuProps,
|
||||
minder: null,
|
||||
});
|
||||
// import useFullScreen from '@/hooks/useFullScreen';
|
||||
// import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
// import { headerProps } from '../props';
|
||||
|
||||
// const props = defineProps({
|
||||
// ...headerProps,
|
||||
// });
|
||||
// const emit = defineEmits<{
|
||||
// (e: 'click', eventTag: string): void;
|
||||
// }>();
|
||||
|
||||
// const { t } = useI18n();
|
||||
|
||||
// const { toggleFullScreen, isFullScreen } = useFullScreen(document.querySelector('.ms-minder-editor-container'));
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
@ -46,4 +82,21 @@
|
|||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
// .ms-minder-editor-header {
|
||||
// @apply absolute z-10 flex items-center bg-white;
|
||||
|
||||
// top: 24px;
|
||||
// right: 0;
|
||||
// padding: 4px 8px;
|
||||
// border-radius: var(--border-radius-small);
|
||||
// box-shadow: 0 4px 10px -1px rgb(100 100 102 / 15%);
|
||||
// .ms-minder-editor-header-icon-button {
|
||||
// &:hover {
|
||||
// background-color: rgb(var(--primary-1)) !important;
|
||||
// .arco-icon {
|
||||
// color: rgb(var(--primary-4)) !important;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div ref="mec" class="minder-container" :style="{ height: `${props.height}px` }">
|
||||
<div ref="mec" class="minder-container">
|
||||
<a-button type="primary" :disabled="props.disabled" class="save-btn bottom-[30px] right-[30px]" @click="save">
|
||||
{{ t('minder.main.main.save') }}
|
||||
</a-button>
|
||||
|
@ -92,13 +92,13 @@
|
|||
import useMinderStore from '@/store/modules/components/minder-editor';
|
||||
import { findNodePathByKey } from '@/utils';
|
||||
|
||||
import { editMenuProps, mainEditorProps, priorityProps, tagProps } from '../props';
|
||||
import { editMenuProps, insertProps, mainEditorProps, MinderJsonNode, priorityProps, tagProps } from '../props';
|
||||
import Editor from '../script/editor';
|
||||
import { markChangeNode, markDeleteNode } from '../script/tool/utils';
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({ ...editMenuProps, ...mainEditorProps, ...tagProps, ...priorityProps });
|
||||
const props = defineProps({ ...editMenuProps, ...insertProps, ...mainEditorProps, ...tagProps, ...priorityProps });
|
||||
|
||||
const emit = defineEmits({
|
||||
afterMount: () => ({}),
|
||||
|
@ -246,6 +246,17 @@
|
|||
}
|
||||
);
|
||||
|
||||
function execInsertCommand(command: string) {
|
||||
const node: MinderJsonNode = window.minder.getSelectedNode();
|
||||
if (props.insertNode) {
|
||||
props.insertNode(node, command);
|
||||
return;
|
||||
}
|
||||
if (window.minder.queryCommandState(command) !== -1) {
|
||||
window.minder.execCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
function handleMinderMenuSelect(val: string | number | Record<string, any> | undefined) {
|
||||
const selectedNode = window.minder.getSelectedNode();
|
||||
switch (val) {
|
||||
|
@ -257,13 +268,13 @@
|
|||
}
|
||||
break;
|
||||
case 'insetParent':
|
||||
window.minder.execCommand('AppendParentNode');
|
||||
execInsertCommand('AppendParentNode');
|
||||
break;
|
||||
case 'insetSon':
|
||||
window.minder.execCommand('AppendChildNode');
|
||||
execInsertCommand('AppendChildNode');
|
||||
break;
|
||||
case 'insetBrother':
|
||||
window.minder.execCommand('AppendSiblingNode');
|
||||
execInsertCommand('AppendSiblingNode');
|
||||
break;
|
||||
case 'copy':
|
||||
window.minder.execCommand('Copy');
|
||||
|
@ -292,7 +303,9 @@
|
|||
@apply !absolute;
|
||||
}
|
||||
.minder-container {
|
||||
@apply relative;
|
||||
@apply relative !bg-white;
|
||||
|
||||
height: calc(100% - 60px);
|
||||
}
|
||||
.minder-dropdown {
|
||||
.arco-dropdown-list-wrapper {
|
||||
|
|
|
@ -18,18 +18,19 @@
|
|||
{{ t('minder.menu.expand.folding') }}
|
||||
</div>
|
||||
<move-box :move-enable="props.moveEnable" :move-confirm="props.moveConfirm" />
|
||||
<insert-box />
|
||||
<insert-box :insert-node="props.insertNode" />
|
||||
<edit-del :del-confirm="props.delConfirm" />
|
||||
</div>
|
||||
<div class="menu-group">
|
||||
<tag-box
|
||||
v-if="props.tagEnable"
|
||||
:tags="props.tags"
|
||||
:tag-disable-check="props.tagDisableCheck"
|
||||
:tag-edit-check="props.tagEditCheck"
|
||||
:distinct-tags="props.distinctTags"
|
||||
/>
|
||||
</div>
|
||||
<tag-box
|
||||
v-if="props.tagEnable"
|
||||
:tags="props.tags"
|
||||
:tag-disable-check="props.tagDisableCheck"
|
||||
:tag-edit-check="props.tagEditCheck"
|
||||
:distinct-tags="props.distinctTags"
|
||||
:replaceable-tags="props.replaceableTags"
|
||||
:single-tag="props.singleTag"
|
||||
:after-tag-edit="props.afterTagEdit"
|
||||
/>
|
||||
<div class="menu-group">
|
||||
<sequence-box
|
||||
v-if="props.sequenceEnable"
|
||||
|
@ -55,10 +56,11 @@
|
|||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { delProps, editMenuProps, moleProps, priorityProps, tagProps, viewMenuProps } from '../../props';
|
||||
import { delProps, editMenuProps, insertProps, moleProps, priorityProps, tagProps, viewMenuProps } from '../../props';
|
||||
|
||||
const props = defineProps({
|
||||
...editMenuProps,
|
||||
...insertProps,
|
||||
...priorityProps,
|
||||
...tagProps,
|
||||
...delProps,
|
||||
|
|
|
@ -26,8 +26,13 @@
|
|||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { insertProps, MinderJsonNode } from '../../props';
|
||||
import { isDisableNode } from '../../script/tool/utils';
|
||||
|
||||
const props = defineProps({
|
||||
...insertProps,
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const minder = ref<any>({});
|
||||
|
@ -58,6 +63,11 @@
|
|||
});
|
||||
|
||||
function execCommand(command: string) {
|
||||
const node: MinderJsonNode = minder.value.getSelectedNode();
|
||||
if (props.insertNode) {
|
||||
props.insertNode(node, command);
|
||||
return;
|
||||
}
|
||||
if (minder.value.queryCommandState(command) !== -1) {
|
||||
minder.value.execCommand(command);
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
return true;
|
||||
}
|
||||
if (props.priorityDisableCheck) {
|
||||
return props.priorityDisableCheck();
|
||||
return props.priorityDisableCheck(node);
|
||||
}
|
||||
return !!minder.queryCommandState && minder.queryCommandState('priority') === -1;
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="flex items-center">
|
||||
<div v-if="tagList.length > 0" class="menu-group flex items-center">
|
||||
<a-tag
|
||||
v-for="item in props.tags"
|
||||
v-for="item in tagList"
|
||||
:key="item"
|
||||
:color="getResourceColor(item)"
|
||||
:class="commandDisabled ? 'disabledTag' : ''"
|
||||
|
@ -15,7 +15,7 @@
|
|||
<script lang="ts" name="TagBox" setup>
|
||||
import { nextTick, onMounted, reactive, ref } from 'vue';
|
||||
|
||||
import { tagProps } from '../../props';
|
||||
import { MinderJsonNode, tagProps } from '../../props';
|
||||
import { isDisableNode, isTagEnable } from '../../script/tool/utils';
|
||||
|
||||
const props = defineProps(tagProps);
|
||||
|
@ -34,11 +34,21 @@
|
|||
return !!minder.queryCommandState && minder.queryCommandState('resource') === -1;
|
||||
};
|
||||
|
||||
const tagList = ref(props.tags);
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
minder = window.minder;
|
||||
minder.on('selectionchange', () => {
|
||||
commandDisabled.value = isDisable();
|
||||
const node: MinderJsonNode = minder.getSelectedNode();
|
||||
if (commandDisabled.value) {
|
||||
tagList.value = [];
|
||||
} else if (props.replaceableTags) {
|
||||
tagList.value = props.replaceableTags(node);
|
||||
} else {
|
||||
tagList.value = [];
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -54,7 +64,8 @@
|
|||
return;
|
||||
}
|
||||
if (props.tagEditCheck) {
|
||||
if (!props.tagEditCheck(resourceName)) {
|
||||
const node: MinderJsonNode = minder.getSelectedNode();
|
||||
if (!props.tagEditCheck(node, resourceName)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -62,22 +73,33 @@
|
|||
return;
|
||||
}
|
||||
const origin = window.minder.queryCommandValue('resource');
|
||||
const index = origin.indexOf(resourceName);
|
||||
// 先删除排他的标签
|
||||
if (props.distinctTags.indexOf(resourceName) > -1) {
|
||||
for (let i = 0; i < origin.length; i++) {
|
||||
if (props.distinctTags.indexOf(origin[i]) > -1) {
|
||||
origin.splice(i, 1);
|
||||
i--;
|
||||
if (props.singleTag) {
|
||||
origin.splice(0, origin.length, resourceName);
|
||||
} else {
|
||||
const index = origin.indexOf(resourceName);
|
||||
// 先删除排他的标签
|
||||
if (props.distinctTags.indexOf(resourceName) > -1) {
|
||||
for (let i = 0; i < origin.length; i++) {
|
||||
if (props.distinctTags.indexOf(origin[i]) > -1) {
|
||||
origin.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index !== -1) {
|
||||
origin.splice(index, 1);
|
||||
} else {
|
||||
origin.push(resourceName);
|
||||
if (index !== -1) {
|
||||
origin.splice(index, 1);
|
||||
} else {
|
||||
origin.push(resourceName);
|
||||
}
|
||||
}
|
||||
window.minder.execCommand('resource', origin);
|
||||
const node: MinderJsonNode = minder.getSelectedNode();
|
||||
if (props.replaceableTags) {
|
||||
tagList.value = props.replaceableTags(node);
|
||||
}
|
||||
if (props.afterTagEdit) {
|
||||
props.afterTagEdit(node, resourceName);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="main-container">
|
||||
<header-menu
|
||||
<div class="ms-minder-editor-container">
|
||||
<minderHeader
|
||||
:sequence-enable="props.sequenceEnable"
|
||||
:tag-enable="props.tagEnable"
|
||||
:progress-enable="props.progressEnable"
|
||||
|
@ -20,9 +20,13 @@
|
|||
:mold-enable="props.moldEnable"
|
||||
:font-enable="props.fontEnable"
|
||||
:style-enable="props.styleEnable"
|
||||
:replaceable-tags="props.replaceableTags"
|
||||
:single-tag="props.singleTag"
|
||||
:insert-node="props.insertNode"
|
||||
:after-tag-edit="props.afterTagEdit"
|
||||
@mold-change="handleMoldChange"
|
||||
/>
|
||||
<main-editor
|
||||
<mainEditor
|
||||
:disabled="props.disabled"
|
||||
:sequence-enable="props.sequenceEnable"
|
||||
:tag-enable="props.tagEnable"
|
||||
|
@ -38,6 +42,7 @@
|
|||
:priority-count="props.priorityCount"
|
||||
:priority-prefix="props.priorityPrefix"
|
||||
:priority-start-with-zero="props.priorityStartWithZero"
|
||||
:insert-node="props.insertNode"
|
||||
@after-mount="emit('afterMount')"
|
||||
@save="save"
|
||||
@enter-node="handleEnterNode"
|
||||
|
@ -48,10 +53,20 @@
|
|||
<script lang="ts" name="minderEditor" setup>
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
import headerMenu from './main/header.vue';
|
||||
import minderHeader from './main/header.vue';
|
||||
import mainEditor from './main/mainEditor.vue';
|
||||
|
||||
import { delProps, editMenuProps, mainEditorProps, moleProps, priorityProps, tagProps, viewMenuProps } from './props';
|
||||
import {
|
||||
delProps,
|
||||
editMenuProps,
|
||||
headerProps,
|
||||
insertProps,
|
||||
mainEditorProps,
|
||||
moleProps,
|
||||
priorityProps,
|
||||
tagProps,
|
||||
viewMenuProps,
|
||||
} from './props';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'moldChange', data: number): void;
|
||||
|
@ -62,6 +77,8 @@
|
|||
}>();
|
||||
|
||||
const props = defineProps({
|
||||
...headerProps,
|
||||
...insertProps,
|
||||
...editMenuProps,
|
||||
...mainEditorProps,
|
||||
...moleProps,
|
||||
|
@ -103,3 +120,9 @@
|
|||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ms-minder-editor-container {
|
||||
@apply relative h-full;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -2,9 +2,35 @@
|
|||
* Api 列表
|
||||
*/
|
||||
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
export interface MinderIconButtonItem {
|
||||
icon: string;
|
||||
tooltip: string;
|
||||
eventTag: string;
|
||||
}
|
||||
export interface MinderJsonNodeData {
|
||||
id: string;
|
||||
text: string;
|
||||
resource?: string[];
|
||||
expandState?: string;
|
||||
priority?: number;
|
||||
}
|
||||
export interface MinderJsonNode {
|
||||
parent?: MinderJsonNode;
|
||||
data?: MinderJsonNodeData;
|
||||
children?: MinderJsonNode[];
|
||||
}
|
||||
|
||||
export interface MinderJson {
|
||||
root: MinderJsonNode;
|
||||
template: string;
|
||||
treePath: Record<string, MinderJsonNode>[];
|
||||
}
|
||||
|
||||
export const mainEditorProps = {
|
||||
importJson: {
|
||||
type: Object,
|
||||
type: Object as PropType<MinderJson>,
|
||||
default() {
|
||||
return {
|
||||
root: {},
|
||||
|
@ -20,6 +46,12 @@ export const mainEditorProps = {
|
|||
disabled: Boolean,
|
||||
};
|
||||
|
||||
export const headerProps = {
|
||||
iconButtons: {
|
||||
type: [] as PropType<MinderIconButtonItem[]>,
|
||||
},
|
||||
};
|
||||
|
||||
export const priorityProps = {
|
||||
priorityCount: {
|
||||
type: Number,
|
||||
|
@ -39,27 +71,45 @@ export const priorityProps = {
|
|||
type: String,
|
||||
default: 'P',
|
||||
},
|
||||
priorityDisableCheck: Function,
|
||||
priorityDisableCheck: Function as PropType<(node: MinderJsonNode) => boolean>,
|
||||
operators: [],
|
||||
};
|
||||
|
||||
export interface MinderReplaceTag {
|
||||
tags: string[];
|
||||
condition: (node: MinderJsonNode, tags: string[]) => boolean;
|
||||
}
|
||||
export const tagProps = {
|
||||
tags: {
|
||||
// 自定义标签
|
||||
type: Array<string>,
|
||||
default() {
|
||||
return [] as string[];
|
||||
return [];
|
||||
},
|
||||
},
|
||||
distinctTags: {
|
||||
// 个别标签二选一
|
||||
type: Array<string>,
|
||||
default() {
|
||||
return [] as string[];
|
||||
return [];
|
||||
},
|
||||
},
|
||||
singleTag: {
|
||||
// 单标签
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
replaceableTags: Function as PropType<(node: MinderJsonNode) => string[]>,
|
||||
tagDisableCheck: Function,
|
||||
tagEditCheck: Function,
|
||||
tagEditCheck: Function as PropType<(node: MinderJsonNode, tag: string) => boolean>,
|
||||
afterTagEdit: Function as PropType<(node: MinderJsonNode, tag: string) => void>,
|
||||
};
|
||||
|
||||
export const insertProps = {
|
||||
insertNode: {
|
||||
type: Function as PropType<(node: MinderJsonNode, type: string) => void>,
|
||||
default: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
export const editMenuProps = {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@import 'dropdown-list.less';
|
||||
.mind-tab-panel {
|
||||
@apply h-full w-full;
|
||||
@apply w-full;
|
||||
.menu-container {
|
||||
@apply flex;
|
||||
|
||||
|
|
|
@ -754,6 +754,9 @@
|
|||
.arco-table-td-content {
|
||||
@apply justify-center;
|
||||
}
|
||||
.arco-table-cell {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
:deep(.ms-table-select-all) {
|
||||
.dropdown-icon {
|
||||
|
|
|
@ -173,4 +173,6 @@ export default {
|
|||
'common.fakeError': 'Fake error',
|
||||
'common.belongModule': 'Belong module',
|
||||
'common.moreSetting': 'More settings',
|
||||
'common.remark': 'Remark',
|
||||
'common.case': 'Case',
|
||||
};
|
||||
|
|
|
@ -176,4 +176,6 @@ export default {
|
|||
'common.executionResult': '执行结果',
|
||||
'common.detail': '详情',
|
||||
'common.baseInfo': '基本信息',
|
||||
'common.remark': '备注',
|
||||
'common.case': '用例',
|
||||
};
|
||||
|
|
|
@ -252,7 +252,7 @@
|
|||
JSONPath({
|
||||
json: parseJson.value,
|
||||
path: expressionForm.value.expression,
|
||||
})?.map((e: any) => JSON.stringify(e).replace(/"Number\(([^)]+)\)"|Number\(([^)]+)\)/g, '$1$2')) || [];
|
||||
})?.map((e: any) => JSON.stringify(e).replace(/Number\(([^)]+)\)|Number\(([^)]+)\)/g, '$1$2')) || [];
|
||||
} catch (error) {
|
||||
matchResult.value = JSONPath({ json: props.response || '', path: expressionForm.value.expression }) || [];
|
||||
}
|
||||
|
|
|
@ -184,8 +184,6 @@
|
|||
},
|
||||
],
|
||||
width: 150,
|
||||
titleSlotName: 'typeTitle',
|
||||
typeTitleTooltip: t('apiScenario.params.typeTooltip'),
|
||||
},
|
||||
{
|
||||
title: 'apiScenario.params.paramValue',
|
||||
|
|
|
@ -1,205 +1,231 @@
|
|||
<!-- eslint-disable prefer-destructuring -->
|
||||
<template>
|
||||
<!-- 用例表开始 -->
|
||||
<MsAdvanceFilter
|
||||
v-model:keyword="keyword"
|
||||
:filter-config-list="filterConfigList"
|
||||
:custom-fields-config-list="searchCustomFields"
|
||||
:search-placeholder="t('caseManagement.featureCase.searchPlaceholder')"
|
||||
:row-count="filterRowCount"
|
||||
@keyword-search="fetchData"
|
||||
@adv-search="handleAdvSearch"
|
||||
@refresh="fetchData()"
|
||||
>
|
||||
<template #left>
|
||||
<a-popover title="" position="bottom">
|
||||
<div class="show-table-top-title">
|
||||
<div class="one-line-text max-h-[32px] max-w-[300px] text-[var(--color-text-1)]">
|
||||
{{ moduleNamePath }}
|
||||
</div>
|
||||
<span class="text-[var(--color-text-4)]"> ({{ props.modulesCount[props.activeFolder] || 0 }})</span>
|
||||
</div>
|
||||
<template #content>
|
||||
<div class="max-w-[400px] text-[14px] font-medium text-[var(--color-text-1)]">
|
||||
{{ moduleNamePath }}
|
||||
<span class="text-[var(--color-text-4)]">({{ props.modulesCount[props.activeFolder] || 0 }})</span>
|
||||
</div>
|
||||
</template>
|
||||
</a-popover>
|
||||
</template>
|
||||
<template #right>
|
||||
<!-- TODO 暂时先不展示了 -->
|
||||
<!-- <a-radio-group v-model:model-value="showType" type="button" class="file-show-type">
|
||||
<a-radio value="list" class="show-type-icon p-[2px]"><MsIcon type="icon-icon_view-list_outlined" /></a-radio>
|
||||
<a-radio value="xMind" class="show-type-icon p-[2px]"><MsIcon type="icon-icon_mindnote_outlined" /></a-radio>
|
||||
</a-radio-group> -->
|
||||
</template>
|
||||
</MsAdvanceFilter>
|
||||
<ms-base-table
|
||||
v-if="showType === 'list'"
|
||||
v-bind="propsRes"
|
||||
ref="tableRef"
|
||||
filter-icon-align-left
|
||||
class="mt-[16px]"
|
||||
:action-config="tableBatchActions"
|
||||
@selected-change="handleTableSelect"
|
||||
v-on="propsEvent"
|
||||
@batch-action="handleTableBatch"
|
||||
@change="changeHandler"
|
||||
@module-change="initData()"
|
||||
@cell-click="handleCellClick"
|
||||
>
|
||||
<template #num="{ record }">
|
||||
<span type="text" class="one-line-text cursor-pointer px-0 text-[rgb(var(--primary-5))]">{{ record.num }}</span>
|
||||
</template>
|
||||
<template #name="{ record }">
|
||||
<div class="one-line-text">{{ characterLimit(record.name) }}</div>
|
||||
</template>
|
||||
<template #caseLevel="{ record }">
|
||||
<a-select
|
||||
v-model:model-value="record.caseLevel"
|
||||
:placeholder="t('common.pleaseSelect')"
|
||||
class="param-input w-full"
|
||||
@click.stop
|
||||
@change="() => handleStatusChange(record)"
|
||||
<div class="h-full">
|
||||
<!-- 用例表开始 -->
|
||||
<template v-if="showType === 'list'">
|
||||
<MsAdvanceFilter
|
||||
v-model:keyword="keyword"
|
||||
:filter-config-list="filterConfigList"
|
||||
:custom-fields-config-list="searchCustomFields"
|
||||
:search-placeholder="t('caseManagement.featureCase.searchPlaceholder')"
|
||||
:row-count="filterRowCount"
|
||||
@keyword-search="fetchData"
|
||||
@adv-search="handleAdvSearch"
|
||||
@refresh="fetchData()"
|
||||
>
|
||||
<template #label>
|
||||
<span class="text-[var(--color-text-2)]"> <caseLevel :case-level="record.caseLevel" /></span>
|
||||
<template #left>
|
||||
<a-popover title="" position="bottom">
|
||||
<div class="show-table-top-title">
|
||||
<div class="one-line-text max-h-[32px] max-w-[300px] text-[var(--color-text-1)]">
|
||||
{{ moduleNamePath }}
|
||||
</div>
|
||||
<span class="text-[var(--color-text-4)]"> ({{ props.modulesCount[props.activeFolder] || 0 }})</span>
|
||||
</div>
|
||||
<template #content>
|
||||
<div class="max-w-[400px] text-[14px] font-medium text-[var(--color-text-1)]">
|
||||
{{ moduleNamePath }}
|
||||
<span class="text-[var(--color-text-4)]">({{ props.modulesCount[props.activeFolder] || 0 }})</span>
|
||||
</div>
|
||||
</template>
|
||||
</a-popover>
|
||||
</template>
|
||||
<a-option v-for="item of caseLevelList" :key="item.value" :value="item.value">
|
||||
<caseLevel :case-level="item.text" />
|
||||
</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<!-- 用例等级 -->
|
||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL]="{ filterContent }">
|
||||
<caseLevel :case-level="filterContent.text" />
|
||||
</template>
|
||||
<!-- 执行结果 -->
|
||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT]="{ filterContent }">
|
||||
<ExecuteStatusTag :execute-result="filterContent.value" />
|
||||
</template>
|
||||
<!-- 评审结果 -->
|
||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_REVIEW_RESULT]="{ filterContent }">
|
||||
<MsIcon
|
||||
:type="statusIconMap[filterContent.value]?.icon"
|
||||
class="mr-1"
|
||||
:class="[statusIconMap[filterContent.value].color]"
|
||||
></MsIcon>
|
||||
<span>{{ statusIconMap[filterContent.value]?.statusText }} </span>
|
||||
</template>
|
||||
<template #reviewStatus="{ record }">
|
||||
<MsIcon
|
||||
:type="statusIconMap[record.reviewStatus]?.icon || ''"
|
||||
class="mr-1"
|
||||
:class="[statusIconMap[record.reviewStatus].color]"
|
||||
></MsIcon>
|
||||
<span>{{ statusIconMap[record.reviewStatus]?.statusText || '' }} </span>
|
||||
</template>
|
||||
<template #lastExecuteResult="{ record }">
|
||||
<ExecuteStatusTag :execute-result="record.lastExecuteResult" />
|
||||
</template>
|
||||
<template #moduleId="{ record }">
|
||||
<a-tree-select
|
||||
v-if="record.showModuleTree"
|
||||
v-model:modelValue="record.moduleId"
|
||||
dropdown-class-name="tree-dropdown"
|
||||
class="param-input w-full"
|
||||
:data="caseTreeData"
|
||||
:allow-search="true"
|
||||
:field-names="{
|
||||
title: 'name',
|
||||
key: 'id',
|
||||
children: 'children',
|
||||
}"
|
||||
:tree-props="{
|
||||
virtualListProps: {
|
||||
height: 200,
|
||||
},
|
||||
}"
|
||||
@click.stop
|
||||
@change="(value) => handleChangeModule(record, value)"
|
||||
<template #right>
|
||||
<a-radio-group v-model:model-value="showType" type="button" size="small" class="list-show-type">
|
||||
<a-radio value="list" class="show-type-icon !m-[2px]">
|
||||
<MsIcon :size="14" type="icon-icon_view-list_outlined" />
|
||||
</a-radio>
|
||||
<a-radio value="xMind" class="show-type-icon !m-[2px]">
|
||||
<MsIcon :size="14" type="icon-icon_mindnote_outlined" />
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</template>
|
||||
</MsAdvanceFilter>
|
||||
<ms-base-table
|
||||
v-bind="propsRes"
|
||||
ref="tableRef"
|
||||
filter-icon-align-left
|
||||
class="mt-[16px]"
|
||||
:action-config="tableBatchActions"
|
||||
@selected-change="handleTableSelect"
|
||||
v-on="propsEvent"
|
||||
@batch-action="handleTableBatch"
|
||||
@change="changeHandler"
|
||||
@module-change="initData()"
|
||||
@cell-click="handleCellClick"
|
||||
>
|
||||
<template #tree-slot-title="node">
|
||||
<a-tooltip :content="`${node.name}`" position="tl">
|
||||
<div class="one-line-text max-w-[200px] text-[var(--color-text-1)]">{{ node.name }}</div>
|
||||
<template #num="{ record }">
|
||||
<span type="text" class="one-line-text cursor-pointer px-0 text-[rgb(var(--primary-5))]">{{
|
||||
record.num
|
||||
}}</span>
|
||||
</template>
|
||||
<template #name="{ record }">
|
||||
<div class="one-line-text">{{ characterLimit(record.name) }}</div>
|
||||
</template>
|
||||
<template #caseLevel="{ record }">
|
||||
<a-select
|
||||
v-model:model-value="record.caseLevel"
|
||||
:placeholder="t('common.pleaseSelect')"
|
||||
class="param-input w-full"
|
||||
@click.stop
|
||||
@change="() => handleStatusChange(record)"
|
||||
>
|
||||
<template #label>
|
||||
<span class="text-[var(--color-text-2)]"> <caseLevel :case-level="record.caseLevel" /></span>
|
||||
</template>
|
||||
<a-option v-for="item of caseLevelList" :key="item.value" :value="item.value">
|
||||
<caseLevel :case-level="item.text" />
|
||||
</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<!-- 用例等级 -->
|
||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL]="{ filterContent }">
|
||||
<caseLevel :case-level="filterContent.text" />
|
||||
</template>
|
||||
<!-- 执行结果 -->
|
||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_EXECUTE_RESULT]="{ filterContent }">
|
||||
<ExecuteStatusTag :execute-result="filterContent.value" />
|
||||
</template>
|
||||
<!-- 评审结果 -->
|
||||
<template #reviewStatus="{ record }">
|
||||
<MsIcon
|
||||
:type="statusIconMap[record.reviewStatus]?.icon || ''"
|
||||
class="mr-1"
|
||||
:class="[statusIconMap[record.reviewStatus].color]"
|
||||
></MsIcon>
|
||||
<span>{{ statusIconMap[record.reviewStatus]?.statusText || '' }} </span>
|
||||
</template>
|
||||
<template #lastExecuteResult="{ record }">
|
||||
<executeResult :execute-result="record.lastExecuteResult" />
|
||||
</template>
|
||||
<template #moduleId="{ record }">
|
||||
<a-tree-select
|
||||
v-if="record.showModuleTree"
|
||||
v-model:modelValue="record.moduleId"
|
||||
dropdown-class-name="tree-dropdown"
|
||||
class="param-input w-full"
|
||||
:data="caseTreeData"
|
||||
:allow-search="true"
|
||||
:field-names="{
|
||||
title: 'name',
|
||||
key: 'id',
|
||||
children: 'children',
|
||||
}"
|
||||
:tree-props="{
|
||||
virtualListProps: {
|
||||
height: 200,
|
||||
},
|
||||
}"
|
||||
@click.stop
|
||||
@change="(value) => handleChangeModule(record, value)"
|
||||
>
|
||||
<template #tree-slot-title="node">
|
||||
<a-tooltip :content="`${node.name}`" position="tl">
|
||||
<div class="one-line-text max-w-[200px] text-[var(--color-text-1)]">{{ node.name }}</div>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-tree-select>
|
||||
<a-tooltip v-else :content="getModules(record.moduleId)" position="top">
|
||||
<span class="one-line-text inline-block" @click.stop="record.showModuleTree = true">
|
||||
{{ getModules(record.moduleId) }}
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-tree-select>
|
||||
<a-tooltip v-else :content="getModules(record.moduleId)" position="top">
|
||||
<span class="one-line-text inline-block" @click.stop="record.showModuleTree = true">
|
||||
{{ getModules(record.moduleId) }}
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<!-- 渲染自定义字段开始TODO -->
|
||||
<template v-for="item in customFieldsColumns" :key="item.slotName" #[item.slotName]="{ record }">
|
||||
<a-tooltip
|
||||
:content="getTableFields(record.customFields, item as MsTableColumn, record.createUser)"
|
||||
position="top"
|
||||
:mouse-enter-delay="100"
|
||||
mini
|
||||
>
|
||||
<div class="one-line-text max-w-[300px]">{{
|
||||
getTableFields(record.customFields, item as MsTableColumn, record.createUser)
|
||||
}}</div>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<!-- 渲染自定义字段结束 -->
|
||||
<template #operation="{ record }">
|
||||
<MsButton v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" class="!mr-0" @click="operateCase(record, 'edit')">
|
||||
{{ t('common.edit') }}
|
||||
</MsButton>
|
||||
<a-divider
|
||||
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']"
|
||||
class="!mx-2 h-[12px]"
|
||||
direction="vertical"
|
||||
:margin="8"
|
||||
></a-divider>
|
||||
<MsButton v-permission="['FUNCTIONAL_CASE:READ+ADD']" class="!mr-0" @click="operateCase(record, 'copy')">
|
||||
{{ t('caseManagement.featureCase.copy') }}
|
||||
</MsButton>
|
||||
<a-divider
|
||||
v-permission="['FUNCTIONAL_CASE:READ+ADD']"
|
||||
class="!mx-2 h-[12px]"
|
||||
direction="vertical"
|
||||
:margin="8"
|
||||
></a-divider>
|
||||
<span v-permission="['FUNCTIONAL_CASE:READ+DELETE']">
|
||||
<MsTableMoreAction :list="moreActions" @select="handleMoreActionSelect($event, record)" />
|
||||
</span>
|
||||
</template>
|
||||
<!-- 渲染自定义字段开始TODO -->
|
||||
<template v-for="item in customFieldsColumns" :key="item.slotName" #[item.slotName]="{ record }">
|
||||
<a-tooltip
|
||||
:content="getTableFields(record.customFields, item as MsTableColumn, record.createUser)"
|
||||
position="top"
|
||||
:mouse-enter-delay="100"
|
||||
mini
|
||||
>
|
||||
<div class="one-line-text max-w-[300px]">{{
|
||||
getTableFields(record.customFields, item as MsTableColumn, record.createUser)
|
||||
}}</div>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<!-- 渲染自定义字段结束 -->
|
||||
<template #operation="{ record }">
|
||||
<MsButton v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" class="!mr-0" @click="operateCase(record, 'edit')">
|
||||
{{ t('common.edit') }}
|
||||
</MsButton>
|
||||
<a-divider
|
||||
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']"
|
||||
class="!mx-2 h-[12px]"
|
||||
direction="vertical"
|
||||
:margin="8"
|
||||
></a-divider>
|
||||
<MsButton v-permission="['FUNCTIONAL_CASE:READ+ADD']" class="!mr-0" @click="operateCase(record, 'copy')">
|
||||
{{ t('caseManagement.featureCase.copy') }}
|
||||
</MsButton>
|
||||
<a-divider
|
||||
v-permission="['FUNCTIONAL_CASE:READ+ADD']"
|
||||
class="!mx-2 h-[12px]"
|
||||
direction="vertical"
|
||||
:margin="8"
|
||||
></a-divider>
|
||||
<span v-permission="['FUNCTIONAL_CASE:READ+DELETE']">
|
||||
<MsTableMoreAction :list="moreActions" @select="handleMoreActionSelect($event, record)" />
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template v-if="(keyword || '').trim() === ''" #empty>
|
||||
<div class="flex w-full items-center justify-center p-[8px] text-[var(--color-text-4)]">
|
||||
{{ t('caseManagement.caseReview.tableNoData') }}
|
||||
<MsButton v-permission="['FUNCTIONAL_CASE:READ+ADD']" class="ml-[8px]" @click="createCase">
|
||||
{{ t('caseManagement.featureCase.creatingCase') }}
|
||||
</MsButton>
|
||||
{{ t('caseManagement.featureCase.or') }}
|
||||
<MsButton v-permission="['FUNCTIONAL_CASE:READ+IMPORT']" class="ml-[8px]" @click="emit('import', 'Excel')">
|
||||
{{ t('caseManagement.featureCase.importExcel') }}
|
||||
</MsButton>
|
||||
<!-- <MsButton class="ml-[4px]" @click="emit('import', 'Xmind')">
|
||||
{{ t('caseManagement.featureCase.importXmind') }}
|
||||
</MsButton> -->
|
||||
</div>
|
||||
<template v-if="(keyword || '').trim() === ''" #empty>
|
||||
<div class="flex w-full items-center justify-center p-[8px] text-[var(--color-text-4)]">
|
||||
{{ t('caseManagement.caseReview.tableNoData') }}
|
||||
<MsButton v-permission="['FUNCTIONAL_CASE:READ+ADD']" class="ml-[8px]" @click="createCase">
|
||||
{{ t('caseManagement.featureCase.creatingCase') }}
|
||||
</MsButton>
|
||||
{{ t('caseManagement.featureCase.or') }}
|
||||
<MsButton v-permission="['FUNCTIONAL_CASE:READ+IMPORT']" class="ml-[8px]" @click="emit('import', 'Excel')">
|
||||
{{ t('caseManagement.featureCase.importExcel') }}
|
||||
</MsButton>
|
||||
<!-- <MsButton class="ml-[4px]" @click="emit('import', 'Xmind')">
|
||||
{{ t('caseManagement.featureCase.importXmind') }}
|
||||
</MsButton> -->
|
||||
</div>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
<!-- 用例表结束 -->
|
||||
<!-- 脑图开始 -->
|
||||
<MinderEditor
|
||||
v-else
|
||||
:import-json="importJson"
|
||||
:tags="['模块', '用例', '前置条件', '备注', '步骤', '预期结果']"
|
||||
tag-enable
|
||||
sequence-enable
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
<MsDrawer v-model:visible="visible" :width="480" :mask="false">
|
||||
{{ nodeData.text }}
|
||||
</MsDrawer>
|
||||
<!-- 脑图结束 -->
|
||||
<!-- 用例表结束 -->
|
||||
<div v-else class="h-full">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<a-popover title="" position="bottom">
|
||||
<div class="show-table-top-title">
|
||||
<div class="one-line-text max-h-[32px] max-w-[300px] text-[var(--color-text-1)]">
|
||||
{{ moduleNamePath }}
|
||||
</div>
|
||||
<span class="text-[var(--color-text-4)]"> ({{ props.modulesCount[props.activeFolder] || 0 }})</span>
|
||||
</div>
|
||||
<template #content>
|
||||
<div class="max-w-[400px] text-[14px] font-medium text-[var(--color-text-1)]">
|
||||
{{ moduleNamePath }}
|
||||
<span class="text-[var(--color-text-4)]">({{ props.modulesCount[props.activeFolder] || 0 }})</span>
|
||||
</div>
|
||||
</template>
|
||||
</a-popover>
|
||||
<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 value="list" class="show-type-icon !m-[2px]">
|
||||
<MsIcon :size="14" type="icon-icon_view-list_outlined" />
|
||||
</a-radio>
|
||||
<a-radio value="xMind" class="show-type-icon !m-[2px]">
|
||||
<MsIcon :size="14" type="icon-icon_mindnote_outlined" />
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
<MsTag no-margin size="large" class="cursor-pointer" theme="outline" @click="fetchData">
|
||||
<MsIcon class="text-[16px] text-[var(color-text-4)]" :size="32" type="icon-icon_reset_outlined" />
|
||||
</MsTag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-[calc(100%-32px)]">
|
||||
<!-- 脑图开始 -->
|
||||
<MsMinder minder-type="FeatureCase" :import-json="importJson" @node-click="handleNodeClick" />
|
||||
<MsDrawer v-model:visible="visible" :width="480" :mask="false">
|
||||
{{ nodeData.text }}
|
||||
</MsDrawer>
|
||||
<!-- 脑图结束 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-modal
|
||||
v-model:visible="showBatchMoveDrawer"
|
||||
title-align="start"
|
||||
|
@ -292,14 +318,13 @@
|
|||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import MinderEditor from '@/components/pure/ms-minder-editor/minderEditor.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import type { BatchActionParams, BatchActionQueryParams, MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
||||
import ExecuteStatusTag from '@/components/business/ms-case-associate/executeResult.vue';
|
||||
import executeResult from '@/components/business/ms-case-associate/executeResult.vue';
|
||||
import BatchEditModal from './batchEditModal.vue';
|
||||
import CaseDetailDrawer from './caseDetailDrawer.vue';
|
||||
import FeatureCaseTree from './caseTree.vue';
|
||||
|
@ -337,7 +362,6 @@
|
|||
DemandItem,
|
||||
DragCase,
|
||||
} from '@/models/caseManagement/featureCase';
|
||||
import type { TableQueryParams } from '@/models/common';
|
||||
import { ModuleTreeNode } from '@/models/common';
|
||||
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
||||
import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
@ -1566,4 +1590,10 @@
|
|||
width: 200px !important;
|
||||
}
|
||||
}
|
||||
.list-show-type {
|
||||
padding: 0;
|
||||
:deep(.arco-radio-button-content) {
|
||||
padding: 4px 6px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -105,7 +105,7 @@
|
|||
</div>
|
||||
</template>
|
||||
<template #second>
|
||||
<div class="p-[16px_16px]">
|
||||
<div class="h-full p-[16px_16px]">
|
||||
<CaseTable
|
||||
ref="caseTableRef"
|
||||
:active-folder="activeFolder"
|
||||
|
|
Loading…
Reference in New Issue