This commit is contained in:
chenjianxing 2020-08-19 15:41:24 +08:00
commit 83c21a33b5
33 changed files with 1148 additions and 945 deletions

2
backend/.gitignore vendored
View File

@ -32,4 +32,4 @@ target
.project
.classpath
.factorypath
*.jar
src/main/resources/jmeter/lib/

View File

@ -1,6 +1,7 @@
package io.metersphere.base.domain;
import java.io.Serializable;
import lombok.Data;
@Data
@ -23,5 +24,7 @@ public class Issues implements Serializable {
private String description;
private String model;
private static final long serialVersionUID = 1L;
}

View File

@ -115,9 +115,10 @@
</select>
<select id="list" resultType="io.metersphere.track.dto.TestPlanCaseDTO">
select test_plan_test_case.*, test_case.*
select test_plan_test_case.*, test_case.*,test_case_node.name as model
from test_plan_test_case
inner join test_case on test_plan_test_case.case_id = test_case.id
inner join test_case on test_plan_test_case.case_id = test_case.id left join test_case_node on
test_case_node.id=test_case.node_id
<where>
<if test="request.combine != null">
<include refid="combine">

View File

@ -1,21 +1,14 @@
package io.metersphere.config;
import io.metersphere.interceptor.TestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TestInterceptor());
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();

View File

@ -1,17 +0,0 @@
package io.metersphere.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class TestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// response.setHeader("Authentication-Status", "invalid");
return true;
}
}

View File

@ -1,6 +1,7 @@
package io.metersphere.track.domain;
import com.alibaba.fastjson.JSON;
import io.metersphere.base.domain.Issues;
import io.metersphere.base.domain.TestCaseNode;
import io.metersphere.base.domain.TestCaseNodeExample;
import io.metersphere.base.mapper.TestCaseNodeMapper;
@ -8,6 +9,7 @@ import io.metersphere.commons.constants.TestPlanTestCaseStatus;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.MathUtils;
import io.metersphere.track.dto.*;
import io.metersphere.track.service.IssuesService;
import io.metersphere.track.service.TestCaseNodeService;
import org.apache.commons.lang3.StringUtils;
@ -78,7 +80,9 @@ public class ReportResultComponent extends ReportComponent {
}
private void getModuleResultMap(Map<String, Set<String>> childIdMap, Map<String, TestCaseReportModuleResultDTO> moduleResultMap, TestPlanCaseDTO testCase, List<TestCaseNodeDTO> nodeTrees) {
IssuesService issuesService = (IssuesService) CommonBeanFactory.getBean("issuesService");
childIdMap.forEach((rootNodeId, childIds) -> {
if (childIds.contains(testCase.getNodeId())) {
TestCaseReportModuleResultDTO moduleResult = moduleResultMap.get(rootNodeId);
if (moduleResult == null) {
@ -112,15 +116,11 @@ public class ReportResultComponent extends ReportComponent {
if (StringUtils.equals(testCase.getStatus(), TestPlanTestCaseStatus.Blocking.name())) {
moduleResult.setBlockingCount(moduleResult.getBlockingCount() + 1);
}
if (StringUtils.isNotBlank(testCase.getIssues())) {
if (JSON.parseObject(testCase.getIssues()).getBoolean("hasIssues")) {
moduleResult.setIssuesCount(moduleResult.getIssuesCount() + 1);
}
;
}
moduleResult.setIssuesCount(moduleResult.getIssuesCount() + issuesService.getIssues(testCase.getCaseId()).size());
moduleResultMap.put(rootNodeId, moduleResult);
return;
}
});
}
}

View File

@ -1,5 +1,6 @@
package io.metersphere.track.dto;
import io.metersphere.base.domain.Issues;
import lombok.Getter;
import lombok.Setter;
@ -12,6 +13,7 @@ public class TestCaseReportMetricDTO {
private List<TestCaseReportStatusResultDTO> executeResult;
private List<TestCaseReportModuleResultDTO> moduleExecuteResult;
private List<TestPlanCaseDTO> failureTestCases;
private List<Issues> Issues;
private List<String> executors;
private String principal;
private Long startTime;

View File

@ -1,8 +1,11 @@
package io.metersphere.track.dto;
import io.metersphere.base.domain.Issues;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class TestCaseReportModuleResultDTO {

View File

@ -16,4 +16,5 @@ public class TestPlanCaseDTO extends TestCaseWithBLOBs {
private String caseId;
private String issues;
private String reportId;
private String model;
}

View File

@ -15,6 +15,7 @@ import io.metersphere.commons.constants.TestPlanStatus;
import io.metersphere.commons.constants.TestPlanTestCaseStatus;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.MathUtils;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
@ -43,7 +44,6 @@ import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
public class TestPlanService {
@Resource
TestPlanMapper testPlanMapper;
@ -252,7 +252,7 @@ public class TestPlanService {
}
public TestCaseReportMetricDTO getMetric(String planId) {
IssuesService issuesService = (IssuesService) CommonBeanFactory.getBean("issuesService");
QueryTestPlanRequest queryTestPlanRequest = new QueryTestPlanRequest();
queryTestPlanRequest.setId(planId);
@ -264,16 +264,25 @@ public class TestPlanService {
List<ReportComponent> components = ReportComponentFactory.createComponents(componentIds.toJavaList(String.class), testPlan);
List<TestPlanCaseDTO> testPlanTestCases = listTestCaseByPlanId(planId);
List<Issues> issues = new ArrayList<>();
for (TestPlanCaseDTO testCase : testPlanTestCases) {
List<Issues> issue = issuesService.getIssues(testCase.getCaseId());
if (issue.size() > 0) {
for (Issues i : issue) {
i.setModel(testCase.getModel());
}
issues.addAll(issue);
}
components.forEach(component -> {
component.readRecord(testCase);
});
}
TestCaseReportMetricDTO testCaseReportMetricDTO = new TestCaseReportMetricDTO();
components.forEach(component -> {
component.afterBuild(testCaseReportMetricDTO);
});
testCaseReportMetricDTO.setIssues(issues);
return testCaseReportMetricDTO;
}

@ -1 +1 @@
Subproject commit b86032cbbda9a9e6028308aa95a887cff2192f1c
Subproject commit 8eff343619df1572e1cded52f173257ef4b518a1

View File

@ -38,6 +38,7 @@
"babel-eslint": "^10.0.3",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
"file-writer": "^1.0.2",
"vue-template-compiler": "^2.6.10",
"vue2-ace-editor": "0.0.15"
},

View File

@ -4,9 +4,17 @@
<el-card>
<section class="report-container" v-if="this.report.testId">
<header class="report-header">
<span>{{ report.projectName }} / </span>
<router-link :to="path">{{ report.testName }}</router-link>
<span class="time">{{ report.createTime | timestampFormatDate }}</span>
<el-row>
<el-col>
<span>{{ report.projectName }} / </span>
<router-link :to="path">{{ report.testName }}</router-link>
<span class="time">{{ report.createTime | timestampFormatDate }}</span>
<el-button plain type="primary" size="mini" @click="handleExport(report.name)"
style="margin-left: 1200px">
{{$t('test_track.plan_view.export_report')}}
</el-button>
</el-col>
</el-row>
</header>
<main v-if="this.isNotRunning">
<ms-metric-chart :content="content" :totalTime="totalTime"/>
@ -43,6 +51,8 @@
import MsMetricChart from "./components/MetricChart";
import MsScenarioResults from "./components/ScenarioResults";
import {Scenario} from "../test/model/ScenarioModel";
import writer from "file-writer";
import ResumeCss from "../../../../common/css/main.css";
export default {
name: "MsApiReportView",

View File

@ -43,7 +43,8 @@
type: 'pie',
radius: ['40%', '70%'],
// roseType: 'angle',
data: this.data
data: this.data,
animation: false
}
]
},

View File

@ -22,9 +22,10 @@
@click="rerun(testId)">
{{ $t('report.test_execute_again') }}
</el-button>
<!--<el-button :disabled="isReadOnly" type="info" plain size="mini">
<el-button :disabled="isReadOnly" type="info" plain size="mini" @click="exports(reportName)">
{{$t('report.export')}}
</el-button>
<!--
<el-button :disabled="isReadOnly" type="warning" plain size="mini">
{{$t('report.compare')}}
</el-button>-->
@ -44,24 +45,26 @@
</el-row>
<el-divider/>
<div ref="resume">
<el-tabs v-model="active" type="border-card" :stretch="true">
<el-tab-pane :label="$t('load_test.pressure_config')">
<ms-performance-pressure-config :is-read-only="true" :report="report"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_overview')">
<ms-report-test-overview :report="report" ref="testOverview"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_request_statistics')">
<ms-report-request-statistics :report="report"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_error_log')">
<ms-report-error-log :report="report"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_log_details')">
<ms-report-log-details :report="report"/>
</el-tab-pane>
</el-tabs>
</div>
<el-tabs v-model="active" type="border-card" :stretch="true">
<el-tab-pane :label="$t('load_test.pressure_config')">
<ms-performance-pressure-config :is-read-only="true" :report="report"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_overview')">
<ms-report-test-overview :report="report" ref="testOverview"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_request_statistics')">
<ms-report-request-statistics :report="report"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_error_log')">
<ms-report-error-log :report="report"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_log_details')">
<ms-report-log-details :report="report"/>
</el-tab-pane>
</el-tabs>
</el-card>
<el-dialog :title="$t('report.test_stop_now_confirm')" :visible.sync="dialogFormVisible" width="30%">
@ -79,250 +82,252 @@
</template>
<script>
import MsReportErrorLog from './components/ErrorLog';
import MsReportLogDetails from './components/LogDetails';
import MsReportRequestStatistics from './components/RequestStatistics';
import MsReportTestOverview from './components/TestOverview';
import MsPerformancePressureConfig from "./components/PerformancePressureConfig";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsReportErrorLog from './components/ErrorLog';
import MsReportLogDetails from './components/LogDetails';
import MsReportRequestStatistics from './components/RequestStatistics';
import MsReportTestOverview from './components/TestOverview';
import MsPerformancePressureConfig from "./components/PerformancePressureConfig";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import {checkoutTestManagerOrTestUser} from "@/common/js/utils";
import {checkoutTestManagerOrTestUser} from "@/common/js/utils";
import writer from "file-writer";
import ResumeCss from "../../../../common/css/main.css";
export default {
name: "PerformanceReportView",
components: {
MsReportErrorLog,
MsReportLogDetails,
MsReportRequestStatistics,
MsReportTestOverview,
MsContainer,
MsMainContainer,
MsPerformancePressureConfig
},
data() {
return {
result: {},
active: '1',
reportId: '',
status: '',
reportName: '',
testId: '',
testName: '',
projectId: '',
projectName: '',
startTime: '0',
endTime: '0',
minutes: '0',
seconds: '0',
title: 'Logging',
report: {},
isReadOnly: false,
websocket: null,
dialogFormVisible: false,
testPlan: {testResourcePoolId: null}
}
},
methods: {
initBreadcrumb() {
if (this.reportId) {
this.result = this.$get("/performance/report/test/pro/info/" + this.reportId, res => {
let data = res.data;
if (data) {
this.reportName = data.name;
this.testId = data.testId;
this.testName = data.testName;
this.projectId = data.projectId;
this.projectName = data.projectName;
}
})
export default {
name: "PerformanceReportView",
components: {
MsReportErrorLog,
MsReportLogDetails,
MsReportRequestStatistics,
MsReportTestOverview,
MsContainer,
MsMainContainer,
MsPerformancePressureConfig
},
data() {
return {
result: {},
active: '1',
reportId: '',
status: '',
reportName: '',
testId: '',
testName: '',
projectId: '',
projectName: '',
startTime: '0',
endTime: '0',
minutes: '0',
seconds: '0',
title: 'Logging',
report: {},
isReadOnly: false,
websocket: null,
dialogFormVisible: false,
testPlan: {testResourcePoolId: null}
}
},
initReportTimeInfo() {
if (this.reportId) {
this.result = this.$get("/performance/report/content/report_time/" + this.reportId)
.then(res => {
let data = res.data.data;
methods: {
initBreadcrumb() {
if (this.reportId) {
this.result = this.$get("/performance/report/test/pro/info/" + this.reportId, res => {
let data = res.data;
if (data) {
this.startTime = data.startTime;
this.endTime = data.endTime;
let duration = data.duration;
this.minutes = Math.floor(duration / 60);
this.seconds = duration % 60;
}
}).catch(() => {
this.clearData();
})
}
},
initWebSocket() {
let protocol = "ws://";
if (window.location.protocol === 'https:') {
protocol = "wss://";
}
const uri = protocol + window.location.host + "/performance/report/" + this.reportId;
this.websocket = new WebSocket(uri);
this.websocket.onmessage = this.onMessage;
this.websocket.onopen = this.onOpen;
this.websocket.onerror = this.onError;
this.websocket.onclose = this.onClose;
},
checkReportStatus(status) {
switch (status) {
case 'Error':
this.$warning(this.$t('report.generation_error'));
break;
case 'Starting':
this.$alert(this.$t('report.start_status'));
break;
case 'Reporting':
case 'Running':
case 'Completed':
default:
break;
}
},
clearData() {
this.startTime = '0';
this.endTime = '0';
this.minutes = '0';
this.seconds = '0';
},
stopTest(forceStop) {
this.result = this.$get('/performance/stop/' + this.reportId + '/' + forceStop, () => {
this.$success(this.$t('report.test_stop_success'));
if (forceStop) {
this.$router.push('/performance/report/all');
} else {
this.report.status = 'Completed';
}
});
this.dialogFormVisible = false;
},
rerun(testId) {
this.$confirm(this.$t('report.test_rerun_confirm'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.result = this.$post('/performance/run', {id: testId, triggerMode: 'MANUAL'}, (response) => {
this.reportId = response.data;
this.$router.push({path: '/performance/report/view/' + this.reportId});
// socket
this.initWebSocket();
})
}).catch(() => {
});
},
onOpen() {
window.console.log("socket opening.");
},
onError(e) {
window.console.error(e)
},
onMessage(e) {
this.$set(this.report, "refresh", e.data); //
this.$set(this.report, "status", 'Running');
this.initReportTimeInfo();
window.console.log('receive a message:', e.data);
},
onClose(e) {
this.$set(this.report, "refresh", Math.random()); //
this.$set(this.report, "status", 'Completed');
this.initReportTimeInfo();
window.console.log("socket closed.");
}
},
created() {
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;
}
this.reportId = this.$route.path.split('/')[4];
this.result = this.$get("/performance/report/" + this.reportId, res => {
let data = res.data;
if (data) {
this.status = data.status;
this.$set(this.report, "id", this.reportId);
this.$set(this.report, "status", data.status);
this.$set(this.report, "testId", data.testId);
this.$set(this.report, "loadConfiguration", data.loadConfiguration);
this.checkReportStatus(data.status);
if (this.status === "Completed" || this.status === "Running") {
this.initReportTimeInfo();
}
this.initBreadcrumb();
this.initWebSocket();
} else {
this.$error(this.$t('report.not_exist'))
}
});
},
beforeDestroy() {
this.websocket.close() //websocket
},
watch: {
'$route'(to) {
if (to.name === "perReportView") {
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;
}
let reportId = to.path.split('/')[4];
this.reportId = reportId;
if (reportId) {
this.$get("/performance/report/test/pro/info/" + reportId, response => {
let data = response.data;
if (data) {
this.status = data.status;
this.reportName = data.name;
this.testName = data.testName;
this.testId = data.testId;
this.testName = data.testName;
this.projectId = data.projectId;
this.projectName = data.projectName;
this.$set(this.report, "id", reportId);
this.$set(this.report, "status", data.status);
this.checkReportStatus(data.status);
if (this.status === "Completed") {
this.result = this.$get("/performance/report/content/report_time/" + this.reportId).then(res => {
let data = res.data.data;
if (data) {
this.startTime = data.startTime;
this.endTime = data.endTime;
let duration = data.duration;
this.minutes = Math.floor(duration / 60);
this.seconds = duration % 60;
}
}).catch(() => {
this.clearData();
})
} else {
this.clearData();
}
} else {
this.$error(this.$t('report.not_exist'));
}
});
})
}
},
initReportTimeInfo() {
if (this.reportId) {
this.result = this.$get("/performance/report/content/report_time/" + this.reportId)
.then(res => {
let data = res.data.data;
if (data) {
this.startTime = data.startTime;
this.endTime = data.endTime;
let duration = data.duration;
this.minutes = Math.floor(duration / 60);
this.seconds = duration % 60;
}
}).catch(() => {
this.clearData();
})
}
},
initWebSocket() {
let protocol = "ws://";
if (window.location.protocol === 'https:') {
protocol = "wss://";
}
const uri = protocol + window.location.host + "/performance/report/" + this.reportId;
this.websocket = new WebSocket(uri);
this.websocket.onmessage = this.onMessage;
this.websocket.onopen = this.onOpen;
this.websocket.onerror = this.onError;
this.websocket.onclose = this.onClose;
},
checkReportStatus(status) {
switch (status) {
case 'Error':
this.$warning(this.$t('report.generation_error'));
break;
case 'Starting':
this.$alert(this.$t('report.start_status'));
break;
case 'Reporting':
case 'Running':
case 'Completed':
default:
break;
}
},
clearData() {
this.startTime = '0';
this.endTime = '0';
this.minutes = '0';
this.seconds = '0';
},
stopTest(forceStop) {
this.result = this.$get('/performance/stop/' + this.reportId + '/' + forceStop, () => {
this.$success(this.$t('report.test_stop_success'));
if (forceStop) {
this.$router.push('/performance/report/all');
} else {
this.report.status = 'Completed';
}
});
this.dialogFormVisible = false;
},
rerun(testId) {
this.$confirm(this.$t('report.test_rerun_confirm'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.result = this.$post('/performance/run', {id: testId, triggerMode: 'MANUAL'}, (response) => {
this.reportId = response.data;
this.$router.push({path: '/performance/report/view/' + this.reportId});
// socket
this.initWebSocket();
})
}).catch(() => {
});
},
onOpen() {
window.console.log("socket opening.");
},
onError(e) {
window.console.error(e)
},
onMessage(e) {
this.$set(this.report, "refresh", e.data); //
this.$set(this.report, "status", 'Running');
this.initReportTimeInfo();
window.console.log('receive a message:', e.data);
},
onClose(e) {
this.$set(this.report, "refresh", Math.random()); //
this.$set(this.report, "status", 'Completed');
this.initReportTimeInfo();
window.console.log("socket closed.");
}
},
created() {
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;
}
this.reportId = this.$route.path.split('/')[4];
this.result = this.$get("/performance/report/" + this.reportId, res => {
let data = res.data;
if (data) {
this.status = data.status;
this.$set(this.report, "id", this.reportId);
this.$set(this.report, "status", data.status);
this.$set(this.report, "testId", data.testId);
this.$set(this.report, "loadConfiguration", data.loadConfiguration);
this.checkReportStatus(data.status);
if (this.status === "Completed" || this.status === "Running") {
this.initReportTimeInfo();
}
this.initBreadcrumb();
this.initWebSocket();
} else {
this.$error(this.$t('report.not_exist'))
}
});
},
beforeDestroy() {
this.websocket.close() //websocket
},
watch: {
'$route'(to) {
if (to.name === "perReportView") {
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;
}
let reportId = to.path.split('/')[4];
this.reportId = reportId;
if (reportId) {
this.$get("/performance/report/test/pro/info/" + reportId, response => {
let data = response.data;
if (data) {
this.status = data.status;
this.reportName = data.name;
this.testName = data.testName;
this.testId = data.testId;
this.projectName = data.projectName;
this.$set(this.report, "id", reportId);
this.$set(this.report, "status", data.status);
this.checkReportStatus(data.status);
if (this.status === "Completed") {
this.result = this.$get("/performance/report/content/report_time/" + this.reportId).then(res => {
let data = res.data.data;
if (data) {
this.startTime = data.startTime;
this.endTime = data.endTime;
let duration = data.duration;
this.minutes = Math.floor(duration / 60);
this.seconds = duration % 60;
}
}).catch(() => {
this.clearData();
})
} else {
this.clearData();
}
} else {
this.$error(this.$t('report.not_exist'));
}
});
}
}
}
}
}
}
</script>
<style scoped>
.ms-report-view-btns {
margin-top: 15px;
}
.ms-report-view-btns {
margin-top: 15px;
}
.ms-report-time-desc {
text-align: left;
display: block;
color: #5C7878;
}
.ms-report-time-desc {
text-align: left;
display: block;
color: #5C7878;
}
</style>

View File

@ -9,17 +9,29 @@
<el-row type="flex" justify="space-between" align="middle">
<el-button @click="createApiKey()" plain type="el-icon-question" icon="el-icon-circle-plus-outline"
size="mini">
{{$t('commons.create')}}
{{ $t('commons.create') }}
</el-button>
</el-row>
</div>
</template>
<el-table border class="adjust-table" :data="tableData" style="width: 100%">
<el-table-column prop="accessKey" label="Access Key"/>
<el-table-column prop="accessKey" label="Access Key">
<template v-slot:default="scope">
<div class="variable-combine">
<div class="variable">{{ scope.row.accessKey }}</div>
<div>
<el-tooltip :content="$t('api_test.copied')" manual v-model="scope.row.visible" placement="top"
:visible-arrow="false">
<i class="el-icon-copy-document copy" @click="copy(scope.row, 'accessKey', 'visible')"/>
</el-tooltip>
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="secretKey" label="Secret Key">
<template v-slot:default="scope">
<el-link type="info" @click="showSecretKey(scope.row)">{{$t('commons.show')}}</el-link>
<el-link type="primary" @click="showSecretKey(scope.row)">{{ $t('commons.show') }}</el-link>
</template>
</el-table-column>
<el-table-column prop="status" :label="$t('commons.status')">
@ -46,87 +58,134 @@
</el-table-column>
</el-table>
</el-card>
<el-dialog title="Secret Key" :visible.sync="apiKeysVisible">
<div class="variable">
{{ currentRow.secretKey }}
<el-tooltip :content="$t('api_test.copied')" manual v-model="currentRow.visible2" placement="top"
:visible-arrow="false">
<i class="el-icon-copy-document copy" @click="copy(currentRow, 'secretKey', 'visible2')"/>
</el-tooltip>
</div>
</el-dialog>
</div>
</template>
<script>
import MsDialogFooter from "../../common/components/MsDialogFooter";
import {getCurrentUser} from "../../../../common/js/utils";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsDialogFooter from "../../common/components/MsDialogFooter";
import {getCurrentUser} from "../../../../common/js/utils";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import MsTableHeader from "../../common/components/MsTableHeader";
export default {
name: "MsApiKeys",
components: {MsDialogFooter, MsTableOperatorButton, MsTableHeader},
data() {
return {
result: {},
updateVisible: false,
editPasswordVisible: false,
apiKeysVisible: false,
condition: {},
tableData: [],
}
export default {
name: "MsApiKeys",
components: {MsDialogFooter, MsTableOperatorButton, MsTableHeader},
data() {
return {
result: {},
updateVisible: false,
editPasswordVisible: false,
apiKeysVisible: false,
condition: {},
tableData: [],
currentRow: {},
}
},
activated() {
this.search();
},
methods: {
currentUser: () => {
return getCurrentUser();
},
activated() {
this.search();
search() {
this.result = this.$get("/user/key/info", response => {
this.tableData = response.data;
this.tableData.forEach(d => d.show = false);
}
)
},
methods: {
currentUser: () => {
return getCurrentUser();
},
search() {
this.result = this.$get("/user/key/info", response => {
this.tableData = response.data;
this.tableData.forEach(d => d.show = false);
}
)
},
deleteApiKey(row) {
this.$confirm(this.$t('user.apikey_delete_confirm'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.result = this.$get("/user/key/delete/" + row.id, response => {
this.$success(this.$t('commons.delete_success'));
this.search();
})
}).catch(() => {
this.$info(this.$t('commons.delete_cancel'));
});
},
createApiKey() {
this.result = this.$get("/user/key/generate", response => {
this.$success(this.$t('commons.save_success'));
deleteApiKey(row) {
this.$confirm(this.$t('user.apikey_delete_confirm'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.result = this.$get("/user/key/delete/" + row.id, response => {
this.$success(this.$t('commons.delete_success'));
this.search();
})
},
}).catch(() => {
this.$info(this.$t('commons.delete_cancel'));
});
changeSwitch(row) {
if (row.status === 'ACTIVE') {
this.result = this.$get("/user/key/active/" + row.id, response => {
this.$success(this.$t('commons.save_success'));
});
}
if (row.status === 'DISABLED') {
this.result = this.$get("/user/key/disable/" + row.id, response => {
this.$success(this.$t('commons.save_success'));
});
}
},
showSecretKey(row) {
this.$alert(row.secretKey, 'Secret Key');
},
createApiKey() {
this.result = this.$get("/user/key/generate", response => {
this.$success(this.$t('commons.save_success'));
this.search();
})
},
changeSwitch(row) {
if (row.status === 'ACTIVE') {
this.result = this.$get("/user/key/active/" + row.id, response => {
this.$success(this.$t('commons.save_success'));
});
}
}
if (row.status === 'DISABLED') {
this.result = this.$get("/user/key/disable/" + row.id, response => {
this.$success(this.$t('commons.save_success'));
});
}
},
showSecretKey(row) {
this.apiKeysVisible = true;
this.currentRow = row;
},
copy(row, key, visible) {
let input = document.createElement("input");
document.body.appendChild(input);
input.value = row[key];
input.select();
if (input.setSelectionRange) {
input.setSelectionRange(0, input.value.length);
}
document.execCommand("copy");
document.body.removeChild(input);
row[visible] = true;
setTimeout(() => {
row[visible] = false;
}, 1000);
},
}
}
</script>
<style scoped>
.variable-combine {
color: #7F7F7F;
line-height: 32px;
position: absolute;
top: 10px;
margin-right: -20px;
display: flex;
align-items: center;
}
.variable {
display: inline-block;
margin-right: 10px;
overflow-x: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.copy {
font-size: 14px;
cursor: pointer;
color: #1E90FF;
}
</style>

View File

@ -1,209 +1,206 @@
<template>
<div>
<el-card class="box-card" v-loading="result.loading">
<!--邮件表单-->
<el-form :model="formInline" :rules="rules" ref="formInline" class="demo-form-inline"
:disabled="show" v-loading="loading" size="small">
<el-row>
<el-col>
<el-form-item :label="$t('system_parameter_setting.SMTP_host')" prop="host">
<el-input v-model="formInline.host" :placeholder="$t('system_parameter_setting.SMTP_host')"
v-on:input="change()"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-form-item :label="$t('system_parameter_setting.SMTP_port')" prop="port">
<el-input v-model="formInline.port" :placeholder="$t('system_parameter_setting.SMTP_port')"
v-on:input="change()"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-form-item :label="$t('system_parameter_setting.SMTP_account')" prop="account">
<el-input v-model="formInline.account" :placeholder="$t('system_parameter_setting.SMTP_account')"
v-on:input="change()"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-form-item :label="$t('system_parameter_setting.SMTP_password')" prop="password">
<el-input v-model="formInline.password" :placeholder="$t('system_parameter_setting.SMTP_password')"
autocomplete="new-password" show-password type="text" @focus="changeType" ref="input">
</el-input>
</el-form-item>
</el-col>
</el-row>
<div v-loading="result.loading">
<!--邮件表单-->
<el-form :model="formInline" :rules="rules" ref="formInline" class="demo-form-inline"
:disabled="show" v-loading="loading" size="small">
<el-row>
<el-col>
<el-form-item :label="$t('system_parameter_setting.SMTP_host')" prop="host">
<el-input v-model="formInline.host" :placeholder="$t('system_parameter_setting.SMTP_host')"
v-on:input="change()"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-form-item :label="$t('system_parameter_setting.SMTP_port')" prop="port">
<el-input v-model="formInline.port" :placeholder="$t('system_parameter_setting.SMTP_port')"
v-on:input="change()"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-form-item :label="$t('system_parameter_setting.SMTP_account')" prop="account">
<el-input v-model="formInline.account" :placeholder="$t('system_parameter_setting.SMTP_account')"
v-on:input="change()"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-form-item :label="$t('system_parameter_setting.SMTP_password')" prop="password">
<el-input v-model="formInline.password" :placeholder="$t('system_parameter_setting.SMTP_password')"
autocomplete="new-password" show-password type="text" @focus="changeType" ref="input">
</el-input>
</el-form-item>
</el-col>
</el-row>
<!---->
<div style="border: 0px;margin-bottom: 20px;margin-top: 20px">
<el-checkbox v-model="formInline.SSL" :label="$t('system_parameter_setting.SSL')"></el-checkbox>
</div>
<div style="border: 0px;margin-bottom: 20px">
<el-checkbox v-model="formInline.TLS" :label="$t('system_parameter_setting.TLS')"></el-checkbox>
</div>
<div style="border: 0px;margin-bottom: 20px">
<el-checkbox v-model="formInline.SMTP" :label="$t('system_parameter_setting.SMTP')"></el-checkbox>
</div>
<template v-slot:footer>
</template>
</el-form>
<div>
<el-button type="primary" @click="testConnection('formInline')" :disabled="disabledConnection" size="small">
{{$t('system_parameter_setting.test_connection')}}
</el-button>
<el-button @click="edit" v-if="showEdit" size="small">{{$t('commons.edit')}}</el-button>
<el-button type="success" @click="save('formInline')" v-if="showSave" :disabled="disabledSave" size="small">
{{$t('commons.save')}}
</el-button>
<el-button @click="cancel" type="info" v-if="showCancel" size="small">{{$t('commons.cancel')}}</el-button>
<!---->
<div style="border: 0px;margin-bottom: 20px;margin-top: 20px">
<el-checkbox v-model="formInline.SSL" :label="$t('system_parameter_setting.SSL')"></el-checkbox>
</div>
</el-card>
<div style="border: 0px;margin-bottom: 20px">
<el-checkbox v-model="formInline.TLS" :label="$t('system_parameter_setting.TLS')"></el-checkbox>
</div>
<div style="border: 0px;margin-bottom: 20px">
<el-checkbox v-model="formInline.SMTP" :label="$t('system_parameter_setting.SMTP')"></el-checkbox>
</div>
<template v-slot:footer>
</template>
</el-form>
<div>
<el-button type="primary" @click="testConnection('formInline')" :disabled="disabledConnection" size="small">
{{ $t('system_parameter_setting.test_connection') }}
</el-button>
<el-button @click="edit" v-if="showEdit" size="small">{{ $t('commons.edit') }}</el-button>
<el-button type="success" @click="save('formInline')" v-if="showSave" :disabled="disabledSave" size="small">
{{ $t('commons.save') }}
</el-button>
<el-button @click="cancel" type="info" v-if="showCancel" size="small">{{ $t('commons.cancel') }}</el-button>
</div>
</div>
</template>
<script>
export default {
name: "EmailSetting",
data() {
return {
formInline: {
},
input: '',
visible: true,
result: {},
showEdit: true,
showSave: false,
showCancel: false,
show: true,
disabledConnection: false,
disabledSave: false,
loading: false,
rules: {
host: [
{
required: true,
message: this.$t('system_parameter_setting.host'),
trigger: ['change', 'blur']
},
],
port: [
{
required: true,
message: this.$t('system_parameter_setting.port'),
trigger: ['change', 'blur']
}
],
account: [
{
required: true,
message: this.$t('system_parameter_setting.account'),
trigger: ['change', 'blur']
}]
}
}
},
created() {
this.query()
},
methods: {
changeType() {
this.$refs.input = 'password'
},
query() {
this.result = this.$get("/system/mail/info", response => {
this.$set(this.formInline, "host", response.data[0].paramValue);
this.$set(this.formInline, "port", response.data[1].paramValue);
this.$set(this.formInline, "account", response.data[2].paramValue);
this.$set(this.formInline, "password", response.data[3].paramValue);
this.$set(this.formInline, "SSL", JSON.parse(response.data[4].paramValue));
this.$set(this.formInline, "TLS", JSON.parse(response.data[5].paramValue));
this.$set(this.formInline, "SMTP", JSON.parse(response.data[6].paramValue));
this.$nextTick(() => {
this.$refs.formInline.clearValidate();
})
})
},
change() {
if (!this.formInline.host || !this.formInline.port || !this.formInline.account) {
this.disabledConnection = true;
this.disabledSave = true;
} else {
this.disabledConnection = false;
this.disabledSave = false;
}
},
testConnection(formInline) {
let param = {
"smtp.server": this.formInline.host,
"smtp.port": this.formInline.port,
"smtp.account": this.formInline.account,
"smtp.password": this.formInline.password,
"smtp.ssl": this.formInline.SSL,
"smtp.tls": this.formInline.TLS,
"smtp.smtp": this.formInline.SMTP,
};
this.$refs[formInline].validate((valid) => {
if (valid) {
this.result = this.$post("/system/testConnection", param, response => {
this.$success(this.$t('commons.connection_successful'));
})
} else {
return false;
export default {
name: "EmailSetting",
data() {
return {
formInline: {},
input: '',
visible: true,
result: {},
showEdit: true,
showSave: false,
showCancel: false,
show: true,
disabledConnection: false,
disabledSave: false,
loading: false,
rules: {
host: [
{
required: true,
message: this.$t('system_parameter_setting.host'),
trigger: ['change', 'blur']
},
],
port: [
{
required: true,
message: this.$t('system_parameter_setting.port'),
trigger: ['change', 'blur']
}
})
},
edit() {
this.showEdit = false;
this.showSave = true;
this.showCancel = true;
this.show = false;
},
save(formInline) {
this.showEdit = true;
this.showCancel = false;
this.showSave = false;
this.show = true;
let param = [
{paramKey: "smtp.host", paramValue: this.formInline.host, type: "text", sort: 1},
{paramKey: "smtp.port", paramValue: this.formInline.port, type: "text", sort: 2},
{paramKey: "smtp.account", paramValue: this.formInline.account, type: "text", sort: 3},
{paramKey: "smtp.password", paramValue: this.formInline.password, type: "password", sort: 4},
{paramKey: "smtp.ssl", paramValue: this.formInline.SSL, type: "text", sort: 5},
{paramKey: "smtp.tls", paramValue: this.formInline.TLS, type: "text", sort: 6},
{paramKey: "smtp.smtp", paramValue: this.formInline.SMTP, type: "text", sort: 7}
]
this.$refs[formInline].validate(valid => {
if (valid) {
this.result = this.$post("/system/edit/email", param, response => {
let flag = response.success;
if (flag) {
this.$success(this.$t('commons.save_success'));
} else {
this.$message.error(this.$t('commons.save_failed'));
}
});
} else {
return false;
}
})
},
cancel() {
this.showEdit = true;
this.showCancel = false;
this.showSave = false;
this.show = true;
this.query();
],
account: [
{
required: true,
message: this.$t('system_parameter_setting.account'),
trigger: ['change', 'blur']
}]
}
}
},
created() {
this.query()
},
methods: {
changeType() {
this.$refs.input = 'password'
},
query() {
this.result = this.$get("/system/mail/info", response => {
this.$set(this.formInline, "host", response.data[0].paramValue);
this.$set(this.formInline, "port", response.data[1].paramValue);
this.$set(this.formInline, "account", response.data[2].paramValue);
this.$set(this.formInline, "password", response.data[3].paramValue);
this.$set(this.formInline, "SSL", JSON.parse(response.data[4].paramValue));
this.$set(this.formInline, "TLS", JSON.parse(response.data[5].paramValue));
this.$set(this.formInline, "SMTP", JSON.parse(response.data[6].paramValue));
this.$nextTick(() => {
this.$refs.formInline.clearValidate();
})
})
},
change() {
if (!this.formInline.host || !this.formInline.port || !this.formInline.account) {
this.disabledConnection = true;
this.disabledSave = true;
} else {
this.disabledConnection = false;
this.disabledSave = false;
}
},
testConnection(formInline) {
let param = {
"smtp.server": this.formInline.host,
"smtp.port": this.formInline.port,
"smtp.account": this.formInline.account,
"smtp.password": this.formInline.password,
"smtp.ssl": this.formInline.SSL,
"smtp.tls": this.formInline.TLS,
"smtp.smtp": this.formInline.SMTP,
};
this.$refs[formInline].validate((valid) => {
if (valid) {
this.result = this.$post("/system/testConnection", param, response => {
this.$success(this.$t('commons.connection_successful'));
})
} else {
return false;
}
})
},
edit() {
this.showEdit = false;
this.showSave = true;
this.showCancel = true;
this.show = false;
},
save(formInline) {
this.showEdit = true;
this.showCancel = false;
this.showSave = false;
this.show = true;
let param = [
{paramKey: "smtp.host", paramValue: this.formInline.host, type: "text", sort: 1},
{paramKey: "smtp.port", paramValue: this.formInline.port, type: "text", sort: 2},
{paramKey: "smtp.account", paramValue: this.formInline.account, type: "text", sort: 3},
{paramKey: "smtp.password", paramValue: this.formInline.password, type: "password", sort: 4},
{paramKey: "smtp.ssl", paramValue: this.formInline.SSL, type: "text", sort: 5},
{paramKey: "smtp.tls", paramValue: this.formInline.TLS, type: "text", sort: 6},
{paramKey: "smtp.smtp", paramValue: this.formInline.SMTP, type: "text", sort: 7}
]
this.$refs[formInline].validate(valid => {
if (valid) {
this.result = this.$post("/system/edit/email", param, response => {
let flag = response.success;
if (flag) {
this.$success(this.$t('commons.save_success'));
} else {
this.$message.error(this.$t('commons.save_failed'));
}
});
} else {
return false;
}
})
},
cancel() {
this.showEdit = true;
this.showCancel = false;
this.showSave = false;
this.show = true;
this.query();
}
}
}
</script>
<style scoped>

View File

@ -1,222 +1,221 @@
<template>
<div>
<el-card class="box-card" v-loading="result.loading">
<el-form :model="form" size="small" :rules="rules" :disabled="show" ref="form">
<el-form-item :label="$t('ldap.url')" prop="url">
<el-input v-model="form.url" :placeholder="$t('ldap.input_url_placeholder')"/>
<div v-loading="result.loading">
<el-form :model="form" size="small" :rules="rules" :disabled="show" ref="form">
<el-form-item :label="$t('ldap.url')" prop="url">
<el-input v-model="form.url" :placeholder="$t('ldap.input_url_placeholder')"/>
</el-form-item>
<el-form-item :label="$t('ldap.dn')" prop="dn">
<el-input v-model="form.dn" :placeholder="$t('ldap.input_dn')"/>
</el-form-item>
<el-form-item :label="$t('ldap.password')" prop="password">
<el-input v-model="form.password" :placeholder="$t('ldap.input_password')" show-password
auto-complete="new-password"/>
</el-form-item>
<el-form-item :label="$t('ldap.ou')" prop="ou">
<el-input v-model="form.ou" :placeholder="$t('ldap.input_ou_placeholder')"/>
</el-form-item>
<el-form-item :label="$t('ldap.filter')" prop="filter">
<el-input v-model="form.filter" :placeholder="$t('ldap.input_filter_placeholder')"/>
</el-form-item>
<el-form-item :label="$t('ldap.mapping')" prop="mapping">
<el-input v-model="form.mapping" :placeholder="$t('ldap.input_mapping_placeholder')"/>
</el-form-item>
<el-form-item :label="$t('ldap.open')" prop="open">
<el-checkbox v-model="form.open"/>
</el-form-item>
</el-form>
<div>
<el-button type="primary" size="small" :disabled="!show" @click="testConnection">{{ $t('ldap.test_connect') }}
</el-button>
<el-button type="primary" size="small" :disabled="!showLogin || !show" @click="testLogin">
{{ $t('ldap.test_login') }}
</el-button>
<el-button v-if="showEdit" size="small" @click="edit">{{ $t('ldap.edit') }}</el-button>
<el-button type="success" v-if="showSave" size="small" @click="save('form')">{{ $t('commons.save') }}
</el-button>
<el-button type="info" v-if="showCancel" size="small" @click="cancel">{{ $t('commons.cancel') }}</el-button>
</div>
<el-dialog :title="$t('ldap.test_login')" :visible.sync="loginVisible" width="30%" destroy-on-close
v-loading="result.loading" @close="close">
<el-form :model="loginForm" :rules="loginFormRules" ref="loginForm" label-width="90px">
<el-form-item :label="$t('commons.username')" prop="username">
<el-input v-model="loginForm.username" autocomplete="off" :placeholder="$t('ldap.input_username')"/>
</el-form-item>
<el-form-item :label="$t('ldap.dn')" prop="dn">
<el-input v-model="form.dn" :placeholder="$t('ldap.input_dn')"/>
</el-form-item>
<el-form-item :label="$t('ldap.password')" prop="password">
<el-input v-model="form.password" :placeholder="$t('ldap.input_password')" show-password
auto-complete="new-password"/>
</el-form-item>
<el-form-item :label="$t('ldap.ou')" prop="ou">
<el-input v-model="form.ou" :placeholder="$t('ldap.input_ou_placeholder')"/>
</el-form-item>
<el-form-item :label="$t('ldap.filter')" prop="filter">
<el-input v-model="form.filter" :placeholder="$t('ldap.input_filter_placeholder')"/>
</el-form-item>
<el-form-item :label="$t('ldap.mapping')" prop="mapping">
<el-input v-model="form.mapping" :placeholder="$t('ldap.input_mapping_placeholder')"/>
</el-form-item>
<el-form-item :label="$t('ldap.open')" prop="open">
<el-checkbox v-model="form.open"/>
<el-form-item :label="$t('commons.password')" prop="password">
<el-input v-model="loginForm.password" autocomplete="new-password" :placeholder="$t('ldap.input_password')"
show-password/>
</el-form-item>
</el-form>
<div>
<el-button type="primary" size="small" :disabled="!show" @click="testConnection">{{$t('ldap.test_connect')}}
</el-button>
<el-button type="primary" size="small" :disabled="!showLogin || !show" @click="testLogin">
{{$t('ldap.test_login')}}
</el-button>
<el-button v-if="showEdit" size="small" @click="edit">{{$t('ldap.edit')}}</el-button>
<el-button type="success" v-if="showSave" size="small" @click="save('form')">{{$t('commons.save')}}</el-button>
<el-button type="info" v-if="showCancel" size="small" @click="cancel">{{$t('commons.cancel')}}</el-button>
</div>
<el-dialog :title="$t('ldap.test_login')" :visible.sync="loginVisible" width="30%" destroy-on-close
v-loading="result.loading" @close="close">
<el-form :model="loginForm" :rules="loginFormRules" ref="loginForm" label-width="90px">
<el-form-item :label="$t('commons.username')" prop="username">
<el-input v-model="loginForm.username" autocomplete="off" :placeholder="$t('ldap.input_username')"/>
</el-form-item>
<el-form-item :label="$t('commons.password')" prop="password">
<el-input v-model="loginForm.password" autocomplete="new-password" :placeholder="$t('ldap.input_password')"
show-password/>
</el-form-item>
</el-form>
<span slot="footer">
<span slot="footer">
<ms-dialog-footer
@cancel="loginVisible = false"
@confirm="login('loginForm')"/>
</span>
</el-dialog>
</el-dialog>
</el-card>
</div>
</template>
<script>
import MsDialogFooter from "../../common/components/MsDialogFooter";
import {listenGoBack, removeGoBackListener} from "../../../../common/js/utils";
import MsDialogFooter from "../../common/components/MsDialogFooter";
import {listenGoBack, removeGoBackListener} from "../../../../common/js/utils";
export default {
name: "LdapSetting",
components: {
MsDialogFooter
},
data() {
return {
form: {open: false},
loginForm: {},
result: {},
show: true,
showEdit: true,
showSave: false,
showCancel: false,
showLogin: false,
loginVisible: false,
rules: {
url: {required: true, message: this.$t('ldap.input_url'), trigger: ['change', 'blur']},
dn: {required: true, message: this.$t('ldap.input_dn'), trigger: ['change', 'blur']},
password: {required: true, message: this.$t('ldap.input_password'), trigger: ['change', 'blur']},
ou: {required: true, message: this.$t('ldap.input_ou'), trigger: ['change', 'blur']},
filter: {required: true, message: this.$t('ldap.input_filter'), trigger: ['change', 'blur']},
mapping: {required: true, message: this.$t('ldap.input_mapping'), trigger: ['change', 'blur']}
},
loginFormRules: {
username: {required: true, message: this.$t('ldap.input_username'), trigger: 'blur'},
password: {required: true, message: this.$t('ldap.input_password'), trigger: 'blur'}
}
}
},
created() {
this.init();
},
methods: {
init() {
this.result = this.$get("/system/ldap/info", response => {
this.form = response.data;
this.form.open = this.form.open === 'true';
this.$nextTick(() => {
this.$refs.form.clearValidate();
})
})
export default {
name: "LdapSetting",
components: {
MsDialogFooter
},
data() {
return {
form: {open: false},
loginForm: {},
result: {},
show: true,
showEdit: true,
showSave: false,
showCancel: false,
showLogin: false,
loginVisible: false,
rules: {
url: {required: true, message: this.$t('ldap.input_url'), trigger: ['change', 'blur']},
dn: {required: true, message: this.$t('ldap.input_dn'), trigger: ['change', 'blur']},
password: {required: true, message: this.$t('ldap.input_password'), trigger: ['change', 'blur']},
ou: {required: true, message: this.$t('ldap.input_ou'), trigger: ['change', 'blur']},
filter: {required: true, message: this.$t('ldap.input_filter'), trigger: ['change', 'blur']},
mapping: {required: true, message: this.$t('ldap.input_mapping'), trigger: ['change', 'blur']}
},
edit() {
this.show = false;
this.showEdit = false;
this.showSave = true;
this.showCancel = true;
},
cancel() {
this.showEdit = true;
this.showCancel = false;
this.showSave = false;
this.show = true;
this.init();
},
close() {
removeGoBackListener(this.close);
this.loginVisible = false;
},
testConnection() {
if (!this.checkParam()) {
return false;
}
this.result = this.$post("/ldap/test/connect", this.form, () => {
this.$success(this.$t('commons.connection_successful'));
this.showLogin = true;
}, () => {
this.showLogin = false;
})
},
testLogin() {
if (!this.checkParam()) {
return false;
}
if (!this.form.ou) {
this.$warning(this.$t('ldap.ou_cannot_be_empty'));
return false;
}
if (!this.form.filter) {
this.$warning(this.$t('ldap.filter_cannot_be_empty'));
return false;
}
if (!this.form.mapping) {
this.$warning(this.$t('ldap.mapping_cannot_be_empty'));
return false;
}
this.loginForm = {};
this.loginVisible = true;
listenGoBack(this.close);
},
checkParam() {
if (!this.form.url) {
this.$warning(this.$t('ldap.url_cannot_be_empty'));
return false;
}
if (!this.form.dn) {
this.$warning(this.$t('ldap.dn_cannot_be_empty'));
return false;
}
if (!this.form.password) {
this.$warning(this.$t('ldap.password_cannot_be_empty'));
return false;
}
return true;
},
save(form) {
let param = [
{paramKey: "ldap.url", paramValue: this.form.url, type: "text", sort: 1},
{paramKey: "ldap.dn", paramValue: this.form.dn, type: "text", sort: 2},
{paramKey: "ldap.password", paramValue: this.form.password, type: "password", sort: 3},
{paramKey: "ldap.ou", paramValue: this.form.ou, type: "text", sort: 4},
{paramKey: "ldap.filter", paramValue: this.form.filter, type: "text", sort: 5},
{paramKey: "ldap.mapping", paramValue: this.form.mapping, type: "text", sort: 6},
{paramKey: "ldap.open", paramValue: this.form.open, type: "text", sort: 7}
];
this.$refs[form].validate(valid => {
if (valid) {
this.result = this.$post("/system/save/ldap", param, () => {
this.show = true;
this.showEdit = true;
this.showSave = false;
this.showCancel = false;
this.showLogin = false;
this.$success(this.$t('commons.save_success'));
this.init();
});
} else {
return false;
}
})
},
login(form) {
this.$refs[form].validate(valid => {
if (valid) {
this.result = this.$post("/ldap/test/login", this.loginForm, () => {
this.$success(this.$t('ldap.login_success'));
});
} else {
return false;
}
})
loginFormRules: {
username: {required: true, message: this.$t('ldap.input_username'), trigger: 'blur'},
password: {required: true, message: this.$t('ldap.input_password'), trigger: 'blur'}
}
}
},
created() {
this.init();
},
methods: {
init() {
this.result = this.$get("/system/ldap/info", response => {
this.form = response.data;
this.form.open = this.form.open === 'true';
this.$nextTick(() => {
this.$refs.form.clearValidate();
})
})
},
edit() {
this.show = false;
this.showEdit = false;
this.showSave = true;
this.showCancel = true;
},
cancel() {
this.showEdit = true;
this.showCancel = false;
this.showSave = false;
this.show = true;
this.init();
},
close() {
removeGoBackListener(this.close);
this.loginVisible = false;
},
testConnection() {
if (!this.checkParam()) {
return false;
}
this.result = this.$post("/ldap/test/connect", this.form, () => {
this.$success(this.$t('commons.connection_successful'));
this.showLogin = true;
}, () => {
this.showLogin = false;
})
},
testLogin() {
if (!this.checkParam()) {
return false;
}
if (!this.form.ou) {
this.$warning(this.$t('ldap.ou_cannot_be_empty'));
return false;
}
if (!this.form.filter) {
this.$warning(this.$t('ldap.filter_cannot_be_empty'));
return false;
}
if (!this.form.mapping) {
this.$warning(this.$t('ldap.mapping_cannot_be_empty'));
return false;
}
this.loginForm = {};
this.loginVisible = true;
listenGoBack(this.close);
},
checkParam() {
if (!this.form.url) {
this.$warning(this.$t('ldap.url_cannot_be_empty'));
return false;
}
if (!this.form.dn) {
this.$warning(this.$t('ldap.dn_cannot_be_empty'));
return false;
}
if (!this.form.password) {
this.$warning(this.$t('ldap.password_cannot_be_empty'));
return false;
}
return true;
},
save(form) {
let param = [
{paramKey: "ldap.url", paramValue: this.form.url, type: "text", sort: 1},
{paramKey: "ldap.dn", paramValue: this.form.dn, type: "text", sort: 2},
{paramKey: "ldap.password", paramValue: this.form.password, type: "password", sort: 3},
{paramKey: "ldap.ou", paramValue: this.form.ou, type: "text", sort: 4},
{paramKey: "ldap.filter", paramValue: this.form.filter, type: "text", sort: 5},
{paramKey: "ldap.mapping", paramValue: this.form.mapping, type: "text", sort: 6},
{paramKey: "ldap.open", paramValue: this.form.open, type: "text", sort: 7}
];
this.$refs[form].validate(valid => {
if (valid) {
this.result = this.$post("/system/save/ldap", param, () => {
this.show = true;
this.showEdit = true;
this.showSave = false;
this.showCancel = false;
this.showLogin = false;
this.$success(this.$t('commons.save_success'));
this.init();
});
} else {
return false;
}
})
},
login(form) {
this.$refs[form].validate(valid => {
if (valid) {
this.result = this.$post("/ldap/test/login", this.loginForm, () => {
this.$success(this.$t('ldap.login_success'));
});
} else {
return false;
}
})
}
}
}
</script>
<style scoped>

View File

@ -1,5 +1,5 @@
<template>
<div>
<el-card>
<el-tabs class="system-setting" v-model="activeName">
<el-tab-pane :label="$t('system_parameter_setting.mailbox_service_settings')" name="email">
<email-setting/>
@ -8,23 +8,24 @@
<ldap-setting/>
</el-tab-pane>
</el-tabs>
</div>
</el-card>
</template>
<script>
import EmailSetting from "./EmailSetting";
import LdapSetting from "./LdapSetting";
export default {
name: "SystemParameterSetting",
components: {
EmailSetting, LdapSetting
},
data() {
return {
activeName: 'email'
}
import EmailSetting from "./EmailSetting";
import LdapSetting from "./LdapSetting";
export default {
name: "SystemParameterSetting",
components: {
EmailSetting, LdapSetting
},
data() {
return {
activeName: 'email'
}
}
}
</script>
<style scoped>

View File

@ -14,7 +14,7 @@
<el-option v-for="(type, index) in typeArr" :key="index" :value="type.id" :label="type.name"/>
</el-select>
</el-form-item>
<el-form-item label="更新后属性值为" prop="value">
<el-form-item :label="$t('test_track.case.updated_attr_value')" prop="value">
<el-select v-model="form.value" style="width: 80%" :filterable="filterable">
<el-option v-for="(option, index) in options" :key="index" :value="option.id" :label="option.name">
<div v-if="option.email">
@ -47,7 +47,9 @@
valueArr: Object,
dialogTitle: {
type: String,
default: "批量操作"
default() {
return this.$t('test_track.case.batch_operate')
}
}
},
data() {
@ -56,8 +58,8 @@
form: {},
size: 0,
rules: {
type: {required: true, message: "请选择属性", trigger: ['blur','change']},
value: {required: true, message: "请选择属性对应的值", trigger: ['blur','change']}
type: {required: true, message: this.$t('test_track.case.please_select_attr'), trigger: ['blur','change']},
value: {required: true, message: this.$t('test_track.case.please_select_attr_value'), trigger: ['blur','change']}
},
options: [],
filterable: false,

View File

@ -1,6 +1,6 @@
<template>
<div v-loading="result.loading">
<el-dialog title="选择用例目录"
<el-dialog :title="this.$t('test_track.case.select_catalog')"
:visible.sync="dialogVisible"
:before-close="close"
:destroy-on-close="true"

View File

@ -121,7 +121,7 @@
</el-card>
<batch-edit ref="batchEdit" @batchEdit="batchEdit"
:typeArr="typeArr" :value-arr="valueArr" dialog-title="批量编辑用例"/>
:typeArr="typeArr" :value-arr="valueArr" :dialog-title="$t('test_track.case.batch_edit_case')"/>
</div>
</template>
@ -193,18 +193,18 @@
showMore: false,
buttons: [
{
name: '批量编辑用例', handleClick: this.handleBatchEdit
name: this.$t('test_track.case.batch_edit_case'), handleClick: this.handleBatchEdit
}, {
name: '批量移动用例', handleClick: this.handleBatchMove
name: this.$t('test_track.case.batch_move_case'), handleClick: this.handleBatchMove
}, {
name: '批量删除用例', handleClick: this.handleDeleteBatch
name: this.$t('test_track.case.batch_delete_case'), handleClick: this.handleDeleteBatch
}
],
typeArr: [
{id: 'priority', name: '用例等级'},
{id: 'type', name: '类型'},
{id: 'method', name: '测试方式'},
{id: 'maintainer', name: '维护人'},
{id: 'priority', name: this.$t('test_track.case.priority')},
{id: 'type', name: this.$t('test_track.case.type')},
{id: 'method', name: this.$t('test_track.case.method')},
{id: 'maintainer', name: this.$t('test_track.case.maintainer')},
],
valueArr: {
priority: [

View File

@ -181,7 +181,7 @@
:active-text="$t('test_track.plan_view.submit_issues')">
</el-switch>
<el-tooltip class="item" effect="dark"
content="在系统设置-组织-服务集成中集成缺陷管理平台可以自动提交缺陷到指定缺陷管理平台"
:content="$t('test_track.issue.platform_tip')"
placement="right">
<i class="el-icon-info"/>
</el-tooltip>
@ -192,7 +192,7 @@
<el-col :span="20" :offset="1" class="issues-edit">
<el-input
type="text"
placeholder="请输入标题"
:placeholder="$t('test_track.issue.input_title')"
v-model="testCase.issues.title"
maxlength="100"
show-word-limit
@ -207,14 +207,14 @@
<el-row>
<el-col :span="20" :offset="1" class="issues-edit">
<el-table border class="adjust-table" :data="issues" style="width: 100%">
<el-table-column prop="id" label="缺陷ID" show-overflow-tooltip/>
<el-table-column prop="title" label="缺陷标题"/>
<el-table-column prop="description" label="缺陷描述" show-overflow-tooltip/>
<el-table-column prop="status" label="缺陷状态"/>
<el-table-column prop="platform" label="平台"/>
<el-table-column label="操作">
<el-table-column prop="id" :label="$t('test_track.issue.id')" show-overflow-tooltip/>
<el-table-column prop="title" :label="$t('test_track.issue.title')"/>
<el-table-column prop="description" :label="$t('test_track.issue.description')" show-overflow-tooltip/>
<el-table-column prop="status" :label="$t('test_track.issue.status')"/>
<el-table-column prop="platform" :label="$t('test_track.issue.platform')"/>
<el-table-column :label="$t('test_track.issue.operate')">
<template v-slot:default="scope">
<el-tooltip content="关闭缺陷"
<el-tooltip :content="$t('test_track.issue.close')"
placement="right">
<el-button type="danger" icon="el-icon-circle-close" size="mini"
circle v-if="scope.row.platform === 'Local'"
@ -473,7 +473,7 @@
},
saveIssues() {
if (!this.testCase.issues.title || !this.testCase.issues.content) {
this.$warning("标题和描述必填");
this.$warning(this.$t('test_track.issue.title_description_required'));
return;
}
let param = {};
@ -494,7 +494,7 @@
closeIssue(row) {
this.result = this.$get("/issues/close/" + row.id, () => {
this.getIssues(this.testCase.caseId);
this.$success("关闭成功");
this.$success(this.$t('test_track.issue.close_success'));
});
}
}

View File

@ -104,7 +104,7 @@
<el-table-column
prop="nodePath"
label="缺陷"
:label="$t('test_track.issue.issue')"
show-overflow-tooltip>
<template v-slot:default="scope">
<el-popover
@ -113,10 +113,10 @@
trigger="hover">
<el-table border class="adjust-table" :data="scope.row.issuesContent" style="width: 100%">
<!-- <el-table-column prop="id" label="缺陷ID" show-overflow-tooltip/>-->
<el-table-column prop="title" label="缺陷标题"/>
<el-table-column prop="description" label="缺陷描述" show-overflow-tooltip/>
<el-table-column prop="title" :label="$t('test_track.issue.title')"/>
<el-table-column prop="description" :label="$t('test_track.issue.description')" show-overflow-tooltip/>
<!-- <el-table-column prop="status" label="缺陷状态"/>-->
<el-table-column prop="platform" label="平台"/>
<el-table-column prop="platform" :label="$t('test_track.issue.platform')"/>
</el-table>
<el-button slot="reference" type="text">{{scope.row.issuesSize}}</el-button>
</el-popover>
@ -195,7 +195,7 @@
<test-case-report-view @refresh="initTableData" ref="testCaseReportView"/>
</el-card>
<batch-edit ref="batchEdit" @batchEdit="batchEdit"
:type-arr="typeArr" :value-arr="valueArr" dialog-title="批量更改测试计划"/>
:type-arr="typeArr" :value-arr="valueArr" :dialog-title="$t('test_track.case.batch_edit_plan')"/>
</div>
</template>
@ -280,15 +280,15 @@
showMore: false,
buttons: [
{
name: '批量更改测试计划', handleClick: this.handleBatchEdit
name: this.$t('test_track.case.batch_edit_plan'), handleClick: this.handleBatchEdit
},
{
name: '批量取消用例关联', handleClick: this.handleDeleteBatch
name: this.$t('test_track.case.batch_unlink'), handleClick: this.handleDeleteBatch
}
],
typeArr: [
{id: 'status', name: '执行结果'},
{id: 'executor', name: '执行人'},
{id: 'status', name: this.$t('test_track.plan_view.execute_result')},
{id: 'executor', name: this.$t('test_track.plan_view.executor')},
],
valueArr: {
executor: [],

View File

@ -11,7 +11,7 @@
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="module"
prop="model"
:label="$t('test_track.module.module')"
show-overflow-tooltip>
</el-table-column>
@ -32,13 +32,13 @@
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="currentOwner"
prop="reporter"
:label="$t('test_track.module.current_owner')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="created"
prop="createTime"
:label="$t('test_track.module.creation_time')">
</el-table-column>
</el-table>
@ -67,8 +67,8 @@
title: 'testCase1',
description: "第一个模块测试",
status: "接受/处理",
currentOwner: "Andy",
created: "2010.3.3",
reporter: "Andy",
createTime: "2010.3.3",
},
]
}

View File

@ -0,0 +1,15 @@
<template>
<div>
<chart/>
</div>
</template>
<script>
export default {
name: "ExportTestCaseReport"
}
</script>
<style scoped>
</style>

View File

@ -17,7 +17,7 @@
<test-result-component :test-results="metric.moduleExecuteResult" v-if="preview.id == 2"/>
<test-result-chart-component :execute-result="metric.executeResult" v-if="preview.id == 3"/>
<failure-result-component :failure-test-cases="metric.failureTestCases" v-if="preview.id == 4"/>
<defect-list-component :defect-list="metric.defectList" v-if="preview.id == 5"/>
<defect-list-component :defect-list="metric.issues" v-if="preview.id == 5"/>
<rich-text-component :is-report-view="isReportView" :preview="preview" v-if="preview.type != 'system'"/>
</div>

View File

@ -66,6 +66,8 @@
this.charData.push(data);
});
this.reload();
},
reload() {
this.isShow = false;

View File

@ -13,18 +13,25 @@
<el-row type="flex" class="head-bar">
<el-col :span="12">
<div class="name-edit">
<el-button plain size="mini" icon="el-icon-back" @click="handleClose">{{$t('test_track.return')}}</el-button>
&nbsp;
<el-button plain size="mini" icon="el-icon-back" @click="handleClose">{{$t('test_track.return')}}
</el-button>&nbsp;
<span class="title">{{report.name}}</span>
</div>
</el-col>
<el-col :span="12" class="head-right">
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleSave">{{$t('commons.save')}}</el-button>
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleEdit">{{$t('test_track.plan_view.edit_component')}}</el-button>
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleSave">
{{$t('commons.save')}}
</el-button>
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleEdit">
{{$t('test_track.plan_view.edit_component')}}
</el-button>
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleExport(report.name)">
{{$t('test_track.plan_view.export_report')}}
</el-button>
</el-col>
</el-row>
<div class="container">
<div class="container" ref="resume">
<el-main>
<div class="preview" v-for="item in previews" :key="item.id">
<template-component :isReportView="true" :metric="metric" :preview="item"/>
@ -41,154 +48,188 @@
<script>
import {checkoutTestManagerOrTestUser, jsonToMap, mapToJson} from "../../../../../../../common/js/utils";
import BaseInfoComponent from "./TemplateComponent/BaseInfoComponent";
import TestResultChartComponent from "./TemplateComponent/TestResultChartComponent";
import TestResultComponent from "./TemplateComponent/TestResultComponent";
import RichTextComponent from "./TemplateComponent/RichTextComponent";
import TestCaseReportTemplateEdit from "./TestCaseReportTemplateEdit";
import TemplateComponent from "./TemplateComponent/TemplateComponent";
import BaseInfoComponent from "./TemplateComponent/BaseInfoComponent";
import TestResultChartComponent from "./TemplateComponent/TestResultChartComponent";
import TestResultComponent from "./TemplateComponent/TestResultComponent";
import RichTextComponent from "./TemplateComponent/RichTextComponent";
import TestCaseReportTemplateEdit from "./TestCaseReportTemplateEdit";
import TemplateComponent from "./TemplateComponent/TemplateComponent";
import writer from 'file-writer'
import ResumeCss from "../../../../../../../common/css/main.css";
export default {
name: "TestCaseReportView",
components: {
TemplateComponent,
TestCaseReportTemplateEdit,
RichTextComponent, TestResultComponent, TestResultChartComponent, BaseInfoComponent},
data() {
return {
result: {},
showDialog: false,
previews: [],
report: {},
reportId: '',
metric: {},
planId: '',
componentMap: new Map(
[
[1, { name: this.$t('test_track.plan_view.base_info'), id: 1 , type: 'system'}],
[2, { name: this.$t('test_track.plan_view.test_result'), id: 2 , type: 'system'}],
[3, { name: this.$t('test_track.plan_view.result_distribution'), id: 3 ,type: 'system'}],
[4, { name: this.$t('test_track.plan_view.failure_case'), id: 4 ,type: 'system'}],
[5, { name: this.$t('test_track.plan_view.defect_list'), id: 5 ,type: 'system'}],
[6, { name: this.$t('test_track.plan_view.custom_component'), id: 6 ,type: 'custom'}]
]
),
isTestManagerOrTestUser: false
export default {
name: "TestCaseReportView",
components: {
TemplateComponent, ResumeCss,
TestCaseReportTemplateEdit,
RichTextComponent, TestResultComponent, TestResultChartComponent, BaseInfoComponent
},
data() {
return {
result: {},
imgUrl: "",
showDialog: false,
previews: [],
report: {},
reportId: '',
metric: {},
planId: '',
componentMap: new Map(
[
[1, {name: this.$t('test_track.plan_view.base_info'), id: 1, type: 'system'}],
[2, {name: this.$t('test_track.plan_view.test_result'), id: 2, type: 'system'}],
[3, {name: this.$t('test_track.plan_view.result_distribution'), id: 3, type: 'system'}],
[4, {name: this.$t('test_track.plan_view.failure_case'), id: 4, type: 'system'}],
[5, {name: this.$t('test_track.plan_view.defect_list'), id: 5, type: 'system'}],
[6, {name: this.$t('test_track.plan_view.custom_component'), id: 6, type: 'custom'}]
]
),
isTestManagerOrTestUser: false
}
},
mounted() {
this.isTestManagerOrTestUser = checkoutTestManagerOrTestUser();
},
methods: {
listenGoBack() {
//
if (window.history && window.history.pushState) {
history.pushState(null, null, document.URL);
window.addEventListener('popstate', this.goBack, false);
}
},
mounted() {
this.isTestManagerOrTestUser = checkoutTestManagerOrTestUser();
goBack() {
this.handleClose();
},
methods: {
listenGoBack() {
//
if (window.history && window.history.pushState) {
history.pushState(null, null, document.URL);
window.addEventListener('popstate', this.goBack, false);
open(planId, id) {
this.planId = planId;
if (id) {
this.reportId = id;
}
this.getReport();
this.showDialog = true;
this.listenGoBack();
},
getReport() {
this.result = this.$get('/case/report/get/' + this.reportId, response => {
this.report = response.data;
this.report.content = JSON.parse(response.data.content);
if (this.report.content.customComponent) {
this.report.content.customComponent = jsonToMap(this.report.content.customComponent);
}
},
goBack() {
this.handleClose();
},
open(planId, id) {
this.planId = planId;
if (id) {
this.reportId = id;
}
this.getReport();
this.showDialog = true;
this.listenGoBack();
},
getReport() {
this.result = this.$get('/case/report/get/' + this.reportId, response => {
this.report = response.data;
this.report.content = JSON.parse(response.data.content);
this.getMetric();
this.initPreviews();
});
},
initPreviews() {
this.previews = [];
this.report.content.components.forEach(item => {
let preview = this.componentMap.get(item);
if (preview && preview.type != 'custom') {
this.previews.push(preview);
} else {
if (this.report.content.customComponent) {
this.report.content.customComponent = jsonToMap(this.report.content.customComponent);
}
this.getMetric();
this.initPreviews();
});
},
initPreviews() {
this.previews = [];
this.report.content.components.forEach(item => {
let preview = this.componentMap.get(item);
if (preview && preview.type != 'custom') {
this.previews.push(preview);
} else {
if (this.report.content.customComponent) {
let customComponent = this.report.content.customComponent.get(item.toString());
if (customComponent) {
this.previews.push({id: item, title: customComponent.title, content: customComponent.content});
}
let customComponent = this.report.content.customComponent.get(item.toString());
if (customComponent) {
this.previews.push({id: item, title: customComponent.title, content: customComponent.content});
}
}
});
},
handleClose() {
window.removeEventListener('popstate', this.goBack, false);
this.$emit('refresh');
this.showDialog = false;
},
handleEdit() {
this.$refs.templateEdit.open(this.reportId, true);
},
handleSave() {
let param = {};
this.buildParam(param);
this.result = this.$post('/case/report/edit', param, () =>{
this.$success(this.$t('commons.save_success'));
});
},
buildParam(param) {
let content = {};
content.components = [];
this.previews.forEach(item => {
content.components.push(item.id);
if (!this.componentMap.get(item.id)) {
content.customComponent = new Map();
content.customComponent.set(item.id, {title: item.title, content: item.content})
}
});
param.name = this.report.name;
if (content.customComponent) {
content.customComponent = mapToJson(content.customComponent);
}
param.content = JSON.stringify(content);
param.id = this.report.id;
if (this.metric.startTime) {
param.startTime = this.metric.startTime.getTime();
});
},
handleClose() {
window.removeEventListener('popstate', this.goBack, false);
this.$emit('refresh');
this.showDialog = false;
},
handleEdit() {
this.$refs.templateEdit.open(this.reportId, true);
},
/*导出报告*/
/*handleExport(name) {
let html = this.getHtml();
writer(`${name}.html`, html, 'utf-8');
},
getHtml() {
const template = this.$refs.resume.innerHTML
let html = `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>html</title>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<style>${ResumeCss}</style>
</head>
<body>
<div style="margin:0 auto;width:1200px">
<img id ='div' :src = "imgUrl" :style=" {width: '1000px', height: '500px'}" />
${template}
</div>
</body>
</html>`
return html
},*/
handleSave() {
let param = {};
this.buildParam(param);
this.result = this.$post('/case/report/edit', param, () => {
this.$success(this.$t('commons.save_success'));
});
},
buildParam(param) {
let content = {};
content.components = [];
this.previews.forEach(item => {
content.components.push(item.id);
if (!this.componentMap.get(item.id)) {
content.customComponent = new Map();
content.customComponent.set(item.id, {title: item.title, content: item.content})
}
if (this.metric.endTime) {
param.endTime = this.metric.endTime.getTime();
}
},
getMetric() {
this.result = this.$get('/test/plan/get/metric/' + this.planId, response => {
this.metric = response.data;
if (!this.metric.failureTestCases) {
this.metric.failureTestCases = [];
}
if (!this.metric.executeResult) {
this.metric.executeResult = [];
}
if (!this.metric.moduleExecuteResult) {
this.metric.moduleExecuteResult = [];
}
/*缺陷列表*/
this.metric.defectList = [];
if (this.report.startTime) {
this.metric.startTime = new Date(this.report.startTime);
}
if (this.report.endTime) {
this.metric.endTime = new Date(this.report.endTime);
}
});
});
param.name = this.report.name;
if (content.customComponent) {
content.customComponent = mapToJson(content.customComponent);
}
}
param.content = JSON.stringify(content);
param.id = this.report.id;
if (this.metric.startTime) {
param.startTime = this.metric.startTime.getTime();
}
if (this.metric.endTime) {
param.endTime = this.metric.endTime.getTime();
}
},
getMetric() {
this.result = this.$get('/test/plan/get/metric/' + this.planId, response => {
this.metric = response.data
if (!this.metric.failureTestCases) {
this.metric.failureTestCases = [];
}
if (!this.metric.executeResult) {
this.metric.executeResult = [];
}
if (!this.metric.moduleExecuteResult) {
this.metric.moduleExecuteResult = [];
}
/*缺陷列表*/
if (!this.metric.issues) {
this.metric.issues = [];
}
if (this.report.startTime) {
this.metric.startTime = new Date(this.report.startTime);
}
if (this.report.endTime) {
this.metric.endTime = new Date(this.report.endTime);
}
});
},
}
}
</script>
<style scoped>
@ -204,7 +245,7 @@
line-height: 45px;
padding: 0 10px;
border: 1px solid #EBEEF5;
box-shadow: 0 0 2px 0 rgba(31,31,31,0.15), 0 1px 2px 0 rgba(31,31,31,0.15);
box-shadow: 0 0 2px 0 rgba(31, 31, 31, 0.15), 0 1px 2px 0 rgba(31, 31, 31, 0.15);
}
.container {

@ -1 +1 @@
Subproject commit 7e4d80cc2b870a8cac6dbb9fe6711ab6041faf6d
Subproject commit 06fc0a321a9886419be5c607ddaa6b40efb5179b

View File

@ -589,6 +589,16 @@ export default {
relate_test_not_find: 'The associated test does not exist, please check the test case',
batch_handle: 'Batch processing (select {0} item)',
batch_update: 'Update the attributes of {0} cases',
select_catalog: 'Please select use case catalog',
updated_attr_value: 'The updated attribute value',
batch_operate: 'Batch operation',
please_select_attr: 'Please select attributes',
please_select_attr_value: 'Please select the value corresponding to the attribute',
batch_edit_plan: 'Batch change test plan',
batch_edit_case: 'Batch editing test cases',
batch_move_case: 'Batch move',
batch_delete_case: 'Batch delete',
batch_unlink: 'Batch Unlink',
import: {
import: "Import test case",
case_import: "Import test case",
@ -698,6 +708,21 @@ export default {
report_template: "Report template",
test_detail: "Test detail",
failure_case: "Failure case",
export_report: "Export Report"
},
issue: {
issue: "Issue",
platform_tip: "Integrated defect management platform in the system setting-organization-service integration can automatically submit defects to the designated defect management platform",
input_title: "Please enter title",
id: "Issue ID",
title: "Issue Title",
description: "Issue Description",
status: "Issue Status",
platform: "Platform",
operate: "Operate",
close: "Close",
title_description_required: "Title and description are required",
close_success: "Closed successfully",
}
},
test_resource_pool: {

View File

@ -592,6 +592,16 @@ export default {
relate_test_not_find: '关联的测试不存在,请检查用例',
batch_handle: '批量处理 (选中{0}项)',
batch_update: '更新{0}个用例的属性',
select_catalog: '请选择用例目录',
updated_attr_value: '更新后属性值为',
batch_operate: '批量操作',
please_select_attr: '请选择属性',
please_select_attr_value: '请选择属性对应的值',
batch_edit_plan: '批量更改测试计划',
batch_edit_case: '批量编辑用例',
batch_move_case: '批量移动用例',
batch_delete_case: '批量删除用例',
batch_unlink: '批量取消用例关联',
import: {
import: "导入用例",
case_import: "导入测试用例",
@ -701,6 +711,21 @@ export default {
report_template: "测试报告模版",
test_detail: "测试详情",
failure_case: "失败用例",
export_report: "导出报告"
},
issue: {
issue: "缺陷",
platform_tip: "在系统设置-组织-服务集成中集成缺陷管理平台可以自动提交缺陷到指定缺陷管理平台",
input_title: "请输入标题",
id: "缺陷ID",
title: "缺陷标题",
description: "缺陷描述",
status: "缺陷状态",
platform: "平台",
operate: "操作",
close: "关闭缺陷",
title_description_required: "标题和描述必填",
close_success: "关闭成功",
}
},
test_resource_pool: {

View File

@ -589,6 +589,16 @@ export default {
relate_test_not_find: '關聯的測試不存在,請檢查用例',
batch_handle: '批量處理 (選中{0}項)',
batch_update: '更新{0}個用例的屬性',
select_catalog: '請選擇用例目錄',
updated_attr_value: '更新後屬性值為',
batch_operate: '批量操作',
please_select_attr: '請選擇屬性',
please_select_attr_value: '請選擇屬性對應的值',
batch_edit_plan: '批量更改測試計劃',
batch_edit_case: '批量編輯用例',
batch_move_case: '批量移動用例',
batch_delete_case: '批量刪除用例',
batch_unlink: '批量取消用例關聯',
import: {
import: "導入用例",
case_import: "導入測試用例",
@ -698,6 +708,21 @@ export default {
report_template: "測試報告模版",
test_detail: "測試詳情",
failure_case: "失敗用例",
export_report: "匯出報告"
},
issue: {
issue: "缺陷",
platform_tip: "在系統設置-組織-服務集成中集成缺陷管理平台可以自動提交缺陷到指定缺陷管理平台",
input_title: "請輸入標題",
id: "缺陷ID",
title: "缺陷標題",
description: "缺陷描述",
status: "缺陷狀態",
platform: "平台",
operate: "操作",
close: "關閉缺陷",
title_description_required: "標題和描述必填",
close_success: "關閉成功",
}
},
test_resource_pool: {