fix(全局): 部分 bug 修复
This commit is contained in:
parent
fbd0a27486
commit
eff826b97c
|
@ -295,7 +295,9 @@
|
||||||
});
|
});
|
||||||
const buttonDropDownVisible = ref(false);
|
const buttonDropDownVisible = ref(false);
|
||||||
|
|
||||||
onBeforeMount(() => {
|
watch(
|
||||||
|
() => fileList.value,
|
||||||
|
() => {
|
||||||
// 回显文件
|
// 回显文件
|
||||||
const defaultFiles = fileList.value.filter((item) => item) || [];
|
const defaultFiles = fileList.value.filter((item) => item) || [];
|
||||||
if (defaultFiles.length > 0) {
|
if (defaultFiles.length > 0) {
|
||||||
|
@ -314,7 +316,11 @@
|
||||||
.map((item) => item?.[props.fields.id] || '')
|
.map((item) => item?.[props.fields.id] || '')
|
||||||
.filter((item) => item);
|
.filter((item) => item);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
function handleChange(_fileList: MsFileItem[], fileItem: MsFileItem) {
|
function handleChange(_fileList: MsFileItem[], fileItem: MsFileItem) {
|
||||||
if (props.multiple) {
|
if (props.multiple) {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<span class="mx-[2px]"></span>
|
<span class="mx-[2px]"><slot></slot></span>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="flex flex-col gap-[16px] text-[14px]">
|
<div class="flex flex-col gap-[16px] text-[14px]">
|
||||||
<div class="font-semibold text-[var(--color-text-1)]">
|
<div class="font-semibold text-[var(--color-text-1)]">
|
||||||
|
@ -68,7 +68,6 @@
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
visible: boolean;
|
|
||||||
savingFile?: MsFileItem;
|
savingFile?: MsFileItem;
|
||||||
fileSaveAsSourceId: string | number;
|
fileSaveAsSourceId: string | number;
|
||||||
sourceIdKey?: string; // 资源id对应key
|
sourceIdKey?: string; // 资源id对应key
|
||||||
|
@ -89,7 +88,7 @@
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const saveFilePopoverVisible = defineModel<boolean>('visible', {
|
const saveFilePopoverVisible = defineModel<boolean>('visible', {
|
||||||
required: true,
|
default: false,
|
||||||
});
|
});
|
||||||
const saveFileForm = ref({
|
const saveFileForm = ref({
|
||||||
name: '',
|
name: '',
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { computed, defineComponent, h, ref } from 'vue';
|
import { computed, defineComponent, h, ref } from 'vue';
|
||||||
import { RouteRecordRaw, useRoute, useRouter } from 'vue-router';
|
import { RouteRecordRaw, useRoute, useRouter } from 'vue-router';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep, debounce } from 'lodash-es';
|
||||||
|
|
||||||
import MsAvatar from '@/components/pure/ms-avatar/index.vue';
|
import MsAvatar from '@/components/pure/ms-avatar/index.vue';
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
|
@ -407,7 +407,7 @@
|
||||||
// 渲染菜单项
|
// 渲染菜单项
|
||||||
const renderMenuItem = (element: RouteRecordRaw | null, icon: (() => any) | null) =>
|
const renderMenuItem = (element: RouteRecordRaw | null, icon: (() => any) | null) =>
|
||||||
element?.name === SettingRouteEnum.SETTING_ORGANIZATION ? (
|
element?.name === SettingRouteEnum.SETTING_ORGANIZATION ? (
|
||||||
<a-menu-item key={element?.name} v-slots={{ icon }} onClick={() => goto(element)}>
|
<a-menu-item key={element?.name} v-slots={{ icon }} onClick={debounce(() => goto(element), 100)}>
|
||||||
<div class="inline-flex w-[calc(100%-34px)] items-center justify-between !bg-transparent">
|
<div class="inline-flex w-[calc(100%-34px)] items-center justify-between !bg-transparent">
|
||||||
{collapsed.value ? (
|
{collapsed.value ? (
|
||||||
<div class="text-center text-[12px] leading-[16px]">
|
<div class="text-center text-[12px] leading-[16px]">
|
||||||
|
@ -436,7 +436,7 @@
|
||||||
</div>
|
</div>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
) : (
|
) : (
|
||||||
<a-menu-item key={element?.name} v-slots={{ icon }} onClick={() => goto(element)}>
|
<a-menu-item key={element?.name} v-slots={{ icon }} onClick={debounce(() => goto(element), 100)}>
|
||||||
{collapsed.value ? (
|
{collapsed.value ? (
|
||||||
<div class="text-center text-[12px] leading-[16px]">
|
<div class="text-center text-[12px] leading-[16px]">
|
||||||
{t(element?.meta?.collapsedLocale || element?.meta?.locale || '')}
|
{t(element?.meta?.collapsedLocale || element?.meta?.locale || '')}
|
||||||
|
|
|
@ -37,23 +37,19 @@
|
||||||
>
|
>
|
||||||
{{ t('ms.upload.preview') }}
|
{{ t('ms.upload.preview') }}
|
||||||
</MsButton>
|
</MsButton>
|
||||||
|
<MsButton type="button" status="primary" class="!mr-[4px]" @click="transferFile(item)">
|
||||||
|
{{ t('caseManagement.featureCase.storage') }}
|
||||||
|
</MsButton>
|
||||||
<SaveAsFilePopover
|
<SaveAsFilePopover
|
||||||
v-model:visible="transferVisible"
|
v-if="item.status !== 'init'"
|
||||||
:saving-file="activeTransferFileParams"
|
:saving-file="activeTransferFileParams"
|
||||||
:file-save-as-source-id="activeCase.id || ''"
|
:file-save-as-source-id="activeCase.id || ''"
|
||||||
:file-save-as-api="transferFileRequest"
|
:file-save-as-api="transferFileRequest"
|
||||||
:file-module-options-api="getTransferFileTree"
|
:file-module-options-api="getTransferFileTree"
|
||||||
source-id-key="caseId"
|
source-id-key="caseId"
|
||||||
/>
|
|
||||||
<MsButton
|
|
||||||
v-if="item.status !== 'init'"
|
|
||||||
type="button"
|
|
||||||
status="primary"
|
|
||||||
class="!mr-[4px]"
|
|
||||||
@click="transferFile(item)"
|
|
||||||
>
|
>
|
||||||
{{ t('caseManagement.featureCase.storage') }}
|
<span :id="item.uid"></span>
|
||||||
</MsButton>
|
</SaveAsFilePopover>
|
||||||
<MsButton
|
<MsButton
|
||||||
v-if="item.status !== 'init'"
|
v-if="item.status !== 'init'"
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -198,7 +194,7 @@
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Message.success(t('ms.upload.uploadSuccess'));
|
Message.success(t('common.linkSuccess'));
|
||||||
emit('uploadSuccess');
|
emit('uploadSuccess');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
|
@ -244,14 +240,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const transferVisible = ref<boolean>(false);
|
|
||||||
|
|
||||||
const activeTransferFileParams = ref<MsFileItem>();
|
const activeTransferFileParams = ref<MsFileItem>();
|
||||||
|
|
||||||
// 转存
|
// 转存
|
||||||
function transferFile(item: MsFileItem) {
|
function transferFile(item: MsFileItem) {
|
||||||
activeTransferFileParams.value = { ...item };
|
activeTransferFileParams.value = { ...item };
|
||||||
transferVisible.value = true;
|
document.getElementById(item.uid)?.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除本地文件
|
// 删除本地文件
|
||||||
|
|
|
@ -87,7 +87,7 @@
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
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, findNodeByKey, getGenerateId, mapTree, replaceNodeInTree } from '@/utils';
|
||||||
import { hasAnyPermission } from '@/utils/permission';
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -156,10 +156,9 @@
|
||||||
/**
|
/**
|
||||||
* 初始化用例模块树
|
* 初始化用例模块树
|
||||||
*/
|
*/
|
||||||
async function initCaseTree(notRemote = false) {
|
async function initCaseTree() {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
if (!notRemote) {
|
|
||||||
const res = await getCaseMinderTree({
|
const res = await getCaseMinderTree({
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
moduleId: '', // 始终加载全部,然后再进入对应的模块节点
|
moduleId: '', // 始终加载全部,然后再进入对应的模块节点
|
||||||
|
@ -171,7 +170,7 @@
|
||||||
id: e.id || e.data?.id || '',
|
id: e.id || e.data?.id || '',
|
||||||
text: e.name || e.data?.text || '',
|
text: e.name || e.data?.text || '',
|
||||||
resource: props.modulesCount[e.id] !== undefined ? [moduleTag] : e.data?.resource,
|
resource: props.modulesCount[e.id] !== undefined ? [moduleTag] : e.data?.resource,
|
||||||
expandState: e.level === 1 ? 'expand' : 'collapse',
|
expandState: e.level === 0 ? 'expand' : 'collapse',
|
||||||
count: props.modulesCount[e.id],
|
count: props.modulesCount[e.id],
|
||||||
isNew: false,
|
isNew: false,
|
||||||
changed: false,
|
changed: false,
|
||||||
|
@ -202,24 +201,21 @@
|
||||||
};
|
};
|
||||||
importJson.value.treePath = [];
|
importJson.value.treePath = [];
|
||||||
window.minder.importJson(importJson.value);
|
window.minder.importJson(importJson.value);
|
||||||
}
|
|
||||||
if (notRemote) {
|
|
||||||
if (props.moduleId !== 'all') {
|
if (props.moduleId !== 'all') {
|
||||||
// 携带具体的模块 ID 加载时,进入该模块内
|
// 携带具体的模块 ID 加载时,进入该模块内
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, undefined, [
|
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, undefined, [
|
||||||
window.minder.getNodeById(props.moduleId),
|
findNodeByKey(importJson.value.root.children || [], props.moduleId, 'id', 'data') as MinderJsonNode,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 携带具体的模块 ID 加载时,进入该模块内
|
// 刷新时不需要重新请求数据,进入根节点
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, undefined, [
|
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, undefined, [
|
||||||
importJson.value.root,
|
importJson.value.root,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
@ -291,7 +287,10 @@
|
||||||
try {
|
try {
|
||||||
baseInfoLoading.value = true;
|
baseInfoLoading.value = true;
|
||||||
const res = await getCaseDetail(data?.id || activeCase.value.id);
|
const res = await getCaseDetail(data?.id || activeCase.value.id);
|
||||||
activeCase.value = res;
|
activeCase.value = {
|
||||||
|
...res,
|
||||||
|
isNew: false,
|
||||||
|
};
|
||||||
const fileIds = (res.attachments || []).map((item: any) => item.id) || [];
|
const fileIds = (res.attachments || []).map((item: any) => item.id) || [];
|
||||||
if (fileIds.length) {
|
if (fileIds.length) {
|
||||||
checkUpdateFileIds.value = await checkFileIsUpdateRequest(fileIds);
|
checkUpdateFileIds.value = await checkFileIsUpdateRequest(fileIds);
|
||||||
|
@ -389,7 +388,7 @@
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
moduleId: data.id,
|
moduleId: data.id,
|
||||||
});
|
});
|
||||||
const fakeNode = node.children?.find((e) => e.data?.id === undefined); // 移除占位的虚拟节点
|
const fakeNode = node.children?.find((e) => e.data?.id === 'fakeNode'); // 移除占位的虚拟节点
|
||||||
if (fakeNode) {
|
if (fakeNode) {
|
||||||
window.minder.removeNode(fakeNode);
|
window.minder.removeNode(fakeNode);
|
||||||
}
|
}
|
||||||
|
@ -464,6 +463,10 @@
|
||||||
extraVisible.value = false;
|
extraVisible.value = false;
|
||||||
showDetailMenu.value = false;
|
showDetailMenu.value = false;
|
||||||
resetExtractInfo();
|
resetExtractInfo();
|
||||||
|
const fakeNode = node.children?.find((e) => e.data?.id === 'fakeNode'); // 移除占位的虚拟节点
|
||||||
|
if (fakeNode) {
|
||||||
|
window.minder.removeNode(fakeNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setPriorityView(true, 'P');
|
setPriorityView(true, 'P');
|
||||||
}
|
}
|
||||||
|
@ -483,20 +486,16 @@
|
||||||
node.data.isNew = true;
|
node.data.isNew = true;
|
||||||
window.minder.execCommand('priority');
|
window.minder.execCommand('priority');
|
||||||
if (index === nodes.length - 1) {
|
if (index === nodes.length - 1) {
|
||||||
nextTick(() => {
|
window.minder.toggleSelect(node);
|
||||||
handleNodeSelect(node);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else if (node.data?.resource?.includes(caseTag)) {
|
} else if (tag === caseTag && node.data) {
|
||||||
// 排除是从模块节点切换到用例节点的数据
|
// 排除是从模块节点切换到用例节点的数据
|
||||||
tempMinderParams.value.updateModuleList = tempMinderParams.value.updateModuleList.filter(
|
tempMinderParams.value.updateModuleList = tempMinderParams.value.updateModuleList.filter(
|
||||||
(e) => e.id !== node.data?.id
|
(e) => e.id !== node.data?.id
|
||||||
);
|
);
|
||||||
node.data.isNew = true;
|
node.data.isNew = true;
|
||||||
if (index === nodes.length - 1) {
|
if (index === nodes.length - 1) {
|
||||||
nextTick(() => {
|
window.minder.toggleSelect(node);
|
||||||
handleNodeSelect(node);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else if (node.data?.resource?.some((e) => caseOffspringTags.includes(e))) {
|
} else if (node.data?.resource?.some((e) => caseOffspringTags.includes(e))) {
|
||||||
// 用例子孙节点更新,标记用例节点变化
|
// 用例子孙节点更新,标记用例节点变化
|
||||||
|
@ -522,7 +521,13 @@
|
||||||
case MinderEventName.CUT_NODE:
|
case MinderEventName.CUT_NODE:
|
||||||
// TODO:循环优化
|
// TODO:循环优化
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
if (!caseOffspringTags.some((e) => node.data?.resource?.includes(e))) {
|
if (!node.data?.resource || node.data?.resource.length === 0) {
|
||||||
|
// 删除文本节点
|
||||||
|
tempMinderParams.value.deleteResourceList.push({
|
||||||
|
id: node.data?.id || '',
|
||||||
|
type: 'NONE',
|
||||||
|
});
|
||||||
|
} else if (!caseOffspringTags.some((e) => node.data?.resource?.includes(e))) {
|
||||||
// 非用例下的子孙节点的移除,才加入删除资源队列
|
// 非用例下的子孙节点的移除,才加入删除资源队列
|
||||||
tempMinderParams.value.deleteResourceList.push({
|
tempMinderParams.value.deleteResourceList.push({
|
||||||
id: node.data?.id || '',
|
id: node.data?.id || '',
|
||||||
|
@ -619,7 +624,7 @@
|
||||||
versionId: '',
|
versionId: '',
|
||||||
updateCaseList: [],
|
updateCaseList: [],
|
||||||
updateModuleList: [],
|
updateModuleList: [],
|
||||||
deleteResourceList: [],
|
deleteResourceList: tempMinderParams.value.deleteResourceList, // 删除的资源不清空,避免请求错误导致数据丢失
|
||||||
additionalNodeList: [],
|
additionalNodeList: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -702,7 +707,7 @@
|
||||||
watch(
|
watch(
|
||||||
() => props.moduleId,
|
() => props.moduleId,
|
||||||
() => {
|
() => {
|
||||||
initCaseTree(true);
|
initCaseTree();
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
deep: true,
|
deep: true,
|
||||||
|
|
|
@ -615,7 +615,10 @@ export default function useMinderBaseApi({ hasEditPermission }: { hasEditPermiss
|
||||||
}
|
}
|
||||||
if ([stepTag, textDescTag].some((tag) => node.data?.resource?.includes(tag))) {
|
if ([stepTag, textDescTag].some((tag) => node.data?.resource?.includes(tag))) {
|
||||||
// 用例下的文本描述和步骤描述节点
|
// 用例下的文本描述和步骤描述节点
|
||||||
if (node.data?.resource?.includes(stepExpectTag)) {
|
if (
|
||||||
|
node.children?.length === 0 &&
|
||||||
|
minderStore.clipboard.every((e) => e.data?.resource?.includes(stepExpectTag))
|
||||||
|
) {
|
||||||
// 粘贴的是期望结果节点
|
// 粘贴的是期望结果节点
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,10 @@
|
||||||
@save="handleMinderSave"
|
@save="handleMinderSave"
|
||||||
>
|
>
|
||||||
<template #extractMenu>
|
<template #extractMenu>
|
||||||
<a-tooltip v-if="showAssociateCaseMenu" :content="t('ms.case.associate.title')">
|
<a-tooltip
|
||||||
|
v-if="showAssociateCaseMenu && hasAnyPermission(['PROJECT_TEST_PLAN:READ+ASSOCIATION'])"
|
||||||
|
:content="t('ms.case.associate.title')"
|
||||||
|
>
|
||||||
<MsButton type="icon" class="ms-minder-node-float-menu-icon-button" @click="associateCase">
|
<MsButton type="icon" class="ms-minder-node-float-menu-icon-button" @click="associateCase">
|
||||||
<MsIcon type="icon-icon_add_outlined" class="text-[var(--color-text-4)]" />
|
<MsIcon type="icon-icon_add_outlined" class="text-[var(--color-text-4)]" />
|
||||||
</MsButton>
|
</MsButton>
|
||||||
|
@ -86,7 +89,11 @@
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<a-form ref="configFormRef" :model="configForm" :disabled="!hasEditPermission" layout="vertical">
|
<a-form ref="configFormRef" :model="configForm" :disabled="!hasEditPermission" layout="vertical">
|
||||||
<a-form-item v-if="hasEditPermission && configForm.level === 2">
|
<a-form-item
|
||||||
|
v-if="
|
||||||
|
hasEditPermission && hasAnyPermission(['PROJECT_TEST_PLAN:READ+ASSOCIATION']) && configForm.level === 2
|
||||||
|
"
|
||||||
|
>
|
||||||
<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>
|
||||||
|
@ -116,7 +123,6 @@
|
||||||
</div>
|
</div>
|
||||||
<a-divider margin="8px" direction="vertical" />
|
<a-divider margin="8px" direction="vertical" />
|
||||||
<MsButton
|
<MsButton
|
||||||
v-permission="['CASE_REVIEW:READ+RELEVANCE']"
|
|
||||||
type="text"
|
type="text"
|
||||||
class="font-medium"
|
class="font-medium"
|
||||||
:disabled="!hasEditPermission"
|
:disabled="!hasEditPermission"
|
||||||
|
@ -127,6 +133,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="configForm.type !== PlanMinderCollectionType.FUNCTIONAL && configForm.level === 2"
|
||||||
|
class="hidden-item"
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-[8px]">
|
||||||
|
<a-switch v-model:model-value="configForm.extended" size="small" @change="handleExtendChange"></a-switch>
|
||||||
|
<div>{{ t('ms.minders.extend') }}</div>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
<template v-if="configForm.type !== PlanMinderCollectionType.FUNCTIONAL">
|
<template v-if="configForm.type !== PlanMinderCollectionType.FUNCTIONAL">
|
||||||
<a-form-item :label="t('system.project.resourcePool')">
|
<a-form-item :label="t('system.project.resourcePool')">
|
||||||
<a-select
|
<a-select
|
||||||
|
@ -211,15 +226,6 @@
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template> -->
|
</template> -->
|
||||||
</template>
|
</template>
|
||||||
<a-form-item
|
|
||||||
v-if="configForm.type !== PlanMinderCollectionType.FUNCTIONAL && configForm.level === 2"
|
|
||||||
class="hidden-item"
|
|
||||||
>
|
|
||||||
<div class="flex items-center gap-[8px]">
|
|
||||||
<a-switch v-model:model-value="configForm.extended" size="small" @change="handleExtendChange"></a-switch>
|
|
||||||
<div>{{ t('ms.minders.extend') }}</div>
|
|
||||||
</div>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
<div v-if="hasEditPermission" 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
|
||||||
|
@ -538,30 +544,6 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理节点选中
|
|
||||||
* @param node 节点
|
|
||||||
*/
|
|
||||||
function handleNodeSelect(node: PlanMinderNode) {
|
|
||||||
if (checkConfigFormUnsaved()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
checkNodeCanShowMenu(node);
|
|
||||||
if (extraVisible.value) {
|
|
||||||
if (node.data?.type === PlanMinderCollectionType.FUNCTIONAL && node.data?.level === 1) {
|
|
||||||
// 功能用例分类没有配置
|
|
||||||
extraVisible.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
activePlanSet.value = node;
|
|
||||||
switchingConfigFormData.value = true;
|
|
||||||
configForm.value = cloneDeep(activePlanSet.value.data);
|
|
||||||
nextTick(() => {
|
|
||||||
switchingConfigFormData.value = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 切换测试集配置显示
|
* 切换测试集配置显示
|
||||||
*/
|
*/
|
||||||
|
@ -633,6 +615,10 @@
|
||||||
refId: '',
|
refId: '',
|
||||||
projectId: '',
|
projectId: '',
|
||||||
};
|
};
|
||||||
|
const node: PlanMinderNode = window.minder.getNodeById(activePlanSet.value?.data.id);
|
||||||
|
if (node?.data) {
|
||||||
|
node.data.associateDTOS = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function associateCase() {
|
function associateCase() {
|
||||||
|
@ -658,6 +644,7 @@
|
||||||
() => {
|
() => {
|
||||||
if (!switchingConfigFormData.value && configForm.value) {
|
if (!switchingConfigFormData.value && configForm.value) {
|
||||||
configFormUnsaved.value = true;
|
configFormUnsaved.value = true;
|
||||||
|
minderStore.setMinderUnsaved(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -665,6 +652,31 @@
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理节点选中
|
||||||
|
* @param node 节点
|
||||||
|
*/
|
||||||
|
function handleNodeSelect(node: PlanMinderNode) {
|
||||||
|
if (checkConfigFormUnsaved()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkNodeCanShowMenu(node);
|
||||||
|
if (extraVisible.value) {
|
||||||
|
if (node.data?.type === PlanMinderCollectionType.FUNCTIONAL && node.data?.level === 1) {
|
||||||
|
// 功能用例分类没有配置
|
||||||
|
extraVisible.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
clearSelectedCases();
|
||||||
|
activePlanSet.value = node;
|
||||||
|
switchingConfigFormData.value = true;
|
||||||
|
configForm.value = cloneDeep(activePlanSet.value.data);
|
||||||
|
nextTick(() => {
|
||||||
|
switchingConfigFormData.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否停止拖拽排序动作
|
* 是否停止拖拽排序动作
|
||||||
* @param dragNode 拖动节点
|
* @param dragNode 拖动节点
|
||||||
|
@ -828,7 +840,7 @@
|
||||||
editList: [],
|
editList: [],
|
||||||
deletedIds: [],
|
deletedIds: [],
|
||||||
};
|
};
|
||||||
selectedAssociateCasesParams.value.selectIds = [];
|
clearSelectedCases();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ export default {
|
||||||
'ms.personal.baseInfo': 'Basic Information',
|
'ms.personal.baseInfo': 'Basic Information',
|
||||||
'ms.personal.setPsw': 'Password settings',
|
'ms.personal.setPsw': 'Password settings',
|
||||||
'ms.personal.setting': 'Personal settings',
|
'ms.personal.setting': 'Personal settings',
|
||||||
'ms.personal.apiKey': 'APIKEY',
|
'ms.personal.apiKey': 'API KEY',
|
||||||
'ms.personal.tripartite': 'Tripartite account',
|
'ms.personal.tripartite': 'Tripartite account',
|
||||||
'ms.personal.changeAvatar': 'Change avatar',
|
'ms.personal.changeAvatar': 'Change avatar',
|
||||||
'ms.personal.name': 'User name',
|
'ms.personal.name': 'User name',
|
||||||
|
@ -28,7 +28,7 @@ export default {
|
||||||
'ms.personal.changePswTip': 'After changing the password, you need to use the new password to log in to the system',
|
'ms.personal.changePswTip': 'After changing the password, you need to use the new password to log in to the system',
|
||||||
'ms.personal.updatePswSuccess':
|
'ms.personal.updatePswSuccess':
|
||||||
'The password has been modified successfully and will automatically log out in {count} seconds. Please log in with the new password.',
|
'The password has been modified successfully and will automatically log out in {count} seconds. Please log in with the new password.',
|
||||||
'ms.personal.apiKeyTip': 'The MeterSphere API is accessible through the APIKEY',
|
'ms.personal.apiKeyTip': 'The MeterSphere API is accessible through the API KEY',
|
||||||
'ms.personal.expireTime': 'Expiration',
|
'ms.personal.expireTime': 'Expiration',
|
||||||
'ms.personal.expired': 'Expired',
|
'ms.personal.expired': 'Expired',
|
||||||
'ms.personal.expiredTip': 'The expiration time can be changed in [Settings]',
|
'ms.personal.expiredTip': 'The expiration time can be changed in [Settings]',
|
||||||
|
@ -36,7 +36,7 @@ export default {
|
||||||
'ms.personal.setValidTime': 'Set effective time',
|
'ms.personal.setValidTime': 'Set effective time',
|
||||||
'ms.personal.createTime': 'Created time',
|
'ms.personal.createTime': 'Created time',
|
||||||
'ms.personal.copySuccess': 'Copied successfully',
|
'ms.personal.copySuccess': 'Copied successfully',
|
||||||
'ms.personal.maxTip': 'Up to 5 APIKEYs can be added',
|
'ms.personal.maxTip': 'Up to 5 API KEYs can be added',
|
||||||
'ms.personal.confirmClose': 'Confirm to close?',
|
'ms.personal.confirmClose': 'Confirm to close?',
|
||||||
'ms.personal.closeTip':
|
'ms.personal.closeTip':
|
||||||
'After closing, the test tasks executed using the Access Key will fail. Please operate with caution!',
|
'After closing, the test tasks executed using the Access Key will fail. Please operate with caution!',
|
||||||
|
|
|
@ -4,7 +4,7 @@ export default {
|
||||||
'ms.personal.baseInfo': '基本信息',
|
'ms.personal.baseInfo': '基本信息',
|
||||||
'ms.personal.setPsw': '密码设置',
|
'ms.personal.setPsw': '密码设置',
|
||||||
'ms.personal.setting': '个人设置',
|
'ms.personal.setting': '个人设置',
|
||||||
'ms.personal.apiKey': 'APIKEY',
|
'ms.personal.apiKey': 'API KEY',
|
||||||
'ms.personal.tripartite': '三方平台账号',
|
'ms.personal.tripartite': '三方平台账号',
|
||||||
'ms.personal.changeAvatar': '更换头像',
|
'ms.personal.changeAvatar': '更换头像',
|
||||||
'ms.personal.name': '姓名',
|
'ms.personal.name': '姓名',
|
||||||
|
@ -26,7 +26,7 @@ export default {
|
||||||
'ms.personal.newPsw': '新密码',
|
'ms.personal.newPsw': '新密码',
|
||||||
'ms.personal.changePswTip': '修改密码后,需要使用新的密码登录系统',
|
'ms.personal.changePswTip': '修改密码后,需要使用新的密码登录系统',
|
||||||
'ms.personal.updatePswSuccess': '密码修改成功,将在 {count} 秒后自动退出,请使用新密码登录',
|
'ms.personal.updatePswSuccess': '密码修改成功,将在 {count} 秒后自动退出,请使用新密码登录',
|
||||||
'ms.personal.apiKeyTip': '可通过 APIKEY 访问 MeterSphere API',
|
'ms.personal.apiKeyTip': '可通过 API KEY 访问 MeterSphere API',
|
||||||
'ms.personal.expireTime': '过期时间',
|
'ms.personal.expireTime': '过期时间',
|
||||||
'ms.personal.expired': '已到期',
|
'ms.personal.expired': '已到期',
|
||||||
'ms.personal.expiredTip': '可在【设置】内更改到期时间',
|
'ms.personal.expiredTip': '可在【设置】内更改到期时间',
|
||||||
|
@ -34,7 +34,7 @@ export default {
|
||||||
'ms.personal.setValidTime': '设置有效时间',
|
'ms.personal.setValidTime': '设置有效时间',
|
||||||
'ms.personal.createTime': '创建时间',
|
'ms.personal.createTime': '创建时间',
|
||||||
'ms.personal.copySuccess': '复制成功',
|
'ms.personal.copySuccess': '复制成功',
|
||||||
'ms.personal.maxTip': '最多可添加 5 个APIKEY',
|
'ms.personal.maxTip': '最多可添加 5 个API KEY',
|
||||||
'ms.personal.confirmClose': '确认关闭吗?',
|
'ms.personal.confirmClose': '确认关闭吗?',
|
||||||
'ms.personal.closeTip': '关闭后,将导致使用该 Access Key 执行的测试任务执行失败,请谨慎操作!',
|
'ms.personal.closeTip': '关闭后,将导致使用该 Access Key 执行的测试任务执行失败,请谨慎操作!',
|
||||||
'ms.personal.closeSuccess': '关闭成功',
|
'ms.personal.closeSuccess': '关闭成功',
|
||||||
|
|
|
@ -26,12 +26,8 @@
|
||||||
|
|
||||||
import MsSelect from '@/components/business/ms-select/index';
|
import MsSelect from '@/components/business/ms-select/index';
|
||||||
|
|
||||||
import { useUserStore } from '@/store';
|
|
||||||
|
|
||||||
import initOptionsFunc, { UserRequestTypeEnum } from './utils';
|
import initOptionsFunc, { UserRequestTypeEnum } from './utils';
|
||||||
|
|
||||||
const userStore = useUserStore();
|
|
||||||
|
|
||||||
defineOptions({ name: 'MsUserSelector' });
|
defineOptions({ name: 'MsUserSelector' });
|
||||||
|
|
||||||
export interface MsUserSelectorOption {
|
export interface MsUserSelectorOption {
|
||||||
|
@ -99,7 +95,7 @@
|
||||||
};
|
};
|
||||||
const optionLabelRender = (option: SelectOptionData) => {
|
const optionLabelRender = (option: SelectOptionData) => {
|
||||||
if (option.email !== '') {
|
if (option.email !== '') {
|
||||||
return `<span class='text-[var(--color-text-1)]'>${option.name}</span><span class='text-[var(--color-text-4)] ml-[4px]'>(${option.email})</span>`;
|
return `<span class='text-[var(--color-text-1)]'>${option.name}</span><span class='text-[var(--color-text-4)] ml-[4px]'>(${option.email})</span>`;
|
||||||
}
|
}
|
||||||
return `<span class='text-[var(--color-text-1)]'>${option.name}</span>`;
|
return `<span class='text-[var(--color-text-1)]'>${option.name}</span>`;
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,12 @@
|
||||||
</a-breadcrumb-item>
|
</a-breadcrumb-item>
|
||||||
</a-breadcrumb>
|
</a-breadcrumb>
|
||||||
</div>
|
</div>
|
||||||
<nodeFloatMenu v-if="props.canShowFloatMenu" v-bind="props" @close="emit('floatMenuClose')">
|
<nodeFloatMenu
|
||||||
|
v-if="props.canShowFloatMenu"
|
||||||
|
v-bind="props"
|
||||||
|
v-model:visible="floatMenuVisible"
|
||||||
|
@close="emit('floatMenuClose')"
|
||||||
|
>
|
||||||
<template #extractMenu>
|
<template #extractMenu>
|
||||||
<slot name="extractMenu"></slot>
|
<slot name="extractMenu"></slot>
|
||||||
</template>
|
</template>
|
||||||
|
@ -21,13 +26,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" name="minderContainer" setup>
|
<script lang="ts" name="minderContainer" setup>
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
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';
|
||||||
|
|
||||||
import useLeaveUnSaveTip from '@/hooks/useLeaveUnSaveTip';
|
import useLeaveUnSaveTip from '@/hooks/useLeaveUnSaveTip';
|
||||||
import useMinderStore from '@/store/modules/components/minder-editor';
|
import useMinderStore from '@/store/modules/components/minder-editor';
|
||||||
import { findNodePathByKey, replaceNodeInTree } from '@/utils';
|
import { findNodePathByKey, mapTree, replaceNodeInTree } from '@/utils';
|
||||||
|
|
||||||
import { MinderEventName } from '@/enums/minderEnum';
|
import { MinderEventName } from '@/enums/minderEnum';
|
||||||
|
|
||||||
|
@ -139,9 +146,6 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const menuVisible = ref(false);
|
|
||||||
const menuPopupOffset = ref([0, 0]);
|
|
||||||
|
|
||||||
function getCurrentTreePath() {
|
function getCurrentTreePath() {
|
||||||
if (innerImportJson.value.root.id === 'NONE' || innerImportJson.value.treePath?.length <= 1) {
|
if (innerImportJson.value.root.id === 'NONE' || innerImportJson.value.treePath?.length <= 1) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -176,12 +180,15 @@
|
||||||
const root: MinderJsonNode = window.minder.getRoot();
|
const root: MinderJsonNode = window.minder.getRoot();
|
||||||
window.minder.toggleSelect(root); // 先取消选中
|
window.minder.toggleSelect(root); // 先取消选中
|
||||||
window.minder.select(root); // 再选中,才能触发选中变化事件
|
window.minder.select(root); // 再选中,才能触发选中变化事件
|
||||||
|
window.minder.execCommand('ExpandToLevel', 1);
|
||||||
currentTreePath.value = getCurrentTreePath();
|
currentTreePath.value = getCurrentTreePath();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.minder.execCommand('camera', root);
|
window.minder.execCommand('camera', root);
|
||||||
}, 100); // TODO:暂未知渲染时机,临时延迟解决
|
}, 100); // TODO:暂未知渲染时机,临时延迟解决
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const floatMenuVisible = ref(false);
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
let data = importJson.value;
|
let data = importJson.value;
|
||||||
if (innerImportJson.value.treePath?.length > 1) {
|
if (innerImportJson.value.treePath?.length > 1) {
|
||||||
|
@ -193,25 +200,33 @@
|
||||||
'id'
|
'id'
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
data = window.minder.exportJson();
|
const fullJson = window.minder.exportJson();
|
||||||
|
data = cloneDeep(fullJson);
|
||||||
|
importJson.value = fullJson;
|
||||||
}
|
}
|
||||||
emit('save', data, () => {
|
emit('save', data, () => {
|
||||||
|
importJson.value.root.children = mapTree<MinderJsonNode>(importJson.value.root.children || [], (node) => ({
|
||||||
|
...node,
|
||||||
|
data: {
|
||||||
|
...node.data,
|
||||||
|
isNew: false,
|
||||||
|
changed: false,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
if (innerImportJson.value.treePath?.length > 1) {
|
||||||
|
switchNode(innerImportJson.value.root.data);
|
||||||
|
} else {
|
||||||
|
innerImportJson.value = importJson.value;
|
||||||
|
window.minder.importJson(importJson.value);
|
||||||
|
}
|
||||||
minderStore.setMinderUnsaved(false);
|
minderStore.setMinderUnsaved(false);
|
||||||
menuVisible.value = false;
|
floatMenuVisible.value = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => minderStore.event.eventId,
|
() => minderStore.event.eventId,
|
||||||
() => {
|
() => {
|
||||||
if (minderStore.event.name === MinderEventName.HOTBOX && minderStore.event.nodePosition) {
|
|
||||||
const nodeDomWidth = minderStore.event.nodeDom?.getBoundingClientRect().width || 0;
|
|
||||||
menuPopupOffset.value = [
|
|
||||||
minderStore.event.nodePosition.x + nodeDomWidth / 2,
|
|
||||||
minderStore.event.nodePosition.y - nodeDomWidth / 4,
|
|
||||||
];
|
|
||||||
menuVisible.value = true;
|
|
||||||
}
|
|
||||||
if (minderStore.event.name === MinderEventName.ENTER_NODE && minderStore.event.nodes) {
|
if (minderStore.event.name === MinderEventName.ENTER_NODE && minderStore.event.nodes) {
|
||||||
switchNode(minderStore.event.nodes[0]);
|
switchNode(minderStore.event.nodes[0]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,11 +184,19 @@
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useMinderStore from '@/store/modules/components/minder-editor/index';
|
import useMinderStore from '@/store/modules/components/minder-editor/index';
|
||||||
import { MinderNodePosition } from '@/store/modules/components/minder-editor/types';
|
import { MinderNodePosition } from '@/store/modules/components/minder-editor/types';
|
||||||
import { getGenerateId, sleep, traverseTree } from '@/utils';
|
import { getGenerateId, sleep } from '@/utils';
|
||||||
|
|
||||||
import { MinderEventName } from '@/enums/minderEnum';
|
import { MinderEventName } from '@/enums/minderEnum';
|
||||||
|
|
||||||
import { floatMenuProps, insertProps, mainEditorProps, MinderJsonNode, priorityProps, tagProps } from '../props';
|
import {
|
||||||
|
floatMenuProps,
|
||||||
|
insertProps,
|
||||||
|
mainEditorProps,
|
||||||
|
MinderJsonNode,
|
||||||
|
MinderJsonNodeData,
|
||||||
|
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({
|
||||||
|
@ -208,7 +216,9 @@
|
||||||
const currentNodeTags = ref<string[]>([]);
|
const currentNodeTags = ref<string[]>([]);
|
||||||
const tags = ref<string[]>([]);
|
const tags = ref<string[]>([]);
|
||||||
|
|
||||||
const menuVisible = ref(false);
|
const menuVisible = defineModel<boolean>('visible', {
|
||||||
|
default: false,
|
||||||
|
});
|
||||||
const menuPopupOffset = ref<TriggerPopupTranslate>([0, 0]);
|
const menuPopupOffset = ref<TriggerPopupTranslate>([0, 0]);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
@ -310,6 +320,9 @@
|
||||||
}
|
}
|
||||||
window.minder.execCommand('resource', origin);
|
window.minder.execCommand('resource', origin);
|
||||||
minderStore.dispatchEvent(MinderEventName.SET_TAG, undefined, undefined, undefined, selectedNodes);
|
minderStore.dispatchEvent(MinderEventName.SET_TAG, undefined, undefined, undefined, selectedNodes);
|
||||||
|
if (props.afterTagEdit) {
|
||||||
|
props.afterTagEdit(selectedNodes, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,21 +375,15 @@
|
||||||
case 'paste':
|
case 'paste':
|
||||||
minderStore.dispatchEvent(MinderEventName.PASTE_NODE, undefined, undefined, undefined, selectedNodes);
|
minderStore.dispatchEvent(MinderEventName.PASTE_NODE, undefined, undefined, undefined, selectedNodes);
|
||||||
window.minder.execCommand('Paste');
|
window.minder.execCommand('Paste');
|
||||||
const pastedNode: MinderJsonNode = window.minder.getSelectedNode();
|
let pastedNodes: MinderJsonNode[] = window.minder.getSelectedNodes();
|
||||||
if (pastedNode) {
|
if (pastedNodes.length > 0) {
|
||||||
pastedNode.data = {
|
pastedNodes = pastedNodes.map((e) => {
|
||||||
...pastedNode.data,
|
e.data = {
|
||||||
text: pastedNode.data?.text || '',
|
...(e.data as MinderJsonNodeData),
|
||||||
isNew: true,
|
|
||||||
id: getGenerateId(),
|
|
||||||
};
|
|
||||||
traverseTree(pastedNode.children || [], (node) => {
|
|
||||||
node.data = {
|
|
||||||
...node.data,
|
|
||||||
text: node.data?.text || '',
|
|
||||||
isNew: true,
|
isNew: true,
|
||||||
id: getGenerateId(),
|
id: getGenerateId(),
|
||||||
};
|
};
|
||||||
|
return e;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
tagProps,
|
tagProps,
|
||||||
viewMenuProps,
|
viewMenuProps,
|
||||||
} from './props';
|
} from './props';
|
||||||
|
import { isNodeInMinderView } from './script/tool/utils';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'moldChange', data: number): void;
|
(e: 'moldChange', data: number): void;
|
||||||
|
@ -97,9 +98,13 @@
|
||||||
(val) => {
|
(val) => {
|
||||||
const node: MinderJsonNode = window.minder.getSelectedNode();
|
const node: MinderJsonNode = window.minder.getSelectedNode();
|
||||||
if (val && node) {
|
if (val && node) {
|
||||||
|
const nodePosition = node?.getRenderBox();
|
||||||
|
// 如果节点不在视图中,将节点移动到视图中
|
||||||
|
if (nodePosition && !isNodeInMinderView(undefined, nodePosition, nodePosition.width / 2)) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.minder.execCommand('camera', node, 100);
|
window.minder.execCommand('camera', node, 100);
|
||||||
}, 100);
|
}, 300); // 抽屉动画 300ms
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -238,7 +238,7 @@ export default defineComponent({
|
||||||
const computedCurrent = computed(() => props.current ?? _current.value);
|
const computedCurrent = computed(() => props.current ?? _current.value);
|
||||||
const computedPageSize = computed(() => props.pageSize ?? _pageSize.value);
|
const computedPageSize = computed(() => props.pageSize ?? _pageSize.value);
|
||||||
|
|
||||||
const pages = computed(() => Math.ceil(props.total / computedPageSize.value));
|
const pages = computed(() => Math.ceil(props.total / computedPageSize.value) || 1); // 页码最小是 1
|
||||||
|
|
||||||
const handleClick = (page: number) => {
|
const handleClick = (page: number) => {
|
||||||
// when pageJumper blur and input.value is undefined, page is illegal
|
// when pageJumper blur and input.value is undefined, page is illegal
|
||||||
|
@ -378,14 +378,6 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(pages, (curPages, prePages) => {
|
|
||||||
if (props.autoAdjust && curPages !== prePages && computedCurrent.value > 1 && computedCurrent.value > curPages) {
|
|
||||||
_current.value = curPages;
|
|
||||||
emit('update:current', curPages);
|
|
||||||
emit('change', curPages);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const cls = computed(() => [
|
const cls = computed(() => [
|
||||||
prefixCls,
|
prefixCls,
|
||||||
`${prefixCls}-size-${mergedSize.value}`,
|
`${prefixCls}-size-${mergedSize.value}`,
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
v-model:model-value="innerParams.bodyType"
|
v-model:model-value="innerParams.bodyType"
|
||||||
type="button"
|
type="button"
|
||||||
size="small"
|
size="small"
|
||||||
|
:disabled="props.disabledBodyType"
|
||||||
@change="(val) => changeBodyFormat(val as RequestBodyFormat)"
|
@change="(val) => changeBodyFormat(val as RequestBodyFormat)"
|
||||||
>
|
>
|
||||||
<a-radio v-for="item of RequestBodyFormat" :key="item" :value="item">
|
<a-radio v-for="item of RequestBodyFormat" :key="item" :value="item">
|
||||||
|
@ -109,7 +110,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useVModel } from '@vueuse/core';
|
|
||||||
import { TableColumnData } from '@arco-design/web-vue';
|
import { TableColumnData } from '@arco-design/web-vue';
|
||||||
|
|
||||||
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
|
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
|
||||||
|
@ -132,7 +132,7 @@
|
||||||
import { defaultBodyParamsItem } from '@/views/api-test/components/config';
|
import { defaultBodyParamsItem } from '@/views/api-test/components/config';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
params: ExecuteBody;
|
disabledBodyType?: boolean; // 禁用body类型切换
|
||||||
disabledParamValue?: boolean; // 参数值禁用
|
disabledParamValue?: boolean; // 参数值禁用
|
||||||
disabledExceptParam?: boolean; // 除了可以修改参数值其他都禁用
|
disabledExceptParam?: boolean; // 除了可以修改参数值其他都禁用
|
||||||
uploadTempFileApi?: (file: File) => Promise<any>; // 上传临时文件接口
|
uploadTempFileApi?: (file: File) => Promise<any>; // 上传临时文件接口
|
||||||
|
@ -148,15 +148,24 @@
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const innerParams = useVModel(props, 'params', emit);
|
const innerParams = defineModel<ExecuteBody>('params', {
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
const batchAddKeyValVisible = ref(false);
|
const batchAddKeyValVisible = ref(false);
|
||||||
const fileList = ref<MsFileItem[]>([]);
|
const fileList = ref<MsFileItem[]>([]);
|
||||||
|
|
||||||
onBeforeMount(() => {
|
watch(
|
||||||
if (innerParams.value.binaryBody && innerParams.value.binaryBody.file) {
|
() => innerParams.value.binaryBody?.file,
|
||||||
|
() => {
|
||||||
|
if (innerParams.value.binaryBody?.file) {
|
||||||
fileList.value = [innerParams.value.binaryBody.file as unknown as MsFileItem];
|
fileList.value = [innerParams.value.binaryBody.file as unknown as MsFileItem];
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
async function handleFileChange(files: MsFileItem[], file?: MsFileItem) {
|
async function handleFileChange(files: MsFileItem[], file?: MsFileItem) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -780,7 +780,7 @@
|
||||||
/**
|
/**
|
||||||
* 设置插件表单数据
|
* 设置插件表单数据
|
||||||
*/
|
*/
|
||||||
function setPluginFormData() {
|
async function setPluginFormData() {
|
||||||
const tempForm = temporaryPluginFormMap[requestVModel.value.id];
|
const tempForm = temporaryPluginFormMap[requestVModel.value.id];
|
||||||
if (tempForm || !requestVModel.value.isNew || requestVModel.value.isCopy) {
|
if (tempForm || !requestVModel.value.isNew || requestVModel.value.isCopy) {
|
||||||
// 如果缓存的表单数据存在或者是编辑状态,则需要将之前的输入数据填充
|
// 如果缓存的表单数据存在或者是编辑状态,则需要将之前的输入数据填充
|
||||||
|
@ -809,6 +809,7 @@
|
||||||
isInitPluginForm.value = true;
|
isInitPluginForm.value = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
await nextTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
const pluginError = ref(false);
|
const pluginError = ref(false);
|
||||||
|
@ -823,7 +824,7 @@
|
||||||
pluginError.value = false;
|
pluginError.value = false;
|
||||||
isInitPluginForm.value = false;
|
isInitPluginForm.value = false;
|
||||||
if (pluginScriptMap.value[requestVModel.value.protocol] !== undefined) {
|
if (pluginScriptMap.value[requestVModel.value.protocol] !== undefined) {
|
||||||
setPluginFormData();
|
await setPluginFormData();
|
||||||
// 已经初始化过
|
// 已经初始化过
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -831,7 +832,7 @@
|
||||||
pluginLoading.value = true;
|
pluginLoading.value = true;
|
||||||
const res = await getPluginScript(pluginId);
|
const res = await getPluginScript(pluginId);
|
||||||
pluginScriptMap.value[requestVModel.value.protocol] = res;
|
pluginScriptMap.value[requestVModel.value.protocol] = res;
|
||||||
setPluginFormData();
|
await setPluginFormData();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
@ -1191,7 +1192,13 @@
|
||||||
}
|
}
|
||||||
if (props.request.isExecute && !requestVModel.value.executeLoading) {
|
if (props.request.isExecute && !requestVModel.value.executeLoading) {
|
||||||
// 如果是执行操作打开接口详情,且该接口不在执行状态中,则立即执行
|
// 如果是执行操作打开接口详情,且该接口不在执行状态中,则立即执行
|
||||||
|
if (requestVModel.value.protocol !== 'HTTP') {
|
||||||
|
setTimeout(() => {
|
||||||
execute(isPriorityLocalExec.value ? 'localExec' : 'serverExec');
|
execute(isPriorityLocalExec.value ? 'localExec' : 'serverExec');
|
||||||
|
}, 100);
|
||||||
|
} else {
|
||||||
|
execute(isPriorityLocalExec.value ? 'localExec' : 'serverExec');
|
||||||
|
}
|
||||||
} else if (temporaryResponseMap[props.request.reportId]) {
|
} else if (temporaryResponseMap[props.request.reportId]) {
|
||||||
// 如果有缓存的报告未读取,则直接赋值
|
// 如果有缓存的报告未读取,则直接赋值
|
||||||
requestVModel.value.response = temporaryResponseMap[props.request.reportId];
|
requestVModel.value.response = temporaryResponseMap[props.request.reportId];
|
||||||
|
|
|
@ -239,6 +239,7 @@
|
||||||
v-model:params="requestVModel.body"
|
v-model:params="requestVModel.body"
|
||||||
:disabled-param-value="!isEditableApi && !isEditableParamValue"
|
:disabled-param-value="!isEditableApi && !isEditableParamValue"
|
||||||
:disabled-except-param="!isEditableApi"
|
:disabled-except-param="!isEditableApi"
|
||||||
|
:disabled-body-type="!isEditableApi"
|
||||||
:upload-temp-file-api="uploadTempFile"
|
:upload-temp-file-api="uploadTempFile"
|
||||||
:file-save-as-source-id="props.step?.id"
|
:file-save-as-source-id="props.step?.id"
|
||||||
:file-save-as-api="stepTransferFile"
|
:file-save-as-api="stepTransferFile"
|
||||||
|
|
|
@ -32,7 +32,7 @@ export default {
|
||||||
'Scenario level: Load CSV before executing the scenario. Data can be read from CSV in any step of the current scenario.',
|
'Scenario level: Load CSV before executing the scenario. Data can be read from CSV in any step of the current scenario.',
|
||||||
'apiScenario.params.csvScopedTip2':
|
'apiScenario.params.csvScopedTip2':
|
||||||
'Step level: The CSV needs to be added to the scenario step. The CSV is loaded when executing this step, and the scope is the request within the step.',
|
'Step level: The CSV needs to be added to the scenario step. The CSV is loaded when executing this step, and the scope is the request within the step.',
|
||||||
'apiScenario.params.searchPlaceholder': 'Search by name or tag',
|
'apiScenario.params.searchPlaceholder': 'Search by name/tag',
|
||||||
'apiScenario.params.priority':
|
'apiScenario.params.priority':
|
||||||
'Variable Priority: Temporary Parameters > Scenario Parameters > Environment Parameters > Global Parameters; Note: Avoid using variables with the same name. In case of same name variables, scenario-level CSV has the highest priority.',
|
'Variable Priority: Temporary Parameters > Scenario Parameters > Environment Parameters > Global Parameters; Note: Avoid using variables with the same name. In case of same name variables, scenario-level CSV has the highest priority.',
|
||||||
'apiScenario.params.name': 'Variable Name',
|
'apiScenario.params.name': 'Variable Name',
|
||||||
|
|
|
@ -99,7 +99,7 @@
|
||||||
import useModal from '@/hooks/useModal';
|
import useModal from '@/hooks/useModal';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||||
import { mapTree } from '@/utils';
|
import { mapTree, traverseTree } from '@/utils';
|
||||||
import { hasAnyPermission } from '@/utils/permission';
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import type { CreateOrUpdateModule, UpdateModule } from '@/models/caseManagement/featureCase';
|
import type { CreateOrUpdateModule, UpdateModule } from '@/models/caseManagement/featureCase';
|
||||||
|
@ -386,12 +386,9 @@
|
||||||
watch(
|
watch(
|
||||||
() => props.modulesCount,
|
() => props.modulesCount,
|
||||||
(obj) => {
|
(obj) => {
|
||||||
caseTree.value = mapTree<ModuleTreeNode>(caseTree.value, (node) => {
|
traverseTree(caseTree.value, (node) => {
|
||||||
return {
|
node.count = obj?.[node.id] || 0;
|
||||||
...node,
|
node.hideMoreAction = node.id === 'root' || props.isModal;
|
||||||
hideMoreAction: node.id === 'root' || props.isModal,
|
|
||||||
count: obj?.[node.id] || 0,
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,7 +9,7 @@ export default {
|
||||||
'project.environmental.requestHeader': 'Request Header',
|
'project.environmental.requestHeader': 'Request Header',
|
||||||
'project.environmental.allParams': 'All Parameters',
|
'project.environmental.allParams': 'All Parameters',
|
||||||
'project.environmental.mustContain': 'Must Contain',
|
'project.environmental.mustContain': 'Must Contain',
|
||||||
'project.environmental.searchParamsHolder': 'Search by name or tag',
|
'project.environmental.searchParamsHolder': 'Search by name/tag',
|
||||||
'project.environmental.paramName': 'Parameter Name',
|
'project.environmental.paramName': 'Parameter Name',
|
||||||
'project.environmental.globalVariable': 'Parameter',
|
'project.environmental.globalVariable': 'Parameter',
|
||||||
'project.environmental.paramType': 'Type',
|
'project.environmental.paramType': 'Type',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-4 grid grid-cols-4 gap-2">
|
<div class="mb-[16px] grid grid-cols-4 gap-2">
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
<a-button v-permission="['PROJECT_USER:READ+ADD']" class="mr-3" type="primary" @click="addMember">
|
<a-button v-permission="['PROJECT_USER:READ+ADD']" class="mr-3" type="primary" @click="addMember">
|
||||||
{{ t('project.member.addMember') }}
|
{{ t('project.member.addMember') }}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
export default {
|
export default {
|
||||||
'project.member.addMember': 'Add Member',
|
'project.member.addMember': 'Add Member',
|
||||||
'project.member.updateMember': 'Update Member',
|
'project.member.updateMember': 'Update Member',
|
||||||
'project.member.searchMember': 'Search by name or email address or phone',
|
'project.member.searchMember': 'Search by name/email/phone',
|
||||||
'project.member.remove': 'Remove',
|
'project.member.remove': 'Remove',
|
||||||
'project.member.edit': 'Edit',
|
'project.member.edit': 'Edit',
|
||||||
'project.member.add': 'Add',
|
'project.member.add': 'Add',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
export default {
|
export default {
|
||||||
'organization.member.addMember': 'Add Member',
|
'organization.member.addMember': 'Add Member',
|
||||||
'organization.member.updateMember': 'Update Member({name})',
|
'organization.member.updateMember': 'Update Member({name})',
|
||||||
'organization.member.searchMember': 'Search by name or email address or phone',
|
'organization.member.searchMember': 'Search by name/email/phone',
|
||||||
'organization.member.remove': 'Remove',
|
'organization.member.remove': 'Remove',
|
||||||
'organization.member.edit': 'Edit',
|
'organization.member.edit': 'Edit',
|
||||||
'organization.member.batchActionAddProject': 'Add to project',
|
'organization.member.batchActionAddProject': 'Add to project',
|
||||||
|
|
|
@ -76,7 +76,7 @@ export default {
|
||||||
'system.project.revokeDeleteToolTip': 'The project will be deleted automatically after {count} days',
|
'system.project.revokeDeleteToolTip': 'The project will be deleted automatically after {count} days',
|
||||||
'system.project.removeTip': "Remove it, and you'll lose access to the project.",
|
'system.project.removeTip': "Remove it, and you'll lose access to the project.",
|
||||||
'system.organization.projectIsDisabled': 'The project has ended and can be opened in the project list.',
|
'system.organization.projectIsDisabled': 'The project has ended and can be opened in the project list.',
|
||||||
'system.project.searchPlaceholder': 'Search by name or id',
|
'system.project.searchPlaceholder': 'Search by name/id',
|
||||||
'system.project.afterModule':
|
'system.project.afterModule':
|
||||||
'After the module is canceled, users will be unable to access the specified module, and existing data will remain intact',
|
'After the module is canceled, users will be unable to access the specified module, and existing data will remain intact',
|
||||||
'system.project.projectAdminIsNotNull': 'Project administrator cannot be empty',
|
'system.project.projectAdminIsNotNull': 'Project administrator cannot be empty',
|
||||||
|
|
|
@ -2,7 +2,7 @@ export default {
|
||||||
'system.user.createUser': 'Create User',
|
'system.user.createUser': 'Create User',
|
||||||
'system.user.emailInvite': 'Email Invite',
|
'system.user.emailInvite': 'Email Invite',
|
||||||
'system.user.importUser': 'Import User',
|
'system.user.importUser': 'Import User',
|
||||||
'system.user.searchUser': 'Search by name or email or phone',
|
'system.user.searchUser': 'Search by name/email/phone',
|
||||||
'system.user.editUser': 'Edit',
|
'system.user.editUser': 'Edit',
|
||||||
'system.user.resetPassword': 'Reset PSW',
|
'system.user.resetPassword': 'Reset PSW',
|
||||||
'system.user.disable': 'Disable',
|
'system.user.disable': 'Disable',
|
||||||
|
|
Loading…
Reference in New Issue