Merge remote-tracking branch 'origin/master'

This commit is contained in:
wenyann 2020-08-20 16:51:06 +08:00
commit a722d6419e
51 changed files with 2454 additions and 1972 deletions

2
backend/.gitignore vendored
View File

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

View File

@ -15,4 +15,5 @@ public class Scenario {
private List<KeyValue> headers;
private List<Request> requests;
private DubboConfig dubboConfig;
private Boolean enable;
}

View File

@ -49,4 +49,6 @@ public class DubboRequest implements Request {
private BeanShellPreProcessor beanShellPreProcessor;
@JSONField(ordinal = 13)
private BeanShellPostProcessor beanShellPostProcessor;
@JSONField(ordinal = 14)
private Boolean enable;
}

View File

@ -41,4 +41,10 @@ public class HttpRequest implements Request {
private BeanShellPreProcessor beanShellPreProcessor;
@JSONField(ordinal = 12)
private BeanShellPostProcessor beanShellPostProcessor;
@JSONField(ordinal = 13)
private Boolean enable;
@JSONField(ordinal = 14)
private Integer connectTimeout;
@JSONField(ordinal = 15)
private Integer responseTimeout;
}

View File

@ -149,9 +149,12 @@ public class JmeterDocumentParser {
String path = ele.getTextContent();
Map<String, String> parser = parserUrl(path);
String url = parser.get("URL");
String params = parser.keySet().stream().filter(k -> !"URL".equals(k)).reduce("", (u, k) -> {
String params = parser.keySet().stream().filter(k -> !"URL".equals(k)).reduce("?", (u, k) -> {
String v = parser.get(k);
u += "&" + k + "=" + ScriptEngineUtils.calculate(v);
if (!StringUtils.equals("?", u)) {
u += "&";
}
u += k + "=" + ScriptEngineUtils.calculate(v);
return u;
});
ele.setTextContent(url + params);

View File

@ -18,15 +18,13 @@ import io.metersphere.commons.constants.FileType;
import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.constants.ScheduleType;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.commons.utils.*;
import io.metersphere.controller.request.QueryScheduleRequest;
import io.metersphere.dto.ScheduleDao;
import io.metersphere.i18n.Translator;
import io.metersphere.job.sechedule.ApiTestJob;
import io.metersphere.service.FileService;
import io.metersphere.service.QuotaService;
import io.metersphere.service.ScheduleService;
import io.metersphere.track.service.TestCaseService;
import org.apache.dubbo.common.URL;
@ -37,6 +35,7 @@ import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@ -92,6 +91,7 @@ public class APITestService {
}
public void copy(SaveAPITestRequest request) {
checkQuota();
request.setName(request.getName() + " Copy");
try {
checkNameExist(request);
@ -355,4 +355,11 @@ public class APITestService {
jMeterService.run(request.getId(), reportId, is);
return reportId;
}
private void checkQuota() {
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
if (quotaService != null) {
quotaService.checkAPIQuota();
}
}
}

View File

@ -115,7 +115,7 @@
</select>
<select id="list" resultType="io.metersphere.track.dto.TestPlanCaseDTO">
select test_plan_test_case.*, test_case.*,test_case_node.name as model
select test_case.remark, 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 left join test_case_node on
test_case_node.id=test_case.node_id

View File

@ -16,11 +16,19 @@ public class CommonBeanFactory implements ApplicationContextAware {
}
public static Object getBean(String beanName) {
return context != null && !StringUtils.isBlank(beanName) ? context.getBean(beanName) : null;
try {
return context != null && !StringUtils.isBlank(beanName) ? context.getBean(beanName) : null;
} catch (BeansException e) {
return null;
}
}
public static <T> T getBean(Class<T> className) {
return context != null && className != null ? context.getBean(className) : null;
try {
return context != null && className != null ? context.getBean(className) : null;
} catch (BeansException e) {
return null;
}
}
}

View File

@ -0,0 +1,6 @@
package io.metersphere.service;
public interface QuotaService {
void checkAPIQuota();
}

View File

@ -235,7 +235,7 @@ public class IssuesService {
" \"key\":\"" + jiraKey + "\"\n" +
" },\n" +
" \"summary\":\"" + issuesRequest.getTitle() + "\",\n" +
" \"description\":\"" + issuesRequest.getContent() + "\",\n" +
" \"description\": " + JSON.toJSONString(issuesRequest.getContent()) + ",\n" +
" \"issuetype\":{\n" +
" \"id\":\"10009\",\n" +
" \"name\":\"Defect\"\n" +

View File

@ -0,0 +1,36 @@
package io.metersphere.track.service;
import io.metersphere.base.domain.TestCaseIssues;
import io.metersphere.base.domain.TestCaseIssuesExample;
import io.metersphere.base.mapper.IssuesMapper;
import io.metersphere.base.mapper.TestCaseIssuesMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
public class TestCaseIssueService {
@Resource
private TestCaseIssuesMapper testCaseIssuesMapper;
@Resource
private IssuesMapper issuesMapper;
public void delTestCaseIssues(String testCaseId) {
TestCaseIssuesExample example = new TestCaseIssuesExample();
example.createCriteria().andTestCaseIdEqualTo(testCaseId);
List<TestCaseIssues> testCaseIssues = testCaseIssuesMapper.selectByExample(example);
if (!CollectionUtils.isEmpty(testCaseIssues)) {
List<String> list = testCaseIssues.stream().map(TestCaseIssues::getIssuesId).collect(Collectors.toList());
list.forEach(id -> {
issuesMapper.deleteByPrimaryKey(id);
});
}
testCaseIssuesMapper.deleteByExample(example);
}
}

View File

@ -73,6 +73,9 @@ public class TestCaseService {
@Resource
UserRoleMapper userRoleMapper;
@Resource
TestCaseIssueService testCaseIssueService;
public void addTestCase(TestCaseWithBLOBs testCase) {
testCase.setName(testCase.getName());
checkTestCaseExist(testCase);
@ -149,6 +152,7 @@ public class TestCaseService {
TestPlanTestCaseExample example = new TestPlanTestCaseExample();
example.createCriteria().andCaseIdEqualTo(testCaseId);
testPlanTestCaseMapper.deleteByExample(example);
testCaseIssueService.delTestCaseIssues(testCaseId);
return testCaseMapper.deleteByPrimaryKey(testCaseId);
}
@ -426,6 +430,7 @@ public class TestCaseService {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, request);
testCase.setUpdateTime(System.currentTimeMillis());
testCaseMapper.updateByExampleSelective(
testCase,
testCaseExample);

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

View File

@ -141,4 +141,6 @@ quota_workspace_excess_org_api=The total number of interface tests in the worksp
quota_workspace_excess_org_performance=The total number of performance tests for the workspace cannot exceed the organization's quota
quota_workspace_excess_org_max_threads=The maximum concurrent number of workspaces cannot exceed the quota of the organization
quota_workspace_excess_org_max_duration=The stress test duration of the workspace cannot exceed the organization's quota
quota_workspace_excess_org_resource_pool=The resource pool of the workspace cannot exceed the resource pool of the organization
quota_workspace_excess_org_resource_pool=The resource pool of the workspace cannot exceed the resource pool of the organization
quota_api_excess_workspace=The number of interface tests exceeds the workspace quota
quota_api_excess_organization=The number of interface tests exceeds the organization quota

View File

@ -142,6 +142,8 @@ quota_workspace_excess_org_performance=工作空间的性能测试数量总和
quota_workspace_excess_org_max_threads=工作空间的最大并发数不能超过组织的配额
quota_workspace_excess_org_max_duration=工作空间的压测时长不能超过组织的配额
quota_workspace_excess_org_resource_pool=工作空间的资源池不能超过组织的资源池范围
quota_api_excess_workspace=接口测试数量超过工作空间限额
quota_api_excess_organization=接口测试数量超过组织限额

View File

@ -142,3 +142,5 @@ quota_workspace_excess_org_performance=工作空間的性能測試數量總和
quota_workspace_excess_org_max_threads=工作空間的最大並發數不能超過組織的配額
quota_workspace_excess_org_max_duration=工作空間的壓測時長不能超過組織的配額
quota_workspace_excess_org_resource_pool=工作空間的資源池不能超過組織的資源池範圍
quota_api_excess_workspace=接口測試數量超過工作空間限額
quota_api_excess_organization=接口測試數量超過組織限額

View File

@ -8,8 +8,8 @@
</el-menu-item>
<el-submenu v-permission="['test_manager','test_user','test_viewer']" index="3">
<template v-slot:title>{{$t('commons.project')}}</template>
<ms-recent-list :options="projectRecent"/>
<template v-slot:title>{{ $t('commons.project') }}</template>
<ms-recent-list ref="projectRecent" :options="projectRecent"/>
<el-divider class="menu-divider"/>
<ms-show-all :index="'/api/project/all'"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/api/project/create'"
@ -17,8 +17,8 @@
</el-submenu>
<el-submenu v-permission="['test_manager','test_user','test_viewer']" index="4">
<template v-slot:title>{{$t('commons.test')}}</template>
<ms-recent-list :options="testRecent"/>
<template v-slot:title>{{ $t('commons.test') }}</template>
<ms-recent-list ref="testRecent" :options="testRecent"/>
<el-divider class="menu-divider"/>
<ms-show-all :index="'/api/test/list/all'"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/api/test/create'"
@ -26,8 +26,8 @@
</el-submenu>
<el-submenu v-permission="['test_manager','test_user','test_viewer']" index="5">
<template v-slot:title>{{$t('commons.report')}}</template>
<ms-recent-list :options="reportRecent"/>
<template v-slot:title>{{ $t('commons.report') }}</template>
<ms-recent-list ref="reportRecent" :options="reportRecent"/>
<el-divider class="menu-divider"/>
<ms-show-all :index="'/api/report/list/all'"/>
</el-submenu>
@ -46,57 +46,65 @@
<script>
import MsRecentList from "../../common/head/RecentList";
import MsShowAll from "../../common/head/ShowAll";
import MsCreateButton from "../../common/head/CreateButton";
import MsCreateTest from "../../common/head/CreateTest";
import MsRecentList from "../../common/head/RecentList";
import MsShowAll from "../../common/head/ShowAll";
import MsCreateButton from "../../common/head/CreateButton";
import MsCreateTest from "../../common/head/CreateTest";
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
export default {
name: "MsApiHeaderMenus",
components: {MsCreateTest, MsCreateButton, MsShowAll, MsRecentList},
data() {
return {
projectRecent: {
title: this.$t('project.recent'),
url: "/project/recent/5",
index: function (item) {
return '/api/test/list/' + item.id;
},
router: function (item) {
return {name: 'ApiTestList', params: {projectId: item.id, projectName: item.name}}
}
export default {
name: "MsApiHeaderMenus",
components: {MsCreateTest, MsCreateButton, MsShowAll, MsRecentList},
data() {
return {
projectRecent: {
title: this.$t('project.recent'),
url: "/project/recent/5",
index: function (item) {
return '/api/test/list/' + item.id;
},
testRecent: {
title: this.$t('load_test.recent'),
url: "/api/recent/5",
index: function (item) {
return '/api/test/edit/' + item.id;
},
router: function (item) {
return {path: '/api/test/edit', query: {id: item.id}}
}
router: function (item) {
return {name: 'ApiTestList', params: {projectId: item.id, projectName: item.name}}
}
},
testRecent: {
title: this.$t('load_test.recent'),
url: "/api/recent/5",
index: function (item) {
return '/api/test/edit/' + item.id;
},
reportRecent: {
title: this.$t('report.recent'),
showTime: true,
url: "/api/report/recent/5",
index: function (item) {
return '/api/report/view/' + item.id;
}
router: function (item) {
return {path: '/api/test/edit', query: {id: item.id}}
}
},
reportRecent: {
title: this.$t('report.recent'),
showTime: true,
url: "/api/report/recent/5",
index: function (item) {
return '/api/report/view/' + item.id;
}
}
}
},
mounted() {
ApiEvent.$on(LIST_CHANGE, () => {
this.$refs.projectRecent.recent();
this.$refs.testRecent.recent();
this.$refs.reportRecent.recent();
});
}
}
</script>
<style scoped>
#menu-bar {
border-bottom: 1px solid #E6E6E6;
background-color: #FFF;
}
#menu-bar {
border-bottom: 1px solid #E6E6E6;
background-color: #FFF;
}
.menu-divider {
margin: 0;
}
.menu-divider {
margin: 0;
}
</style>

View File

@ -49,112 +49,115 @@
</template>
<script>
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsApiReportStatus from "./ApiReportStatus";
import {_filter, _sort} from "@/common/js/utils";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import ReportTriggerModeItem from "../../common/tableItem/ReportTriggerModeItem";
import {REPORT_CONFIGS} from "../../common/components/search/search-components";
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsApiReportStatus from "./ApiReportStatus";
import {_filter, _sort} from "@/common/js/utils";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import ReportTriggerModeItem from "../../common/tableItem/ReportTriggerModeItem";
import {REPORT_CONFIGS} from "../../common/components/search/search-components";
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
export default {
components: {
ReportTriggerModeItem,
MsTableOperatorButton,
MsApiReportStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination
},
data() {
return {
result: {},
condition: {
components: REPORT_CONFIGS
},
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
total: 0,
loading: false,
statusFilters: [
{text: 'Saved', value: 'Saved'},
{text: 'Starting', value: 'Starting'},
{text: 'Running', value: 'Running'},
{text: 'Reporting', value: 'Reporting'},
{text: 'Completed', value: 'Completed'},
{text: 'Error', value: 'Error'},
{text: 'Success', value: 'Success'},
],
triggerFilters: [
{text: this.$t('commons.trigger_mode.manual'), value: 'MANUAL'},
{text: this.$t('commons.trigger_mode.schedule'), value: 'SCHEDULE'},
{text: this.$t('commons.trigger_mode.api'), value: 'API'}
],
}
},
watch: {
'$route': 'init',
},
methods: {
search() {
if (this.testId !== 'all') {
this.condition.testId = this.testId;
}
let url = "/api/report/list/" + this.currentPage + "/" + this.pageSize;
this.result = this.$post(url, this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
export default {
components: {
ReportTriggerModeItem,
MsTableOperatorButton,
MsApiReportStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination
},
data() {
return {
result: {},
condition: {
components: REPORT_CONFIGS
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleView(report) {
this.$router.push({
path: '/api/report/view/' + report.id,
})
},
handleDelete(report) {
this.$alert(this.$t('api_report.delete_confirm') + report.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this.result = this.$post("/api/report/delete", {id: report.id}, () => {
this.$success(this.$t('commons.delete_success'));
this.search();
});
}
}
});
},
init() {
this.testId = this.$route.params.testId;
this.search();
},
sort(column) {
_sort(column, this.condition);
this.init();
},
filter(filters) {
_filter(filters, this.condition);
this.init();
},
},
created() {
this.init();
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
total: 0,
loading: false,
statusFilters: [
{text: 'Saved', value: 'Saved'},
{text: 'Starting', value: 'Starting'},
{text: 'Running', value: 'Running'},
{text: 'Reporting', value: 'Reporting'},
{text: 'Completed', value: 'Completed'},
{text: 'Error', value: 'Error'},
{text: 'Success', value: 'Success'},
],
triggerFilters: [
{text: this.$t('commons.trigger_mode.manual'), value: 'MANUAL'},
{text: this.$t('commons.trigger_mode.schedule'), value: 'SCHEDULE'},
{text: this.$t('commons.trigger_mode.api'), value: 'API'}
],
}
},
watch: {
'$route': 'init',
},
methods: {
search() {
if (this.testId !== 'all') {
this.condition.testId = this.testId;
}
let url = "/api/report/list/" + this.currentPage + "/" + this.pageSize;
this.result = this.$post(url, this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleView(report) {
this.$router.push({
path: '/api/report/view/' + report.id,
})
},
handleDelete(report) {
this.$alert(this.$t('api_report.delete_confirm') + report.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this.result = this.$post("/api/report/delete", {id: report.id}, () => {
this.$success(this.$t('commons.delete_success'));
this.search();
// 广 head
ApiEvent.$emit(LIST_CHANGE);
});
}
}
});
},
init() {
this.testId = this.$route.params.testId;
this.search();
},
sort(column) {
_sort(column, this.condition);
this.init();
},
filter(filters) {
_filter(filters, this.condition);
this.init();
},
},
created() {
this.init();
}
}
</script>
<style scoped>
.table-content {
width: 100%;
}
.table-content {
width: 100%;
}
</style>

View File

@ -61,16 +61,16 @@
</el-tab-pane>
<el-tab-pane :label="$t('api_report.request_result')" name="result">
<ms-request-metric :request="request"/>
<ms-request-text :request="request"/>
<ms-request-text :request="request"/>
<br>
<ms-response-text :response="request.responseResult"/>
</el-tab-pane>
</el-tabs>
<div v-else>
<ms-request-metric :request="request"/>
<ms-request-text :request="request"/>
<ms-request-text v-if="isCodeEditAlive" :request="request"/>
<br>
<ms-response-text :response="request.responseResult"/>
<ms-response-text v-if="isCodeEditAlive" :response="request.responseResult"/>
</div>
</div>
</el-collapse-transition>
@ -96,12 +96,17 @@
return {
isActive: true,
activeName: "sub",
isCodeEditAlive: true
}
},
methods: {
active() {
this.isActive = !this.isActive;
},
reload() {
this.isCodeEditAlive = false;
this.$nextTick(() => (this.isCodeEditAlive = true));
}
},
@ -111,7 +116,7 @@
},
hasSub() {
return this.request.subRequestResults.length > 0;
}
},
}
}
</script>

View File

@ -56,173 +56,176 @@
</template>
<script>
import OneClickOperation from './OneClickOperation';
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsTableOperator from "../../common/components/MsTableOperator";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsApiTestStatus from "./ApiTestStatus";
import MsTableOperators from "../../common/components/MsTableOperators";
import {_filter, _sort} from "@/common/js/utils";
import {TEST_CONFIGS} from "../../common/components/search/search-components";
import OneClickOperation from './OneClickOperation';
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsTableOperator from "../../common/components/MsTableOperator";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsApiTestStatus from "./ApiTestStatus";
import MsTableOperators from "../../common/components/MsTableOperators";
import {_filter, _sort} from "@/common/js/utils";
import {TEST_CONFIGS} from "../../common/components/search/search-components";
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
export default {
components: {
OneClickOperation,
MsTableOperators,
MsApiTestStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, MsTableOperator
export default {
components: {
OneClickOperation,
MsTableOperators,
MsApiTestStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, MsTableOperator
},
data() {
return {
result: {},
condition: {
components: TEST_CONFIGS
},
projectId: null,
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
total: 0,
loading: false,
selectIds: new Set(),
selectNames: new Set(),
selectProjectNames: new Set(),
buttons: [
{
tip: this.$t('commons.edit'), icon: "el-icon-edit",
exec: this.handleEdit
}, {
tip: this.$t('commons.copy'), icon: "el-icon-copy-document", type: "success",
exec: this.handleCopy
}, {
tip: this.$t('commons.delete'), icon: "el-icon-delete", type: "danger",
exec: this.handleDelete
}
],
statusFilters: [
{text: 'Saved', value: 'Saved'},
{text: 'Starting', value: 'Starting'},
{text: 'Running', value: 'Running'},
{text: 'Reporting', value: 'Reporting'},
{text: 'Completed', value: 'Completed'},
{text: 'Error', value: 'Error'}
]
}
},
watch: {
'$route': 'init'
},
methods: {
create() {
this.$router.push('/api/test/create');
},
data() {
return {
result: {},
condition: {
components: TEST_CONFIGS
},
projectId: null,
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
total: 0,
loading: false,
selectIds: new Set(),
selectNames: new Set(),
selectProjectNames: new Set(),
buttons: [
{
tip: this.$t('commons.edit'), icon: "el-icon-edit",
exec: this.handleEdit
}, {
tip: this.$t('commons.copy'), icon: "el-icon-copy-document", type: "success",
exec: this.handleCopy
}, {
tip: this.$t('commons.delete'), icon: "el-icon-delete", type: "danger",
exec: this.handleDelete
}
],
statusFilters: [
{text: 'Saved', value: 'Saved'},
{text: 'Starting', value: 'Starting'},
{text: 'Running', value: 'Running'},
{text: 'Reporting', value: 'Reporting'},
{text: 'Completed', value: 'Completed'},
{text: 'Error', value: 'Error'}
]
handleSelectAll(selection) {
if (selection.length > 0) {
this.tableData.forEach(item => {
this.selectIds.add(item.id);
this.selectProjectNames.add(item.projectName)
});
} else {
this.selectIds.clear()
this.selectProjectNames.clear()
}
},
watch: {
'$route': 'init'
selectionChange(selection, row) {
if (this.selectIds.has(row.id)) {
this.selectIds.delete(row.id);
this.selectProjectNames.delete(row.projectName)
} else {
this.selectIds.add(row.id);
this.selectProjectNames.add(row.projectName)
}
},
methods: {
create() {
this.$router.push('/api/test/create');
},
handleSelectAll(selection) {
if (selection.length > 0) {
this.tableData.forEach(item => {
this.selectIds.add(item.id);
this.selectProjectNames.add(item.projectName)
});
} else {
this.selectIds.clear()
this.selectProjectNames.clear()
}
},
selectionChange(selection, row) {
if (this.selectIds.has(row.id)) {
this.selectIds.delete(row.id);
this.selectProjectNames.delete(row.projectName)
} else {
this.selectIds.add(row.id);
this.selectProjectNames.add(row.projectName)
}
},
runTest() {
if (this.selectIds.size < 1) {
this.$warning(this.$t('test_track.plan_view.select_manipulate'));
} else {
this.$refs.OneClickOperation.openOneClickOperation();
}
},
search() {
if (this.projectId !== 'all') {
this.condition.projectId = this.projectId;
}
let url = "/api/list/" + this.currentPage + "/" + this.pageSize;
this.result = this.$post(url, this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
this.tableData.forEach(item => {
this.selectNames.add(item.name)
})
});
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleEdit(test) {
this.$router.push({
path: '/api/test/edit?id=' + test.id,
runTest() {
if (this.selectIds.size < 1) {
this.$warning(this.$t('test_track.plan_view.select_manipulate'));
} else {
this.$refs.OneClickOperation.openOneClickOperation();
}
},
search() {
if (this.projectId !== 'all') {
this.condition.projectId = this.projectId;
}
let url = "/api/list/" + this.currentPage + "/" + this.pageSize;
this.result = this.$post(url, this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
this.tableData.forEach(item => {
this.selectNames.add(item.name)
})
},
handleView(test) {
this.$router.push({
path: '/api/test/view?id=' + test.id,
})
},
handleDelete(test) {
this.$alert(this.$t('load_test.delete_confirm') + test.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this.result = this.$post("/api/delete", {id: test.id}, () => {
this.$success(this.$t('commons.delete_success'));
this.search();
});
}
});
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleEdit(test) {
this.$router.push({
path: '/api/test/edit?id=' + test.id,
})
},
handleView(test) {
this.$router.push({
path: '/api/test/view?id=' + test.id,
})
},
handleDelete(test) {
this.$alert(this.$t('load_test.delete_confirm') + test.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this.result = this.$post("/api/delete", {id: test.id}, () => {
this.$success(this.$t('commons.delete_success'));
this.search();
// 广 head
ApiEvent.$emit(LIST_CHANGE);
});
}
});
},
handleCopy(test) {
this.result = this.$post("/api/copy", {projectId: test.projectId, id: test.id, name: test.name}, () => {
this.$success(this.$t('commons.copy_success'));
this.search();
});
},
init() {
this.projectId = this.$route.params.projectId;
if (this.projectId && this.projectId !== "all") {
this.$store.commit('setProjectId', this.projectId);
}
this.search();
},
sort(column) {
_sort(column, this.condition);
this.init();
},
filter(filters) {
_filter(filters, this.condition);
this.init();
},
});
},
created() {
handleCopy(test) {
this.result = this.$post("/api/copy", {projectId: test.projectId, id: test.id, name: test.name}, () => {
this.$success(this.$t('commons.copy_success'));
this.search();
});
},
init() {
this.projectId = this.$route.params.projectId;
if (this.projectId && this.projectId !== "all") {
this.$store.commit('setProjectId', this.projectId);
}
this.search();
},
sort(column) {
_sort(column, this.condition);
this.init();
}
},
filter(filters) {
_filter(filters, this.condition);
this.init();
},
},
created() {
this.init();
}
}
</script>
<style scoped>
.table-content {
width: 100%;
}
.table-content {
width: 100%;
}
.el-table {
cursor: pointer;
}
.el-table {
cursor: pointer;
}
</style>

View File

@ -0,0 +1,31 @@
<template>
<div>
<el-form :model="request" ref="request" label-width="100px">
<el-form-item :label="$t('api_test.request.connect_timeout')" prop="connectTimeout">
<el-input-number size="small" :disabled="isReadOnly" v-model="request.connectTimeout" :placeholder="$t('commons.millisecond')" :maxlength="1000*10000000" />
</el-form-item>
<el-form-item :label="$t('api_test.request.response_timeout')" prop="responseTimeout">
<el-input-number size="small" :disabled="isReadOnly" v-model="request.responseTimeout" :placeholder="$t('commons.millisecond')" :maxlength="1000*10000000"/>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "MsApiAdvancedConfig",
props: {
request: Object,
isReadOnly: {
type: Boolean,
default: false
}
}
}
</script>
<style scoped>
</style>

View File

@ -5,13 +5,13 @@
<ms-api-collapse v-model="activeName" @change="handleChange" accordion>
<draggable :list="scenarios" group="Scenario" class="scenario-draggable" ghost-class="scenario-ghost">
<ms-api-collapse-item v-for="(scenario, index) in scenarios" :key="index"
:title="scenario.name" :name="index">
:title="scenario.name" :name="index" :class="{'disable-scenario': !scenario.enable}">
<template slot="title">
<div class="scenario-name">
{{scenario.name}}
<span id="hint" v-if="!scenario.name">
{{$t('api_test.scenario.config')}}
</span>
{{$t('api_test.scenario.config')}}
</span>
</div>
<el-dropdown trigger="click" @command="handleCommand">
<span class="el-dropdown-link el-icon-more scenario-btn"/>
@ -22,6 +22,12 @@
<el-dropdown-item :disabled="isReadOnly" :command="{type:'delete', index:index}">
{{$t('api_test.scenario.delete')}}
</el-dropdown-item>
<el-dropdown-item v-if="scenario.enable" :disabled="isReadOnly" :command="{type:'disable', index:index}">
{{$t('api_test.scenario.disable')}}
</el-dropdown-item>
<el-dropdown-item v-if="!scenario.enable" :disabled="isReadOnly" :command="{type:'enable', index:index}">
{{$t('api_test.scenario.enable')}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
@ -108,6 +114,12 @@ export default {
this.select(this.scenarios[0]);
}
},
disableScenario: function (index) {
this.scenarios[index].enable = false;
},
enableScenario: function (index) {
this.scenarios[index].enable = true;
},
handleChange: function (index) {
this.select(this.scenarios[index]);
},
@ -119,6 +131,12 @@ export default {
case "delete":
this.deleteScenario(command.index);
break;
case "disable":
this.disableScenario(command.index);
break;
case "enable":
this.enableScenario(command.index);
break;
}
},
select: function (obj, scenario) {
@ -203,9 +221,9 @@ export default {
width: 100%;
}
.scenario-name > #hint {
color: #8a8b8d;
}
/*.scenario-name > #hint {*/
/*color: #8a8b8d;*/
/*}*/
.scenario-btn {
text-align: center;
@ -241,4 +259,9 @@ export default {
.scenario-draggable {
background-color: #909399;
}
.disable-scenario >>> .el-collapse-item__header {
border-right: 2px solid #909399;
color: #8a8b8d;
}
</style>

View File

@ -113,6 +113,7 @@
<style scoped>
.el-collapse-item__header {
padding-left: 7px;
border-right: 2px solid #409eff;
}
.el-collapse-item__header.is-active {
@ -122,4 +123,5 @@
.el-collapse-item__content {
padding-bottom: 0;
}
</style>

View File

@ -11,7 +11,7 @@
</el-select>
</el-form-item>
<el-button class="debug-button" size="small" type="primary" @click="runDebug">{{$t('load_test.save_and_run')}}</el-button>
<el-button :disabled="!request.enable || !scenario.enable || isReadOnly" class="debug-button" size="small" type="primary" @click="runDebug">{{$t('load_test.save_and_run')}}</el-button>
<el-tabs v-model="activeName">
<el-tab-pane label="Interface" name="interface">
@ -58,7 +58,7 @@
import MsApiKeyValue from "../ApiKeyValue";
import MsApiBody from "../ApiBody";
import MsApiAssertions from "../assertion/ApiAssertions";
import {DubboRequest} from "../../model/ScenarioModel";
import {DubboRequest, Scenario} from "../../model/ScenarioModel";
import MsApiExtract from "../extract/ApiExtract";
import ApiRequestMethodSelect from "../collapse/ApiRequestMethodSelect";
import MsDubboInterface from "@/business/components/api/test/components/request/dubbo/Interface";
@ -78,6 +78,7 @@
},
props: {
request: DubboRequest,
scenario: Scenario,
isReadOnly: {
type: Boolean,
default: false

View File

@ -40,8 +40,7 @@
</el-switch>
</el-form-item>
<el-button class="debug-button" size="small" type="primary" @click="runDebug">{{ $t('load_test.save_and_run') }}
</el-button>
<el-button :disabled="!request.enable || !scenario.enable || isReadOnly" class="debug-button" size="small" type="primary" @click="runDebug">{{ $t('load_test.save_and_run') }}</el-button>
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('api_test.request.parameters')" name="parameters">
@ -74,6 +73,9 @@
<el-tab-pane :label="$t('api_test.request.processor.post_exec_script')" name="beanShellPostProcessor">
<ms-bean-shell-processor :is-read-only="isReadOnly" :bean-shell-processor="request.beanShellPostProcessor"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.timeout_config')" name="advancedConfig">
<ms-api-advanced-config :is-read-only="isReadOnly" :request="request"/>
</el-tab-pane>
</el-tabs>
</el-form>
</template>
@ -88,10 +90,12 @@ import ApiRequestMethodSelect from "../collapse/ApiRequestMethodSelect";
import {REQUEST_HEADERS} from "@/common/js/constants";
import MsApiVariable from "@/business/components/api/test/components/ApiVariable";
import MsBeanShellProcessor from "../processor/BeanShellProcessor";
import MsApiAdvancedConfig from "../ApiAdvancedConfig";
export default {
name: "MsApiHttpRequestForm",
components: {
MsApiAdvancedConfig,
MsBeanShellProcessor,
MsApiVariable, ApiRequestMethodSelect, MsApiExtract, MsApiAssertions, MsApiBody, MsApiKeyValue},
props: {

View File

@ -2,7 +2,7 @@
<div class="request-container">
<draggable :list="this.scenario.requests" group="Request" class="request-draggable" ghost-class="request-ghost">
<div class="request-item" v-for="(request, index) in this.scenario.requests" :key="index" @click="select(request)"
:class="{'selected': isSelected(request)}">
:class="{'selected': isSelected(request), 'disable-request': !request.enable || !scenario.enable}">
<el-row type="flex" align="middle">
<div class="request-type">
{{request.showType()}}
@ -23,6 +23,12 @@
<el-dropdown-item :disabled="isReadOnly" :command="{type: 'delete', index: index}">
{{$t('api_test.request.delete')}}
</el-dropdown-item>
<el-dropdown-item v-if="request.enable" :disabled="isReadOnly" :command="{type: 'disable', index: index}">
{{$t('api_test.scenario.disable')}}
</el-dropdown-item>
<el-dropdown-item v-if="!request.enable" :disabled="isReadOnly" :command="{type: 'enable', index: index}">
{{$t('api_test.scenario.enable')}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
@ -70,7 +76,7 @@
computed: {
isSelected() {
return function (request) {
return this.selected.id === request.id;
return this.selected === request;
}
}
},
@ -89,6 +95,12 @@
let request = this.scenario.requests[index];
this.scenario.requests.push(new RequestFactory(request));
},
disableRequest: function (index) {
this.scenario.requests[index].enable = false;
},
enableRequest: function (index) {
this.scenario.requests[index].enable = true;
},
deleteRequest: function (index) {
this.scenario.requests.splice(index, 1);
if (this.scenario.requests.length === 0) {
@ -103,6 +115,12 @@
case "delete":
this.deleteRequest(command.index);
break;
case "disable":
this.disableRequest(command.index);
break;
case "enable":
this.enableRequest(command.index);
break;
}
},
select: function (request) {
@ -186,4 +204,17 @@
opacity: 0.5;
background-color: #909399;
}
.request-item.disable-request {
border-left-color: #909399;
}
.disable-request .request-type {
background-color: #909399;
}
.disable-request .request-method {
color: #909399;
}
</style>

View File

@ -1,7 +1,8 @@
<template>
<div class="request-form">
<component @runDebug="runDebug" :is="component" :is-read-only="isReadOnly" :request="request" :scenario="scenario"/>
<ms-request-result-tail v-loading="debugReportLoading" v-if="isCompleted" :request="request.debugRequestResult ? request.debugRequestResult : {responseResult: {}, subRequestResults: []}" :scenario-name="request.debugScenario ? request.debugScenario.name : ''"/>
<ms-request-result-tail v-loading="debugReportLoading" v-if="isCompleted" :request="request.debugRequestResult ? request.debugRequestResult : {responseResult: {}, subRequestResults: []}"
:scenario-name="request.debugScenario ? request.debugScenario.name : ''" ref="msDebugResult"/>
</div>
</template>
@ -72,9 +73,15 @@ export default {
if (res) {
this.debugReportLoading = false;
this.request.debugReport = res;
this.request.debugScenario = res.scenarios[0];
this.request.debugRequestResult = this.request.debugScenario.requestResults[0];
this.deleteReport(this.debugReportId)
if (res.scenarios && res.scenarios.length > 0) {
this.request.debugScenario = res.scenarios[0];
this.request.debugRequestResult = this.request.debugScenario.requestResults[0];
this.deleteReport(this.debugReportId);
} else {
this.request.debugScenario = new Scenario();
this.request.debugRequestResult = {responseResult: {}, subRequestResults: []};
}
this.$refs.msDebugResult.reload();
} else {
setTimeout(this.getReport, 2000)
}

View File

@ -289,6 +289,12 @@ export class HTTPSamplerProxy extends DefaultTestElement {
} else {
this.stringProp("HTTPSampler.port", options.port);
}
if (options.connectTimeout) {
this.stringProp('HTTPSampler.connect_timeout', options.connectTimeout);
}
if (options.responseTimeout) {
this.stringProp('HTTPSampler.response_timeout', options.responseTimeout);
}
this.boolProp("HTTPSampler.follow_redirects", options.follow, true);
this.boolProp("HTTPSampler.use_keepalive", options.keepalive, true);

View File

@ -207,6 +207,7 @@ export class Scenario extends BaseConfig {
this.dubboConfig = undefined;
this.environment = undefined;
this.enableCookieShare = false;
this.enable = true;
this.set(options);
this.sets({variables: KeyValue, headers: KeyValue, requests: RequestFactory}, options);
@ -304,6 +305,9 @@ export class HttpRequest extends Request {
this.debugReport = undefined;
this.beanShellPreProcessor = undefined;
this.beanShellPostProcessor = undefined;
this.enable = true;
this.connectTimeout = 60*1000;
this.responseTimeout = undefined;
this.set(options);
this.sets({parameters: KeyValue, headers: KeyValue}, options);
@ -383,6 +387,7 @@ export class DubboRequest extends Request {
this.debugReport = undefined;
this.beanShellPreProcessor = new BeanShellProcessor(options.beanShellPreProcessor);
this.beanShellPostProcessor = new BeanShellProcessor(options.beanShellPostProcessor);
this.enable = true;
this.sets({args: KeyValue, attachmentArgs: KeyValue}, options);
}
@ -723,6 +728,9 @@ class JMXHttpRequest {
let url = new URL(environment.protocol + "://" + environment.socket);
this.path = this.getPostQueryParameters(request, decodeURIComponent(url.pathname + (request.path ? request.path : '')));
}
this.connectTimeout = request.connectTimeout;
this.responseTimeout = request.responseTimeout;
}
}
@ -807,44 +815,50 @@ class JMXGenerator {
addScenarios(testPlan, scenarios) {
scenarios.forEach(s => {
let scenario = s.clone();
let threadGroup = new ThreadGroup(scenario.name || "");
if (s.enable) {
let scenario = s.clone();
this.addScenarioVariables(threadGroup, scenario);
let threadGroup = new ThreadGroup(scenario.name || "");
this.addScenarioHeaders(threadGroup, scenario);
this.addScenarioVariables(threadGroup, scenario);
this.addScenarioCookieManager(threadGroup, scenario);
this.addScenarioHeaders(threadGroup, scenario);
scenario.requests.forEach(request => {
if (!request.isValid()) return;
let sampler;
this.addScenarioCookieManager(threadGroup, scenario);
if (request instanceof DubboRequest) {
sampler = new DubboSample(request.name || "", new JMXDubboRequest(request, scenario.dubboConfig));
}
scenario.requests.forEach(request => {
if (request.enable) {
if (!request.isValid()) return;
let sampler;
if (request instanceof HttpRequest) {
sampler = new HTTPSamplerProxy(request.name || "", new JMXHttpRequest(request, scenario.environment));
this.addRequestHeader(sampler, request);
if (request.method.toUpperCase() === 'GET') {
this.addRequestArguments(sampler, request);
} else {
this.addRequestBody(sampler, request);
if (request instanceof DubboRequest) {
sampler = new DubboSample(request.name || "", new JMXDubboRequest(request, scenario.dubboConfig));
}
if (request instanceof HttpRequest) {
sampler = new HTTPSamplerProxy(request.name || "", new JMXHttpRequest(request, scenario.environment));
this.addRequestHeader(sampler, request);
if (request.method.toUpperCase() === 'GET') {
this.addRequestArguments(sampler, request);
} else {
this.addRequestBody(sampler, request);
}
}
this.addBeanShellProcessor(sampler, request);
this.addRequestAssertion(sampler, request);
this.addRequestExtractor(sampler, request);
threadGroup.put(sampler);
}
}
})
this.addBeanShellProcessor(sampler, request);
testPlan.put(threadGroup);
}
this.addRequestAssertion(sampler, request);
this.addRequestExtractor(sampler, request);
threadGroup.put(sampler);
})
testPlan.put(threadGroup);
})
}

View File

@ -0,0 +1,7 @@
import Vue from 'vue'
export const LIST_CHANGE = 'LIST_CHANGE';
export let ApiEvent = new Vue();
export let TrackEvent = new Vue();
export let PerformanceEvent = new Vue();

View File

@ -9,26 +9,28 @@
<el-submenu v-permission="['test_manager','test_user','test_viewer']"
index="3" popper-class="submenu">
<template v-slot:title>{{$t('commons.project')}}</template>
<ms-recent-list :options="projectRecent"/>
<template v-slot:title>{{ $t('commons.project') }}</template>
<ms-recent-list ref="projectRecent" :options="projectRecent"/>
<el-divider/>
<ms-show-all :index="'/performance/project/all'"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/performance/project/create'" :title="$t('project.create')"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/performance/project/create'"
:title="$t('project.create')"/>
</el-submenu>
<el-submenu v-permission="['test_manager','test_user','test_viewer']"
index="4" popper-class="submenu">
<template v-slot:title>{{$t('commons.test')}}</template>
<ms-recent-list :options="testRecent"/>
<template v-slot:title>{{ $t('commons.test') }}</template>
<ms-recent-list ref="testRecent" :options="testRecent"/>
<el-divider/>
<ms-show-all :index="'/performance/test/all'"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/performance/test/create'" :title="$t('load_test.create')"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/performance/test/create'"
:title="$t('load_test.create')"/>
</el-submenu>
<el-submenu v-permission="['test_manager','test_user','test_viewer']"
index="5" popper-class="submenu">
<template v-slot:title>{{$t('commons.report')}}</template>
<ms-recent-list :options="reportRecent"/>
<template v-slot:title>{{ $t('commons.report') }}</template>
<ms-recent-list ref="reportRecent" :options="reportRecent"/>
<el-divider/>
<ms-show-all :index="'/performance/report/all'"/>
</el-submenu>
@ -46,67 +48,75 @@
<script>
import MsCreateTest from "../../common/head/CreateTest";
import MsRecentList from "../../common/head/RecentList";
import MsCreateButton from "../../common/head/CreateButton";
import MsShowAll from "../../common/head/ShowAll";
import MsCreateTest from "../../common/head/CreateTest";
import MsRecentList from "../../common/head/RecentList";
import MsCreateButton from "../../common/head/CreateButton";
import MsShowAll from "../../common/head/ShowAll";
import {LIST_CHANGE, PerformanceEvent} from "@/business/components/common/head/ListEvent";
export default {
name: "PerformanceHeaderMenus",
components: {
MsCreateButton,
MsShowAll,
MsRecentList,
MsCreateTest
},
data() {
return {
projectRecent: {
title: this.$t('project.recent'),
url: "/project/recent/5",
index(item) {
return '/performance/test/' + item.id;
},
router(item) {
return {name: 'perPlan', params: {projectId: item.id, projectName: item.name}}
}
export default {
name: "PerformanceHeaderMenus",
components: {
MsCreateButton,
MsShowAll,
MsRecentList,
MsCreateTest
},
data() {
return {
projectRecent: {
title: this.$t('project.recent'),
url: "/project/recent/5",
index(item) {
return '/performance/test/' + item.id;
},
testRecent: {
title: this.$t('load_test.recent'),
url: "/performance/recent/5",
index(item) {
return '/performance/test/edit/' + item.id;
},
router(item) {
}
router(item) {
return {name: 'perPlan', params: {projectId: item.id, projectName: item.name}}
}
},
testRecent: {
title: this.$t('load_test.recent'),
url: "/performance/recent/5",
index(item) {
return '/performance/test/edit/' + item.id;
},
reportRecent: {
title: this.$t('report.recent'),
url: "/performance/report/recent/5",
showTime: true,
index(item) {
return '/performance/report/view/' + item.id;
},
router(item) {
}
router(item) {
}
},
reportRecent: {
title: this.$t('report.recent'),
url: "/performance/report/recent/5",
showTime: true,
index(item) {
return '/performance/report/view/' + item.id;
},
router(item) {
}
}
},
}
},
mounted() {
PerformanceEvent.$on(LIST_CHANGE, () => {
this.$refs.projectRecent.recent();
this.$refs.testRecent.recent();
this.$refs.reportRecent.recent();
});
}
}
</script>
<style scoped>
.el-divider--horizontal {
margin: 0;
}
.el-divider--horizontal {
margin: 0;
}
.el-menu.el-menu--horizontal {
border-bottom: none;
}
.el-menu.el-menu--horizontal {
border-bottom: none;
}
#menu-bar {
border-bottom: 1px solid #E6E6E6;
background-color: #FFF;
}
#menu-bar {
border-bottom: 1px solid #E6E6E6;
background-color: #FFF;
}
</style>

View File

@ -45,7 +45,8 @@
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="triggerMode" width="150" :label="'触发方式'" column-key="triggerMode" :filters="triggerFilters">
<el-table-column prop="triggerMode" width="150" :label="'触发方式'" column-key="triggerMode"
:filters="triggerFilters">
<template v-slot:default="scope">
<report-trigger-mode-item :trigger-mode="scope.row.triggerMode"/>
</template>
@ -63,8 +64,10 @@
width="150"
:label="$t('commons.operating')">
<template v-slot:default="scope">
<ms-table-operator-button :tip="$t('api_report.detail')" icon="el-icon-s-data" @exec="handleEdit(scope.row)" type="primary"/>
<ms-table-operator-button :is-tester-permission="true" :tip="$t('api_report.delete')" icon="el-icon-delete" @exec="handleDelete(scope.row)" type="danger"/>
<ms-table-operator-button :tip="$t('api_report.detail')" icon="el-icon-s-data"
@exec="handleEdit(scope.row)" type="primary"/>
<ms-table-operator-button :is-tester-permission="true" :tip="$t('api_report.delete')"
icon="el-icon-delete" @exec="handleDelete(scope.row)" type="danger"/>
</template>
</el-table-column>
</el-table>
@ -76,125 +79,129 @@
</template>
<script>
import MsTablePagination from "../../common/pagination/TablePagination";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsPerformanceReportStatus from "./PerformanceReportStatus";
import {_filter, _sort} from "../../../../common/js/utils";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import ReportTriggerModeItem from "../../common/tableItem/ReportTriggerModeItem";
import {REPORT_CONFIGS} from "../../common/components/search/search-components";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsTablePagination from "../../common/pagination/TablePagination";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsPerformanceReportStatus from "./PerformanceReportStatus";
import {_filter, _sort} from "../../../../common/js/utils";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import ReportTriggerModeItem from "../../common/tableItem/ReportTriggerModeItem";
import {REPORT_CONFIGS} from "../../common/components/search/search-components";
import MsTableHeader from "../../common/components/MsTableHeader";
import {LIST_CHANGE, PerformanceEvent} from "@/business/components/common/head/ListEvent";
export default {
name: "PerformanceTestReport",
components: {
MsTableHeader,
ReportTriggerModeItem,
MsTableOperatorButton, MsPerformanceReportStatus, MsTablePagination, MsContainer, MsMainContainer},
created: function () {
export default {
name: "PerformanceTestReport",
components: {
MsTableHeader,
ReportTriggerModeItem,
MsTableOperatorButton, MsPerformanceReportStatus, MsTablePagination, MsContainer, MsMainContainer
},
created: function () {
this.initTableData();
},
data() {
return {
result: {},
queryPath: "/performance/report/list/all",
deletePath: "/performance/report/delete/",
condition: {
components: REPORT_CONFIGS
},
projectId: null,
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
total: 0,
loading: false,
testId: null,
statusFilters: [
{text: 'Starting', value: 'Starting'},
{text: 'Running', value: 'Running'},
{text: 'Reporting', value: 'Reporting'},
{text: 'Completed', value: 'Completed'},
{text: 'Error', value: 'Error'}
],
triggerFilters: [
{text: '手动', value: 'MANUAL'},
{text: '定时任务', value: 'SCHEDULE'},
{text: 'API', value: 'API'}
],
}
},
watch: {
'$route'(to) {
this.projectId = to.params.projectId;
this.initTableData();
}
},
methods: {
initTableData() {
if (this.testId !== 'all') {
this.condition.testId = this.testId;
}
this.result = this.$post(this.buildPagePath(this.queryPath), this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
search(combine) {
this.initTableData(combine);
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleEdit(report) {
if (report.status === "Error") {
this.$warning(this.$t('report.generation_error'));
return false
} else if (report.status === "Starting") {
this.$info(this.$t('report.being_generated'))
return false
}
this.$router.push({
path: '/performance/report/view/' + report.id
})
},
handleDelete(report) {
this.$alert(this.$t('report.delete_confirm') + report.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this._handleDelete(report);
}
}
});
},
_handleDelete(report) {
this.result = this.$post(this.deletePath + report.id, {}, () => {
this.$success(this.$t('commons.delete_success'));
this.initTableData();
// 广 head
PerformanceEvent.$emit(LIST_CHANGE);
});
},
sort(column) {
_sort(column, this.condition);
this.initTableData();
},
data() {
return {
result: {},
queryPath: "/performance/report/list/all",
deletePath: "/performance/report/delete/",
condition: {
components: REPORT_CONFIGS
},
projectId: null,
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
total: 0,
loading: false,
testId: null,
statusFilters: [
{text: 'Starting', value: 'Starting'},
{text: 'Running', value: 'Running'},
{text: 'Reporting', value: 'Reporting'},
{text: 'Completed', value: 'Completed'},
{text: 'Error', value: 'Error'}
],
triggerFilters: [
{text: '手动', value: 'MANUAL'},
{text: '定时任务', value: 'SCHEDULE'},
{text: 'API', value: 'API'}
],
}
filter(filters) {
_filter(filters, this.condition);
this.initTableData();
},
watch: {
'$route'(to) {
this.projectId = to.params.projectId;
this.initTableData();
}
},
methods: {
initTableData() {
if (this.testId !== 'all') {
this.condition.testId = this.testId;
}
this.result = this.$post(this.buildPagePath(this.queryPath), this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
search(combine) {
this.initTableData(combine);
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleEdit(report) {
if (report.status === "Error") {
this.$warning(this.$t('report.generation_error'));
return false
} else if (report.status === "Starting") {
this.$info(this.$t('report.being_generated'))
return false
}
this.$router.push({
path: '/performance/report/view/' + report.id
})
},
handleDelete(report) {
this.$alert(this.$t('report.delete_confirm') + report.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this._handleDelete(report);
}
}
});
},
_handleDelete(report) {
this.result = this.$post(this.deletePath + report.id, {}, () => {
this.$success(this.$t('commons.delete_success'));
this.initTableData();
});
},
sort(column) {
_sort(column, this.condition);
this.initTableData();
},
filter(filters) {
_filter(filters, this.condition);
this.initTableData();
},
}
}
}
</script>
<style scoped>
.test-content {
width: 100%;
}
.test-content {
width: 100%;
}
</style>

View File

@ -74,159 +74,162 @@
</template>
<script>
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableOperator from "../../common/components/MsTableOperator";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsPerformanceTestStatus from "./PerformanceTestStatus";
import MsTableOperators from "../../common/components/MsTableOperators";
import {_filter, _sort} from "../../../../common/js/utils";
import MsTableHeader from "../../common/components/MsTableHeader";
import {TEST_CONFIGS} from "../../common/components/search/search-components";
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableOperator from "../../common/components/MsTableOperator";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsPerformanceTestStatus from "./PerformanceTestStatus";
import MsTableOperators from "../../common/components/MsTableOperators";
import {_filter, _sort} from "../../../../common/js/utils";
import MsTableHeader from "../../common/components/MsTableHeader";
import {TEST_CONFIGS} from "../../common/components/search/search-components";
import {LIST_CHANGE, PerformanceEvent} from "@/business/components/common/head/ListEvent";
export default {
components: {
MsTableHeader,
MsPerformanceTestStatus,
MsTablePagination,
MsTableOperator,
MsContainer,
MsMainContainer,
MsTableOperators
export default {
components: {
MsTableHeader,
MsPerformanceTestStatus,
MsTablePagination,
MsTableOperator,
MsContainer,
MsMainContainer,
MsTableOperators
},
data() {
return {
result: {},
queryPath: "/performance/list",
deletePath: "/performance/delete",
condition: {
components: TEST_CONFIGS
},
projectId: null,
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
total: 0,
loading: false,
testId: null,
buttons: [
{
tip: this.$t('commons.edit'), icon: "el-icon-edit",
exec: this.handleEdit
}, {
tip: this.$t('commons.copy'), icon: "el-icon-copy-document", type: "success",
exec: this.handleCopy
}, {
tip: this.$t('commons.delete'), icon: "el-icon-delete", type: "danger",
exec: this.handleDelete
}
],
statusFilters: [
{text: 'Saved', value: 'Saved'},
{text: 'Starting', value: 'Starting'},
{text: 'Running', value: 'Running'},
{text: 'Reporting', value: 'Reporting'},
{text: 'Completed', value: 'Completed'},
{text: 'Error', value: 'Error'}
]
}
},
watch: {
'$route'(to) {
this.projectId = to.params.projectId;
this.initTableData();
}
},
created: function () {
this.projectId = this.$route.params.projectId;
this.initTableData();
},
methods: {
initTableData() {
if (this.projectId !== 'all') {
this.condition.projectId = this.projectId;
}
this.result = this.$post(this.buildPagePath(this.queryPath), this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
data() {
return {
result: {},
queryPath: "/performance/list",
deletePath: "/performance/delete",
condition: {
components: TEST_CONFIGS
},
projectId: null,
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
total: 0,
loading: false,
testId: null,
buttons: [
{
tip: this.$t('commons.edit'), icon: "el-icon-edit",
exec: this.handleEdit
}, {
tip: this.$t('commons.copy'), icon: "el-icon-copy-document", type: "success",
exec: this.handleCopy
}, {
tip: this.$t('commons.delete'), icon: "el-icon-delete", type: "danger",
exec: this.handleDelete
search(combine) {
this.initTableData(combine);
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleEdit(testPlan) {
this.$router.push({
path: '/performance/test/edit/' + testPlan.id,
})
},
handleCopy(testPlan) {
this.result = this.$post("/performance/copy", {id: testPlan.id}, () => {
this.$success(this.$t('commons.copy_success'));
this.search();
});
},
handleDelete(testPlan) {
this.$alert(this.$t('load_test.delete_confirm') + testPlan.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this._handleDelete(testPlan);
}
],
statusFilters: [
{text: 'Saved', value: 'Saved'},
{text: 'Starting', value: 'Starting'},
{text: 'Running', value: 'Running'},
{text: 'Reporting', value: 'Reporting'},
{text: 'Completed', value: 'Completed'},
{text: 'Error', value: 'Error'}
]
}
}
});
},
watch: {
'$route'(to) {
this.projectId = to.params.projectId;
_handleDelete(testPlan) {
let data = {
id: testPlan.id
};
this.result = this.$post(this.deletePath, data, () => {
this.$success(this.$t('commons.delete_success'));
this.initTableData();
}
// 广 head
PerformanceEvent.$emit(LIST_CHANGE);
});
},
created: function () {
this.projectId = this.$route.params.projectId;
sort(column) {
_sort(column, this.condition);
this.initTableData();
},
methods: {
initTableData() {
if (this.projectId !== 'all') {
this.condition.projectId = this.projectId;
}
this.result = this.$post(this.buildPagePath(this.queryPath), this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
search(combine) {
this.initTableData(combine);
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleEdit(testPlan) {
this.$router.push({
path: '/performance/test/edit/' + testPlan.id,
})
},
handleCopy(testPlan) {
this.result = this.$post("/performance/copy", {id: testPlan.id}, () => {
this.$success(this.$t('commons.copy_success'));
this.search();
});
},
handleDelete(testPlan) {
this.$alert(this.$t('load_test.delete_confirm') + testPlan.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this._handleDelete(testPlan);
}
}
});
},
_handleDelete(testPlan) {
let data = {
id: testPlan.id
};
this.result = this.$post(this.deletePath, data, () => {
this.$success(this.$t('commons.delete_success'));
this.initTableData();
});
},
sort(column) {
_sort(column, this.condition);
this.initTableData();
},
filter(filters) {
_filter(filters, this.condition);
this.initTableData();
},
link(row) {
this.$router.push({
path: '/performance/test/edit/' + row.id,
})
},
create() {
this.$router.push('/performance/test/create');
}
filter(filters) {
_filter(filters, this.condition);
this.initTableData();
},
link(row) {
this.$router.push({
path: '/performance/test/edit/' + row.id,
})
},
create() {
this.$router.push('/performance/test/create');
}
}
}
</script>
<style scoped>
.test-content {
width: 100%;
}
.test-content {
width: 100%;
}
.table-page {
padding-top: 20px;
margin-right: -9px;
float: right;
}
.table-page {
padding-top: 20px;
margin-right: -9px;
float: right;
}
.el-table {
cursor: pointer;
}
.el-table {
cursor: pointer;
}
</style>

View File

@ -10,7 +10,7 @@
<el-table-column prop="name" :label="$t('commons.name')" width="250" show-overflow-tooltip/>
<el-table-column prop="description" :label="$t('commons.description')" show-overflow-tooltip>
<template v-slot:default="scope">
<pre>{{scope.row.description}}</pre>
<pre>{{ scope.row.description }}</pre>
</template>
</el-table-column>
<!--<el-table-column prop="workspaceName" :label="$t('project.owning_workspace')"/>-->
@ -34,7 +34,8 @@
</el-table-column>
<el-table-column :label="$t('commons.operating')">
<template v-slot:default="scope">
<ms-table-operator :is-tester-permission="true" @editClick="edit(scope.row)" @deleteClick="handleDelete(scope.row)">
<ms-table-operator :is-tester-permission="true" @editClick="edit(scope.row)"
@deleteClick="handleDelete(scope.row)">
<template v-if="baseUrl == 'api'" v-slot:behind>
<ms-table-operator-button :is-tester-permission="true" :tip="'环境配置'" icon="el-icon-setting"
type="info" @exec="openEnvironmentConfig(scope.row)"/>
@ -56,10 +57,10 @@
<el-form-item :label="$t('commons.description')" prop="description">
<el-input :autosize="{ minRows: 2, maxRows: 4}" type="textarea" v-model="form.description"></el-input>
</el-form-item>
<el-form-item label="TAPD项目ID" v-if="tapd">
<el-form-item :label="$t('project.tapd_id')" v-if="tapd">
<el-input v-model="form.tapdId" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="JIRA项目key" v-if="jira">
<el-form-item :label="$t('project.jira_key')" v-if="jira">
<el-input v-model="form.jiraKey" autocomplete="off"></el-input>
</el-form-item>
</el-form>
@ -80,203 +81,209 @@
</template>
<script>
import MsCreateBox from "../settings/CreateBox";
import {Message} from "element-ui";
import MsTablePagination from "../common/pagination/TablePagination";
import MsTableHeader from "../common/components/MsTableHeader";
import MsTableOperator from "../common/components/MsTableOperator";
import MsDialogFooter from "../common/components/MsDialogFooter";
import {_sort, getCurrentUser, listenGoBack, removeGoBackListener} from "../../../common/js/utils";
import MsContainer from "../common/components/MsContainer";
import MsMainContainer from "../common/components/MsMainContainer";
import MsDeleteConfirm from "../common/components/MsDeleteConfirm";
import MsTableOperatorButton from "../common/components/MsTableOperatorButton";
import ApiEnvironmentConfig from "../api/test/components/ApiEnvironmentConfig";
import TemplateComponent from "../track/plan/view/comonents/report/TemplateComponent/TemplateComponent";
import MsCreateBox from "../settings/CreateBox";
import {Message} from "element-ui";
import MsTablePagination from "../common/pagination/TablePagination";
import MsTableHeader from "../common/components/MsTableHeader";
import MsTableOperator from "../common/components/MsTableOperator";
import MsDialogFooter from "../common/components/MsDialogFooter";
import {_sort, getCurrentUser, listenGoBack, removeGoBackListener} from "../../../common/js/utils";
import MsContainer from "../common/components/MsContainer";
import MsMainContainer from "../common/components/MsMainContainer";
import MsDeleteConfirm from "../common/components/MsDeleteConfirm";
import MsTableOperatorButton from "../common/components/MsTableOperatorButton";
import ApiEnvironmentConfig from "../api/test/components/ApiEnvironmentConfig";
import TemplateComponent from "../track/plan/view/comonents/report/TemplateComponent/TemplateComponent";
import {ApiEvent, LIST_CHANGE, PerformanceEvent, TrackEvent} from "@/business/components/common/head/ListEvent";
export default {
name: "MsProject",
components: {
TemplateComponent,
ApiEnvironmentConfig,
MsTableOperatorButton,
MsDeleteConfirm,
MsMainContainer,
MsContainer, MsTableOperator, MsCreateBox, MsTablePagination, MsTableHeader, MsDialogFooter},
data() {
return {
createVisible: false,
result: {},
btnTips: this.$t('project.create'),
title: this.$t('project.create'),
condition: {},
items: [],
tapd: false,
jira: false,
form: {},
currentPage: 1,
pageSize: 5,
total: 0,
rules: {
name: [
{required: true, message: this.$t('project.input_name'), trigger: 'blur'},
{min: 2, max: 50, message: this.$t('commons.input_limit', [2, 50]), trigger: 'blur'}
],
description: [
{max: 500, message: this.$t('commons.input_limit', [0, 500]), trigger: 'blur'}
],
},
}
},
props: {
baseUrl: {
type: String
}
},
mounted() {
export default {
name: "MsProject",
components: {
TemplateComponent,
ApiEnvironmentConfig,
MsTableOperatorButton,
MsDeleteConfirm,
MsMainContainer,
MsContainer, MsTableOperator, MsCreateBox, MsTablePagination, MsTableHeader, MsDialogFooter
},
data() {
return {
createVisible: false,
result: {},
btnTips: this.$t('project.create'),
title: this.$t('project.create'),
condition: {},
items: [],
tapd: false,
jira: false,
form: {},
currentPage: 1,
pageSize: 5,
total: 0,
rules: {
name: [
{required: true, message: this.$t('project.input_name'), trigger: 'blur'},
{min: 2, max: 50, message: this.$t('commons.input_limit', [2, 50]), trigger: 'blur'}
],
description: [
{max: 500, message: this.$t('commons.input_limit', [0, 500]), trigger: 'blur'}
],
},
}
},
props: {
baseUrl: {
type: String
}
},
mounted() {
if (this.$route.path.split('/')[2] === 'project' &&
this.$route.path.split('/')[3] === 'create') {
this.create();
this.$router.push('/' + this.baseUrl + '/project/all');
}
this.list();
},
activated() {
this.list();
},
watch: {
'$route'(to) {
if (this.$route.path.split('/')[2] === 'project' &&
this.$route.path.split('/')[3] === 'create') {
to.path.split('/')[3] === 'create') {
this.create();
this.$router.push('/' + this.baseUrl + '/project/all');
}
this.list();
},
activated() {
this.list();
},
watch: {
'$route'(to) {
if (this.$route.path.split('/')[2] === 'project' &&
to.path.split('/')[3] === 'create') {
this.create();
this.$router.push('/' + this.baseUrl + '/project/all');
} else if (this.$route.path.split('/')[2] === 'project' &&
to.path.split('/')[3] === 'all') {
this.list();
}
}
},
computed: {
currentUser: () => {
return getCurrentUser();
}
},
destroyed() {
this.createVisible = false;
},
methods: {
create() {
let workspaceId = this.currentUser.lastWorkspaceId;
if (!workspaceId) {
this.$warning(this.$t('project.please_choose_workspace'));
return false;
}
this.title = this.$t('project.create');
listenGoBack(this.handleClose);
this.createVisible = true;
this.form = {};
},
edit(row) {
this.title = this.$t('project.edit');
this.createVisible = true;
listenGoBack(this.handleClose);
this.form = Object.assign({}, row);
if (this.baseUrl === 'track') {
this.$get("/service/integration/all/" + getCurrentUser().lastOrganizationId, response => {
let data = response.data;
let platforms = data.map(d => d.platform);
if (platforms.indexOf("Tapd") !== -1) {
this.tapd = true;
}
if (platforms.indexOf("Jira") !== -1) {
this.jira = true;
}
});
}
},
submit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
let saveType = "add";
if (this.form.id) {
saveType = "update"
}
this.result = this.$post("/project/" + saveType, this.form, () => {
this.createVisible = false;
this.list();
Message.success(this.$t('commons.save_success'));
});
} else {
return false;
}
});
},
handleDelete(project) {
this.$refs.deleteConfirm.open(project);
},
_handleDelete(project) {
this.$confirm(this.$t('project.delete_tip'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.$get('/project/delete/' + project.id, () => {
Message.success(this.$t('commons.delete_success'));
this.list();
});
}).catch(() => {
this.$message({
type: 'info',
message: this.$t('commons.delete_cancelled')
});
});
},
handleClose() {
removeGoBackListener(this.handleClose);
this.createVisible = false;
},
search() {
} else if (this.$route.path.split('/')[2] === 'project' &&
to.path.split('/')[3] === 'all') {
this.list();
},
list() {
let url = "/project/list/" + this.currentPage + '/' + this.pageSize;
this.result = this.$post(url, this.condition, (response) => {
let data = response.data;
this.items = data.listObject;
this.total = data.itemCount;
})
},
link(row) {
// performance_test project link
if (this.$route.name === 'perProject') {
this.$router.push({
path: '/performance/test/' + row.id,
})
} else if (this.$route.name === 'fucProject') {
this.$router.push({
path: '/api/test/list/' + row.id
})
} else if (this.$route.name ==='trackProject') {
this.$router.push({
path: '/track/case/' + row.id
})
}
},
sort(column) {
_sort(column, this.condition);
this.list();
},
openEnvironmentConfig(project) {
this.$refs.environmentConfig.open(project.id);
}
}
},
computed: {
currentUser: () => {
return getCurrentUser();
}
},
destroyed() {
this.createVisible = false;
},
methods: {
create() {
let workspaceId = this.currentUser.lastWorkspaceId;
if (!workspaceId) {
this.$warning(this.$t('project.please_choose_workspace'));
return false;
}
this.title = this.$t('project.create');
listenGoBack(this.handleClose);
this.createVisible = true;
this.form = {};
},
edit(row) {
this.title = this.$t('project.edit');
this.createVisible = true;
listenGoBack(this.handleClose);
this.form = Object.assign({}, row);
if (this.baseUrl === 'track') {
this.$get("/service/integration/all/" + getCurrentUser().lastOrganizationId, response => {
let data = response.data;
let platforms = data.map(d => d.platform);
if (platforms.indexOf("Tapd") !== -1) {
this.tapd = true;
}
if (platforms.indexOf("Jira") !== -1) {
this.jira = true;
}
});
}
},
submit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
let saveType = "add";
if (this.form.id) {
saveType = "update"
}
this.result = this.$post("/project/" + saveType, this.form, () => {
this.createVisible = false;
this.list();
Message.success(this.$t('commons.save_success'));
});
} else {
return false;
}
});
},
handleDelete(project) {
this.$refs.deleteConfirm.open(project);
},
_handleDelete(project) {
this.$confirm(this.$t('project.delete_tip'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.$get('/project/delete/' + project.id, () => {
Message.success(this.$t('commons.delete_success'));
this.list();
// 广 head
ApiEvent.$emit(LIST_CHANGE);
TrackEvent.$emit(LIST_CHANGE);
PerformanceEvent.$emit(LIST_CHANGE);
});
}).catch(() => {
this.$message({
type: 'info',
message: this.$t('commons.delete_cancelled')
});
});
},
handleClose() {
removeGoBackListener(this.handleClose);
this.createVisible = false;
},
search() {
this.list();
},
list() {
let url = "/project/list/" + this.currentPage + '/' + this.pageSize;
this.result = this.$post(url, this.condition, (response) => {
let data = response.data;
this.items = data.listObject;
this.total = data.itemCount;
})
},
link(row) {
// performance_test project link
if (this.$route.name === 'perProject') {
this.$router.push({
path: '/performance/test/' + row.id,
})
} else if (this.$route.name === 'fucProject') {
this.$router.push({
path: '/api/test/list/' + row.id
})
} else if (this.$route.name === 'trackProject') {
this.$router.push({
path: '/track/case/' + row.id
})
}
},
sort(column) {
_sort(column, this.condition);
this.list();
},
openEnvironmentConfig(project) {
this.$refs.environmentConfig.open(project.id);
}
}
}
</script>
<style scoped>
.el-table {
cursor:pointer;
}
.el-table {
cursor: pointer;
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<el-card class="header-title" v-loading="result.loading">
<div class="header-title" v-loading="result.loading">
<div>
<div>{{$t('organization.integration.select_defect_platform')}}</div>
<el-radio-group v-model="platform" style="margin-top: 10px" @change="change">
@ -33,7 +33,8 @@
</el-button>
<el-button v-if="showEdit" size="mini" @click="edit">{{$t('commons.edit')}}</el-button>
<el-button type="primary" v-if="showSave" size="mini" @click="save('form')">{{$t('commons.save')}}</el-button>
<el-button v-if="showCancel" size="mini" @click="cancelEdit">{{$t('organization.integration.cancel_edit')}}</el-button>
<el-button v-if="showCancel" size="mini" @click="cancelEdit">{{$t('organization.integration.cancel_edit')}}
</el-button>
<el-button type="info" size="mini" @click="cancelIntegration('form')" :disabled="!show">
{{$t('organization.integration.cancel_integration')}}
</el-button>
@ -46,11 +47,12 @@
</div>
<div>
2. {{$t('organization.integration.use_tip_two')}}
<router-link to="/track/project/all" style="margin-left: 5px">{{$t('organization.integration.link_the_project_now')}}
<router-link to="/track/project/all" style="margin-left: 5px">
{{$t('organization.integration.link_the_project_now')}}
</router-link>
</div>
</div>
</el-card>
</div>
</template>
<script>
@ -69,9 +71,21 @@
showSave: false,
showCancel: false,
rules: {
account: {required: true, message: this.$t('organization.integration.input_api_account'), trigger: ['change', 'blur']},
password: {required: true, message: this.$t('organization.integration.input_api_password'), trigger: ['change', 'blur']},
url: {required: true, message: this.$t('organization.integration.input_jira_url'), trigger: ['change', 'blur']}
account: {
required: true,
message: this.$t('organization.integration.input_api_account'),
trigger: ['change', 'blur']
},
password: {
required: true,
message: this.$t('organization.integration.input_api_password'),
trigger: ['change', 'blur']
},
url: {
required: true,
message: this.$t('organization.integration.input_jira_url'),
trigger: ['change', 'blur']
}
},
}
},
@ -126,7 +140,7 @@
}
}
});
} else {
} else {
this.$warning(this.$t('organization.integration.not_integrated'));
}
},
@ -188,9 +202,14 @@
});
},
testConnection() {
this.result = this.$get("issues/auth/" + this.platform, () => {
this.$success(this.$t('organization.integration.verified'));
});
if (this.form.account && this.form.password && this.platform) {
this.result = this.$get("issues/auth/" + this.platform, () => {
this.$success(this.$t('organization.integration.verified'));
});
} else {
this.$warning(this.$t('organization.integration.not_integrated'));
return false;
}
}
}
}

View File

@ -1,11 +1,11 @@
<template>
<div>
<el-card>
<el-tabs class="system-setting" v-model="activeName">
<el-tab-pane :label="$t('organization.defect_manage')" name="defect">
<defect-management/>
</el-tab-pane>
</el-tabs>
</div>
</el-card>
</template>
<script>

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,380 +121,385 @@
</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>
<script>
import MsCreateBox from '../../../settings/CreateBox';
import TestCaseImport from '../components/TestCaseImport';
import TestCaseExport from '../components/TestCaseExport';
import MsTablePagination from '../../../../components/common/pagination/TablePagination';
import NodeBreadcrumb from '../../common/NodeBreadcrumb';
import MsTableHeader from '../../../../components/common/components/MsTableHeader';
import PriorityTableItem from "../../common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../common/tableItems/planview/MethodTableItem";
import MsTableOperator from "../../../common/components/MsTableOperator";
import MsTableOperatorButton from "../../../common/components/MsTableOperatorButton";
import MsTableButton from "../../../common/components/MsTableButton";
import {_filter, _sort} from "../../../../../common/js/utils";
import {TEST_CASE_CONFIGS} from "../../../common/components/search/search-components";
import ShowMoreBtn from "./ShowMoreBtn";
import BatchEdit from "./BatchEdit";
import {WORKSPACE_ID} from "../../../../../common/js/constants";
import MsCreateBox from '../../../settings/CreateBox';
import TestCaseImport from '../components/TestCaseImport';
import TestCaseExport from '../components/TestCaseExport';
import MsTablePagination from '../../../../components/common/pagination/TablePagination';
import NodeBreadcrumb from '../../common/NodeBreadcrumb';
import MsTableHeader from '../../../../components/common/components/MsTableHeader';
import PriorityTableItem from "../../common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../common/tableItems/planview/MethodTableItem";
import MsTableOperator from "../../../common/components/MsTableOperator";
import MsTableOperatorButton from "../../../common/components/MsTableOperatorButton";
import MsTableButton from "../../../common/components/MsTableButton";
import {_filter, _sort} from "../../../../../common/js/utils";
import {TEST_CASE_CONFIGS} from "../../../common/components/search/search-components";
import ShowMoreBtn from "./ShowMoreBtn";
import BatchEdit from "./BatchEdit";
import {WORKSPACE_ID} from "../../../../../common/js/constants";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
export default {
name: "TestCaseList",
components: {
MsTableButton,
MsTableOperatorButton,
MsTableOperator,
MethodTableItem,
TypeTableItem,
PriorityTableItem,
MsCreateBox,
TestCaseImport,
TestCaseExport,
MsTablePagination,
NodeBreadcrumb,
MsTableHeader,
ShowMoreBtn,
BatchEdit
},
data() {
return {
result: {},
deletePath: "/test/case/delete",
condition: {
components: TEST_CASE_CONFIGS
},
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0,
selectRows: new Set(),
priorityFilters: [
{text: 'P0', value: 'P0'},
{text: 'P1', value: 'P1'},
{text: 'P2', value: 'P2'},
{text: 'P3', value: 'P3'}
export default {
name: "TestCaseList",
components: {
MsTableButton,
MsTableOperatorButton,
MsTableOperator,
MethodTableItem,
TypeTableItem,
PriorityTableItem,
MsCreateBox,
TestCaseImport,
TestCaseExport,
MsTablePagination,
NodeBreadcrumb,
MsTableHeader,
ShowMoreBtn,
BatchEdit
},
data() {
return {
result: {},
deletePath: "/test/case/delete",
condition: {
components: TEST_CASE_CONFIGS
},
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0,
selectRows: new Set(),
priorityFilters: [
{text: 'P0', value: 'P0'},
{text: 'P1', value: 'P1'},
{text: 'P2', value: 'P2'},
{text: 'P3', value: 'P3'}
],
methodFilters: [
{text: this.$t('test_track.case.manual'), value: 'manual'},
{text: this.$t('test_track.case.auto'), value: 'auto'}
],
typeFilters: [
{text: this.$t('commons.functional'), value: 'functional'},
{text: this.$t('commons.performance'), value: 'performance'},
{text: this.$t('commons.api'), value: 'api'}
],
showMore: false,
buttons: [
{
name: this.$t('test_track.case.batch_edit_case'), handleClick: this.handleBatchEdit
}, {
name: this.$t('test_track.case.batch_move_case'), handleClick: this.handleBatchMove
}, {
name: this.$t('test_track.case.batch_delete_case'), handleClick: this.handleDeleteBatch
}
],
typeArr: [
{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: [
{name: 'P0', id: 'P0'},
{name: 'P1', id: 'P1'},
{name: 'P2', id: 'P2'},
{name: 'P3', id: 'P3'}
],
methodFilters: [
{text: this.$t('test_track.case.manual'), value: 'manual'},
{text: this.$t('test_track.case.auto'), value: 'auto'}
type: [
{name: this.$t('commons.functional'), id: 'functional'},
{name: this.$t('commons.performance'), id: 'performance'},
{name: this.$t('commons.api'), id: 'api'}
],
typeFilters: [
{text: this.$t('commons.functional'), value: 'functional'},
{text: this.$t('commons.performance'), value: 'performance'},
{text: this.$t('commons.api'), value: 'api'}
method: [
{name: this.$t('test_track.case.manual'), id: 'manual'},
{name: this.$t('test_track.case.auto'), id: 'auto'}
],
showMore: false,
buttons: [
{
name: '批量编辑用例', handleClick: this.handleBatchEdit
}, {
name: '批量移动用例', handleClick: this.handleBatchMove
}, {
name: '批量删除用例', handleClick: this.handleDeleteBatch
}
],
typeArr: [
{id: 'priority', name: '用例等级'},
{id: 'type', name: '类型'},
{id: 'method', name: '测试方式'},
{id: 'maintainer', name: '维护人'},
],
valueArr: {
priority: [
{name: 'P0', id: 'P0'},
{name: 'P1', id: 'P1'},
{name: 'P2', id: 'P2'},
{name: 'P3', id: 'P3'}
],
type: [
{name: this.$t('commons.functional'), id: 'functional'},
{name: this.$t('commons.performance'), id: 'performance'},
{name: this.$t('commons.api'), id: 'api'}
],
method: [
{name: this.$t('test_track.case.manual'), id: 'manual'},
{name: this.$t('test_track.case.auto'), id: 'auto'}
],
maintainer: [],
}
}
},
props: {
currentProject: {
type: Object
},
selectNodeIds: {
type: Array
},
selectParentNodes: {
type: Array
}
},
created: function () {
this.initTableData();
},
watch: {
currentProject() {
this.initTableData();
},
selectNodeIds() {
this.initTableData();
}
},
methods: {
initTableData() {
if (this.planId) {
// param.planId = this.planId;
this.condition.planId = this.planId;
}
if (this.selectNodeIds && this.selectNodeIds.length > 0) {
// param.nodeIds = this.selectNodeIds;
this.condition.nodeIds = this.selectNodeIds;
}
if (this.currentProject) {
this.condition.projectId = this.currentProject.id;
this.result = this.$post(this.buildPagePath('/test/case/list'), this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
// this.selectIds.clear();
this.selectRows.clear();
});
}
},
search() {
this.initTableData();
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
testCaseCreate() {
this.$emit('testCaseEdit');
},
handleEdit(testCase) {
this.$emit('testCaseEdit', testCase);
},
handleCopy(testCase) {
this.$emit('testCaseCopy', testCase);
},
handleDelete(testCase) {
this.$alert(this.$t('test_track.case.delete_confirm') + '\'' + testCase.name + '\'' + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this._handleDelete(testCase);
}
}
});
},
handleDeleteBatch() {
this.$alert(this.$t('test_track.case.delete_confirm') + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let ids = Array.from(this.selectRows).map(row => row.id);
this.$post('/test/case/batch/delete', {ids: ids}, () => {
this.selectRows.clear();
this.$emit("refresh");
this.$success(this.$t('commons.delete_success'));
});
}
}
});
},
_handleDelete(testCase) {
let testCaseId = testCase.id;
this.$post('/test/case/delete/' + testCaseId, {}, () => {
this.initTableData();
this.$success(this.$t('commons.delete_success'));
});
},
refresh() {
this.condition = {components: TEST_CASE_CONFIGS};
// this.selectIds.clear();
this.selectRows.clear();
this.$emit('refresh');
},
showDetail(row, event, column) {
this.$emit('testCaseDetail', row);
},
handleSelectAll(selection) {
if (selection.length > 0) {
if (selection.length === 1) {
this.selectRows.add(selection[0]);
} else {
this.tableData.forEach(item => {
this.$set(item, "showMore", true);
this.selectRows.add(item);
});
}
} else {
this.selectRows.clear();
this.tableData.forEach(row => {
this.$set(row, "showMore", false);
})
}
},
handleSelectionChange(selection, row) {
// if (this.selectIds.has(row.id)) {
// this.selectIds.delete(row.id);
// } else {
// this.selectIds.add(row.id);
// }
if (this.selectRows.has(row)) {
this.$set(row, "showMore", false);
this.selectRows.delete(row);
} else {
this.$set(row, "showMore", true);
this.selectRows.add(row);
}
let arr = Array.from(this.selectRows);
// 1
if (this.selectRows.size === 1) {
this.$set(arr[0], "showMore", false);
} else if (this.selectRows.size === 2) {
arr.forEach(row => {
this.$set(row, "showMore", true);
})
}
},
importTestCase() {
this.$refs.testCaseImport.open();
},
exportTestCase() {
let ids = Array.from(this.selectRows).map(row => row.id);
let config = {
url: '/test/case/export/testcase',
method: 'post',
responseType: 'blob',
// data: {ids: [...this.selectIds]}
data: {ids: ids}
};
this.result = this.$request(config).then(response => {
const filename = this.$t('test_track.case.test_case') + ".xlsx";
const blob = new Blob([response.data]);
if ("download" in document.createElement("a")) {
let aTag = document.createElement('a');
aTag.download = filename;
aTag.href = URL.createObjectURL(blob);
aTag.click();
URL.revokeObjectURL(aTag.href)
} else {
navigator.msSaveBlob(blob, filename);
}
});
},
handleBatch(type) {
if (this.selectRows.size < 1) {
this.$warning(this.$t('test_track.plan_view.select_manipulate'));
return;
}
if (type === 'move') {
let ids = Array.from(this.selectRows).map(row => row.id);
this.$emit('moveToNode', ids);
} else if (type === 'delete') {
this.handleDeleteBatch();
} else {
this.exportTestCase();
}
},
batchEdit(form) {
let sign = false;
let arr = Array.from(this.selectRows);
//
if (form.type === 'method' && form.value === 'auto') {
arr.forEach(row => {
if (row.type === 'functional') {
sign = true;
return;
}
});
}
if (form.type === 'type' && form.value === 'functional') {
arr.forEach(row => {
if (row.method === 'auto') {
sign = true;
return;
}
});
}
let ids = arr.map(row => row.id);
let param = {};
param[form.type] = form.value;
param.ids = ids;
if (!sign) {
this.$post('/test/case/batch/edit' , param, () => {
this.$success(this.$t('commons.save_success'));
this.refresh();
});
} else {
this.$warning("功能测试的测试方式不能设置为自动!");
}
},
filter(filters) {
_filter(filters, this.condition);
this.initTableData();
},
sort(column) {
//
if (this.condition.orders) {
this.condition.orders = [];
}
_sort(column, this.condition);
this.initTableData();
},
handleBatchEdit() {
this.getMaintainerOptions();
this.$refs.batchEdit.open();
},
handleBatchMove() {
this.$emit("batchMove", Array.from(this.selectRows).map(row => row.id));
},
getMaintainerOptions() {
let workspaceId = localStorage.getItem(WORKSPACE_ID);
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
this.valueArr.maintainer = response.data;
});
maintainer: [],
}
}
},
props: {
currentProject: {
type: Object
},
selectNodeIds: {
type: Array
},
selectParentNodes: {
type: Array
}
},
created: function () {
this.initTableData();
},
watch: {
currentProject() {
this.initTableData();
},
selectNodeIds() {
this.initTableData();
}
},
methods: {
initTableData() {
if (this.planId) {
// param.planId = this.planId;
this.condition.planId = this.planId;
}
if (this.selectNodeIds && this.selectNodeIds.length > 0) {
// param.nodeIds = this.selectNodeIds;
this.condition.nodeIds = this.selectNodeIds;
}
if (this.currentProject) {
this.condition.projectId = this.currentProject.id;
this.result = this.$post(this.buildPagePath('/test/case/list'), this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
// this.selectIds.clear();
this.selectRows.clear();
});
}
},
search() {
this.initTableData();
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
testCaseCreate() {
this.$emit('testCaseEdit');
},
handleEdit(testCase) {
this.$emit('testCaseEdit', testCase);
},
handleCopy(testCase) {
this.$emit('testCaseCopy', testCase);
},
handleDelete(testCase) {
this.$alert(this.$t('test_track.case.delete_confirm') + '\'' + testCase.name + '\'' + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this._handleDelete(testCase);
}
}
});
},
handleDeleteBatch() {
this.$alert(this.$t('test_track.case.delete_confirm') + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let ids = Array.from(this.selectRows).map(row => row.id);
this.$post('/test/case/batch/delete', {ids: ids}, () => {
this.selectRows.clear();
this.$emit("refresh");
this.$success(this.$t('commons.delete_success'));
// 广 head
TrackEvent.$emit(LIST_CHANGE);
});
}
}
});
},
_handleDelete(testCase) {
let testCaseId = testCase.id;
this.$post('/test/case/delete/' + testCaseId, {}, () => {
this.initTableData();
this.$success(this.$t('commons.delete_success'));
// 广 head
TrackEvent.$emit(LIST_CHANGE);
});
},
refresh() {
this.condition = {components: TEST_CASE_CONFIGS};
// this.selectIds.clear();
this.selectRows.clear();
this.$emit('refresh');
},
showDetail(row, event, column) {
this.$emit('testCaseDetail', row);
},
handleSelectAll(selection) {
if (selection.length > 0) {
if (selection.length === 1) {
this.selectRows.add(selection[0]);
} else {
this.tableData.forEach(item => {
this.$set(item, "showMore", true);
this.selectRows.add(item);
});
}
} else {
this.selectRows.clear();
this.tableData.forEach(row => {
this.$set(row, "showMore", false);
})
}
},
handleSelectionChange(selection, row) {
// if (this.selectIds.has(row.id)) {
// this.selectIds.delete(row.id);
// } else {
// this.selectIds.add(row.id);
// }
if (this.selectRows.has(row)) {
this.$set(row, "showMore", false);
this.selectRows.delete(row);
} else {
this.$set(row, "showMore", true);
this.selectRows.add(row);
}
let arr = Array.from(this.selectRows);
// 1
if (this.selectRows.size === 1) {
this.$set(arr[0], "showMore", false);
} else if (this.selectRows.size === 2) {
arr.forEach(row => {
this.$set(row, "showMore", true);
})
}
},
importTestCase() {
this.$refs.testCaseImport.open();
},
exportTestCase() {
let ids = Array.from(this.selectRows).map(row => row.id);
let config = {
url: '/test/case/export/testcase',
method: 'post',
responseType: 'blob',
// data: {ids: [...this.selectIds]}
data: {ids: ids}
};
this.result = this.$request(config).then(response => {
const filename = this.$t('test_track.case.test_case') + ".xlsx";
const blob = new Blob([response.data]);
if ("download" in document.createElement("a")) {
let aTag = document.createElement('a');
aTag.download = filename;
aTag.href = URL.createObjectURL(blob);
aTag.click();
URL.revokeObjectURL(aTag.href)
} else {
navigator.msSaveBlob(blob, filename);
}
});
},
handleBatch(type) {
if (this.selectRows.size < 1) {
this.$warning(this.$t('test_track.plan_view.select_manipulate'));
return;
}
if (type === 'move') {
let ids = Array.from(this.selectRows).map(row => row.id);
this.$emit('moveToNode', ids);
} else if (type === 'delete') {
this.handleDeleteBatch();
} else {
this.exportTestCase();
}
},
batchEdit(form) {
let sign = false;
let arr = Array.from(this.selectRows);
//
if (form.type === 'method' && form.value === 'auto') {
arr.forEach(row => {
if (row.type === 'functional') {
sign = true;
return;
}
});
}
if (form.type === 'type' && form.value === 'functional') {
arr.forEach(row => {
if (row.method === 'auto') {
sign = true;
return;
}
});
}
let ids = arr.map(row => row.id);
let param = {};
param[form.type] = form.value;
param.ids = ids;
if (!sign) {
this.$post('/test/case/batch/edit', param, () => {
this.$success(this.$t('commons.save_success'));
this.refresh();
});
} else {
this.$warning("功能测试的测试方式不能设置为自动!");
}
},
filter(filters) {
_filter(filters, this.condition);
this.initTableData();
},
sort(column) {
//
if (this.condition.orders) {
this.condition.orders = [];
}
_sort(column, this.condition);
this.initTableData();
},
handleBatchEdit() {
this.getMaintainerOptions();
this.$refs.batchEdit.open();
},
handleBatchMove() {
this.$emit("batchMove", Array.from(this.selectRows).map(row => row.id));
},
getMaintainerOptions() {
let workspaceId = localStorage.getItem(WORKSPACE_ID);
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
this.valueArr.maintainer = response.data;
});
}
}
}
</script>
<style scoped>
.table-page {
padding-top: 20px;
margin-right: -9px;
float: right;
}
.table-page {
padding-top: 20px;
margin-right: -9px;
float: right;
}
.operate-button {
float: right;
}
.operate-button {
float: right;
}
.operate-button > div {
display: inline-block;
margin-left: 10px;
}
.operate-button > div {
display: inline-block;
margin-left: 10px;
}
.search {
margin-left: 10px;
width: 240px;
}
.search {
margin-left: 10px;
width: 240px;
}
.el-table {
cursor: pointer;
}
.el-table {
cursor: pointer;
}
</style>

View File

@ -10,31 +10,34 @@
</el-menu-item>
<el-submenu :class="{'deactivation':!isProjectActivation}"
v-permission="['test_manager','test_user','test_viewer']" index="3" popper-class="submenu">
<template v-slot:title>{{$t('commons.project')}}</template>
<ms-recent-list :options="projectRecent"/>
<template v-slot:title>{{ $t('commons.project') }}</template>
<ms-recent-list ref="projectRecent" :options="projectRecent"/>
<el-divider/>
<ms-show-all :index="'/track/project/all'"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/project/create'" :title="$t('project.create')"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/project/create'"
:title="$t('project.create')"/>
</el-submenu>
<el-submenu v-permission="['test_manager','test_user','test_viewer']"
index="6" popper-class="submenu">
<template v-slot:title>{{$t('test_track.case.test_case')}}</template>
<ms-recent-list :options="caseRecent"/>
<template v-slot:title>{{ $t('test_track.case.test_case') }}</template>
<ms-recent-list ref="caseRecent" :options="caseRecent"/>
<el-divider/>
<ms-show-all :index="'/track/case/all'"/>
<el-menu-item :index="testCaseEditPath" class="blank_item"></el-menu-item>
<el-menu-item :index="testCaseProjectPath" class="blank_item"></el-menu-item>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/case/create'" :title="$t('test_track.case.create_case')"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/case/create'"
:title="$t('test_track.case.create_case')"/>
</el-submenu>
<el-submenu v-permission="['test_manager','test_user','test_viewer']" index="7" popper-class="submenu">
<template v-slot:title>{{$t('test_track.plan.test_plan')}}</template>
<ms-recent-list :options="planRecent"/>
<template v-slot:title>{{ $t('test_track.plan.test_plan') }}</template>
<ms-recent-list ref="planRecent" :options="planRecent"/>
<el-divider/>
<ms-show-all :index="'/track/plan/all'"/>
<el-menu-item :index="testPlanViewPath" class="blank_item"></el-menu-item>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/plan/create'" :title="$t('test_track.plan.create_plan')"/>
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/plan/create'"
:title="$t('test_track.plan.create_plan')"/>
</el-submenu>
</el-menu>
</el-col>
@ -45,107 +48,115 @@
</template>
<script>
import MsShowAll from "../../common/head/ShowAll";
import MsRecentList from "../../common/head/RecentList";
import MsCreateButton from "../../common/head/CreateButton";
import MsShowAll from "../../common/head/ShowAll";
import MsRecentList from "../../common/head/RecentList";
import MsCreateButton from "../../common/head/CreateButton";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
export default {
name: "TrackHeaderMenus",
components: {MsShowAll, MsRecentList, MsCreateButton},
data() {
return {
testPlanViewPath: '',
isRouterAlive: true,
testCaseEditPath: '',
testCaseProjectPath: '',
isProjectActivation: true,
projectRecent: {
title: this.$t('project.recent'),
url: "/project/recent/5",
index: function (item) {
return '/track/case/' + item.id;
},
router: function (item) {
return {name: 'testCase', params: {projectId: item.id, projectName: item.name}}
}
export default {
name: "TrackHeaderMenus",
components: {MsShowAll, MsRecentList, MsCreateButton},
data() {
return {
testPlanViewPath: '',
isRouterAlive: true,
testCaseEditPath: '',
testCaseProjectPath: '',
isProjectActivation: true,
projectRecent: {
title: this.$t('project.recent'),
url: "/project/recent/5",
index: function (item) {
return '/track/case/' + item.id;
},
caseRecent: {
title: this.$t('test_track.recent_case'),
url: "/test/case/recent/5",
index: function (item) {
return '/track/case/edit/' + item.id;
},
router: function (item) {}
},
planRecent: {
title: this.$t('test_track.recent_plan'),
url: "/test/plan/recent/5",
index: function (item) {
return '/track/plan/view/' + item.id;
},
router: function (item) {}
router: function (item) {
return {name: 'testCase', params: {projectId: item.id, projectName: item.name}}
}
}
},
watch: {
'$route'(to) {
this.init();
}
},
mounted() {
this.init();
},
methods: {
reload() {
this.isRouterAlive = false;
this.$nextTick(function () {
this.isRouterAlive = true;
});
},
init() {
let path = this.$route.path;
if (path.indexOf("/track/case") >= 0 && !!this.$route.params.projectId) {
this.testCaseProjectPath = path;
//
this.isProjectActivation = false;
this.reload();
} else {
this.isProjectActivation = true;
caseRecent: {
title: this.$t('test_track.recent_case'),
url: "/test/case/recent/5",
index: function (item) {
return '/track/case/edit/' + item.id;
},
router: function (item) {
}
if (path.indexOf("/track/plan/view") >= 0) {
this.testPlanViewPath = path;
this.reload();
}
if (path.indexOf("/track/case/edit") >= 0) {
this.testCaseEditPath = path;
this.reload();
},
planRecent: {
title: this.$t('test_track.recent_plan'),
url: "/test/plan/recent/5",
index: function (item) {
return '/track/plan/view/' + item.id;
},
router: function (item) {
}
}
}
},
watch: {
'$route'(to) {
this.init();
}
},
mounted() {
this.init();
TrackEvent.$on(LIST_CHANGE, () => {
this.$refs.projectRecent.recent();
this.$refs.planRecent.recent();
this.$refs.caseRecent.recent();
});
},
methods: {
reload() {
this.isRouterAlive = false;
this.$nextTick(function () {
this.isRouterAlive = true;
});
},
init() {
let path = this.$route.path;
if (path.indexOf("/track/case") >= 0 && !!this.$route.params.projectId) {
this.testCaseProjectPath = path;
//
this.isProjectActivation = false;
this.reload();
} else {
this.isProjectActivation = true;
}
if (path.indexOf("/track/plan/view") >= 0) {
this.testPlanViewPath = path;
this.reload();
}
if (path.indexOf("/track/case/edit") >= 0) {
this.testCaseEditPath = path;
this.reload();
}
}
}
}
</script>
<style scoped>
.el-divider--horizontal {
margin: 0;
}
.el-divider--horizontal {
margin: 0;
}
#menu-bar {
border-bottom: 1px solid #E6E6E6;
background-color: #FFF;
}
#menu-bar {
border-bottom: 1px solid #E6E6E6;
background-color: #FFF;
}
.blank_item {
display: none;
}
.blank_item {
display: none;
}
.el-divider--horizontal {
margin: 0;
}
.el-divider--horizontal {
margin: 0;
}
.deactivation >>> .el-submenu__title {
border-bottom: white !important;
}
.deactivation >>> .el-submenu__title {
border-bottom: white !important;
}
</style>

View File

@ -38,13 +38,15 @@
</span>
<el-dropdown-menu slot="dropdown" chang>
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Prepare'}">
{{$t('test_track.plan.plan_status_prepare')}}
{{ $t('test_track.plan.plan_status_prepare') }}
</el-dropdown-item>
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Underway'}">
{{$t('test_track.plan.plan_status_running')}}
<el-dropdown-item :disabled="!isTestManagerOrTestUser"
:command="{id: scope.row.id, status: 'Underway'}">
{{ $t('test_track.plan.plan_status_running') }}
</el-dropdown-item>
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Completed'}">
{{$t('test_track.plan.plan_status_completed')}}
<el-dropdown-item :disabled="!isTestManagerOrTestUser"
:command="{id: scope.row.id, status: 'Completed'}">
{{ $t('test_track.plan.plan_status_completed') }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
@ -87,10 +89,15 @@
<el-table-column
:label="$t('commons.operating')">
<template v-slot:default="scope">
<ms-table-operator :is-tester-permission="true" @editClick="handleEdit(scope.row)" @deleteClick="handleDelete(scope.row)">
<ms-table-operator :is-tester-permission="true" @editClick="handleEdit(scope.row)"
@deleteClick="handleDelete(scope.row)">
<template v-slot:middle>
<ms-table-operator-button type="success" v-if="!scope.row.reportId" :tip="$t('test_track.plan_view.create_report')" icon="el-icon-document" @exec="openTestReportTemplate(scope.row)"/>
<ms-table-operator-button type="success" v-if="scope.row.reportId" :tip="$t('test_track.plan_view.view_report')" icon="el-icon-document" @exec="openReport(scope.row.id, scope.row.reportId)"/>
<ms-table-operator-button :isTesterPermission="true" type="success" v-if="!scope.row.reportId"
:tip="$t('test_track.plan_view.create_report')" icon="el-icon-document"
@exec="openTestReportTemplate(scope.row)"/>
<ms-table-operator-button type="success" v-if="scope.row.reportId"
:tip="$t('test_track.plan_view.view_report')" icon="el-icon-document"
@exec="openReport(scope.row.id, scope.row.reportId)"/>
</template>
</ms-table-operator>
</template>
@ -108,143 +115,147 @@
</template>
<script>
import MsCreateBox from '../../../settings/CreateBox';
import MsTablePagination from '../../../../components/common/pagination/TablePagination';
import MsTableHeader from "../../../common/components/MsTableHeader";
import MsDialogFooter from "../../../common/components/MsDialogFooter";
import MsTableOperatorButton from "../../../common/components/MsTableOperatorButton";
import MsTableOperator from "../../../common/components/MsTableOperator";
import PlanStatusTableItem from "../../common/tableItems/plan/PlanStatusTableItem";
import PlanStageTableItem from "../../common/tableItems/plan/PlanStageTableItem";
import {_filter, _sort, checkoutTestManagerOrTestUser} from "../../../../../common/js/utils";
import TestReportTemplateList from "../view/comonents/TestReportTemplateList";
import TestCaseReportView from "../view/comonents/report/TestCaseReportView";
import MsDeleteConfirm from "../../../common/components/MsDeleteConfirm";
import {TEST_PLAN_CONFIGS} from "../../../common/components/search/search-components";
import MsCreateBox from '../../../settings/CreateBox';
import MsTablePagination from '../../../../components/common/pagination/TablePagination';
import MsTableHeader from "../../../common/components/MsTableHeader";
import MsDialogFooter from "../../../common/components/MsDialogFooter";
import MsTableOperatorButton from "../../../common/components/MsTableOperatorButton";
import MsTableOperator from "../../../common/components/MsTableOperator";
import PlanStatusTableItem from "../../common/tableItems/plan/PlanStatusTableItem";
import PlanStageTableItem from "../../common/tableItems/plan/PlanStageTableItem";
import {_filter, _sort, checkoutTestManagerOrTestUser} from "../../../../../common/js/utils";
import TestReportTemplateList from "../view/comonents/TestReportTemplateList";
import TestCaseReportView from "../view/comonents/report/TestCaseReportView";
import MsDeleteConfirm from "../../../common/components/MsDeleteConfirm";
import {TEST_PLAN_CONFIGS} from "../../../common/components/search/search-components";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
export default {
name: "TestPlanList",
components: {
MsDeleteConfirm,
TestCaseReportView,
TestReportTemplateList,
PlanStageTableItem,
PlanStatusTableItem,
MsTableOperator, MsTableOperatorButton, MsDialogFooter, MsTableHeader, MsCreateBox, MsTablePagination},
data() {
return {
result: {},
queryPath: "/test/plan/list",
deletePath: "/test/plan/delete",
condition: {
components: TEST_PLAN_CONFIGS
},
currentPage: 1,
pageSize: 10,
isTestManagerOrTestUser: false,
total: 0,
tableData: [],
statusFilters: [
{text: this.$t('test_track.plan.plan_status_prepare'), value: 'Prepare'},
{text: this.$t('test_track.plan.plan_status_running'), value: 'Underway'},
{text: this.$t('test_track.plan.plan_status_completed'), value: 'Completed'}
],
stageFilters: [
{text: this.$t('test_track.plan.smoke_test'), value: 'smoke'},
{text: this.$t('test_track.plan.system_test'), value: 'system'},
{text: this.$t('test_track.plan.regression_test'), value: 'regression'},
],
}
export default {
name: "TestPlanList",
components: {
MsDeleteConfirm,
TestCaseReportView,
TestReportTemplateList,
PlanStageTableItem,
PlanStatusTableItem,
MsTableOperator, MsTableOperatorButton, MsDialogFooter, MsTableHeader, MsCreateBox, MsTablePagination
},
data() {
return {
result: {},
queryPath: "/test/plan/list",
deletePath: "/test/plan/delete",
condition: {
components: TEST_PLAN_CONFIGS
},
watch: {
'$route'(to, from) {
if (to.path.indexOf("/track/plan/all") >= 0){
this.initTableData();
}
}
},
created() {
this.projectId = this.$route.params.projectId;
this.isTestManagerOrTestUser = checkoutTestManagerOrTestUser();
currentPage: 1,
pageSize: 10,
isTestManagerOrTestUser: false,
total: 0,
tableData: [],
statusFilters: [
{text: this.$t('test_track.plan.plan_status_prepare'), value: 'Prepare'},
{text: this.$t('test_track.plan.plan_status_running'), value: 'Underway'},
{text: this.$t('test_track.plan.plan_status_completed'), value: 'Completed'}
],
stageFilters: [
{text: this.$t('test_track.plan.smoke_test'), value: 'smoke'},
{text: this.$t('test_track.plan.system_test'), value: 'system'},
{text: this.$t('test_track.plan.regression_test'), value: 'regression'},
],
}
},
watch: {
'$route'(to, from) {
if (to.path.indexOf("/track/plan/all") >= 0) {
this.initTableData();
},
methods: {
initTableData() {
if (this.planId) {
// param.planId = this.planId;
this.condition.planId = this.planId;
}
if (this.selectNodeIds && this.selectNodeIds.length > 0) {
// param.nodeIds = this.selectNodeIds;
this.condition.nodeIds = this.selectNodeIds;
}
this.result = this.$post(this.buildPagePath(this.queryPath), this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
testPlanCreate() {
this.$emit('openTestPlanEditDialog');
},
handleEdit(testPlan) {
this.$emit('testPlanEdit', testPlan);
},
statusChange(param) {
this.$post('/test/plan/edit' , param, () => {
for (let i = 0; i < this.tableData.length; i++) {
if (this.tableData[i].id == param.id) {
this.tableData[i].status = param.status;
break;
}
}
});
},
handleDelete(testPlan) {
this.$refs.deleteConfirm.open(testPlan);
},
_handleDelete(testPlan) {
let testPlanId = testPlan.id;
this.$post('/test/plan/delete/' + testPlanId, {}, () => {
this.initTableData();
this.$success(this.$t('commons.delete_success'));
});
},
intoPlan(row, event, column) {
this.$router.push('/track/plan/view/' + row.id);
},
filter(filters) {
_filter(filters, this.condition);
this.initTableData();
},
sort(column) {
_sort(column, this.condition);
this.initTableData();
},
openTestReportTemplate(data) {
this.$refs.testReportTemplateList.open(data.id);
},
openReport(planId, reportId) {
if (reportId) {
this.$refs.testCaseReportView.open(planId, reportId);
}
},
}
}
},
created() {
this.projectId = this.$route.params.projectId;
this.isTestManagerOrTestUser = checkoutTestManagerOrTestUser();
this.initTableData();
},
methods: {
initTableData() {
if (this.planId) {
// param.planId = this.planId;
this.condition.planId = this.planId;
}
if (this.selectNodeIds && this.selectNodeIds.length > 0) {
// param.nodeIds = this.selectNodeIds;
this.condition.nodeIds = this.selectNodeIds;
}
this.result = this.$post(this.buildPagePath(this.queryPath), this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
testPlanCreate() {
this.$emit('openTestPlanEditDialog');
},
handleEdit(testPlan) {
this.$emit('testPlanEdit', testPlan);
},
statusChange(param) {
this.$post('/test/plan/edit', param, () => {
for (let i = 0; i < this.tableData.length; i++) {
if (this.tableData[i].id == param.id) {
this.tableData[i].status = param.status;
break;
}
}
});
},
handleDelete(testPlan) {
this.$refs.deleteConfirm.open(testPlan);
},
_handleDelete(testPlan) {
let testPlanId = testPlan.id;
this.$post('/test/plan/delete/' + testPlanId, {}, () => {
this.initTableData();
this.$success(this.$t('commons.delete_success'));
// 广 head
TrackEvent.$emit(LIST_CHANGE);
});
},
intoPlan(row, event, column) {
this.$router.push('/track/plan/view/' + row.id);
},
filter(filters) {
_filter(filters, this.condition);
this.initTableData();
},
sort(column) {
_sort(column, this.condition);
this.initTableData();
},
openTestReportTemplate(data) {
this.$refs.testReportTemplateList.open(data.id);
},
openReport(planId, reportId) {
if (reportId) {
this.$refs.testCaseReportView.open(planId, reportId);
}
},
}
}
</script>
<style scoped>
.table-page {
padding-top: 20px;
margin-right: -9px;
float: right;
}
.table-page {
padding-top: 20px;
margin-right: -9px;
float: right;
}
.el-table {
cursor:pointer;
}
.el-table {
cursor: pointer;
}
</style>

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,28 @@
<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')">
<template v-slot:default="scope">
<el-tooltip content="关闭缺陷"
<el-popover
placement="left"
width="400"
trigger="hover"
>
<ckeditor :editor="editor" disabled
v-model="scope.row.description"/>
<!-- <span v-html="scope.row.description"/>-->
<!-- <span slot="reference">{{scope.row.description}}</span>-->
<el-button slot="reference" type="text">预览</el-button>
</el-popover>
</template>
</el-table-column>
<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="$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 +487,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 +508,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,24 @@
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')">
<template v-slot:default="scope">
<el-popover
placement="left"
width="400"
trigger="hover"
>
<ckeditor :editor="editor" disabled
v-model="scope.row.description"/>
<!-- <span v-html="scope.row.description"/>-->
<!-- <span slot="reference">{{scope.row.description}}</span>-->
<el-button slot="reference" type="text">预览</el-button>
</el-popover>
</template>
</el-table-column>
<!-- <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 +209,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>
@ -222,6 +236,7 @@
import {TEST_CASE_CONFIGS} from "../../../../common/components/search/search-components";
import ShowMoreBtn from "../../../case/components/ShowMoreBtn";
import BatchEdit from "../../../case/components/BatchEdit";
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
export default {
name: "TestPlanTestCaseList",
@ -280,15 +295,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: [],
@ -298,7 +313,8 @@
{name: this.$t('test_track.plan_view.blocking'), id: 'Blocking'},
{name: this.$t('test_track.plan_view.skip'), id: 'Skip'}
]
}
},
editor: ClassicEditor,
}
},
props: {

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

View File

@ -107,6 +107,7 @@ export default {
please_save: 'Please save first',
formatErr: 'Format Error',
id: 'ID',
millisecond: 'ms',
please_upload: 'Please upload file',
reference_documentation: "Reference documentation",
date: {
@ -213,6 +214,8 @@ export default {
owning_workspace: 'Owning Workspace',
please_choose_workspace: 'Please select Workspace',
special_characters_are_not_supported: 'Incorrect format (special characters are not supported and cannot end with \'-\')',
tapd_id: 'TAPD Project ID',
jira_key: 'JIRA Project key',
},
member: {
create: 'Create',
@ -397,7 +400,9 @@ export default {
headers: "Headers",
kv_description: "Variables are available for all requests",
copy: "Copy scenario",
delete: "Delete scenario"
delete: "Delete scenario",
disable: "Disable",
enable: "Enable"
},
request: {
copy: "Copy request",
@ -433,6 +438,9 @@ export default {
body: "Body",
body_kv: "Key Value",
body_text: "Raw",
timeout_config: "Timeout Config",
connect_timeout: "Connect Timeout",
response_timeout: "Response Timeout",
assertions: {
label: "Assertion",
text: "Text",
@ -587,6 +595,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",
@ -697,6 +715,20 @@ export default {
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

@ -109,6 +109,7 @@ export default {
please_save: '请先保存',
reference_documentation: "参考文档",
id: 'ID',
millisecond: '毫秒',
date: {
select_date: '选择日期',
start_date: '开始日期',
@ -213,6 +214,8 @@ export default {
owning_workspace: '所属工作空间',
please_choose_workspace: '请选择工作空间',
special_characters_are_not_supported: '格式错误(不支持特殊字符,且不能以\'-\'开头结尾)',
tapd_id: 'TAPD项目ID',
jira_key: 'JIRA项目key',
},
member: {
create: '添加成员',
@ -397,7 +400,9 @@ export default {
headers: "请求头",
kv_description: "所有请求可以使用自定义变量",
copy: "复制场景",
delete: "删除场景"
delete: "删除场景",
disable: "禁用",
enable: "启用"
},
request: {
copy: "复制请求",
@ -434,6 +439,9 @@ export default {
body: "请求内容",
body_kv: "键值对",
body_text: "文本",
timeout_config: "超时设置",
connect_timeout: "连接超时",
response_timeout: "响应超时",
assertions: {
label: "断言",
text: "文本",
@ -590,6 +598,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: "导入测试用例",
@ -700,6 +718,20 @@ export default {
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

@ -105,6 +105,7 @@ export default {
formatErr: '格式錯誤',
please_save: '請先保存',
id: 'ID',
millisecond: '毫秒',
reference_documentation: "參考文檔",
please_upload: '請上傳文件',
date: {
@ -211,6 +212,8 @@ export default {
owning_workspace: '所屬工作空間',
please_choose_workspace: '請選擇工作空間',
special_characters_are_not_supported: '格式錯誤(不支持特殊字符,且不能以\'-\'開頭結尾)',
tapd_id: 'TAPD項目ID',
jira_key: 'JIRA項目key',
},
member: {
create: '添加成員',
@ -396,7 +399,9 @@ export default {
headers: "請求頭",
kv_description: "所有請求可以使用自定義變數",
copy: "複製場景",
delete: "删除場景"
delete: "删除場景",
disable: "禁用",
enable: "啟用"
},
request: {
copy: "複製請求",
@ -433,6 +438,9 @@ export default {
body: "請求內容",
body_kv: "鍵值對",
body_text: "文字",
timeout_config: "超時設置",
connect_timeout: "連接超時",
response_timeout: "響應超時",
assertions: {
label: "斷言",
text: "文字",
@ -587,6 +595,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: "導入測試用例",
@ -697,6 +715,20 @@ export default {
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: {