feat(LDAP): 测试报告导出
This commit is contained in:
parent
1cff9c1d02
commit
509e5c9fbb
|
@ -38,6 +38,7 @@
|
||||||
"babel-eslint": "^10.0.3",
|
"babel-eslint": "^10.0.3",
|
||||||
"eslint": "^5.16.0",
|
"eslint": "^5.16.0",
|
||||||
"eslint-plugin-vue": "^5.0.0",
|
"eslint-plugin-vue": "^5.0.0",
|
||||||
|
"file-writer": "^1.0.2",
|
||||||
"vue-template-compiler": "^2.6.10",
|
"vue-template-compiler": "^2.6.10",
|
||||||
"vue2-ace-editor": "0.0.15"
|
"vue2-ace-editor": "0.0.15"
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,9 +4,17 @@
|
||||||
<el-card>
|
<el-card>
|
||||||
<section class="report-container" v-if="this.report.testId">
|
<section class="report-container" v-if="this.report.testId">
|
||||||
<header class="report-header">
|
<header class="report-header">
|
||||||
<span>{{ report.projectName }} / </span>
|
<el-row>
|
||||||
<router-link :to="path">{{ report.testName }}</router-link>
|
<el-col>
|
||||||
<span class="time">{{ report.createTime | timestampFormatDate }}</span>
|
<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>
|
</header>
|
||||||
<main v-if="this.isNotRunning">
|
<main v-if="this.isNotRunning">
|
||||||
<ms-metric-chart :content="content" :totalTime="totalTime"/>
|
<ms-metric-chart :content="content" :totalTime="totalTime"/>
|
||||||
|
@ -43,6 +51,8 @@
|
||||||
import MsMetricChart from "./components/MetricChart";
|
import MsMetricChart from "./components/MetricChart";
|
||||||
import MsScenarioResults from "./components/ScenarioResults";
|
import MsScenarioResults from "./components/ScenarioResults";
|
||||||
import {Scenario} from "../test/model/ScenarioModel";
|
import {Scenario} from "../test/model/ScenarioModel";
|
||||||
|
import writer from "file-writer";
|
||||||
|
import ResumeCss from "../../../../common/css/main.css";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiReportView",
|
name: "MsApiReportView",
|
||||||
|
@ -62,6 +72,29 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
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">
|
||||||
|
${template}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>`
|
||||||
|
return html
|
||||||
|
},
|
||||||
init() {
|
init() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.report = {};
|
this.report = {};
|
||||||
|
|
|
@ -43,7 +43,8 @@
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['40%', '70%'],
|
radius: ['40%', '70%'],
|
||||||
// roseType: 'angle',
|
// roseType: 'angle',
|
||||||
data: this.data
|
data: this.data,
|
||||||
|
animation: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,9 +22,10 @@
|
||||||
@click="rerun(testId)">
|
@click="rerun(testId)">
|
||||||
{{ $t('report.test_execute_again') }}
|
{{ $t('report.test_execute_again') }}
|
||||||
</el-button>
|
</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')}}
|
{{$t('report.export')}}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<!--
|
||||||
<el-button :disabled="isReadOnly" type="warning" plain size="mini">
|
<el-button :disabled="isReadOnly" type="warning" plain size="mini">
|
||||||
{{$t('report.compare')}}
|
{{$t('report.compare')}}
|
||||||
</el-button>-->
|
</el-button>-->
|
||||||
|
@ -44,24 +45,26 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-divider/>
|
<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-card>
|
||||||
<el-dialog :title="$t('report.test_stop_now_confirm')" :visible.sync="dialogFormVisible" width="30%">
|
<el-dialog :title="$t('report.test_stop_now_confirm')" :visible.sync="dialogFormVisible" width="30%">
|
||||||
|
@ -79,250 +82,275 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MsReportErrorLog from './components/ErrorLog';
|
import MsReportErrorLog from './components/ErrorLog';
|
||||||
import MsReportLogDetails from './components/LogDetails';
|
import MsReportLogDetails from './components/LogDetails';
|
||||||
import MsReportRequestStatistics from './components/RequestStatistics';
|
import MsReportRequestStatistics from './components/RequestStatistics';
|
||||||
import MsReportTestOverview from './components/TestOverview';
|
import MsReportTestOverview from './components/TestOverview';
|
||||||
import MsPerformancePressureConfig from "./components/PerformancePressureConfig";
|
import MsPerformancePressureConfig from "./components/PerformancePressureConfig";
|
||||||
import MsContainer from "../../common/components/MsContainer";
|
import MsContainer from "../../common/components/MsContainer";
|
||||||
import MsMainContainer from "../../common/components/MsMainContainer";
|
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 {
|
export default {
|
||||||
name: "PerformanceReportView",
|
name: "PerformanceReportView",
|
||||||
components: {
|
components: {
|
||||||
MsReportErrorLog,
|
MsReportErrorLog,
|
||||||
MsReportLogDetails,
|
MsReportLogDetails,
|
||||||
MsReportRequestStatistics,
|
MsReportRequestStatistics,
|
||||||
MsReportTestOverview,
|
MsReportTestOverview,
|
||||||
MsContainer,
|
MsContainer,
|
||||||
MsMainContainer,
|
MsMainContainer,
|
||||||
MsPerformancePressureConfig
|
MsPerformancePressureConfig
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
result: {},
|
result: {},
|
||||||
active: '1',
|
active: '1',
|
||||||
reportId: '',
|
reportId: '',
|
||||||
status: '',
|
status: '',
|
||||||
reportName: '',
|
reportName: '',
|
||||||
testId: '',
|
testId: '',
|
||||||
testName: '',
|
testName: '',
|
||||||
projectId: '',
|
projectId: '',
|
||||||
projectName: '',
|
projectName: '',
|
||||||
startTime: '0',
|
startTime: '0',
|
||||||
endTime: '0',
|
endTime: '0',
|
||||||
minutes: '0',
|
minutes: '0',
|
||||||
seconds: '0',
|
seconds: '0',
|
||||||
title: 'Logging',
|
title: 'Logging',
|
||||||
report: {},
|
report: {},
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
websocket: null,
|
websocket: null,
|
||||||
dialogFormVisible: false,
|
dialogFormVisible: false,
|
||||||
testPlan: {testResourcePoolId: null}
|
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;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
initReportTimeInfo() {
|
methods: {
|
||||||
if (this.reportId) {
|
exports(name) {
|
||||||
this.result = this.$get("/performance/report/content/report_time/" + this.reportId)
|
let html = this.getHtml();
|
||||||
.then(res => {
|
writer(`${name}.html`, html, 'utf-8');
|
||||||
let data = res.data.data;
|
},
|
||||||
|
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></style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div style="margin:0 auto;width:1200px">
|
||||||
|
${template}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>`
|
||||||
|
return html
|
||||||
|
},
|
||||||
|
initBreadcrumb() {
|
||||||
|
if (this.reportId) {
|
||||||
|
this.result = this.$get("/performance/report/test/pro/info/" + this.reportId, res => {
|
||||||
|
let data = res.data;
|
||||||
if (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.reportName = data.name;
|
||||||
this.testName = data.testName;
|
|
||||||
this.testId = data.testId;
|
this.testId = data.testId;
|
||||||
|
this.testName = data.testName;
|
||||||
|
this.projectId = data.projectId;
|
||||||
this.projectName = data.projectName;
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
.ms-report-view-btns {
|
.ms-report-view-btns {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ms-report-time-desc {
|
.ms-report-time-desc {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
display: block;
|
display: block;
|
||||||
color: #5C7878;
|
color: #5C7878;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<chart/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ExportTestCaseReport"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -66,6 +66,8 @@
|
||||||
this.charData.push(data);
|
this.charData.push(data);
|
||||||
});
|
});
|
||||||
this.reload();
|
this.reload();
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
reload() {
|
reload() {
|
||||||
this.isShow = false;
|
this.isShow = false;
|
||||||
|
|
|
@ -13,18 +13,25 @@
|
||||||
<el-row type="flex" class="head-bar">
|
<el-row type="flex" class="head-bar">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<div class="name-edit">
|
<div class="name-edit">
|
||||||
<el-button plain size="mini" icon="el-icon-back" @click="handleClose">{{$t('test_track.return')}}</el-button>
|
<el-button plain size="mini" icon="el-icon-back" @click="handleClose">{{$t('test_track.return')}}
|
||||||
|
</el-button>
|
||||||
<span class="title">{{report.name}}</span>
|
<span class="title">{{report.name}}</span>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12" class="head-right">
|
<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="handleSave">
|
||||||
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleEdit">{{$t('test_track.plan_view.edit_component')}}</el-button>
|
{{$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-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container" ref="resume">
|
||||||
<el-main>
|
<el-main>
|
||||||
<div class="preview" v-for="item in previews" :key="item.id">
|
<div class="preview" v-for="item in previews" :key="item.id">
|
||||||
<template-component :isReportView="true" :metric="metric" :preview="item"/>
|
<template-component :isReportView="true" :metric="metric" :preview="item"/>
|
||||||
|
@ -41,154 +48,184 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {checkoutTestManagerOrTestUser, jsonToMap, mapToJson} from "../../../../../../../common/js/utils";
|
import {checkoutTestManagerOrTestUser, jsonToMap, mapToJson} from "../../../../../../../common/js/utils";
|
||||||
import BaseInfoComponent from "./TemplateComponent/BaseInfoComponent";
|
import BaseInfoComponent from "./TemplateComponent/BaseInfoComponent";
|
||||||
import TestResultChartComponent from "./TemplateComponent/TestResultChartComponent";
|
import TestResultChartComponent from "./TemplateComponent/TestResultChartComponent";
|
||||||
import TestResultComponent from "./TemplateComponent/TestResultComponent";
|
import TestResultComponent from "./TemplateComponent/TestResultComponent";
|
||||||
import RichTextComponent from "./TemplateComponent/RichTextComponent";
|
import RichTextComponent from "./TemplateComponent/RichTextComponent";
|
||||||
import TestCaseReportTemplateEdit from "./TestCaseReportTemplateEdit";
|
import TestCaseReportTemplateEdit from "./TestCaseReportTemplateEdit";
|
||||||
import TemplateComponent from "./TemplateComponent/TemplateComponent";
|
import TemplateComponent from "./TemplateComponent/TemplateComponent";
|
||||||
|
import writer from 'file-writer'
|
||||||
|
import ResumeCss from "../../../../../../../common/css/main.css";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "TestCaseReportView",
|
name: "TestCaseReportView",
|
||||||
components: {
|
components: {
|
||||||
TemplateComponent,
|
TemplateComponent, ResumeCss,
|
||||||
TestCaseReportTemplateEdit,
|
TestCaseReportTemplateEdit,
|
||||||
RichTextComponent, TestResultComponent, TestResultChartComponent, BaseInfoComponent},
|
RichTextComponent, TestResultComponent, TestResultChartComponent, BaseInfoComponent
|
||||||
data() {
|
},
|
||||||
return {
|
data() {
|
||||||
result: {},
|
return {
|
||||||
showDialog: false,
|
result: {},
|
||||||
previews: [],
|
imgUrl: "",
|
||||||
report: {},
|
showDialog: false,
|
||||||
reportId: '',
|
previews: [],
|
||||||
metric: {},
|
report: {},
|
||||||
planId: '',
|
reportId: '',
|
||||||
componentMap: new Map(
|
metric: {},
|
||||||
[
|
planId: '',
|
||||||
[1, { name: this.$t('test_track.plan_view.base_info'), id: 1 , type: 'system'}],
|
componentMap: new Map(
|
||||||
[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'}],
|
[1, {name: this.$t('test_track.plan_view.base_info'), id: 1, type: 'system'}],
|
||||||
[4, { name: this.$t('test_track.plan_view.failure_case'), id: 4 ,type: 'system'}],
|
[2, {name: this.$t('test_track.plan_view.test_result'), id: 2, type: 'system'}],
|
||||||
[5, { name: this.$t('test_track.plan_view.defect_list'), id: 5 ,type: 'system'}],
|
[3, {name: this.$t('test_track.plan_view.result_distribution'), id: 3, type: 'system'}],
|
||||||
[6, { name: this.$t('test_track.plan_view.custom_component'), id: 6 ,type: 'custom'}]
|
[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
|
]
|
||||||
|
),
|
||||||
|
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() {
|
goBack() {
|
||||||
this.isTestManagerOrTestUser = checkoutTestManagerOrTestUser();
|
this.handleClose();
|
||||||
},
|
},
|
||||||
methods: {
|
open(planId, id) {
|
||||||
listenGoBack() {
|
this.planId = planId;
|
||||||
//监听浏览器返回操作,关闭该对话框
|
if (id) {
|
||||||
if (window.history && window.history.pushState) {
|
this.reportId = id;
|
||||||
history.pushState(null, null, document.URL);
|
}
|
||||||
window.addEventListener('popstate', this.goBack, false);
|
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);
|
||||||
}
|
}
|
||||||
},
|
this.getMetric();
|
||||||
goBack() {
|
this.initPreviews();
|
||||||
this.handleClose();
|
});
|
||||||
},
|
},
|
||||||
open(planId, id) {
|
initPreviews() {
|
||||||
this.planId = planId;
|
this.previews = [];
|
||||||
if (id) {
|
this.report.content.components.forEach(item => {
|
||||||
this.reportId = id;
|
let preview = this.componentMap.get(item);
|
||||||
}
|
if (preview && preview.type != 'custom') {
|
||||||
this.getReport();
|
this.previews.push(preview);
|
||||||
this.showDialog = true;
|
} else {
|
||||||
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) {
|
if (this.report.content.customComponent) {
|
||||||
this.report.content.customComponent = jsonToMap(this.report.content.customComponent);
|
let customComponent = this.report.content.customComponent.get(item.toString());
|
||||||
}
|
if (customComponent) {
|
||||||
this.getMetric();
|
this.previews.push({id: item, title: customComponent.title, content: customComponent.content});
|
||||||
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});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
},
|
|
||||||
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) {
|
handleClose() {
|
||||||
param.startTime = this.metric.startTime.getTime();
|
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();
|
param.name = this.report.name;
|
||||||
}
|
if (content.customComponent) {
|
||||||
},
|
content.customComponent = mapToJson(content.customComponent);
|
||||||
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.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 = [];
|
||||||
|
}
|
||||||
|
/*缺陷列表*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -204,7 +241,7 @@
|
||||||
line-height: 45px;
|
line-height: 45px;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
border: 1px solid #EBEEF5;
|
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 {
|
.container {
|
||||||
|
|
|
@ -669,6 +669,7 @@ export default {
|
||||||
report_template: "Report template",
|
report_template: "Report template",
|
||||||
test_detail: "Test detail",
|
test_detail: "Test detail",
|
||||||
failure_case: "Failure case",
|
failure_case: "Failure case",
|
||||||
|
export_report: "Export Report"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
test_resource_pool: {
|
test_resource_pool: {
|
||||||
|
|
|
@ -674,6 +674,7 @@ export default {
|
||||||
report_template: "测试报告模版",
|
report_template: "测试报告模版",
|
||||||
test_detail: "测试详情",
|
test_detail: "测试详情",
|
||||||
failure_case: "失败用例",
|
failure_case: "失败用例",
|
||||||
|
export_report: "导出报告"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
test_resource_pool: {
|
test_resource_pool: {
|
||||||
|
|
|
@ -669,6 +669,7 @@ export default {
|
||||||
report_template: "測試報告模版",
|
report_template: "測試報告模版",
|
||||||
test_detail: "測試詳情",
|
test_detail: "測試詳情",
|
||||||
failure_case: "失敗用例",
|
failure_case: "失敗用例",
|
||||||
|
export_report: "匯出報告"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
test_resource_pool: {
|
test_resource_pool: {
|
||||||
|
|
Loading…
Reference in New Issue