fix(脑图): 脑图bug修复&权限

This commit is contained in:
baiqi 2024-06-17 12:57:35 +08:00 committed by Craftsman
parent 68231064cb
commit ae0732dc6c
12 changed files with 155 additions and 89 deletions

View File

@ -2,6 +2,7 @@
<a-spin :loading="attachmentLoading" class="block h-full pl-[16px]">
<MsAddAttachment
v-model:file-list="fileList"
:disabled="!hasEditPermission"
multiple
only-button
@change="(files, file) => handleFileChange(file ? [file] : [])"
@ -81,6 +82,7 @@
v-if="activeCase.id && item.isUpdateFlag"
type="button"
status="primary"
:disabled="!hasEditPermission"
@click="handleUpdateFile(item)"
>
{{ t('common.update') }}
@ -125,6 +127,7 @@
import useModal from '@/hooks/useModal';
import useAppStore from '@/store/modules/app';
import { downloadByteFile } from '@/utils';
import { hasAnyPermission } from '@/utils/permission';
import { AssociatedList } from '@/models/caseManagement/featureCase';
import { TableQueryParams } from '@/models/common';
@ -151,6 +154,7 @@
hiddenIds: [],
},
});
const hasEditPermission = hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE']);
//
watch(

View File

@ -6,7 +6,7 @@
<a-skeleton-line :rows="10" :line-height="30" :line-spacing="30" />
</a-space>
</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
field="name"
:label="t('ms.minders.caseName')"
@ -21,13 +21,14 @@
v-model:api="fApi"
v-model:form-item="formItem"
:form-rule="formRules"
:disabled="!hasEditPermission"
/>
<a-form-item field="tags" :label="t('common.tag')">
<MsTagsInput v-model:model-value="baseInfoForm.tags" :max-tag-count="6" />
</a-form-item>
</a-form>
</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
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']"
type="primary"
@ -57,6 +58,7 @@
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import useUserStore from '@/store/modules/user';
import { hasAnyPermission } from '@/utils/permission';
import { OptionsFieldId } from '@/models/caseManagement/featureCase';
@ -75,6 +77,7 @@
const userStore = useUserStore();
const { t } = useI18n();
const hasEditPermission = hasAnyPermission(['FUNCTIONAL_CASE:READ+MINDER']);
const baseInfoFormRef = ref<FormInstance>();
const baseInfoForm = ref({
name: '',

View File

@ -1,6 +1,6 @@
<template>
<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') }}
</a-button>
<a-button v-permission="['PROJECT_BUG:READ+ADD']" type="outline" @click="createBug">
@ -22,7 +22,7 @@
<div class="bug-item">
<div class="mb-[4px] flex items-center justify-between">
<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') }}
</MsButton>
</div>
@ -83,6 +83,7 @@
const appStore = useAppStore();
const { t } = useI18n();
const hasEditPermission = hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE']);
const bugList = ref<any[]>([]);
const noMoreData = ref(false);
const pageNation = ref({

View File

@ -33,6 +33,7 @@
</template>
</div>
<inputComment
v-if="hasEditPermission"
ref="commentInputRef"
v-model:content="content"
v-model:notice-user-ids="noticeUserIds"
@ -71,6 +72,7 @@
import { PreviewEditorImageUrl } from '@/api/requrls/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import { hasAnyPermission } from '@/utils/permission';
const props = defineProps<{
activeCase: Record<string, any>;
@ -79,6 +81,8 @@
const { t } = useI18n();
const { openModal } = useModal();
const hasEditPermission = hasAnyPermission(['FUNCTIONAL_CASE:READ+COMMENT']);
async function handleUploadImage(file: File) {
const { data } = await editorUploadFile({
fileList: [file],

View File

@ -11,6 +11,7 @@
:priority-disable-check="priorityDisableCheck"
:after-tag-edit="afterTagEdit"
:extract-content-tab-list="extractContentTabList"
:can-show-float-menu="canShowFloatMenu()"
:can-show-enter-node="canShowEnterNode"
:insert-sibling-menus="insertSiblingMenus"
:insert-son-menus="insertSonMenus"
@ -18,6 +19,7 @@
:can-show-more-menu="canShowMoreMenu()"
:can-show-priority-menu="canShowPriorityMenu()"
:priority-tooltip="t('caseManagement.caseReview.caseLevel')"
:disabled="!hasEditPermission"
single-tag
tag-enable
sequence-enable
@ -89,6 +91,7 @@
import useMinderStore from '@/store/modules/components/minder-editor/index';
import { MinderCustomEvent } from '@/store/modules/components/minder-editor/types';
import { filterTree, getGenerateId, mapTree, replaceNodeInTree } from '@/utils';
import { hasAnyPermission } from '@/utils/permission';
import {
FeatureCaseMinderEditType,
@ -114,6 +117,7 @@
const { t } = useI18n();
const minderStore = useMinderStore();
const hasEditPermission = hasAnyPermission(['FUNCTIONAL_CASE:READ+MINDER']);
const {
caseTag,
moduleTag,
@ -127,13 +131,14 @@
insertNode,
handleBeforeExecCommand,
stopPaste,
canShowFloatMenu,
checkNodeCanShowMenu,
canShowMoreMenu,
canShowPriorityMenu,
handleContentChange,
replaceableTags,
priorityDisableCheck,
} = useMinderBaseApi();
} = useMinderBaseApi({ hasEditPermission });
const importJson = ref<MinderJson>({
root: {} as MinderJsonNode,
template: 'default',
@ -220,61 +225,6 @@
});
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 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,
};
}
/**
* 生成脑图保存的入参
*/

View File

@ -13,7 +13,7 @@ import { getGenerateId } from '@/utils';
*
* @returns API
*/
export default function useMinderBaseApi() {
export default function useMinderBaseApi({ hasEditPermission }: { hasEditPermission: boolean }) {
const { t } = useI18n();
const minderStore = useMinderStore();
@ -29,6 +29,22 @@ export default function useMinderBaseApi() {
const caseChildTags = [prerequisiteTag, stepTag, 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 insertSonMenus = ref<InsertMenuItem[]>([]);
@ -37,6 +53,11 @@ export default function useMinderBaseApi() {
* @param node
*/
function checkNodeCanShowMenu(node: MinderJsonNode) {
if (!hasEditPermission) {
insertSiblingMenus.value = [];
insertSonMenus.value = [];
return;
}
const { data } = node;
if (data?.resource?.includes(moduleTag)) {
// 模块节点
@ -191,6 +212,9 @@ export default function useMinderBaseApi() {
*
*/
function canShowMoreMenu() {
if (!hasEditPermission) {
return false;
}
if (window.minder) {
const node: MinderJsonNode = window.minder.getSelectedNode();
// 选中节点不为虚拟根节点时,可展示更多菜单
@ -203,6 +227,9 @@ export default function useMinderBaseApi() {
*
*/
function canShowPriorityMenu() {
if (!hasEditPermission) {
return false;
}
if (window.minder) {
const node: MinderJsonNode = window.minder.getSelectedNode();
// 选中节点是用例节点时,可展示优先级菜单
@ -280,6 +307,9 @@ export default function useMinderBaseApi() {
*
*/
function priorityDisableCheck(node: MinderJsonNode) {
if (!hasEditPermission) {
return false;
}
if (node.data?.resource?.includes(caseTag)) {
return false;
}
@ -678,6 +708,7 @@ export default function useMinderBaseApi() {
handleBeforeExecCommand,
stopPaste,
checkNodeCanShowMenu,
canShowFloatMenu,
canShowMoreMenu,
canShowPriorityMenu,
handleContentChange,

View File

@ -13,6 +13,7 @@
:can-show-priority-menu="false"
:can-show-float-menu="canShowFloatMenu"
:can-show-delete-menu="canShowDeleteMenu"
:disable="!hasEditPermission"
custom-priority
single-tag
tag-enable
@ -20,7 +21,6 @@
@node-select="(node) => handleNodeSelect(node as PlanMinderNode)"
@before-exec-command="handleBeforeExecCommand"
@save="handleMinderSave"
@float-menu-close="handleFloatMenuClose"
>
<template #extractMenu>
<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>
</a-tooltip>
</div>
<a-form ref="configFormRef" :model="configForm" layout="vertical">
<a-form-item>
<a-form ref="configFormRef" :model="configForm" :disabled="!hasEditPermission" layout="vertical">
<a-form-item v-if="hasEditPermission">
<template #label>
<div class="flex items-center">
<div>{{ t('testPlan.planForm.pickCases') }}</div>
@ -94,6 +94,7 @@
<MsButton
type="text"
:disabled="
!hasEditPermission ||
(selectedAssociateCasesParams.totalCount || selectedAssociateCasesParams.selectIds.length) === 0
"
@click="clearSelectedCases"
@ -118,7 +119,8 @@
v-permission="['CASE_REVIEW:READ+RELEVANCE']"
type="text"
class="font-medium"
@click="caseAssociateVisible = true"
:disabled="!hasEditPermission"
@click="openCaseAssociateDrawer"
>
{{ t('ms.case.associate.title') }}
</MsButton>
@ -149,7 +151,8 @@
<div>{{ t('ms.minders.failStop') }}</div>
</div>
</a-form-item>
<a-form-item class="hidden-item">
<!-- 暂时不上 -->
<!-- <a-form-item class="hidden-item">
<div class="flex items-center gap-[8px]">
<a-switch v-model:model-value="configForm.retryOnFail" size="small"></a-switch>
<div>{{ t('ms.minders.failRetry') }}</div>
@ -196,7 +199,7 @@
class="w-[120px]"
></a-input-number>
</a-form-item>
</template>
</template> -->
</template>
<a-form-item
v-if="configForm.type !== PlanMinderCollectionType.FUNCTIONAL && configForm.level === 2"
@ -208,7 +211,7 @@
</div>
</a-form-item>
</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
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']"
type="primary"
@ -232,7 +235,7 @@
</template>
<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 MsButton from '@/components/pure/ms-button/index.vue';
@ -253,6 +256,7 @@
import useAppStore from '@/store/modules/app';
import useMinderStore from '@/store/modules/components/minder-editor';
import { filterTree, getGenerateId, mapTree } from '@/utils';
import { hasAnyPermission } from '@/utils/permission';
import {
AssociateCaseRequest,
@ -262,9 +266,7 @@
} from '@/models/testPlan/testPlan';
import { CaseLinkEnum } from '@/enums/caseEnum';
import { MinderEventName } from '@/enums/minderEnum';
import { FailRetry, PlanMinderAssociateType, PlanMinderCollectionType, RunMode } from '@/enums/testPlanEnum';
import Message from '@arco-design/web-vue/es/message';
import { PlanMinderAssociateType, PlanMinderCollectionType, RunMode } from '@/enums/testPlanEnum';
const props = defineProps<{
planId: string;
@ -290,11 +292,12 @@
const insertSiblingMenus = ref<InsertMenuItem[]>([]);
const insertSonMenus = ref<InsertMenuItem[]>([]);
const showAssociateCaseMenu = ref(false);
const canShowExecuteMethodMenu = ref(true);
const canShowExecuteMethodMenu = ref(false);
const executeMethodMenuVisible = ref(false);
const showConfigMenu = ref(false);
const canShowDeleteMenu = ref(false);
const extraVisible = ref<boolean>(false);
const hasEditPermission = hasAnyPermission(['PROJECT_TEST_PLAN:READ+UPDATE']);
/**
* 检测节点可展示的菜单项
@ -303,6 +306,17 @@
function checkNodeCanShowMenu(node: PlanMinderNode) {
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) {
canShowFloatMenu.value = true;
if (data?.type === PlanMinderCollectionType.FUNCTIONAL) {
@ -499,12 +513,6 @@
return false;
}
function handleFloatMenuClose() {
if (!checkConfigFormUnsaved()) {
handleConfigCancel();
}
}
/**
* 处理节点选中
* @param node 节点
@ -607,6 +615,11 @@
});
}
function openCaseAssociateDrawer() {
currentSelectCase.value = (activePlanSet.value?.data?.type as unknown as CaseLinkEnum) || CaseLinkEnum.FUNCTIONAL;
caseAssociateVisible.value = true;
}
watch(
() => [configForm.value, selectedAssociateCasesParams.value.selectIds],
() => {
@ -732,6 +745,11 @@
loading.value = true;
await editPlanMinder(makeMinderParams(fullJson));
Message.success(t('common.saveSuccess'));
tempMinderParams.value = {
planId: props.planId,
editList: [],
deletedIds: [],
};
handleConfigCancel();
initMinder();
callback();

View File

@ -21,8 +21,6 @@
</template>
<script lang="ts" name="minderContainer" setup>
import { onMounted, ref, watch } from 'vue';
import nodeFloatMenu from '../menu/nodeFloatMenu.vue';
import minderHeader from './header.vue';
import Navigator from './navigator.vue';

View File

@ -188,10 +188,11 @@
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';
const props = defineProps({
...mainEditorProps,
...floatMenuProps,
...insertProps,
...tagProps,
@ -219,7 +220,7 @@
if (minderStore.event.name === MinderEventName.NODE_SELECT) {
nodePosition = minderStore.event.nodePosition;
currentNodeTags.value = minderStore.event.nodes?.[0].data?.resource || [];
if (props.replaceableTags) {
if (props.replaceableTags && !props.disabled) {
tags.value = props.replaceableTags(selectedNodes);
} else {
tags.value = [];

View File

@ -104,15 +104,12 @@
}
);
onMounted(async () => {
window.minderProps = props;
});
function save(data: MinderJson, callback: () => void) {
emit('save', data, callback);
}
onMounted(() => {
window.minderProps = props;
useMinderEventListener({
handleSelectionChange: (node?: MinderJsonNode) => {
if (node) {

View File

@ -233,7 +233,7 @@
...cloneDeep(defaultDebugParams),
id,
isNew: !defaultProps?.id, // tabidid
protocol: activeDebug.value.protocol, // tab使tab
protocol: activeDebug.value.protocol || defaultDebugParams.protocol, // tab使tab
...defaultProps,
});
activeDebug.value = debugTabs.value[debugTabs.value.length - 1];

View File

@ -306,7 +306,7 @@
id,
isNew: !defaultProps?.id, // tabidid
definitionActiveKey: !defaultProps ? 'definition' : 'preview',
protocol: activeApiTab.value.protocol, // tab使tab
protocol: activeApiTab.value.protocol || defaultDefinitionParams.protocol, // tab使tab
...defaultProps,
});
activeApiTab.value = apiTabs.value[apiTabs.value.length - 1];