fix(接口场景): 场景步骤渲染 id 更改&部分 bug 修复

This commit is contained in:
baiqi 2024-04-03 10:43:39 +08:00 committed by Craftsman
parent 84491cbfa4
commit 2b796a9b27
14 changed files with 94 additions and 80 deletions

View File

@ -37,7 +37,13 @@
<div v-if="_props.hideMoreAction !== true" class="ms-tree-node-extra">
<slot name="extra" v-bind="_props"></slot>
<MsTableMoreAction
v-if="props.nodeMoreActions"
v-if="
props.nodeMoreActions &&
(typeof props.filterMoreActionFunc === 'function'
? props.filterMoreActionFunc(props.nodeMoreActions, _props)
: props.nodeMoreActions
).length > 0
"
:list="
typeof props.filterMoreActionFunc === 'function'
? props.filterMoreActionFunc(props.nodeMoreActions, _props)
@ -47,14 +53,6 @@
@select="handleNodeMoreSelect($event, _props)"
@close="moreActionsClose"
>
<!-- <MsButton-->
<!-- type="text"-->
<!-- :size="props.nodeMoreActionSize || 'mini'"-->
<!-- class="ms-tree-node-extra__more"-->
<!-- @click="focusNodeKey = _props[props.fieldNames.key]"-->
<!-- >-->
<!-- <MsIcon type="icon-icon_more_outlined" size="14" class="text-[var(&#45;&#45;color-text-4)]" />-->
<!-- </MsButton>-->
</MsTableMoreAction>
</div>
<div class="ms-tree-node-extra-end">
@ -78,8 +76,6 @@
import { nextTick, onBeforeMount, Ref, ref, watch } from 'vue';
import { cloneDeep, debounce } from 'lodash-es';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';

View File

@ -488,7 +488,6 @@ export function handleTreeDragDrop<T>(
return false;
}
const index = parentChildren.findIndex((node: TreeNode<T>) => node[customKey] === dragNode[customKey]);
console.log('index', parentChildren, dragNode, index);
if (index !== -1) {
parentChildren.splice(index, 1);

View File

@ -508,7 +508,8 @@
);
const currentLoop = ref(1);
const currentResponse = computed(() => {
if (props.step?.uniqueId) {
if (requestVModel.value.stepId === props.step?.uniqueId && props.step?.uniqueId) {
// id id
return props.stepResponses?.[props.step?.uniqueId]?.[currentLoop.value - 1];
}
});
@ -1059,7 +1060,7 @@
url: res.path,
name: res.name, // requestnamenull
resourceId: res.id,
stepId: props.step?.id || '',
stepId: props.step?.uniqueId || '',
responseActiveTab: ResponseComposition.BODY,
...parseRequestBodyResult,
};

View File

@ -936,7 +936,7 @@
url: res.path,
name: res.name, // requestnamenull
resourceId: res.id,
stepId: activeStep.value?.id || '',
stepId: activeStep.value?.uniqueId || '',
...parseRequestBodyResult,
};
nextTick(() => {

View File

@ -240,11 +240,13 @@
});
if (refType === ScenarioStepRefType.COPY) {
fullScenarioArr = mapTree<MsTableDataItem<ApiScenarioTableItem>>(fullScenarioArr, (node) => {
const id = getGenerateId();
return {
...node,
copyFromStepId: node.id,
originProjectId: node.projectId,
id: getGenerateId(),
id,
uniqueId: id,
};
});
emit(
@ -258,18 +260,24 @@
handleCancel();
} else {
fullScenarioArr = fullScenarioArr.map((e) => {
const id = getGenerateId();
return {
...e,
children: mapTree<MsTableDataItem<ApiScenarioTableItem>>(e.children || [], (node) => {
const childId = getGenerateId();
return {
...node,
originProjectId: node.projectId,
uniqueId: childId,
isQuoteScenarioStep: true,
isRefScenarioStep: true, //
draggable: false,
};
}),
id: getGenerateId(),
id,
uniqueId: id,
originProjectId: e.projectId,
draggable: false,
};
});
emit(

View File

@ -138,7 +138,6 @@
<a-select
v-if="hasAnyPermission(['PROJECT_API_SCENARIO:READ+UPDATE'])"
v-model:model-value="record.status"
v-permission="['PROJECT_API_SCENARIO:READ+UPDATE']"
class="param-input w-full"
size="mini"
@change="() => handleStatusChange(record)"

View File

@ -163,7 +163,7 @@
case ScenarioAddStepActionType.CUSTOM_API:
case ScenarioAddStepActionType.SCRIPT_OPERATION:
if (step.value) {
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, step.value.id, 'id');
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, step.value.uniqueId, 'uniqueId');
if (realStep) {
emit('otherCreate', val, realStep as ScenarioStepItem);
}

View File

@ -6,7 +6,7 @@
position="br"
@popup-visible-change="handleActionTriggerChange"
>
<MsButton :id="step.id" type="icon" class="ms-tree-node-extra__btn !mr-[4px]" @click="emit('click')">
<MsButton :id="step.uniqueId" type="icon" class="ms-tree-node-extra__btn !mr-[4px]" @click="emit('click')">
<MsIcon type="icon-icon_add_outlined" size="14" class="text-[var(--color-text-4)]" />
</MsButton>
<template #content>
@ -135,7 +135,7 @@
function handleActionsClose() {
activeCreateAction.value = undefined;
innerStep.value.createActionsVisible = false;
document.getElementById(innerStep.value.id.toString())?.click();
document.getElementById(innerStep.value.uniqueId.toString())?.click();
}
</script>

View File

@ -19,7 +19,7 @@ export default function useCreateActions() {
/**
*
* @param selectedKeys id
* @param selectedKeys uniqueId
* @param steps
* @param parent
*/
@ -28,9 +28,9 @@ export default function useCreateActions() {
steps: (ScenarioStepItem | TreeNode<ScenarioStepItem>)[],
parent?: TreeNode<ScenarioStepItem>
) {
if (parent && selectedKeys.includes(parent.id)) {
if (parent && selectedKeys.includes(parent.uniqueId)) {
// 添加子节点时,当前节点已选中,则需要把新节点也需要选中(因为父级选中子级也会展示选中状态)
selectedKeys.push(...steps.map((item) => item.id));
selectedKeys.push(...steps.map((item) => item.uniqueId));
}
}
@ -40,7 +40,7 @@ export default function useCreateActions() {
* @param step
* @param steps
* @param createStepAction
* @param selectedKeys id
* @param selectedKeys uniqueId
*/
function handleCreateStep(
defaultStepInfo: Record<string, any>,
@ -49,18 +49,20 @@ export default function useCreateActions() {
createStepAction: CreateStepAction,
selectedKeys: (string | number)[]
) {
const id = getGenerateId();
const newStep = {
...cloneDeep(defaultStepItemCommon),
id: getGenerateId(),
id,
uniqueId: id,
...defaultStepInfo,
};
insertNodes<ScenarioStepItem>(
step.parent?.children || steps,
step.id,
step.uniqueId,
newStep,
createStepAction,
(newNode, parent) => selectedIfNeed(selectedKeys, [newNode], parent),
'id'
'uniqueId'
);
}
@ -113,12 +115,12 @@ export default function useCreateActions() {
} else if (stepType === ScenarioStepType.API_SCENARIO) {
config = cloneDeep(defaultScenarioStepConfig);
}
if (item.id || item.resourceId) {
if (item.resourceId || item.id) {
// 引用复制接口、用例、场景时的源资源信息
resourceField = {
resourceId: item.id || item.resourceId, // 场景会调接口获取信息所以有resourceId接口、用例没有下同
resourceNum: item.num || item.resourceNum,
resourceName: item.name || item.resourceName,
resourceId: item.resourceId || item.id, // 场景会调接口获取信息所以有resourceId接口、用例没有下同
resourceNum: item.resourceNum || item.num,
resourceName: item.resourceName || item.name,
};
}
if (item.protocol) {
@ -170,11 +172,11 @@ export default function useCreateActions() {
) {
insertNodes<ScenarioStepItem>(
step.parent?.children || steps,
step.id,
step.uniqueId,
readyInsertSteps,
createStepAction,
undefined,
'id'
'uniqueId'
);
selectedIfNeed(selectedKeys, readyInsertSteps, step);
}

View File

@ -60,7 +60,7 @@
<div class="text-[var(--color-text-1)]">{{ t('common.fail') }}</div>
<div class="text-[rgb(var(--success-6))]">{{ scenario.executeFailCount }}</div>
</div>
<MsButton v-if="scenario.isDebug === false" type="text" @click="checkReport">
<MsButton v-if="scenario.isDebug === false && !scenario.executeLoading" type="text" @click="checkReport">
{{ t('apiScenario.checkReport') }}
</MsButton>
</div>
@ -234,14 +234,14 @@
const ids = new Set(checkedKeys.value);
if (batchToggleRange.value === 'top') {
scenario.value.steps = scenario.value.steps.map((item) => {
if (ids.has(item.id)) {
if (ids.has(item.uniqueId)) {
item.enable = isBatchEnable.value;
}
return item;
});
} else {
scenario.value.steps = mapTree(scenario.value.steps, (node) => {
if (ids.has(node.id) && node.isRefScenarioStep !== true) {
if (ids.has(node.uniqueId) && node.isRefScenarioStep !== true) {
//
node.enable = isBatchEnable.value;
}
@ -257,7 +257,7 @@
}
function batchDelete() {
deleteNodes(scenario.value.steps, checkedKeys.value, 'id');
deleteNodes(scenario.value.steps, checkedKeys.value, 'uniqueId');
Message.success(t('common.deleteSuccess'));
if (scenario.value.steps.length === 0) {
checkedAll.value = false;
@ -321,9 +321,9 @@
child.isQuoteScenarioStep = child.parent.isQuoteScenarioStep; //
child.isRefScenarioStep = child.parent.isRefScenarioStep; //
}
if (selectedKeys.value.includes(node.id) && !selectedKeys.value.includes(child.id)) {
if (selectedKeys.value.includes(node.uniqueId) && !selectedKeys.value.includes(child.uniqueId)) {
//
selectedKeys.value.push(child.id);
selectedKeys.value.push(child.uniqueId);
}
return child;
}) as ScenarioStepItem[];
@ -344,10 +344,10 @@
scenario.value.executeLoading = true;
const checkedKeysSet = new Set(checkedKeys.value);
const waitTingDebugSteps = filterTree(scenario.value.steps, (node) => {
if (checkedKeysSet.has(node.id)) {
if (checkedKeysSet.has(node.uniqueId)) {
if (!node.enable) {
// id便waitingDebugStepDetails
checkedKeysSet.delete(node.id);
// uniqueId便waitingDebugStepDetails
checkedKeysSet.delete(node.uniqueId);
node.executeStatus = undefined;
} else {
node.executeStatus = ScenarioExecuteStatus.EXECUTING;

View File

@ -25,7 +25,7 @@
</a-select>
<a-tooltip :content="innerData.value" :disabled="!innerData.value">
<a-input
:id="innerData.id"
:id="props.stepId"
v-model:model-value="innerData.value"
size="mini"
class="w-[110px] px-[8px]"

View File

@ -254,7 +254,7 @@
() => dbClick?.value.timeStamp,
() => {
// @ts-ignore
if ((dbClick?.value.e?.target as Element).parentNode?.id === props.stepId) {
if ((dbClick?.value.e?.target as Element).parentNode?.uniqueId === props.stepId) {
emit(
'quickInput',
innerData.value.whileController.conditionType === WhileConditionType.CONDITION

View File

@ -11,7 +11,7 @@
:expand-all="props.expandAll"
:node-more-actions="stepMoreActions"
:filter-more-action-func="setStepMoreAction"
:field-names="{ title: 'name', key: 'id', children: 'children' }"
:field-names="{ title: 'name', key: 'uniqueId', children: 'children' }"
:virtual-list-props="{
height: '100%',
threshold: 20,
@ -89,7 +89,7 @@
<component
:is="getStepContent(step)"
:data="checkStepIsApi(step) || step.stepType === ScenarioStepType.API_SCENARIO ? step : step.config"
:step-id="step.id"
:step-id="step.uniqueId"
:disabled="!!step.isQuoteScenarioStep"
@quick-input="setQuickInput(step, $event)"
@change="handleStepContentChange($event, step)"
@ -99,7 +99,7 @@
<template v-if="checkStepIsApi(step)">
<apiMethodName v-if="checkStepShowMethod(step)" :method="step.config.method" />
<div
v-if="step.id === showStepNameEditInputStepId"
v-if="step.uniqueId === showStepNameEditInputStepId"
class="name-warp absolute left-0 top-[-2px] z-10 w-[calc(100%-24px)]"
@click.stop
>
@ -129,7 +129,7 @@
<!-- 其他步骤描述 -->
<template v-else>
<div
v-if="step.id === showStepDescEditInputStepId"
v-if="step.uniqueId === showStepDescEditInputStepId"
class="desc-warp absolute left-0 top-[-2px] z-10 w-[calc(100%-24px)]"
>
<a-input
@ -171,10 +171,17 @@
</template>
<template #extra="step">
<stepInsertStepTrigger
v-if="
!step.isQuoteScenarioStep &&
!(
step.stepType === ScenarioStepType.API_SCENARIO &&
[ScenarioStepRefType.REF, ScenarioStepRefType.PARTIAL_REF].includes(step.refType)
)
"
v-model:selected-keys="selectedKeys"
v-model:steps="steps"
:step="step"
@click="setFocusNodeKey(step.id)"
@click="setFocusNodeKey(step.uniqueId)"
@other-create="handleOtherCreate"
@close="setFocusNodeKey('')"
@add-done="handleAddStepDone"
@ -525,7 +532,7 @@
function handleResponsePopoverVisibleChange(visible: boolean, step: ScenarioStepItem) {
if (visible) {
setFocusNodeKey(step.id);
setFocusNodeKey(step.uniqueId);
} else {
setFocusNodeKey('');
}
@ -565,9 +572,9 @@
* 增加步骤时判断父节点是否选中如果选中则需要把新节点也选中
*/
function selectedIfNeed(step: TreeNode<ScenarioStepItem>, parent?: TreeNode<ScenarioStepItem>) {
if (parent && selectedKeys.value.includes(parent.id)) {
if (parent && selectedKeys.value.includes(parent.uniqueId)) {
//
selectedKeys.value.push(step.id);
selectedKeys.value.push(step.uniqueId);
}
}
@ -637,6 +644,9 @@
},
];
}
if ((node as ScenarioStepItem).isQuoteScenarioStep) {
return [];
}
return stepMoreActions;
}
@ -732,7 +742,7 @@
//
function saveScenarioConfig() {
if (activeStep.value) {
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, activeStep.value.id, 'id');
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, activeStep.value.uniqueId, 'uniqueId');
if (realStep) {
realStep.refType = scenarioConfigForm.value.refType; //
realStep.config = {
@ -773,7 +783,7 @@
}
insertNodes<ScenarioStepItem>(
steps.value,
node.id,
node.uniqueId,
{
...cloneDeep(
mapTree<ScenarioStepItem>(node, (childNode) => {
@ -786,7 +796,7 @@
stepDetails.value[childId] = cloneDeep(childStepDetail);
}
if (childStepFileParam) {
//
//
scenario.value.stepFileParam[id] = cloneDeep(childStepFileParam);
}
if (!isQuoteScenario) {
@ -818,7 +828,7 @@
},
'after',
selectedIfNeed,
'id'
'uniqueId'
);
scenario.value.unSaved = true;
break;
@ -831,7 +841,7 @@
showScenarioConfig.value = true;
break;
case 'delete':
deleteNode(steps.value, node.id, 'id');
deleteNode(steps.value, node.uniqueId, 'uniqueId');
scenario.value.unSaved = true;
break;
default:
@ -850,7 +860,7 @@
const tempStepName = ref('');
function handleStepNameClick(step: ScenarioStepItem) {
tempStepName.value = step.name;
showStepNameEditInputStepId.value = step.id;
showStepNameEditInputStepId.value = step.uniqueId;
nextTick(() => {
//
const input = treeRef.value?.$el.querySelector('.name-warp .arco-input-wrapper .arco-input') as HTMLInputElement;
@ -859,7 +869,7 @@
}
function applyStepNameChange(step: ScenarioStepItem) {
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, step.id, 'id');
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, step.uniqueId, 'uniqueId');
if (realStep) {
realStep.name = tempStepName.value;
}
@ -874,7 +884,7 @@
const tempStepDesc = ref('');
function handleStepDescClick(step: ScenarioStepItem) {
tempStepDesc.value = step.name;
showStepDescEditInputStepId.value = step.id;
showStepDescEditInputStepId.value = step.uniqueId;
nextTick(() => {
//
const input = treeRef.value?.$el.querySelector('.desc-warp .arco-input-wrapper .arco-input') as HTMLInputElement;
@ -883,7 +893,7 @@
}
function applyStepDescChange(step: ScenarioStepItem) {
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, step.id, 'id');
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, step.uniqueId, 'uniqueId');
if (realStep) {
realStep.name = tempStepDesc.value;
}
@ -892,7 +902,7 @@
}
function handleStepContentChange($event, step: ScenarioStepItem) {
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, step.id, 'id');
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, step.uniqueId, 'uniqueId');
if (realStep) {
Object.keys($event).forEach((key) => {
realStep.config[key] = $event[key];
@ -905,14 +915,14 @@
* 处理步骤展开折叠
*/
function handleStepExpand(data: MsTreeExpandedData) {
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, data.node?.id, 'id');
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, data.node?.uniqueId, 'uniqueId');
if (realStep) {
realStep.expanded = !realStep.expanded;
}
}
function handleStepToggleEnable(data: ScenarioStepItem) {
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, data.id, 'id');
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, data.uniqueId, 'uniqueId');
if (realStep) {
realStep.enable = !realStep.enable;
scenario.value.unSaved = true;
@ -973,10 +983,10 @@
const _stepType = getStepType(step);
const offspringIds: string[] = [];
mapTree(step.children || [], (e) => {
offspringIds.push(e.id);
offspringIds.push(e.uniqueId);
return e;
});
selectedKeys.value = [step.id, ...offspringIds];
selectedKeys.value = [step.uniqueId, ...offspringIds];
if (_stepType.isCopyApi || _stepType.isQuoteApi || step.stepType === ScenarioStepType.CUSTOM_REQUEST) {
// api api api
activeStep.value = step;
@ -1140,7 +1150,7 @@
* @param executeType 执行类型
*/
function handleApiExecute(request: RequestParam, executeType?: 'localExec' | 'serverExec') {
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, request.stepId, 'id');
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, request.stepId, 'uniqueId');
if (realStep) {
delete scenario.value.stepResponses[realStep.uniqueId]; //
realStep.reportId = getGenerateId();
@ -1373,7 +1383,7 @@
function deleteCaseStep(step?: ScenarioStepItem) {
if (step) {
customCaseDrawerVisible.value = false;
deleteNode(steps.value, step.id, 'id');
deleteNode(steps.value, step.uniqueId, 'uniqueId');
activeStep.value = undefined;
scenario.value.unSaved = true;
}
@ -1389,6 +1399,7 @@
handleCreateStep(
{
id,
uniqueId: id,
refType: ScenarioStepRefType.DIRECT,
stepType: ScenarioStepType.SCRIPT,
name,
@ -1461,20 +1472,20 @@
loading.value = true;
const offspringIds: string[] = [];
mapTree(dragNode.children || [], (e) => {
offspringIds.push(e.id);
offspringIds.push(e.uniqueId);
return e;
});
const stepIdAndOffspringIds = [dragNode.id, ...offspringIds];
const stepIdAndOffspringIds = [dragNode.uniqueId, ...offspringIds];
if (dropPosition === 0) {
//
if (selectedKeys.value.includes(dropNode.id)) {
if (selectedKeys.value.includes(dropNode.uniqueId)) {
//
selectedKeys.value = selectedKeys.value.concat(stepIdAndOffspringIds);
}
} else if (dropNode.parent && selectedKeys.value.includes(dropNode.parent.id)) {
} else if (dropNode.parent && selectedKeys.value.includes(dropNode.parent.uniqueId)) {
//
selectedKeys.value = selectedKeys.value.concat(stepIdAndOffspringIds);
} else if (dragNode.parent && selectedKeys.value.includes(dragNode.parent.id)) {
} else if (dragNode.parent && selectedKeys.value.includes(dragNode.parent.uniqueId)) {
//
selectedKeys.value = selectedKeys.value.filter((e) => {
for (let i = 0; i < stepIdAndOffspringIds.length; i++) {
@ -1487,8 +1498,7 @@
return true;
});
}
console.log(dragNode, dropNode);
const dragResult = handleTreeDragDrop(steps.value, dragNode, dropNode, dropPosition, 'id');
const dragResult = handleTreeDragDrop(steps.value, dragNode, dropNode, dropPosition, 'uniqueId');
if (dragResult) {
Message.success(t('common.moveSuccess'));
scenario.value.unSaved = true;
@ -1508,7 +1518,7 @@
const quickInputDataKey = ref('');
function setQuickInput(step: ScenarioStepItem, dataKey: string) {
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, step.id, 'id');
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, step.uniqueId, 'uniqueId');
if (realStep) {
activeStep.value = realStep as ScenarioStepItem;
}

View File

@ -318,6 +318,7 @@
if (isCopy) {
// copyFromStepId
copySteps = mapTree(defaultScenarioInfo.steps, (node) => {
node.copyFromStepId = node.id;
if (
node.parent &&
node.parent.stepType === ScenarioStepType.API_SCENARIO &&
@ -337,8 +338,7 @@
//
node.id = getGenerateId(); // ID
}
node.copyFromStepId = node.id;
node.uniqueId = getGenerateId();
node.uniqueId = node.id;
return node;
});
} else {
@ -366,7 +366,6 @@
...defaultScenarioInfo,
steps: copySteps,
id: isCopy ? getGenerateId() : defaultScenarioInfo.id || '',
uniqueId: getGenerateId(),
label: isCopy ? `copy-${defaultScenarioInfo.name}` : defaultScenarioInfo.name,
name: isCopy ? `copy-${defaultScenarioInfo.name}` : defaultScenarioInfo.name,
isNew: isCopy,