feat(接口管理): 新增用例详情的引用执行变更记录的列表
This commit is contained in:
parent
10fb5fead9
commit
ae34ffe61f
|
@ -81,6 +81,7 @@ import {
|
|||
ApiCaseChangeHistoryParams,
|
||||
ApiCaseDependencyParams,
|
||||
ApiCaseDetail,
|
||||
ApiCaseExecuteHistoryItem,
|
||||
ApiCaseExecuteHistoryParams,
|
||||
ApiCasePageParams,
|
||||
ApiDefinitionBatchDeleteParams,
|
||||
|
@ -115,7 +116,6 @@ import {
|
|||
DragSortParams,
|
||||
ModuleTreeNode,
|
||||
MoveModules,
|
||||
TableQueryParams,
|
||||
TransferFileParams,
|
||||
} from '@/models/common';
|
||||
import { ResourcePoolItem } from '@/models/setting/resourcePool';
|
||||
|
@ -457,7 +457,7 @@ export function getEnvList(projectId: string) {
|
|||
|
||||
// 获取接口用例-执行历史
|
||||
export function getApiCaseExecuteHistory(data: ApiCaseExecuteHistoryParams) {
|
||||
return MSR.post({ url: GetExecuteHistoryUrl, data });
|
||||
return MSR.post<CommonList<ApiCaseExecuteHistoryItem>>({ url: GetExecuteHistoryUrl, data });
|
||||
}
|
||||
|
||||
// 获取接口用例-变更历史
|
||||
|
|
|
@ -70,7 +70,7 @@ export const GetCaseDetailUrl = '/api/case/get-detail'; // 获取接口用例详
|
|||
export const GetEnvListUrl = '/api/test/env-list'; // 接口测试-环境列表
|
||||
export const BatchExecuteCaseUrl = '/api/case/batch/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 GetChangeHistoryUrl = '/api/case/operation-history/page'; // 获取用例的依赖关系
|
||||
export const ToggleFollowCaseUrl = '/api/case/follow'; // 接口定义-关注/取消关注
|
||||
|
|
|
@ -278,3 +278,11 @@ export enum ExecuteStatusFilters {
|
|||
FAKE_ERROR = 'FAKE_ERROR',
|
||||
STOPPED = 'STOPPED',
|
||||
}
|
||||
|
||||
// 变更历史类型
|
||||
export enum ChangeHistoryStatusFilters {
|
||||
ADD = 'ADD',
|
||||
UPDATE = 'UPDATE',
|
||||
IMPORT = 'IMPORT',
|
||||
DELETE = 'DELETE',
|
||||
}
|
||||
|
|
|
@ -383,3 +383,15 @@ export interface ApiCaseChangeHistoryParams extends TableQueryParams {
|
|||
export interface ApiCaseDependencyParams extends TableQueryParams {
|
||||
resourceId: string;
|
||||
}
|
||||
|
||||
// 用例-执行历史-请求参数
|
||||
export interface ApiCaseExecuteHistoryItem {
|
||||
id: string;
|
||||
num: string;
|
||||
name: string;
|
||||
operationUser: string;
|
||||
createUser: string;
|
||||
startTime: number;
|
||||
status: string;
|
||||
triggerMode: string;
|
||||
}
|
||||
|
|
|
@ -50,12 +50,15 @@
|
|||
<detailTab :detail="caseDetail" :protocols="protocols" is-case />
|
||||
</a-tab-pane>
|
||||
<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 key="dependencies" :title="t('apiTestManagement.dependencies')" class="px-[18px] py-[16px]">
|
||||
</a-tab-pane> -->
|
||||
<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-tabs>
|
||||
</div>
|
||||
|
@ -72,11 +75,12 @@
|
|||
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
||||
import type { CaseLevel } from '@/components/business/ms-case-associate/types';
|
||||
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 apiMethodName from '@/views/api-test/components/apiMethodName.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 { toggleFollowCase } from '@/api/modules/api-test/management';
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -18,6 +18,7 @@ export default {
|
|||
'apiTestManagement.moveSearchTip': 'Please enter the module name to search',
|
||||
'apiTestManagement.noMatchModule': 'No matching module/api yet',
|
||||
'apiTestManagement.execute': 'Execute',
|
||||
'apiTestManagement.executeMethod': 'Execute method',
|
||||
'apiTestManagement.recycle.batchRecover': 'Recover',
|
||||
'apiTestManagement.recycle.recoveredSuccessfully': 'recovery was successful',
|
||||
'apiTestManagement.recycle.batchCleanOut': 'Completely delete',
|
||||
|
@ -133,6 +134,7 @@ export default {
|
|||
'apiTestManagement.reference': 'Reference',
|
||||
'apiTestManagement.dependencies': 'Dependency',
|
||||
'apiTestManagement.changeHistory': 'Change history',
|
||||
'apiTestManagement.executeHistory': 'Execute history',
|
||||
'apiTestManagement.requestParams': 'Request parameters',
|
||||
'apiTestManagement.responseContent': 'Response content',
|
||||
'apiTestManagement.requestHeader': 'Request header',
|
||||
|
@ -144,6 +146,7 @@ export default {
|
|||
'apiTestManagement.historyListTip':
|
||||
'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.order': 'Serial number',
|
||||
'apiTestManagement.type': 'Type',
|
||||
'apiTestManagement.recover': 'Recover',
|
||||
'apiTestManagement.quote': 'Quote',
|
||||
|
@ -200,4 +203,7 @@ export default {
|
|||
'case.detail.operator': 'operator',
|
||||
'case.detail.tableColumnUpdateTime': 'UpdateTime',
|
||||
'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',
|
||||
};
|
||||
|
|
|
@ -18,6 +18,7 @@ export default {
|
|||
'apiTestManagement.moveSearchTip': '请输入模块名称搜索',
|
||||
'apiTestManagement.noMatchModule': '暂无匹配的模块/接口',
|
||||
'apiTestManagement.execute': '执行',
|
||||
'apiTestManagement.executeMethod': '执行方式',
|
||||
'apiTestManagement.recycle.batchRecover': '恢复',
|
||||
'apiTestManagement.recycle.recoveredSuccessfully': '恢复成功',
|
||||
'apiTestManagement.recycle.batchCleanOut': '彻底删除',
|
||||
|
@ -126,6 +127,7 @@ export default {
|
|||
'apiTestManagement.reference': '引用关系',
|
||||
'apiTestManagement.dependencies': '依赖关系',
|
||||
'apiTestManagement.changeHistory': '变更历史',
|
||||
'apiTestManagement.executeHistory': '执行历史',
|
||||
'apiTestManagement.requestParams': '请求参数',
|
||||
'apiTestManagement.responseContent': '响应内容',
|
||||
'apiTestManagement.requestHeader': '请求头',
|
||||
|
@ -136,6 +138,7 @@ export default {
|
|||
'apiTestManagement.apiNameRequired': '接口名称不能为空',
|
||||
'apiTestManagement.historyListTip': '查看、对比历史修改,根据管理员设置规则,变更历史数据将自动删除',
|
||||
'apiTestManagement.changeOrder': '变更序号',
|
||||
'apiTestManagement.order': '序号',
|
||||
'apiTestManagement.type': '类型',
|
||||
'apiTestManagement.recover': '恢复',
|
||||
'apiTestManagement.quote': '引用',
|
||||
|
@ -192,4 +195,7 @@ export default {
|
|||
'case.detail.operator': '操作人',
|
||||
'case.detail.tableColumnUpdateTime': '更新时间',
|
||||
'case.detail.execute.success': '执行成功',
|
||||
'case.detail.execute.history.list': '执行历史列表',
|
||||
'case.detail.dependency.list': '引用关系列表',
|
||||
'case.detail.resource.api': '接口测试',
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue