feat(接口测试): 定义新的组件
This commit is contained in:
parent
ad4709e502
commit
6461c1a6a6
|
@ -11,7 +11,7 @@
|
|||
|
||||
<ms-dropdown :default-command="body.format" v-if="body.type == 'Raw'" :commands="modes" @command="modeChange"/>
|
||||
|
||||
<ms-api-key-value :is-read-only="isReadOnly" :items="body.kvs" v-if="body.isKV()"/>
|
||||
<ms-api-variable :is-read-only="isReadOnly" :items="body.kvs" v-if="body.isKV()"/>
|
||||
|
||||
<div class="body-raw" v-if="body.type == 'Raw'">
|
||||
<ms-code-edit :mode="body.format" :read-only="isReadOnly" :data.sync="body.raw" :modes="modes" ref="codeEdit"/>
|
||||
|
@ -21,24 +21,25 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import MsApiKeyValue from "./ApiKeyValue";
|
||||
import {Body, BODY_FORMAT, BODY_TYPE} from "../model/ScenarioModel";
|
||||
import MsCodeEdit from "../../../common/components/MsCodeEdit";
|
||||
import MsDropdown from "../../../common/components/MsDropdown";
|
||||
import MsApiKeyValue from "./ApiKeyValue";
|
||||
import {Body, BODY_FORMAT, BODY_TYPE} from "../model/ScenarioModel";
|
||||
import MsCodeEdit from "../../../common/components/MsCodeEdit";
|
||||
import MsDropdown from "../../../common/components/MsDropdown";
|
||||
import MsApiVariable from "@/business/components/api/test/components/ApiVariable";
|
||||
|
||||
export default {
|
||||
name: "MsApiBody",
|
||||
components: {MsDropdown, MsCodeEdit, MsApiKeyValue},
|
||||
props: {
|
||||
body: Body,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
export default {
|
||||
name: "MsApiBody",
|
||||
components: {MsApiVariable, MsDropdown, MsCodeEdit, MsApiKeyValue},
|
||||
props: {
|
||||
body: Body,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
data() {
|
||||
return {
|
||||
type: BODY_TYPE,
|
||||
modes: ['text', 'json', 'xml', 'html']
|
||||
};
|
||||
|
|
|
@ -15,18 +15,8 @@
|
|||
|
||||
</el-col>
|
||||
<el-col>
|
||||
<el-autocomplete
|
||||
:disabled="isReadOnly"
|
||||
size="small"
|
||||
class="input-with-autocomplete"
|
||||
v-model="item.value"
|
||||
:fetch-suggestions="funcSearch"
|
||||
:placeholder="valueText"
|
||||
value-key="name"
|
||||
highlight-first-item
|
||||
@select="change">
|
||||
<i slot="suffix" class="el-input__icon el-icon-edit" style="cursor: pointer;" @click="advanced(item)"></i>
|
||||
</el-autocomplete>
|
||||
<el-input :disabled="isReadOnly" v-model="item.value" size="small" @change="change"
|
||||
:placeholder="valueText" show-word-limit/>
|
||||
</el-col>
|
||||
<el-col class="kv-delete">
|
||||
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"
|
||||
|
@ -34,82 +24,11 @@
|
|||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-dialog :title="$t('api_test.request.parameters_advance')"
|
||||
:visible.sync="itemValueVisible"
|
||||
class="advanced-item-value"
|
||||
width="70%">
|
||||
<el-tabs tab-position="top" style="height: 50vh;">
|
||||
<el-tab-pane :label="$t('api_test.request.parameters_advance_mock')">
|
||||
<el-row type="flex" :gutter="20" style="overflow-x: auto;">
|
||||
<el-col :span="6">
|
||||
<el-autocomplete
|
||||
:disabled="isReadOnly"
|
||||
size="small"
|
||||
class="input-with-autocomplete"
|
||||
v-model="itemValue"
|
||||
:fetch-suggestions="funcSearch"
|
||||
:placeholder="valueText"
|
||||
value-key="name"
|
||||
highlight-first-item
|
||||
@select="change">
|
||||
</el-autocomplete>
|
||||
</el-col>
|
||||
<el-col :span="6" v-for="(itemFunc, itemIndex) in itemFuncs" :key="itemIndex">
|
||||
<div v-for="(func, funcIndex) in funcs"
|
||||
:key="`${itemIndex}-${funcIndex}`">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-radio size="mini" v-model="itemFunc.name" :label="func.name"
|
||||
@change="methodChange(itemFunc, func)"/>
|
||||
</el-col>
|
||||
<el-col :span="12" v-if="itemFunc.name === func.name">
|
||||
<div v-for="(p, pIndex) in itemFunc.params" :key="`${itemIndex}-${funcIndex}-${pIndex}`">
|
||||
<el-input :placeholder="p.name" size="mini" v-model="p.value" @change="showPreview"/>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="变量">
|
||||
<el-row>
|
||||
<el-col :span="6">
|
||||
环境
|
||||
场景
|
||||
请求1
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div style="padding-top: 10px;">
|
||||
<el-row type="flex" align="middle">
|
||||
<el-col :span="12">
|
||||
<el-button size="small" type="primary" plain @click="saveAdvanced()">
|
||||
{{ $t('commons.save') }}
|
||||
</el-button>
|
||||
<el-button size="small" type="info" plain @click="addFunc()">
|
||||
{{ $t('api_test.request.parameters_advance_add_func') }}
|
||||
</el-button>
|
||||
<el-button size="small" type="success" plain @click="showPreview()">
|
||||
{{ $t('api_test.request.parameters_preview') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<div> {{ itemValuePreview }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {KeyValue} from "../model/ScenarioModel";
|
||||
import {MOCKJS_FUNC} from "@/common/js/constants";
|
||||
import {calculate} from "@/business/components/api/test/model/ScenarioModel";
|
||||
|
||||
export default {
|
||||
name: "MsApiKeyValue",
|
||||
|
@ -126,40 +45,6 @@ export default {
|
|||
suggestions: Array
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
itemValueVisible: false,
|
||||
itemValue: null,
|
||||
funcs: [
|
||||
{name: "md5"},
|
||||
{name: "base64"},
|
||||
{name: "unbase64"},
|
||||
{
|
||||
name: "substr",
|
||||
params: [{name: "start"}, {name: "length"}]
|
||||
},
|
||||
{
|
||||
name: "concat",
|
||||
params: [{name: "suffix"}]
|
||||
},
|
||||
{name: "lconcat", params: [{name: "prefix"}]},
|
||||
{name: "sha1"},
|
||||
{name: "sha224"},
|
||||
{name: "sha256"},
|
||||
{name: "sha384"},
|
||||
{name: "sha512"},
|
||||
{name: "lower"},
|
||||
{name: "upper"},
|
||||
{name: "length"},
|
||||
{name: "number"}
|
||||
],
|
||||
itemValuePreview: null,
|
||||
itemFuncs: [],
|
||||
currentFunc: "",
|
||||
mockFuncs: MOCKJS_FUNC,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
keyText() {
|
||||
return this.keyPlaceholder || this.$t("api_test.key");
|
||||
|
@ -206,80 +91,6 @@ export default {
|
|||
return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0);
|
||||
};
|
||||
},
|
||||
funcSearch(queryString, cb) {
|
||||
let funcs = MOCKJS_FUNC;
|
||||
let results = queryString ? funcs.filter(this.funcFilter(queryString)) : funcs;
|
||||
// 调用 callback 返回建议列表的数据
|
||||
cb(results);
|
||||
},
|
||||
funcFilter(queryString) {
|
||||
return (func) => {
|
||||
return (func.name.toLowerCase().indexOf(queryString.toLowerCase()) > -1);
|
||||
};
|
||||
},
|
||||
showPreview() {
|
||||
// 找到变量本身
|
||||
if (!this.itemValue) {
|
||||
return;
|
||||
}
|
||||
let index = this.itemValue.indexOf("|");
|
||||
if (index > -1) {
|
||||
this.itemValue = this.itemValue.substring(0, index).trim();
|
||||
}
|
||||
|
||||
this.itemFuncs.forEach(f => {
|
||||
if (!f.name) {
|
||||
return;
|
||||
}
|
||||
this.itemValue += "|" + f.name;
|
||||
if (f.params) {
|
||||
this.itemValue += ":" + f.params.map(p => p.value).join(",");
|
||||
}
|
||||
});
|
||||
|
||||
this.itemValuePreview = calculate(this.itemValue);
|
||||
},
|
||||
methodChange(itemFunc, func) {
|
||||
let index = this.itemFuncs.indexOf(itemFunc);
|
||||
this.itemFuncs = this.itemFuncs.slice(0, index);
|
||||
// 这里要用 deep copy
|
||||
this.itemFuncs.push(JSON.parse(JSON.stringify(func)));
|
||||
this.showPreview();
|
||||
},
|
||||
addFunc() {
|
||||
if (this.itemFuncs.length > 4) {
|
||||
this.$info(this.$t('api_test.request.parameters_advance_add_func_limit'));
|
||||
return;
|
||||
}
|
||||
if (this.itemFuncs.length > 0) {
|
||||
let func = this.itemFuncs[this.itemFuncs.length - 1];
|
||||
if (!func.name) {
|
||||
this.$warning(this.$t('api_test.request.parameters_advance_add_func_error'));
|
||||
return;
|
||||
}
|
||||
if (func.params) {
|
||||
for (let j = 0; j < func.params.length; j++) {
|
||||
if (!func.params[j].value) {
|
||||
this.$warning(this.$t('api_test.request.parameters_advance_add_param_error'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.itemFuncs.push({name: '', params: []});
|
||||
},
|
||||
advanced(item) {
|
||||
this.currentItem = item;
|
||||
this.itemValueVisible = true;
|
||||
this.itemValue = '';
|
||||
this.itemValuePreview = null;
|
||||
this.itemFuncs = [];
|
||||
},
|
||||
saveAdvanced() {
|
||||
this.currentItem.value = this.itemValue;
|
||||
this.itemValueVisible = false;
|
||||
this.itemFuncs = [];
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.items.length === 0) {
|
||||
|
@ -305,13 +116,4 @@ export default {
|
|||
.el-autocomplete {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.advanced-item-value >>> .el-dialog__body {
|
||||
padding: 15px 25px;
|
||||
}
|
||||
|
||||
.el-row {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -11,18 +11,18 @@
|
|||
:label="environment.name + ': ' + environment.protocol + '://' + environment.socket"
|
||||
:value="environment.id"/>
|
||||
<el-button class="environment-button" size="mini" type="primary" @click="openEnvironmentConfig">
|
||||
{{$t('api_test.environment.environment_config')}}
|
||||
{{ $t('api_test.environment.environment_config') }}
|
||||
</el-button>
|
||||
<template v-slot:empty>
|
||||
<div class="empty-environment">
|
||||
<el-button class="environment-button" size="mini" type="primary" @click="openEnvironmentConfig">
|
||||
{{$t('api_test.environment.environment_config')}}
|
||||
{{ $t('api_test.environment.environment_config') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-select>
|
||||
<el-form-item class="cookie-item">
|
||||
<el-checkbox v-model="scenario.enableCookieShare">{{'共享 Cookie'}}</el-checkbox>
|
||||
<el-checkbox v-model="scenario.enableCookieShare">{{ '共享 Cookie' }}</el-checkbox>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
|||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('api_test.scenario.headers')" name="headers">
|
||||
<ms-api-key-value :is-read-only="isReadOnly" :items="scenario.headers" :suggestions="headerSuggestions"
|
||||
:environment="scenario.environment"
|
||||
:description="$t('api_test.scenario.kv_description')"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('api_test.scenario.dubbo')" name="dubbo">
|
||||
|
@ -52,127 +53,127 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import MsApiKeyValue from "./ApiKeyValue";
|
||||
import {Scenario} from "../model/ScenarioModel";
|
||||
import MsApiScenarioVariables from "./ApiScenarioVariables";
|
||||
import ApiEnvironmentConfig from "./ApiEnvironmentConfig";
|
||||
import {REQUEST_HEADERS} from "@/common/js/constants";
|
||||
import MsDubboRegistryCenter from "@/business/components/api/test/components/request/dubbo/RegistryCenter";
|
||||
import MsDubboConfigCenter from "@/business/components/api/test/components/request/dubbo/ConfigCenter";
|
||||
import MsDubboConsumerService from "@/business/components/api/test/components/request/dubbo/ConsumerAndService";
|
||||
import MsApiKeyValue from "./ApiKeyValue";
|
||||
import {Scenario} from "../model/ScenarioModel";
|
||||
import MsApiScenarioVariables from "./ApiScenarioVariables";
|
||||
import ApiEnvironmentConfig from "./ApiEnvironmentConfig";
|
||||
import {REQUEST_HEADERS} from "@/common/js/constants";
|
||||
import MsDubboRegistryCenter from "@/business/components/api/test/components/request/dubbo/RegistryCenter";
|
||||
import MsDubboConfigCenter from "@/business/components/api/test/components/request/dubbo/ConfigCenter";
|
||||
import MsDubboConsumerService from "@/business/components/api/test/components/request/dubbo/ConsumerAndService";
|
||||
|
||||
export default {
|
||||
name: "MsApiScenarioForm",
|
||||
components: {
|
||||
MsDubboConsumerService,
|
||||
MsDubboConfigCenter, MsDubboRegistryCenter, ApiEnvironmentConfig, MsApiScenarioVariables, MsApiKeyValue
|
||||
},
|
||||
props: {
|
||||
scenario: Scenario,
|
||||
projectId: String,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
export default {
|
||||
name: "MsApiScenarioForm",
|
||||
components: {
|
||||
MsDubboConsumerService,
|
||||
MsDubboConfigCenter, MsDubboRegistryCenter, ApiEnvironmentConfig, MsApiScenarioVariables, MsApiKeyValue
|
||||
},
|
||||
props: {
|
||||
scenario: Scenario,
|
||||
projectId: String,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getEnvironments();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
activeName: "parameters",
|
||||
environments: [],
|
||||
rules: {
|
||||
name: [
|
||||
{max: 100, message: this.$t('commons.input_limit', [1, 100]), trigger: 'blur'}
|
||||
],
|
||||
url: [
|
||||
{max: 100, message: this.$t('commons.input_limit', [1, 100]), trigger: 'blur'}
|
||||
]
|
||||
},
|
||||
headerSuggestions: REQUEST_HEADERS
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
projectId() {
|
||||
this.getEnvironments();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
activeName: "parameters",
|
||||
environments: [],
|
||||
rules: {
|
||||
name: [
|
||||
{max: 100, message: this.$t('commons.input_limit', [1, 100]), trigger: 'blur'}
|
||||
],
|
||||
url: [
|
||||
{max: 100, message: this.$t('commons.input_limit', [1, 100]), trigger: 'blur'}
|
||||
]
|
||||
},
|
||||
headerSuggestions: REQUEST_HEADERS
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
projectId() {
|
||||
this.getEnvironments();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getEnvironments() {
|
||||
if (this.projectId) {
|
||||
this.result = this.$get('/api/environment/list/' + this.projectId, response => {
|
||||
this.environments = response.data;
|
||||
let hasEnvironment = false;
|
||||
for (let i in this.environments) {
|
||||
if (this.environments[i].id === this.scenario.environmentId) {
|
||||
this.scenario.environment = this.environments[i];
|
||||
hasEnvironment = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getEnvironments() {
|
||||
if (this.projectId) {
|
||||
this.result = this.$get('/api/environment/list/' + this.projectId, response => {
|
||||
this.environments = response.data;
|
||||
let hasEnvironment = false;
|
||||
for (let i in this.environments) {
|
||||
if (this.environments[i].id === this.scenario.environmentId) {
|
||||
this.scenario.environment = this.environments[i];
|
||||
hasEnvironment = true;
|
||||
break;
|
||||
}
|
||||
if (!hasEnvironment) {
|
||||
this.scenario.environmentId = '';
|
||||
this.scenario.environment = undefined;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.scenario.environmentId = '';
|
||||
this.scenario.environment = undefined;
|
||||
}
|
||||
},
|
||||
environmentChange(value) {
|
||||
for (let i in this.environments) {
|
||||
if (this.environments[i].id === value) {
|
||||
this.scenario.environment = this.environments[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!value) {
|
||||
this.scenario.environment = undefined;
|
||||
this.scenario.requests.forEach(request => {
|
||||
request.environment = undefined;
|
||||
});
|
||||
}
|
||||
},
|
||||
openEnvironmentConfig() {
|
||||
if (!this.projectId) {
|
||||
this.$error(this.$t('api_test.select_project'));
|
||||
return;
|
||||
}
|
||||
this.$refs.environmentConfig.open(this.projectId);
|
||||
},
|
||||
environmentConfigClose() {
|
||||
this.getEnvironments();
|
||||
if (!hasEnvironment) {
|
||||
this.scenario.environmentId = '';
|
||||
this.scenario.environment = undefined;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.scenario.environmentId = '';
|
||||
this.scenario.environment = undefined;
|
||||
}
|
||||
},
|
||||
environmentChange(value) {
|
||||
for (let i in this.environments) {
|
||||
if (this.environments[i].id === value) {
|
||||
this.scenario.environment = this.environments[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!value) {
|
||||
this.scenario.environment = undefined;
|
||||
this.scenario.requests.forEach(request => {
|
||||
request.environment = undefined;
|
||||
});
|
||||
}
|
||||
},
|
||||
openEnvironmentConfig() {
|
||||
if (!this.projectId) {
|
||||
this.$error(this.$t('api_test.select_project'));
|
||||
return;
|
||||
}
|
||||
this.$refs.environmentConfig.open(this.projectId);
|
||||
},
|
||||
environmentConfigClose() {
|
||||
this.getEnvironments();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.environment-select {
|
||||
width: 100%;
|
||||
}
|
||||
.environment-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.environment-button {
|
||||
margin-left: 20px;
|
||||
padding: 7px;
|
||||
}
|
||||
.environment-button {
|
||||
margin-left: 20px;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
.empty-environment {
|
||||
padding: 10px 0;
|
||||
}
|
||||
.empty-environment {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.dubbo-config-title {
|
||||
margin-bottom: 10px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.dubbo-config-title {
|
||||
margin-bottom: 10px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.cookie-item {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.cookie-item {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,341 @@
|
|||
<template>
|
||||
<div>
|
||||
<span class="kv-description" v-if="description">
|
||||
{{ description }}
|
||||
</span>
|
||||
<div class="kv-row" v-for="(item, index) in items" :key="index">
|
||||
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
|
||||
<el-col>
|
||||
<el-input v-if="!suggestions" :disabled="isReadOnly" v-model="item.name" size="small" maxlength="200"
|
||||
@change="change"
|
||||
:placeholder="keyText" show-word-limit/>
|
||||
<el-autocomplete :disabled="isReadOnly" :maxlength="200" v-if="suggestions" v-model="item.name" size="small"
|
||||
:fetch-suggestions="querySearch" @change="change" :placeholder="keyText"
|
||||
show-word-limit/>
|
||||
|
||||
</el-col>
|
||||
<el-col>
|
||||
<el-autocomplete
|
||||
:disabled="isReadOnly"
|
||||
size="small"
|
||||
class="input-with-autocomplete"
|
||||
v-model="item.value"
|
||||
:fetch-suggestions="funcSearch"
|
||||
:placeholder="valueText"
|
||||
value-key="name"
|
||||
highlight-first-item
|
||||
@select="change">
|
||||
<i slot="suffix" class="el-input__icon el-icon-edit" style="cursor: pointer;" @click="advanced(item)"></i>
|
||||
</el-autocomplete>
|
||||
</el-col>
|
||||
<el-col class="kv-delete">
|
||||
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"
|
||||
:disabled="isDisable(index) || isReadOnly"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-dialog :title="$t('api_test.request.parameters_advance')"
|
||||
:visible.sync="itemValueVisible"
|
||||
class="advanced-item-value"
|
||||
width="70%">
|
||||
<el-tabs tab-position="top" style="height: 50vh;">
|
||||
<el-tab-pane :label="$t('api_test.request.parameters_advance_mock')">
|
||||
<el-row type="flex" :gutter="20" style="overflow-x: auto;">
|
||||
<el-col :span="6">
|
||||
<el-autocomplete
|
||||
:disabled="isReadOnly"
|
||||
size="small"
|
||||
class="input-with-autocomplete"
|
||||
v-model="itemValue"
|
||||
:fetch-suggestions="funcSearch"
|
||||
:placeholder="valueText"
|
||||
value-key="name"
|
||||
highlight-first-item
|
||||
@select="change">
|
||||
</el-autocomplete>
|
||||
</el-col>
|
||||
<el-col :span="6" v-for="(itemFunc, itemIndex) in itemFuncs" :key="itemIndex">
|
||||
<div v-for="(func, funcIndex) in funcs"
|
||||
:key="`${itemIndex}-${funcIndex}`">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-radio size="mini" v-model="itemFunc.name" :label="func.name"
|
||||
@change="methodChange(itemFunc, func)"/>
|
||||
</el-col>
|
||||
<el-col :span="12" v-if="itemFunc.name === func.name">
|
||||
<div v-for="(p, pIndex) in itemFunc.params" :key="`${itemIndex}-${funcIndex}-${pIndex}`">
|
||||
<el-input :placeholder="p.name" size="mini" v-model="p.value" @change="showPreview"/>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="变量">
|
||||
<el-row>
|
||||
<el-col :span="6">
|
||||
<div v-if="environment">
|
||||
<el-tree :data="environmentParams" :props="{ children: 'children', label: 'name'}"></el-tree>
|
||||
</div>
|
||||
场景
|
||||
请求1
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div style="padding-top: 10px;">
|
||||
<el-row type="flex" align="middle">
|
||||
<el-col :span="12">
|
||||
<el-button size="small" type="primary" plain @click="saveAdvanced()">
|
||||
{{ $t('commons.save') }}
|
||||
</el-button>
|
||||
<el-button size="small" type="info" plain @click="addFunc()">
|
||||
{{ $t('api_test.request.parameters_advance_add_func') }}
|
||||
</el-button>
|
||||
<el-button size="small" type="success" plain @click="showPreview()">
|
||||
{{ $t('api_test.request.parameters_preview') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<div> {{ itemValuePreview }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {KeyValue} from "../model/ScenarioModel";
|
||||
import {MOCKJS_FUNC} from "@/common/js/constants";
|
||||
import {calculate} from "@/business/components/api/test/model/ScenarioModel";
|
||||
|
||||
export default {
|
||||
name: "MsApiVariable",
|
||||
|
||||
props: {
|
||||
keyPlaceholder: String,
|
||||
valuePlaceholder: String,
|
||||
description: String,
|
||||
items: Array,
|
||||
environment: Object,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
suggestions: Array
|
||||
},
|
||||
mounted() {
|
||||
if (this.environment) {
|
||||
let variables = JSON.parse(this.environment.variables);
|
||||
let headers = JSON.parse(this.environment.headers);
|
||||
this.environmentParams = [
|
||||
{
|
||||
name: this.environment.name,
|
||||
children: [
|
||||
{
|
||||
name: 'variables',
|
||||
children: variables.filter(v => v.name)
|
||||
},
|
||||
{
|
||||
name: 'headers',
|
||||
children: headers.filter(v => v.name)
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
itemValueVisible: false,
|
||||
itemValue: null,
|
||||
funcs: [
|
||||
{name: "md5"},
|
||||
{name: "base64"},
|
||||
{name: "unbase64"},
|
||||
{
|
||||
name: "substr",
|
||||
params: [{name: "start"}, {name: "length"}]
|
||||
},
|
||||
{
|
||||
name: "concat",
|
||||
params: [{name: "suffix"}]
|
||||
},
|
||||
{name: "lconcat", params: [{name: "prefix"}]},
|
||||
{name: "sha1"},
|
||||
{name: "sha224"},
|
||||
{name: "sha256"},
|
||||
{name: "sha384"},
|
||||
{name: "sha512"},
|
||||
{name: "lower"},
|
||||
{name: "upper"},
|
||||
{name: "length"},
|
||||
{name: "number"}
|
||||
],
|
||||
itemValuePreview: null,
|
||||
itemFuncs: [],
|
||||
currentFunc: "",
|
||||
mockFuncs: MOCKJS_FUNC,
|
||||
environmentParams: [],
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
keyText() {
|
||||
return this.keyPlaceholder || this.$t("api_test.key");
|
||||
},
|
||||
valueText() {
|
||||
return this.valuePlaceholder || this.$t("api_test.value");
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
remove: function (index) {
|
||||
this.items.splice(index, 1);
|
||||
this.$emit('change', this.items);
|
||||
},
|
||||
change: function () {
|
||||
let isNeedCreate = true;
|
||||
let removeIndex = -1;
|
||||
this.items.forEach((item, index) => {
|
||||
if (!item.name && !item.value) {
|
||||
// 多余的空行
|
||||
if (index !== this.items.length - 1) {
|
||||
removeIndex = index;
|
||||
}
|
||||
// 没有空行,需要创建空行
|
||||
isNeedCreate = false;
|
||||
}
|
||||
});
|
||||
if (isNeedCreate) {
|
||||
this.items.push(new KeyValue());
|
||||
}
|
||||
this.$emit('change', this.items);
|
||||
// TODO 检查key重复
|
||||
},
|
||||
isDisable: function (index) {
|
||||
return this.items.length - 1 === index;
|
||||
},
|
||||
querySearch(queryString, cb) {
|
||||
let suggestions = this.suggestions;
|
||||
let results = queryString ? suggestions.filter(this.createFilter(queryString)) : suggestions;
|
||||
cb(results);
|
||||
},
|
||||
createFilter(queryString) {
|
||||
return (restaurant) => {
|
||||
return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0);
|
||||
};
|
||||
},
|
||||
funcSearch(queryString, cb) {
|
||||
let funcs = MOCKJS_FUNC;
|
||||
let results = queryString ? funcs.filter(this.funcFilter(queryString)) : funcs;
|
||||
// 调用 callback 返回建议列表的数据
|
||||
cb(results);
|
||||
},
|
||||
funcFilter(queryString) {
|
||||
return (func) => {
|
||||
return (func.name.toLowerCase().indexOf(queryString.toLowerCase()) > -1);
|
||||
};
|
||||
},
|
||||
showPreview() {
|
||||
// 找到变量本身
|
||||
if (!this.itemValue) {
|
||||
return;
|
||||
}
|
||||
let index = this.itemValue.indexOf("|");
|
||||
if (index > -1) {
|
||||
this.itemValue = this.itemValue.substring(0, index).trim();
|
||||
}
|
||||
|
||||
this.itemFuncs.forEach(f => {
|
||||
if (!f.name) {
|
||||
return;
|
||||
}
|
||||
this.itemValue += "|" + f.name;
|
||||
if (f.params) {
|
||||
this.itemValue += ":" + f.params.map(p => p.value).join(",");
|
||||
}
|
||||
});
|
||||
|
||||
this.itemValuePreview = calculate(this.itemValue);
|
||||
},
|
||||
methodChange(itemFunc, func) {
|
||||
let index = this.itemFuncs.indexOf(itemFunc);
|
||||
this.itemFuncs = this.itemFuncs.slice(0, index);
|
||||
// 这里要用 deep copy
|
||||
this.itemFuncs.push(JSON.parse(JSON.stringify(func)));
|
||||
this.showPreview();
|
||||
},
|
||||
addFunc() {
|
||||
if (this.itemFuncs.length > 4) {
|
||||
this.$info(this.$t('api_test.request.parameters_advance_add_func_limit'));
|
||||
return;
|
||||
}
|
||||
if (this.itemFuncs.length > 0) {
|
||||
let func = this.itemFuncs[this.itemFuncs.length - 1];
|
||||
if (!func.name) {
|
||||
this.$warning(this.$t('api_test.request.parameters_advance_add_func_error'));
|
||||
return;
|
||||
}
|
||||
if (func.params) {
|
||||
for (let j = 0; j < func.params.length; j++) {
|
||||
if (!func.params[j].value) {
|
||||
this.$warning(this.$t('api_test.request.parameters_advance_add_param_error'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.itemFuncs.push({name: '', params: []});
|
||||
},
|
||||
advanced(item) {
|
||||
this.currentItem = item;
|
||||
this.itemValueVisible = true;
|
||||
this.itemValue = '';
|
||||
this.itemValuePreview = null;
|
||||
this.itemFuncs = [];
|
||||
},
|
||||
saveAdvanced() {
|
||||
this.currentItem.value = this.itemValue;
|
||||
this.itemValueVisible = false;
|
||||
this.itemFuncs = [];
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.items.length === 0) {
|
||||
this.items.push(new KeyValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.kv-description {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.kv-row {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.kv-delete {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.el-autocomplete {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.advanced-item-value >>> .el-dialog__body {
|
||||
padding: 15px 25px;
|
||||
}
|
||||
|
||||
.el-row {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -26,9 +26,10 @@
|
|||
|
||||
<el-form-item v-if="request.useEnvironment" :label="$t('api_test.request.address')" class="adjust-margin-bottom">
|
||||
<el-tag class="environment-display">
|
||||
<span class="environment-name">{{request.environment ? request.environment.name + ': ' : ''}}</span>
|
||||
<span class="environment-url">{{displayUrl}}</span>
|
||||
<span v-if="!displayUrl" class="environment-url-tip">{{$t('api_test.request.please_configure_environment_in_scenario')}}</span>
|
||||
<span class="environment-name">{{ request.environment ? request.environment.name + ': ' : '' }}</span>
|
||||
<span class="environment-url">{{ displayUrl }}</span>
|
||||
<span v-if="!displayUrl"
|
||||
class="environment-url-tip">{{ $t('api_test.request.please_configure_environment_in_scenario') }}</span>
|
||||
</el-tag>
|
||||
</el-form-item>
|
||||
|
||||
|
@ -39,12 +40,14 @@
|
|||
</el-switch>
|
||||
</el-form-item>
|
||||
|
||||
<el-button class="debug-button" size="small" type="primary" @click="runDebug">{{$t('load_test.save_and_run')}}</el-button>
|
||||
<el-button class="debug-button" size="small" type="primary" @click="runDebug">{{ $t('load_test.save_and_run') }}
|
||||
</el-button>
|
||||
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane :label="$t('api_test.request.parameters')" name="parameters">
|
||||
<ms-api-key-value :is-read-only="isReadOnly" :items="request.parameters"
|
||||
:description="$t('api_test.request.parameters_desc')"/>
|
||||
<ms-api-variable :is-read-only="isReadOnly" :items="request.parameters"
|
||||
:environment="request.environment"
|
||||
:description="$t('api_test.request.parameters_desc')"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('api_test.request.headers')" name="headers">
|
||||
<ms-api-key-value :is-read-only="isReadOnly" :suggestions="headerSuggestions" :items="request.headers"/>
|
||||
|
@ -70,10 +73,11 @@ import {HttpRequest, KeyValue} from "../../model/ScenarioModel";
|
|||
import MsApiExtract from "../extract/ApiExtract";
|
||||
import ApiRequestMethodSelect from "../collapse/ApiRequestMethodSelect";
|
||||
import {REQUEST_HEADERS} from "@/common/js/constants";
|
||||
import MsApiVariable from "@/business/components/api/test/components/ApiVariable";
|
||||
|
||||
export default {
|
||||
name: "MsApiHttpRequestForm",
|
||||
components: {ApiRequestMethodSelect, MsApiExtract, MsApiAssertions, MsApiBody, MsApiKeyValue},
|
||||
components: {MsApiVariable, ApiRequestMethodSelect, MsApiExtract, MsApiAssertions, MsApiBody, MsApiKeyValue},
|
||||
props: {
|
||||
request: HttpRequest,
|
||||
isReadOnly: {
|
||||
|
@ -91,109 +95,109 @@ export default {
|
|||
}
|
||||
};
|
||||
return {
|
||||
activeName: "parameters",
|
||||
rules: {
|
||||
name: [
|
||||
{max: 300, message: this.$t('commons.input_limit', [1, 300]), trigger: 'blur'}
|
||||
],
|
||||
url: [
|
||||
{max: 500, required: true, message: this.$t('commons.input_limit', [1, 500]), trigger: 'blur'},
|
||||
{validator: validateURL, trigger: 'blur'}
|
||||
],
|
||||
path: [
|
||||
{max: 500, message: this.$t('commons.input_limit', [0, 500]), trigger: 'blur'},
|
||||
]
|
||||
},
|
||||
headerSuggestions: REQUEST_HEADERS
|
||||
activeName: "parameters",
|
||||
rules: {
|
||||
name: [
|
||||
{max: 300, message: this.$t('commons.input_limit', [1, 300]), trigger: 'blur'}
|
||||
],
|
||||
url: [
|
||||
{max: 500, required: true, message: this.$t('commons.input_limit', [1, 500]), trigger: 'blur'},
|
||||
{validator: validateURL, trigger: 'blur'}
|
||||
],
|
||||
path: [
|
||||
{max: 500, message: this.$t('commons.input_limit', [0, 500]), trigger: 'blur'},
|
||||
]
|
||||
},
|
||||
headerSuggestions: REQUEST_HEADERS
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
urlChange() {
|
||||
if (!this.request.url) return;
|
||||
let url = this.getURL(this.addProtocol(this.request.url));
|
||||
if (url) {
|
||||
this.request.url = decodeURIComponent(url.origin + url.pathname);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
urlChange() {
|
||||
if (!this.request.url) return;
|
||||
let url = this.getURL(this.addProtocol(this.request.url));
|
||||
if (url) {
|
||||
this.request.url = decodeURIComponent(url.origin + url.pathname);
|
||||
}
|
||||
},
|
||||
pathChange() {
|
||||
if (!this.request.path) return;
|
||||
let url = this.getURL(this.displayUrl);
|
||||
let urlStr = url.origin + url.pathname;
|
||||
let envUrl = this.request.environment.protocol + '://' + this.request.environment.socket;
|
||||
this.request.path = decodeURIComponent(urlStr.substring(envUrl.length, urlStr.length));
|
||||
},
|
||||
getURL(urlStr) {
|
||||
try {
|
||||
let url = new URL(urlStr);
|
||||
url.searchParams.forEach((value, key) => {
|
||||
if (key && value) {
|
||||
this.request.parameters.splice(0, 0, new KeyValue(key, value));
|
||||
}
|
||||
});
|
||||
return url;
|
||||
} catch (e) {
|
||||
this.$error(this.$t('api_test.request.url_invalid'), 2000);
|
||||
}
|
||||
},
|
||||
methodChange(value) {
|
||||
if (value === 'GET' && this.activeName === 'body') {
|
||||
this.activeName = 'parameters';
|
||||
}
|
||||
},
|
||||
useEnvironmentChange(value) {
|
||||
if (value && !this.request.environment) {
|
||||
this.$error(this.$t('api_test.request.please_add_environment_to_scenario'), 2000);
|
||||
this.request.useEnvironment = false;
|
||||
}
|
||||
this.$refs["request"].clearValidate();
|
||||
},
|
||||
addProtocol(url) {
|
||||
if (url) {
|
||||
if (!url.toLowerCase().startsWith("https") && !url.toLowerCase().startsWith("http")) {
|
||||
return "https://" + url;
|
||||
pathChange() {
|
||||
if (!this.request.path) return;
|
||||
let url = this.getURL(this.displayUrl);
|
||||
let urlStr = url.origin + url.pathname;
|
||||
let envUrl = this.request.environment.protocol + '://' + this.request.environment.socket;
|
||||
this.request.path = decodeURIComponent(urlStr.substring(envUrl.length, urlStr.length));
|
||||
},
|
||||
getURL(urlStr) {
|
||||
try {
|
||||
let url = new URL(urlStr);
|
||||
url.searchParams.forEach((value, key) => {
|
||||
if (key && value) {
|
||||
this.request.parameters.splice(0, 0, new KeyValue(key, value));
|
||||
}
|
||||
}
|
||||
});
|
||||
return url;
|
||||
},
|
||||
runDebug() {
|
||||
this.$emit('runDebug');
|
||||
} catch (e) {
|
||||
this.$error(this.$t('api_test.request.url_invalid'), 2000);
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
isNotGet() {
|
||||
return this.request.method !== "GET";
|
||||
},
|
||||
displayUrl() {
|
||||
return this.request.environment ? this.request.environment.protocol + '://' + this.request.environment.socket + (this.request.path ? this.request.path : '') : '';
|
||||
methodChange(value) {
|
||||
if (value === 'GET' && this.activeName === 'body') {
|
||||
this.activeName = 'parameters';
|
||||
}
|
||||
},
|
||||
useEnvironmentChange(value) {
|
||||
if (value && !this.request.environment) {
|
||||
this.$error(this.$t('api_test.request.please_add_environment_to_scenario'), 2000);
|
||||
this.request.useEnvironment = false;
|
||||
}
|
||||
this.$refs["request"].clearValidate();
|
||||
},
|
||||
addProtocol(url) {
|
||||
if (url) {
|
||||
if (!url.toLowerCase().startsWith("https") && !url.toLowerCase().startsWith("http")) {
|
||||
return "https://" + url;
|
||||
}
|
||||
}
|
||||
return url;
|
||||
},
|
||||
runDebug() {
|
||||
this.$emit('runDebug');
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
isNotGet() {
|
||||
return this.request.method !== "GET";
|
||||
},
|
||||
displayUrl() {
|
||||
return this.request.environment ? this.request.environment.protocol + '://' + this.request.environment.socket + (this.request.path ? this.request.path : '') : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-tag {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
.el-tag {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.environment-display {
|
||||
font-size: 14px;
|
||||
}
|
||||
.environment-display {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.environment-name {
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
}
|
||||
.environment-name {
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.adjust-margin-bottom {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.adjust-margin-bottom {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.environment-url-tip {
|
||||
color: #F56C6C;
|
||||
}
|
||||
.environment-url-tip {
|
||||
color: #F56C6C;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue