fix(脑图): 脑图bug修复&权限
This commit is contained in:
parent
68231064cb
commit
ae0732dc6c
|
@ -2,6 +2,7 @@
|
||||||
<a-spin :loading="attachmentLoading" class="block h-full pl-[16px]">
|
<a-spin :loading="attachmentLoading" class="block h-full pl-[16px]">
|
||||||
<MsAddAttachment
|
<MsAddAttachment
|
||||||
v-model:file-list="fileList"
|
v-model:file-list="fileList"
|
||||||
|
:disabled="!hasEditPermission"
|
||||||
multiple
|
multiple
|
||||||
only-button
|
only-button
|
||||||
@change="(files, file) => handleFileChange(file ? [file] : [])"
|
@change="(files, file) => handleFileChange(file ? [file] : [])"
|
||||||
|
@ -81,6 +82,7 @@
|
||||||
v-if="activeCase.id && item.isUpdateFlag"
|
v-if="activeCase.id && item.isUpdateFlag"
|
||||||
type="button"
|
type="button"
|
||||||
status="primary"
|
status="primary"
|
||||||
|
:disabled="!hasEditPermission"
|
||||||
@click="handleUpdateFile(item)"
|
@click="handleUpdateFile(item)"
|
||||||
>
|
>
|
||||||
{{ t('common.update') }}
|
{{ t('common.update') }}
|
||||||
|
@ -125,6 +127,7 @@
|
||||||
import useModal from '@/hooks/useModal';
|
import useModal from '@/hooks/useModal';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import { downloadByteFile } from '@/utils';
|
import { downloadByteFile } from '@/utils';
|
||||||
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import { AssociatedList } from '@/models/caseManagement/featureCase';
|
import { AssociatedList } from '@/models/caseManagement/featureCase';
|
||||||
import { TableQueryParams } from '@/models/common';
|
import { TableQueryParams } from '@/models/common';
|
||||||
|
@ -151,6 +154,7 @@
|
||||||
hiddenIds: [],
|
hiddenIds: [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const hasEditPermission = hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE']);
|
||||||
|
|
||||||
// 监视文件列表处理关联和本地文件
|
// 监视文件列表处理关联和本地文件
|
||||||
watch(
|
watch(
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<a-skeleton-line :rows="10" :line-height="30" :line-spacing="30" />
|
<a-skeleton-line :rows="10" :line-height="30" :line-spacing="30" />
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-skeleton>
|
</a-skeleton>
|
||||||
<a-form v-else ref="baseInfoFormRef" :model="baseInfoForm" layout="vertical">
|
<a-form v-else ref="baseInfoFormRef" :model="baseInfoForm" :disabled="!hasEditPermission" layout="vertical">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
field="name"
|
field="name"
|
||||||
:label="t('ms.minders.caseName')"
|
:label="t('ms.minders.caseName')"
|
||||||
|
@ -21,13 +21,14 @@
|
||||||
v-model:api="fApi"
|
v-model:api="fApi"
|
||||||
v-model:form-item="formItem"
|
v-model:form-item="formItem"
|
||||||
:form-rule="formRules"
|
:form-rule="formRules"
|
||||||
|
:disabled="!hasEditPermission"
|
||||||
/>
|
/>
|
||||||
<a-form-item field="tags" :label="t('common.tag')">
|
<a-form-item field="tags" :label="t('common.tag')">
|
||||||
<MsTagsInput v-model:model-value="baseInfoForm.tags" :max-tag-count="6" />
|
<MsTagsInput v-model:model-value="baseInfoForm.tags" :max-tag-count="6" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-[12px] bg-white py-[16px]">
|
<div v-if="hasEditPermission" class="flex items-center gap-[12px] bg-white py-[16px]">
|
||||||
<a-button
|
<a-button
|
||||||
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']"
|
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']"
|
||||||
type="primary"
|
type="primary"
|
||||||
|
@ -57,6 +58,7 @@
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import useUserStore from '@/store/modules/user';
|
import useUserStore from '@/store/modules/user';
|
||||||
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import { OptionsFieldId } from '@/models/caseManagement/featureCase';
|
import { OptionsFieldId } from '@/models/caseManagement/featureCase';
|
||||||
|
|
||||||
|
@ -75,6 +77,7 @@
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const hasEditPermission = hasAnyPermission(['FUNCTIONAL_CASE:READ+MINDER']);
|
||||||
const baseInfoFormRef = ref<FormInstance>();
|
const baseInfoFormRef = ref<FormInstance>();
|
||||||
const baseInfoForm = ref({
|
const baseInfoForm = ref({
|
||||||
name: '',
|
name: '',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<a-spin :loading="bugListLoading" class="block h-full pl-[16px]">
|
<a-spin :loading="bugListLoading" class="block h-full pl-[16px]">
|
||||||
<a-button v-if="hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE'])" class="mr-3" type="primary" @click="linkBug">
|
<a-button v-if="hasEditPermission" class="mr-3" type="primary" @click="linkBug">
|
||||||
{{ t('caseManagement.featureCase.linkDefect') }}
|
{{ t('caseManagement.featureCase.linkDefect') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button v-permission="['PROJECT_BUG:READ+ADD']" type="outline" @click="createBug">
|
<a-button v-permission="['PROJECT_BUG:READ+ADD']" type="outline" @click="createBug">
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
<div class="bug-item">
|
<div class="bug-item">
|
||||||
<div class="mb-[4px] flex items-center justify-between">
|
<div class="mb-[4px] flex items-center justify-between">
|
||||||
<MsButton type="text" @click="goBug(item.id)">{{ item.num }}</MsButton>
|
<MsButton type="text" @click="goBug(item.id)">{{ item.num }}</MsButton>
|
||||||
<MsButton type="text" @click="disassociateBug(item.id)">
|
<MsButton v-if="hasEditPermission" type="text" @click="disassociateBug(item.id)">
|
||||||
{{ t('ms.add.attachment.cancelAssociate') }}
|
{{ t('ms.add.attachment.cancelAssociate') }}
|
||||||
</MsButton>
|
</MsButton>
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,6 +83,7 @@
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const hasEditPermission = hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE']);
|
||||||
const bugList = ref<any[]>([]);
|
const bugList = ref<any[]>([]);
|
||||||
const noMoreData = ref(false);
|
const noMoreData = ref(false);
|
||||||
const pageNation = ref({
|
const pageNation = ref({
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<inputComment
|
<inputComment
|
||||||
|
v-if="hasEditPermission"
|
||||||
ref="commentInputRef"
|
ref="commentInputRef"
|
||||||
v-model:content="content"
|
v-model:content="content"
|
||||||
v-model:notice-user-ids="noticeUserIds"
|
v-model:notice-user-ids="noticeUserIds"
|
||||||
|
@ -71,6 +72,7 @@
|
||||||
import { PreviewEditorImageUrl } from '@/api/requrls/case-management/featureCase';
|
import { PreviewEditorImageUrl } from '@/api/requrls/case-management/featureCase';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useModal from '@/hooks/useModal';
|
import useModal from '@/hooks/useModal';
|
||||||
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
activeCase: Record<string, any>;
|
activeCase: Record<string, any>;
|
||||||
|
@ -79,6 +81,8 @@
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { openModal } = useModal();
|
const { openModal } = useModal();
|
||||||
|
|
||||||
|
const hasEditPermission = hasAnyPermission(['FUNCTIONAL_CASE:READ+COMMENT']);
|
||||||
|
|
||||||
async function handleUploadImage(file: File) {
|
async function handleUploadImage(file: File) {
|
||||||
const { data } = await editorUploadFile({
|
const { data } = await editorUploadFile({
|
||||||
fileList: [file],
|
fileList: [file],
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
:priority-disable-check="priorityDisableCheck"
|
:priority-disable-check="priorityDisableCheck"
|
||||||
:after-tag-edit="afterTagEdit"
|
:after-tag-edit="afterTagEdit"
|
||||||
:extract-content-tab-list="extractContentTabList"
|
:extract-content-tab-list="extractContentTabList"
|
||||||
|
:can-show-float-menu="canShowFloatMenu()"
|
||||||
:can-show-enter-node="canShowEnterNode"
|
:can-show-enter-node="canShowEnterNode"
|
||||||
:insert-sibling-menus="insertSiblingMenus"
|
:insert-sibling-menus="insertSiblingMenus"
|
||||||
:insert-son-menus="insertSonMenus"
|
:insert-son-menus="insertSonMenus"
|
||||||
|
@ -18,6 +19,7 @@
|
||||||
:can-show-more-menu="canShowMoreMenu()"
|
:can-show-more-menu="canShowMoreMenu()"
|
||||||
:can-show-priority-menu="canShowPriorityMenu()"
|
:can-show-priority-menu="canShowPriorityMenu()"
|
||||||
:priority-tooltip="t('caseManagement.caseReview.caseLevel')"
|
:priority-tooltip="t('caseManagement.caseReview.caseLevel')"
|
||||||
|
:disabled="!hasEditPermission"
|
||||||
single-tag
|
single-tag
|
||||||
tag-enable
|
tag-enable
|
||||||
sequence-enable
|
sequence-enable
|
||||||
|
@ -89,6 +91,7 @@
|
||||||
import useMinderStore from '@/store/modules/components/minder-editor/index';
|
import useMinderStore from '@/store/modules/components/minder-editor/index';
|
||||||
import { MinderCustomEvent } from '@/store/modules/components/minder-editor/types';
|
import { MinderCustomEvent } from '@/store/modules/components/minder-editor/types';
|
||||||
import { filterTree, getGenerateId, mapTree, replaceNodeInTree } from '@/utils';
|
import { filterTree, getGenerateId, mapTree, replaceNodeInTree } from '@/utils';
|
||||||
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FeatureCaseMinderEditType,
|
FeatureCaseMinderEditType,
|
||||||
|
@ -114,6 +117,7 @@
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const minderStore = useMinderStore();
|
const minderStore = useMinderStore();
|
||||||
|
|
||||||
|
const hasEditPermission = hasAnyPermission(['FUNCTIONAL_CASE:READ+MINDER']);
|
||||||
const {
|
const {
|
||||||
caseTag,
|
caseTag,
|
||||||
moduleTag,
|
moduleTag,
|
||||||
|
@ -127,13 +131,14 @@
|
||||||
insertNode,
|
insertNode,
|
||||||
handleBeforeExecCommand,
|
handleBeforeExecCommand,
|
||||||
stopPaste,
|
stopPaste,
|
||||||
|
canShowFloatMenu,
|
||||||
checkNodeCanShowMenu,
|
checkNodeCanShowMenu,
|
||||||
canShowMoreMenu,
|
canShowMoreMenu,
|
||||||
canShowPriorityMenu,
|
canShowPriorityMenu,
|
||||||
handleContentChange,
|
handleContentChange,
|
||||||
replaceableTags,
|
replaceableTags,
|
||||||
priorityDisableCheck,
|
priorityDisableCheck,
|
||||||
} = useMinderBaseApi();
|
} = useMinderBaseApi({ hasEditPermission });
|
||||||
const importJson = ref<MinderJson>({
|
const importJson = ref<MinderJson>({
|
||||||
root: {} as MinderJsonNode,
|
root: {} as MinderJsonNode,
|
||||||
template: 'default',
|
template: 'default',
|
||||||
|
@ -220,61 +225,6 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
const baseInfoRef = ref<InstanceType<typeof baseInfo>>();
|
const baseInfoRef = ref<InstanceType<typeof baseInfo>>();
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析用例节点信息
|
|
||||||
* @param node 用例节点
|
|
||||||
*/
|
|
||||||
function getCaseNodeInfo(node: MinderJsonNode) {
|
|
||||||
let textStep: MinderJsonNode | undefined; // 文本描述
|
|
||||||
let prerequisiteNode: MinderJsonNode | undefined; // 前置条件
|
|
||||||
let remarkNode: MinderJsonNode | undefined; // 备注
|
|
||||||
const stepNodes: MinderJsonNode[] = []; // 步骤描述
|
|
||||||
node.children?.forEach((item) => {
|
|
||||||
if (item.data?.resource?.includes(textDescTag)) {
|
|
||||||
textStep = item;
|
|
||||||
} else if (item.data?.resource?.includes(stepTag)) {
|
|
||||||
stepNodes.push(item);
|
|
||||||
} else if (item.data?.resource?.includes(prerequisiteTag)) {
|
|
||||||
prerequisiteNode = item;
|
|
||||||
} else if (item.data?.resource?.includes(remarkTag)) {
|
|
||||||
remarkNode = item;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const steps: FeatureCaseMinderStepItem[] = stepNodes.map((child, i) => {
|
|
||||||
return {
|
|
||||||
id: child.data?.id || getGenerateId(),
|
|
||||||
num: i,
|
|
||||||
desc: child.data?.text || '',
|
|
||||||
result: child.children?.[0].data?.text || '',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
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 || '',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取节点的移动信息
|
|
||||||
* @param node 节点
|
|
||||||
* @param parent 父节点
|
|
||||||
*/
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseInfoLoading = ref(false);
|
const baseInfoLoading = ref(false);
|
||||||
|
|
||||||
const formRules = ref<FormItem[]>([]);
|
const formRules = ref<FormItem[]>([]);
|
||||||
|
@ -603,6 +553,65 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析用例节点信息
|
||||||
|
* @param node 用例节点
|
||||||
|
*/
|
||||||
|
function getCaseNodeInfo(node: MinderJsonNode) {
|
||||||
|
let textStep: MinderJsonNode | undefined; // 文本描述
|
||||||
|
let prerequisiteNode: MinderJsonNode | undefined; // 前置条件
|
||||||
|
let remarkNode: MinderJsonNode | undefined; // 备注
|
||||||
|
const stepNodes: MinderJsonNode[] = []; // 步骤描述
|
||||||
|
node.children?.forEach((item) => {
|
||||||
|
if (item.data?.resource?.includes(textDescTag)) {
|
||||||
|
textStep = item;
|
||||||
|
} else if (item.data?.resource?.includes(stepTag)) {
|
||||||
|
stepNodes.push(item);
|
||||||
|
} else if (item.data?.resource?.includes(prerequisiteTag)) {
|
||||||
|
prerequisiteNode = item;
|
||||||
|
} else if (item.data?.resource?.includes(remarkTag)) {
|
||||||
|
remarkNode = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const steps: FeatureCaseMinderStepItem[] = stepNodes.map((child, i) => {
|
||||||
|
return {
|
||||||
|
id: child.data?.id || getGenerateId(),
|
||||||
|
num: i,
|
||||||
|
desc: child.data?.text || '',
|
||||||
|
result: child.children?.[0].data?.text || '',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
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 || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取节点的移动信息
|
||||||
|
* @param node 节点
|
||||||
|
* @param parent 父节点
|
||||||
|
*/
|
||||||
|
function getNodeMoveInfo(nodeIndex: number, parent?: MinderJsonNode): { moveMode: MoveMode; targetId?: string } {
|
||||||
|
const moveMode = nodeIndex === 0 ? 'BEFORE' : 'AFTER'; // 除了第一个以外,其他都是在目标节点后面插入
|
||||||
|
if (!parent) {
|
||||||
|
// 没有父节点的话,说明是根节点下的子节点
|
||||||
|
parent = importJson.value.root;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
moveMode,
|
||||||
|
targetId:
|
||||||
|
moveMode === 'BEFORE'
|
||||||
|
? parent?.children?.[1]?.data?.id
|
||||||
|
: parent?.children?.[(nodeIndex || parent.children.length - 1) - 1]?.data?.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成脑图保存的入参
|
* 生成脑图保存的入参
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { getGenerateId } from '@/utils';
|
||||||
* 封装用例脑图基础功能,包含菜单显隐判断、节点插入、节点替换、节点拖拽等
|
* 封装用例脑图基础功能,包含菜单显隐判断、节点插入、节点替换、节点拖拽等
|
||||||
* @returns API 集合
|
* @returns API 集合
|
||||||
*/
|
*/
|
||||||
export default function useMinderBaseApi() {
|
export default function useMinderBaseApi({ hasEditPermission }: { hasEditPermission: boolean }) {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const minderStore = useMinderStore();
|
const minderStore = useMinderStore();
|
||||||
|
|
||||||
|
@ -29,6 +29,22 @@ export default function useMinderBaseApi() {
|
||||||
const caseChildTags = [prerequisiteTag, stepTag, textDescTag, remarkTag];
|
const caseChildTags = [prerequisiteTag, stepTag, textDescTag, remarkTag];
|
||||||
const caseOffspringTags = [...caseChildTags, stepTag, stepExpectTag, textDescTag, remarkTag];
|
const caseOffspringTags = [...caseChildTags, stepTag, stepExpectTag, textDescTag, remarkTag];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否可展示浮动菜单
|
||||||
|
*/
|
||||||
|
function canShowFloatMenu() {
|
||||||
|
if (window.minder) {
|
||||||
|
const node: MinderJsonNode = window.minder.getSelectedNode();
|
||||||
|
if (node.data?.resource?.includes(caseTag)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!hasEditPermission) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const insertSiblingMenus = ref<InsertMenuItem[]>([]);
|
const insertSiblingMenus = ref<InsertMenuItem[]>([]);
|
||||||
const insertSonMenus = ref<InsertMenuItem[]>([]);
|
const insertSonMenus = ref<InsertMenuItem[]>([]);
|
||||||
|
|
||||||
|
@ -37,6 +53,11 @@ export default function useMinderBaseApi() {
|
||||||
* @param node 选中节点
|
* @param node 选中节点
|
||||||
*/
|
*/
|
||||||
function checkNodeCanShowMenu(node: MinderJsonNode) {
|
function checkNodeCanShowMenu(node: MinderJsonNode) {
|
||||||
|
if (!hasEditPermission) {
|
||||||
|
insertSiblingMenus.value = [];
|
||||||
|
insertSonMenus.value = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
const { data } = node;
|
const { data } = node;
|
||||||
if (data?.resource?.includes(moduleTag)) {
|
if (data?.resource?.includes(moduleTag)) {
|
||||||
// 模块节点
|
// 模块节点
|
||||||
|
@ -191,6 +212,9 @@ export default function useMinderBaseApi() {
|
||||||
* 是否可展示更多菜单
|
* 是否可展示更多菜单
|
||||||
*/
|
*/
|
||||||
function canShowMoreMenu() {
|
function canShowMoreMenu() {
|
||||||
|
if (!hasEditPermission) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (window.minder) {
|
if (window.minder) {
|
||||||
const node: MinderJsonNode = window.minder.getSelectedNode();
|
const node: MinderJsonNode = window.minder.getSelectedNode();
|
||||||
// 选中节点不为虚拟根节点时,可展示更多菜单
|
// 选中节点不为虚拟根节点时,可展示更多菜单
|
||||||
|
@ -203,6 +227,9 @@ export default function useMinderBaseApi() {
|
||||||
* 是否可展示优先级菜单
|
* 是否可展示优先级菜单
|
||||||
*/
|
*/
|
||||||
function canShowPriorityMenu() {
|
function canShowPriorityMenu() {
|
||||||
|
if (!hasEditPermission) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (window.minder) {
|
if (window.minder) {
|
||||||
const node: MinderJsonNode = window.minder.getSelectedNode();
|
const node: MinderJsonNode = window.minder.getSelectedNode();
|
||||||
// 选中节点是用例节点时,可展示优先级菜单
|
// 选中节点是用例节点时,可展示优先级菜单
|
||||||
|
@ -280,6 +307,9 @@ export default function useMinderBaseApi() {
|
||||||
* 检查节点是否可打优先级
|
* 检查节点是否可打优先级
|
||||||
*/
|
*/
|
||||||
function priorityDisableCheck(node: MinderJsonNode) {
|
function priorityDisableCheck(node: MinderJsonNode) {
|
||||||
|
if (!hasEditPermission) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (node.data?.resource?.includes(caseTag)) {
|
if (node.data?.resource?.includes(caseTag)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -678,6 +708,7 @@ export default function useMinderBaseApi() {
|
||||||
handleBeforeExecCommand,
|
handleBeforeExecCommand,
|
||||||
stopPaste,
|
stopPaste,
|
||||||
checkNodeCanShowMenu,
|
checkNodeCanShowMenu,
|
||||||
|
canShowFloatMenu,
|
||||||
canShowMoreMenu,
|
canShowMoreMenu,
|
||||||
canShowPriorityMenu,
|
canShowPriorityMenu,
|
||||||
handleContentChange,
|
handleContentChange,
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
:can-show-priority-menu="false"
|
:can-show-priority-menu="false"
|
||||||
:can-show-float-menu="canShowFloatMenu"
|
:can-show-float-menu="canShowFloatMenu"
|
||||||
:can-show-delete-menu="canShowDeleteMenu"
|
:can-show-delete-menu="canShowDeleteMenu"
|
||||||
|
:disable="!hasEditPermission"
|
||||||
custom-priority
|
custom-priority
|
||||||
single-tag
|
single-tag
|
||||||
tag-enable
|
tag-enable
|
||||||
|
@ -20,7 +21,6 @@
|
||||||
@node-select="(node) => handleNodeSelect(node as PlanMinderNode)"
|
@node-select="(node) => handleNodeSelect(node as PlanMinderNode)"
|
||||||
@before-exec-command="handleBeforeExecCommand"
|
@before-exec-command="handleBeforeExecCommand"
|
||||||
@save="handleMinderSave"
|
@save="handleMinderSave"
|
||||||
@float-menu-close="handleFloatMenuClose"
|
|
||||||
>
|
>
|
||||||
<template #extractMenu>
|
<template #extractMenu>
|
||||||
<a-tooltip v-if="showAssociateCaseMenu" :content="t('ms.case.associate.title')">
|
<a-tooltip v-if="showAssociateCaseMenu" :content="t('ms.case.associate.title')">
|
||||||
|
@ -85,8 +85,8 @@
|
||||||
<div class="one-line-text font-medium">{{ configForm.text }}</div>
|
<div class="one-line-text font-medium">{{ configForm.text }}</div>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<a-form ref="configFormRef" :model="configForm" layout="vertical">
|
<a-form ref="configFormRef" :model="configForm" :disabled="!hasEditPermission" layout="vertical">
|
||||||
<a-form-item>
|
<a-form-item v-if="hasEditPermission">
|
||||||
<template #label>
|
<template #label>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div>{{ t('testPlan.planForm.pickCases') }}</div>
|
<div>{{ t('testPlan.planForm.pickCases') }}</div>
|
||||||
|
@ -94,6 +94,7 @@
|
||||||
<MsButton
|
<MsButton
|
||||||
type="text"
|
type="text"
|
||||||
:disabled="
|
:disabled="
|
||||||
|
!hasEditPermission ||
|
||||||
(selectedAssociateCasesParams.totalCount || selectedAssociateCasesParams.selectIds.length) === 0
|
(selectedAssociateCasesParams.totalCount || selectedAssociateCasesParams.selectIds.length) === 0
|
||||||
"
|
"
|
||||||
@click="clearSelectedCases"
|
@click="clearSelectedCases"
|
||||||
|
@ -118,7 +119,8 @@
|
||||||
v-permission="['CASE_REVIEW:READ+RELEVANCE']"
|
v-permission="['CASE_REVIEW:READ+RELEVANCE']"
|
||||||
type="text"
|
type="text"
|
||||||
class="font-medium"
|
class="font-medium"
|
||||||
@click="caseAssociateVisible = true"
|
:disabled="!hasEditPermission"
|
||||||
|
@click="openCaseAssociateDrawer"
|
||||||
>
|
>
|
||||||
{{ t('ms.case.associate.title') }}
|
{{ t('ms.case.associate.title') }}
|
||||||
</MsButton>
|
</MsButton>
|
||||||
|
@ -149,7 +151,8 @@
|
||||||
<div>{{ t('ms.minders.failStop') }}</div>
|
<div>{{ t('ms.minders.failStop') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item class="hidden-item">
|
<!-- 暂时不上 -->
|
||||||
|
<!-- <a-form-item class="hidden-item">
|
||||||
<div class="flex items-center gap-[8px]">
|
<div class="flex items-center gap-[8px]">
|
||||||
<a-switch v-model:model-value="configForm.retryOnFail" size="small"></a-switch>
|
<a-switch v-model:model-value="configForm.retryOnFail" size="small"></a-switch>
|
||||||
<div>{{ t('ms.minders.failRetry') }}</div>
|
<div>{{ t('ms.minders.failRetry') }}</div>
|
||||||
|
@ -196,7 +199,7 @@
|
||||||
class="w-[120px]"
|
class="w-[120px]"
|
||||||
></a-input-number>
|
></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template> -->
|
||||||
</template>
|
</template>
|
||||||
<a-form-item
|
<a-form-item
|
||||||
v-if="configForm.type !== PlanMinderCollectionType.FUNCTIONAL && configForm.level === 2"
|
v-if="configForm.type !== PlanMinderCollectionType.FUNCTIONAL && configForm.level === 2"
|
||||||
|
@ -208,7 +211,7 @@
|
||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
<div class="flex items-center gap-[12px] bg-white pb-[16px]">
|
<div v-if="hasEditPermission" class="flex items-center gap-[12px] bg-white pb-[16px]">
|
||||||
<a-button
|
<a-button
|
||||||
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']"
|
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']"
|
||||||
type="primary"
|
type="primary"
|
||||||
|
@ -232,7 +235,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { FormInstance, SelectOptionData } from '@arco-design/web-vue';
|
import { type FormInstance, Message, type SelectOptionData } from '@arco-design/web-vue';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
@ -253,6 +256,7 @@
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import useMinderStore from '@/store/modules/components/minder-editor';
|
import useMinderStore from '@/store/modules/components/minder-editor';
|
||||||
import { filterTree, getGenerateId, mapTree } from '@/utils';
|
import { filterTree, getGenerateId, mapTree } from '@/utils';
|
||||||
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AssociateCaseRequest,
|
AssociateCaseRequest,
|
||||||
|
@ -262,9 +266,7 @@
|
||||||
} from '@/models/testPlan/testPlan';
|
} from '@/models/testPlan/testPlan';
|
||||||
import { CaseLinkEnum } from '@/enums/caseEnum';
|
import { CaseLinkEnum } from '@/enums/caseEnum';
|
||||||
import { MinderEventName } from '@/enums/minderEnum';
|
import { MinderEventName } from '@/enums/minderEnum';
|
||||||
import { FailRetry, PlanMinderAssociateType, PlanMinderCollectionType, RunMode } from '@/enums/testPlanEnum';
|
import { PlanMinderAssociateType, PlanMinderCollectionType, RunMode } from '@/enums/testPlanEnum';
|
||||||
|
|
||||||
import Message from '@arco-design/web-vue/es/message';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
planId: string;
|
planId: string;
|
||||||
|
@ -290,11 +292,12 @@
|
||||||
const insertSiblingMenus = ref<InsertMenuItem[]>([]);
|
const insertSiblingMenus = ref<InsertMenuItem[]>([]);
|
||||||
const insertSonMenus = ref<InsertMenuItem[]>([]);
|
const insertSonMenus = ref<InsertMenuItem[]>([]);
|
||||||
const showAssociateCaseMenu = ref(false);
|
const showAssociateCaseMenu = ref(false);
|
||||||
const canShowExecuteMethodMenu = ref(true);
|
const canShowExecuteMethodMenu = ref(false);
|
||||||
const executeMethodMenuVisible = ref(false);
|
const executeMethodMenuVisible = ref(false);
|
||||||
const showConfigMenu = ref(false);
|
const showConfigMenu = ref(false);
|
||||||
const canShowDeleteMenu = ref(false);
|
const canShowDeleteMenu = ref(false);
|
||||||
const extraVisible = ref<boolean>(false);
|
const extraVisible = ref<boolean>(false);
|
||||||
|
const hasEditPermission = hasAnyPermission(['PROJECT_TEST_PLAN:READ+UPDATE']);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检测节点可展示的菜单项
|
* 检测节点可展示的菜单项
|
||||||
|
@ -303,6 +306,17 @@
|
||||||
function checkNodeCanShowMenu(node: PlanMinderNode) {
|
function checkNodeCanShowMenu(node: PlanMinderNode) {
|
||||||
const { data } = node;
|
const { data } = node;
|
||||||
|
|
||||||
|
if (!hasEditPermission && (data?.level === 1 || data?.level === 2)) {
|
||||||
|
// 没有编辑权限,只能查看配置菜单(功能用例只有关联用例,所以配置菜单也不能看)
|
||||||
|
if (data?.type === PlanMinderCollectionType.FUNCTIONAL) {
|
||||||
|
canShowFloatMenu.value = false;
|
||||||
|
} else {
|
||||||
|
canShowFloatMenu.value = true;
|
||||||
|
showConfigMenu.value = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (data?.level === 1 || data?.level === 2) {
|
if (data?.level === 1 || data?.level === 2) {
|
||||||
canShowFloatMenu.value = true;
|
canShowFloatMenu.value = true;
|
||||||
if (data?.type === PlanMinderCollectionType.FUNCTIONAL) {
|
if (data?.type === PlanMinderCollectionType.FUNCTIONAL) {
|
||||||
|
@ -499,12 +513,6 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFloatMenuClose() {
|
|
||||||
if (!checkConfigFormUnsaved()) {
|
|
||||||
handleConfigCancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理节点选中
|
* 处理节点选中
|
||||||
* @param node 节点
|
* @param node 节点
|
||||||
|
@ -607,6 +615,11 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openCaseAssociateDrawer() {
|
||||||
|
currentSelectCase.value = (activePlanSet.value?.data?.type as unknown as CaseLinkEnum) || CaseLinkEnum.FUNCTIONAL;
|
||||||
|
caseAssociateVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => [configForm.value, selectedAssociateCasesParams.value.selectIds],
|
() => [configForm.value, selectedAssociateCasesParams.value.selectIds],
|
||||||
() => {
|
() => {
|
||||||
|
@ -732,6 +745,11 @@
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
await editPlanMinder(makeMinderParams(fullJson));
|
await editPlanMinder(makeMinderParams(fullJson));
|
||||||
Message.success(t('common.saveSuccess'));
|
Message.success(t('common.saveSuccess'));
|
||||||
|
tempMinderParams.value = {
|
||||||
|
planId: props.planId,
|
||||||
|
editList: [],
|
||||||
|
deletedIds: [],
|
||||||
|
};
|
||||||
handleConfigCancel();
|
handleConfigCancel();
|
||||||
initMinder();
|
initMinder();
|
||||||
callback();
|
callback();
|
||||||
|
|
|
@ -21,8 +21,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" name="minderContainer" setup>
|
<script lang="ts" name="minderContainer" setup>
|
||||||
import { onMounted, ref, watch } from 'vue';
|
|
||||||
|
|
||||||
import nodeFloatMenu from '../menu/nodeFloatMenu.vue';
|
import nodeFloatMenu from '../menu/nodeFloatMenu.vue';
|
||||||
import minderHeader from './header.vue';
|
import minderHeader from './header.vue';
|
||||||
import Navigator from './navigator.vue';
|
import Navigator from './navigator.vue';
|
||||||
|
|
|
@ -188,10 +188,11 @@
|
||||||
|
|
||||||
import { MinderEventName } from '@/enums/minderEnum';
|
import { MinderEventName } from '@/enums/minderEnum';
|
||||||
|
|
||||||
import { floatMenuProps, insertProps, MinderJsonNode, priorityProps, tagProps } from '../props';
|
import { floatMenuProps, insertProps, mainEditorProps, MinderJsonNode, priorityProps, tagProps } from '../props';
|
||||||
import { isDisableNode, isNodeInMinderView, setPriorityView } from '../script/tool/utils';
|
import { isDisableNode, isNodeInMinderView, setPriorityView } from '../script/tool/utils';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
...mainEditorProps,
|
||||||
...floatMenuProps,
|
...floatMenuProps,
|
||||||
...insertProps,
|
...insertProps,
|
||||||
...tagProps,
|
...tagProps,
|
||||||
|
@ -219,7 +220,7 @@
|
||||||
if (minderStore.event.name === MinderEventName.NODE_SELECT) {
|
if (minderStore.event.name === MinderEventName.NODE_SELECT) {
|
||||||
nodePosition = minderStore.event.nodePosition;
|
nodePosition = minderStore.event.nodePosition;
|
||||||
currentNodeTags.value = minderStore.event.nodes?.[0].data?.resource || [];
|
currentNodeTags.value = minderStore.event.nodes?.[0].data?.resource || [];
|
||||||
if (props.replaceableTags) {
|
if (props.replaceableTags && !props.disabled) {
|
||||||
tags.value = props.replaceableTags(selectedNodes);
|
tags.value = props.replaceableTags(selectedNodes);
|
||||||
} else {
|
} else {
|
||||||
tags.value = [];
|
tags.value = [];
|
||||||
|
|
|
@ -104,15 +104,12 @@
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
window.minderProps = props;
|
|
||||||
});
|
|
||||||
|
|
||||||
function save(data: MinderJson, callback: () => void) {
|
function save(data: MinderJson, callback: () => void) {
|
||||||
emit('save', data, callback);
|
emit('save', data, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
window.minderProps = props;
|
||||||
useMinderEventListener({
|
useMinderEventListener({
|
||||||
handleSelectionChange: (node?: MinderJsonNode) => {
|
handleSelectionChange: (node?: MinderJsonNode) => {
|
||||||
if (node) {
|
if (node) {
|
||||||
|
|
|
@ -233,7 +233,7 @@
|
||||||
...cloneDeep(defaultDebugParams),
|
...cloneDeep(defaultDebugParams),
|
||||||
id,
|
id,
|
||||||
isNew: !defaultProps?.id, // 新开的tab标记为前端新增的调试,因为此时都已经有id了;但是如果是查看打开的会有携带id
|
isNew: !defaultProps?.id, // 新开的tab标记为前端新增的调试,因为此时都已经有id了;但是如果是查看打开的会有携带id
|
||||||
protocol: activeDebug.value.protocol, // 新开的tab默认使用当前激活的tab的协议
|
protocol: activeDebug.value.protocol || defaultDebugParams.protocol, // 新开的tab默认使用当前激活的tab的协议
|
||||||
...defaultProps,
|
...defaultProps,
|
||||||
});
|
});
|
||||||
activeDebug.value = debugTabs.value[debugTabs.value.length - 1];
|
activeDebug.value = debugTabs.value[debugTabs.value.length - 1];
|
||||||
|
|
|
@ -306,7 +306,7 @@
|
||||||
id,
|
id,
|
||||||
isNew: !defaultProps?.id, // 新开的tab标记为前端新增的调试,因为此时都已经有id了;但是如果是查看打开的会有携带id
|
isNew: !defaultProps?.id, // 新开的tab标记为前端新增的调试,因为此时都已经有id了;但是如果是查看打开的会有携带id
|
||||||
definitionActiveKey: !defaultProps ? 'definition' : 'preview',
|
definitionActiveKey: !defaultProps ? 'definition' : 'preview',
|
||||||
protocol: activeApiTab.value.protocol, // 新开的tab默认使用当前激活的tab的协议
|
protocol: activeApiTab.value.protocol || defaultDefinitionParams.protocol, // 新开的tab默认使用当前激活的tab的协议
|
||||||
...defaultProps,
|
...defaultProps,
|
||||||
});
|
});
|
||||||
activeApiTab.value = apiTabs.value[apiTabs.value.length - 1];
|
activeApiTab.value = apiTabs.value[apiTabs.value.length - 1];
|
||||||
|
|
Loading…
Reference in New Issue