fix(接口管理): 用例执行响应内容默认为空且折叠&补充api权限&样式细节调整

This commit is contained in:
teukkk 2024-03-20 19:45:12 +08:00 committed by Craftsman
parent 1e598e6da9
commit 66844564d6
11 changed files with 120 additions and 108 deletions

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.1118 1.33333C10.5538 1.33333 10.9777 1.50892 11.2903 1.82148L12.5764 3.10753L14.1782 4.70939C14.4907 5.02195 14.6663 5.44586 14.6663 5.8879V13C14.6663 13.9205 13.9202 14.6667 12.9997 14.6667H2.99967C2.0792 14.6667 1.33301 13.9205 1.33301 13V2.99999C1.33301 2.07952 2.0792 1.33333 2.99967 1.33333H10.1118ZM10.1118 2.66666H2.99967C2.81558 2.66666 2.66634 2.8159 2.66634 2.99999V13C2.66634 13.1841 2.81557 13.3333 2.99967 13.3333H12.9997C13.1838 13.3333 13.333 13.1841 13.333 13V5.8879C13.333 5.79949 13.2979 5.71472 13.2354 5.65219L10.3475 2.76429C10.285 2.70177 10.2002 2.66666 10.1118 2.66666ZM6.24054 6.66666V7.37835H4.42034V7.90814H6.10882V8.58799H4.42034V9.24511H6.29323V10H3.33301V6.66666H6.24054ZM8.64034 7.53069C8.91018 7.53069 9.12134 7.60686 9.27382 7.7592C9.4263 7.91155 9.50254 8.14688 9.50254 8.46521V10H8.52298V8.67212C8.52298 8.52053 8.49344 8.41329 8.43437 8.35038C8.37529 8.28747 8.29226 8.25602 8.18529 8.25602C8.06713 8.25602 7.97133 8.29846 7.89789 8.38335C7.82444 8.46824 7.78772 8.62058 7.78772 8.84038V10H6.81295V7.58526H7.72066V7.97862C7.85637 7.81794 7.99369 7.70312 8.1326 7.63415C8.27151 7.56518 8.44075 7.53069 8.64034 7.53069ZM10.6785 7.58526L11.1719 9.1155L11.682 7.58526H12.6663L11.5862 10H10.7216L9.66301 7.58526H10.6785Z" fill="#959598"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -104,6 +104,14 @@
emit('batchAction', item as BatchActionParams);
};
const handleMoreActionLength = () => {
moreAction.value?.forEach((key) => {
if (key.permission && hasAllPermission(key.permission as string[])) {
moreActionLength.value += 1;
}
});
};
const computedLastVisibleIndex = () => {
if (!refWrapper.value) {
return;
@ -146,17 +154,14 @@
const value = menuItemIndex - 1;
baseAction.value = allAction.value.slice(0, value);
moreAction.value = allAction.value.slice(value);
handleMoreActionLength();
computedStatus.value = false;
return;
}
}
moreAction.value = props.actionConfig?.moreAction || [];
baseAction.value = props.actionConfig?.baseAction || [];
moreAction.value.forEach((key) => {
if (key.permission && hasAllPermission(key.permission as string[])) {
moreActionLength.value += 1;
}
});
handleMoreActionLength();
};
watch(

View File

@ -10,7 +10,7 @@
>
<template #prefix>
<div class="flex cursor-pointer p-[8px]" @click.stop="goEnv">
<icon-location class="text-[var(--color-text-4)]" />
<svg-icon width="14px" height="14px" :name="'icon_env'" class="text-[var(--color-text-4)]" />
</div>
</template>
</a-select>
@ -21,13 +21,14 @@
import { SelectOptionData } from '@arco-design/web-vue';
import { getEnvironment, getEnvList } from '@/api/modules/api-test/common';
import router from '@/router';
import useOpenNewPage from '@/hooks/useOpenNewPage';
import useAppStore from '@/store/modules/app';
import { EnvConfig } from '@/models/projectManagement/environmental';
import { ProjectManagementRouteEnum } from '@/enums/routeEnum';
const appStore = useAppStore();
const { openNewPage } = useOpenNewPage();
const currentEnv = ref('');
@ -64,9 +65,7 @@
}
function goEnv() {
router.push({
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_ENVIRONMENT_MANAGEMENT,
});
openNewPage(ProjectManagementRouteEnum.PROJECT_MANAGEMENT_ENVIRONMENT_MANAGEMENT);
}
onBeforeMount(() => {

View File

@ -180,7 +180,7 @@
>
<div class="tab-pane-container">
<a-spin
v-if="requestVModel.activeTab === RequestComposition.PLUGIN"
v-show="requestVModel.activeTab === RequestComposition.PLUGIN"
:loading="pluginLoading"
class="min-h-[100px] w-full"
>
@ -276,14 +276,14 @@
:is-expanded="isVerticalExpanded"
:hide-layout-switch="props.hideResponseLayoutSwitch"
:request-task-result="requestVModel.response"
:is-edit="props.isDefinition && isHttpProtocol"
:is-edit="props.isDefinition && isHttpProtocol && !props.isCase"
:upload-temp-file-api="props.uploadTempFileApi"
:loading="requestVModel.executeLoading || loading"
:is-definition="props.isDefinition"
@change-expand="changeVerticalExpand"
@change-layout="handleActiveLayoutChange"
@change="handleActiveDebugChange"
@execute="execute"
@execute="(executeType) => (props.isCase ? emit('execute', executeType) : execute(executeType))"
/>
</template>
</MsSplitBox>
@ -587,7 +587,7 @@
const props = defineProps<{
request: RequestParam; //
moduleTree?: ModuleTreeNode[]; //
isCase?: boolean; //
isCase?: boolean; // ,,
apiDetail?: RequestParam; //
detailLoading?: boolean; //
isDefinition?: boolean; //
@ -608,7 +608,7 @@
update: string;
};
}>();
const emit = defineEmits(['addDone']);
const emit = defineEmits(['addDone', 'execute']);
const appStore = useAppStore();
const { t } = useI18n();
@ -931,7 +931,8 @@
() =>
isHttpProtocol.value ||
!props.isDefinition ||
requestVModel.value.response?.requestResults[0]?.responseResult.responseCode
requestVModel.value.response?.requestResults[0]?.responseResult.responseCode ||
props.isCase
);
const splitBoxSize = ref<string | number>(!showResponse.value ? 1 : 0.6);
const activeLayout = ref<'horizontal' | 'vertical'>('vertical');
@ -1543,6 +1544,7 @@
defineExpose({
makeRequestParams,
changeVerticalExpand,
});
</script>

View File

@ -79,7 +79,7 @@
</template>
<template #method="{ record }">
<a-select
v-if="props.protocol === 'HTTP'"
v-if="props.protocol === 'HTTP' && hasAnyPermission(['PROJECT_API_DEFINITION:READ+UPDATE'])"
v-model:model-value="record.method"
class="param-input w-full"
size="mini"
@ -96,6 +96,7 @@
</template>
<template #status="{ record }">
<a-select
v-if="hasAnyPermission(['PROJECT_API_DEFINITION:READ+UPDATE'])"
v-model:model-value="record.status"
class="param-input w-full"
size="mini"
@ -108,16 +109,27 @@
<apiStatus :status="item" size="small" />
</a-option>
</a-select>
<apiStatus v-else :status="record.status" size="small" />
</template>
<template #action="{ record }">
<MsButton type="text" class="!mr-0" @click="executeDefinition(record)">
<MsButton
v-permission="['PROJECT_API_DEFINITION:READ+EXECUTE']"
type="text"
class="!mr-0"
@click="executeDefinition(record)"
>
{{ t('apiTestManagement.execute') }}
</MsButton>
<a-divider direction="vertical" :margin="8"></a-divider>
<MsButton type="text" class="!mr-0" @click="copyDefinition(record)">
<a-divider v-permission="['PROJECT_API_DEFINITION:READ+EXECUTE']" direction="vertical" :margin="8"></a-divider>
<MsButton
v-permission="['PROJECT_API_DEFINITION:READ+ADD']"
type="text"
class="!mr-0"
@click="copyDefinition(record)"
>
{{ t('common.copy') }}
</MsButton>
<a-divider direction="vertical" :margin="8"></a-divider>
<a-divider v-permission="['PROJECT_API_DEFINITION:READ+ADD']" direction="vertical" :margin="8"></a-divider>
<MsTableMoreAction :list="tableMoreActionList" @select="handleTableMoreActionSelect($event, record)" />
</template>
</ms-base-table>
@ -263,6 +275,7 @@
import useModal from '@/hooks/useModal';
import useTableStore from '@/hooks/useTableStore';
import useAppStore from '@/store/modules/app';
import { hasAnyPermission } from '@/utils/permission';
import { ApiDefinitionDetail } from '@/models/apiTest/management';
import { DragSortParams } from '@/models/common';
@ -289,6 +302,13 @@
const refreshModuleTree: (() => Promise<any>) | undefined = inject('refreshModuleTree');
const keyword = ref('');
const hasOperationPermission = computed(() =>
hasAnyPermission([
'PROJECT_API_DEFINITION:READ+DELETE',
'PROJECT_API_DEFINITION:READ+ADD',
'PROJECT_API_DEFINITION:READ+EXECUTE',
])
);
let columns: MsTableColumn = [
{
title: 'ID',
@ -363,11 +383,11 @@
width: 180,
},
{
title: 'common.operation',
title: hasOperationPermission.value ? 'common.operation' : '',
slotName: 'action',
dataIndex: 'operation',
fixed: 'right',
width: 150,
width: hasOperationPermission.value ? 150 : 50,
},
];
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector } = useTable(
@ -398,10 +418,12 @@
{
label: 'common.edit',
eventTag: 'edit',
permission: ['PROJECT_API_DEFINITION:READ+UPDATE'],
},
{
label: 'common.move',
eventTag: 'move',
permission: ['PROJECT_API_DEFINITION:READ+UPDATE'],
},
],
moreAction: [
@ -418,6 +440,7 @@
eventTag: 'delete',
label: t('common.delete'),
danger: true,
permission: ['PROJECT_API_DEFINITION:READ+DELETE'],
},
];

View File

@ -236,9 +236,10 @@
</a-collapse-item>
<a-collapse-item
v-if="
previewDetail.responseDefinition &&
(previewDetail.responseDefinition &&
previewDetail.responseDefinition.length > 0 &&
props.detail.protocol === 'HTTP'
props.detail.protocol === 'HTTP') ||
props.isCase
"
key="response"
>

View File

@ -83,11 +83,11 @@
import MsDetailCard from '@/components/pure/ms-detail-card/index.vue';
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
import type { CaseLevel } from '@/components/business/ms-case-associate/types';
import environmentSelect from '../../environmentSelect.vue';
import detailTab from '../api/preview/detail.vue';
import createAndEditCaseDrawer from './createAndEditCaseDrawer.vue';
import execute from './execute.vue';
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
import environmentSelect from '@/views/api-test/components/environmentSelect.vue';
import execute from '@/views/api-test/components/executeButton.vue';
import { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
import TabCaseChangeHistory from '@/views/api-test/management/components/management/case/tabContent/tabCaseChangeHistory.vue';
import TabCaseDependency from '@/views/api-test/management/components/management/case/tabContent/tabCaseDependency.vue';
@ -236,17 +236,29 @@
</script>
<style lang="less" scoped>
.arco-tabs {
@apply flex flex-col;
:deep(.arco-tabs-nav) {
border-bottom: 1px solid var(--color-text-n8);
}
:deep(.arco-tabs-nav-extra) {
&-extra {
line-height: 32px;
}
}
:deep(.arco-tabs-content) {
@apply pt-0;
@apply flex-1 pt-0;
.arco-tabs-content-item {
@apply px-0;
}
.arco-tabs-content-list {
@apply h-full;
}
.arco-tabs-content-list .arco-tabs-content-item:nth-of-type(1) .arco-tabs-pane {
@apply h-full overflow-hidden;
}
.arco-collapse {
height: calc(100% - 85px);
}
}
}
:deep(.ms-detail-card-desc) {
gap: 16px;

View File

@ -569,7 +569,7 @@
selectable: true,
showSelectAll: true,
draggable: { type: 'handle', width: 32 },
heightUsed: 308,
heightUsed: props.isApi ? 356 : 308,
});
const batchActions = {
baseAction: [

View File

@ -36,6 +36,7 @@
/>
<environmentSelect ref="environmentSelectRef" />
<execute
ref="executeRef"
v-model:detail="detailForm"
:environment-id="currentEnvConfig?.id as string"
:request="requestCompositionRef?.makeRequestParams"
@ -56,10 +57,10 @@
<a-form-item field="status" :label="t('apiTestManagement.apiStatus')">
<a-select v-model:model-value="detailForm.status" :placeholder="t('common.pleaseSelect')">
<template #label>
<apiStatus :status="detailForm.status" />
<apiStatus :status="detailForm.status" size="small" />
</template>
<a-option v-for="item of Object.values(RequestDefinitionStatus)" :key="item" :value="item">
<apiStatus :status="item" />
<apiStatus :status="item" size="small" />
</a-option>
</a-select>
</a-form-item>
@ -82,7 +83,8 @@
:file-module-options-api="getTransferOptionsCase"
:file-save-as-api="transferFileCase"
:current-env-config="currentEnvConfig"
:is-definition="true"
is-definition
@execute="(val: 'localExec' | 'serverExec')=>executeRef?.execute(val)"
/>
</div>
</div>
@ -98,10 +100,10 @@
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
import type { CaseLevel } from '@/components/business/ms-case-associate/types';
import environmentSelect from '../../environmentSelect.vue';
import execute from './execute.vue';
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
import apiStatus from '@/views/api-test/components/apiStatus.vue';
import environmentSelect from '@/views/api-test/components/environmentSelect.vue';
import execute from '@/views/api-test/components/executeButton.vue';
import requestComposition, { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
import {
@ -173,6 +175,7 @@
});
const detailForm = ref(cloneDeep(defaultDetail.value));
const isEdit = ref(false);
const executeRef = ref<InstanceType<typeof execute>>();
async function open(apiId: string, record?: ApiCaseDetail | RequestParam, isCopy?: boolean) {
apiDefinitionId.value = apiId;
@ -185,10 +188,15 @@
//
detailForm.value = {
...cloneDeep(defaultDetail.value),
...(apiDetailInfo.value.protocol === 'HTTP'
? {
headers: apiDetailInfo.value.headers ?? apiDetailInfo.value.request.headers,
body: apiDetailInfo.value.body ?? apiDetailInfo.value.request.body,
body: apiDetailInfo.value?.body ?? apiDetailInfo.value.request.body,
rest: apiDetailInfo.value.rest ?? apiDetailInfo.value.request.rest,
query: apiDetailInfo.value.query ?? apiDetailInfo.value.request.query,
}
: {}),
url: apiDetailInfo.value.url ?? apiDetailInfo.value.request.url,
};
//
if (isCopy) {
@ -201,6 +209,8 @@
detailForm.value.isNew = false;
}
innerVisible.value = true;
await nextTick();
requestCompositionRef.value?.changeVerticalExpand(false); //
}
function handleSaveCaseCancel() {
@ -250,7 +260,16 @@
if (!isContinue) {
handleSaveCaseCancel();
}
detailForm.value = cloneDeep(defaultDetail.value);
//
detailForm.value = {
...cloneDeep(defaultDetail.value),
id: `case-${Date.now()}`,
headers: apiDetailInfo.value.headers ?? apiDetailInfo.value.request.headers,
body: apiDetailInfo.value.body ?? apiDetailInfo.value.request.body,
rest: apiDetailInfo.value.rest ?? apiDetailInfo.value.request.rest,
query: apiDetailInfo.value.query ?? apiDetailInfo.value.request.query,
url: apiDetailInfo.value.url ?? apiDetailInfo.value.request.url,
};
drawerLoading.value = false;
}
});

View File

@ -21,20 +21,7 @@
</a-tooltip>
</template>
</MsEditableTab>
<a-select
v-model:model-value="currentEnv"
:options="envOptions"
class="!w-[200px] pl-0 pr-[8px]"
:loading="envLoading"
allow-search
@change="initEnvironment"
>
<template #prefix>
<div class="flex cursor-pointer p-[8px]" @click.stop="goEnv">
<icon-location class="text-[var(--color-text-4)]" />
</div>
</template>
</a-select>
<environmentSelect ref="environmentSelectRef" />
</div>
<api
v-show="(activeApiTab.id === 'all' && currentTab === 'api') || activeApiTab.type === 'api'"
@ -59,20 +46,19 @@
</template>
<script setup lang="ts">
import { SelectOptionData } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
import MsEditableTab from '@/components/pure/ms-editable-tab/index.vue';
import api from './api/index.vue';
import apiCase from './case/index.vue';
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
import environmentSelect from '@/views/api-test/components/environmentSelect.vue';
import { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
// import MockTable from '@/views/api-test/management/components/management/mock/mockTable.vue';
import { getEnvironment, getEnvList, getProtocolList } from '@/api/modules/api-test/common';
import { getProtocolList } from '@/api/modules/api-test/common';
import { getLocalConfig } from '@/api/modules/user/index';
import { useI18n } from '@/hooks/useI18n';
import router from '@/router';
import useAppStore from '@/store/modules/app';
import { ProtocolItem } from '@/models/apiTest/common';
@ -86,7 +72,6 @@
RequestMethods,
ResponseComposition,
} from '@/enums/apiEnum';
import { ProjectManagementRouteEnum } from '@/enums/routeEnum';
import { defaultBodyParams, defaultResponse, defaultResponseItem } from '@/views/api-test/components/config';
@ -230,49 +215,10 @@
}
);
const currentEnv = ref('');
const currentEnvConfig = ref<EnvConfig>();
const envLoading = ref(false);
const envOptions = ref<SelectOptionData[]>([]);
async function initEnvironment() {
try {
currentEnvConfig.value = await getEnvironment(currentEnv.value);
currentEnvConfig.value.id = currentEnv.value;
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
async function initEnvList() {
try {
envLoading.value = true;
const res = await getEnvList(appStore.currentProjectId);
envOptions.value = res.map((item) => ({
label: item.name,
value: item.id,
}));
currentEnv.value = res[0]?.id || '';
initEnvironment();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
envLoading.value = false;
}
}
function refreshApiTable() {
apiRef.value?.refreshTable();
}
function goEnv() {
router.push({
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_ENVIRONMENT_MANAGEMENT,
});
}
/**
* 同步模块树的接口信息更新操作
*/
@ -333,7 +279,7 @@
const apiLocalExec = ref<Record<string, any> | LocalConfig | undefined>({});
async function initLocalConfig() {
try {
const res = await getLocalConfig(); // TODO:
const res = await getLocalConfig();
apiLocalExec.value = res.find((e) => e.type === 'API');
} catch (error) {
// eslint-disable-next-line no-console
@ -341,8 +287,10 @@
}
}
const environmentSelectRef = ref<InstanceType<typeof environmentSelect>>();
const currentEnvConfig = computed<EnvConfig | undefined>(() => environmentSelectRef.value?.currentEnvConfig);
onBeforeMount(() => {
initEnvList();
initProtocolList();
initLocalConfig();
});