feat(接口测试): 增加复制的查看引用关系

--story=1010958 --user=郭雨琦
https://www.tapd.cn/55049933/prong/stories/view/1155049933001010958
This commit is contained in:
guoyuqi 2023-01-17 11:12:28 +08:00 committed by xiaomeinvG
parent 4a045e276b
commit c3ca90e4b3
6 changed files with 346 additions and 79 deletions

View File

@ -40,4 +40,6 @@ public class ApiScenarioRequest extends BaseQueryRequest {
//测试计划关联场景过滤掉步骤为0的场景
private String stepTotal;
//场景引用类型
private String refType;
}

View File

@ -462,9 +462,11 @@
<select id="selectReference" resultType="io.metersphere.api.dto.automation.ApiScenarioDTO">
select a.id, a.name , a.num,p.name AS projectName, a.update_time, a.create_time,
w.name AS workspaceName, p.workspace_id AS workspaceId, p.id AS projectId from api_scenario a
w.name AS workspaceName, p.workspace_id AS workspaceId, p.id AS projectId, v.name AS versionName, a.version_id
from api_scenario a
LEFT JOIN project p ON a.project_id = p.id
LEFT JOIN `workspace` w ON p.workspace_id = w.id
LEFT JOIN `workspace` w ON p.workspace_id = w.id
LEFT JOIN `project_version` v ON a.version_id = v.id
<where>
a.status != 'Trash'
<if test="request.ids != null and request.ids.size() > 0">
@ -489,6 +491,12 @@
#{value}
</foreach>
</when>
<when test="key=='version_id'">
and a.version_id in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
</choose>
</if>
</foreach>

View File

@ -1024,7 +1024,7 @@ public class ApiDefinitionService {
public void getReferenceIds(ApiScenarioRequest request) {
ApiScenarioReferenceIdExample example = new ApiScenarioReferenceIdExample();
example.createCriteria().andReferenceIdEqualTo(request.getId()).andReferenceTypeEqualTo(MsTestElementConstants.REF.name());
example.createCriteria().andReferenceIdEqualTo(request.getId()).andReferenceTypeEqualTo(request.getRefType());
List<ApiScenarioReferenceId> scenarioReferenceIds = apiScenarioReferenceIdMapper.selectByExample(example);
List<String> scenarioIds = scenarioReferenceIds.stream().map(ApiScenarioReferenceId::getApiScenarioId).collect(Collectors.toList());
request.setIds(scenarioIds);

View File

@ -0,0 +1,54 @@
<template>
<el-dropdown class="scenario-ext-btn">
<el-link type="primary" :underline="false">
<el-icon class="el-icon-more"></el-icon>
</el-link>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="(item,index) in dropdownItems" :key="index" @click.native.stop="click(item)"
:disabled="isDisable(item)"
:command="item.value"
v-modules="item.modules"
>{{ item.name }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
import {hasPermissions} from 'metersphere-frontend/src/utils/permission';
export default {
name: "TableExtendBtns",
props: {
dropdownItems: Array,
row: Object,
},
methods: {
click(btn) {
if (btn.exec instanceof Function) {
btn.exec(this.row);
}
},
isDisable(item) {
if (item.isDisable) {
if (item.isDisable instanceof Function) {
return item.isDisable();
} else {
return item.isDisable;
}
}
if (item.permissions && item.permissions.length > 0) {
return !hasPermissions(...item.permissions);
}
return false;
}
}
}
</script>
<style scoped>
.scenario-ext-btn {
margin-left: 10px;
}
</style>

View File

@ -201,15 +201,20 @@
:field="item"
min-width="120px"
:fields-width="fieldsWidth"
:label="$t('api_test.definition.api_case_passing_rate')" />
:label="$t('api_test.definition.api_case_passing_rate')"/>
<ms-table-column
prop="description"
:field="item"
min-width="120px"
:fields-width="fieldsWidth"
:label="$t('commons.description')" />
:label="$t('commons.description')"/>
</span>
<template v-if="!trashEnable" v-slot:opt-behind="scope">
<table-extend-btns
:dropdown-items="dropdownItems"
:row="scope.row"/>
</template>
</ms-table>
<ms-table-pagination
:change="initTable"
@ -224,16 +229,18 @@
@batchEdit="batchEdit"
:data-count="$refs.table ? $refs.table.selectDataCounts : 0"
:typeArr="typeArr"
:value-arr="valueArr" />
:value-arr="valueArr"/>
<!--从指定版本复制数据-->
<version-selector @handleSave="handleCopyDataFromVersion" ref="versionSelector" />
<version-selector @handleSave="handleCopyDataFromVersion" ref="versionSelector"/>
<!--高级搜索-->
<ms-table-adv-search-bar :condition.sync="condition" :showLink="false" ref="searchBar" @search="search" />
<case-batch-move @refresh="initTable" @moveSave="moveSave" ref="testCaseBatchMove" />
<ms-table-adv-search-bar :condition.sync="condition" :showLink="false" ref="searchBar" @search="search"/>
<!--查看引用-->
<ms-show-reference ref="viewRef" :show-plan="false" :is-has-ref="false" api-type="API"/>
<case-batch-move @refresh="initTable" @moveSave="moveSave" ref="testCaseBatchMove"/>
<mx-relationship-graph-drawer v-xpack :graph-data="graphData" ref="relationshipGraph" />
<mx-relationship-graph-drawer v-xpack :graph-data="graphData" ref="relationshipGraph"/>
<!-- 删除接口提示 -->
<list-item-delete-confirm ref="apiDeleteConfirm" @handleDelete="_handleDelete" />
<list-item-delete-confirm ref="apiDeleteConfirm" @handleDelete="_handleDelete"/>
</span>
</template>
@ -290,12 +297,14 @@ import {
initCondition,
} from 'metersphere-frontend/src/utils/tableUtils';
import HeaderLabelOperate from 'metersphere-frontend/src/components/head/HeaderLabelOperate';
import { Body } from '@/business/definition/model/ApiTestModel';
import { getGraphByCondition } from '@/api/graph';
import {Body} from '@/business/definition/model/ApiTestModel';
import {getGraphByCondition} from '@/api/graph';
import ListItemDeleteConfirm from 'metersphere-frontend/src/components/ListItemDeleteConfirm';
import MsSearch from 'metersphere-frontend/src/components/search/MsSearch';
import { buildNodePath } from 'metersphere-frontend/src/model/NodeTree';
import {buildNodePath} from 'metersphere-frontend/src/model/NodeTree';
import VersionSelector from '@/business/definition/components/version/VersionSelector';
import TableExtendBtns from "@/business/definition/components/complete/table/TableExtendBtns";
import MsShowReference from "@/business/definition/components/reference/ShowReference";
export default {
name: 'ApiList',
@ -321,6 +330,8 @@ export default {
MsTableColumn,
MsSearch,
VersionSelector,
TableExtendBtns,
MsShowReference,
MsApiReportStatus: () => import('../../../automation/report/ApiReportStatus'),
MxRelationshipGraphDrawer: () => import('metersphere-frontend/src/components/graph/MxRelationshipGraphDrawer'),
},
@ -401,13 +412,6 @@ export default {
exec: this.editApi,
permissions: ['PROJECT_API_DEFINITION:READ+EDIT_API'],
},
{
tip: 'CASE',
exec: this.handleTestCase,
isDivButton: true,
type: 'primary',
permissions: ['PROJECT_API_DEFINITION:READ'],
},
{
tip: this.$t('commons.delete'),
exec: this.handleDelete,
@ -437,11 +441,25 @@ export default {
permissions: ['PROJECT_API_DEFINITION:READ+DELETE_API'],
},
],
dropdownItems: [
{
name: this.$t('api_test.automation.view_ref'),
value: "ref",
permissions: ['PROJECT_API_DEFINITION:READ'],
exec: this.showCaseRef,
},
{
name: this.$t('commons.view') + "CASE",
value: "case",
permissions: ['PROJECT_API_DEFINITION:READ'],
exec: this.handleTestCase,
}
],
typeArr: [
{ id: 'status', name: this.$t('api_test.definition.api_status') },
{ id: 'method', name: this.$t('api_test.definition.api_type') },
{ id: 'userId', name: this.$t('api_test.definition.api_principal') },
{ id: 'tags', name: this.$t('commons.tag') },
{id: 'status', name: this.$t('api_test.definition.api_status')},
{id: 'method', name: this.$t('api_test.definition.api_type')},
{id: 'userId', name: this.$t('api_test.definition.api_principal')},
{id: 'tags', name: this.$t('commons.tag')},
],
statusFilters: [
{
@ -1024,6 +1042,12 @@ export default {
this.$emit('handleTestCase', api);
// this.$refs.caseList.open(this.selectApi);
},
showCaseRef(row) {
let param = {};
Object.assign(param, row);
param.moduleId = undefined;
this.$refs.viewRef.open(param, 'API');
},
handleDelete(api) {
if (this.trashEnable) {
delDefinition(api.id).then(() => {

View File

@ -1,9 +1,12 @@
<template>
<el-dialog :visible.sync="isVisible" class="advanced-item-value" width="50%">
<el-tabs tab-position="top" style="width: 100%" v-model="activeName" @tab-click="handleClick">
<el-tab-pane :label="$t('api_test.automation.scenario_ref')" name="scenario">
<el-tab-pane
:label="$t('api_test.home_page.api_details_card.title')+$t('api_test.home_page.test_scene_details_card.title')"
name="scenario">
<ms-table
:data="scenarioData"
v-if="!isHasRef"
:data="scenarioCopyData"
style="width: 100%"
:screen-height="screenHeight"
:total="total"
@ -11,10 +14,10 @@
:enable-selection="false"
@refresh="search"
:condition="condition">
<ms-table-column prop="num" label="ID" sortable width="80" />
<ms-table-column prop="num" label="ID" sortable width="80"/>
<ms-table-column prop="name" :label="$t('api_report.scenario_name')" width="200">
<template v-slot:default="{ row }">
<el-link @click="openScenario(row)" style="cursor: pointer">{{ row.name }} </el-link>
<el-link @click="openScenario(row)" style="cursor: pointer">{{ row.name }}</el-link>
</template>
</ms-table-column>
<ms-table-column
@ -31,10 +34,101 @@
column-key="projectId"
width="200">
</ms-table-column>
<ms-table-column
prop="versionName"
:label="$t('project.version.name')"
width="200"
column-key="versionId"
:filters="versionFilters">
</ms-table-column>
</ms-table>
<ms-tab-button
v-if="isHasRef"
:active-dom.sync="activeDom"
:left-content="$t('api_test.scenario.clone')"
:right-content="$t('api_test.scenario.reference')"
@changeTab="changeTab"
:middle-button-enable="false">
<ms-table
v-if="activeDom === 'right'"
:data="scenarioRefData"
style="width: 100%"
:screen-height="screenHeight"
:total="total"
:page-size="pageSize"
:enable-selection="false"
@refresh="search"
:condition="condition">
<ms-table-column prop="num" label="ID" sortable width="80"/>
<ms-table-column prop="name" :label="$t('api_report.scenario_name')" width="200">
<template v-slot:default="{ row }">
<el-link @click="openScenario(row)" style="cursor: pointer">{{ row.name }}</el-link>
</template>
</ms-table-column>
<ms-table-column
prop="workspaceName"
:label="$t('group.belong_workspace')"
width="200"
column-key="workspaceId"
:filters="workspaceFilters">
</ms-table-column>
<ms-table-column
prop="projectName"
:label="$t('group.belong_project')"
:filters="projectFilters"
column-key="projectId"
width="200">
</ms-table-column>
<ms-table-column
prop="versionName"
:label="$t('project.version.name')"
width="200"
column-key="versionId"
:filters="versionFilters">
</ms-table-column>
</ms-table>
<ms-table
v-if="activeDom === 'left'"
:data="scenarioCopyData"
style="width: 100%"
:screen-height="screenHeight"
:total="total"
:page-size="pageSize"
:enable-selection="false"
@refresh="search"
:condition="condition">
<ms-table-column prop="num" label="ID" sortable width="80"/>
<ms-table-column prop="name" :label="$t('api_report.scenario_name')" width="200">
<template v-slot:default="{ row }">
<el-link @click="openScenario(row)" style="cursor: pointer">{{ row.name }}</el-link>
</template>
</ms-table-column>
<ms-table-column
prop="workspaceName"
:label="$t('group.belong_workspace')"
width="200"
column-key="workspaceId"
:filters="workspaceFilters">
</ms-table-column>
<ms-table-column
prop="projectName"
:label="$t('group.belong_project')"
:filters="projectFilters"
column-key="projectId"
width="200">
</ms-table-column>
<ms-table-column
prop="versionName"
:label="$t('project.version.name')"
width="200"
column-key="versionId"
:filters="versionFilters">
</ms-table-column>
</ms-table>
</ms-tab-button>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.automation.plan_ref')" name="testPlan">
<el-tab-pane v-if="showPlan" :label="$t('api_test.home_page.running_task_list.test_plan_schedule')"
name="testPlan">
<ms-table
:data="planData"
style="width: 100%"
@ -72,17 +166,24 @@
import MsTablePagination from 'metersphere-frontend/src/components/pagination/TablePagination';
import { apiProjectRelated, getOwnerProjectIds, getProject, getUserWorkspace, projectRelated } from '@/api/project';
import { getCurrentProjectID, getCurrentUserId, getCurrentWorkspaceId } from 'metersphere-frontend/src/utils/token';
import { getUUID } from 'metersphere-frontend/src/utils';
import { getDefinitionReference, getPlanReference } from '@/api/definition';
import {getUUID} from 'metersphere-frontend/src/utils';
import {hasLicense} from 'metersphere-frontend/src/utils/permission';
import {getDefinitionReference, getPlanReference} from '@/api/definition';
import MsTable from 'metersphere-frontend/src/components/table/MsTable';
import MsTableColumn from 'metersphere-frontend/src/components/table/MsTableColumn';
import MsTabButton from '@/business/commons/MsTabs';
import {getProjectVersions} from "@/api/xpack";
export default {
name: 'ShowReference',
data() {
return {
isVisible: false,
scenarioData: [],
isCopy: true,
showTextColor: "showTextColor",
unShowTextColor: "unShowTextColor",
scenarioRefData: [],
scenarioCopyData: [],
planData: [],
currentPage: 1,
pageSize: 10,
@ -92,19 +193,36 @@ export default {
workspaceList: [],
workspaceFilters: [],
projectFilters: [],
versionFilters: [],
projectList: [],
screenHeight: 'calc(100vh - 400px)',
condition: {},
type: '',
projectPlanFilters: [],
activeDom: 'left',
};
},
props: {
apiType: String,
showPlan: {
type: Boolean,
default: true,
},
isHasRef: {
type: Boolean,
default: true,
},
},
components: {
MsTablePagination,
MsTable,
MsTableColumn,
MsTabButton
},
watch: {
activeDom() {
this.getScenarioData();
},
activeName(o) {
if (o) {
this.init();
@ -126,6 +244,23 @@ export default {
this.projectList = res.data ? res.data : [];
});
},
getVersionOptions(currentVersion) {
if (hasLicense()) {
getProjectVersions(getCurrentProjectID()).then((response) => {
if (currentVersion) {
this.versionFilters = response.data
.filter((u) => u.id === currentVersion)
.map((u) => {
return {text: u.name, value: u.id};
});
} else {
this.versionFilters = response.data.map((u) => {
return {text: u.name, value: u.id};
});
}
});
}
},
/**
* 操作方法
*/
@ -134,7 +269,8 @@ export default {
this.pageSize = 10;
this.total = 0;
this.condition = {};
this.scenarioData = [];
this.scenarioRefData = [];
this.scenarioCopyData = [];
this.planData = [];
},
open(row, type) {
@ -142,6 +278,7 @@ export default {
this.init();
this.getUserProjectList();
this.getWorkSpaceList();
this.getVersionOptions();
this.isVisible = true;
this.scenarioId = row.id;
this.type = type;
@ -150,6 +287,55 @@ export default {
close() {
this.isVisible = false;
},
getReferenceData(condition) {
getDefinitionReference(this.currentPage, this.pageSize, condition).then((res) => {
let data = res.data || [];
this.total = data.itemCount || 0;
if (this.workspaceList) {
if (this.workspaceFilters.length === 0) {
this.workspaceFilters = this.workspaceList
.filter((workspace) => {
return data.listObject.find((i) => i.workspaceId === workspace.id);
})
.map((e) => {
return {text: e.name, value: e.id};
});
}
let workspaceIds = [];
if (
this.condition.filters &&
this.condition.filters.workspace_id &&
this.condition.filters.workspace_id.length > 0
) {
this.condition.filters.workspace_id.map((item) => {
workspaceIds.push(item);
});
} else {
this.workspaceFilters.map((item) => {
workspaceIds.push(item.value);
});
}
apiProjectRelated({
userId: getCurrentUserId(),
workspaceIds: workspaceIds,
}).then((res) => {
this.projectFilters = res.data
.filter((project) => {
return data.listObject.find((i) => i.projectId === project.id);
})
.map((e) => {
return {text: e.name, value: e.id};
});
});
}
if (this.activeDom === 'left') {
this.scenarioCopyData = data.listObject || [];
} else {
this.scenarioRefData = data.listObject || [];
}
});
},
search(row) {
this.condition.id = this.scenarioId;
if (row) {
@ -160,49 +346,14 @@ export default {
this.condition.workspaceId = getCurrentWorkspaceId();
this.condition.scenarioType = this.type;
if (this.activeName === 'scenario') {
getDefinitionReference(this.currentPage, this.pageSize, this.condition).then((res) => {
let data = res.data || [];
this.total = data.itemCount || 0;
if (this.workspaceList) {
if (this.workspaceFilters.length === 0) {
this.workspaceFilters = this.workspaceList
.filter((workspace) => {
return data.listObject.find((i) => i.workspaceId === workspace.id);
})
.map((e) => {
return { text: e.name, value: e.id };
});
}
let workspaceIds = [];
if (
this.condition.filters &&
this.condition.filters.workspace_id &&
this.condition.filters.workspace_id.length > 0
) {
this.condition.filters.workspace_id.map((item) => {
workspaceIds.push(item);
});
} else {
this.workspaceFilters.map((item) => {
workspaceIds.push(item.value);
});
}
apiProjectRelated({
userId: getCurrentUserId(),
workspaceIds: workspaceIds,
}).then((res) => {
this.projectFilters = res.data
.filter((project) => {
return data.listObject.find((i) => i.projectId === project.id);
})
.map((e) => {
return { text: e.name, value: e.id };
});
});
}
this.scenarioData = data.listObject || [];
});
if (!this.isHasRef) {
this.condition.refType = "Copy"
this.activeDom = 'left'
} else {
this.condition.refType = "REF"
this.activeDom = 'right'
}
this.getReferenceData(this.condition);
} else {
getPlanReference(this.currentPage, this.pageSize, this.condition).then((res) => {
let data = res.data || [];
@ -212,7 +363,7 @@ export default {
return data.listObject.find((i) => i.projectId === project.id);
})
.map((e) => {
return { text: e.name, value: e.id };
return {text: e.name, value: e.id};
});
this.planData = data.listObject || [];
});
@ -279,9 +430,37 @@ export default {
});
window.open(automationData.href, '_blank');
},
getScenarioData() {
if (this.activeDom === 'left') {
this.condition.refType = 'Copy'
this.getReferenceData(this.condition);
} else {
this.condition.refType = 'REF'
this.getReferenceData(this.condition);
}
},
changeTab(active) {
this.activeDom = active;
},
},
};
</script>
<style type="text/css" scoped>
.showTextColor {
color: var(--primary_color);
cursor: pointer;
}
.unShowTextColor {
cursor: pointer;
}
.changeTap {
margin: auto;
width: 50%;
text-align: center;
}
</style>
<style scoped>
:deep(.el-table__empty-block) {
padding-right: 0 !important;