style(接口测试): 编辑场景模块选项样式调整

【【接口测试】github#30574,接口场景模块展示-在修改场景时无法直接定位到当前场景所在模块,需要一层一层打开并且当有多层级目录时无法展示完整】https://www.tapd.cn/55049933/bugtrace/bugs/view?bug_id=1155049933001040224

Signed-off-by: fit2-zhao <yong.zhao@fit2cloud.com>
This commit is contained in:
fit2-zhao 2024-07-03 14:48:41 +08:00 committed by Craftsman
parent b42f7311a7
commit 0911a02805
3 changed files with 571 additions and 344 deletions

View File

@ -24,6 +24,7 @@
<ms-select-tree
size="small"
:data="moduleOptions"
:default-expand-all="true"
:defaultKey="currentScenario.apiScenarioModuleId"
@getValue="setModule"
:obj="moduleObj"
@ -1209,7 +1210,7 @@ export default {
if (node.parent && node.parent.data && node.parent.data.id) {
this.evaluationParent(node.parent, status);
}
if (node.data.code === 'ERROR') {
if (node.data.code === 'ERROR') {
return;
}
if (node.data.code === 'FAKE_ERROR') {
@ -2409,7 +2410,7 @@ export default {
let currentEnvironment = {};
this.environments.forEach((environment) => {
//
if (environment.id === request.originalEnvironmentId !== envId) {
if ((environment.id === request.originalEnvironmentId) !== envId) {
parseEnvironment(environment);
if (environment.config && environment.config.databaseConfigs) {
environment.config.databaseConfigs.forEach((item) => {

View File

@ -1,39 +1,51 @@
<template>
<div v-loading="loading" ref="projectButton">
<div class="mask" v-show="isShowSelect"></div>
<el-popover placement="bottom-start" :width="popoverWidth" trigger="manual" v-model="isShowSelect"
@hide="popoverHide" @show="show">
<el-input
size="mini"
prefix-icon="el-icon-search"
v-model="filterText">
</el-input>
<el-tree class="common-tree" :width="width" ref="tree" :data="treeData" :props="obj"
:show-checkbox="multiple"
:node-key="obj.id"
:check-strictly="checkStrictly"
:expand-on-click-node="multiple&&expandClickNode"
:check-on-click-node="checkClickNode"
:highlight-current="true"
@check="clickDeal"
:default-checked-keys="checkedId"
:filter-node-method="filterNode"
@node-click="nodeClick"/>
<el-select slot="reference" ref="select" :size="size"
v-model="returnDataKeys"
:multiple="multiple"
:clearable="clearable"
:collapse-tags="collapseTags"
:disabled="disabled"
@click.native="selectClick"
@remove-tag="removeTag"
@clear="clean"
:placeholder="placeholder"
class="ms-tree-select">
<el-popover
placement="bottom-start"
trigger="manual"
v-model="isShowSelect"
:style="{ minWidth: `${popoverWidth}px !important` }"
style="min-width: 200px !important; max-height: 400px; overflow: auto"
@hide="popoverHide"
@show="show">
<el-input size="mini" prefix-icon="el-icon-search" v-model="filterText"> </el-input>
<el-tree
class="common-tree"
:style="{ minWidth: `${popoverWidth}px !important` }"
:width="width"
ref="tree"
:data="treeData"
:props="obj"
:show-checkbox="multiple"
:node-key="obj.id"
:check-strictly="checkStrictly"
:expand-on-click-node="multiple && expandClickNode"
:check-on-click-node="checkClickNode"
:default-expand-all="defaultExpandAll"
:highlight-current="true"
@check="clickDeal"
:default-checked-keys="checkedId"
:filter-node-method="filterNode"
@node-click="nodeClick" />
<el-select
slot="reference"
ref="select"
:size="size"
v-model="returnDataKeys"
:multiple="multiple"
:clearable="clearable"
:collapse-tags="collapseTags"
:disabled="disabled"
@click.native="selectClick"
@remove-tag="removeTag"
@clear="clean"
:placeholder="placeholder"
class="ms-tree-select">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
<el-row>
<el-button v-if="multiple" class="ok" @click="isShowSelect=false" size="mini" type="text">
<el-button v-if="multiple" class="ok" @click="isShowSelect = false" size="mini" type="text">
{{ $t('commons.confirm') }}
</el-button>
</el-row>
@ -42,7 +54,6 @@
</template>
<script>
export default {
name: 'SelectTree',
props: {
@ -51,22 +62,22 @@ export default {
type: Array,
default() {
return [];
}
},
},
obj: {
type: Object,
required: false,
default: () => {
return {
id: 'id',// ID
label: 'name',//
id: 'id', // ID
label: 'name', //
children: 'children', //
path: 'path',//
content: 'content',//
pid: 'pid',//id
disabled: this.isDisabled
}
}
path: 'path', //
content: 'content', //
pid: 'pid', //id
disabled: this.isDisabled,
};
},
},
disabled: {
type: Boolean,
@ -77,91 +88,99 @@ export default {
type: Boolean,
default() {
return false;
}
},
},
//
clearable: {
type: Boolean,
default() {
return false;
}
},
},
//
collapseTags: {
type: Boolean,
default() {
return false;
}
},
},
//
checkStrictly: {
type: Boolean,
default() {
return false;
}
},
},
defaultExpandAll: {
type: Boolean,
default() {
return false;
},
},
//
checkClickNode: {
type: Boolean,
default() {
return false;
}
},
},
//
expandClickNode: {
type: Boolean,
default() {
return false;
}
},
},
// key
defaultKey: {
type: [Number, String, Array, Object],
default() {
return [];
}
},
},
size: {
type: String,
default() {
return 'small';
}
},
},
width: {
type: String,
default() {
return '100%';
}
},
},
height: {
type: String,
default() {
return '300px';
}
},
},
placeholder: {
type: String,
default() {
return this.$t('el.select.placeholder');
}
}
},
},
},
//
data() {
return {
popoverWidth: "0px",//
popoverWidth: '200px', //
isShowSelect: false, //
options: [],//select option
returnDatas: [],//
returnDataKeys: [],//
filterText: "",
options: [], //select option
returnDatas: [], //
returnDataKeys: [], //
filterText: '',
loading: false,
checkedId: [],
selectNodeIds: []
selectNodeIds: [],
};
},
computed: {
treeData() { //
treeData() {
//
return JSON.stringify(this.data).indexOf(this.obj.children) !== -1 ? this.data : this.switchTree();
},
},
@ -171,54 +190,57 @@ export default {
methods: {
updated() {
//
this.$refs.tree.setCheckedKeys(this.checkedId)
this.$refs.tree.setCheckedKeys(this.checkedId);
},
clickDeal(currentObj, treeStatus) {
//
let selected = treeStatus.checkedKeys.indexOf(currentObj.id) // -1
let selected = treeStatus.checkedKeys.indexOf(currentObj.id); // -1
//
if (selected !== -1) {
//
// this.selectedParent(currentObj)
//
this.uniteChildSame(currentObj, true)
this.uniteChildSame(currentObj, true);
} else {
//
if (currentObj.children && currentObj.children.length !== 0) {
this.uniteChildSame(currentObj, false)
this.uniteChildSame(currentObj, false);
}
}
this.nodeClick(currentObj, treeStatus)
this.nodeClick(currentObj, treeStatus);
},
//
uniteChildSame(treeList, isSelected) {
this.$refs.tree.setChecked(treeList.id, isSelected)
this.$refs.tree.setChecked(treeList.id, isSelected);
if (treeList.children) {
for (let i = 0; i < treeList.children.length; i++) {
this.uniteChildSame(treeList.children[i], isSelected)
this.uniteChildSame(treeList.children[i], isSelected);
}
}
},
//
selectedParent(currentObj) {
let currentNode = this.$refs.tree.getNode(currentObj)
let currentNode = this.$refs.tree.getNode(currentObj);
if (currentNode.parent.key !== undefined) {
this.$refs.tree.setChecked(currentNode.parent, true)
this.selectedParent(currentNode.parent)
this.$refs.tree.setChecked(currentNode.parent, true);
this.selectedParent(currentNode.parent);
}
},
outsideClick() {
this.isShowSelect = false;
},
init() {
if (this.defaultKey != undefined && this.defaultKey.length > 0) {
if (this.defaultKey !== undefined && this.defaultKey.length > 0) {
if (this.multiple) {
//
if (Object.prototype.toString.call(this.defaultKey).indexOf("Array") != -1) {
if (Object.prototype.toString.call(this.defaultKey[0]).indexOf("Object") != -1) {//
if (Object.prototype.toString.call(this.defaultKey).indexOf('Array') !== -1) {
if (Object.prototype.toString.call(this.defaultKey[0]).indexOf('Object') !== -1) {
//
this.setDatas(this.defaultKey);
} else if (Object.prototype.toString.call(this.defaultKey[0]).indexOf("Number") != -1
|| Object.prototype.toString.call(this.defaultKey[0]).indexOf("String") != -1) {
} else if (
Object.prototype.toString.call(this.defaultKey[0]).indexOf('Number') !== -1 ||
Object.prototype.toString.call(this.defaultKey[0]).indexOf('String') !== -1
) {
this.setKeys(this.defaultKey);
} else {
return;
@ -226,12 +248,13 @@ export default {
} else {
return;
}
} else {
//
if (Object.prototype.toString.call(this.defaultKey).indexOf("Number") != -1
|| Object.prototype.toString.call(this.defaultKey).indexOf("String") != -1
|| Object.prototype.toString.call(this.defaultKey).indexOf("Object") != -1) {
if (
Object.prototype.toString.call(this.defaultKey).indexOf('Number') !== -1 ||
Object.prototype.toString.call(this.defaultKey).indexOf('String') !== -1 ||
Object.prototype.toString.call(this.defaultKey).indexOf('Object') !== -1
) {
this.setKey(this.defaultKey);
} else {
return;
@ -244,28 +267,32 @@ export default {
},
//select[]
selectClick() {
this.$emit("selectClick")
this.$emit('selectClick');
if (this.disabled) {
return;
}
this.$nextTick(function () {//
this.$nextTick(function () {
//
this.popoverWidth = this.$refs.select.$el.clientWidth - 26;
})
});
//
return this.isShowSelect = !this.isShowSelect
return (this.isShowSelect = !this.isShowSelect);
},
//:
nodeClick(data, node) {
if (!this.multiple) {//
if (!this.multiple) {
//
this.isShowSelect = false;
this.setKey(node.key);
} else {//
} else {
//
let checkedKeys = this.$refs.tree.getCheckedKeys(); // key
let t = [];
this.options = checkedKeys.map((item) => {//option
this.options = checkedKeys.map((item) => {
//option
let node = this.$refs.tree.getNode(item); // node
t.push(node.data);
return {label: node.label, value: node.key};
return { label: node.label, value: node.key };
});
this.returnDataKeys = this.options.map((item) => {
return item.value;
@ -289,7 +316,7 @@ export default {
},
//:
clean() {
this.$refs.tree.setCurrentKey(null);//key
this.$refs.tree.setCurrentKey(null); //key
this.returnDatas = null;
this.returnDataKeys = '';
this.selectNodeIds = [];
@ -308,9 +335,9 @@ export default {
//
setData(data) {
this.options = [];
this.options.push({label: data[this.obj.label], value: data[this.obj.id]});
this.options.push({ label: data[this.obj.label], value: data[this.obj.id] });
this.returnDatas = data;
this.returnDataKeys = data[this.obj.id]
this.returnDataKeys = data[this.obj.id];
this.selectNodeIds = [];
this.getChildNodeId(data, this.selectNodeIds);
},
@ -320,34 +347,36 @@ export default {
this.returnDataKeys = thisKeys;
let t = [];
this.options = [];
thisKeys.map((item) => {//option
thisKeys.map((item) => {
//option
let node = this.$refs.tree.getNode(item); // node
if (node) {
t.push(node.data);
this.options.push({label: node.label, value: node.key});
return {label: node.label, value: node.key};
this.options.push({ label: node.label, value: node.key });
return { label: node.label, value: node.key };
}
});
this.returnDatas = t;
this.popoverHide()
this.popoverHide();
},
//:
setDatas(data) {
this.$refs.tree.setCheckedNodes(data);
this.returnDatas = data;
let t = [];
data.map((item) => {//option
data.map((item) => {
//option
t.push(item[this.obj.id]);
});
this.returnDataKeys = t;
this.popoverHide()
this.popoverHide();
},
// ,select
removeTag(val) {
this.$refs.tree.setChecked(val, false);//
let node = this.$refs.tree.getNode(val);//
this.$refs.tree.setChecked(val, false); //
let node = this.$refs.tree.getNode(val); //
if (!this.checkStrictly && node.childNodes.length > 0) {
this.treeToList(node).map(item => {
this.treeToList(node).map((item) => {
if (item.childNodes.length <= 0) {
this.$refs.tree.setChecked(item, false);
}
@ -355,21 +384,20 @@ export default {
}
this.nodeClick();
this.popoverHide();
},
show(){
document.addEventListener('click', this.hidePanel, false)
show() {
document.addEventListener('click', this.hidePanel, false);
},
//
popoverHide() {
this.$emit('setSelectNodeIds', this.selectNodeIds);
this.$emit('getValue', this.returnDataKeys, this.returnDatas ? this.returnDatas : {});
document.removeEventListener('click', this.hidePanel, false)
document.removeEventListener('click', this.hidePanel, false);
},
hidePanel (e) {
hidePanel(e) {
if (!this.$refs.projectButton.contains(e.target)) {
this.isShowSelect = false
this.popoverHide()
this.isShowSelect = false;
this.popoverHide();
}
},
//
@ -422,9 +450,9 @@ export default {
return false;
},
reload() {
this.loading = true
this.loading = true;
this.$nextTick(() => {
this.loading = false
this.loading = false;
});
},
},
@ -435,13 +463,14 @@ export default {
// select
this.$refs.select.blur();
},
treeData: {//tree
treeData: {
//tree
handler: function () {
this.$nextTick(() => {
this.init();
})
});
},
deep: true
deep: true,
},
defaultKey: {
handler: function () {
@ -452,7 +481,7 @@ export default {
}
}
},
deep: true
deep: true,
},
data: {
handler: function () {
@ -462,7 +491,7 @@ export default {
}
}
},
deep: true
deep: true,
},
filterText(val) {
this.$nextTick(() => {
@ -474,10 +503,9 @@ export default {
this.$refs.tree.filter(val);
});
},
}
},
};
</script>
<style scoped>
.mask {
height: 100%;
@ -499,16 +527,13 @@ export default {
z-index: 111;
}
:deep(.el-tree-node__children ) {
:deep(.el-tree-node__children) {
overflow: inherit;
}
.ok {
float: right;
}
.el-row {
padding-top: 0px !important;
}
</style>

View File

@ -2,38 +2,61 @@
<div>
<el-card class="table-card" v-loading="loading">
<template v-slot:header>
<ms-table-header :create-permission="['SYSTEM_TEST_POOL:READ+CREATE']" :condition.sync="condition"
@search="search" @create="create"
:create-tip="$t('test_resource_pool.create_resource_pool')"
:title="$t('commons.test_resource_pool')"/>
<ms-table-header
:create-permission="['SYSTEM_TEST_POOL:READ+CREATE']"
:condition.sync="condition"
@search="search"
@create="create"
:create-tip="$t('test_resource_pool.create_resource_pool')"
:title="$t('commons.test_resource_pool')"
/>
</template>
<el-table border class="adjust-table" :data="items" style="width: 100%"
:height="screenHeight"
<el-table
border
class="adjust-table"
:data="items"
style="width: 100%"
:height="screenHeight"
>
<el-table-column prop="name" :label="$t('commons.name')"/>
<el-table-column prop="description" :label="$t('commons.description')"/>
<el-table-column prop="name" :label="$t('commons.name')" />
<el-table-column
prop="description"
:label="$t('commons.description')"
/>
<el-table-column prop="type" :label="$t('test_resource_pool.type')">
<template v-slot:default="scope">
<span v-if="scope.row.type === 'NODE'">Node</span>
<span v-if="scope.row.type === 'K8S'" v-xpack>Kubernetes</span>
</template>
</el-table-column>
<el-table-column prop="status" :label="$t('test_resource_pool.enable_disable')">
<el-table-column
prop="status"
:label="$t('test_resource_pool.enable_disable')"
>
<template v-slot:default="scope">
<el-switch v-model="scope.row.status"
inactive-color="#DCDFE6"
active-value="VALID"
inactive-value="INVALID"
@change="changeSwitch(scope.row)"
<el-switch
v-model="scope.row.status"
inactive-color="#DCDFE6"
active-value="VALID"
inactive-value="INVALID"
@change="changeSwitch(scope.row)"
/>
</template>
</el-table-column>
<el-table-column prop="createTime" :label="$t('commons.create_time')" width="180">
<el-table-column
prop="createTime"
:label="$t('commons.create_time')"
width="180"
>
<template v-slot:default="scope">
<span>{{ scope.row.createTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column prop="updateTime" :label="$t('commons.update_time')" width="180">
<el-table-column
prop="updateTime"
:label="$t('commons.update_time')"
width="180"
>
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | datetimeFormat }}</span>
</template>
@ -41,74 +64,122 @@
<el-table-column :label="$t('commons.operating')">
<template v-slot:default="scope">
<div>
<ms-table-operator :edit-permission="['SYSTEM_TEST_POOL:READ+EDIT']"
:delete-permission="['SYSTEM_TEST_POOL:READ+DELETE']"
@editClick="edit(scope.row)" @deleteClick="del(scope.row)"/>
<ms-table-operator
:edit-permission="['SYSTEM_TEST_POOL:READ+EDIT']"
:delete-permission="['SYSTEM_TEST_POOL:READ+DELETE']"
@editClick="edit(scope.row)"
@deleteClick="del(scope.row)"
/>
</div>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="initTableData" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
<ms-table-pagination
:change="initTableData"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:total="total"
/>
</el-card>
<el-dialog
:close-on-click-modal="false"
:title="form.id ? $t('test_resource_pool.update_resource_pool') : $t('test_resource_pool.create_resource_pool')"
:visible.sync="dialogVisible" width="80%"
:title="
form.id
? $t('test_resource_pool.update_resource_pool')
: $t('test_resource_pool.create_resource_pool')
"
:visible.sync="dialogVisible"
width="80%"
top="5%"
@closed="closeFunc"
:destroy-on-close="true"
v-loading="dialogLoading"
>
<div style="height: 60vh;overflow: auto;">
<el-form :model="form" label-position="right" label-width="140px" size="small" :rules="rule"
ref="testResourcePoolForm">
<div style="height: 60vh; overflow: auto">
<el-form
:model="form"
label-position="right"
label-width="140px"
size="small"
:rules="rule"
ref="testResourcePoolForm"
>
<el-form-item :label="$t('commons.name')" prop="name">
<el-input v-model="form.name" autocomplete="off"/>
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
<el-form-item :label="$t('commons.description')" prop="description">
<el-input v-model="form.description" autocomplete="off"/>
<el-input v-model="form.description" autocomplete="off" />
</el-form-item>
<el-form-item :label="$t('commons.image')" prop="image">
<el-input v-model="form.image"/>
<el-input v-model="form.image" />
</el-form-item>
<el-form-item :label="$t('test_resource_pool.backend_listener')" prop="backendListener" v-xpack>
<el-switch v-model="form.backendListener"/>
<el-form-item
:label="$t('test_resource_pool.backend_listener')"
prop="backendListener"
v-xpack
>
<el-switch v-model="form.backendListener" />
</el-form-item>
<el-form-item :label="$t('test_resource_pool.usage')" prop="usage">
<el-checkbox :label="$t('commons.api')" v-model="form.api"></el-checkbox>
<el-checkbox :label="$t('commons.performance')" v-model="form.performance"></el-checkbox>
<el-checkbox
:label="$t('commons.api')"
v-model="form.api"
></el-checkbox>
<el-checkbox
:label="$t('commons.performance')"
v-model="form.performance"
></el-checkbox>
</el-form-item>
<el-form-item label="JMeter HEAP" prop="HEAP">
<el-input v-model="form.heap" placeholder="-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m" maxlength="200" show-word-limit/>
<el-input
v-model="form.heap"
placeholder="-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m"
maxlength="200"
show-word-limit
/>
</el-form-item>
<el-form-item label="JMeter GC_ALGO" prop="GC_ALGO">
<el-input v-model="form.gcAlgo"
placeholder="-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:G1ReservePercent=20" maxlength="200" show-word-limit/>
<el-input
v-model="form.gcAlgo"
placeholder="-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:G1ReservePercent=20"
maxlength="200"
show-word-limit
/>
</el-form-item>
<el-form-item prop="type" :label="$t('test_resource_pool.type')">
<el-select v-model="form.type" :placeholder="$t('test_resource_pool.select_pool_type')"
@change="changeResourceType(form.type)">
<el-select
v-model="form.type"
:placeholder="$t('test_resource_pool.select_pool_type')"
@change="changeResourceType(form.type)"
>
<el-option key="NODE" value="NODE" label="Node">Node</el-option>
<el-option key="K8S" value="K8S" label="Kubernetes" v-xpack>Kubernetes</el-option>
<el-option key="K8S" value="K8S" label="Kubernetes" v-xpack
>Kubernetes</el-option
>
</el-select>
</el-form-item>
<div class="node-line" v-if="form.type === 'K8S'" v-xpack>
<div v-for="(item,index) in infoList " :key="index">
<div v-for="(item, index) in infoList" :key="index">
<el-row>
<el-col>
<el-form-item label="Master URL"
:rules="requiredRules">
<el-input v-model="item.masterUrl" autocomplete="new-password"/>
<el-form-item label="Master URL" :rules="requiredRules">
<el-input
v-model="item.masterUrl"
autocomplete="new-password"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-form-item label="Token"
:rules="requiredRules">
<el-input v-model="item.token" type="password" show-password autocomplete="new-password"/>
<el-form-item label="Token" :rules="requiredRules">
<el-input
v-model="item.token"
type="password"
show-password
autocomplete="new-password"
/>
</el-form-item>
</el-col>
</el-row>
@ -120,64 +191,96 @@
<el-popover
placement="bottom"
width="450"
trigger="hover">
trigger="hover"
>
<div>
<strong>{{ $t('test_resource_pool.k8s_sa_tips') }}</strong><br>
<el-link type="primary" @click="downloadYaml(item, 'role')">role.yaml</el-link>
<strong>{{
$t("test_resource_pool.k8s_sa_tips")
}}</strong
><br />
<el-link
type="primary"
@click="downloadYaml(item, 'role')"
>role.yaml</el-link
>
</div>
<div style="padding-top: 20px">
<strong>{{ $t('test_resource_pool.k8s_deploy_type_tips') }}</strong><br>
<el-link type="primary" @click="downloadYaml(item, 'DaemonSet')">daemonset.yaml</el-link>
<strong>{{
$t("test_resource_pool.k8s_deploy_type_tips")
}}</strong
><br />
<el-link
type="primary"
@click="downloadYaml(item, 'DaemonSet')"
>daemonset.yaml</el-link
>
&nbsp;
<el-link type="primary" @click="downloadYaml(item, 'Deployment')">deployment.yaml</el-link>
<el-link
type="primary"
@click="downloadYaml(item, 'Deployment')"
>deployment.yaml</el-link
>
</div>
<i class="el-icon-info" slot="reference"></i>
</el-popover>
</template>
<el-input v-model="item.namespace" type="text"/>
<el-input v-model="item.namespace" type="text" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-form-item label="Deploy Name" :rules="requiredRules">
<el-input v-model="item.deployName"/>
<el-input v-model="item.deployName" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-form-item label="API Image">
<el-input v-model="item.apiImage" type="text"/>
<el-input v-model="item.apiImage" type="text" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="6">
<el-form-item :label="$t('test_resource_pool.max_threads')"
:rules="requiredRules">
<el-input-number v-model="item.maxConcurrency" :min="1" :max="1000000000"/>
<el-form-item
:label="$t('test_resource_pool.max_threads')"
:rules="requiredRules"
>
<el-input-number
v-model="item.maxConcurrency"
:min="1"
:max="1000000000"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item :label="$t('test_resource_pool.pod_thread_limit')"
:rules="requiredRules">
<el-input-number v-model="item.podThreadLimit" :min="1" :max="1000000"/>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item :label="$t('test_resource_pool.sync_jar')">
<el-checkbox v-model="item.enable"/>
<el-form-item
:label="$t('test_resource_pool.pod_thread_limit')"
:rules="requiredRules"
>
<el-input-number
v-model="item.podThreadLimit"
:min="1"
:max="1000000"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item>
<template v-slot:label>
<el-link type="primary" @click="jobTemplate">{{ $t('system.test_resource_pool.edit_job_template') }}</el-link>
<el-tooltip :content="$t('system.test_resource_pool.edit_job_template_tip')"
effect="light"
trigger="hover">
<el-link type="primary" @click="jobTemplate">{{
$t("system.test_resource_pool.edit_job_template")
}}</el-link>
<el-tooltip
:content="
$t('system.test_resource_pool.edit_job_template_tip')
"
effect="light"
trigger="hover"
>
<i class="el-icon-info"></i>
</el-tooltip>
</template>
@ -185,90 +288,122 @@
</el-col>
</el-row>
</div>
<job-template ref="jobTemplate" @saveJobTemplate="saveJobTemplate"/>
<job-template
ref="jobTemplate"
@saveJobTemplate="saveJobTemplate"
/>
</div>
<div class="node-line" v-if="form.type === 'NODE'">
<el-row>
<el-col :span="22" :offset="2">
<el-row style="margin-bottom: 10px;">
<el-row style="margin-bottom: 10px">
<el-col :span="8">
<el-button icon="el-icon-circle-plus-outline" plain size="mini"
@click="addResourceInfo()">
{{ $t('commons.add') }}
<el-button
icon="el-icon-circle-plus-outline"
plain
size="mini"
@click="addResourceInfo()"
>
{{ $t("commons.add") }}
</el-button>
<el-button icon="el-icon-circle-plus-outline" plain size="mini"
@click="batchAddResource">
{{ $t('commons.batch_add') }}
<el-button
icon="el-icon-circle-plus-outline"
plain
size="mini"
@click="batchAddResource"
>
{{ $t("commons.batch_add") }}
</el-button>
</el-col>
</el-row>
<el-table :data="infoList" class="tb-edit" align="center" border highlight-current-row>
<el-table-column type="index" width="50"/>
<el-table-column
align="center"
prop="ip"
label="IP">
<template v-slot:default="{row}">
<el-input size="small" v-model="row.ip" autocomplete="off"/>
<el-table
:data="infoList"
class="tb-edit"
align="center"
border
highlight-current-row
>
<el-table-column type="index" width="50" />
<el-table-column align="center" prop="ip" label="IP">
<template v-slot:default="{ row }">
<el-input
size="small"
v-model="row.ip"
autocomplete="off"
/>
</template>
</el-table-column>
<el-table-column
align="center"
prop="port"
label="Port">
<template v-slot:default="{row}">
<el-input-number size="small" v-model="row.port" :min="1" :max="65535"></el-input-number>
<el-table-column align="center" prop="port" label="Port">
<template v-slot:default="{ row }">
<el-input-number
size="small"
v-model="row.port"
:min="1"
:max="65535"
></el-input-number>
</template>
</el-table-column>
<el-table-column
align="center"
prop="monitorPort"
label="Monitor">
<template v-slot:default="{row}">
<el-input-number size="small" v-model="row.monitorPort" :min="1" :max="65535"></el-input-number>
label="Monitor"
>
<template v-slot:default="{ row }">
<el-input-number
size="small"
v-model="row.monitorPort"
:min="1"
:max="65535"
></el-input-number>
</template>
</el-table-column>
<el-table-column
align="center"
prop="maxConcurrency"
:label="$t('test_resource_pool.max_threads')">
<template v-slot:default="{row}">
<el-input-number size="small" v-model="row.maxConcurrency" :min="1"
:max="1000000000"></el-input-number>
:label="$t('test_resource_pool.max_threads')"
>
<template v-slot:default="{ row }">
<el-input-number
size="small"
v-model="row.maxConcurrency"
:min="1"
:max="1000000000"
></el-input-number>
</template>
</el-table-column>
<el-table-column
align="center"
prop="enable"
:label="$t('test_resource_pool.sync_jar')">
<template v-slot:default="{row}">
<el-checkbox size="small" v-model="row.enable"/>
:label="$t('commons.operating')"
>
<template v-slot:default="{ $index }">
<el-button
@click="removeResourceInfo($index)"
type="danger"
icon="el-icon-delete"
size="mini"
circle
/>
</template>
</el-table-column>
<el-table-column align="center" :label="$t('commons.operating')">
<template v-slot:default="{$index}">
<el-button @click="removeResourceInfo($index)" type="danger" icon="el-icon-delete" size="mini"
circle/>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
</div>
<batch-add-resource ref="batchAddResource" @batchSave="batchSave"/>
<batch-add-resource ref="batchAddResource" @batchSave="batchSave" />
</el-form>
</div>
<template v-slot:footer>
<ms-dialog-footer
v-if="form.id"
@cancel="dialogVisible = false"
@confirm="updateTestResourcePool()"/>
@confirm="updateTestResourcePool()"
/>
<ms-dialog-footer
v-else
@cancel="dialogVisible = false"
@confirm="createTestResourcePool()"/>
@confirm="createTestResourcePool()"
/>
</template>
</el-dialog>
</div>
@ -279,24 +414,34 @@ import MsTablePagination from "metersphere-frontend/src/components/pagination/Ta
import MsTableHeader from "metersphere-frontend/src/components/MsTableHeader";
import MsTableOperator from "metersphere-frontend/src/components/MsTableOperator";
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
import {listenGoBack, removeGoBackListener} from "metersphere-frontend/src/utils";
import {
listenGoBack,
removeGoBackListener,
} from "metersphere-frontend/src/utils";
import BatchAddResource from "../components/BatchAddResource";
import {getYaml} from "./test-resource-pool";
import { getYaml } from "./test-resource-pool";
import {
checkResourcePoolUse,
createResourcePool,
delResourcePoolById,
getResourcePoolPages,
modifyResourcePool,
modifyResourcePoolStatus
modifyResourcePoolStatus,
} from "../../../api/resource-pool";
import {getSystemVersion} from "../../../api/system";
import {operationConfirm} from "metersphere-frontend/src/utils";
import { getSystemVersion } from "../../../api/system";
import { operationConfirm } from "metersphere-frontend/src/utils";
import JobTemplate from "@/business/system/components/JobTemplate";
export default {
name: "MsTestResourcePool",
components: {JobTemplate, BatchAddResource, MsTablePagination, MsTableHeader, MsTableOperator, MsDialogFooter},
components: {
JobTemplate,
BatchAddResource,
MsTablePagination,
MsTableHeader,
MsTableOperator,
MsDialogFooter,
},
data() {
return {
loading: false,
@ -310,36 +455,59 @@ export default {
currentPage: 1,
pageSize: 10,
total: 0,
form: {performance: true, api: true, backendListener: true},
screenHeight: 'calc(100vh - 155px)',
requiredRules: [{required: true, message: this.$t('test_resource_pool.fill_the_data'), trigger: 'blur'}],
form: { performance: true, api: true, backendListener: true },
screenHeight: "calc(100vh - 155px)",
requiredRules: [
{
required: true,
message: this.$t("test_resource_pool.fill_the_data"),
trigger: "blur",
},
],
rule: {
name: [
{required: true, message: this.$t('test_resource_pool.input_pool_name'), trigger: 'blur'},
{min: 2, max: 20, message: this.$t('commons.input_limit', [2, 20]), trigger: 'blur'},
{
required: true,
message: this.$t("test_resource_pool.input_pool_name"),
trigger: "blur",
},
{
min: 2,
max: 20,
message: this.$t("commons.input_limit", [2, 20]),
trigger: "blur",
},
{
required: true,
pattern: /^[\u4e00-\u9fa5_a-zA-Z0-9.·-]+$/,
message: this.$t('test_resource_pool.pool_name_valid'),
trigger: 'blur'
}
message: this.$t("test_resource_pool.pool_name_valid"),
trigger: "blur",
},
],
image: [
{max: 100, message: this.$t('commons.input_limit', [0, 100])}
{ max: 100, message: this.$t("commons.input_limit", [0, 100]) },
],
description: [
{max: 60, message: this.$t('commons.input_limit', [0, 60]), trigger: 'blur'}
{
max: 60,
message: this.$t("commons.input_limit", [0, 60]),
trigger: "blur",
},
],
type: [
{required: true, message: this.$t('test_resource_pool.select_pool_type'), trigger: 'blur'}
]
{
required: true,
message: this.$t("test_resource_pool.select_pool_type"),
trigger: "blur",
},
],
},
updatePool: {
testName: '',
haveTestUsePool: false
testName: "",
haveTestUsePool: false,
},
apiImage: '',
apiImageTag: '',
apiImage: "",
apiImageTag: "",
};
},
activated() {
@ -348,28 +516,31 @@ export default {
},
methods: {
initTableData() {
this.loading = getResourcePoolPages(this.currentPage, this.pageSize, this.condition)
.then(res => {
let {listObject, itemCount} = res.data;
this.items = listObject;
this.total = itemCount;
});
this.loading = getResourcePoolPages(
this.currentPage,
this.pageSize,
this.condition
).then((res) => {
let { listObject, itemCount } = res.data;
this.items = listObject;
this.total = itemCount;
});
},
changeResourceType(type) {
this.infoList = [];
let info = {};
if (type === 'NODE') {
info.ip = '';
info.port = '8082';
info.monitorPort = '9100';
if (type === "NODE") {
info.ip = "";
info.port = "8082";
info.monitorPort = "9100";
}
if (type === 'K8S') {
info.masterUrl = '';
info.token = '';
info.namespace = '';
if (type === "K8S") {
info.masterUrl = "";
info.token = "";
info.namespace = "";
info.podThreadLimit = 5000;
info.deployType = 'DaemonSet';
info.deployName = 'ms-node-controller';
info.deployType = "DaemonSet";
info.deployName = "ms-node-controller";
}
info.maxConcurrency = 100;
this.infoList.push(info);
@ -377,16 +548,16 @@ export default {
addResourceInfo() {
this.infoList.push({
port: '8082',
monitorPort: '9100',
maxConcurrency: 100
port: "8082",
monitorPort: "9100",
maxConcurrency: 100,
});
},
removeResourceInfo(index) {
if (this.infoList.length > 1) {
this.infoList.splice(index, 1);
} else {
this.$warning(this.$t('test_resource_pool.cannot_remove_all_node'));
this.$warning(this.$t("test_resource_pool.cannot_remove_all_node"));
}
},
batchAddResource() {
@ -397,14 +568,14 @@ export default {
},
batchSave(resources) {
let targets = this._handleBatchVars(resources);
targets.forEach(row => {
targets.forEach((row) => {
this.infoList.push(row);
});
},
_handleBatchVars(data) {
let params = data.split("\n");
let keyValues = [];
params.forEach(item => {
params.forEach((item) => {
let line = item.split(/|,/);
if (line.length < 3) {
return;
@ -419,23 +590,29 @@ export default {
return keyValues;
},
saveJobTemplate(template) {
this.infoList.forEach(item => {
this.infoList.forEach((item) => {
item.jobTemplate = template;
});
},
validateResourceInfo() {
if (this.infoList.length <= 0) {
return {validate: false, msg: this.$t('test_resource_pool.cannot_empty')};
return {
validate: false,
msg: this.$t("test_resource_pool.cannot_empty"),
};
}
let resourcePoolType = this.form.type;
let resultValidate = {validate: true, msg: this.$t('test_resource_pool.fill_the_data')};
this.infoList.forEach(info => {
let resultValidate = {
validate: true,
msg: this.$t("test_resource_pool.fill_the_data"),
};
this.infoList.forEach((info) => {
for (let key in info) {
//
if (key === 'nodeSelector' || key === 'apiImage') {
if (key === "nodeSelector" || key === "apiImage") {
continue;
}
if (info[key] != '0' && !info[key]) {
if (info[key] != "0" && !info[key]) {
resultValidate.validate = false;
return false;
}
@ -446,11 +623,13 @@ export default {
return false;
}
if (resourcePoolType === 'K8S' && info.nodeSelector) {
if (resourcePoolType === "K8S" && info.nodeSelector) {
let validate = this.isJsonString(info.nodeSelector);
if (!validate) {
resultValidate.validate = false;
resultValidate.msg = this.$t('test_resource_pool.node_selector_invalid');
resultValidate.msg = this.$t(
"test_resource_pool.node_selector_invalid"
);
}
}
});
@ -475,26 +654,32 @@ export default {
this.form.resources.forEach(function (resource) {
let configuration = JSON.parse(resource.configuration);
configuration.id = resource.id;
configuration.monitorPort = configuration.monitorPort || '9100';
configuration.deployType = configuration.deployType || 'DaemonSet';
configuration.deployName = configuration.deployName || 'ms-node-controller';
configuration.monitorPort = configuration.monitorPort || "9100";
configuration.deployType = configuration.deployType || "DaemonSet";
configuration.deployName =
configuration.deployName || "ms-node-controller";
resources.push(configuration);
});
}
this.infoList = resources;
},
del(row) {
operationConfirm(this, this.$t('test_resource_pool.delete_prompt'), () => {
this.loading = delResourcePoolById(row.id).then(() => {
this.initTableData();
this.$success(this.$t('commons.delete_success'));
})
}, () => {
this.$info(this.$t('commons.delete_cancel'));
})
operationConfirm(
this,
this.$t("test_resource_pool.delete_prompt"),
() => {
this.loading = delResourcePoolById(row.id).then(() => {
this.initTableData();
this.$success(this.$t("commons.delete_success"));
});
},
() => {
this.$info(this.$t("commons.delete_cancel"));
}
);
},
createTestResourcePool() {
this.$refs.testResourcePoolForm.validate(valid => {
this.$refs.testResourcePoolForm.validate((valid) => {
if (!valid) {
return false;
}
@ -505,7 +690,7 @@ export default {
}
this.convertSubmitResources();
this.dialogLoading = createResourcePool(this.form).then(() => {
this.$success(this.$t('commons.save_success'));
this.$success(this.$t("commons.save_success"));
this.dialogVisible = false;
this.initTableData();
});
@ -516,7 +701,7 @@ export default {
let poolId = this.form.id;
this.infoList.forEach(function (info) {
let configuration = JSON.stringify(info);
let resource = {"configuration": configuration, id: info.id};
let resource = { configuration: configuration, id: info.id };
if (poolId) {
resource.testResourcePoolId = poolId;
}
@ -525,7 +710,7 @@ export default {
this.form.resources = resources;
},
updateTestResourcePool() {
this.$refs.testResourcePoolForm.validate(valid => {
this.$refs.testResourcePoolForm.validate((valid) => {
if (!valid) {
return false;
}
@ -535,41 +720,47 @@ export default {
}
this.convertSubmitResources();
this.dialogLoading = modifyResourcePool(this.form).then(() => {
this.$success(this.$t('commons.modify_success'));
this.$success(this.$t("commons.modify_success"));
this.dialogVisible = false;
this.initTableData();
});
});
},
closeFunc() {
this.form = {performance: true, api: true, backendListener: true};
this.form = { performance: true, api: true, backendListener: true };
this.dialogVisible = false;
removeGoBackListener(this.closeFunc);
},
changeSwitch(row) {
this.result.loading = true;
this.$info(this.$t('test_resource_pool.check_in'), 1000);
if (row.status === 'VALID') {
this.$info(this.$t("test_resource_pool.check_in"), 1000);
if (row.status === "VALID") {
this.updatePoolStatus(row);
return false;
}
// 使
if (row.status === 'INVALID') {
if (row.status === "INVALID") {
this.checkHaveTestUsePool(row).then(() => {
if (this.updatePool && this.updatePool.haveTestUsePool) {
let testIndex = this.updatePool.testName.indexOf(";")
let testIndex = this.updatePool.testName.indexOf(";");
let subPrompt = this.updatePool.testName.substring(0, testIndex);
this.$confirm(this.$t('test_resource_pool.update_prompt', [subPrompt]), this.$t('commons.prompt'), {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.updatePoolStatus(row);
}).catch(() => {
row.status = 'VALID';
this.result.loading = false;
this.$info(this.$t('commons.cancel'));
});
this.$confirm(
this.$t("test_resource_pool.update_prompt", [subPrompt]),
this.$t("commons.prompt"),
{
confirmButtonText: this.$t("commons.confirm"),
cancelButtonText: this.$t("commons.cancel"),
type: "warning",
}
)
.then(() => {
this.updatePoolStatus(row);
})
.catch(() => {
row.status = "VALID";
this.result.loading = false;
this.$info(this.$t("commons.cancel"));
});
} else {
this.updatePoolStatus(row);
}
@ -577,50 +768,62 @@ export default {
}
},
checkHaveTestUsePool(row) {
return checkResourcePoolUse(row.id).then(res => {
return checkResourcePoolUse(row.id).then((res) => {
this.updatePool = res.data;
})
});
},
updatePoolStatus(row) {
modifyResourcePoolStatus(row.id, row.status).then(() => {
this.$success(row.status === 'VALID' ? this.$t('commons.enable_success') : this.$t('commons.disable_success'));
}).catch(() => {
this.$error(this.$t('test_resource_pool.status_change_failed'));
row.status = 'INVALID';
})
modifyResourcePoolStatus(row.id, row.status)
.then(() => {
this.$success(
row.status === "VALID"
? this.$t("commons.enable_success")
: this.$t("commons.disable_success")
);
})
.catch(() => {
this.$error(this.$t("test_resource_pool.status_change_failed"));
row.status = "INVALID";
});
},
downloadYaml(item, deployType) {
if (!item.namespace) {
this.$error(this.$t('test_resource_pool.fill_the_data'));
this.$error(this.$t("test_resource_pool.fill_the_data"));
return;
}
if (!item.deployName) {
this.$error(this.$t('test_resource_pool.fill_the_data'));
this.$error(this.$t("test_resource_pool.fill_the_data"));
return;
}
let apiImage = 'registry.cn-qingdao.aliyuncs.com/metersphere/node-controller:' + this.apiImageTag;
let apiImage =
"registry.cn-qingdao.aliyuncs.com/metersphere/node-controller:" +
this.apiImageTag;
if (item.apiImage) {
apiImage = item.apiImage;
}
let yaml = getYaml(deployType, item.deployName, item.namespace, apiImage);
let blob = new Blob([yaml], {type: 'application/yaml'});
let blob = new Blob([yaml], { type: "application/yaml" });
let url = URL.createObjectURL(blob);
let downloadAnchorNode = document.createElement('a')
let downloadAnchorNode = document.createElement("a");
downloadAnchorNode.setAttribute("href", url);
downloadAnchorNode.setAttribute("download", deployType.toLowerCase() + ".yaml")
downloadAnchorNode.setAttribute(
"download",
deployType.toLowerCase() + ".yaml"
);
downloadAnchorNode.click();
downloadAnchorNode.remove();
},
getApiImageTag() {
getSystemVersion().then(res => {
if (!res.data) {
this.apiImageTag = 'dev';
return;
}
let i = res.data.lastIndexOf('-');
this.apiImageTag = res.data.substring(0, i);
}).catch(err => {
})
getSystemVersion()
.then((res) => {
if (!res.data) {
this.apiImageTag = "dev";
return;
}
let i = res.data.lastIndexOf("-");
this.apiImageTag = res.data.substring(0, i);
})
.catch((err) => {});
},
isJsonString(str) {
try {
@ -631,15 +834,13 @@ export default {
return false;
}
return false;
}
}
},
},
};
</script>
<style scoped>
.box {
padding-left: 5px;
}
</style>