Merge branch 'master' into dev
This commit is contained in:
commit
5a7c21304a
|
@ -1,12 +1,18 @@
|
|||
package io.metersphere.controller;
|
||||
|
||||
import io.metersphere.requests.testplan.FileOperationRequest;
|
||||
import io.metersphere.service.FileService;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
@RestController
|
||||
|
@ -16,7 +22,22 @@ public class TestPlanController {
|
|||
private FileService fileService;
|
||||
|
||||
@PostMapping("/file/upload")
|
||||
public void upload(MultipartFile file) throws IOException {
|
||||
public void uploadJmx(MultipartFile file) throws IOException {
|
||||
fileService.upload(file.getOriginalFilename(), file);
|
||||
}
|
||||
|
||||
@PostMapping("/file/delete")
|
||||
public void deleteJmx(@RequestBody FileOperationRequest request) {
|
||||
System.out.println(String.format("delete %s", request.getName()));
|
||||
}
|
||||
|
||||
@PostMapping("/file/download")
|
||||
public ResponseEntity<org.springframework.core.io.Resource> downloadJmx(@RequestBody FileOperationRequest fileOperationRequest, HttpServletResponse response) {
|
||||
org.springframework.core.io.Resource resource = fileService.loadFileAsResource(fileOperationRequest.getName());
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileOperationRequest.getName() + "\"")
|
||||
.body(resource);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package io.metersphere.requests.testplan;
|
||||
|
||||
public class FileOperationRequest {
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -1,18 +1,41 @@
|
|||
package io.metersphere.service;
|
||||
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class FileService {
|
||||
// 将上传的文件保存在内存,方便测试
|
||||
private Map<String, MultipartFile> fileMap = new ConcurrentHashMap<>();
|
||||
|
||||
public void upload(String name, MultipartFile file) throws IOException {
|
||||
String result = new BufferedReader(new InputStreamReader(file.getInputStream()))
|
||||
.lines().collect(Collectors.joining("\n"));
|
||||
System.out.println(String.format("upload file: %s, content: \n%s", name, result));
|
||||
|
||||
fileMap.put(name, file);
|
||||
}
|
||||
|
||||
public Resource loadFileAsResource(String name) {
|
||||
final MultipartFile file = fileMap.get(name);
|
||||
|
||||
if (file != null) {
|
||||
try {
|
||||
return new InputStreamResource(file.getInputStream());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
let timestampFormatDate = function (timestamp) {
|
||||
if (!timestamp) {
|
||||
return timestamp
|
||||
}
|
||||
|
||||
let date = new Date(timestamp);
|
||||
|
||||
let y = date.getFullYear();
|
||||
|
||||
let MM = date.getMonth() + 1;
|
||||
MM = MM < 10 ? ('0' + MM) : MM;
|
||||
|
||||
let d = date.getDate();
|
||||
d = d < 10 ? ('0' + d) : d;
|
||||
|
||||
let h = date.getHours();
|
||||
h = h < 10 ? ('0' + h) : h;
|
||||
|
||||
let m = date.getMinutes();
|
||||
m = m < 10 ? ('0' + m) : m;
|
||||
|
||||
let s = date.getSeconds();
|
||||
s = s < 10 ? ('0' + s) : s;
|
||||
|
||||
return y + '-' + MM + '-' + d + ' ' + h + ':' + m + ':' + s
|
||||
};
|
||||
export default timestampFormatDate
|
|
@ -1,11 +1,29 @@
|
|||
<template>
|
||||
<div class="testplan-container">
|
||||
<el-row>
|
||||
<el-col :span="10">
|
||||
<el-input placeholder="请输入名称" v-model="testplanName" class="input-with-select">
|
||||
<el-select v-model="project" slot="prepend" placeholder="请选择项目">
|
||||
<el-option
|
||||
v-for="item in projects"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.name">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-input>
|
||||
</el-col>
|
||||
<el-button type="primary" plain @click="save">保存</el-button>
|
||||
<el-button type="primary" plain @click="saveAndRun">保存并执行</el-button>
|
||||
<el-button type="warning" plain @click="cancel">取消</el-button>
|
||||
</el-row>
|
||||
|
||||
<el-tabs v-model="active" type="border-card" :stretch="true">
|
||||
<el-tab-pane
|
||||
v-for="item in tabs"
|
||||
:key="item.id"
|
||||
:label="item.title"
|
||||
>
|
||||
>
|
||||
<component :is="active === item.id ? item.component : false"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
@ -24,8 +42,26 @@
|
|||
PressureConfig,
|
||||
AdvancedConfig
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
project: '',
|
||||
projects: [{
|
||||
id: '选项1',
|
||||
name: '黄金糕'
|
||||
}, {
|
||||
id: '选项2',
|
||||
name: '双皮奶'
|
||||
}, {
|
||||
id: '选项3',
|
||||
name: '蚵仔煎'
|
||||
}, {
|
||||
id: '选项4',
|
||||
name: '龙须面'
|
||||
}, {
|
||||
id: '选项5',
|
||||
name: '北京烤鸭'
|
||||
}],
|
||||
testplanName: '',
|
||||
active: '0',
|
||||
tabs: [{
|
||||
title: '场景配置',
|
||||
|
@ -41,6 +77,29 @@
|
|||
component: 'AdvancedConfig'
|
||||
}]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
save() {
|
||||
window.console.log("save")
|
||||
|
||||
/// todo: save
|
||||
this.$message({
|
||||
message: '保存成功!',
|
||||
type: 'success'
|
||||
});
|
||||
},
|
||||
saveAndRun() {
|
||||
window.console.log("saveAndRun")
|
||||
|
||||
/// todo: saveAndRun
|
||||
this.$message({
|
||||
message: '保存成功,开始运行!',
|
||||
type: 'success'
|
||||
});
|
||||
},
|
||||
cancel() {
|
||||
this.$router.push({path: '/'})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -50,4 +109,12 @@
|
|||
float: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.testplan-container .el-select .el-input {
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
.testplan-container .input-with-select .el-input-group__prepend {
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,12 +1,53 @@
|
|||
<template>
|
||||
<el-upload
|
||||
accept=".jmx"
|
||||
drag
|
||||
:action="jmxUploadPath">
|
||||
<i class="el-icon-upload"/>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__tip" slot="tip">只能上传jmx文件</div>
|
||||
</el-upload>
|
||||
<div>
|
||||
<el-upload
|
||||
accept=".jmx"
|
||||
drag
|
||||
:limit="1"
|
||||
:show-file-list="false"
|
||||
:action="jmxUploadPath"
|
||||
:before-upload="beforeUpload"
|
||||
:file-list="fileList">
|
||||
<i class="el-icon-upload"/>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__tip" slot="tip">只能上传jmx文件</div>
|
||||
</el-upload>
|
||||
|
||||
<el-table
|
||||
:data="tableData"
|
||||
style="width: 100%">
|
||||
<el-table-column
|
||||
prop="name"
|
||||
label="文件名">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="size"
|
||||
label="文件大小">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="type"
|
||||
label="文件类型">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="修改时间">
|
||||
<template slot-scope="scope">
|
||||
<i class="el-icon-time"/>
|
||||
<span style="margin-left: 10px">{{ scope.row.lastModified | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="status"
|
||||
label="文件状态">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="操作">
|
||||
<template slot-scope="scope">
|
||||
<el-button @click="handleDownload(scope.row)" type="text" size="small">下载</el-button>
|
||||
<el-button @click="handleDelete(scope.row, scope.$index)" type="text" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -14,9 +55,88 @@
|
|||
data() {
|
||||
return {
|
||||
jmxUploadPath: '/testplan/file/upload',
|
||||
jmxDownloadPath: '/testplan/file/download',
|
||||
jmxDeletePath: '/testplan/file/delete',
|
||||
fileList: [],
|
||||
tableData: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
beforeUpload(file) {
|
||||
window.console.log(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;
|
||||
},
|
||||
handleDownload(file) {
|
||||
let data = {
|
||||
name: file.name
|
||||
};
|
||||
|
||||
this.$post(this.jmxDownloadPath, data).then(response => {
|
||||
if (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((response) => {
|
||||
this.$message.error(response.message);
|
||||
});
|
||||
},
|
||||
handleDelete(file, index) {
|
||||
this.$alert('确认删除文件: ' + file.name + "?", '', {
|
||||
confirmButtonText: '确定',
|
||||
callback: () => {
|
||||
this._handleDelete(file, index);
|
||||
}
|
||||
});
|
||||
},
|
||||
_handleDelete(file, index) {
|
||||
let data = {
|
||||
name: file.name
|
||||
};
|
||||
|
||||
this.$post(this.jmxDeletePath, data).then(response => {
|
||||
if (response.data.success) {
|
||||
this.fileList.splice(index, 1);
|
||||
this.tableData.splice(index, 1);
|
||||
|
||||
this.$message({
|
||||
message: '删除成功!',
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
this.$message.error(response.message);
|
||||
}
|
||||
});
|
||||
},
|
||||
fileValidator(file) {
|
||||
/// todo: 是否需要对文件内容和大小做限制
|
||||
return file.size > 0;
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -8,6 +8,7 @@ import App from './App.vue';
|
|||
import router from "./components/router/router";
|
||||
import store from './store'
|
||||
import i18n from "../i18n/i18n";
|
||||
import timestampFormatDate from "./components/common/filter/TimestampFormatDateFilter";
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
Vue.use(icon);
|
||||
|
@ -17,6 +18,9 @@ Vue.use(ElementUI, {
|
|||
Vue.use(filters);
|
||||
Vue.use(ajax);
|
||||
|
||||
// filter
|
||||
Vue.filter('timestampFormatDate', timestampFormatDate);
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router,
|
||||
|
|
Loading…
Reference in New Issue