Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
MeterSphere
metersphere
提交
f9f1809f
M
metersphere
项目概览
MeterSphere
/
metersphere
上一次同步 大约 3 年
通知
25
Star
1
Fork
1
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
M
metersphere
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
f9f1809f
编写于
4月 15, 2020
作者:
Q
q4speed
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
api界面,未完待续
上级
336b6c4a
变更
13
隐藏空白更改
内联
并排
Showing
13 changed file
with
796 addition
and
565 deletion
+796
-565
frontend/src/business/components/api/test/ApiScenarioConfig.vue
...nd/src/business/components/api/test/ApiScenarioConfig.vue
+170
-0
frontend/src/business/components/api/test/EditApiTest.vue
frontend/src/business/components/api/test/EditApiTest.vue
+0
-242
frontend/src/business/components/api/test/components/ApiBody.vue
...d/src/business/components/api/test/components/ApiBody.vue
+47
-0
frontend/src/business/components/api/test/components/ApiCollapse.vue
...c/business/components/api/test/components/ApiCollapse.vue
+70
-0
frontend/src/business/components/api/test/components/ApiCollapseItem.vue
...siness/components/api/test/components/ApiCollapseItem.vue
+125
-0
frontend/src/business/components/api/test/components/ApiKeyValue.vue
...c/business/components/api/test/components/ApiKeyValue.vue
+83
-0
frontend/src/business/components/api/test/components/ApiRequest.vue
...rc/business/components/api/test/components/ApiRequest.vue
+151
-0
frontend/src/business/components/api/test/components/ApiRequestForm.vue
...usiness/components/api/test/components/ApiRequestForm.vue
+75
-0
frontend/src/business/components/api/test/components/ApiScenarioForm.vue
...siness/components/api/test/components/ApiScenarioForm.vue
+49
-0
frontend/src/business/components/api/test/components/ApiTestRuntimeConfig.vue
...s/components/api/test/components/ApiTestRuntimeConfig.vue
+0
-121
frontend/src/business/components/api/test/components/ApiTestSceneConfig.vue
...ess/components/api/test/components/ApiTestSceneConfig.vue
+0
-196
frontend/src/business/components/common/router/router.js
frontend/src/business/components/common/router/router.js
+5
-5
frontend/src/i18n/zh-CN.js
frontend/src/i18n/zh-CN.js
+21
-1
未找到文件。
frontend/src/business/components/api/test/ApiScenarioConfig.vue
0 → 100644
浏览文件 @
f9f1809f
<
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
>
frontend/src/business/components/api/test/EditApiTest.vue
已删除
100644 → 0
浏览文件 @
336b6c4a
<
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
>
frontend/src/business/components/api/test/components/ApiBody.vue
0 → 100644
浏览文件 @
f9f1809f
<
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
>
frontend/src/business/components/api/test/components/ApiCollapse.vue
0 → 100644
浏览文件 @
f9f1809f
<
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
>
frontend/src/business/components/api/test/components/ApiCollapseItem.vue
0 → 100644
浏览文件 @
f9f1809f
<
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
>
frontend/src/business/components/api/test/components/ApiKeyValue.vue
0 → 100644
浏览文件 @
f9f1809f
<
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
>
frontend/src/business/components/api/test/components/ApiRequest.vue
0 → 100644
浏览文件 @
f9f1809f
<
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
>
frontend/src/business/components/api/test/components/ApiRequestForm.vue
0 → 100644
浏览文件 @
f9f1809f
<
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
>
frontend/src/business/components/api/test/components/ApiScenarioForm.vue
0 → 100644
浏览文件 @
f9f1809f
<
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
>
frontend/src/business/components/api/test/components/ApiTestRuntimeConfig.vue
已删除
100644 → 0
浏览文件 @
336b6c4a
<
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
>
frontend/src/business/components/api/test/components/ApiTestSceneConfig.vue
已删除
100644 → 0
浏览文件 @
336b6c4a
<
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
>
frontend/src/business/components/common/router/router.js
浏览文件 @
f9f1809f
...
...
@@ -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
:
"
create
Fuc
Test
"
,
component
:
EditApiTest
,
name
:
"
create
API
Test
"
,
component
:
ApiScenarioConfig
,
},
{
path
:
"
test/edit/:testId
"
,
name
:
"
edit
Fuc
Test
"
,
component
:
EditApiTest
,
name
:
"
edit
API
Test
"
,
component
:
ApiScenarioConfig
,
props
:
{
content
:
(
route
)
=>
{
return
{
...
...
frontend/src/i18n/zh-CN.js
浏览文件 @
f9f1809f
...
...
@@ -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.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录