feat(接口管理): 新增用例详情的引用执行变更记录的列表

This commit is contained in:
guoyuqi 2024-03-18 15:21:46 +08:00 committed by Craftsman
parent 10fb5fead9
commit ae34ffe61f
11 changed files with 571 additions and 271 deletions

View File

@ -81,6 +81,7 @@ import {
ApiCaseChangeHistoryParams, ApiCaseChangeHistoryParams,
ApiCaseDependencyParams, ApiCaseDependencyParams,
ApiCaseDetail, ApiCaseDetail,
ApiCaseExecuteHistoryItem,
ApiCaseExecuteHistoryParams, ApiCaseExecuteHistoryParams,
ApiCasePageParams, ApiCasePageParams,
ApiDefinitionBatchDeleteParams, ApiDefinitionBatchDeleteParams,
@ -115,7 +116,6 @@ import {
DragSortParams, DragSortParams,
ModuleTreeNode, ModuleTreeNode,
MoveModules, MoveModules,
TableQueryParams,
TransferFileParams, TransferFileParams,
} from '@/models/common'; } from '@/models/common';
import { ResourcePoolItem } from '@/models/setting/resourcePool'; import { ResourcePoolItem } from '@/models/setting/resourcePool';
@ -457,7 +457,7 @@ export function getEnvList(projectId: string) {
// 获取接口用例-执行历史 // 获取接口用例-执行历史
export function getApiCaseExecuteHistory(data: ApiCaseExecuteHistoryParams) { export function getApiCaseExecuteHistory(data: ApiCaseExecuteHistoryParams) {
return MSR.post({ url: GetExecuteHistoryUrl, data }); return MSR.post<CommonList<ApiCaseExecuteHistoryItem>>({ url: GetExecuteHistoryUrl, data });
} }
// 获取接口用例-变更历史 // 获取接口用例-变更历史

View File

@ -70,7 +70,7 @@ export const GetCaseDetailUrl = '/api/case/get-detail'; // 获取接口用例详
export const GetEnvListUrl = '/api/test/env-list'; // 接口测试-环境列表 export const GetEnvListUrl = '/api/test/env-list'; // 接口测试-环境列表
export const BatchExecuteCaseUrl = '/api/case/batch/run'; // 批量执行接口用例 export const BatchExecuteCaseUrl = '/api/case/batch/run'; // 批量执行接口用例
export const ExecuteCaseUrl = '/api/case/run/'; // 单独执行接口用例 export const ExecuteCaseUrl = '/api/case/run/'; // 单独执行接口用例
export const GetExecuteHistoryUrl = 'api/case/execute/page'; // 获取用的执行历史 export const GetExecuteHistoryUrl = '/api/case/execute/page'; // 获取用的执行历史
export const GetDependencyUrl = '/api/case/get-reference'; // 获取用例的依赖关系 export const GetDependencyUrl = '/api/case/get-reference'; // 获取用例的依赖关系
export const GetChangeHistoryUrl = '/api/case/operation-history/page'; // 获取用例的依赖关系 export const GetChangeHistoryUrl = '/api/case/operation-history/page'; // 获取用例的依赖关系
export const ToggleFollowCaseUrl = '/api/case/follow'; // 接口定义-关注/取消关注 export const ToggleFollowCaseUrl = '/api/case/follow'; // 接口定义-关注/取消关注

View File

@ -278,3 +278,11 @@ export enum ExecuteStatusFilters {
FAKE_ERROR = 'FAKE_ERROR', FAKE_ERROR = 'FAKE_ERROR',
STOPPED = 'STOPPED', STOPPED = 'STOPPED',
} }
// 变更历史类型
export enum ChangeHistoryStatusFilters {
ADD = 'ADD',
UPDATE = 'UPDATE',
IMPORT = 'IMPORT',
DELETE = 'DELETE',
}

View File

@ -383,3 +383,15 @@ export interface ApiCaseChangeHistoryParams extends TableQueryParams {
export interface ApiCaseDependencyParams extends TableQueryParams { export interface ApiCaseDependencyParams extends TableQueryParams {
resourceId: string; resourceId: string;
} }
// 用例-执行历史-请求参数
export interface ApiCaseExecuteHistoryItem {
id: string;
num: string;
name: string;
operationUser: string;
createUser: string;
startTime: number;
status: string;
triggerMode: string;
}

View File

@ -50,12 +50,15 @@
<detailTab :detail="caseDetail" :protocols="protocols" is-case /> <detailTab :detail="caseDetail" :protocols="protocols" is-case />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="reference" :title="t('apiTestManagement.reference')" class="px-[18px] py-[16px]"> <a-tab-pane key="reference" :title="t('apiTestManagement.reference')" class="px-[18px] py-[16px]">
<quote :source-id="caseDetail.id" /> <tab-case-dependency :source-id="caseDetail.id" />
</a-tab-pane>
<a-tab-pane key="executeHistory" :title="t('apiTestManagement.executeHistory')" class="px-[18px] py-[16px]">
<tab-case-execute-history :source-id="caseDetail.id" module-type="API_REPORT" />
</a-tab-pane> </a-tab-pane>
<!-- <a-tab-pane key="dependencies" :title="t('apiTestManagement.dependencies')" class="px-[18px] py-[16px]"> <!-- <a-tab-pane key="dependencies" :title="t('apiTestManagement.dependencies')" class="px-[18px] py-[16px]">
</a-tab-pane> --> </a-tab-pane> -->
<a-tab-pane key="changeHistory" :title="t('apiTestManagement.changeHistory')" class="px-[18px] py-[16px]"> <a-tab-pane key="changeHistory" :title="t('apiTestManagement.changeHistory')" class="px-[18px] py-[16px]">
<history :source-id="caseDetail.id" /> <tab-case-change-history :source-id="caseDetail.id" />
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
</div> </div>
@ -72,11 +75,12 @@
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue'; import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
import type { CaseLevel } from '@/components/business/ms-case-associate/types'; import type { CaseLevel } from '@/components/business/ms-case-associate/types';
import detailTab from '../api/preview/detail.vue'; import detailTab from '../api/preview/detail.vue';
import history from '../api/preview/history.vue';
import quote from '../api/preview/quote.vue';
import createAndEditCaseDrawer from './createAndEditCaseDrawer.vue'; import createAndEditCaseDrawer from './createAndEditCaseDrawer.vue';
import apiMethodName from '@/views/api-test/components/apiMethodName.vue'; import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
import { RequestParam } from '@/views/api-test/components/requestComposition/index.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';
import TabCaseExecuteHistory from '@/views/api-test/management/components/management/case/tabContent/tabCaseExecuteHistory.vue';
import { getProtocolList } from '@/api/modules/api-test/common'; import { getProtocolList } from '@/api/modules/api-test/common';
import { toggleFollowCase } from '@/api/modules/api-test/management'; import { toggleFollowCase } from '@/api/modules/api-test/management';

View File

@ -1,264 +0,0 @@
<template>
<div>
<a-alert v-if="isShowTip" class="mb-6" type="warning">
<div class="flex items-start justify-between">
<span class="w-[80%]">{{ t('case.detail.changeHistoryTip') }}</span>
<span class="cursor-pointer text-[var(--color-text-2)]" @click="noRemindHandler">{{
t('case.detail.noReminders')
}}</span>
</div>
</a-alert>
<ms-base-table v-bind="propsRes" v-on="propsEvent">
<template #name="{ record }">
<a-button type="text" class="px-0">{{ record.name }}</a-button>
</template>
<template #type="{ record }">
{{ t(typeOptions.find((e) => e.value === record.type)?.label || '') }}
</template>
<template #operation="{ record }">
<!-- TODO 这一版本不上 -->
<!-- <MsRemoveButton
position="br"
:title="
t('caseManagement.featureCase.confirmRecoverChangeHistoryTitle', { name: characterLimit(record.name) })
"
:sub-title-tip="
t('caseManagement.featureCase.recoverChangeHistoryTip', { name: characterLimit(record.name) })
"
:loading="recoverLoading"
@ok="recoverHandler(record)"
/> -->
<MsButton @click="saveAsHandler(record)">{{ t('caseManagement.featureCase.saveAsVersion') }}</MsButton>
</template>
</ms-base-table>
<a-modal
v-model:visible="showModal"
title-align="start"
class="ms-modal-form ms-modal-medium"
:ok-text="t('organization.member.Confirm')"
:cancel-text="t('organization.member.Cancel')"
unmount-on-close
@close="handleCancel"
>
<template #title> {{ t('caseManagement.featureCase.saveAsVersion') }} </template>
<div class="form">
<a-form ref="versionFormRef" :model="form" size="large" layout="vertical">
<a-form-item
field="versionId"
:label="t('caseManagement.featureCase.tableColumnVersion')"
asterisk-position="end"
:rules="[{ required: true, message: t('caseManagement.featureCase.saveAsVersionPlaceholder') }]"
>
<a-select
v-model="form.versionId"
multiple
allow-clear
:placeholder="t('organization.member.selectUserScope')"
>
<a-option v-for="item of versionOptions" :key="item.id" :value="item.id">{{ item.name }}</a-option>
</a-select>
<MsFormItemSub :text="t('caseManagement.featureCase.saveAsVersionTip')" :show-fill-icon="false" />
</a-form-item>
</a-form>
</div>
<template #footer>
<a-button type="secondary" @click="handleCancel">{{ t('common.cancel') }}</a-button>
<a-button class="ml-[12px]" type="primary" :loading="confirmLoading" @click="handleOK">
{{ t('common.save') }}
</a-button>
</template>
</a-modal>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { FormInstance, Message, ValidatedError } from '@arco-design/web-vue';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import type { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import MsFormItemSub from '@/components/business/ms-form-item-sub/index.vue';
import { getApiCaseChangeHistory } from '@/api/modules/api-test/management';
import { useI18n } from '@/hooks/useI18n';
import useVisit from '@/hooks/useVisit';
import { useAppStore } from '@/store';
import { TableKeyEnum } from '@/enums/tableEnum';
const { t } = useI18n();
const appStore = useAppStore();
const visitedKey = 'notRemindChangeHistoryTip';
const { addVisited } = useVisit(visitedKey);
const { getIsVisited } = useVisit(visitedKey);
const props = defineProps<{
caseId: string;
}>();
const columns: MsTableColumn = [
{
title: 'case.detail.changeNumber',
dataIndex: 'id',
showTooltip: true,
width: 90,
},
{
title: 'case.detail.changeType',
slotName: 'type',
dataIndex: 'type',
width: 200,
},
{
title: 'case.detail.operator',
dataIndex: 'createUserName',
slotName: 'createUserName',
width: 150,
},
{
title: 'case.detail.tableColumnUpdateTime',
slotName: 'createTime',
dataIndex: 'createTime',
width: 200,
},
// {
// title: 'caseManagement.featureCase.tableColumnActions',
// slotName: 'operation',
// dataIndex: 'operation',
// fixed: 'right',
// width: 140,
// showInTable: true,
// showDrag: false,
// },
];
const typeOptions = [
{
label: 'system.log.operateType.add',
value: 'ADD',
},
{
label: 'system.log.operateType.update',
value: 'UPDATE',
},
{
label: 'system.log.operateType.import',
value: 'IMPORT',
},
{
label: 'system.log.operateType.delete',
value: 'DELETE',
},
];
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector } = useTable(getApiCaseChangeHistory, {
columns,
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_CHANGE_HISTORY,
scroll: { x: '100%' },
selectable: false,
heightUsed: 340,
enableDrag: false,
});
const form = ref({
versionId: '',
});
const versionOptions = ref([
{
id: '1001',
name: 'v1.0',
},
{
id: '1002',
name: 'v1.1',
},
]);
const recoverLoading = ref<boolean>(false);
//
async function recoverHandler(record: any) {
recoverLoading.value = true;
try {
Message.success(t('caseManagement.featureCase.recoveredSuccessfully'));
resetSelector();
} catch (error) {
console.log(error);
} finally {
recoverLoading.value = false;
}
}
const showModal = ref<boolean>(false);
//
function saveAsHandler(record: any) {
showModal.value = true;
}
const handleCancel = () => {
showModal.value = false;
};
const confirmLoading = ref<boolean>(false);
const versionFormRef = ref<FormInstance | null>(null);
const handleOK = () => {
versionFormRef.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => {
if (!errors) {
try {
confirmLoading.value = true;
Message.success(t('common.saveSuccess'));
handleCancel();
} catch (error) {
console.log(error);
} finally {
confirmLoading.value = false;
}
} else {
return false;
}
});
};
const isShowTip = ref<boolean>(true);
const noRemindHandler = () => {
isShowTip.value = false;
addVisited();
};
const doCheckIsTip = () => {
isShowTip.value = !getIsVisited();
};
async function initData() {
setLoadListParams({
projectId: appStore.currentProjectId,
sourceId: props.caseId,
types: ['IMPORT', 'ADD', 'UPDATE', 'DELETE'],
modules: ['API_TEST_MANAGEMENT_CASE'],
});
await loadList();
}
// watch(
// () => activeTab.value,
// (val) => {
// if (val === 'changeHistory') {
// doCheckIsTip();
// initData();
// }
// }
// );
onMounted(() => {
doCheckIsTip();
initData();
});
</script>
<style scoped></style>

View File

@ -0,0 +1,180 @@
<template>
<div class="history-container">
<a-alert v-if="!getIsVisited()" :show-icon="false" class="mb-[16px]" type="warning" closable @close="addVisited">
{{ t('apiTestManagement.historyListTip') }}
<template #close-element>
<span class="text-[14px]">{{ t('common.notRemind') }}</span>
</template>
</a-alert>
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
<template #typeFilter="{ columnConfig }">
<a-trigger
v-model:popup-visible="statusFilterVisible"
trigger="click"
@popup-visible-change="handleFilterHidden"
>
<MsButton type="text" class="arco-btn-text--secondary" @click="statusFilterVisible = true">
{{ t(columnConfig.title as string) }}
<icon-down :class="statusFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
</MsButton>
<template #content>
<div class="arco-table-filters-content">
<div class="flex items-center justify-center px-[6px] py-[2px]">
<a-checkbox-group v-model:model-value="typeFilter" direction="vertical" size="small">
<a-checkbox v-for="val of typeOptions" :key="val.value" :value="val.value">
<span>{{ t(val.label) }}</span>
</a-checkbox>
</a-checkbox-group>
</div>
</div>
</template>
</a-trigger>
</template>
</ms-base-table>
</div>
</template>
<script setup lang="ts">
import dayjs from 'dayjs';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import { getApiCaseChangeHistory } from '@/api/modules/api-test/management';
import { operationTypeOptions } from '@/config/common';
import { useI18n } from '@/hooks/useI18n';
import useVisit from '@/hooks/useVisit';
import useAppStore from '@/store/modules/app';
import { hasAnyPermission } from '@/utils/permission';
import { ChangeHistoryStatusFilters } from '@/enums/apiEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
const typeFilter = ref(Object.keys(ChangeHistoryStatusFilters));
const statusFilterVisible = ref(false);
const props = defineProps<{
sourceId: string | number;
}>();
const appStore = useAppStore();
const { t } = useI18n();
const visitedKey = 'messageManagementRobotListTip';
const { addVisited, getIsVisited } = useVisit(visitedKey);
const typeOptions = [
{
label: 'system.log.operateType.add',
value: 'ADD',
},
{
label: 'system.log.operateType.update',
value: 'UPDATE',
},
{
label: 'system.log.operateType.import',
value: 'IMPORT',
},
{
label: 'system.log.operateType.delete',
value: 'DELETE',
},
];
const columns: MsTableColumn = [
{
title: 'apiTestManagement.changeOrder',
dataIndex: 'id',
width: 200,
},
{
title: 'apiTestManagement.type',
dataIndex: 'type',
slotName: 'type',
titleSlotName: 'typeFilter',
width: 100,
},
{
title: 'mockManagement.operationUser',
dataIndex: 'createUserName',
showTooltip: true,
width: 150,
},
{
title: 'apiTestManagement.updateTime',
dataIndex: 'createTime',
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
width: 180,
},
// {
// title: 'common.operation',
// slotName: 'action',
// dataIndex: 'operation',
// width: 50,
// },
];
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(
getApiCaseChangeHistory,
{
columns,
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_CHANGE_HISTORY,
scroll: { x: '100%' },
selectable: false,
heightUsed: 374,
},
(item) => ({
...item,
type: t(operationTypeOptions.find((e) => e.value === item.type)?.label || ''),
createTime: dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss'),
updateTime: dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss'),
})
);
function loadHistory() {
setLoadListParams({
projectId: appStore.currentProjectId,
sourceId: props.sourceId,
modules: ['API_TEST_MANAGEMENT_CASE'],
types: typeFilter.value.length === Object.keys(ChangeHistoryStatusFilters).length ? undefined : typeFilter.value,
});
loadList();
}
function handleFilterHidden(val: boolean) {
if (!val) {
loadHistory();
}
}
onBeforeMount(() => {
if (hasAnyPermission(['PROJECT_API_DEFINITION_CASE:READ', 'PROJECT_API_DEFINITION_CASE:READ+UPDATE'])) {
loadHistory();
}
});
// async function recover(record: any) {
// try {
// await recovergetApiCaseChangeHistory({
// id: record.id,
// sourceId: props.sourceId,
// });
// } catch (error) {
// // eslint-disable-next-line no-console
// console.log(error);
// }
// }
</script>
<style lang="less" scoped>
.history-container {
@apply h-full overflow-y-auto;
.ms-scroll-bar();
}
</style>

View File

@ -0,0 +1,132 @@
<template>
<div class="history-container">
<div class="history-table-before">
<span class="text-[var(--color-text-1)]">{{ t('case.detail.dependency.list') }}</span>
<a-input-search
v-model:model-value="keyword"
:placeholder="t('apiTestManagement.quoteSearchPlaceholder')"
allow-clear
class="mr-[8px] w-[240px]"
@search="loadQuoteList"
@press-enter="loadQuoteList"
/>
</div>
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
<template #id="{ record }">
<MsButton type="text">{{ record.id }}</MsButton>
</template>
</ms-base-table>
</div>
</template>
<script setup lang="ts">
import MsButton from '@/components/pure/ms-button/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import { getApiCaseDependency } from '@/api/modules/api-test/management';
import { useI18n } from '@/hooks/useI18n';
const props = defineProps<{
sourceId: string | number;
}>();
const { t } = useI18n();
const keyword = ref('');
const quoteLocaleMap = {
COPY: 'common.copy',
REF: 'apiTestManagement.quote',
};
const resourceLocaleMap = {
API: 'case.detail.resource.api',
};
const columns: MsTableColumn = [
{
title: 'ID',
dataIndex: 'num',
slotName: 'num',
sortIndex: 1,
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
width: 150,
},
{
title: 'apiTestManagement.resourceName',
dataIndex: 'resourceName',
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
showTooltip: true,
width: 150,
},
{
title: 'apiTestManagement.resourceType',
dataIndex: 'resourceType',
width: 100,
},
{
title: 'apiTestManagement.quoteType',
dataIndex: 'refType',
width: 100,
},
{
title: 'apiTestManagement.belongOrg',
dataIndex: 'organizationName',
showTooltip: true,
width: 150,
},
{
title: 'apiTestManagement.belongProject',
dataIndex: 'projectName',
showTooltip: true,
width: 150,
},
];
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(
getApiCaseDependency,
{
columns,
scroll: { x: '100%' },
selectable: false,
heightUsed: 374,
},
(item) => ({
...item,
refType: t(quoteLocaleMap[item.refType] || ''),
resourceType: t(resourceLocaleMap[item.resourceType] || ''),
})
);
function loadQuoteList() {
setLoadListParams({
keyword: keyword.value,
resourceId: props.sourceId,
});
loadList();
}
onBeforeMount(() => {
loadQuoteList();
});
</script>
<style lang="less" scoped>
.history-container {
@apply h-full overflow-y-auto;
.ms-scroll-bar();
}
.history-table-before {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
</style>

View File

@ -0,0 +1,216 @@
<template>
<div class="history-container">
<div class="history-table-before">
<span class="text-[var(--color-text-1)]">{{ t('case.detail.execute.history.list') }}</span>
<a-input-search
v-model:model-value="keyword"
:placeholder="t('apiTestManagement.quoteSearchPlaceholder')"
allow-clear
class="mr-[8px] w-[240px]"
@search="loadExecuteList"
@press-enter="loadExecuteList"
/>
</div>
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
<template #triggerModeFilter="{ columnConfig }">
<a-trigger
v-model:popup-visible="triggerModeFilterVisible"
trigger="click"
@popup-visible-change="handleFilterHidden"
>
<MsButton type="text" class="arco-btn-text--secondary" @click="triggerModeFilterVisible = true">
<div class="font-medium">
{{ t(columnConfig.title as string) }}
</div>
<icon-down :class="triggerModeFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
</MsButton>
<template #content>
<div class="arco-table-filters-content">
<div class="flex items-center justify-center px-[6px] py-[2px]">
<a-checkbox-group v-model:model-value="triggerModeListFilters" direction="vertical" size="small">
<a-checkbox v-for="(key, value) of TriggerModeLabel" :key="key" :value="value">
<div class="font-medium">{{ t(key) }}</div>
</a-checkbox>
</a-checkbox-group>
</div>
</div>
</template>
</a-trigger>
</template>
<template #statusFilter="{ columnConfig }">
<a-trigger
v-model:popup-visible="statusFilterVisible"
trigger="click"
@popup-visible-change="handleFilterHidden"
>
<MsButton type="text" class="arco-btn-text--secondary" @click="statusFilterVisible = true">
{{ t(columnConfig.title as string) }}
<icon-down :class="statusFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
</MsButton>
<template #content>
<div class="arco-table-filters-content">
<div class="flex items-center justify-center px-[6px] py-[2px]">
<a-checkbox-group v-model:model-value="statusFilters" direction="vertical" size="small">
<a-checkbox v-for="val of Object.values(ExecuteStatusFilters)" :key="val" :value="val">
<execute-status :status="val" />
</a-checkbox>
</a-checkbox-group>
</div>
</div>
</template>
</a-trigger>
</template>
<template #triggerMode="{ record }">
<span>{{ t(TriggerModeLabel[record.triggerMode]) }}</span>
</template>
<template #status="{ record }">
<execute-status :status="record.status" />
</template>
<template #operation="{ record }">
<MsButton class="!mr-0" @click="showResult(record)"
>{{ t('apiScenario.executeHistory.execution.operation') }}
</MsButton>
</template>
</ms-base-table>
</div>
</template>
<script setup lang="ts">
import dayjs from 'dayjs';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import ExecuteStatus from '@/views/api-test/scenario/components/executeStatus.vue';
import { getApiCaseExecuteHistory } from '@/api/modules/api-test/management';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import { ApiCaseExecuteHistoryItem } from '@/models/apiTest/management';
import { ExecuteStatusFilters } from '@/enums/apiEnum';
import { TriggerModeLabel } from '@/enums/reportEnum';
const triggerModeListFilters = ref<string[]>(Object.keys(TriggerModeLabel));
const triggerModeFilterVisible = ref(false);
const statusFilterVisible = ref(false);
const statusFilters = ref(Object.keys(ExecuteStatusFilters));
const props = defineProps<{
sourceId: string | number;
moduleType: string;
}>();
const appStore = useAppStore();
const { t } = useI18n();
const keyword = ref('');
const columns: MsTableColumn = [
{
title: 'apiTestManagement.order',
dataIndex: 'num',
slotName: 'num',
sortIndex: 1,
width: 150,
},
{
title: 'apiTestManagement.executeMethod',
dataIndex: 'triggerMode',
slotName: 'triggerMode',
titleSlotName: 'triggerModeFilter',
showTooltip: true,
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
width: 150,
},
{
title: 'apiTestManagement.executeResult',
dataIndex: 'status',
slotName: 'status',
titleSlotName: 'statusFilter',
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
width: 100,
},
{
title: 'apiTestManagement.taskOperator',
dataIndex: 'operationUser',
width: 100,
},
{
title: 'apiTestManagement.taskOperationTime',
dataIndex: 'startTime',
showTooltip: true,
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
width: 150,
},
{
title: 'common.operation',
slotName: 'operation',
dataIndex: 'operation',
fixed: 'right',
showInTable: true,
showDrag: false,
width: 150,
},
];
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(
getApiCaseExecuteHistory,
{
columns,
scroll: { x: '100%' },
selectable: false,
heightUsed: 374,
},
(item) => ({
...item,
startTime: dayjs(item.startTime).format('YYYY-MM-DD HH:mm:ss'),
})
);
function loadExecuteList() {
setLoadListParams({
projectId: appStore.currentProjectId,
keyword: keyword.value,
id: props.sourceId,
filter: {
triggerMode: triggerModeListFilters.value,
status: statusFilters.value,
},
});
loadList();
}
function handleFilterHidden(val: boolean) {
if (!val) {
loadExecuteList();
}
}
function showResult(record: ApiCaseExecuteHistoryItem) {}
onBeforeMount(() => {
loadExecuteList();
});
</script>
<style lang="less" scoped>
.history-container {
@apply h-full overflow-y-auto;
.ms-scroll-bar();
}
.history-table-before {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
</style>

View File

@ -18,6 +18,7 @@ export default {
'apiTestManagement.moveSearchTip': 'Please enter the module name to search', 'apiTestManagement.moveSearchTip': 'Please enter the module name to search',
'apiTestManagement.noMatchModule': 'No matching module/api yet', 'apiTestManagement.noMatchModule': 'No matching module/api yet',
'apiTestManagement.execute': 'Execute', 'apiTestManagement.execute': 'Execute',
'apiTestManagement.executeMethod': 'Execute method',
'apiTestManagement.recycle.batchRecover': 'Recover', 'apiTestManagement.recycle.batchRecover': 'Recover',
'apiTestManagement.recycle.recoveredSuccessfully': 'recovery was successful', 'apiTestManagement.recycle.recoveredSuccessfully': 'recovery was successful',
'apiTestManagement.recycle.batchCleanOut': 'Completely delete', 'apiTestManagement.recycle.batchCleanOut': 'Completely delete',
@ -133,6 +134,7 @@ export default {
'apiTestManagement.reference': 'Reference', 'apiTestManagement.reference': 'Reference',
'apiTestManagement.dependencies': 'Dependency', 'apiTestManagement.dependencies': 'Dependency',
'apiTestManagement.changeHistory': 'Change history', 'apiTestManagement.changeHistory': 'Change history',
'apiTestManagement.executeHistory': 'Execute history',
'apiTestManagement.requestParams': 'Request parameters', 'apiTestManagement.requestParams': 'Request parameters',
'apiTestManagement.responseContent': 'Response content', 'apiTestManagement.responseContent': 'Response content',
'apiTestManagement.requestHeader': 'Request header', 'apiTestManagement.requestHeader': 'Request header',
@ -144,6 +146,7 @@ export default {
'apiTestManagement.historyListTip': 'apiTestManagement.historyListTip':
'View and compare historical changes. According to the rules set by the administrator, the change history data will be automatically deleted.', 'View and compare historical changes. According to the rules set by the administrator, the change history data will be automatically deleted.',
'apiTestManagement.changeOrder': 'Change serial number', 'apiTestManagement.changeOrder': 'Change serial number',
'apiTestManagement.order': 'Serial number',
'apiTestManagement.type': 'Type', 'apiTestManagement.type': 'Type',
'apiTestManagement.recover': 'Recover', 'apiTestManagement.recover': 'Recover',
'apiTestManagement.quote': 'Quote', 'apiTestManagement.quote': 'Quote',
@ -200,4 +203,7 @@ export default {
'case.detail.operator': 'operator', 'case.detail.operator': 'operator',
'case.detail.tableColumnUpdateTime': 'UpdateTime', 'case.detail.tableColumnUpdateTime': 'UpdateTime',
'case.detail.execute.success': 'Execute success', 'case.detail.execute.success': 'Execute success',
'case.detail.execute.history.list': 'Execution history list',
'case.detail.dependency.list': 'Reference relationship list',
'case.detail.resource.api': 'API',
}; };

View File

@ -18,6 +18,7 @@ export default {
'apiTestManagement.moveSearchTip': '请输入模块名称搜索', 'apiTestManagement.moveSearchTip': '请输入模块名称搜索',
'apiTestManagement.noMatchModule': '暂无匹配的模块/接口', 'apiTestManagement.noMatchModule': '暂无匹配的模块/接口',
'apiTestManagement.execute': '执行', 'apiTestManagement.execute': '执行',
'apiTestManagement.executeMethod': '执行方式',
'apiTestManagement.recycle.batchRecover': '恢复', 'apiTestManagement.recycle.batchRecover': '恢复',
'apiTestManagement.recycle.recoveredSuccessfully': '恢复成功', 'apiTestManagement.recycle.recoveredSuccessfully': '恢复成功',
'apiTestManagement.recycle.batchCleanOut': '彻底删除', 'apiTestManagement.recycle.batchCleanOut': '彻底删除',
@ -126,6 +127,7 @@ export default {
'apiTestManagement.reference': '引用关系', 'apiTestManagement.reference': '引用关系',
'apiTestManagement.dependencies': '依赖关系', 'apiTestManagement.dependencies': '依赖关系',
'apiTestManagement.changeHistory': '变更历史', 'apiTestManagement.changeHistory': '变更历史',
'apiTestManagement.executeHistory': '执行历史',
'apiTestManagement.requestParams': '请求参数', 'apiTestManagement.requestParams': '请求参数',
'apiTestManagement.responseContent': '响应内容', 'apiTestManagement.responseContent': '响应内容',
'apiTestManagement.requestHeader': '请求头', 'apiTestManagement.requestHeader': '请求头',
@ -136,6 +138,7 @@ export default {
'apiTestManagement.apiNameRequired': '接口名称不能为空', 'apiTestManagement.apiNameRequired': '接口名称不能为空',
'apiTestManagement.historyListTip': '查看、对比历史修改,根据管理员设置规则,变更历史数据将自动删除', 'apiTestManagement.historyListTip': '查看、对比历史修改,根据管理员设置规则,变更历史数据将自动删除',
'apiTestManagement.changeOrder': '变更序号', 'apiTestManagement.changeOrder': '变更序号',
'apiTestManagement.order': '序号',
'apiTestManagement.type': '类型', 'apiTestManagement.type': '类型',
'apiTestManagement.recover': '恢复', 'apiTestManagement.recover': '恢复',
'apiTestManagement.quote': '引用', 'apiTestManagement.quote': '引用',
@ -192,4 +195,7 @@ export default {
'case.detail.operator': '操作人', 'case.detail.operator': '操作人',
'case.detail.tableColumnUpdateTime': '更新时间', 'case.detail.tableColumnUpdateTime': '更新时间',
'case.detail.execute.success': '执行成功', 'case.detail.execute.success': '执行成功',
'case.detail.execute.history.list': '执行历史列表',
'case.detail.dependency.list': '引用关系列表',
'case.detail.resource.api': '接口测试',
}; };