refactor(接口自动化): 重构报告页面,按照层级显示
This commit is contained in:
parent
1a8a6c779f
commit
7b5c15a565
|
@ -201,7 +201,7 @@ public abstract class MsTestElement {
|
|||
return null;
|
||||
}
|
||||
|
||||
protected void addCsvDataSet(HashTree tree, List<ScenarioVariable> variables,ParameterConfig config) {
|
||||
protected void addCsvDataSet(HashTree tree, List<ScenarioVariable> variables, ParameterConfig config) {
|
||||
if (CollectionUtils.isNotEmpty(variables)) {
|
||||
List<ScenarioVariable> list = variables.stream().filter(ScenarioVariable::isCSVValid).collect(Collectors.toList());
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
|
@ -276,10 +276,7 @@ public abstract class MsTestElement {
|
|||
if (element.getParent() == null) {
|
||||
return;
|
||||
}
|
||||
if (MsTestElementConstants.LoopController.name().equals(element.getType())) {
|
||||
return;
|
||||
}
|
||||
path.append(element.getResourceId()).append("/");
|
||||
path.append(StringUtils.isEmpty(element.getName()) ? element.getType() : element.getName()).append("^@~@^");
|
||||
getFullPath(element.getParent(), path);
|
||||
}
|
||||
|
||||
|
@ -300,7 +297,6 @@ public abstract class MsTestElement {
|
|||
// 获取全路径以备后面使用
|
||||
StringBuilder fullPath = new StringBuilder();
|
||||
getFullPath(parent, fullPath);
|
||||
|
||||
return fullPath + "<->" + parent.getName();
|
||||
}
|
||||
return "";
|
||||
|
|
|
@ -66,6 +66,8 @@ public class MsJmeterElement extends MsTestElement {
|
|||
}
|
||||
if (CollectionUtils.isNotEmpty(hashTree)) {
|
||||
for (MsTestElement el : hashTree) {
|
||||
// 给所有孩子加一个父亲标志
|
||||
el.setParent(this);
|
||||
el.toHashTree(elementTree, el.getHashTree(), config);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,50 +43,16 @@ public class TestResult {
|
|||
private static final String SEPARATOR = "<->";
|
||||
|
||||
public void addScenario(ScenarioResult result) {
|
||||
Map<String, List<RequestResult>> requestResultMap = new LinkedHashMap<>();
|
||||
if (result != null && CollectionUtils.isNotEmpty(result.getRequestResults())) {
|
||||
result.getRequestResults().forEach(item -> {
|
||||
if (StringUtils.isNotEmpty(item.getName()) && item.getName().indexOf(SEPARATOR) != -1) {
|
||||
String array[] = item.getName().split(SEPARATOR);
|
||||
String scenarioName = item.getName().replace(array[0] + SEPARATOR, "");
|
||||
item.setName(array[0]);
|
||||
if (requestResultMap.containsKey(scenarioName)) {
|
||||
requestResultMap.get(scenarioName).add(item);
|
||||
} else {
|
||||
List<RequestResult> requestResults = new LinkedList<>();
|
||||
requestResults.add(item);
|
||||
requestResultMap.put(scenarioName, requestResults);
|
||||
}
|
||||
item.setName(array[1] + array[0]);
|
||||
item.getSubRequestResults().forEach(subItem -> {
|
||||
subItem.setName(item.getName());
|
||||
subItem.setName(array[0]);
|
||||
});
|
||||
} else {
|
||||
if (requestResultMap.containsKey(result.getName())) {
|
||||
requestResultMap.get(result.getName()).add(item);
|
||||
} else {
|
||||
List<RequestResult> requestResults = new LinkedList<>();
|
||||
requestResults.add(item);
|
||||
requestResultMap.put(result.getName(), requestResults);
|
||||
}
|
||||
item.getSubRequestResults().forEach(subItem -> {
|
||||
subItem.setName(item.getName());
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!requestResultMap.isEmpty()) {
|
||||
requestResultMap.forEach((k, v) -> {
|
||||
ScenarioResult scenarioResult = new ScenarioResult();
|
||||
BeanUtils.copyBean(scenarioResult, result);
|
||||
scenarioResult.setName(k);
|
||||
if (k.indexOf(SEPARATOR) != -1) {
|
||||
scenarioResult.setName(k.split(SEPARATOR)[1]);
|
||||
}
|
||||
scenarioResult.setRequestResults(v);
|
||||
scenarios.add(scenarioResult);
|
||||
});
|
||||
} else {
|
||||
scenarios.add(result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,26 +3,21 @@
|
|||
<ms-main-container>
|
||||
<el-card>
|
||||
<section class="report-container" v-if="this.report.testId">
|
||||
|
||||
<ms-api-report-view-header :debug="debug" :report="report" @reportExport="handleExport" @reportSave="handleSave"/>
|
||||
|
||||
<main v-if="this.isNotRunning">
|
||||
<main v-if="isNotRunning">
|
||||
<ms-metric-chart :content="content" :totalTime="totalTime"/>
|
||||
<div>
|
||||
<!--<ms-scenario-results :scenarios="content.scenarios" v-on:requestResult="requestResult"/>-->
|
||||
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick">
|
||||
<el-tab-pane :label="$t('api_report.total')" name="total">
|
||||
<ms-scenario-results :scenarios="content.scenarios" v-on:requestResult="requestResult"/>
|
||||
<ms-scenario-results :treeData="fullTreeNodes" v-on:requestResult="requestResult"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="fail">
|
||||
<template slot="label">
|
||||
<span class="fail">{{ $t('api_report.fail') }}</span>
|
||||
</template>
|
||||
<ms-scenario-results v-on:requestResult="requestResult" :scenarios="fails"/>
|
||||
<ms-scenario-results v-on:requestResult="requestResult" :treeData="failsTreeNodes"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
</div>
|
||||
<ms-api-report-export v-if="reportExportVisible" id="apiTestReport" :title="report.testName"
|
||||
:content="content" :total-time="totalTime"/>
|
||||
|
@ -62,6 +57,7 @@
|
|||
report: {},
|
||||
loading: true,
|
||||
fails: [],
|
||||
failsTreeNodes: [],
|
||||
totalTime: 0,
|
||||
isRequestResult: false,
|
||||
request: {},
|
||||
|
@ -69,6 +65,7 @@
|
|||
scenarioName: null,
|
||||
reportExportVisible: false,
|
||||
requestType: undefined,
|
||||
fullTreeNodes: [],
|
||||
}
|
||||
},
|
||||
activated() {
|
||||
|
@ -92,6 +89,8 @@
|
|||
this.content = {};
|
||||
this.fails = [];
|
||||
this.report = {};
|
||||
this.fullTreeNodes = [];
|
||||
this.failsTreeNodes = [];
|
||||
this.isRequestResult = false;
|
||||
},
|
||||
handleClick(tab, event) {
|
||||
|
@ -102,17 +101,81 @@
|
|||
},
|
||||
formatResult(res) {
|
||||
let resMap = new Map;
|
||||
let array = [];
|
||||
let i = 0;
|
||||
if (res && res.scenarios) {
|
||||
res.scenarios.forEach(item => {
|
||||
if (item && item.requestResults) {
|
||||
item.requestResults.forEach(req => {
|
||||
resMap.set(req.id, req);
|
||||
req.index = i;
|
||||
i++;
|
||||
array.push(req);
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
this.formatTree(array, this.fullTreeNodes);
|
||||
this.sort(this.fullTreeNodes);
|
||||
this.$emit('refresh', resMap);
|
||||
},
|
||||
formatTree(array, tree) {
|
||||
array.map((item) => {
|
||||
let key = item.name;
|
||||
let nodeArray = key.split('^@~@^');
|
||||
let children = tree;
|
||||
// 循环构建子节点
|
||||
for (let i in nodeArray) {
|
||||
let node = {
|
||||
label: nodeArray[i],
|
||||
value: item,
|
||||
};
|
||||
if (i != nodeArray.length) {
|
||||
node.children = [];
|
||||
}
|
||||
|
||||
if (children.length == 0) {
|
||||
children.push(node);
|
||||
}
|
||||
|
||||
let isExist = false;
|
||||
for (let j in children) {
|
||||
if (children[j].label == node.label) {
|
||||
if (i != nodeArray.length - 1 && !children[j].children) {
|
||||
children[j].children = [];
|
||||
}
|
||||
children = (i == nodeArray.length - 1 ? children : children[j].children);
|
||||
isExist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isExist) {
|
||||
children.push(node);
|
||||
if (i != nodeArray.length - 1 && !children[children.length - 1].children) {
|
||||
children[children.length - 1].children = [];
|
||||
}
|
||||
children = (i == nodeArray.length - 1 ? children : children[children.length - 1].children);
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
recursiveSorting(arr) {
|
||||
for (let i in arr) {
|
||||
arr[i].index = Number(i) + 1;
|
||||
if (arr[i].children != undefined && arr[i].children.length > 0) {
|
||||
this.recursiveSorting(arr[i].children);
|
||||
}
|
||||
}
|
||||
},
|
||||
sort(scenarioDefinition) {
|
||||
for (let i in scenarioDefinition) {
|
||||
// 排序
|
||||
scenarioDefinition[i].index = Number(i) + 1;
|
||||
if (scenarioDefinition[i].children != undefined && scenarioDefinition[i].children.length > 0) {
|
||||
this.recursiveSorting(scenarioDefinition[i].children);
|
||||
}
|
||||
}
|
||||
},
|
||||
getReport() {
|
||||
this.init();
|
||||
if (this.reportId) {
|
||||
|
@ -146,6 +209,7 @@
|
|||
getFails() {
|
||||
if (this.isNotRunning) {
|
||||
this.fails = [];
|
||||
let array = [];
|
||||
this.totalTime = 0
|
||||
if (this.content.scenarios) {
|
||||
this.content.scenarios.forEach((scenario) => {
|
||||
|
@ -158,11 +222,14 @@
|
|||
if (!request.success) {
|
||||
let failRequest = Object.assign({}, request);
|
||||
failScenario.requestResults.push(failRequest);
|
||||
array.push(request)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
this.formatTree(array, this.failsTreeNodes);
|
||||
this.sort(this.failsTreeNodes);
|
||||
}
|
||||
},
|
||||
computeTotalTime() {
|
||||
|
|
|
@ -1,55 +1,58 @@
|
|||
<template>
|
||||
<div class="request-result">
|
||||
<p class="el-divider--horizontal"></p>
|
||||
<div @click="active">
|
||||
<el-row :gutter="10" type="flex" align="middle" class="info">
|
||||
<el-col :span="14" v-if="indexNumber!=undefined">
|
||||
<div class="method">
|
||||
<div class="el-step__icon is-text ms-api-col" v-if="indexNumber%2 ==0">
|
||||
<div class="el-step__icon-inner"> {{ indexNumber+1 }}</div>
|
||||
<el-card class="ms-cards">
|
||||
<div class="request-result">
|
||||
<div @click="active">
|
||||
<el-row :gutter="10" type="flex" align="middle" class="info">
|
||||
<el-col :span="10" v-if="indexNumber!=undefined">
|
||||
<div class="method">
|
||||
<div class="el-step__icon is-text ms-api-col-create">
|
||||
<div class="el-step__icon-inner"> {{ indexNumber }}</div>
|
||||
</div>
|
||||
<i class="icon el-icon-arrow-right" :class="{'is-active': isActive}" @click="active" @click.stop/>
|
||||
{{ getName(request.name) }}
|
||||
</div>
|
||||
<div class="el-step__icon is-text ms-api-col-create" v-else>
|
||||
<div class="el-step__icon-inner"> {{ indexNumber+1 }}</div>
|
||||
</div>
|
||||
{{ request.name }}
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="5">
|
||||
<el-tooltip effect="dark" :content="request.responseResult.responseCode" placement="bottom" :open-delay="800">
|
||||
<div style="color: #5daf34" v-if="request.success">{{ request.responseResult.responseCode }}</div>
|
||||
<div style="color: #FE6F71" v-else>{{ request.responseResult.responseCode }}</div>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
</el-col>
|
||||
<el-col :span="9">
|
||||
<el-tooltip effect="dark" :content="request.responseResult.responseCode" placement="bottom" :open-delay="800">
|
||||
<div style="color: #5daf34" v-if="request.success">
|
||||
{{ request.responseResult.responseCode }}
|
||||
</div>
|
||||
<div style="color: #FE6F71" v-else>
|
||||
{{ request.responseResult.responseCode }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<span v-if="request.success">
|
||||
{{request.responseResult.responseTime}} ms
|
||||
</span>
|
||||
<span style="color: #FE6F71" v-else>
|
||||
<span style="color: #FE6F71" v-else>
|
||||
{{request.responseResult.responseTime}} ms
|
||||
</span>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="2">
|
||||
<div>
|
||||
<el-tag size="mini" type="success" v-if="request.success">
|
||||
{{ $t('api_report.success') }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="danger" v-else>
|
||||
{{ $t('api_report.fail') }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-collapse-transition>
|
||||
<div v-show="isActive" style="width: 99%">
|
||||
<ms-request-result-tail v-if="isActive" :request-type="requestType" :request="request"
|
||||
:scenario-name="scenarioName"/>
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
<div>
|
||||
<el-tag size="mini" type="success" v-if="request.success">
|
||||
{{ $t('api_report.success') }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="danger" v-else>
|
||||
{{ $t('api_report.fail') }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
</div>
|
||||
|
||||
<el-collapse-transition>
|
||||
<div v-show="isActive" style="width: 99%">
|
||||
<ms-request-result-tail :scenario-name="scenarioName"
|
||||
:request-type="requestType"
|
||||
:request="request"
|
||||
v-if="isActive"/>
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -61,19 +64,46 @@
|
|||
|
||||
export default {
|
||||
name: "MsRequestResult",
|
||||
components: {MsResponseText, MsRequestText, MsAssertionResults, MsRequestMetric, MsRequestResultTail},
|
||||
components: {
|
||||
MsResponseText,
|
||||
MsRequestText,
|
||||
MsAssertionResults,
|
||||
MsRequestMetric,
|
||||
MsRequestResultTail
|
||||
},
|
||||
props: {
|
||||
request: Object,
|
||||
scenarioName: String,
|
||||
indexNumber: Number,
|
||||
},
|
||||
data() {
|
||||
return {isActive: false, requestType: undefined,}
|
||||
return {
|
||||
isActive: false,
|
||||
requestType: "",
|
||||
color: {
|
||||
type: String,
|
||||
default() {
|
||||
return "#B8741A";
|
||||
}
|
||||
},
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
default() {
|
||||
return "#F9F1EA";
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
active() {
|
||||
this.isActive = !this.isActive;
|
||||
//this.$emit("requestResult", {request: this.request, scenarioName: this.scenarioName});
|
||||
},
|
||||
getName(name) {
|
||||
if (name && name.indexOf("^@~@^") != -1) {
|
||||
let arr = name.split("^@~@^");
|
||||
return arr[arr.length - 1];
|
||||
}
|
||||
return name;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -81,8 +111,7 @@
|
|||
|
||||
<style scoped>
|
||||
.request-result {
|
||||
width: 100%;
|
||||
min-height: 40px;
|
||||
min-height: 30px;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
|
@ -95,7 +124,7 @@
|
|||
color: #1E90FF;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
line-height: 35px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
|
@ -128,6 +157,10 @@
|
|||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.ms-cards >>> .el-card__body {
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.sub-result:last-child {
|
||||
border-bottom: 1px solid #EBEEF5;
|
||||
}
|
||||
|
@ -148,10 +181,20 @@
|
|||
color: #008080;
|
||||
}
|
||||
|
||||
/deep/ .el-step__icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.el-divider--horizontal {
|
||||
margin: 2px 0;
|
||||
background: 0 0;
|
||||
border-top: 1px solid #e8eaec;
|
||||
}
|
||||
|
||||
.icon.is-active {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -1,24 +1,19 @@
|
|||
<template>
|
||||
<div class="scenario-result" v-if="scenario && scenario.requestResults && scenario.requestResults.length>0">
|
||||
|
||||
<div @click="active">
|
||||
<el-row :gutter="10" type="flex" align="middle" class="info">
|
||||
<el-col :span="16">
|
||||
<i class="icon el-icon-arrow-right" :class="{'is-active': isActive}"/>
|
||||
{{scenario.name}}
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-collapse-transition>
|
||||
<div v-show="isActive">
|
||||
<div v-for="(request, index) in scenario.requestResults" :key="index">
|
||||
<ms-request-result :key="index" :request="request" :indexNumber="index"
|
||||
v-on:requestResult="requestResult"
|
||||
:scenarioName="scenario.name"/>
|
||||
<div class="scenario-result">
|
||||
<div v-if="node.children && node.children.length >0 ">
|
||||
<el-card class="ms-card">
|
||||
<div class="el-step__icon is-text ms-api-col">
|
||||
<div class="el-step__icon-inner"> {{ node.index }}</div>
|
||||
</div>
|
||||
{{node.label}}
|
||||
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
</el-card>
|
||||
</div>
|
||||
<div v-else>
|
||||
<ms-request-result :request="node.value" :indexNumber="node.index"
|
||||
v-on:requestResult="requestResult"
|
||||
:scenarioName="node.label"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -27,11 +22,10 @@
|
|||
|
||||
export default {
|
||||
name: "MsScenarioResult",
|
||||
|
||||
components: {MsRequestResult},
|
||||
|
||||
props: {
|
||||
scenario: Object,
|
||||
node: Object,
|
||||
},
|
||||
|
||||
data() {
|
||||
|
@ -69,6 +63,10 @@
|
|||
border-top: 1px solid #DCDFE6;
|
||||
}
|
||||
|
||||
.ms-card >>> .el-card__body {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.scenario-result .info {
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
|
@ -82,4 +80,25 @@
|
|||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.ms-api-col {
|
||||
background-color: #EFF0F0;
|
||||
border-color: #EFF0F0;
|
||||
margin-right: 10px;
|
||||
font-size: 12px;
|
||||
color: #64666A;
|
||||
}
|
||||
|
||||
.ms-api-col-create {
|
||||
background-color: #EBF2F2;
|
||||
border-color: #008080;
|
||||
margin-right: 10px;
|
||||
font-size: 12px;
|
||||
color: #008080;
|
||||
}
|
||||
|
||||
/deep/ .el-step__icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
<template>
|
||||
<el-card class="scenario-results">
|
||||
<ms-scenario-result v-for="(scenario, index) in scenarios" :key="index" :scenario="scenario" :indexNumber="index"
|
||||
v-on:requestResult="requestResult"/>
|
||||
<el-tree :data="treeData"
|
||||
:expand-on-click-node="false"
|
||||
highlight-current
|
||||
class="ms-tree ms-report-tree">
|
||||
<span slot-scope="{ node, data}" style="width: 99%" @click="nodeClick(node)">
|
||||
<ms-scenario-result :node="data" v-on:requestResult="requestResult"/>
|
||||
</span>
|
||||
</el-tree>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
|
@ -10,26 +16,61 @@
|
|||
|
||||
export default {
|
||||
name: "MsScenarioResults",
|
||||
|
||||
components: {MsScenarioResult},
|
||||
|
||||
props: {
|
||||
scenarios: Array
|
||||
scenarios: Array,
|
||||
treeData: Array,
|
||||
},
|
||||
methods: {
|
||||
requestResult(requestResult) {
|
||||
this.$emit("requestResult", requestResult);
|
||||
},
|
||||
nodeClick(node) {
|
||||
node.expanded = !node.expanded;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.scenario-header {
|
||||
border: 1px solid #EBEEF5;
|
||||
background-color: #F9FCFF;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
padding: 5px 0;
|
||||
.scenario-results {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ms-report-tree >>> .el-tree-node__content {
|
||||
height: 100%;
|
||||
vertical-align: center;
|
||||
}
|
||||
|
||||
/deep/ .el-drawer__body {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/deep/ .el-step__icon.is-text {
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
/deep/ .el-drawer__header {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
/deep/ .el-link {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/deep/ .el-checkbox {
|
||||
color: #303133;
|
||||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/deep/ .el-checkbox__label {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.ms-sc-variable-header >>> .el-dialog__body {
|
||||
padding: 0px 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -1199,7 +1199,7 @@
|
|||
|
||||
/deep/ .el-tree-node__content {
|
||||
height: 100%;
|
||||
margin-top: 8px;
|
||||
margin-top: 3px;
|
||||
vertical-align: center;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue