fix(全局): 部分 bug 修复

This commit is contained in:
baiqi 2024-06-20 17:12:44 +08:00 committed by Craftsman
parent fbd0a27486
commit eff826b97c
25 changed files with 275 additions and 227 deletions

View File

@ -295,26 +295,32 @@
});
const buttonDropDownVisible = ref(false);
onBeforeMount(() => {
//
const defaultFiles = fileList.value.filter((item) => item) || [];
if (defaultFiles.length > 0) {
if (props.multiple) {
inputFiles.value = defaultFiles.map((item) => ({
...item,
//
value: item?.[props.fields.id] || item.uid || '', // uid id
label: item?.[props.fields.name] || item?.name || '',
}));
} else {
inputFileName.value = defaultFiles[0]?.[props.fields.name] || defaultFiles[0]?.name || '';
watch(
() => fileList.value,
() => {
//
const defaultFiles = fileList.value.filter((item) => item) || [];
if (defaultFiles.length > 0) {
if (props.multiple) {
inputFiles.value = defaultFiles.map((item) => ({
...item,
//
value: item?.[props.fields.id] || item.uid || '', // uid id
label: item?.[props.fields.name] || item?.name || '',
}));
} else {
inputFileName.value = defaultFiles[0]?.[props.fields.name] || defaultFiles[0]?.name || '';
}
getListFunParams.value.combine.hiddenIds = defaultFiles
.filter((item) => !item?.local)
.map((item) => item?.[props.fields.id] || '')
.filter((item) => item);
}
getListFunParams.value.combine.hiddenIds = defaultFiles
.filter((item) => !item?.local)
.map((item) => item?.[props.fields.id] || '')
.filter((item) => item);
},
{
immediate: true,
}
});
);
function handleChange(_fileList: MsFileItem[], fileItem: MsFileItem) {
if (props.multiple) {

View File

@ -12,7 +12,7 @@
}
"
>
<span class="mx-[2px]"></span>
<span class="mx-[2px]"><slot></slot></span>
<template #content>
<div class="flex flex-col gap-[16px] text-[14px]">
<div class="font-semibold text-[var(--color-text-1)]">
@ -68,7 +68,6 @@
const props = withDefaults(
defineProps<{
visible: boolean;
savingFile?: MsFileItem;
fileSaveAsSourceId: string | number;
sourceIdKey?: string; // idkey
@ -89,7 +88,7 @@
const { t } = useI18n();
const saveFilePopoverVisible = defineModel<boolean>('visible', {
required: true,
default: false,
});
const saveFileForm = ref({
name: '',

View File

@ -2,7 +2,7 @@
import { computed, defineComponent, h, ref } from 'vue';
import { RouteRecordRaw, useRoute, useRouter } from 'vue-router';
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 MsIcon from '@/components/pure/ms-icon-font/index.vue';
@ -407,7 +407,7 @@
//
const renderMenuItem = (element: RouteRecordRaw | null, icon: (() => any) | null) =>
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">
{collapsed.value ? (
<div class="text-center text-[12px] leading-[16px]">
@ -436,7 +436,7 @@
</div>
</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 ? (
<div class="text-center text-[12px] leading-[16px]">
{t(element?.meta?.collapsedLocale || element?.meta?.locale || '')}

View File

@ -37,23 +37,19 @@
>
{{ t('ms.upload.preview') }}
</MsButton>
<MsButton type="button" status="primary" class="!mr-[4px]" @click="transferFile(item)">
{{ t('caseManagement.featureCase.storage') }}
</MsButton>
<SaveAsFilePopover
v-model:visible="transferVisible"
v-if="item.status !== 'init'"
:saving-file="activeTransferFileParams"
:file-save-as-source-id="activeCase.id || ''"
:file-save-as-api="transferFileRequest"
:file-module-options-api="getTransferFileTree"
source-id-key="caseId"
/>
<MsButton
v-if="item.status !== 'init'"
type="button"
status="primary"
class="!mr-[4px]"
@click="transferFile(item)"
>
{{ t('caseManagement.featureCase.storage') }}
</MsButton>
<span :id="item.uid"></span>
</SaveAsFilePopover>
<MsButton
v-if="item.status !== 'init'"
type="button"
@ -198,7 +194,7 @@
return item;
});
}
Message.success(t('ms.upload.uploadSuccess'));
Message.success(t('common.linkSuccess'));
emit('uploadSuccess');
} catch (error) {
// eslint-disable-next-line no-console
@ -244,14 +240,12 @@
}
}
const transferVisible = ref<boolean>(false);
const activeTransferFileParams = ref<MsFileItem>();
//
function transferFile(item: MsFileItem) {
activeTransferFileParams.value = { ...item };
transferVisible.value = true;
document.getElementById(item.uid)?.click();
}
//

View File

@ -87,7 +87,7 @@
import useAppStore from '@/store/modules/app';
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 { filterTree, findNodeByKey, getGenerateId, mapTree, replaceNodeInTree } from '@/utils';
import { hasAnyPermission } from '@/utils/permission';
import {
@ -156,69 +156,65 @@
/**
* 初始化用例模块树
*/
async function initCaseTree(notRemote = false) {
async function initCaseTree() {
try {
loading.value = true;
if (!notRemote) {
const res = await getCaseMinderTree({
projectId: appStore.currentProjectId,
moduleId: '', //
});
caseTree.value = mapTree<MinderJsonNode>(res, (e) => ({
...e,
data: {
...e.data,
id: e.id || e.data?.id || '',
text: e.name || e.data?.text || '',
resource: props.modulesCount[e.id] !== undefined ? [moduleTag] : e.data?.resource,
expandState: e.level === 1 ? 'expand' : 'collapse',
count: props.modulesCount[e.id],
isNew: false,
changed: false,
},
children:
props.modulesCount[e.id] > 0 && !e.children?.length
? [
{
data: {
id: 'fakeNode',
text: 'fakeNode',
resource: ['fakeNode'],
isNew: false,
changed: false,
},
const res = await getCaseMinderTree({
projectId: appStore.currentProjectId,
moduleId: '', //
});
caseTree.value = mapTree<MinderJsonNode>(res, (e) => ({
...e,
data: {
...e.data,
id: e.id || e.data?.id || '',
text: e.name || e.data?.text || '',
resource: props.modulesCount[e.id] !== undefined ? [moduleTag] : e.data?.resource,
expandState: e.level === 0 ? 'expand' : 'collapse',
count: props.modulesCount[e.id],
isNew: false,
changed: false,
},
children:
props.modulesCount[e.id] > 0 && !e.children?.length
? [
{
data: {
id: 'fakeNode',
text: 'fakeNode',
resource: ['fakeNode'],
isNew: false,
changed: false,
},
]
: e.children,
}));
importJson.value.root = {
children: caseTree.value,
data: {
id: 'NONE',
text: t('ms.minders.allModule'),
resource: [moduleTag],
disabled: true,
},
};
importJson.value.treePath = [];
window.minder.importJson(importJson.value);
}
if (notRemote) {
if (props.moduleId !== 'all') {
// ID
nextTick(() => {
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, undefined, [
window.minder.getNodeById(props.moduleId),
]);
});
} else {
// ID
nextTick(() => {
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, undefined, [
importJson.value.root,
]);
});
}
},
]
: e.children,
}));
importJson.value.root = {
children: caseTree.value,
data: {
id: 'NONE',
text: t('ms.minders.allModule'),
resource: [moduleTag],
disabled: true,
},
};
importJson.value.treePath = [];
window.minder.importJson(importJson.value);
if (props.moduleId !== 'all') {
// ID
nextTick(() => {
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, undefined, [
findNodeByKey(importJson.value.root.children || [], props.moduleId, 'id', 'data') as MinderJsonNode,
]);
});
} else {
//
nextTick(() => {
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, undefined, [
importJson.value.root,
]);
});
}
} catch (error) {
// eslint-disable-next-line no-console
@ -291,7 +287,10 @@
try {
baseInfoLoading.value = true;
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) || [];
if (fileIds.length) {
checkUpdateFileIds.value = await checkFileIsUpdateRequest(fileIds);
@ -389,7 +388,7 @@
projectId: appStore.currentProjectId,
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) {
window.minder.removeNode(fakeNode);
}
@ -464,6 +463,10 @@
extraVisible.value = false;
showDetailMenu.value = false;
resetExtractInfo();
const fakeNode = node.children?.find((e) => e.data?.id === 'fakeNode'); //
if (fakeNode) {
window.minder.removeNode(fakeNode);
}
}
setPriorityView(true, 'P');
}
@ -483,20 +486,16 @@
node.data.isNew = true;
window.minder.execCommand('priority');
if (index === nodes.length - 1) {
nextTick(() => {
handleNodeSelect(node);
});
window.minder.toggleSelect(node);
}
} else if (node.data?.resource?.includes(caseTag)) {
} else if (tag === caseTag && node.data) {
//
tempMinderParams.value.updateModuleList = tempMinderParams.value.updateModuleList.filter(
(e) => e.id !== node.data?.id
);
node.data.isNew = true;
if (index === nodes.length - 1) {
nextTick(() => {
handleNodeSelect(node);
});
window.minder.toggleSelect(node);
}
} else if (node.data?.resource?.some((e) => caseOffspringTags.includes(e))) {
//
@ -522,7 +521,13 @@
case MinderEventName.CUT_NODE:
// TODO:
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({
id: node.data?.id || '',
@ -619,7 +624,7 @@
versionId: '',
updateCaseList: [],
updateModuleList: [],
deleteResourceList: [],
deleteResourceList: tempMinderParams.value.deleteResourceList, //
additionalNodeList: [],
};
}
@ -702,7 +707,7 @@
watch(
() => props.moduleId,
() => {
initCaseTree(true);
initCaseTree();
},
{
deep: true,

View File

@ -615,7 +615,10 @@ export default function useMinderBaseApi({ hasEditPermission }: { hasEditPermiss
}
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;
}

View File

@ -23,7 +23,10 @@
@save="handleMinderSave"
>
<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">
<MsIcon type="icon-icon_add_outlined" class="text-[var(--color-text-4)]" />
</MsButton>
@ -86,7 +89,11 @@
</a-tooltip>
</div>
<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>
<div class="flex items-center">
<div>{{ t('testPlan.planForm.pickCases') }}</div>
@ -116,7 +123,6 @@
</div>
<a-divider margin="8px" direction="vertical" />
<MsButton
v-permission="['CASE_REVIEW:READ+RELEVANCE']"
type="text"
class="font-medium"
:disabled="!hasEditPermission"
@ -127,6 +133,15 @@
</div>
</div>
</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">
<a-form-item :label="t('system.project.resourcePool')">
<a-select
@ -211,15 +226,6 @@
</a-form-item>
</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>
<div v-if="hasEditPermission" class="flex items-center gap-[12px] bg-white pb-[16px]">
<a-button
@ -538,30 +544,6 @@
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: '',
projectId: '',
};
const node: PlanMinderNode = window.minder.getNodeById(activePlanSet.value?.data.id);
if (node?.data) {
node.data.associateDTOS = [];
}
}
function associateCase() {
@ -658,6 +644,7 @@
() => {
if (!switchingConfigFormData.value && configForm.value) {
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 拖动节点
@ -828,7 +840,7 @@
editList: [],
deletedIds: [],
};
selectedAssociateCasesParams.value.selectIds = [];
clearSelectedCases();
}
}

View File

@ -4,7 +4,7 @@ export default {
'ms.personal.baseInfo': 'Basic Information',
'ms.personal.setPsw': 'Password settings',
'ms.personal.setting': 'Personal settings',
'ms.personal.apiKey': 'APIKEY',
'ms.personal.apiKey': 'API KEY',
'ms.personal.tripartite': 'Tripartite account',
'ms.personal.changeAvatar': 'Change avatar',
'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.updatePswSuccess':
'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.expired': 'Expired',
'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.createTime': 'Created time',
'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.closeTip':
'After closing, the test tasks executed using the Access Key will fail. Please operate with caution!',

View File

@ -4,7 +4,7 @@ export default {
'ms.personal.baseInfo': '基本信息',
'ms.personal.setPsw': '密码设置',
'ms.personal.setting': '个人设置',
'ms.personal.apiKey': 'APIKEY',
'ms.personal.apiKey': 'API KEY',
'ms.personal.tripartite': '三方平台账号',
'ms.personal.changeAvatar': '更换头像',
'ms.personal.name': '姓名',
@ -26,7 +26,7 @@ export default {
'ms.personal.newPsw': '新密码',
'ms.personal.changePswTip': '修改密码后,需要使用新的密码登录系统',
'ms.personal.updatePswSuccess': '密码修改成功,将在 {count} 秒后自动退出,请使用新密码登录',
'ms.personal.apiKeyTip': '可通过 APIKEY 访问 MeterSphere API',
'ms.personal.apiKeyTip': '可通过 API KEY 访问 MeterSphere API',
'ms.personal.expireTime': '过期时间',
'ms.personal.expired': '已到期',
'ms.personal.expiredTip': '可在【设置】内更改到期时间',
@ -34,7 +34,7 @@ export default {
'ms.personal.setValidTime': '设置有效时间',
'ms.personal.createTime': '创建时间',
'ms.personal.copySuccess': '复制成功',
'ms.personal.maxTip': '最多可添加 5 个APIKEY',
'ms.personal.maxTip': '最多可添加 5 个API KEY',
'ms.personal.confirmClose': '确认关闭吗?',
'ms.personal.closeTip': '关闭后,将导致使用该 Access Key 执行的测试任务执行失败,请谨慎操作!',
'ms.personal.closeSuccess': '关闭成功',

View File

@ -26,12 +26,8 @@
import MsSelect from '@/components/business/ms-select/index';
import { useUserStore } from '@/store';
import initOptionsFunc, { UserRequestTypeEnum } from './utils';
const userStore = useUserStore();
defineOptions({ name: 'MsUserSelector' });
export interface MsUserSelectorOption {
@ -99,7 +95,7 @@
};
const optionLabelRender = (option: SelectOptionData) => {
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>`;
};

View File

@ -12,7 +12,12 @@
</a-breadcrumb-item>
</a-breadcrumb>
</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>
<slot name="extractMenu"></slot>
</template>
@ -21,13 +26,15 @@
</template>
<script lang="ts" name="minderContainer" setup>
import { cloneDeep } from 'lodash-es';
import nodeFloatMenu from '../menu/nodeFloatMenu.vue';
import minderHeader from './header.vue';
import Navigator from './navigator.vue';
import useLeaveUnSaveTip from '@/hooks/useLeaveUnSaveTip';
import useMinderStore from '@/store/modules/components/minder-editor';
import { findNodePathByKey, replaceNodeInTree } from '@/utils';
import { findNodePathByKey, mapTree, replaceNodeInTree } from '@/utils';
import { MinderEventName } from '@/enums/minderEnum';
@ -139,9 +146,6 @@
});
});
const menuVisible = ref(false);
const menuPopupOffset = ref([0, 0]);
function getCurrentTreePath() {
if (innerImportJson.value.root.id === 'NONE' || innerImportJson.value.treePath?.length <= 1) {
return [];
@ -176,12 +180,15 @@
const root: MinderJsonNode = window.minder.getRoot();
window.minder.toggleSelect(root); //
window.minder.select(root); //
window.minder.execCommand('ExpandToLevel', 1);
currentTreePath.value = getCurrentTreePath();
setTimeout(() => {
window.minder.execCommand('camera', root);
}, 100); // TODO:
}
const floatMenuVisible = ref(false);
function save() {
let data = importJson.value;
if (innerImportJson.value.treePath?.length > 1) {
@ -193,25 +200,33 @@
'id'
);
} else {
data = window.minder.exportJson();
const fullJson = window.minder.exportJson();
data = cloneDeep(fullJson);
importJson.value = fullJson;
}
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);
menuVisible.value = false;
floatMenuVisible.value = false;
});
}
watch(
() => 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) {
switchNode(minderStore.event.nodes[0]);
}

View File

@ -184,11 +184,19 @@
import { useI18n } from '@/hooks/useI18n';
import useMinderStore from '@/store/modules/components/minder-editor/index';
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 { 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';
const props = defineProps({
@ -208,7 +216,9 @@
const currentNodeTags = ref<string[]>([]);
const tags = ref<string[]>([]);
const menuVisible = ref(false);
const menuVisible = defineModel<boolean>('visible', {
default: false,
});
const menuPopupOffset = ref<TriggerPopupTranslate>([0, 0]);
watch(
@ -310,6 +320,9 @@
}
window.minder.execCommand('resource', origin);
minderStore.dispatchEvent(MinderEventName.SET_TAG, undefined, undefined, undefined, selectedNodes);
if (props.afterTagEdit) {
props.afterTagEdit(selectedNodes, value);
}
}
}
@ -362,21 +375,15 @@
case 'paste':
minderStore.dispatchEvent(MinderEventName.PASTE_NODE, undefined, undefined, undefined, selectedNodes);
window.minder.execCommand('Paste');
const pastedNode: MinderJsonNode = window.minder.getSelectedNode();
if (pastedNode) {
pastedNode.data = {
...pastedNode.data,
text: pastedNode.data?.text || '',
isNew: true,
id: getGenerateId(),
};
traverseTree(pastedNode.children || [], (node) => {
node.data = {
...node.data,
text: node.data?.text || '',
let pastedNodes: MinderJsonNode[] = window.minder.getSelectedNodes();
if (pastedNodes.length > 0) {
pastedNodes = pastedNodes.map((e) => {
e.data = {
...(e.data as MinderJsonNodeData),
isNew: true,
id: getGenerateId(),
};
return e;
});
}
break;

View File

@ -48,6 +48,7 @@
tagProps,
viewMenuProps,
} from './props';
import { isNodeInMinderView } from './script/tool/utils';
const emit = defineEmits<{
(e: 'moldChange', data: number): void;
@ -97,9 +98,13 @@
(val) => {
const node: MinderJsonNode = window.minder.getSelectedNode();
if (val && node) {
setTimeout(() => {
window.minder.execCommand('camera', node, 100);
}, 100);
const nodePosition = node?.getRenderBox();
//
if (nodePosition && !isNodeInMinderView(undefined, nodePosition, nodePosition.width / 2)) {
setTimeout(() => {
window.minder.execCommand('camera', node, 100);
}, 300); // 300ms
}
}
}
);

View File

@ -238,7 +238,7 @@ export default defineComponent({
const computedCurrent = computed(() => props.current ?? _current.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) => {
// 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(() => [
prefixCls,
`${prefixCls}-size-${mergedSize.value}`,

View File

@ -4,6 +4,7 @@
v-model:model-value="innerParams.bodyType"
type="button"
size="small"
:disabled="props.disabledBodyType"
@change="(val) => changeBodyFormat(val as RequestBodyFormat)"
>
<a-radio v-for="item of RequestBodyFormat" :key="item" :value="item">
@ -109,7 +110,6 @@
</template>
<script setup lang="ts">
import { useVModel } from '@vueuse/core';
import { TableColumnData } from '@arco-design/web-vue';
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
@ -132,7 +132,7 @@
import { defaultBodyParamsItem } from '@/views/api-test/components/config';
const props = defineProps<{
params: ExecuteBody;
disabledBodyType?: boolean; // body
disabledParamValue?: boolean; //
disabledExceptParam?: boolean; //
uploadTempFileApi?: (file: File) => Promise<any>; //
@ -148,15 +148,24 @@
const appStore = useAppStore();
const { t } = useI18n();
const innerParams = useVModel(props, 'params', emit);
const innerParams = defineModel<ExecuteBody>('params', {
required: true,
});
const batchAddKeyValVisible = ref(false);
const fileList = ref<MsFileItem[]>([]);
onBeforeMount(() => {
if (innerParams.value.binaryBody && innerParams.value.binaryBody.file) {
fileList.value = [innerParams.value.binaryBody.file as unknown as MsFileItem];
watch(
() => innerParams.value.binaryBody?.file,
() => {
if (innerParams.value.binaryBody?.file) {
fileList.value = [innerParams.value.binaryBody.file as unknown as MsFileItem];
}
},
{
immediate: true,
deep: true,
}
});
);
async function handleFileChange(files: MsFileItem[], file?: MsFileItem) {
try {

View File

@ -780,7 +780,7 @@
/**
* 设置插件表单数据
*/
function setPluginFormData() {
async function setPluginFormData() {
const tempForm = temporaryPluginFormMap[requestVModel.value.id];
if (tempForm || !requestVModel.value.isNew || requestVModel.value.isCopy) {
//
@ -809,6 +809,7 @@
isInitPluginForm.value = true;
});
}
await nextTick();
}
const pluginError = ref(false);
@ -823,7 +824,7 @@
pluginError.value = false;
isInitPluginForm.value = false;
if (pluginScriptMap.value[requestVModel.value.protocol] !== undefined) {
setPluginFormData();
await setPluginFormData();
//
return;
}
@ -831,7 +832,7 @@
pluginLoading.value = true;
const res = await getPluginScript(pluginId);
pluginScriptMap.value[requestVModel.value.protocol] = res;
setPluginFormData();
await setPluginFormData();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
@ -1191,7 +1192,13 @@
}
if (props.request.isExecute && !requestVModel.value.executeLoading) {
//
execute(isPriorityLocalExec.value ? 'localExec' : 'serverExec');
if (requestVModel.value.protocol !== 'HTTP') {
setTimeout(() => {
execute(isPriorityLocalExec.value ? 'localExec' : 'serverExec');
}, 100);
} else {
execute(isPriorityLocalExec.value ? 'localExec' : 'serverExec');
}
} else if (temporaryResponseMap[props.request.reportId]) {
//
requestVModel.value.response = temporaryResponseMap[props.request.reportId];

View File

@ -239,6 +239,7 @@
v-model:params="requestVModel.body"
:disabled-param-value="!isEditableApi && !isEditableParamValue"
:disabled-except-param="!isEditableApi"
:disabled-body-type="!isEditableApi"
:upload-temp-file-api="uploadTempFile"
:file-save-as-source-id="props.step?.id"
:file-save-as-api="stepTransferFile"

View File

@ -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.',
'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.',
'apiScenario.params.searchPlaceholder': 'Search by name or tag',
'apiScenario.params.searchPlaceholder': 'Search by name/tag',
'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.',
'apiScenario.params.name': 'Variable Name',

View File

@ -99,7 +99,7 @@
import useModal from '@/hooks/useModal';
import useAppStore from '@/store/modules/app';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
import { mapTree } from '@/utils';
import { mapTree, traverseTree } from '@/utils';
import { hasAnyPermission } from '@/utils/permission';
import type { CreateOrUpdateModule, UpdateModule } from '@/models/caseManagement/featureCase';
@ -386,12 +386,9 @@
watch(
() => props.modulesCount,
(obj) => {
caseTree.value = mapTree<ModuleTreeNode>(caseTree.value, (node) => {
return {
...node,
hideMoreAction: node.id === 'root' || props.isModal,
count: obj?.[node.id] || 0,
};
traverseTree(caseTree.value, (node) => {
node.count = obj?.[node.id] || 0;
node.hideMoreAction = node.id === 'root' || props.isModal;
});
}
);

View File

@ -9,7 +9,7 @@ export default {
'project.environmental.requestHeader': 'Request Header',
'project.environmental.allParams': 'All Parameters',
'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.globalVariable': 'Parameter',
'project.environmental.paramType': 'Type',

View File

@ -1,6 +1,6 @@
<template>
<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">
<a-button v-permission="['PROJECT_USER:READ+ADD']" class="mr-3" type="primary" @click="addMember">
{{ t('project.member.addMember') }}

View File

@ -1,7 +1,7 @@
export default {
'project.member.addMember': 'Add 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.edit': 'Edit',
'project.member.add': 'Add',

View File

@ -1,7 +1,7 @@
export default {
'organization.member.addMember': 'Add Member',
'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.edit': 'Edit',
'organization.member.batchActionAddProject': 'Add to project',

View File

@ -76,7 +76,7 @@ export default {
'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.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':
'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',

View File

@ -2,7 +2,7 @@ export default {
'system.user.createUser': 'Create User',
'system.user.emailInvite': 'Email Invite',
'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.resetPassword': 'Reset PSW',
'system.user.disable': 'Disable',