feat(系统设置):增加专门的环境配置页面,方便对环境的各类操作。 (#2172)

* feat(系统设置):增加专门的环境配置页面,方便对环境的各类操作。

* style:使代码更加规范

将==改成===,给单行if语句添加{}
This commit is contained in:
Ambitiousliga 2021-04-22 12:10:22 +08:00 committed by GitHub
parent 8d75e6220b
commit f393834aad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 477 additions and 14 deletions

View File

@ -1,5 +1,5 @@
<template>
<el-main v-loading="result.loading">
<el-main v-loading="result.loading" class="environment-edit">
<el-form :model="environment" :rules="rules" ref="environment">
<span>{{$t('api_test.environment.name')}}</span>
@ -108,6 +108,7 @@
environment.id = response.data;
}
this.$success(this.$t('commons.save_success'));
this.$emit('refreshAfterSave'); //EnvironmentList.vue使
});
},
buildParam: function (environment) {

View File

@ -10,12 +10,18 @@
</span>
</slot>
</div>
<div :style="{'height': itemBarHeight + 'px'}" v-for="(item, index) in data" :key="index" class="item-bar" @click="itemSelected(index, item)" :class="{'item-selected' : index == selectIndex}">
<input class="item-input" :style="{'height': itemBarHeight - 12 + 'px', 'line-height': itemBarHeight - 12 + 'px', 'width': width - 90 + 'px'}" v-model="item.name" :placeholder="$t('commons.input_content')"/>
<span :style="{'line-height': itemBarHeight - 10 + 'px'}" class="item-right">
<i v-for="(operator, operatorIndex) in itemOperators" :key="operatorIndex" :class="operator.icon" @click.stop="operator.func(item, index)"/>
<slot name="content">
<div :style="{'height': itemBarHeight + 'px'}" v-for="(item, index) in data" :key="index" class="item-bar"
@click="itemSelected(index, item)" :class="{'item-selected' : index == selectIndex}">
<input class="item-input"
:style="{'height': itemBarHeight - 12 + 'px', 'line-height': itemBarHeight - 12 + 'px', 'width': width - 90 + 'px'}"
v-model="item.name" :placeholder="$t('commons.input_content')"/>
<span :style="{'line-height': itemBarHeight - 10 + 'px'}" class="item-right">
<i v-for="(operator, operatorIndex) in itemOperators" :key="operatorIndex" :class="operator.icon"
@click.stop="operator.func(item, index)"/>
</span>
</div>
</div>
</slot>
</ms-aside-container>
</template>

View File

@ -36,15 +36,25 @@
</el-submenu>
</el-submenu>
<el-menu-item v-for="menu in project" :key="menu.index" :index="'/setting/project/all'" class="setting-item"
v-permission="['test_user','test_manager', 'org_admin', 'admin']">
<el-submenu index="4" v-permission="['test_user','test_manager', 'org_admin', 'admin']">
<template v-slot:title>
<font-awesome-icon class="icon" :icon="['fa', 'bars']" size="lg"/>
<span>{{ $t(menu.title) }}</span>
<span>{{$t('commons.project')}}</span>
</template>
</el-menu-item>
<el-menu-item v-for="menu in project" :key="menu.index" :index="menu.index" class="setting-item">
{{ $t(menu.title) }}
</el-menu-item>
</el-submenu>
<el-submenu index="4">
<!-- <el-menu-item v-for="menu in project" :key="menu.index" :index="'/setting/project/all'" class="setting-item"-->
<!-- v-permission="['test_user','test_manager', 'org_admin', 'admin']">-->
<!-- <template v-slot:title>-->
<!-- <font-awesome-icon class="icon" :icon="['fa', 'bars']" size="lg"/>-->
<!-- <span>{{ $t(menu.title) }}</span>-->
<!-- </template>-->
<!-- </el-menu-item>-->
<el-submenu index="5">
<template v-slot:title>
<font-awesome-icon class="icon" :icon="['far', 'user']" size="lg"/>
<span>{{ $t('commons.personal_info') }}</span>

View File

@ -0,0 +1,136 @@
<template>
<el-dialog :visible="dialogVisible" :title="dialogTitle"
@close="close" :close-on-click-modal="false"
width="50%">
<el-form>
<el-row>
<el-col :span="6">
<div class="project-item">
<div style="margin-bottom: 10px">
{{$t('project.select')}}
</div>
<el-select v-model="currentProjectId" filterable clearable >
<el-option v-for="item in projectList" :key="item.id"
:label="item.name" :value="item.id">
</el-option>
</el-select>
</div>
</el-col>
<el-col :span="16" :offset="1">
<el-upload
class="api-upload" drag action="alert"
:on-change="handleFileChange"
:limit="1" :file-list="uploadFiles"
:on-remove="handleRemove"
:on-exceed="handleExceed"
:auto-upload="false" accept=".json">
<i class="el-icon-upload"></i>
<div class="el-upload__text" v-html="$t('load_test.upload_tips')"></div>
<div class="el-upload__tip" slot="tip">
{{ $t('api_test.api_import.file_size_limit') }} {{'' + $t('api_test.api_import.ms_env_import_file_limit') }}
</div>
</el-upload>
</el-col>
</el-row>
</el-form>
<template v-slot:footer>
<el-button type="primary" @click="save">
{{ $t('commons.confirm') }}
</el-button>
</template>
</el-dialog>
</template>
<script>
export default {
name: "EnvironmentImport",
props: {
projectList: {
type: Array,
default() {
return [];
}
},
},
data() {
return {
currentProjectId: '', //id
uploadFiles: [],
dialogTitle: this.$t('api_test.environment.import'),
dialogVisible: false
}
},
watch: {
//
dialogVisible(val, oldVal) {
if (oldVal === false) {
this.currentProjectId = '';
this.uploadFiles = [];
}
}
},
methods: {
handleFileChange(file, uploadFiles) {
this.uploadFiles = uploadFiles;
},
save() {
if (this.uploadFiles.length > 0) {
for (let i = 0; i < this.uploadFiles.length; i++) {
this.uploadValidate(this.uploadFiles[i]);
let file = this.uploadFiles[i];
if (!file) {
continue;
}
let reader = new FileReader();
reader.readAsText(file.raw)
reader.onload = (e) => {
let fileString = e.target.result;
JSON.parse(fileString).map(env => {
//projectIdnullprojectId
env.projectId = this.currentProjectId === '' ? null : this.currentProjectId;
this.$post('/api/environment/add', env, response => {
this.$emit('refresh');
this.$success(this.$t('commons.save_success'));
})
})
}
}
}
},
handleExceed() {
this.$warning(this.$t('api_test.api_import.file_exceed_limit'));
},
handleRemove() {
},
uploadValidate(file) { //.json20M
const extension = file.name.substring(file.name.lastIndexOf('.') + 1);
if (!(extension === 'json')) {
this.$warning(this.$t('api_test.api_import.ms_env_import_file_limit'));
}
if (file.size / 1024 / 1024 > 20) {
this.$warning(this.$t('api_test.api_import.file_size_limit'));
}
},
open() {
this.dialogVisible = true;
},
close() {
this.dialogVisible = false;
}
},
}
</script>
<style scoped>
.project-item {
padding-left: 20px;
padding-right: 20px;
}
</style>

View File

@ -0,0 +1,286 @@
<template>
<div v-loading="result.loading">
<el-card class="table-card">
<!-- 表头 -->
<template v-slot:header>
<ms-table-header :title="$t('api_test.environment.environment_list')" :create-tip="btnTips"
:condition.sync="condition" :is-tester-permission="isTesterPermission" @search="search" @create="createEnv">
<template v-slot:button>
<ms-table-button :is-tester-permission="isTesterPermission" icon="el-icon-box"
:content="$t('commons.import')" @click="importJSON"/>
<ms-table-button :is-tester-permission="isTesterPermission" icon="el-icon-box"
:content="$t('commons.export')" @click="exportJSON"/>
</template>
</ms-table-header>
</template>
<!-- 环境列表内容 -->
<!-- 实现搜索,根据搜索内容变换显示的环境列表 -->
<el-table border :data="environments.filter(env => !searchText || env.name.toLowerCase().includes(searchText.toLowerCase()))"
@selection-change="handleSelectionChange" max-height="515" class="adjust-table" style="width: 100%" ref="table">
<el-table-column type="selection"></el-table-column>
<el-table-column :label="$t('commons.project')" width="250" show-overflow-tooltip>
<template v-slot="scope">
<span>{{idNameMap.get(scope.row.projectId)}}</span>
</template>
</el-table-column>
<el-table-column :label="$t('api_test.environment.name')" prop="name" show-overflow-tooltip>
</el-table-column>
<el-table-column :label="$t('api_test.environment.socket')" show-overflow-tooltip>
<template v-slot="scope">
<span>{{ parseDomainName(scope.row) }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')">
<template v-slot:default="scope">
<ms-table-operator @editClick="editEnv(scope.row)" @deleteClick="deleteEnv(scope.row)">
<template v-slot:middle>
<ms-table-operator-button :tip="$t('commons.copy')" @exec="copyEnv(scope.row)" :is-tester-permission="isTesterPermission"
icon="el-icon-document-copy" type="info"/>
</template>
</ms-table-operator>
</template>
</el-table-column>
</el-table>
<el-row type="flex" justify="end">
<el-pagination layout="total" :total="total"></el-pagination>
</el-row>
</el-card>
<!-- 创建编辑复制环境时的对话框 -->
<el-dialog :visible.sync="dialogVisible" :close-on-click-modal="false" :title="dialogTitle" width="66%">
<div class="project-item">
<div>
{{$t('project.select')}}
</div>
<el-select v-model="currentProjectId" filterable clearable >
<el-option v-for="item in projectList" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</div>
<environment-edit :environment="currentEnvironment" ref="environmentEdit" @close="close"
@refreshAfterSave="refresh">
</environment-edit>
</el-dialog>
<environment-import :project-list="projectList" @refresh="refresh" ref="envImport"></environment-import>
</div>
</template>
<script>
import MsTableHeader from "@/business/components/common/components/MsTableHeader";
import MsTableButton from "@/business/components/common/components/MsTableButton";
import MsTableOperator from "@/business/components/common/components/MsTableOperator";
import MsTableOperatorButton from "@/business/components/common/components/MsTableOperatorButton";
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
import ApiEnvironmentConfig from "@/business/components/api/test/components/ApiEnvironmentConfig";
import {Environment, parseEnvironment} from "@/business/components/api/test/model/EnvironmentModel";
import EnvironmentEdit from "@/business/components/api/test/components/environment/EnvironmentEdit";
import MsAsideItem from "@/business/components/common/components/MsAsideItem";
import MsAsideContainer from "@/business/components/common/components/MsAsideContainer";
import ProjectSwitch from "@/business/components/common/head/ProjectSwitch";
import SearchList from "@/business/components/common/head/SearchList";
import {checkoutTestManagerOrTestUser, downloadFile} from "@/common/js/utils";
import EnvironmentImport from "@/business/components/settings/project/EnvironmentImport";
export default {
name: "EnvironmentList",
components: {
EnvironmentImport,
SearchList,
ProjectSwitch,
MsAsideContainer,
MsAsideItem,
EnvironmentEdit,
ApiEnvironmentConfig,
MsTablePagination, MsTableOperatorButton, MsTableOperator, MsTableButton, MsTableHeader},
data() {
return {
btnTips: this.$t('api_test.environment.create'),
projectList: [],
condition: {envName: ''}, //
environments: [],
currentEnvironment: new Environment(),
result: {},
dialogVisible: false,
idNameMap: new Map(),
dialogTitle: '',
currentProjectId: '', //id
selectRows: [],
isTesterPermission: false,
}
},
computed: {
searchText() { //
return this.condition.name;
},
/*
搜索后对应的总条目搜索内容为空的话就是全部记录条数搜索内容不为空的话就是匹配的记录条数
*/
total() {
return this.environments
.filter(env => !this.searchText || env.name.toLowerCase().includes(this.searchText.toLowerCase())).length;
},
},
watch: {
//projectId
currentProjectId() {
// el-select''''projectId使
if (this.currentProjectId === '') {
this.currentEnvironment.projectId = null;
} else {
this.currentEnvironment.projectId = this.currentProjectId;
}
}
},
methods: {
list() {
this.environments = [];
let url = "/project/listAll";
this.result = this.$get(url, (response) => { //
this.projectList = response.data; //,
this.projectList.forEach(project => {
this.idNameMap.set(project.id, project.name);
});
//
this.projectList.map((project) => {
this.$get('/api/environment/list/' + project.id, response => {
let envData = response.data;
envData.forEach(env => {
this.environments.push(env);
})
})
})
})
},
createEnv() {
this.currentProjectId = '';
this.dialogTitle = this.$t('api_test.environment.create');
this.dialogVisible = true;
this.currentEnvironment = new Environment();
},
search(searchText) {
},
editEnv(environment) {
this.dialogTitle = this.$t('api_test.environment.config_environment');
this.currentProjectId = environment.projectId;
const temEnv = {};
Object.assign(temEnv, environment);
parseEnvironment(temEnv); //parseEnvironment
this.currentEnvironment = temEnv;
this.dialogVisible = true;
},
copyEnv(environment) {
this.currentProjectId = environment.projectId; //
this.dialogTitle = this.$t('api_test.environment.copy_environment');
const temEnv = {};
Object.assign(temEnv, environment);
parseEnvironment(temEnv); //parseEnvironment
let newEnvironment = new Environment(temEnv);
newEnvironment.id = null;
newEnvironment.name = this.getNoRepeatName(newEnvironment.name);
this.dialogVisible = true;
this.currentEnvironment = newEnvironment;
},
deleteEnv(environment) {
if (environment.id) {
this.result = this.$get('/api/environment/delete/' + environment.id, () => {
this.$success(this.$t('commons.delete_success'));
this.list();
});
}
},
getNoRepeatName(name) {
for (let i in this.environments) {
if (this.environments[i].name === name) {
return this.getNoRepeatName(name + ' copy');
}
}
return name;
},
//
close() {
this.dialogVisible = false;
this.$refs.environmentEdit.clearValidate();
},
refresh() {
this.list();
},
handleSelectionChange(value) {
this.selectRows = value;
},
importJSON() {
this.$refs.envImport.open();
},
exportJSON() {
if (this.selectRows.length < 1) {
this.$warning('请选择想要导出的环境');
return ;
}
//idprojectId
const envs = JSON.parse(JSON.stringify(this.selectRows));
envs.map(env => { //idprojectId
delete env.id;
delete env.projectId;
})
downloadFile('MS_'+ envs.length + '_Environments.json' , JSON.stringify(envs));
},
parseDomainName(environment){ //
if (environment.config) {
const config = JSON.parse(environment.config);
if (!config.httpConfig.protocol) {
return '';
} else {
return config.httpConfig.protocol + "://" + config.httpConfig.domain;
}
} else { //config,protocoldomain
return environment.protocol + '://' + environment.domain;
}
}
},
created() {
this.isTesterPermission = checkoutTestManagerOrTestUser();
},
activated() {
this.list();
},
}
</script>
<style scoped>
.item-bar {
width: 100%;
background: #F9F9F9;
padding: 5px 10px;
box-sizing: border-box;
border: solid 1px #e6e6e6;
}
.item-selected {
background: #ECF5FF;
border-left: solid #409EFF 5px;
}
.item-selected .item-right {
visibility: visible;
}
.environment-edit {
margin-left: 0;
width: 100%;
border: 0;
}
.project-item {
padding-left: 20px;
padding-right: 20px;
}
.project-item .el-select {
margin-top: 15px;
}
</style>

View File

@ -99,6 +99,11 @@ export default {
component: () => import('@/business/components/settings/project/MsProject'),
meta: {project: true, title: 'project.manager'}
},
{
path: 'envlist',
component: () => import('@/business/components/settings/project/EnvironmentList'),
meta: {project: true, title: 'api_test.environment.environment_config'}
}
]
}

View File

@ -12,12 +12,14 @@ export default {
import_mode: 'Import mode',
import_module: 'Import module',
import_user: 'Import user',
export: 'Export',
please_fill_in_the_template: 'Please fill in the template',
cut_back_old_version: 'Cut back to old version',
cut_back_new_version: 'Switch back to new version',
comment: 'comment',
examples: 'examples',
help_documentation: 'Help documentation',
confirm_delete: 'Whether to delete',
delete_cancelled: 'Delete cancelled',
option_cannot_spread_pages: 'This options not support spread pages.Do you want continue?',
workspace: 'Workspace',
@ -806,6 +808,7 @@ export default {
loop_message: "There is more than one request in the current cycle and cannot be closed",
},
environment: {
create: 'Create environment',
name: "Environment Name",
socket: "Socket",
condition_enable: "Activation conditions",
@ -813,6 +816,7 @@ export default {
environment_list: "Environment List",
environment_config: "Environment Config",
config_environment: "Config Environment",
copy_environment: "copy environment",
environment: "Environment",
select_environment: "Please select environment",
please_save_test: "Please Save Test First",
@ -820,6 +824,7 @@ export default {
http_config: "HTTP Config",
database_config: "Database Config",
tcp_config: "TCP Config",
import: "Import Environment",
},
scenario: {
scenario: "Scenario",
@ -1016,7 +1021,8 @@ export default {
swagger_url_import: "Import using URL",
timing_synchronization: "Timing synchronization",
next_synchronization_time: "Next synchronization time",
ms_env_import_file_limit: "It supports JSON format files exported through metersphere",
file_exceed_limit: "The number of files exceeds the limit",
},
home_page: {
unit_of_measurement: "",

View File

@ -12,6 +12,7 @@ export default {
import_mode: '导入模式',
import_module: '导入模块',
import_user: '导入用户',
export: '导出',
please_fill_in_the_template: '请填写模版内容',
cut_back_old_version: '切回旧版',
cut_back_new_version: '切回新版',
@ -19,6 +20,7 @@ export default {
examples: '示例',
help_documentation: '帮助文档',
api_help_documentation: 'API文档',
confirm_delete: '是否删除',
delete_cancelled: '已取消删除',
workspace: '工作空间',
organization: '组织',
@ -816,6 +818,7 @@ export default {
loop_message: "当前循环下超过一个请求,不能关闭状态",
},
environment: {
create: '创建环境',
name: "环境名称",
socket: "环境域名",
condition_enable: "启用条件",
@ -823,6 +826,7 @@ export default {
environment_list: "环境列表",
environment_config: "环境配置",
config_environment: "配置环境",
copy_environment: "复制环境",
environment: "环境",
select_environment: "请选择环境",
please_save_test: "请先保存测试",
@ -830,6 +834,7 @@ export default {
http_config: "HTTP配置",
database_config: "数据库配置",
tcp_config: "TCP配置",
import: "导入环境",
},
scenario: {
scenario: "场景",
@ -1027,7 +1032,9 @@ export default {
suffixFormatErr: "文件格式不符合要求",
swagger_url_import: "使用URL导入",
timing_synchronization: "定时同步",
next_synchronization_time: "下次同步时间"
next_synchronization_time: "下次同步时间",
ms_env_import_file_limit: "支持通过MeterSphere导出的json格式文件",
file_exceed_limit: "文件数量超出限制",
},

View File

@ -12,6 +12,7 @@ export default {
import_mode: '導入模式',
import_module: '導入模塊',
import_user: '導入用戶',
export: "導出",
please_fill_in_the_template: '請填寫模版內容',
cut_back_old_version: '切回舊版',
cut_back_new_version: '切回新版',
@ -19,6 +20,7 @@ export default {
examples: '示例',
help_documentation: '幫助文檔',
api_help_documentation: 'API文檔',
confirm_delete: '是否刪除',
delete_cancelled: '已取消刪除',
workspace: '工作空間',
organization: '組織',
@ -806,6 +808,7 @@ export default {
loop_message: "當前循環下超過一個請求,不能關閉狀態",
},
environment: {
create: '創建環境',
name: "環境名稱",
socket: "環境域名",
condition_enable: "啟用條件",
@ -813,6 +816,7 @@ export default {
environment_list: "環境列表",
environment_config: "環境配置",
config_environment: "配置環境",
copy_environment: "複製環境",
environment: "環境",
select_environment: "請選擇環境",
please_save_test: "請先保存測試",
@ -820,6 +824,7 @@ export default {
http_config: "HTTP配置",
database_config: "數據庫配置",
tcp_config: "TCP配置",
import: "導入環境",
},
scenario: {
scenario: "場景",
@ -1018,7 +1023,8 @@ export default {
swagger_url_import: "使用URL導入",
timing_synchronization: "定時同步",
next_synchronization_time: "下次同步時間",
ms_env_import_file_limit: "支持通過MeterSphere導出的json格式文件",
file_exceed_limit: "文件數量超出限制",
},
home_page: {
unit_of_measurement: "個",