fix(工作台): 接口CASE批量执行环境弹窗保持一致

--bug=1027073 --user=宋昌昌 【工作台】我的待办-接口用例-待完成/待更新-批量执行-未显示环境 https://www.tapd.cn/55049933/s/1390207
This commit is contained in:
song-cc-rock 2023-07-07 11:04:31 +08:00 committed by 刘瑞斌
parent 4d8f3b3fee
commit df2f4bb8a6
9 changed files with 1271 additions and 20 deletions

View File

@ -50,6 +50,10 @@ export function testCaseBatchRun(params) {
return post(CASE_URL+'batch/run', params);
}
export function getApiCaseEnvironments(param) {
return post(CASE_URL + '/get/env', param);
}
/*sync*/

View File

@ -1,5 +1,5 @@
import {get, post} from "metersphere-frontend/src/plugins/request"
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
export function listAllProject() {
return get("/project/listAll")
@ -38,3 +38,7 @@ export function getProjectConfig(projectId) {
let url = '/project_application/get/config/' + projectId;
return get(url);
}
export function getAll() {
return get('/project/listAll/' + getCurrentWorkspaceId());
}

View File

@ -207,6 +207,7 @@
<api-case-run-mode-with-env
:project-id="projectId"
:run-case-ids="runCaseIds"
@handleRunBatch="runBatch"
@close="initTable"
ref="batchRun"/>
@ -472,7 +473,8 @@ export default {
hasEditPermission: false,
store: {},
openUpdateRule: true,
showColum: false
showColum: false,
runCaseIds: []
};
},
props: {
@ -941,7 +943,10 @@ export default {
});
},
handleRunBatch() {
this.$refs.batchRun.open();
this.runCaseIds = Array.from(this.selectRows).map((row) => row.id);
this.$nextTick(() => {
this.$refs.batchRun.open();
});
},
runBatch(config) {
let obj = {};

View File

@ -7,18 +7,20 @@
:visible.sync="runModeVisible"
>
<div style="margin-bottom: 10px;">
<span class="ms-mode-span">{{ $t("commons.environment") }}</span>
<env-popover :project-ids="projectIds"
:placement="'bottom-start'"
:project-list="projectList"
:project-env-map="projectEnvListMap"
:environment-type.sync="runConfig.environmentType"
:group-id="runConfig.environmentGroupId"
@setEnvGroup="setEnvGroup"
@setProjectEnvMap="setProjectEnvMap"
@showPopover="showPopover"
:show-env-group="false"
ref="envPopover" class="env-popover"/>
<div>{{ $t('commons.environment') }}</div>
<env-select-popover
:project-ids="projectIds"
:project-list="projectList"
:case-id-env-name-map="caseIdEnvNameMap"
:environment-type.sync="runConfig.environmentType"
:is-scenario="false"
:has-option-group="true"
:project-env-map="projectEnvListMap"
:group-id="runConfig.environmentGroupId"
@setProjectEnvMap="setProjectEnvMap"
@setEnvGroup="setEnvGroup"
ref="envSelectPopover"
class="env-select-popover"></env-select-popover>
</div>
<div>
<span class="ms-mode-span">{{ $t("run_mode.title") }}</span>
@ -78,7 +80,7 @@
<script>
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
import EnvPopover from "@/business/module/environment/EnvPopover";
import EnvSelectPopover from "../environment/EnvSelectPopover";
import {strMapToObj} from "metersphere-frontend/src/utils";
import {ENV_TYPE} from "metersphere-frontend/src/utils/constants";
import {parseEnvironment} from "metersphere-frontend/src/model/EnvironmentModel";
@ -86,10 +88,11 @@ import { getOwnerProjects, getProjectConfig} from "@/api/project";
import {getTestResourcePools} from "@/api/test-resource-pool";
import {getEnvironmentByProjectId} from "metersphere-frontend/src/api/environment";
import { getCurrentProjectID } from 'metersphere-frontend/src/utils/token';
import { getApiCaseEnvironments } from "@/api/api";
export default {
name: "MsApiCaseRunModeWithEnv",
components: {EnvPopover, MsDialogFooter},
components: {EnvSelectPopover, MsDialogFooter},
data() {
return {
runModeVisible: false,
@ -108,15 +111,20 @@ export default {
projectEnvListMap: {},
projectList: [],
projectIds: new Set(),
caseIdEnvNameMap: {},
};
},
props: ['projectId'],
props: {
runCaseIds: Array,
projectId: String
},
methods: {
open() {
this.runModeVisible = true;
this.getResourcePools();
this.getWsProjects();
this.getDefaultResourcePool();
this.showPopover();
},
getDefaultResourcePool() {
getProjectConfig(getCurrentProjectID())
@ -189,9 +197,16 @@ export default {
})
},
showPopover() {
let currentProjectID = getCurrentProjectID();
this.projectIds.clear();
this.projectIds.add(this.projectId);
this.$refs.envPopover.openEnvSelect();
this.projectIds.add(currentProjectID);
getApiCaseEnvironments(this.runCaseIds).then((res) => {
let data = res.data;
if (data) {
this.caseIdEnvNameMap = data;
}
this.$refs.envSelectPopover.open();
});
},
},
};

View File

@ -0,0 +1,297 @@
<template>
<div>
<el-radio-group v-model="radio" style="width: 100%" @change="radioChange" class="radio-change">
<el-radio :label="ENV_TYPE.JSON">{{ $t('workspace.env_group.env_list') }}</el-radio>
<el-radio :label="ENV_TYPE.GROUP" v-if="isScenario">{{ $t('workspace.env_group.name') }}<i class="el-icon-tickets mode-span" @click="viewGroup"></i></el-radio>
</el-radio-group>
<div v-for="(pe, pIndex) in eventData" :key="pe.id" v-show="!radio || radio === ENV_TYPE.JSON">
<el-card shadow="never" style="margin-top: 8px; background: #f5f6f7; border-radius: 4px">
<i
@click="expandCard(pIndex)"
v-if="pe.expendStatus === 'close'"
class="el-icon-caret-right"
style="color: var(--primary_color)" />
<i @click="expandCard(pIndex)" v-else class="el-icon-caret-bottom" style="color: var(--primary_color)" />
<span class="project-name" :title="getProjectName(pe.id)"> {{ getProjectName(pe.id) }} </span><br />
<div v-if="pe.expendStatus === 'open'">
<el-radio-group
v-model="pe.envRadio"
style="width: 100%"
@change="envRadioChange(pe.envRadio, pIndex)"
class="radio-change">
<el-radio label="DEFAULT_ENV" style="margin-top: 7px">{{
$t('api_test.environment.default_environment')
}}</el-radio>
<el-radio label="CUSTOMIZE_ENV" style="margin-top: 7px">{{
$t('api_test.environment.choose_new_environment')
}}</el-radio>
</el-radio-group>
<el-tag
v-show="!pe.showEnvSelect"
v-for="(itemName, index) in selectedEnvName.get(pe.id)"
:key="index"
size="mini"
style="margin-left: 0; margin-right: 2px; margin-top: 8px"
>{{ itemName }}</el-tag
>
<el-select
v-show="pe.showEnvSelect"
v-model="pe['selectEnv']"
:placeholder="$t('api_test.environment.select_environment')"
style="margin-top: 8px; width: 100%"
size="small"
filterable
@change="chooseEnv">
<el-option
v-for="(environment, index) in pe.envs"
:key="index"
:label="environment.name"
:value="environment.id" />
</el-select>
</div>
</el-card>
</div>
<div v-show="radio === ENV_TYPE.GROUP ">
<div >
<el-select v-show="!hasOptionGroup" v-model="envGroupId" :placeholder="$t('workspace.env_group.select')" @change="chooseEnvGroup"
style="margin-top: 8px;width: 100%;" size="small">
<el-option v-for="(group, index) in groups" :key="index"
:disabled="group.disabled"
:label="group.name"
:value="group.id"/>
</el-select>
<el-select
v-show="hasOptionGroup"
v-model="envGroupId"
:placeholder="$t('workspace.env_group.select')"
style="margin-top: 8px;width: 100%;"
size="small"
@change="chooseEnvGroup"
clearable>
<el-option-group v-for="group in groups" :key="group.label" :label="group.label">
<el-option
v-for="item in group.options"
:key="item.name"
:label="item.name"
:disabled="item.disabled"
:value="item.id">
</el-option>
</el-option-group>
</el-select>
</div>
<el-dialog :visible="visible" append-to-body :title="$t('workspace.env_group.name')" @close="visible = false"
style="height: 800px;">
<template>
<environment-group style="overflow-y: auto;"
:screen-height="'350px'"
:read-only="true"
></environment-group>
</template>
</el-dialog>
</div>
<!-- 对环境组选项进行分类 可用不可用 -->
</div>
</template>
<script>
import { ENV_TYPE } from 'metersphere-frontend/src/utils/constants';
import { environmentGetALL,getEnvironmentOptions } from 'metersphere-frontend/src/api/environment';
import MsTag from 'metersphere-frontend/src/components/MsTag';
import { parseEnvironment } from 'metersphere-frontend/src/model/EnvironmentModel';
import { getEnvironments } from '@/api/environment';
import EnvironmentGroup from './common/EnvironmentGroupList';
import EnvGroupWithOption from "./common/EnvGroupWithOption";
export default {
name: 'EnvSelectPopover',
components: { EnvironmentGroup,MsTag,EnvGroupWithOption},
data() {
return {
radio: this.environmentType,
visible: false,
groups: [],
disabledGroups: [],
notDisabledGroups: [],
selectedEnvName: new Map(),
showEnvName: false,
eventData: [],
evnList: [],
selectEnvMap: new Map(),
envGroupId: this.groupId,
};
},
computed: {
ENV_TYPE() {
return ENV_TYPE;
},
},
props: {
projectIds: Set,
projectList: Array,
projectEnvMap: Object,
caseIdEnvNameMap: Object,
envMap: Map,
environmentType: String,
groupId: {
type: String,
default() {
return '';
},
},
isScenario: {
type: Boolean,
default: true,
},
hasOptionGroup: {
type: Boolean,
default() {
return false;
},
},
btnStyle: {
type: Object,
default() {
return { width: '360px' };
},
},
},
watch: {
groupId(val) {
this.envGroupId = val;
},
environmentType(val) {
this.radio = val;
},
},
methods: {
open() {
this.envGroupId = this.groupId;
this.initDefaultEnv();
this.getgroups();
},
chooseEnvGroup(envGroupId){
this.$emit("setEnvGroup", envGroupId);
},
radioChange(val) {
this.$emit('update:environmentType', val);
},
getProjectName(id) {
const project = this.projectList.find((p) => p.id === id);
return project ? project.name : '';
},
envRadioChange(val, index) {
this.eventData[index].envRadio = val;
this.eventData[index].showEnvSelect = this.eventData[index].envRadio === 'CUSTOMIZE_ENV';
},
viewGroup() {
this.visible = true;
},
getgroups() {
if (this.hasOptionGroup) {
getEnvironmentOptions({
projectIds: [...this.projectIds],
}).then((res) => {
let groups = res.data;
this.disabledGroups = groups.filter((group) => group.disabled === true);
this.notDisabledGroups = groups.filter((group) => group.disabled === false);
this.$set(this.groups, 0, {
label: this.$t('workspace.env_group.available_group'),
options: this.notDisabledGroups,
});
this.$set(this.groups, 1, {
label: this.$t('workspace.env_group.not_available_group'),
options: this.disabledGroups,
});
});
} else {
environmentGetALL().then((res) => {
let data = res.data;
this.groups = data ? data : [];
});
}
},
chooseEnv(val) {
let filter = this.evnList.filter((e) => e.id === val);
this.selectEnvMap.set(filter[0].projectId, val);
this.$emit('setProjectEnvMap', this.selectEnvMap);
},
initDefaultEnv() {
this.selectedEnvName = new Map();
this.evnList = [];
this.projectIds.forEach((d) => {
let item = {
id: d,
envs: [],
selectEnv: '',
envRadio: 'DEFAULT_ENV',
showEnvSelect: false,
expendStatus: 'open',
};
this.eventData.push(item);
getEnvironments(d).then((res) => {
let envs = res.data;
envs.forEach((environment) => {
parseEnvironment(environment);
});
//
let temp = this.eventData.find((dt) => dt.id === d);
temp.envs = envs;
envs.forEach((t) => {
this.evnList.push(t);
});
if (this.envMap && this.envMap.size > 0) {
let envId = this.envMap.get(id);
//
temp.selectEnv = envs.filter((e) => e.id === envId).length === 0 ? null : envId;
}
if (this.isScenario) {
if (this.projectEnvMap) {
let projectEnvMapElement = this.projectEnvMap[d];
if (projectEnvMapElement && projectEnvMapElement.length > 0) {
projectEnvMapElement.forEach((envId) => {
let filter = envs.filter((e) => e.id === envId);
if (!this.selectedEnvName.has(d)) {
let name = [];
name.push(filter[0].name);
this.selectedEnvName.set(d, name);
} else {
this.selectedEnvName.get(d).push(filter[0].name);
}
});
}
}
} else {
if (this.caseIdEnvNameMap) {
let envName = new Set();
for (let key in this.caseIdEnvNameMap) {
envName.add(this.caseIdEnvNameMap[key]);
}
this.selectedEnvName.set(d, envName);
}
}
});
});
},
expandCard(index) {
if (this.eventData[index].expendStatus === 'open') {
this.eventData[index].expendStatus = 'close';
} else {
this.eventData[index].expendStatus = 'open';
}
},
},
};
</script>
<style scoped>
.mode-span {
margin-left: 6px;
}
</style>
<style lang="scss" scoped>
.radio-change:deep(.el-radio__input.is-checked + .el-radio__label) {
color: #606266 !important;
}
</style>

View File

@ -0,0 +1,119 @@
<template>
<div v-loading="result">
<el-dialog
:close-on-click-modal="false"
:title="title"
:visible.sync="createVisible"
destroy-on-close
@close="handleClose"
width="60%">
<el-form :model="form" :rules="rules" ref="form" label-position="right" label-width="90px" size="small">
<el-form-item :label="$t('commons.name')" prop="name">
<el-input v-model="form.name" autocomplete="off" show-word-limit maxlength="50"></el-input>
</el-form-item>
<el-form-item :label="$t('commons.description')" prop="description">
<el-input
v-model="form.description"
autocomplete="off"
type="textarea"
show-word-limit
maxlength="200"></el-input>
</el-form-item>
<el-form-item>
<environment-group-row
ref="environmentGroupRow"
:env-group-id="environmentId"
:read-only="false"
:show-save-btn="true" />
</el-form-item>
</el-form>
<template v-slot:footer>
<div class="dialog-footer">
<ms-dialog-footer btn-size="medium" @cancel="createVisible = false" @confirm="submit('form')" />
</div>
</template>
</el-dialog>
</div>
</template>
<script>
import { addGroupEnvironment } from 'metersphere-frontend/src/api/environment';
import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter';
import EnvironmentGroupRow from './EnvironmentGroupRow';
export default {
name: 'EditEnvironmentGroup',
components: {
EnvironmentGroupRow,
MsDialogFooter,
},
data() {
return {
title: this.$t('workspace.env_group.create'),
form: {},
createVisible: false,
rules: {
name: [
{
required: true,
message: this.$t('api_test.environment.please_input_env_group_name'),
trigger: 'blur',
},
{
min: 1,
max: 50,
message: this.$t('commons.input_limit', [1, 50]),
trigger: 'blur',
},
],
description: [
{
max: 200,
message: this.$t('commons.input_limit', [0, 200]),
trigger: 'blur',
},
],
},
environmentGroup: {},
environmentId: '',
result: false,
};
},
methods: {
handleClose() {},
submit() {
this.$refs['form'].validate((valid) => {
if (valid) {
let sign = this.$refs.environmentGroupRow.valid();
if (!sign) {
this.$warning(this.$t('workspace.env_group.not_intact'));
return false;
}
let envGroupProject = this.$refs.environmentGroupRow.envGroupProject;
let param = {
envGroupProject,
name: this.form.name,
description: this.form.description,
};
this.result = addGroupEnvironment(param).then(() => {
this.$success(this.$t('commons.save_success'));
this.createVisible = false;
this.$emit('refresh');
});
} else {
return false;
}
});
},
open() {
this.createVisible = true;
this.form = {
name: '',
description: '',
};
},
},
};
</script>
<style scoped></style>

View File

@ -0,0 +1,148 @@
<template>
<div>
<div style="margin-left: 20px">
<el-select
v-model="envGroupId"
:placeholder="$t('workspace.env_group.select')"
style="margin-top: 8px; width: 200px"
size="small"
clearable>
<el-option-group v-for="group in groups" :key="group.label" :label="group.label">
<el-option
v-for="item in group.options"
:key="item.name"
:label="item.name"
:disabled="item.disabled"
:value="item.id">
</el-option>
</el-option-group>
</el-select>
<span style="margin-left: 8px">{{ $t('workspace.env_group.name') }}</span>
<i class="el-icon-view icon-view-btn" @click="viewGroup"></i>
</div>
<el-button type="primary" @click="handleConfirm" size="small" class="env-confirm">
{{ $t('workspace.env_group.confirm') }}
</el-button>
<el-dialog
:visible="visible"
append-to-body
:title="$t('workspace.env_group.name')"
@close="visible = false"
style="height: 800px">
<template>
<environment-group style="overflow-y: auto" :screen-height="'350px'" :read-only="true"></environment-group>
</template>
</el-dialog>
</div>
</template>
<script>
import EnvironmentGroup from './EnvironmentGroupList';
import { getEnvironmentMapByGroupId, getEnvironmentOptions } from 'metersphere-frontend/src/api/environment';
export default {
name: 'EnvGroupWithOption',
components: { EnvironmentGroup },
data() {
return {
groups: [],
envGroupId: this.groupId,
visible: false,
disabledGroups: [],
notDisabledGroups: [],
result: false,
};
},
props: {
groupId: {
type: String,
default() {
return '';
},
},
projectIds: Set,
},
watch: {
groupId(val) {
this.envGroupId = val;
},
},
created() {
this.init();
},
methods: {
open() {
this.envGroupId = this.groupId;
},
init() {
this.result = getEnvironmentOptions({
projectIds: [...this.projectIds],
}).then((res) => {
let groups = res.data;
this.disabledGroups = groups.filter((group) => group.disabled === true);
this.notDisabledGroups = groups.filter((group) => group.disabled === false);
this.$set(this.groups, 0, {
label: this.$t('workspace.env_group.available_group'),
options: this.notDisabledGroups,
});
this.$set(this.groups, 1, {
label: this.$t('workspace.env_group.not_available_group'),
options: this.disabledGroups,
});
});
},
viewGroup() {
this.visible = true;
},
async handleConfirm() {
const sign = await this.checkEnv();
if (sign) {
this.$emit('setEnvGroup', this.envGroupId);
this.$emit('close');
}
},
checkEnv() {
return new Promise((resolve) => {
if (!this.envGroupId) {
this.$warning(this.$t('workspace.env_group.select'));
resolve(false);
return false;
}
getEnvironmentMapByGroupId(this.envGroupId).then((res) => {
let data = res.data;
if (!data) {
this.$warning(this.$t('workspace.env_group.lack_env'));
resolve(false);
return;
}
let map = new Map(Object.entries(data));
for (let id of this.projectIds) {
if (!map.get(id)) {
this.$warning(this.$t('workspace.env_group.lack_necessary_environment'));
resolve(false);
return;
}
}
resolve(true);
});
});
},
},
};
</script>
<style scoped>
.env-confirm {
margin-left: 20px;
width: 360px;
margin-top: 10px;
}
.icon-view-btn {
margin-left: 5px;
}
.icon-view-btn:hover {
cursor: pointer;
}
</style>

View File

@ -0,0 +1,206 @@
<template>
<div>
<el-card class="table-card" v-loading="result">
<template v-slot:header>
<ms-table-header
:create-tip="btnTips"
:condition.sync="condition"
:show-create="!readOnly"
@search="search"
@create="createEnvironment"
:create-permission="['WORKSPACE_PROJECT_ENVIRONMENT:READ+CREATE_GROUP']">
</ms-table-header>
</template>
<el-table
:data="environmentGroupList"
style="width: 100%"
ref="table"
row-key="id"
@expand-change="expandChange"
:height="screenHeight">
<el-table-column type="expand" prop="id">
<template v-slot:default="scope">
<environment-group-row
:env-group-id="scope.row.id"
ref="environmentGroupRow"
:read-only="!scope.row.readOnly"
style="overflow-x: hidden; overflow-y: auto; height: 180px" />
</template>
</el-table-column>
<el-table-column :label="$t('commons.name')" prop="name" show-overflow-tooltip min-width="200">
<template v-slot:default="scope">
<span @click.stop v-if="scope.row.showNameInput">
<el-input
size="mini"
v-model="scope.row.name"
class="name-input"
@blur="updateGroupName(scope.row)"
show-word-limit
maxlength="50"
:placeholder="$t('commons.input_name')"
ref="nameEdit" />
</span>
<span v-else>
<span>{{ scope.row.name }}</span>
<i
class="el-icon-edit"
style="cursor: pointer; margin-left: 4px"
@click="editName(scope.row)"
v-permission="['WORKSPACE_PROJECT_ENVIRONMENT:READ+EDIT_GROUP']" />
</span>
</template>
</el-table-column>
<el-table-column prop="createTime" :label="$t('commons.create_time')" min-width="200">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | datetimeFormat }}</span>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="init" :current-page.sync="currentPage" :page-size.sync="pageSize" :total="total" />
</el-card>
<edit-environment-group ref="editEnvironmentGroup" @refresh="init" />
<ms-delete-confirm :title="$t('workspace.env_group.delete')" @delete="_handleDelete" ref="deleteConfirm" />
</div>
</template>
<script>
import {
copyEnvironment,
delEnvironment,
envGroupList,
environmentGroupModify,
} from 'metersphere-frontend/src/api/environment';
import MsTableHeader from 'metersphere-frontend/src/components/MsTableHeader';
import MsTableButton from 'metersphere-frontend/src/components/MsTableButton';
import MsTableOperator from 'metersphere-frontend/src/components/MsTableOperator';
import MsTableOperatorButton from 'metersphere-frontend/src/components/MsTableOperatorButton';
import MsTablePagination from 'metersphere-frontend/src/components/pagination/TablePagination';
import ApiEnvironmentConfig from 'metersphere-frontend/src/components/environment/ApiEnvironmentConfig';
import MsAsideItem from 'metersphere-frontend/src/components/MsAsideItem';
import MsAsideContainer from 'metersphere-frontend/src/components/MsAsideContainer';
import ProjectSwitch from 'metersphere-frontend/src/components/head/ProjectSwitch';
import EnvironmentGroupRow from './EnvironmentGroupRow';
import EditEnvironmentGroup from './EditEnvironmentGroup';
import MsDeleteConfirm from 'metersphere-frontend/src/components/MsDeleteConfirm';
export default {
name: 'EnvironmentGroup',
components: {
EditEnvironmentGroup,
ProjectSwitch,
MsAsideContainer,
MsAsideItem,
ApiEnvironmentConfig,
MsTablePagination,
MsTableOperatorButton,
MsTableOperator,
MsTableButton,
MsTableHeader,
EnvironmentGroupRow,
MsDeleteConfirm,
},
data() {
return {
btnTips: this.$t('workspace.env_group.create'),
envGroupId: '',
condition: {},
environmentGroupList: [],
result: false,
currentPage: 1,
pageSize: 10,
total: 0,
showNameInput: false,
};
},
props: {
screenHeight: {
type: String,
default() {
return 'calc(100vh - 170px)';
},
},
readOnly: {
type: Boolean,
default() {
return false;
},
},
},
created() {
this.init();
},
activated() {
this.init();
},
methods: {
init() {
this.result = envGroupList(this.currentPage, this.pageSize, this.condition).then((res) => {
let data = res.data;
let { listObject, itemCount } = data;
this.environmentGroupList = listObject;
this.total = itemCount;
});
},
createEnvironment() {
this.$refs.editEnvironmentGroup.open();
},
editEnvironment(row) {
this.$refs.table.toggleRowExpansion(row, true);
this.$set(row, 'readOnly', true);
},
deleteEnvironment(row) {
if (row.system) {
this.$warning(this.$t('group.admin_not_allow_delete'));
return;
}
this.$refs.deleteConfirm.open(row);
},
copyEnvironment(row) {
if (row && row.id) {
copyEnvironment(row.id).then(() => {
this.$success(this.$t('commons.copy_success'));
this.init();
});
}
},
search() {
this.init();
},
_handleDelete(row) {
delEnvironment(row.id).then(() => {
this.$success(this.$t('commons.delete_success'));
this.init();
});
},
expandChange(row, expanded) {
if (expanded) {
this.$set(row, 'readOnly', false);
}
},
editName(row) {
this.$set(row, 'showNameInput', true);
this.$nextTick(() => {
this.$refs.nameEdit.focus();
});
},
updateGroupName(row) {
if (!row || !row.id || !row.name) {
this.init();
return false;
}
let param = {
id: row.id,
name: row.name,
workspaceId: row.workspaceId,
};
environmentGroupModify(param).then(() => {
this.$success(this.$t('commons.modify_success'));
this.init();
});
},
},
};
</script>
<style scoped></style>

View File

@ -0,0 +1,453 @@
<template>
<div>
<div class="btn-div">
<el-button size="mini" type="primary" class="save-btn" v-if="!rowReadOnly && !showSaveBtn" @click="update">
{{ $t('commons.save') }}
</el-button>
</div>
<div v-loading="result" class="group-row">
<el-form class="row-form">
<el-form-item v-for="(item, index) in envGroupProject" :key="index">
<el-row type="flex" justify="space-between" :gutter="10">
<el-col :span="6">
<el-select
v-model="item.projectId"
filterable
clearable
style="width: 100%"
@change="projectChange(item)"
:size="itemSize"
@clear="clearProjectSelect"
:placeholder="$t('workspace.env_group.please_select_project')"
:disabled="rowReadOnly">
<el-option
v-for="(project, projectIndex) in projectList"
:key="projectIndex"
:label="project.name"
:disabled="project.disabled"
:value="project.id"></el-option>
</el-select>
</el-col>
<el-col :span="6">
<el-select
v-model="item.environmentId"
filterable
clearable
style="width: 100%"
@change="environmentChange(item)"
:size="itemSize"
:placeholder="$t('workspace.env_group.please_select_env')"
:disabled="rowReadOnly">
<el-option
v-for="(environment, envIndex) in item.environments"
:key="envIndex"
:label="environment.name"
:value="environment.id"></el-option>
</el-select>
</el-col>
<el-col :span="4">
<el-button
:size="itemSize"
icon="el-icon-s-data"
style="width: 100%"
@click="showDomainInfo(item)"
v-if="item.moreDomain">
{{ $t('workspace.env_group.view_details') }}
</el-button>
<el-input v-else v-model="item.domainName" :disabled="true" :size="itemSize" />
</el-col>
<el-col :span="5">
<el-input
prop="description"
show-overflow-tooltip
:placeholder="$t('commons.description')"
maxlength="100"
v-model="item.domainDescription"
show-word-limit
:size="itemSize"
:disabled="true" />
</el-col>
<el-col :span="3">
<el-button
type="info"
icon="el-icon-plus"
circle
:size="itemSize"
:disabled="rowReadOnly"
@click="change"></el-button>
<el-button
type="danger"
icon="el-icon-delete"
circle
:size="itemSize"
:disabled="rowReadOnly"
@click="remove(index)"></el-button>
</el-col>
</el-row>
</el-form-item>
</el-form>
<el-dialog :title="$t('workspace.env_group.domain_list')" :visible.sync="domainVisible" append-to-body>
<el-table :data="conditions">
<el-table-column prop="socket" :label="$t('load_test.domain')" show-overflow-tooltip width="180">
<template v-slot:default="{ row }">
{{ row.conditionType ? row.server : getUrl(row) }}
</template>
</el-table-column>
<el-table-column :label="$t('commons.type')" show-overflow-tooltip min-width="100px">
<template v-slot:default="{ row }">
<el-tag type="info" size="mini">{{ row.conditionType ? row.conditionType : 'HTTP' }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="type"
:label="$t('api_test.environment.condition_enable')"
show-overflow-tooltip
min-width="100px">
<template v-slot:default="{ row }">
{{ row.conditionType ? '-' : getName(row) }}
</template>
</el-table-column>
<el-table-column prop="details" show-overflow-tooltip min-width="120px" :label="$t('api_test.value')">
<template v-slot:default="{ row }">
{{ row.conditionType ? '-' : getDetails(row) }}
</template>
</el-table-column>
<el-table-column
prop="description"
show-overflow-tooltip
min-width="120px"
:label="$t('commons.description')">
<template v-slot:default="{ row }">
<span>{{ row.description ? row.description : '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="createTime" show-overflow-tooltip min-width="120px" :label="$t('commons.create_time')">
<template v-slot:default="{ row }">
<span v-if="!row.conditionType">{{ row.time | datetimeFormat }}</span>
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
<el-button @click="domainVisible = false" size="small">{{ $t('commons.cancel') }}</el-button>
<el-button type="primary" @click="domainVisible = false" size="small">{{ $t('commons.confirm') }}</el-button>
</span>
</el-dialog>
</div>
</div>
</template>
<script>
import { getAll } from '@/api/project';
import { environmentGroupList, getEnvironmentByProjectId } from 'metersphere-frontend/src/api/environment';
export default {
name: 'EnvironmentGroupRow',
data() {
return {
result: false,
envGroupProject: [],
projectList: [],
environmentList: [],
environments: [],
domainVisible: false,
conditions: [],
rowReadOnly: this.readOnly,
};
},
watch: {
readOnly: {
handler(v) {
this.rowReadOnly = v;
},
},
},
props: {
envGroupId: {
type: String,
default() {
return '';
},
},
readOnly: {
type: Boolean,
default() {
return true;
},
},
itemSize: {
type: String,
default() {
return 'mini';
},
},
showSaveBtn: {
type: Boolean,
default() {
return false;
},
},
},
created() {
this.getEnvironmentGroupProject();
this.getProjects();
},
methods: {
projectChange(item) {
if (item && item.projectId) {
let project = this.projectList.find((project) => project.id === item.projectId);
if (project) {
project.disabled = true;
}
this.clearProjectSelect();
getEnvironmentByProjectId(this.projectId).then((res) => {
this.$set(item, 'environments', res.data);
});
} else {
this.$set(item, 'environments', []);
}
this.$set(item, 'environmentId', '');
this.$set(item, 'domainName', '');
},
clearProjectSelect() {
let usedProjectId = this.envGroupProject.map((egp) => egp.projectId);
if (usedProjectId) {
this.projectList.forEach((p) => {
p.disabled = !!usedProjectId.find((id) => id === p.id);
});
}
},
environmentChange(item) {
// todo
//
this.$set(item, 'moreDomain', false);
this.$set(item, 'domainName', '');
this.$set(item, 'domainDescription', '');
let environments = item.environments;
let index = environments.findIndex((e) => e.id === item.environmentId);
if (index === -1) {
this.$set(item, 'domainName', '');
this.$set(item, 'domainDescription', '');
return;
}
let environment = environments[index].config;
if (environment) {
const config = JSON.parse(environment);
if (config.httpConfig && !config.httpConfig.conditions) {
if (config.httpConfig.protocol && config.httpConfig.domain) {
let domain = config.httpConfig.protocol + '://' + config.httpConfig.domain;
this.$set(item, 'domainName', domain);
this.$set(item, 'domainDescription', config.httpConfig.description ? config.httpConfig.description : '');
return;
}
} else {
if (config.httpConfig.conditions.length === 1) {
if (config.tcpConfig && config.tcpConfig.server) {
this.$set(item, 'moreDomain', true);
return;
}
let obj = config.httpConfig.conditions[0];
if (obj.protocol && obj.socket) {
this.$set(item, 'domainName', obj.protocol + '://' + obj.socket);
this.$set(item, 'domainDescription', obj.description ? obj.description : '');
return;
}
} else if (config.httpConfig.conditions.length > 1) {
this.$set(item, 'moreDomain', true);
return;
} else if (config.tcpConfig && config.tcpConfig.server) {
this.$set(item, 'domainName', config.tcpConfig.server);
this.$set(item, 'domainDescription', config.tcpConfig.description);
return;
}
}
} else {
this.$set(item, 'domainName', environment.protocol + '://' + environment.domain);
this.$set(item, 'domainDescription', environment.description ? environment.description : '');
return;
}
this.$set(item, 'domainName', '');
this.$set(item, 'domainDescription', '');
},
showDomainInfo(item) {
const index = item.environments.findIndex((e) => e.id === item.environmentId);
if (index === -1) {
return '';
}
let environment = item.environments[index];
const config = JSON.parse(environment.config);
this.conditions = config.httpConfig.conditions;
if (config.tcpConfig && config.tcpConfig.server) {
let condition = {
conditionType: 'TCP',
server: config.tcpConfig.server,
description: config.tcpConfig.description,
};
this.conditions.push(condition);
}
this.domainVisible = true;
},
getProjects() {
//
getAll().then((response) => {
let data = response.data;
if (data) {
this.projectList = data;
this.projectList.forEach((project) => {
this.$set(project, 'disabled', false);
});
}
});
},
getEnvironmentGroupProject() {
if (!this.envGroupId) {
this.envGroupProject = [{}];
return;
}
this.result = environmentGroupList(this.envGroupId).then((response) => {
this.envGroupProject = response.data;
if (this.envGroupProject) {
//
this.envGroupProject.forEach((env) => {
this.disabledOption(env.projectId, true);
this._parseDomainName(env);
});
}
if (this.envGroupProject.length < 1) {
this.envGroupProject.push({});
}
});
},
_parseDomainName(item) {
let { environmentId } = item;
const index = item.environments.findIndex((e) => e.id === environmentId);
if (index === -1) {
this.$set(item, 'domainName', '');
return;
}
let environment = item.environments[index].config;
if (environment) {
const config = JSON.parse(environment);
if (config.httpConfig && !config.httpConfig.conditions) {
if (config.httpConfig.protocol && config.httpConfig.domain) {
let domain = config.httpConfig.protocol + '://' + config.httpConfig.domain;
this.$set(item, 'domainName', domain);
this.$set(item, 'domainDescription', config.httpConfig.description ? config.httpConfig.description : '');
}
} else {
if (config.httpConfig.conditions.length === 1) {
if (config.tcpConfig && config.tcpConfig.server) {
this.$set(item, 'moreDomain', true);
return;
}
let obj = config.httpConfig.conditions[0];
if (obj.protocol && obj.socket) {
this.$set(item, 'domainName', obj.protocol + '://' + obj.socket);
this.$set(item, 'domainDescription', obj.description ? obj.description : '');
}
} else if (config.httpConfig.conditions.length > 1) {
this.$set(item, 'moreDomain', true);
return;
} else if (config.tcpConfig && config.tcpConfig.server) {
this.$set(item, 'domainName', config.tcpConfig.server);
this.$set(item, 'domainDescription', config.tcpConfig.description);
return;
}
}
} else {
this.$set(item, 'domainName', environment.protocol + '://' + environment.domain);
this.$set(item, 'domainDescription', environment.description ? environment.description : '');
}
},
getName(row) {
switch (row.type) {
case 'NONE':
return this.$t('api_test.definition.document.data_set.none');
case 'MODULE':
return this.$t('test_track.module.module');
case 'PATH':
return this.$t('api_test.definition.api_path');
}
},
getUrl(row) {
return row.protocol + '://' + row.socket;
},
getDetails(row) {
if (row && row.type === 'MODULE') {
if (row.details && row.details instanceof Array) {
let value = '';
row.details.forEach((item) => {
value += item.name + ',';
});
if (value.endsWith(',')) {
value = value.substr(0, value.length - 1);
}
return value;
}
} else if (row && row.type === 'PATH' && row.details.length > 0 && row.details[0].name) {
return row.details[0].value === 'equals'
? this.$t('commons.adv_search.operators.equals') + row.details[0].name
: this.$t('api_test.request.assertions.contains') + row.details[0].name;
} else {
return '';
}
},
remove(index) {},
disabledOption(projectId, sign) {
let project = this.projectList.find((project) => project.id === projectId);
if (project) {
project.disabled = sign;
}
},
change() {
let isNeedCreate = true;
let removeIndex = -1;
this.envGroupProject.forEach((item, index) => {
if (!item.projectId && !item.environmentId) {
//
if (index !== this.envGroupProject.length - 1) {
removeIndex = index;
}
//
isNeedCreate = false;
}
});
if (isNeedCreate) {
this.envGroupProject.push({});
}
},
update() {},
valid() {
if (this.envGroupProject) {
for (let egp of this.envGroupProject) {
if (egp) {
let { projectId, environmentId, description } = egp;
let book = projectId ? environmentId : !description;
if (!book) {
return false;
}
}
}
return true;
}
},
},
};
</script>
<style scoped>
.row-form :deep(.el-form-item) {
margin-bottom: 0;
}
.btn-div {
position: absolute;
top: 10px;
right: 10px;
z-index: 999;
}
</style>