feat(接口场景): 场景步骤查看步骤详情
This commit is contained in:
parent
d608df0e93
commit
adb640ec13
|
@ -83,6 +83,6 @@ export const apiSocket = (url: string, host?: string) => {
|
||||||
return new WebSocket(uri);
|
return new WebSocket(uri);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getSocket(reportId: string, socketUrl?: string, host?: string) {
|
export function getSocket(reportId: string | number, socketUrl?: string, host?: string) {
|
||||||
return apiSocket(`${socketUrl || ConnectionWebsocketUrl}/${reportId}`, host);
|
return apiSocket(`${socketUrl || ConnectionWebsocketUrl}/${reportId}`, host);
|
||||||
}
|
}
|
||||||
|
|
|
@ -263,7 +263,7 @@
|
||||||
border: 1px solid var(--color-text-input-border);
|
border: 1px solid var(--color-text-input-border);
|
||||||
background-color: var(--color-text-fff);
|
background-color: var(--color-text-fff);
|
||||||
&:not(:disabled):hover {
|
&:not(:disabled):hover {
|
||||||
border-color: rgb(var(--primary-5));
|
border-color: rgb(var(--primary-5)) !important;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
input::placeholder {
|
input::placeholder {
|
||||||
|
@ -363,8 +363,20 @@
|
||||||
background: none;
|
background: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
.arco-form-item-status-success .arco-input-wrapper:not(.arco-input-disabled) {
|
.arco-form-item-status-success {
|
||||||
border-color: var(--color-text-input-border);
|
&:hover {
|
||||||
|
.arco-input-wrapper:not(.arco-input-disabled),
|
||||||
|
.arco-select-view:not(.arco-select-view-disabled),
|
||||||
|
.arco-textarea-wrapper:not(.arco-textarea-disabled) {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.arco-input-wrapper:not(.arco-input-disabled),
|
||||||
|
.arco-select-view:not(.arco-select-view-disabled),
|
||||||
|
.arco-textarea-wrapper:not(.arco-textarea-disabled) {
|
||||||
|
border-color: var(--color-text-input-border);
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.arco-form-item-message {
|
.arco-form-item-message {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -331,6 +331,7 @@ export interface ScenarioStepItem {
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
versionId?: string;
|
versionId?: string;
|
||||||
children?: ScenarioStepItem[];
|
children?: ScenarioStepItem[];
|
||||||
|
isNew: boolean; // 是否新建的步骤,引用复制类型以此区分调用步骤详情还是资源详情
|
||||||
// 页面渲染以及交互需要字段
|
// 页面渲染以及交互需要字段
|
||||||
checked?: boolean; // 是否选中
|
checked?: boolean; // 是否选中
|
||||||
expanded?: boolean; // 是否展开
|
expanded?: boolean; // 是否展开
|
||||||
|
|
|
@ -283,7 +283,6 @@
|
||||||
import { cloneDeep, debounce } from 'lodash-es';
|
import { cloneDeep, debounce } from 'lodash-es';
|
||||||
|
|
||||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||||
import { TabItem } from '@/components/pure/ms-editable-tab/types';
|
|
||||||
import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue';
|
import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue';
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
||||||
|
@ -322,7 +321,6 @@
|
||||||
RequestConditionProcessor,
|
RequestConditionProcessor,
|
||||||
RequestMethods,
|
RequestMethods,
|
||||||
ResponseComposition,
|
ResponseComposition,
|
||||||
ScenarioStepRefType,
|
|
||||||
ScenarioStepType,
|
ScenarioStepType,
|
||||||
} from '@/enums/apiEnum';
|
} from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
@ -349,20 +347,25 @@
|
||||||
|
|
||||||
export interface RequestCustomAttr {
|
export interface RequestCustomAttr {
|
||||||
type: 'api';
|
type: 'api';
|
||||||
|
name: string;
|
||||||
|
stepId: string | number; // 所属步骤 id
|
||||||
|
resourceId: string | number; // 引用、复制的资源 id
|
||||||
isNew: boolean;
|
isNew: boolean;
|
||||||
protocol: string;
|
protocol: string;
|
||||||
activeTab: RequestComposition;
|
activeTab: RequestComposition;
|
||||||
executeLoading: boolean; // 执行中loading
|
executeLoading: boolean; // 执行中loading
|
||||||
isCopy?: boolean; // 是否是复制
|
isCopy?: boolean; // 是否是复制
|
||||||
isExecute?: boolean; // 是否是执行
|
isExecute?: boolean; // 是否是执行
|
||||||
|
responseActiveTab: ResponseComposition;
|
||||||
|
unSaved: boolean;
|
||||||
|
uploadFileIds: string[];
|
||||||
|
linkFileIds: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RequestParam = ExecuteApiRequestFullParams & {
|
export type RequestParam = ExecuteApiRequestFullParams & {
|
||||||
response?: RequestTaskResult;
|
response?: RequestTaskResult;
|
||||||
customizeRequestEnvEnable: boolean;
|
customizeRequestEnvEnable: boolean;
|
||||||
request?: ExecuteApiRequestFullParams; // 请求参数集合
|
} & RequestCustomAttr;
|
||||||
} & RequestCustomAttr &
|
|
||||||
TabItem;
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
request?: RequestParam; // 请求参数集合
|
request?: RequestParam; // 请求参数集合
|
||||||
|
@ -398,8 +401,10 @@
|
||||||
const loading = defineModel<boolean>('detailLoading', { default: false });
|
const loading = defineModel<boolean>('detailLoading', { default: false });
|
||||||
|
|
||||||
const defaultDebugParams: RequestParam = {
|
const defaultDebugParams: RequestParam = {
|
||||||
|
name: '',
|
||||||
type: 'api',
|
type: 'api',
|
||||||
id: '',
|
stepId: '',
|
||||||
|
resourceId: '',
|
||||||
customizeRequestEnvEnable: false,
|
customizeRequestEnvEnable: false,
|
||||||
protocol: 'HTTP',
|
protocol: 'HTTP',
|
||||||
url: '',
|
url: '',
|
||||||
|
@ -607,7 +612,8 @@
|
||||||
const currentPluginScript = computed<Record<string, any>[]>(
|
const currentPluginScript = computed<Record<string, any>[]>(
|
||||||
() => pluginScriptMap.value[requestVModel.value.protocol]?.script || []
|
() => pluginScriptMap.value[requestVModel.value.protocol]?.script || []
|
||||||
);
|
);
|
||||||
const isCopyApiNeedInit = computed(() => _stepType.value.isCopyApi && props.request?.request === null);
|
// 复制 api 只要加载过一次后就会保存,所以 props.request 是不为空的
|
||||||
|
const isCopyApiNeedInit = computed(() => _stepType.value.isCopyApi && props.request === undefined);
|
||||||
const isEditableApi = computed(
|
const isEditableApi = computed(
|
||||||
() => _stepType.value.isCopyApi || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST || !props.step
|
() => _stepType.value.isCopyApi || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST || !props.step
|
||||||
);
|
);
|
||||||
|
@ -616,7 +622,7 @@
|
||||||
const handlePluginFormChange = debounce(() => {
|
const handlePluginFormChange = debounce(() => {
|
||||||
if (isEditableApi.value) {
|
if (isEditableApi.value) {
|
||||||
// 复制或者新建的时候需要缓存表单数据,引用的不能更改
|
// 复制或者新建的时候需要缓存表单数据,引用的不能更改
|
||||||
temporaryPluginFormMap[requestVModel.value.id] = fApi.value?.formData();
|
temporaryPluginFormMap[requestVModel.value.stepId] = fApi.value?.formData();
|
||||||
}
|
}
|
||||||
handleActiveDebugChange();
|
handleActiveDebugChange();
|
||||||
}, 300);
|
}, 300);
|
||||||
|
@ -636,7 +642,6 @@
|
||||||
if (currentFormFields && currentFormFields.length < fields.length) {
|
if (currentFormFields && currentFormFields.length < fields.length) {
|
||||||
fApi.value?.hidden(false, fields);
|
fApi.value?.hidden(false, fields);
|
||||||
fApi.value?.hidden(true, currentFormFields?.filter((e) => !fields.includes(e)) || []);
|
fApi.value?.hidden(true, currentFormFields?.filter((e) => !fields.includes(e)) || []);
|
||||||
fApi.value?.refresh();
|
|
||||||
} else {
|
} else {
|
||||||
// 隐藏多余的字段
|
// 隐藏多余的字段
|
||||||
fApi.value?.hidden(true, currentFormFields?.filter((e) => !fields.includes(e)) || []);
|
fApi.value?.hidden(true, currentFormFields?.filter((e) => !fields.includes(e)) || []);
|
||||||
|
@ -648,27 +653,30 @@
|
||||||
* 设置插件表单数据
|
* 设置插件表单数据
|
||||||
*/
|
*/
|
||||||
function setPluginFormData() {
|
function setPluginFormData() {
|
||||||
const tempForm = temporaryPluginFormMap[requestVModel.value.id];
|
const tempForm = temporaryPluginFormMap[requestVModel.value.stepId];
|
||||||
|
console.log('setPluginFormData', temporaryPluginFormMap, requestVModel.value.stepId);
|
||||||
if (tempForm || !requestVModel.value.isNew) {
|
if (tempForm || !requestVModel.value.isNew) {
|
||||||
// 如果缓存的表单数据存在或者是编辑状态,则需要将之前的输入数据填充
|
// 如果缓存的表单数据存在或者是编辑状态,则需要将之前的输入数据填充
|
||||||
const formData = isEditableApi.value ? tempForm || requestVModel.value : requestVModel.value;
|
const formData = isEditableApi.value ? tempForm || requestVModel.value : requestVModel.value;
|
||||||
if (fApi.value) {
|
nextTick(() => {
|
||||||
fApi.value.nextRefresh(() => {
|
if (fApi.value) {
|
||||||
const form = {};
|
fApi.value.nextRefresh(() => {
|
||||||
controlPluginFormFields().forEach((key) => {
|
const form = {};
|
||||||
form[key] = formData[key];
|
controlPluginFormFields().forEach((key) => {
|
||||||
|
form[key] = formData[key];
|
||||||
|
});
|
||||||
|
fApi.value?.setValue(cloneDeep(form));
|
||||||
|
setTimeout(() => {
|
||||||
|
// 初始化时赋值会触发表单数据变更,300ms 是为了与 handlePluginFormChange的防抖时间保持一致
|
||||||
|
isInitPluginForm.value = true;
|
||||||
|
}, 300);
|
||||||
});
|
});
|
||||||
fApi.value?.setValue(cloneDeep(form));
|
}
|
||||||
fApi.value?.clearValidateState();
|
});
|
||||||
setTimeout(() => {
|
|
||||||
// 初始化时赋值会触发表单数据变更,300ms 是为了与 handlePluginFormChange的防抖时间保持一致
|
|
||||||
isInitPluginForm.value = true;
|
|
||||||
}, 300);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
fApi.value?.nextTick(() => {
|
nextTick(() => {
|
||||||
controlPluginFormFields();
|
controlPluginFormFields();
|
||||||
|
fApi.value?.clearValidateState();
|
||||||
fApi.value?.resetFields();
|
fApi.value?.resetFields();
|
||||||
isInitPluginForm.value = true;
|
isInitPluginForm.value = true;
|
||||||
});
|
});
|
||||||
|
@ -831,7 +839,6 @@
|
||||||
return conditionCopy;
|
return conditionCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
const reportId = ref('');
|
|
||||||
const websocket = ref<WebSocket>();
|
const websocket = ref<WebSocket>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -839,14 +846,14 @@
|
||||||
*/
|
*/
|
||||||
function debugSocket(executeType?: 'localExec' | 'serverExec') {
|
function debugSocket(executeType?: 'localExec' | 'serverExec') {
|
||||||
websocket.value = getSocket(
|
websocket.value = getSocket(
|
||||||
reportId.value,
|
requestVModel.value.stepId,
|
||||||
executeType === 'localExec' ? '/ws/debug' : '',
|
executeType === 'localExec' ? '/ws/debug' : '',
|
||||||
executeType === 'localExec' ? localExecuteUrl.value : ''
|
executeType === 'localExec' ? localExecuteUrl.value : ''
|
||||||
);
|
);
|
||||||
websocket.value.addEventListener('message', (event) => {
|
websocket.value.addEventListener('message', (event) => {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
if (data.msgType === 'EXEC_RESULT') {
|
if (data.msgType === 'EXEC_RESULT') {
|
||||||
if (requestVModel.value.reportId === data.reportId) {
|
if (requestVModel.value.stepId === data.reportId) {
|
||||||
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
||||||
requestVModel.value.response = data.taskResult;
|
requestVModel.value.response = data.taskResult;
|
||||||
requestVModel.value.executeLoading = false;
|
requestVModel.value.executeLoading = false;
|
||||||
|
@ -922,7 +929,8 @@
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...requestParams,
|
...requestParams,
|
||||||
id: requestVModel.value.id,
|
resourceId: requestVModel.value.resourceId,
|
||||||
|
stepId: requestVModel.value.stepId,
|
||||||
activeTab: requestVModel.value.protocol === 'HTTP' ? RequestComposition.HEADER : RequestComposition.PLUGIN,
|
activeTab: requestVModel.value.protocol === 'HTTP' ? RequestComposition.HEADER : RequestComposition.PLUGIN,
|
||||||
responseActiveTab: ResponseComposition.BODY,
|
responseActiveTab: ResponseComposition.BODY,
|
||||||
protocol: requestVModel.value.protocol,
|
protocol: requestVModel.value.protocol,
|
||||||
|
@ -1016,7 +1024,7 @@
|
||||||
async function initQuoteApiDetail() {
|
async function initQuoteApiDetail() {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const res = await getDefinitionDetail(requestVModel.value.id);
|
const res = await getDefinitionDetail(props.step?.resourceId || '');
|
||||||
let parseRequestBodyResult;
|
let parseRequestBodyResult;
|
||||||
if (res.protocol === 'HTTP') {
|
if (res.protocol === 'HTTP') {
|
||||||
parseRequestBodyResult = parseRequestBodyFiles(res.request.body); // 解析请求体中的文件,将详情中的文件 id 集合收集,更新时以判断文件是否删除以及是否新上传的文件
|
parseRequestBodyResult = parseRequestBodyFiles(res.request.body); // 解析请求体中的文件,将详情中的文件 id 集合收集,更新时以判断文件是否删除以及是否新上传的文件
|
||||||
|
@ -1032,9 +1040,10 @@
|
||||||
response: cloneDeep(defaultResponse),
|
response: cloneDeep(defaultResponse),
|
||||||
url: res.path,
|
url: res.path,
|
||||||
name: res.name, // request里面还有个name但是是null
|
name: res.name, // request里面还有个name但是是null
|
||||||
id: res.id,
|
resourceId: res.id,
|
||||||
...parseRequestBodyResult,
|
...parseRequestBodyResult,
|
||||||
};
|
};
|
||||||
|
console.log('initQuoteApiDetail', requestVModel.value);
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
// 等待内容渲染出来再隐藏loading
|
// 等待内容渲染出来再隐藏loading
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
@ -1054,53 +1063,26 @@
|
||||||
await initProtocolList();
|
await initProtocolList();
|
||||||
}
|
}
|
||||||
if (props.request) {
|
if (props.request) {
|
||||||
|
// 查看自定义请求、引用 api、复制 api
|
||||||
requestVModel.value = cloneDeep({
|
requestVModel.value = cloneDeep({
|
||||||
...defaultDebugParams,
|
...defaultDebugParams,
|
||||||
...props.request,
|
...props.request,
|
||||||
isNew: false,
|
isNew: false,
|
||||||
});
|
});
|
||||||
if (
|
if (_stepType.value.isQuoteApi) {
|
||||||
_stepType.value.isQuoteApi ||
|
// 引用接口时,每次都要获取源接口数据
|
||||||
isCopyApiNeedInit.value
|
|
||||||
// 引用接口时,需要初始化引用接口的详情;复制只在第一次初始化的时候需要加载后台数据(request.request是复制请求时列表参数字段request会为 null,以此判断释放第一次初始化)
|
|
||||||
) {
|
|
||||||
await initQuoteApiDetail();
|
await initQuoteApiDetail();
|
||||||
}
|
}
|
||||||
if (
|
handleActiveDebugProtocolChange(requestVModel.value.protocol);
|
||||||
props.step?.stepType === ScenarioStepType.API &&
|
} else if (_stepType.value.isQuoteApi || isCopyApiNeedInit.value) {
|
||||||
props.step?.refType === ScenarioStepRefType.REF &&
|
// 引用接口时,需要初始化引用接口的详情;复制只在第一次初始化的时候需要加载后台数据,复制 api 只要加载过一次后就会保存,所以 props.request 是不为空的
|
||||||
props.request.request &&
|
await initQuoteApiDetail();
|
||||||
requestVModel.value.request
|
|
||||||
) {
|
|
||||||
// 初始化引用的详情后,需要要把外面传入的数据的请求头、请求体、query、rest里面的参数值写入
|
|
||||||
['headers', 'query', 'rest'].forEach((type) => {
|
|
||||||
props.request?.request?.[type]?.forEach((item) => {
|
|
||||||
const index = requestVModel.value.request?.[type]?.findIndex((itemReq) => itemReq.key === item.key);
|
|
||||||
if (index > -1 && requestVModel.value.request) {
|
|
||||||
requestVModel.value.request[type][index].value = item.value;
|
|
||||||
requestVModel.value[type] = requestVModel.value.request?.[type];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (props.request.request.body.bodyType !== 'NONE') {
|
|
||||||
['formDataBody', 'wwwFormBody'].forEach((type) => {
|
|
||||||
props.request?.request?.body[type].formValues.forEach((item) => {
|
|
||||||
const index = requestVModel.value.request?.body[type].formValues.findIndex(
|
|
||||||
(itemReq) => itemReq.key === item.key
|
|
||||||
);
|
|
||||||
if (index > -1 && requestVModel.value.request?.body) {
|
|
||||||
requestVModel.value.request.body[type].formValues[index].value = item.value;
|
|
||||||
requestVModel.value.body = requestVModel.value.request?.body;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handleActiveDebugProtocolChange(requestVModel.value.protocol);
|
handleActiveDebugProtocolChange(requestVModel.value.protocol);
|
||||||
} else {
|
} else {
|
||||||
|
// 新建自定义请求
|
||||||
requestVModel.value = cloneDeep({
|
requestVModel.value = cloneDeep({
|
||||||
...defaultDebugParams,
|
...defaultDebugParams,
|
||||||
id: getGenerateId(),
|
stepId: getGenerateId(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,6 @@ export default function useCreateActions() {
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...cloneDeep(defaultStepItemCommon),
|
...cloneDeep(defaultStepItemCommon),
|
||||||
...item,
|
|
||||||
id,
|
id,
|
||||||
config: {
|
config: {
|
||||||
...defaultStepItemCommon.config,
|
...defaultStepItemCommon.config,
|
||||||
|
|
|
@ -85,7 +85,7 @@
|
||||||
@change="handleStepContentChange($event, step)"
|
@change="handleStepContentChange($event, step)"
|
||||||
@click.stop
|
@click.stop
|
||||||
/>
|
/>
|
||||||
<!-- API、CASE、场景步骤名称 -->
|
<!-- 自定义请求、API、CASE、场景步骤名称 -->
|
||||||
<template v-if="checkStepIsApi(step)">
|
<template v-if="checkStepIsApi(step)">
|
||||||
<apiMethodName v-if="checkStepShowMethod(step)" :method="step.config.method" />
|
<apiMethodName v-if="checkStepShowMethod(step)" :method="step.config.method" />
|
||||||
<div
|
<div
|
||||||
|
@ -329,6 +329,8 @@
|
||||||
return quoteContent;
|
return quoteContent;
|
||||||
}
|
}
|
||||||
switch (step.stepType) {
|
switch (step.stepType) {
|
||||||
|
case ScenarioStepType.CUSTOM_REQUEST:
|
||||||
|
return quoteContent;
|
||||||
case ScenarioStepType.LOOP_CONTROLLER:
|
case ScenarioStepType.LOOP_CONTROLLER:
|
||||||
return loopControlContent;
|
return loopControlContent;
|
||||||
case ScenarioStepType.IF_CONTROLLER:
|
case ScenarioStepType.IF_CONTROLLER:
|
||||||
|
@ -595,8 +597,8 @@
|
||||||
if (_stepType.isCopyApi || _stepType.isQuoteApi || step.stepType === ScenarioStepType.CUSTOM_REQUEST) {
|
if (_stepType.isCopyApi || _stepType.isQuoteApi || step.stepType === ScenarioStepType.CUSTOM_REQUEST) {
|
||||||
// 复制 api、引用 api、自定义 api打开抽屉
|
// 复制 api、引用 api、自定义 api打开抽屉
|
||||||
activeStep.value = step;
|
activeStep.value = step;
|
||||||
if (stepDetails.value[step.id] === undefined) {
|
if (stepDetails.value[step.id] === undefined && !step.isNew) {
|
||||||
// 详情映射中没有加载过该 api 详情,说明是初次查看详情,引用的 api 不需要在这里加载详情
|
// 查看场景详情时,详情映射中没有对应数据,初始化步骤详情
|
||||||
await getStepDetail(step);
|
await getStepDetail(step);
|
||||||
}
|
}
|
||||||
customApiDrawerVisible.value = true;
|
customApiDrawerVisible.value = true;
|
||||||
|
@ -700,14 +702,14 @@
|
||||||
*/
|
*/
|
||||||
function addCustomApiStep(request: RequestParam) {
|
function addCustomApiStep(request: RequestParam) {
|
||||||
request.isNew = false;
|
request.isNew = false;
|
||||||
stepDetails.value[request.id] = request;
|
stepDetails.value[request.stepId] = request;
|
||||||
if (activeStep.value && activeCreateAction.value) {
|
if (activeStep.value && activeCreateAction.value) {
|
||||||
handleCreateStep(
|
handleCreateStep(
|
||||||
{
|
{
|
||||||
stepType: ScenarioStepType.CUSTOM_REQUEST,
|
stepType: ScenarioStepType.CUSTOM_REQUEST,
|
||||||
name: t('apiScenario.customApi'),
|
name: t('apiScenario.customApi'),
|
||||||
method: request.method,
|
method: request.method,
|
||||||
id: request.id,
|
id: request.stepId,
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
},
|
},
|
||||||
activeStep.value,
|
activeStep.value,
|
||||||
|
@ -724,7 +726,7 @@
|
||||||
protocol: request.protocol,
|
protocol: request.protocol,
|
||||||
method: request.method,
|
method: request.method,
|
||||||
},
|
},
|
||||||
id: request.id,
|
id: request.stepId,
|
||||||
sort: steps.value.length + 1,
|
sort: steps.value.length + 1,
|
||||||
stepType: ScenarioStepType.CUSTOM_REQUEST,
|
stepType: ScenarioStepType.CUSTOM_REQUEST,
|
||||||
refType: ScenarioStepRefType.DIRECT,
|
refType: ScenarioStepRefType.DIRECT,
|
||||||
|
@ -732,6 +734,7 @@
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
console.log(steps.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<MsSplitBox :size="0.7" :max="0.9" :min="0.7" direction="horizontal" expand-direction="right">
|
<MsSplitBox ref="splitBoxRef" :size="0.7" :max="0.9" :min="0.7" direction="horizontal" expand-direction="right">
|
||||||
<template #first>
|
<template #first>
|
||||||
<a-tabs v-model:active-key="activeKey" class="h-full" animation lazy-load>
|
<a-tabs v-model:active-key="activeKey" class="h-full" animation lazy-load>
|
||||||
<a-tab-pane :key="ScenarioCreateComposition.STEP" :title="t('apiScenario.step')" class="p-[16px]">
|
<a-tab-pane :key="ScenarioCreateComposition.STEP" :title="t('apiScenario.step')" class="p-[16px]">
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
<div class="p-[16px]">
|
<div class="p-[16px]">
|
||||||
<!-- TODO:第一版没有模板 -->
|
<!-- TODO:第一版没有模板 -->
|
||||||
<!-- <MsFormCreate v-model:api="fApi" :rule="currentApiTemplateRules" :option="options" /> -->
|
<!-- <MsFormCreate v-model:api="fApi" :rule="currentApiTemplateRules" :option="options" /> -->
|
||||||
<a-form ref="activeApiTabFormRef" :model="scenario" layout="vertical">
|
<a-form ref="createFormRef" :model="scenario" layout="vertical">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
field="name"
|
field="name"
|
||||||
:label="t('apiScenario.name')"
|
:label="t('apiScenario.name')"
|
||||||
|
@ -141,6 +141,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { FormInstance } from '@arco-design/web-vue';
|
||||||
|
|
||||||
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
||||||
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
|
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
|
||||||
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
||||||
|
@ -172,6 +174,23 @@
|
||||||
const scenario = defineModel<Scenario>('scenario', {
|
const scenario = defineModel<Scenario>('scenario', {
|
||||||
required: true,
|
required: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const splitBoxRef = ref<InstanceType<typeof MsSplitBox>>();
|
||||||
|
const createFormRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
function validScenarioForm(cb: () => Promise<void>) {
|
||||||
|
createFormRef.value?.validate(async (errors) => {
|
||||||
|
if (errors) {
|
||||||
|
splitBoxRef.value?.expand();
|
||||||
|
} else {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
validScenarioForm,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -3,14 +3,14 @@
|
||||||
<div class="flex items-center justify-between p-[24px_24px_8px_24px]">
|
<div class="flex items-center justify-between p-[24px_24px_8px_24px]">
|
||||||
<MsEditableTab
|
<MsEditableTab
|
||||||
v-model:active-tab="activeScenarioTab"
|
v-model:active-tab="activeScenarioTab"
|
||||||
v-model:tabs="apiTabs"
|
v-model:tabs="scenarioTabs"
|
||||||
class="flex-1 overflow-hidden"
|
class="flex-1 overflow-hidden"
|
||||||
@add="() => newTab()"
|
@add="() => newTab()"
|
||||||
>
|
>
|
||||||
<template #label="{ tab }">
|
<template #label="{ tab }">
|
||||||
<a-tooltip :content="tab.label" :mouse-enter-delay="500">
|
<a-tooltip :content="tab.name || tab.label" :mouse-enter-delay="500">
|
||||||
<div class="one-line-text max-w-[144px]">
|
<div class="one-line-text max-w-[144px]">
|
||||||
{{ tab.label }}
|
{{ tab.name || tab.label }}
|
||||||
</div>
|
</div>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
|
@ -61,7 +61,7 @@
|
||||||
</MsSplitBox>
|
</MsSplitBox>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="activeScenarioTab.isNew" class="pageWrap">
|
<div v-else-if="activeScenarioTab.isNew" class="pageWrap">
|
||||||
<create v-model:scenario="activeScenarioTab" :module-tree="folderTree"></create>
|
<create ref="createRef" v-model:scenario="activeScenarioTab" :module-tree="folderTree"></create>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="pageWrap">
|
<div v-else class="pageWrap">
|
||||||
<detail v-model:scenario="activeScenarioTab"></detail>
|
<detail v-model:scenario="activeScenarioTab"></detail>
|
||||||
|
@ -107,33 +107,33 @@
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const apiTabs = ref<ScenarioParams[]>([
|
const scenarioTabs = ref<ScenarioParams[]>([
|
||||||
{
|
{
|
||||||
id: 'all',
|
id: 'all',
|
||||||
label: t('apiScenario.allScenario'),
|
label: t('apiScenario.allScenario'),
|
||||||
closable: false,
|
closable: false,
|
||||||
} as ScenarioParams,
|
} as ScenarioParams,
|
||||||
]);
|
]);
|
||||||
const activeScenarioTab = ref<ScenarioParams>(apiTabs.value[0] as ScenarioParams);
|
const activeScenarioTab = ref<ScenarioParams>(scenarioTabs.value[0] as ScenarioParams);
|
||||||
|
|
||||||
function newTab(defaultScenarioInfo?: Scenario, isCopy = false) {
|
function newTab(defaultScenarioInfo?: Scenario, isCopy = false) {
|
||||||
if (defaultScenarioInfo) {
|
if (defaultScenarioInfo) {
|
||||||
apiTabs.value.push({
|
scenarioTabs.value.push({
|
||||||
...defaultScenarioInfo,
|
...defaultScenarioInfo,
|
||||||
id: isCopy ? getGenerateId() : defaultScenarioInfo.id || '',
|
id: isCopy ? getGenerateId() : defaultScenarioInfo.id || '',
|
||||||
label: isCopy ? `copy-${defaultScenarioInfo.name}` : defaultScenarioInfo.name,
|
label: isCopy ? `copy-${defaultScenarioInfo.name}` : defaultScenarioInfo.name,
|
||||||
isNew: false,
|
isNew: false,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
apiTabs.value.push({
|
scenarioTabs.value.push({
|
||||||
...cloneDeep(defaultScenario),
|
...cloneDeep(defaultScenario),
|
||||||
id: `${t('apiScenario.createScenario')}${apiTabs.value.length}`,
|
id: getGenerateId(),
|
||||||
label: `${t('apiScenario.createScenario')}${apiTabs.value.length}`,
|
label: `${t('apiScenario.createScenario')}${scenarioTabs.value.length}`,
|
||||||
moduleId: 'root',
|
moduleId: 'root',
|
||||||
priority: 'P0',
|
priority: 'P0',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
activeScenarioTab.value = apiTabs.value[apiTabs.value.length - 1] as ScenarioParams;
|
activeScenarioTab.value = scenarioTabs.value[scenarioTabs.value.length - 1] as ScenarioParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
const folderTree = ref<ModuleTreeNode[]>([]);
|
const folderTree = ref<ModuleTreeNode[]>([]);
|
||||||
|
@ -182,9 +182,10 @@
|
||||||
|
|
||||||
onBeforeMount(selectRecycleCount);
|
onBeforeMount(selectRecycleCount);
|
||||||
|
|
||||||
|
const createRef = ref<InstanceType<typeof create>>();
|
||||||
const saveLoading = ref(false);
|
const saveLoading = ref(false);
|
||||||
|
|
||||||
async function saveScenario() {
|
async function realSaveScenario() {
|
||||||
try {
|
try {
|
||||||
saveLoading.value = true;
|
saveLoading.value = true;
|
||||||
if (activeScenarioTab.value.isNew) {
|
if (activeScenarioTab.value.isNew) {
|
||||||
|
@ -195,7 +196,19 @@
|
||||||
const scenarioDetail = await getScenarioDetail(res.id);
|
const scenarioDetail = await getScenarioDetail(res.id);
|
||||||
scenarioDetail.stepDetails = {};
|
scenarioDetail.stepDetails = {};
|
||||||
scenarioDetail.isNew = false;
|
scenarioDetail.isNew = false;
|
||||||
activeScenarioTab.value = scenarioDetail as ScenarioParams;
|
scenarioDetail.id = res.id;
|
||||||
|
if (!scenarioDetail.steps) {
|
||||||
|
scenarioDetail.steps = [];
|
||||||
|
}
|
||||||
|
const index = scenarioTabs.value.findIndex((e) => e.id === activeScenarioTab.value.id);
|
||||||
|
if (index !== -1) {
|
||||||
|
const newScenarioTab = {
|
||||||
|
...cloneDeep(activeScenarioTab.value),
|
||||||
|
...scenarioDetail,
|
||||||
|
};
|
||||||
|
scenarioTabs.value.splice(index, 1, newScenarioTab);
|
||||||
|
activeScenarioTab.value = newScenarioTab;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await updateScenario({
|
await updateScenario({
|
||||||
...activeScenarioTab.value,
|
...activeScenarioTab.value,
|
||||||
|
@ -212,11 +225,22 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveScenario() {
|
||||||
|
if (activeScenarioTab.value.isNew) {
|
||||||
|
createRef.value?.validScenarioForm(realSaveScenario);
|
||||||
|
} else {
|
||||||
|
realSaveScenario();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function openScenarioTab(record: ApiScenarioTableItem, isCopy?: boolean) {
|
async function openScenarioTab(record: ApiScenarioTableItem, isCopy?: boolean) {
|
||||||
try {
|
try {
|
||||||
appStore.showLoading();
|
appStore.showLoading();
|
||||||
const res = await getScenarioDetail(record.id);
|
const res = await getScenarioDetail(record.id);
|
||||||
res.stepDetails = {};
|
res.stepDetails = {};
|
||||||
|
if (!res.steps) {
|
||||||
|
res.steps = [];
|
||||||
|
}
|
||||||
newTab(res, isCopy);
|
newTab(res, isCopy);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
|
|
Loading…
Reference in New Issue