提交 f9f1809f 编写于 作者: Q q4speed

api界面,未完待续

上级 336b6c4a
<template>
<div class="container">
<div class="main-content">
<el-card>
<el-container class="scenario-container">
<el-header>
<span class="scenario-title">场景配置</span>
</el-header>
<el-container>
<el-aside class="scenario-aside">
<div class="scenario-list">
<ms-api-collapse v-model="activeName" @change="handleChange" accordion>
<ms-api-collapse-item v-for="(scenario, index) in scenarios" :key="index"
:title="scenario.name" :name="index">
<template slot="title">
<div class="scenario-name">{{scenario.name}}</div>
<el-dropdown trigger="click" @command="handleCommand">
<span class="el-dropdown-link el-icon-more scenario-btn"/>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{type:'delete', index:index}">删除场景</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<ms-api-request :requests="scenario.requests" :open="select"/>
</ms-api-collapse-item>
</ms-api-collapse>
</div>
<el-button class="scenario-create" type="primary" size="mini" icon="el-icon-plus" plain @click="create"/>
</el-aside>
<el-main class="scenario-main">
<div class="scenario-form">
<ms-api-scenario-form :scenario="selected"></ms-api-scenario-form>
<ms-api-request-form :request="selected"></ms-api-request-form>
</div>
</el-main>
</el-container>
</el-container>
</el-card>
</div>
</div>
</template>
<script>
import MsApiCollapseItem from "./components/ApiCollapseItem";
import MsApiCollapse from "./components/ApiCollapse";
import MsApiRequest from "./components/ApiRequest";
import MsApiRequestForm from "./components/ApiRequestForm";
import MsApiScenarioForm from "./components/ApiScenarioForm";
export default {
name: "MsApiScenarioConfig",
components: {MsApiScenarioForm, MsApiRequestForm, MsApiRequest, MsApiCollapse, MsApiCollapseItem},
data() {
return {
activeName: 0,
scenarios: [],
selected: Object
}
},
methods: {
handleChange: function (index) {
this.select(this.scenarios[index]);
},
handleCommand: function (command) {
switch (command.type) {
case "delete":
this.deleteScenario(command.index);
break;
}
},
createScenario: function () {
return {
type: "Scenario",
name: "Scenario",
address: "",
file: "",
variables: [],
headers: [],
requests: []
}
},
deleteScenario: function (index) {
this.scenarios.splice(index, 1);
if (this.scenarios.length === 0) {
this.create();
}
},
create: function () {
let scenario = this.createScenario();
this.scenarios.push(scenario);
},
select: function (obj) {
this.selected = obj;
}
},
created() {
if (this.scenarios.length === 0) {
this.create();
this.select(this.scenarios[0]);
}
}
}
</script>
<style scoped>
.scenario-container {
height: calc(100vh - 150px);
min-height: 600px;
}
.scenario-title {
font-size: 16px;
margin-left: -20px;
}
.scenario-aside {
position: relative;
border-radius: 4px;
border: 1px solid #EBEEF5;
box-sizing: border-box;
}
.scenario-list {
overflow-y: auto;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 28px;
}
.scenario-name {
font-size: 14px;
width: 100%;
}
.scenario-btn {
text-align: center;
padding: 13px;
}
.scenario-create {
position: absolute;
bottom: 0;
width: 100%;
}
.scenario-main {
position: relative;
margin-left: 20px;
border: 1px solid #EBEEF5;
}
.scenario-form {
padding: 20px;
overflow-y: auto;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
</style>
<template>
<div class="edit-testplan-container" >
<div class="main-content">
<el-card>
<el-row>
<el-col :span="10">
<el-input :placeholder="$t('load_test.input_name')" v-model="testPlan.name" class="input-with-select">
<template v-slot:prepend>
<el-select v-model="testPlan.projectId" :placeholder="$t('load_test.select_project')">
<el-option
v-for="item in projects"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</template>
</el-input>
</el-col>
<el-button type="primary" plain @click="save">{{$t('commons.save')}}</el-button>
<el-button type="primary" plain @click="saveAndRun">{{$t('load_test.save_and_run')}}</el-button>
<el-button type="warning" plain @click="cancel">{{$t('commons.cancel')}}</el-button>
</el-row>
<el-tabs class="testplan-config" v-model="active" type="border-card" :stretch="true">
<el-tab-pane :label="$t('load_test.basic_config')">
<api-test-scene-config :test-plan="testPlan" />
</el-tab-pane>
<el-tab-pane :label="$t('load_test.runtime_config')">
<api-test-runtime-config :test-plan="testPlan" ref="runtimeConfig"/>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</div>
</template>
<script>
import ApiTestSceneConfig from './components/ApiTestSceneConfig';
import ApiTestRuntimeConfig from './components/ApiTestRuntimeConfig';
export default {
name: "EditApiTest",
data() {
return {
result: {},
testPlan: {},
listProjectPath: "/project/listAll",
savePath: "/api/save",
editPath: "/api/edit",
runPath: "/api/run",
projects: [],
active: '0',
tabs: [{
title: this.$t('load_test.basic_config'),
id: '0',
component: 'ApiTestSceneConfig'
}, {
title: this.$t('load_test.runtime_config'),
id: '1',
component: 'ApiTestRuntimeConfig'
}]
}
},
components: {
ApiTestSceneConfig,
ApiTestRuntimeConfig,
},
watch: {
'$route'(to) {
// 如果是创建测试
if (to.name === 'createFucTest') {
window.location.reload();
return;
}
let testId = to.path.split('/')[4]; // find testId
if (testId) {
this.$get('/api/get/' + testId, response => {
this.testPlan = response.data;
});
}
}
},
created() {
let testId = this.$route.path.split('/')[4];
if (testId) {
this.$get('/api/get/' + testId, response => {
this.testPlan = response.data;
});
}
this.listProjects();
},
methods: {
listProjects() {
this.result = this.$get(this.listProjectPath, response => {
this.projects = response.data;
})
},
save() {
if (!this.validTestPlan()) {
return;
}
let options = this.getSaveOption();
this.result = this.$request(options, () => {
this.$message({
message: this.$t('commons.save_success'),
type: 'success'
});
this.$refs.runtimeConfig.cancelAllEdit();
this.$router.push({path: '/api/test/all'})
});
},
saveAndRun() {
if (!this.validTestPlan()) {
return;
}
let options = this.getSaveOption();
this.result = this.$request(options, (response) => {
this.testPlan.id = response.data;
this.$message({
message: this.$t('commons.save_success'),
type: 'success'
});
this.result = this.$post(this.runPath, {id: this.testPlan.id}, () => {
this.$message({
message: this.$t('load_test.is_running'),
type: 'success'
});
})
});
},
getSaveOption() {
let formData = new FormData();
let url = this.testPlan.id ? this.editPath : this.savePath;
if (!this.testPlan.file.id) {
formData.append("file", this.testPlan.file);
}
this.testPlan.runtimeConfiguration = JSON.stringify(this.$refs.runtimeConfig.configurations());
// file属性不需要json化
let requestJson = JSON.stringify(this.testPlan, function (key, value) {
return key === "file" ? undefined : value
});
formData.append('request', new Blob([requestJson], {
type: "application/json"
}));
return {
method: 'POST',
url: url,
data: formData,
headers: {
'Content-Type': undefined
}
};
},
cancel() {
this.$router.push({path: '/api/test/all'})
},
validTestPlan() {
if (!this.testPlan.name) {
this.$message({
message: this.$t('load_test.test_name_is_null'),
type: 'error'
});
return false;
}
if (!this.testPlan.projectId) {
this.$message({
message: this.$t('load_test.project_is_null'),
type: 'error'
});
return false;
}
if (!this.testPlan.file) {
this.$message({
message: this.$t('load_test.jmx_is_null'),
type: 'error'
});
return false;
}
if (!this.$refs.runtimeConfig.validConfig()) {
return false;
}
/// todo: 其他校验
return true;
}
}
}
</script>
<style scoped>
.edit-testplan-container {
float: none;
text-align: center;
padding: 15px;
width: 100%;
height: 100%;
box-sizing: border-box;
}
.edit-testplan-container .main-content {
margin: 0 auto;
width: 100%;
max-width: 1200px;
}
.edit-testplan-container .testplan-config {
margin-top: 15px;
}
.el-select {
min-width: 130px;
}
.edit-testplan-container .input-with-select .el-input-group__prepend {
background-color: #fff;
}
.advanced-config {
height: calc(100vh - 280px);
overflow: auto;
}
</style>
<template>
<div>
<el-radio-group v-model="body.type" size="mini">
<el-radio-button label="kv">
{{$t('api_test.request.body_kv')}}
</el-radio-button>
<el-radio-button label="text">
{{$t('api_test.request.body_text')}}
</el-radio-button>
</el-radio-group>
<ms-api-key-value :items="body.kvs" v-if="isKV"/>
<el-input class="textarea" type="textarea" v-model="body.text" :autosize="{ minRows: 10, maxRows: 25}" resize="none"
v-else/>
</div>
</template>
<script>
import MsApiKeyValue from "./ApiKeyValue";
export default {
name: "MsApiBody",
components: {MsApiKeyValue},
props: {
body: Object
},
data() {
return {};
},
methods: {},
computed: {
isKV() {
return this.body.type === "kv";
}
}
}
</script>
<style scoped>
.textarea {
margin-top: 10px;
}
</style>
<template>
<div role="tablist" aria-multiselectable="true">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'MsApiCollapse',
componentName: 'MsApiCollapse',
props: {
accordion: Boolean,
value: {
type: [Array, String, Number],
default() {
return [];
}
}
},
data() {
return {
activeNames: [].concat(this.value)
};
},
provide() {
return {
collapse: this
};
},
watch: {
value(value) {
this.activeNames = [].concat(value);
}
},
methods: {
setActiveNames(activeNames) {
activeNames = [].concat(activeNames);
let value = this.accordion ? activeNames[0] : activeNames;
this.activeNames = activeNames;
this.$emit('input', value);
this.$emit('change', value);
},
handleItemClick(item) {
if (this.accordion) {
this.setActiveNames(
(this.activeNames[0] || this.activeNames[0] === 0) && item.name);
} else {
let activeNames = this.activeNames.slice(0);
let index = activeNames.indexOf(item.name);
if (index > -1) {
activeNames.splice(index, 1);
} else {
activeNames.push(item.name);
}
this.setActiveNames(activeNames);
}
}
},
created() {
this.$on('item-click', this.handleItemClick);
}
};
</script>
<template>
<div class="el-collapse-item"
:class="{'is-active': isActive, 'is-disabled': disabled }">
<div
role="tab"
:aria-expanded="isActive"
:aria-controls="`el-collapse-content-${id}`"
:aria-describedby="`el-collapse-content-${id}`"
>
<div
class="el-collapse-item__header"
@click="handleHeaderClick"
role="button"
:id="`el-collapse-head-${id}`"
:tabindex="disabled ? undefined : 0"
@keyup.space.enter.stop="handleEnterClick"
:class="{
'focusing': focusing,
'is-active': isActive
}"
@focus="handleFocus"
@blur="focusing = false"
>
<i
class="el-collapse-item__arrow el-icon-arrow-right"
:class="{'is-active': isActive}">
</i>
<slot name="title">{{title}}</slot>
</div>
</div>
<el-collapse-transition>
<div
class="el-collapse-item__wrap"
v-show="isActive"
role="tabpanel"
:aria-hidden="!isActive"
:aria-labelledby="`el-collapse-head-${id}`"
:id="`el-collapse-content-${id}`"
>
<div class="el-collapse-item__content">
<slot></slot>
</div>
</div>
</el-collapse-transition>
</div>
</template>
<script>
import Emitter from 'element-ui/src/mixins/emitter';
import {generateId} from 'element-ui/src/utils/util';
export default {
name: 'MsApiCollapseItem',
componentName: 'MsApiCollapseItem',
mixins: [Emitter],
data() {
return {
contentWrapStyle: {
height: 'auto',
display: 'block'
},
contentHeight: 0,
focusing: false,
isClick: false,
id: generateId()
};
},
inject: ['collapse'],
props: {
title: String,
name: {
type: [String, Number],
default() {
return this._uid;
}
},
disabled: Boolean
},
computed: {
isActive() {
return this.collapse.activeNames.indexOf(this.name) > -1;
}
},
methods: {
handleFocus() {
setTimeout(() => {
if (!this.isClick) {
this.focusing = true;
} else {
this.isClick = false;
}
}, 50);
},
handleHeaderClick() {
if (this.disabled) return;
this.dispatch('MsApiCollapse', 'item-click', this);
this.focusing = false;
this.isClick = true;
},
handleEnterClick() {
this.dispatch('MsApiCollapse', 'item-click', this);
}
}
};
</script>
<style scoped>
.el-collapse-item__header {
padding-left: 7px;
}
.el-collapse-item__header.is-active {
background-color: #E9E9E9;
}
.el-collapse-item__content {
padding-bottom: 0;
}
</style>
<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 :span="11">
<el-input v-model="item.key" placeholder="Key" size="small" maxlength="100" @change="check"/>
</el-col>
<el-col :span="11">
<el-input v-model="item.value" placeholder="Value" size="small" maxlength="100" @change="check"/>
</el-col>
<el-col :span="1">
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"/>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
export default {
name: "MsApiKeyValue",
props: {
description: String,
items: Array
},
methods: {
create: function () {
return {
key: "",
value: ""
}
},
remove: function (index) {
this.items.splice(index, 1);
if (this.items.length === 0) {
this.items.push(this.create());
}
},
check: function () {
let isNeedCreate = true;
let removeIndex = -1;
this.items.forEach((item, index) => {
if (item.key === "" && item.value === "") {
// 多余的空行
if (index !== this.items.length - 1) {
removeIndex = index;
}
// 没有空行,需要创建空行
isNeedCreate = false;
}
});
if (isNeedCreate) {
this.items.push(this.create());
}
if (removeIndex !== -1) {
this.remove(removeIndex);
}
// TODO 检查key重复
}
},
created() {
if (this.items.length === 0) {
this.items.push(this.create());
}
}
}
</script>
<style scoped>
.kv-description {
font-size: 14px;
}
.kv-row {
margin-top: 10px;
}
</style>
<template>
<div class="request-container">
<div class="request-item" v-for="(request, index) in requests" :key="index" @click="select(request)"
:class="{'selected': isSelected(request)}">
<span class="request-method">
{{request.method}}
</span>
<span class="request-name">
{{request.name}}
</span>
<span class="request-btn">
<el-dropdown trigger="click" @command="handleCommand">
<span class="el-dropdown-link el-icon-more"></span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{type: 'copy', index: index}">复制请求</el-dropdown-item>
<el-dropdown-item :command="{type: 'delete', index: index}">删除请求</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</span>
</div>
<el-button class="request-create" type="primary" size="mini" icon="el-icon-plus" plain @click="create"/>
</div>
</template>
<script>
import {generateId} from 'element-ui/src/utils/util';
export default {
name: "MsApiRequest",
props: {
requests: Array,
open: Function
},
data() {
return {
selected: 0
}
},
computed: {
isSelected() {
return function (request) {
return this.selected.randomId === request.randomId;
}
}
},
methods: {
create: function () {
let request = this.createRequest();
this.requests.push(request);
},
handleCommand: function (command) {
switch (command.type) {
case "copy":
this.copyRequest(command.index);
break;
case "delete":
this.deleteRequest(command.index);
break;
}
},
copyRequest: function (index) {
let request = this.requests[index];
this.requests.push(JSON.parse(JSON.stringify(request)));
},
deleteRequest: function (index) {
this.requests.splice(index, 1);
if (this.requests.length === 0) {
this.create();
}
},
createRequest: function () {
return {
randomId: generateId(),
type: "Request",
method: "GET",
name: "",
parameters: [],
headers: [],
body: {
type: "kv",
kvs: [],
text: ""
},
assertions: [],
extract: []
}
},
select: function (request) {
this.selected = request;
this.open(request);
}
},
created() {
if (this.requests.length === 0) {
this.create();
this.select(this.requests[0]);
}
}
}
</script>
<style scoped>
.request-item {
border-left: 5px solid #1E90FF;
line-height: 40px;
max-height: 40px;
border-top: 1px solid #EBEEF5;
cursor: pointer;
}
.request-item:first-child {
border-top: 0;
}
.request-item:hover, .request-item.selected:hover {
background-color: #ECF5FF;
}
.request-item.selected {
background-color: #F5F5F5;
}
.request-method {
padding: 0 5px;
width: 60px;
color: #1E90FF;
}
.request-name {
font-size: 14px;
width: 100%;
}
.request-btn {
float: right;
text-align: center;
height: 40px;
}
.request-btn .el-icon-more {
padding: 13px;
}
.request-create {
width: 100%;
}
</style>
<template>
<el-form :model="request" :rules="rules" ref="request" label-width="100px" label-position="left" v-if="isRequest">
<el-form-item :label="$t('api_test.request.name')" prop="name">
<el-input v-model="request.name"></el-input>
</el-form-item>
<el-form-item :label="$t('api_test.request.url')" prop="url">
<el-input v-model="request.url" :placeholder="$t('api_test.request.url_describe')">
<el-select v-model="request.method" slot="prepend" class="request-method-select">
<el-option label="GET" value="GET"></el-option>
<el-option label="POST" value="POST"></el-option>
<el-option label="PUT" value="PUT"></el-option>
<el-option label="PATCH" value="PATCH"></el-option>
<el-option label="DELETE" value="DELETE"></el-option>
<el-option label="OPTIONS" value="OPTIONS"></el-option>
<el-option label="HEAD" value="HEAD"></el-option>
<el-option label="CONNECT" value="CONNECT"></el-option>
</el-select>
</el-input>
</el-form-item>
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('api_test.request.parameters')" name="parameters">
<ms-api-key-value :items="request.parameters" :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 :items="request.headers"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.body')" name="body" v-if="isNotGet">
<ms-api-body :body="request.body"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.assertions')" name="assertions" v-if="false">
TODO
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.extract')" name="extract" v-if="false">
TODO
</el-tab-pane>
</el-tabs>
</el-form>
</template>
<script>
import MsApiKeyValue from "./ApiKeyValue";
import MsApiBody from "./ApiBody";
export default {
name: "MsApiRequestForm",
components: {MsApiBody, MsApiKeyValue},
props: {
request: Object
},
data() {
return {
activeName: "parameters",
rules: {}
}
},
computed: {
isRequest() {
return this.request.type === "Request";
},
isNotGet() {
return this.request.method !== "GET";
}
}
}
</script>
<style scoped>
.request-method-select {
width: 110px;
}
</style>
<template>
<el-form :model="scenario" :rules="rules" ref="scenario" label-width="100px" label-position="left" v-if="isScenario">
<el-form-item :label="$t('api_test.scenario.name')" prop="name">
<el-input v-model="scenario.name"></el-input>
</el-form-item>
<el-form-item :label="$t('api_test.scenario.base_url')" prop="url">
<el-input :placeholder="$t('api_test.scenario.base_url_describe')" v-model="scenario.url"></el-input>
</el-form-item>
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('api_test.scenario.variables')" name="variables">
<ms-api-key-value :items="scenario.variables"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.scenario.headers')" name="headers">
<ms-api-key-value :items="scenario.headers"/>
</el-tab-pane>
</el-tabs>
</el-form>
</template>
<script>
import MsApiKeyValue from "./ApiKeyValue";
export default {
name: "MsApiScenarioForm",
components: {MsApiKeyValue},
props: {
scenario: Object
},
data() {
return {
activeName: "variables",
rules: {}
}
},
computed: {
isScenario() {
return this.scenario.type === "Scenario";
}
},
}
</script>
<style scoped>
</style>
<template>
<div>
<el-row>
<el-col :span="5" :offset="6">
<span>浏览器</span>
</el-col>
</el-row>
<el-row >
<el-col :span="20" :offset="2">
<el-radio-group v-model="browser.value" class="browser-radio">
<el-radio v-for="item in browser.options" :key="item.label" :label="item.label">
<img :src="item.url"/>
</el-radio>
</el-radio-group>
</el-col>
</el-row>
<el-row>
<el-col :span="5" :offset="6">
<span>资源池</span>
</el-col>
</el-row>
<el-row>
<el-col :span="18" :offset="1">
<el-select v-model="resourcePool.value" filterable placeholder="请选择">
<el-option
v-for="item in resourcePool.options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: "ApiTestRuntimeConfig",
data() {
return {
resourcePool: {
options: [
{
value: '选项1',
label: '资源池1'
},
{
value: '选项2',
label: '资源池3'
},
{
value: '选项3',
label: '资源池3'
}],
value: ''
},
browser: {
options: [{
url: require('@/assets/browser/firefox.svg'),
label: 'firefox',
},
{
url: require('@/assets/browser/chrome.svg'),
label: 'chrome',
},
{
url: require('@/assets/browser/ie.svg'),
label: 'ie',
},
{
url: require('@/assets/browser/opera.svg'),
label: 'opera',
}
],
value: 'firefox'
}
}
},
methods: {
validConfig() {
if (this.resourcePool.value == '') {
this.$message.error(this.$t('api_test.select_resource_pool'));
return false;
}
return true;
},
configurations() {
return {
resourcePool: this.resourcePool,
browser: this.browser
}
},
cancelAllEdit() {
this.browser.value = 'firefox';
this.resourcePool.value = '';
}
}
}
</script>
<style scoped>
.el-row {
margin-top: 30px;
margin-bottom: 30px;
}
span {
font-size: 20px;
font-weight: bold;
color: dimgray;
}
</style>
<template>
<div v-loading="result.loading">
<el-upload
accept=".jmx"
drag
action=""
:limit="1"
:show-file-list="false"
:before-upload="beforeUpload"
:http-request="handleUpload"
:on-exceed="handleExceed"
:file-list="fileList">
<i class="el-icon-upload"/>
<div class="el-upload__text" v-html="$t('load_test.upload_tips')"></div>
<template v-slot:tip>
<div class="el-upload__tip">{{$t('load_test.upload_type')}}</div>
</template>
</el-upload>
<el-table class="basic-config" :data="tableData">
<el-table-column
prop="name"
:label="$t('load_test.file_name')">
</el-table-column>
<el-table-column
prop="size"
:label="$t('load_test.file_size')">
</el-table-column>
<el-table-column
prop="type"
:label="$t('load_test.file_type')">
</el-table-column>
<el-table-column
:label="$t('load_test.last_modify_time')">
<template v-slot:default="scope">
<i class="el-icon-time"/>
<span class="last-modified">{{ scope.row.lastModified | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column
prop="status"
:label="$t('load_test.file_status')">
</el-table-column>
<el-table-column
:label="$t('commons.operating')">
<template v-slot:default="scope">
<el-button @click="handleDownload(scope.row)" :disabled="!scope.row.id" type="primary" icon="el-icon-download"
size="mini" circle/>
<el-button @click="handleDelete(scope.row, scope.$index)" type="danger" icon="el-icon-delete" size="mini"
circle/>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import {Message} from "element-ui";
export default {
name: "ApiTestSceneConfig",
props: ["testPlan"],
data() {
return {
result: {},
getFileMetadataPath: "/api/file/metadata",
jmxDownloadPath: '/api/file/download',
jmxDeletePath: '/api/file/delete',
fileList: [],
tableData: [],
};
},
created() {
if (this.testPlan.id) {
this.getFileMetadata(this.testPlan)
}
},
watch: {
testPlan() {
if (this.testPlan.id) {
this.getFileMetadata(this.testPlan)
}
}
},
methods: {
getFileMetadata(testPlan) {
this.fileList = [];// 一个测试只有一个文件
this.tableData = [];// 一个测试只有一个文件
this.result = this.$get(this.getFileMetadataPath + "/" + testPlan.id, response => {
let file = response.data;
if (!file) {
Message.error({message: this.$t('load_test.related_file_not_found'), showClose: true});
return;
}
this.testPlan.file = file;
this.fileList.push({
id: file.id,
name: file.name
});
this.tableData.push({
id: file.id,
name: file.name,
size: file.size + 'Byte', /// todo: 按照大小显示Byte、KB、MB等
type: 'JMX',
lastModified: file.updateTime,
status: 'todo',
});
})
},
beforeUpload(file) {
if (!this.fileValidator(file)) {
/// todo: 显示错误信息
return false;
}
this.tableData.push({
name: file.name,
size: file.size + 'Byte', /// todo: 按照大小显示Byte、KB、MB等
type: 'JMX',
lastModified: file.lastModified,
status: 'todo',
});
return true;
},
handleUpload(uploadResources) {
this.testPlan.file = uploadResources.file;
},
handleDownload(file) {
let data = {
name: file.name,
id: file.id,
};
let config = {
url: this.jmxDownloadPath,
method: 'post',
data: data,
responseType: 'blob'
};
this.result = this.$request(config).then(response => {
const content = response.data;
const blob = new Blob([content]);
if ("download" in document.createElement("a")) {
// 非IE下载
// chrome/firefox
let aTag = document.createElement('a');
aTag.download = file.name;
aTag.href = URL.createObjectURL(blob);
aTag.click();
URL.revokeObjectURL(aTag.href)
} else {
// IE10+下载
navigator.msSaveBlob(blob, this.filename)
}
}).catch(e => {
Message.error({message: e.message, showClose: true});
});
},
handleDelete(file, index) {
this.$alert(this.$t('commons.delete_file_confirm') + file.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this._handleDelete(file, index);
}
}
});
},
_handleDelete(file, index) {
this.fileList.splice(index, 1);
this.tableData.splice(index, 1);
this.testPlan.file = null;
},
handleExceed() {
this.$message.error(this.$t('load_test.delete_file'));
},
fileValidator(file) {
/// todo: 是否需要对文件内容和大小做限制
return file.size > 0;
},
},
}
</script>
<style scoped>
.basic-config {
width: 100%
}
.last-modified {
margin-left: 5px;
}
</style>
......@@ -18,7 +18,7 @@ import PerformanceTestReport from "../../performance/report/PerformanceTestRepor
import ApiTestReport from "../../api/report/ApiTestReport";
import ApiTest from "../../api/ApiTest";
import PerformanceTest from "../../performance/PerformanceTest";
import EditApiTest from "../../api/test/EditApiTest";
import ApiScenarioConfig from "../../api/test/ApiScenarioConfig";
import PerformanceTestHome from "../../performance/home/PerformanceTestHome";
import ApiTestList from "../../api/test/ApiTestList";
import ApiTestHome from "../../api/home/ApiTestHome";
......@@ -96,13 +96,13 @@ const router = new VueRouter({
},
{
path: 'test/create',
name: "createFucTest",
component: EditApiTest,
name: "createAPITest",
component: ApiScenarioConfig,
},
{
path: "test/edit/:testId",
name: "editFucTest",
component: EditApiTest,
name: "editAPITest",
component: ApiScenarioConfig,
props: {
content: (route) => {
return {
......
......@@ -168,7 +168,27 @@ export default {
'resource_pool_is_null': '资源池为空',
},
api_test: {
'select_resource_pool': '请选择资源池'
scenario: {
name: "场景名称",
base_url: "基础URL",
base_url_describe: "基础URL作为所有请求的URL前缀",
variables: "变量",
headers: "请求头"
},
request: {
name: "请求名称",
method: "请求方法",
url: "请求URL",
url_describe: "例如: https://fit2cloud.com",
parameters: "请求参数",
parameters_desc: "参数追加到URL,例如https://fit2cloud.com/entries?key1=Value1&Key2=Value2",
headers: "请求头",
body: "请求内容",
body_kv: "键值对",
body_text: "文本",
assertions: "断言",
extract: "提取"
}
},
test_track: {
'test_track': '测试跟踪',
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册