This commit is contained in:
chenjianxing 2020-11-05 17:16:48 +08:00
commit 63e234b74b
42 changed files with 478 additions and 339 deletions

View File

@ -104,9 +104,8 @@ public class APITestController {
@PostMapping("/delete")
public void delete(@RequestBody DeleteAPITestRequest request) {
String testId = request.getId();
checkownerService.checkApiTestOwner(testId);
apiTestService.delete(testId);
checkownerService.checkApiTestOwner(request.getId());
apiTestService.delete(request);
}
@PostMapping(value = "/run")

View File

@ -8,4 +8,8 @@ import lombok.Setter;
public class DeleteAPITestRequest {
private String id;
/**
* 是否强制删除删除项目时不检查关联关系强制删除资源
*/
private boolean forceDelete = false;
}

View File

@ -193,8 +193,11 @@ public class APITestService {
return extApiTestMapper.getApiTestByProjectId(projectId);
}
public void delete(String testId) {
testCaseService.checkIsRelateTest(testId);
public void delete(DeleteAPITestRequest request) {
String testId = request.getId();
if (!request.isForceDelete()) {
testCaseService.checkIsRelateTest(testId);
}
deleteFileByTestId(testId);
apiReportService.deleteByTestId(testId);
scheduleService.deleteByResourceId(testId);

View File

@ -3,7 +3,7 @@ package io.metersphere.commons.constants;
public enum FileType {
JMX(".jmx"), CSV(".csv"), JSON(".json"), PDF(".pdf"),
JPG(".jpg"), PNG(".png"), JPEG(".jpeg"), DOC(".doc"),
XLSX(".xlsx"), DOCX(".docx");
XLSX(".xlsx"), DOCX(".docx"), JAR(".jar");
// 保存后缀
private String suffix;

View File

@ -28,7 +28,7 @@ import java.util.Map;
import java.util.Objects;
@Configuration
@ConditionalOnProperty(prefix="sso",name = "mode", havingValue = "local", matchIfMissing = true)
@ConditionalOnProperty(prefix = "sso", name = "mode", havingValue = "local", matchIfMissing = true)
public class ShiroConfig implements EnvironmentAware {
private Environment env;
@ -43,6 +43,8 @@ public class ShiroConfig implements EnvironmentAware {
shiroFilterFactoryBean.getFilters().put("apikey", new ApiKeyFilter());
Map<String, String> filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap();
ShiroUtils.loadBaseFilterChain(filterChainDefinitionMap);
filterChainDefinitionMap.put("/display/info", "anon");
filterChainDefinitionMap.put("/display/file/*", "anon");
filterChainDefinitionMap.put("/**", "apikey, authc");
return shiroFilterFactoryBean;
}

View File

@ -144,7 +144,7 @@ public class MailService {
context.put("testCaseName", testCaseWithBLOBs.getName());
context.put("description", request.getDescription());
context.put("url", baseSystemConfigDTO.getUrl());
context.put("id", testCaseWithBLOBs.getId());
context.put("id", request.getReviewId());
try {
String commentTemplate = IOUtils.toString(this.getClass().getResource("/mail/ReviewComments.html"), StandardCharsets.UTF_8);
sendReviewNotice(addresseeIdList(messageDetail, userIds, eventType), context, commentTemplate);

View File

@ -17,6 +17,7 @@ public class EngineContext {
private Map<String, Object> properties = new HashMap<>();
private Map<String, String> testData = new HashMap<>();
private Map<String, String> env = new HashMap<>();
private Map<String, byte[]> testJars = new HashMap<>();
public String getTestId() {
return testId;
@ -126,4 +127,13 @@ public class EngineContext {
public void setResourceIndex(Integer resourceIndex) {
this.resourceIndex = resourceIndex;
}
public Map<String, byte[]> getTestJars() {
return testJars;
}
public void setTestJars(Map<String, byte[]> testJars) {
this.testJars = testJars;
}
}

View File

@ -63,6 +63,7 @@ public class EngineFactory {
});
List<FileMetadata> csvFiles = fileMetadataList.stream().filter(f -> StringUtils.equalsIgnoreCase(f.getType(), FileType.CSV.name())).collect(Collectors.toList());
List<FileMetadata> jarFiles = fileMetadataList.stream().filter(f -> StringUtils.equalsIgnoreCase(f.getType(), FileType.JAR.name())).collect(Collectors.toList());
final FileContent fileContent = fileService.getFileContent(jmxFile.getId());
if (fileContent == null) {
MSException.throwException(Translator.get("run_load_test_file_content_not_found") + loadTest.getId());
@ -125,6 +126,15 @@ public class EngineFactory {
engineContext.setTestData(data);
}
if (CollectionUtils.isNotEmpty(jarFiles)) {
Map<String, byte[]> data = new HashMap<>();
jarFiles.forEach(jf -> {
FileContent content = fileService.getFileContent(jf.getId());
data.put(jf.getName(), content.getFile());
});
engineContext.setTestJars(data);
}
return engineContext;
}

View File

@ -83,6 +83,7 @@ public class DockerTestEngine extends AbstractEngine {
testRequest.setFileString(content);
testRequest.setImage(JMETER_IMAGE);
testRequest.setTestData(context.getTestData());
testRequest.setTestJars(context.getTestJars());
testRequest.setEnv(context.getEnv());

View File

@ -14,4 +14,5 @@ public class TestRequest extends BaseRequest {
private String image;
private Map<String, String> testData = new HashMap<>();
private Map<String, String> env = new HashMap<>();
private Map<String, byte[]> testJars = new HashMap<>();
}

View File

@ -47,27 +47,26 @@ public class PerformanceNoticeTask {
}
public void registerNoticeTask(LoadTestReportWithBLOBs loadTestReport) {
executorService.submit(() -> {
while (isRunning) {
LoadTestReportWithBLOBs loadTestReportFromDatabase = loadTestReportMapper.selectByPrimaryKey(loadTestReport.getId());
if (StringUtils.equals(loadTestReportFromDatabase.getStatus(), PerformanceTestStatus.Completed.name())) {
isRunning = false;
sendSuccessNotice(loadTestReportFromDatabase);
return;
}
if (StringUtils.equals(loadTestReportFromDatabase.getStatus(), PerformanceTestStatus.Error.name())) {
isRunning = false;
sendFailNotice(loadTestReportFromDatabase);
return;
}
try {
Thread.sleep(1000 * 60);// 每分钟检查 loadtest 的状态
} catch (InterruptedException e) {
LogUtil.error(e);
}
int count = 20;
while (count-- > 0) {
LoadTestReportWithBLOBs loadTestReportFromDatabase = loadTestReportMapper.selectByPrimaryKey(loadTestReport.getId());
if (StringUtils.equals(loadTestReportFromDatabase.getStatus(), PerformanceTestStatus.Completed.name())) {
isRunning = false;
sendSuccessNotice(loadTestReportFromDatabase);
return;
}
});
if (StringUtils.equals(loadTestReportFromDatabase.getStatus(), PerformanceTestStatus.Error.name())) {
isRunning = false;
sendFailNotice(loadTestReportFromDatabase);
return;
}
count--;
try {
Thread.sleep(1000 * 4L);// 每分钟检查 loadtest 的状态
} catch (InterruptedException e) {
LogUtil.error(e);
}
}
}
public void sendSuccessNotice(LoadTestReportWithBLOBs loadTestReport) {

View File

@ -85,7 +85,9 @@ public class PerformanceTestService {
public void delete(DeleteTestPlanRequest request) {
String testId = request.getId();
testCaseService.checkIsRelateTest(testId);
if (!request.isForceDelete()) {
testCaseService.checkIsRelateTest(testId);
}
LoadTestReportExample loadTestReportExample = new LoadTestReportExample();
loadTestReportExample.createCriteria().andTestIdEqualTo(testId);

View File

@ -14,6 +14,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@ -141,4 +142,12 @@ public class FileService {
example.createCriteria().andIdIn(fileIds);
return fileMetadataMapper.selectByExample(example);
}
public void deleteFileById(String fileId) {
deleteFileByIds(Collections.singletonList(fileId));
}
public FileMetadata getFileMetadataById(String fileId) {
return fileMetadataMapper.selectByPrimaryKey(fileId);
}
}

View File

@ -1,5 +1,6 @@
package io.metersphere.service;
import io.metersphere.api.dto.DeleteAPITestRequest;
import io.metersphere.api.dto.QueryAPITestRequest;
import io.metersphere.api.service.APITestService;
import io.metersphere.base.domain.LoadTest;
@ -96,6 +97,7 @@ public class ProjectService {
loadTestIdList.forEach(loadTestId -> {
DeleteTestPlanRequest deleteTestPlanRequest = new DeleteTestPlanRequest();
deleteTestPlanRequest.setId(loadTestId);
deleteTestPlanRequest.setForceDelete(true);
performanceTestService.delete(deleteTestPlanRequest);
});
@ -122,7 +124,10 @@ public class ProjectService {
QueryAPITestRequest request = new QueryAPITestRequest();
request.setProjectId(projectId);
apiTestService.list(request).forEach(test -> {
apiTestService.delete(test.getId());
DeleteAPITestRequest deleteAPITestRequest = new DeleteAPITestRequest();
deleteAPITestRequest.setId(test.getId());
deleteAPITestRequest.setForceDelete(true);
apiTestService.delete(deleteAPITestRequest);
});
}

View File

@ -73,8 +73,14 @@ public class TestResourcePoolService {
LoadTestExample example = new LoadTestExample();
example.createCriteria()
.andTestResourcePoolIdEqualTo(testResourcePoolId);
if (loadTestMapper.countByExample(example) > 0) {
MSException.throwException(Translator.get("test_resource_pool_is_use"));
List<LoadTest> loadTests = loadTestMapper.selectByExample(example);
StringBuilder loadTestNames = new StringBuilder();
if (loadTests.size() > 0) {
for (LoadTest loadTest : loadTests) {
loadTestNames = loadTestNames.append(loadTest.getName()).append(",");
}
String str = loadTestNames.substring(0, loadTestNames.length() - 1);
MSException.throwException(Translator.get("load_test") + " " + str + " " + Translator.get("test_resource_pool_is_use"));
}
}

View File

@ -1,4 +1,13 @@
package io.metersphere.track.request.testplan;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class DeleteTestPlanRequest extends TestPlanRequest {
/**
* 是否强制删除删除项目时不检查关联关系强制删除资源
*/
private boolean forceDelete = false;
}

View File

@ -7,5 +7,6 @@ import lombok.Setter;
@Getter
@Setter
public class SaveCommentRequest extends TestCaseComment {
private String reviewId;
}

View File

@ -546,7 +546,7 @@ public class TestCaseService {
for (TestCase testCase : testCases) {
caseName = caseName.append(testCase.getName()).append(",");
}
String str = caseName.toString().substring(0, caseName.length() - 1);
String str = caseName.substring(0, caseName.length() - 1);
MSException.throwException(Translator.get("related_case_del_fail_prefix") + " " + str + " " + Translator.get("related_case_del_fail_suffix"));
}
}

@ -1 +1 @@
Subproject commit ee74568be0beba46da19616f5832e83f9164c688
Subproject commit 24047fea950a74f7848a9fdaa857a22b884c4ce2

View File

@ -57,7 +57,8 @@ workspace_not_exists=Workspace is not exists
test_resource_pool_id_is_null=Test Resource Pool ID cannot be null
test_resource_pool_name_is_null=Test Resource Pool name cannot be null
test_resource_pool_name_already_exists=The test resource pool name already exists
test_resource_pool_is_use=The test resource pool is in use and cannot be deleted
load_test=Load Test
test_resource_pool_is_use=This resource pool is in use and cannot be deleted
#project
project_name_is_null=Project name cannot be null
project_name_already_exists=The project name already exists

View File

@ -57,7 +57,8 @@ workspace_not_exists=工作空间不存在
test_resource_pool_id_is_null=资源池ID不能为空
test_resource_pool_name_is_null=资源池名称不能为空
test_resource_pool_name_already_exists=资源池名称已存在
test_resource_pool_is_use=资源池正在使用中,无法删除
load_test=性能测试
test_resource_pool_is_use=正在使用此资源池,无法删除
#project
project_name_is_null=项目名称不能为空
project_name_already_exists=项目名称已存在

View File

@ -57,7 +57,8 @@ workspace_not_exists=工作空間不存在
test_resource_pool_id_is_null=資源池ID不能為空
test_resource_pool_name_is_null=資源池名稱不能為空
test_resource_pool_name_already_exists=資源池名稱已存在
test_resource_pool_is_use=資源池正在使用中,無法刪除
load_test=性能測試
test_resource_pool_is_use=正在使用此資源池,無法刪除
#project
project_name_is_null=項目名稱不能為空
project_name_already_exists=項目名稱已存在

View File

@ -10,7 +10,7 @@
${testCaseName}<br/>
添加评论:${description}<br/>
点击下面链接进入用例评审页面</p>
<a href="${url}/#/track/review/view">${url}/#/track/review/view</a>
<a href="${url}/#/track/review/view/${id}">${url}/#/track/review/view/${id}</a>
</div>
</body>
</html>

View File

@ -1215,7 +1215,7 @@ view.results.tree.renderers_order=.RenderAsText,.RenderAsRegexp,.RenderAsBoundar
# All entries will be added to the class path of the system class loader
# and also to the path of the JMeter internal loader.
# Paths with spaces may cause problems for the JVM
#user.classpath=../classes;../lib
user.classpath=/test/
# List of directories (separated by ;) that JMeter will search for utility
# and plugin dependency classes.

View File

@ -7,7 +7,8 @@
</el-row>
<el-row id="header-top" type="flex" justify="space-between" align="middle">
<el-col :span="12">
<a class="logo"/>
<img v-if="logoId" :src="'/display/file/' + logoId" style="width: 156px;height: 37px;" alt="">
<a v-else class="logo"/>
<ms-top-menus/>
</el-col>
@ -24,107 +25,113 @@
</template>
<script>
import MsTopMenus from "./components/common/head/HeaderTopMenus";
import MsView from "./components/common/router/View";
import MsUser from "./components/common/head/HeaderUser";
import MsHeaderOrgWs from "./components/common/head/HeaderOrgWs";
import MsLanguageSwitch from "./components/common/head/LanguageSwitch";
import {saveLocalStorage} from "../common/js/utils";
import MsTopMenus from "./components/common/head/HeaderTopMenus";
import MsView from "./components/common/router/View";
import MsUser from "./components/common/head/HeaderUser";
import MsHeaderOrgWs from "./components/common/head/HeaderOrgWs";
import MsLanguageSwitch from "./components/common/head/LanguageSwitch";
import {saveLocalStorage} from "@/common/js/utils";
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const header = requireComponent.keys().length > 0 ? requireComponent("./license/LicenseMessage.vue") : {};
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const header = requireComponent.keys().length > 0 ? requireComponent("./license/LicenseMessage.vue") : {};
const display = requireComponent.keys().length > 0 ? requireComponent("./display/Display.vue") : {};
export default {
name: 'app',
props: {},
data() {
return {
licenseHeader: null,
auth: false,
header: {},
}
},
beforeCreate() {
this.$get("/isLogin").then(response => {
if (response.data.success) {
this.$setLang(response.data.data.language);
saveLocalStorage(response.data);
this.auth = true;
//
if (header.default !== undefined) {
this.licenseHeader = "LicenseMessage";
}
} else {
window.location.href = "/login"
}
}).catch(() => {
window.location.href = "/login"
});
},
components: {
MsLanguageSwitch,
MsUser,
MsView,
MsTopMenus,
MsHeaderOrgWs,
"LicenseMessage": header.default
export default {
name: 'app',
props: {},
data() {
return {
licenseHeader: null,
auth: false,
header: {},
logoId: '_blank',
}
},
beforeCreate() {
this.$get("/isLogin").then(response => {
if (response.data.success) {
this.$setLang(response.data.data.language);
saveLocalStorage(response.data);
this.auth = true;
//
if (header.default !== undefined) {
this.licenseHeader = "LicenseMessage";
}
//
if (display.default !== undefined) {
display.default.valid(this);
}
} else {
window.location.href = "/login"
}
}).catch(() => {
window.location.href = "/login"
});
},
components: {
MsLanguageSwitch,
MsUser,
MsView,
MsTopMenus,
MsHeaderOrgWs,
"LicenseMessage": header.default
}
}
</script>
<style scoped>
#header-top {
width: 100%;
padding: 0 10px;
background-color: rgb(44, 42, 72);
color: rgb(245, 245, 245);
font-size: 14px;
}
#header-top {
width: 100%;
padding: 0 10px;
background-color: rgb(44, 42, 72);
color: rgb(245, 245, 245);
font-size: 14px;
}
.logo {
width: 156px;
margin-bottom: 0;
border: 0;
margin-right: 20px;
display: inline-block;
line-height: 37px;
background-size: 156px 30px;
box-sizing: border-box;
height: 37px;
background-repeat: no-repeat;
background-position: 50% center;
background-image: url("../assets/logo-light-MeterSphere.svg");
}
.logo {
width: 156px;
margin-bottom: 0;
border: 0;
margin-right: 20px;
display: inline-block;
line-height: 37px;
background-size: 156px 30px;
box-sizing: border-box;
height: 37px;
background-repeat: no-repeat;
background-position: 50% center;
background-image: url("../assets/logo-light-MeterSphere.svg");
}
.menus > * {
color: inherit;
padding: 0;
max-width: 180px;
white-space: pre;
cursor: pointer;
line-height: 40px;
}
.menus > * {
color: inherit;
padding: 0;
max-width: 180px;
white-space: pre;
cursor: pointer;
line-height: 40px;
}
.header-top-menus {
display: inline-block;
border: 0;
}
.header-top-menus {
display: inline-block;
border: 0;
}
.menus > a {
padding-right: 15px;
text-decoration: none;
}
.menus > a {
padding-right: 15px;
text-decoration: none;
}
.align-right {
float: right;
}
.align-right {
float: right;
}
.license-head {
height: 30px;
background: #BA331B;
text-align: center;
line-height: 30px;
color: white;
}
.license-head {
height: 30px;
background: #BA331B;
text-align: center;
line-height: 30px;
color: white;
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div v-loading="result.loading">
<el-upload
accept=".jmx,.csv"
accept=".jmx,.csv,.jar"
drag
action=""
:limit="fileNumLimit"
@ -77,7 +77,7 @@ export default {
fileList: [],
tableData: [],
uploadList: [],
fileNumLimit: 5,
fileNumLimit: 10,
};
},
created() {
@ -197,7 +197,7 @@ export default {
return this.fileList;//
},
validConfig() {
let newJmxNum = 0, oldJmxNum = 0, newCsvNum = 0, oldCsvNum = 0;
let newJmxNum = 0, oldJmxNum = 0, newCsvNum = 0, oldCsvNum = 0, newJarNum = 0, oldJarNum = 0;
if (this.uploadList.length > 0) {
this.uploadList.forEach(f => {
if (f.name.toLowerCase().endsWith(".jmx")) {
@ -206,6 +206,9 @@ export default {
if (f.name.toLowerCase().endsWith(".csv")) {
newCsvNum++;
}
if (f.name.toLowerCase().endsWith(".jar")) {
newJarNum++;
}
});
}
if (this.fileList.length > 0) {
@ -216,9 +219,12 @@ export default {
if (f.name.toLowerCase().endsWith(".csv")) {
oldCsvNum++;
}
if (f.name.toLowerCase().endsWith(".jar")) {
oldJarNum++;
}
});
}
if (newCsvNum + oldCsvNum > this.fileNumLimit - 1) {
if (newCsvNum + oldCsvNum + newJarNum + oldJarNum > this.fileNumLimit - 1) {
this.handleExceed();
return false;
}

View File

@ -204,7 +204,11 @@ export default {
})
},
removeRowTask(index, data) { //
data.splice(index, 1)
if (!data[index].identification) {
data.splice(index, 1)
} else {
data[index].isSet = false
}
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {

View File

@ -205,7 +205,12 @@ export default {
})
},
removeRowTask(index, data) { //
data.splice(index, 1)
if (!data[index].identification) {
data.splice(index, 1)
} else {
data[index].isSet = false
}
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {

View File

@ -196,7 +196,11 @@ export default {
})
},
removeRowTask(index, data) { //
data.splice(index, 1)
if (!data[index].identification) {
data.splice(index, 1)
} else {
data[index].isSet = false
}
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {

View File

@ -210,7 +210,11 @@ export default {
})
},
removeRowTask(index, data) { //
data.splice(index, 1)
if (!data[index].identification) {
data.splice(index, 1)
} else {
data[index].isSet = false
}
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {

View File

@ -211,7 +211,11 @@ export default {
})
},
removeRowTask(index, data) { //
data.splice(index, 1)
if (!data[index].identification) {
data.splice(index, 1)
} else {
data[index].isSet = false
}
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {

View File

@ -32,7 +32,9 @@ export default {
component: () => import('@/business/components/settings/system/SystemParameterSetting'),
meta: {system: true, title: 'commons.system_parameter_setting'}
},
...requireContext.keys().map(key => requireContext(key).system),...requireContext.keys().map(key => requireContext(key).license),
...requireContext.keys().map(key => requireContext(key).system),
...requireContext.keys().map(key => requireContext(key).license),
...requireContext.keys().map(key => requireContext(key).display),
{
path: 'organizationmember',
component: () => import('@/business/components/settings/organization/OrganizationMember'),

View File

@ -295,7 +295,6 @@ export default {
this.infoList = resources;
},
del(row) {
window.console.log(row);
this.$confirm(this.$t('test_resource_pool.delete_prompt'), this.$t('commons.prompt'), {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),

View File

@ -368,8 +368,9 @@ export default {
}
],
phone: [
{required: true, message: this.$t('user.input_phone'), trigger: 'blur'},
{
required: false,
required: true,
pattern: '^1(3|4|5|7|8)\\d{9}$',
message: this.$t('user.mobile_number_format_is_incorrect'),
trigger: 'blur'

View File

@ -40,7 +40,8 @@ export default {
components: {ReviewCommentItem},
props: {
caseId: String,
comments: Array
comments: Array,
reviewId:String,
},
data() {
return {
@ -53,6 +54,7 @@ export default {
let comment = {};
comment.caseId = this.caseId;
comment.description = this.textarea;
comment.reviewId=this.reviewId;
if (!this.textarea) {
this.$warning(this.$t('test_track.comment.description_is_null'));
return;

View File

@ -229,7 +229,7 @@
<i class="el-icon-refresh" @click="getComments(testCase)"
style="margin-left:10px;font-size: 14px; cursor: pointer"/>
</template>
<review-comment :comments="comments" :case-id="testCase.caseId" @getComments="getComments"/>
<review-comment :comments="comments" :case-id="testCase.caseId" :review-id="testCase.reviewId" @getComments="getComments"/>
</el-card>
</el-col>
</div>

View File

@ -300,6 +300,7 @@ export default {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
handleEdit(testCase, index) {
console.log(testCase)
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;

@ -1 +1 @@
Subproject commit cc38137a69a0f20fadece9c0f9f50a9468c4ace9
Subproject commit b9091a47b197faef77c72b134b7cf604fe3209f0

View File

@ -180,6 +180,14 @@ export default {
invalid: 'invalid',
expired: 'expired',
},
display: {
title: 'Theme',
logo: 'System LOGO',
loginLogo: 'Picture on the right side of the login page',
loginImage: 'Login page upper left corner LOGO',
loginTitle: 'Login page prompt information',
pageTitle: 'Page Title',
},
system_config: {
base_config: 'Base Config',
base: {
@ -228,7 +236,7 @@ export default {
mail: 'mail',
nail_robot: 'Nail robot',
enterprise_wechat_robot: 'Enterprise wechat robot',
notes:'Note: 1. Nail and create a custom robot in the enterprise group, and then copy the webhook address on our platform;\n' +
notes: 'Note: 1. Nail and create a custom robot in the enterprise group, and then copy the webhook address on our platform;\n' +
'\n' +
'2. Robots are selected as swarm robots, and "custom keyword" is selected for security verification: "task notification";\n' +
'\n' +
@ -389,7 +397,7 @@ export default {
file_status: 'File Status',
last_modify_time: 'Modify time',
upload_tips: 'Drag files here, or <em> click to upload </em>',
upload_type: 'Only JMX/CSV files can be uploaded',
upload_type: 'Only JMX/CSV/JAR files can be uploaded',
related_file_not_found: "No related test file found!",
delete_file_confirm: 'Confirm delete file:',
file_size_limit: "The number of files exceeds the limit",

View File

@ -180,6 +180,14 @@ export default {
invalid: '无效',
expired: '已过期',
},
display: {
title: '显示设置',
logo: '系统 LOGO',
loginLogo: '登陆页面右侧图片',
loginImage: '登录页左上角 LOGO',
loginTitle: '登陆页面提示信息',
pageTitle: '页面 Title',
},
system_config: {
base_config: '基本配置',
base: {
@ -229,7 +237,7 @@ export default {
mail: '邮件',
nail_robot: '钉钉机器人',
enterprise_wechat_robot: '企业微信机器人',
notes:'注意:1.钉钉和企业群里新建一个自定义机器人,然后复制 webhook 地址在我们平台上;\n' +
notes: '注意:1.钉钉和企业群里新建一个自定义机器人,然后复制 webhook 地址在我们平台上;\n' +
' 2.机器人选择为群机器人,安全验证选择“自定义关键词” "任务通知";\n' +
' 3.选择接收人时必须是你所建的群里包含的人,接收人手机号为必填项且为钉钉企业所使用的手机号,',
message: '事件,接收人,接收方式为必填项',
@ -387,7 +395,7 @@ export default {
file_status: '文件状态',
last_modify_time: '修改时间',
upload_tips: '将文件拖到此处,或<em>点击上传</em>',
upload_type: '只能上传JMX/CSV文件',
upload_type: '只能上传JMX/CSV/JAR文件',
related_file_not_found: "未找到关联的测试文件!",
delete_file_confirm: '确认删除文件: ',
file_size_limit: "文件个数超出限制!",

View File

@ -180,6 +180,14 @@ export default {
invalid: '無效',
expired: '已過期',
},
display: {
title: '顯示設置',
logo: '系統 LOGO',
loginLogo: '登陸頁面右側圖片',
loginImage: '登錄頁左上角 LOGO',
loginTitle: '登陸頁面提示信息',
pageTitle: '頁面 Title',
},
system_config: {
base_config: '基本配置',
base: {
@ -389,7 +397,7 @@ export default {
file_status: '文件狀態',
last_modify_time: '修改時間',
upload_tips: '將文件拖到此處,或<em>點擊上傳</em>',
upload_type: '只能上傳JMX/CSV文件',
upload_type: '只能上傳JMX/CSV/JAR文件',
related_file_not_found: "未找到關聯的測試文件!",
delete_file_confirm: '確認刪除文件: ',
file_size_limit: "文件個數超出限制!",

View File

@ -4,15 +4,16 @@
<el-col :span="12">
<el-form :model="form" :rules="rules" ref="form">
<div class="logo">
<img src="../assets/logo-dark-MeterSphere.svg" style="width: 224px" alt="">
<img v-if="loginLogoId" :src="'/display/file/' + loginLogoId" style="width: 224px;height: 45px;" alt="">
<img v-else src="../assets/logo-dark-MeterSphere.svg" style="width: 224px; " alt="">
</div>
<div class="title">
<span id="s1">{{$t('commons.login')}}</span>
<span id="s1">{{ $t('commons.login') }}</span>
<span id="s2">MeterSphere</span>
</div>
<div class="border"></div>
<div class="welcome">
{{$t('commons.welcome')}}
{{ $t('commons.welcome') }}
</div>
<div class="form">
<el-form-item v-slot:default>
@ -32,241 +33,252 @@
</div>
<div class="btn">
<el-button type="primary" class="submit" @click="submit('form')">
{{$t('commons.login')}}
{{ $t('commons.login') }}
</el-button>
</div>
<div class="msg">
{{msg}}
{{ msg }}
</div>
</el-form>
</el-col>
<el-col :span="12" class="image">
<div></div>
<el-col :span="12">
<img v-if="loginImageId" :src="'/display/file/' + loginImageId" style="height: 560px; width: 100%">
<img v-else src="../assets/info.png" style="height: 560px; width: 100%">
</el-col>
</el-row>
</div>
</template>
<script>
import {saveLocalStorage} from '../common/js/utils';
import {DEFAULT_LANGUAGE} from "../common/js/constants";
import {saveLocalStorage} from '@/common/js/utils';
import {DEFAULT_LANGUAGE} from "@/common/js/constants";
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const display = requireComponent.keys().length > 0 ? requireComponent("./display/Display.vue") : {};
export default {
name: "Login",
data() {
/*let validateEmail = (rule, value, callback) => {
// eslint-disable-next-line no-useless-escape
let EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (!EMAIL_REGEX.test(value)) {
callback(new Error('邮箱格式不正确'));
export default {
name: "Login",
data() {
/*let validateEmail = (rule, value, callback) => {
// eslint-disable-next-line no-useless-escape
let EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (!EMAIL_REGEX.test(value)) {
callback(new Error('邮箱格式不正确'));
} else {
callback();
}
};*/
return {
result: {},
form: {
username: '',
password: '',
authenticate: 'LOCAL'
},
rules: {
username: [
{required: true, message: this.$t('commons.input_login_username'), trigger: 'blur'},
],
password: [
{required: true, message: this.$t('commons.input_password'), trigger: 'blur'},
{min: 6, max: 20, message: this.$t('commons.input_limit', [6, 20]), trigger: 'blur'}
]
},
msg: '',
ready: false,
openLdap: false,
loginLogoId: '_blank',
loginImageId: '_blank',
}
},
beforeCreate() {
this.result = this.$get("/isLogin").then(response => {
if (display.default !== undefined) {
display.default.valid(this);
}
if (!response.data.success) {
if (response.data.message === 'sso') {
window.location.href = "/sso/login"
} else {
callback();
this.ready = true;
}
};*/
return {
result: {},
form: {
username: '',
password: '',
authenticate: 'LOCAL'
},
rules: {
username: [
{required: true, message: this.$t('commons.input_login_username'), trigger: 'blur'},
],
password: [
{required: true, message: this.$t('commons.input_password'), trigger: 'blur'},
{min: 6, max: 20, message: this.$t('commons.input_limit', [6, 20]), trigger: 'blur'}
]
},
msg: '',
ready: false,
openLdap: false
} else {
let user = response.data.data;
saveLocalStorage(response.data);
this.getLanguage(user.language);
window.location.href = "/"
}
});
this.$get("/ldap/open", response => {
this.openLdap = response.data;
})
},
created: function () {
// ,,
document.addEventListener("keydown", this.watchEnter);
},
destroyed() {
//
document.removeEventListener("keydown", this.watchEnter);
},
methods: {
//
watchEnter(e) {
let keyNum = e.which; //
//keycody=13
if (keyNum === 13) {
//
this.submit('form');
}
},
beforeCreate() {
this.$get("/isLogin").then(response => {
if (!response.data.success) {
if (response.data.message === 'sso') {
window.location.href = "/sso/login"
} else {
this.ready = true;
submit(form) {
this.$refs[form].validate((valid) => {
if (valid) {
switch (this.form.authenticate) {
case "LOCAL":
this.normalLogin();
break;
case "LDAP":
this.ldapLogin();
break;
default:
this.normalLogin();
}
} else {
let user = response.data.data;
saveLocalStorage(response.data);
this.getLanguage(user.language);
window.location.href = "/"
return false;
}
});
this.$get("/ldap/open", response => {
this.openLdap = response.data;
})
},
created: function () {
// ,,
document.addEventListener("keydown", this.watchEnter);
normalLogin() {
this.result = this.$post("signin", this.form, response => {
saveLocalStorage(response);
sessionStorage.setItem('loginSuccess', 'true');
this.getLanguage(response.data.language);
});
},
destroyed() {
//
document.removeEventListener("keydown", this.watchEnter);
ldapLogin() {
this.result = this.$post("ldap/signin", this.form, response => {
saveLocalStorage(response);
sessionStorage.setItem('loginSuccess', 'true');
this.getLanguage(response.data.language);
});
},
methods: {
//
watchEnter(e) {
let keyNum = e.which; //
//keycody=13
if (keyNum === 13) {
//
this.submit('form');
}
},
submit(form) {
this.$refs[form].validate((valid) => {
if (valid) {
switch (this.form.authenticate) {
case "LOCAL":
this.normalLogin();
break;
case "LDAP":
this.ldapLogin();
break;
default:
this.normalLogin();
}
} else {
return false;
}
});
},
normalLogin() {
this.result = this.$post("signin", this.form, response => {
saveLocalStorage(response);
sessionStorage.setItem('loginSuccess', 'true');
this.getLanguage(response.data.language);
});
},
ldapLogin() {
this.result = this.$post("ldap/signin", this.form, response => {
saveLocalStorage(response);
sessionStorage.setItem('loginSuccess', 'true');
this.getLanguage(response.data.language);
});
},
getLanguage(language) {
if (!language) {
this.$get("language", response => {
language = response.data;
localStorage.setItem(DEFAULT_LANGUAGE, language);
window.location.href = "/"
})
} else {
getLanguage(language) {
if (!language) {
this.$get("language", response => {
language = response.data;
localStorage.setItem(DEFAULT_LANGUAGE, language);
window.location.href = "/"
}
})
} else {
window.location.href = "/"
}
}
}
}
</script>
<style scoped>
.container {
min-width: 800px;
max-width: 1440px;
height: 560px;
margin: calc((100vh - 560px) / 2) auto 0;
background-color: #FFFFFF;
}
.container {
min-width: 800px;
max-width: 1440px;
height: 560px;
margin: calc((100vh - 560px) / 2) auto 0;
background-color: #FFFFFF;
}
.logo {
margin: 30px 30px 0;
}
.logo {
margin: 30px 30px 0;
}
.title {
margin-top: 50px;
font-size: 32px;
letter-spacing: 0;
text-align: center;
}
.title {
margin-top: 50px;
font-size: 32px;
letter-spacing: 0;
text-align: center;
}
.title > #s1 {
color: #999999;
}
.title > #s1 {
color: #999999;
}
.title > #s2 {
color: #151515;
}
.title > #s2 {
color: #151515;
}
.border {
height: 2px;
margin: 20px auto 20px;
position: relative;
width: 80px;
background: #8B479B;
}
.border {
height: 2px;
margin: 20px auto 20px;
position: relative;
width: 80px;
background: #8B479B;
}
.welcome {
margin-top: 50px;
font-size: 14px;
color: #999999;
letter-spacing: 0;
line-height: 18px;
text-align: center;
}
.welcome {
margin-top: 50px;
font-size: 14px;
color: #999999;
letter-spacing: 0;
line-height: 18px;
text-align: center;
}
.form {
margin-top: 30px;
padding: 0 40px;
}
.form {
margin-top: 30px;
padding: 0 40px;
}
.btn {
margin-top: 40px;
padding: 0 40px;
}
.btn {
margin-top: 40px;
padding: 0 40px;
}
.btn > .submit {
width: 100%;
border-radius: 0;
border-color: #8B479B;
background-color: #8B479B;
}
.btn > .submit {
width: 100%;
border-radius: 0;
border-color: #8B479B;
background-color: #8B479B;
}
.btn > .submit:hover {
border-color: rgba(139, 71, 155, 0.9);
background-color: rgba(139, 71, 155, 0.9);
}
.btn > .submit:hover {
border-color: rgba(139, 71, 155, 0.9);
background-color: rgba(139, 71, 155, 0.9);
}
.btn > .submit:active {
border-color: rgba(139, 71, 155, 0.8);
background-color: rgba(139, 71, 155, 0.8);
}
.btn > .submit:active {
border-color: rgba(139, 71, 155, 0.8);
background-color: rgba(139, 71, 155, 0.8);
}
.msg {
margin-top: 10px;
padding: 0 40px;
color: red;
text-align: center;
}
.msg {
margin-top: 10px;
padding: 0 40px;
color: red;
text-align: center;
}
.image {
background: url(../assets/info.png);
height: 560px;
}
.image {
background: url(../assets/info.png);
height: 560px;
}
</style>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Neue Haas Grotesk Text Pro", "Arial Nova", "Segoe UI", "Helvetica Neue", ".PingFang SC", "PingFang SC", "Source Han Sans SC", "Noto Sans CJK SC", "Source Han Sans CN", "Noto Sans SC", "Source Han Sans TC", "Noto Sans CJK TC", "Hiragino Sans GB", sans-serif;
font-size: 14px;
background-color: #F5F5F5;
line-height: 26px;
color: #2B415C;
-webkit-font-smoothing: antialiased;
margin: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Neue Haas Grotesk Text Pro", "Arial Nova", "Segoe UI", "Helvetica Neue", ".PingFang SC", "PingFang SC", "Source Han Sans SC", "Noto Sans CJK SC", "Source Han Sans CN", "Noto Sans SC", "Source Han Sans TC", "Noto Sans CJK TC", "Hiragino Sans GB", sans-serif;
font-size: 14px;
background-color: #F5F5F5;
line-height: 26px;
color: #2B415C;
-webkit-font-smoothing: antialiased;
margin: 0;
}
.form .el-input > .el-input__inner {
border-radius: 0;
}
.form .el-input > .el-input__inner {
border-radius: 0;
}
</style>