This commit is contained in:
chenjianxing 2020-10-27 23:47:00 +08:00
commit c63327397a
27 changed files with 1257 additions and 636 deletions

View File

@ -0,0 +1,17 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.track.dto.TestCaseCommentDTO;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface ExtTestCaseCommentMapper {
/**
* 获取用例的评论
* @param caseId
* @return
*/
List<TestCaseCommentDTO> getCaseComments(@Param("caseId") String caseId);
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="io.metersphere.base.mapper.ext.ExtTestCaseCommentMapper">
<select id="getCaseComments" resultType="io.metersphere.track.dto.TestCaseCommentDTO"
parameterType="java.lang.String">
select *, user.name as authorName from test_case_comment, user
where test_case_comment.author = user.id and case_id = #{caseId}
order by test_case_comment.update_time desc
</select>
</mapper>

View File

@ -0,0 +1,84 @@
package io.metersphere.ldap.service;
import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class CustomSSLSocketFactory extends SSLSocketFactory {
private SSLSocketFactory socketFactory;
public CustomSSLSocketFactory() {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]{new DummyTrustmanager()}, new SecureRandom());
socketFactory = ctx.getSocketFactory();
} catch (Exception ex) {
ex.printStackTrace(System.err);
}
}
public static SocketFactory getDefault() {
return new CustomSSLSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return socketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return socketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket socket, String string, int num, boolean bool) throws IOException {
return socketFactory.createSocket(socket, string, num, bool);
}
@Override
public Socket createSocket(String string, int num) throws IOException, UnknownHostException {
return socketFactory.createSocket(string, num);
}
@Override
public Socket createSocket(String string, int num, InetAddress netAdd, int i) throws IOException, UnknownHostException {
return socketFactory.createSocket(string, num, netAdd, i);
}
@Override
public Socket createSocket(InetAddress netAdd, int num) throws IOException {
return socketFactory.createSocket(netAdd, num);
}
@Override
public Socket createSocket(InetAddress netAdd1, int num, InetAddress netAdd2, int i) throws IOException {
return socketFactory.createSocket(netAdd1, num, netAdd2, i);
}
/**
* 证书
*/
public static class DummyTrustmanager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] cert, String string) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] cert, String string) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[0];
}
}
}

View File

@ -146,8 +146,17 @@ public class LdapService {
preConnect(url, dn, password); preConnect(url, dn, password);
String credentials = EncryptUtils.aesDecrypt(password).toString(); String credentials = EncryptUtils.aesDecrypt(password).toString();
LdapContextSource sourceLdapCtx;
LdapContextSource sourceLdapCtx = new LdapContextSource(); if (StringUtils.startsWith(url, "ldaps://")) {
sourceLdapCtx = new SSLLdapContextSource();
// todo 这里加上strategy 会报错
// DefaultTlsDirContextAuthenticationStrategy strategy = new DefaultTlsDirContextAuthenticationStrategy();
// strategy.setShutdownTlsGracefully(true);
// strategy.setHostnameVerifier((hostname, session) -> true);
// sourceLdapCtx.setAuthenticationStrategy(strategy);
} else {
sourceLdapCtx = new LdapContextSource();
}
sourceLdapCtx.setUrl(url); sourceLdapCtx.setUrl(url);
sourceLdapCtx.setUserDn(dn); sourceLdapCtx.setUserDn(dn);
sourceLdapCtx.setPassword(credentials); sourceLdapCtx.setPassword(credentials);

View File

@ -0,0 +1,16 @@
package io.metersphere.ldap.service;
import org.springframework.ldap.core.support.LdapContextSource;
import javax.naming.Context;
import java.util.Hashtable;
public class SSLLdapContextSource extends LdapContextSource {
public Hashtable<String, Object> getAnonymousEnv() {
Hashtable<String, Object> anonymousEnv = super.getAnonymousEnv();
anonymousEnv.put("java.naming.security.protocol", "ssl");
anonymousEnv.put("java.naming.ldap.factory.socket", CustomSSLSocketFactory.class.getName());
anonymousEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
return anonymousEnv;
}
}

View File

@ -1,6 +1,6 @@
package io.metersphere.track.controller; package io.metersphere.track.controller;
import io.metersphere.base.domain.TestCaseComment; import io.metersphere.track.dto.TestCaseCommentDTO;
import io.metersphere.track.request.testreview.SaveCommentRequest; import io.metersphere.track.request.testreview.SaveCommentRequest;
import io.metersphere.track.service.TestCaseCommentService; import io.metersphere.track.service.TestCaseCommentService;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -13,7 +13,7 @@ import java.util.List;
public class TestCaseCommentController { public class TestCaseCommentController {
@Resource @Resource
TestCaseCommentService testCaseCommentService; private TestCaseCommentService testCaseCommentService;
@PostMapping("/save") @PostMapping("/save")
public void saveComment(@RequestBody SaveCommentRequest request) { public void saveComment(@RequestBody SaveCommentRequest request) {
@ -21,7 +21,17 @@ public class TestCaseCommentController {
} }
@GetMapping("/list/{caseId}") @GetMapping("/list/{caseId}")
public List<TestCaseComment> getComments(@PathVariable String caseId) { public List<TestCaseCommentDTO> getCaseComments(@PathVariable String caseId) {
return testCaseCommentService.getComments(caseId); return testCaseCommentService.getCaseComments(caseId);
}
@GetMapping("/delete/{commentId}")
public void deleteComment(@PathVariable String commentId) {
testCaseCommentService.delete(commentId);
}
@PostMapping("/edit")
public void editComment(@RequestBody SaveCommentRequest request) {
testCaseCommentService.edit(request);
} }
} }

View File

@ -106,7 +106,7 @@ public class TestCaseReviewController {
} }
@PostMapping("/get/{reviewId}") @GetMapping("/get/{reviewId}")
public TestCaseReview getTestReview(@PathVariable String reviewId) { public TestCaseReview getTestReview(@PathVariable String reviewId) {
checkOwnerService.checkTestReviewOwner(reviewId); checkOwnerService.checkTestReviewOwner(reviewId);
return testCaseReviewService.getTestReview(reviewId); return testCaseReviewService.getTestReview(reviewId);

View File

@ -0,0 +1,9 @@
package io.metersphere.track.dto;
import io.metersphere.base.domain.TestCaseComment;
import lombok.Data;
@Data
public class TestCaseCommentDTO extends TestCaseComment {
private String authorName;
}

View File

@ -3,21 +3,23 @@ package io.metersphere.track.service;
import io.metersphere.base.domain.TestCaseComment; import io.metersphere.base.domain.TestCaseComment;
import io.metersphere.base.domain.TestCaseCommentExample; import io.metersphere.base.domain.TestCaseCommentExample;
import io.metersphere.base.domain.TestCaseWithBLOBs; import io.metersphere.base.domain.TestCaseWithBLOBs;
import io.metersphere.base.domain.User;
import io.metersphere.base.mapper.TestCaseCommentMapper; import io.metersphere.base.mapper.TestCaseCommentMapper;
import io.metersphere.base.mapper.TestCaseMapper; import io.metersphere.base.mapper.TestCaseMapper;
import io.metersphere.base.mapper.TestCaseReviewMapper; import io.metersphere.base.mapper.ext.ExtTestCaseCommentMapper;
import io.metersphere.base.mapper.UserMapper;
import io.metersphere.commons.constants.NoticeConstants; import io.metersphere.commons.constants.NoticeConstants;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.i18n.Translator;
import io.metersphere.notice.domain.MessageDetail; import io.metersphere.notice.domain.MessageDetail;
import io.metersphere.notice.domain.MessageSettingDetail; import io.metersphere.notice.domain.MessageSettingDetail;
import io.metersphere.notice.service.DingTaskService; import io.metersphere.notice.service.DingTaskService;
import io.metersphere.notice.service.MailService; import io.metersphere.notice.service.MailService;
import io.metersphere.notice.service.NoticeService; import io.metersphere.notice.service.NoticeService;
import io.metersphere.notice.service.WxChatTaskService; import io.metersphere.notice.service.WxChatTaskService;
import io.metersphere.track.dto.TestCaseCommentDTO;
import io.metersphere.track.request.testreview.SaveCommentRequest; import io.metersphere.track.request.testreview.SaveCommentRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -33,21 +35,19 @@ import java.util.UUID;
public class TestCaseCommentService { public class TestCaseCommentService {
@Resource @Resource
TestCaseCommentMapper testCaseCommentMapper; private TestCaseCommentMapper testCaseCommentMapper;
@Resource @Resource
private TestCaseReviewMapper testCaseReviewMapper; private MailService mailService;
@Resource @Resource
UserMapper userMapper; private TestCaseMapper testCaseMapper;
@Resource @Resource
MailService mailService; private DingTaskService dingTaskService;
@Resource @Resource
TestCaseMapper testCaseMapper; private WxChatTaskService wxChatTaskService;
@Resource @Resource
DingTaskService dingTaskService; private NoticeService noticeService;
@Resource @Resource
WxChatTaskService wxChatTaskService; private ExtTestCaseCommentMapper extTestCaseCommentMapper;
@Resource
NoticeService noticeService;
public void saveComment(SaveCommentRequest request) { public void saveComment(SaveCommentRequest request) {
@ -86,21 +86,11 @@ public class TestCaseCommentService {
} }
public List<TestCaseComment> getComments(String caseId) { public List<TestCaseCommentDTO> getCaseComments(String caseId) {
TestCaseCommentExample testCaseCommentExample = new TestCaseCommentExample(); return extTestCaseCommentMapper.getCaseComments(caseId);
testCaseCommentExample.setOrderByClause("update_time desc");
testCaseCommentExample.createCriteria().andCaseIdEqualTo(caseId);
List<TestCaseComment> testCaseComments = testCaseCommentMapper.selectByExampleWithBLOBs(testCaseCommentExample);
testCaseComments.forEach(testCaseComment -> {
String authorId = testCaseComment.getAuthor();
User user = userMapper.selectByPrimaryKey(authorId);
String author = user == null ? authorId : user.getName();
testCaseComment.setAuthor(author);
});
return testCaseComments;
} }
public void deleteComment(String caseId) { public void deleteCaseComment(String caseId) {
TestCaseCommentExample testCaseCommentExample = new TestCaseCommentExample(); TestCaseCommentExample testCaseCommentExample = new TestCaseCommentExample();
testCaseCommentExample.createCriteria().andCaseIdEqualTo(caseId); testCaseCommentExample.createCriteria().andCaseIdEqualTo(caseId);
testCaseCommentMapper.deleteByExample(testCaseCommentExample); testCaseCommentMapper.deleteByExample(testCaseCommentExample);
@ -118,4 +108,22 @@ public class TestCaseCommentService {
context = "测试评审任务通知:" + testCaseComment.getAuthor() + "" + start + "" + "'" + testCaseWithBLOBs.getName() + "'" + "添加评论:" + testCaseComment.getDescription(); context = "测试评审任务通知:" + testCaseComment.getAuthor() + "" + start + "" + "'" + testCaseWithBLOBs.getName() + "'" + "添加评论:" + testCaseComment.getDescription();
return context; return context;
} }
public void delete(String commentId) {
checkCommentOwner(commentId);
testCaseCommentMapper.deleteByPrimaryKey(commentId);
}
public void edit(SaveCommentRequest request) {
checkCommentOwner(request.getId());
testCaseCommentMapper.updateByPrimaryKeySelective(request);
}
private void checkCommentOwner(String commentId) {
TestCaseComment comment = testCaseCommentMapper.selectByPrimaryKey(commentId);
if (!StringUtils.equals(comment.getAuthor(), SessionUtils.getUser().getId())) {
MSException.throwException(Translator.get("check_owner_comment"));
}
}
} }

View File

@ -170,7 +170,7 @@ public class TestCaseService {
example.createCriteria().andCaseIdEqualTo(testCaseId); example.createCriteria().andCaseIdEqualTo(testCaseId);
testPlanTestCaseMapper.deleteByExample(example); testPlanTestCaseMapper.deleteByExample(example);
testCaseIssueService.delTestCaseIssues(testCaseId); testCaseIssueService.delTestCaseIssues(testCaseId);
testCaseCommentService.deleteComment(testCaseId); testCaseCommentService.deleteCaseComment(testCaseId);
return testCaseMapper.deleteByPrimaryKey(testCaseId); return testCaseMapper.deleteByPrimaryKey(testCaseId);
} }

View File

@ -0,0 +1,2 @@
ALTER TABLE `test_plan_test_case` ADD INDEX index_name ( `case_id` );
ALTER TABLE `test_case_review_test_case` ADD INDEX index_name ( `case_id` );

View File

@ -164,6 +164,7 @@ check_owner_test=The current user does not have permission to operate this test
check_owner_case=The current user does not have permission to operate this use case check_owner_case=The current user does not have permission to operate this use case
check_owner_plan=The current user does not have permission to operate this plan check_owner_plan=The current user does not have permission to operate this plan
check_owner_review=The current user does not have permission to operate this review check_owner_review=The current user does not have permission to operate this review
check_owner_comment=The current user does not have permission to manipulate this comment
upload_content_is_null=Imported content is empty upload_content_is_null=Imported content is empty
test_plan_notification=Test plan notification test_plan_notification=Test plan notification
task_defect_notification=Task defect notification task_defect_notification=Task defect notification

View File

@ -164,6 +164,7 @@ check_owner_test=当前用户没有操作此测试的权限
check_owner_case=当前用户没有操作此用例的权限 check_owner_case=当前用户没有操作此用例的权限
check_owner_plan=当前用户没有操作此计划的权限 check_owner_plan=当前用户没有操作此计划的权限
check_owner_review=当前用户没有操作此评审的权限 check_owner_review=当前用户没有操作此评审的权限
check_owner_comment=当前用户没有操作此评论的权限
upload_content_is_null=导入内容为空 upload_content_is_null=导入内容为空
test_plan_notification=测试计划通知 test_plan_notification=测试计划通知
task_defect_notification=缺陷任务通知 task_defect_notification=缺陷任务通知

View File

@ -165,6 +165,7 @@ check_owner_test=當前用戶沒有操作此測試的權限
check_owner_case=當前用戶沒有操作此用例的權限 check_owner_case=當前用戶沒有操作此用例的權限
check_owner_plan=當前用戶沒有操作此計劃的權限 check_owner_plan=當前用戶沒有操作此計劃的權限
check_owner_review=當前用戶沒有操作此評審的權限 check_owner_review=當前用戶沒有操作此評審的權限
check_owner_comment=當前用戶沒有操作此評論的權限
upload_content_is_null=導入內容為空 upload_content_is_null=導入內容為空
test_plan_notification=測試計畫通知 test_plan_notification=測試計畫通知
task_defect_notification=缺陷任務通知 task_defect_notification=缺陷任務通知

View File

@ -1,488 +1,48 @@
<template> <template>
<div style="margin-left: 40px"> <ms-container>
<el-alert <ms-main-container>
:title="$t('organization.message.notes')" <el-alert
type="info"> :title="$t('organization.message.notes')"
</el-alert> type="info">
<el-form :model="form" ref="from"> </el-alert>
<el-row class="row"> <jenkins-notification :jenkins-receiver-options="jenkinsReceiverOptions"/>
<el-col :span="24"> <test-plan-task-notification :test-plan-receiver-options="testPlanReceiverOptions"/>
<div class="grid-content bg-purple-dark"> <test-review-notification :review-receiver-options="reviewReceiverOptions"/>
<el-row> <defect-task-notification :defect-receiver-options="defectReceiverOptions"/>
<el-col :span="10"> </ms-main-container>
<span style="font-weight:bold;">{{ $t('organization.message.jenkins_task_notification') }}</span> </ms-container>
</el-col>
<el-col :span="10">
<el-button type="text" icon="el-icon-plus" size="mini"
@click="handleAddTaskModel('jenkinsTask')">
{{ $t('organization.message.create_new_notification') }}
</el-button>
</el-col>
</el-row>
</div>
<el-table
:data="form.jenkinsTask"
class="tb-edit"
border
size="mini"
:cell-style="rowClass"
:header-cell-style="headClass">
<el-table-column :label="$t('schedule.event')" min-width="20%" prop="events">
<template slot-scope="scope">
<el-select v-model="scope.row.event"
:placeholder="$t('organization.message.select_events')"
prop="events" :disabled="!scope.row.isSet">
<el-option
v-for="item in jenkinsEventOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiver')" prop="userIds" min-width="20%">
<template v-slot:default="{row}">
<el-select v-model="row.userIds" filterable multiple
:placeholder="$t('commons.please_select')" style="width: 100%;" :disabled="!row.isSet">
<el-option
v-for="item in jenkinsReceiverOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiving_mode')" min-width="20%" prop="type">
<template slot-scope="scope">
<el-select v-model="scope.row.type" :placeholder="$t('organization.message.select_receiving_method')"
:disabled="!scope.row.isSet" @change="handleEdit(scope.$index, scope.row)"
>
<el-option
v-for="item in receiveTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="webhook" min-width="20%" prop="webhook">
<template v-slot:default="scope">
<el-input v-model="scope.row.webhook" placeholder="webhook地址"
:disabled="!scope.row.isSet||!scope.row.isReadOnly"></el-input>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')" min-width="20%" prop="result">
<template v-slot:default="scope">
<el-button
type="primary"
size="mini"
v-show="scope.row.isSet"
@click="handleAddTask(scope.$index,scope.row)"
>{{ $t('commons.add') }}
</el-button>
<el-button
size="mini"
v-show="scope.row.isSet"
@click.native.prevent="removeRowTask(scope.$index,form.jenkinsTask)"
>{{ $t('commons.cancel') }}
</el-button>
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
v-show="!scope.row.isSet"
@click.native.prevent="deleteRowTask(scope.$index,scope.row)"
></el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
<el-row class="row">
<el-col :span="24">
<div class="grid-content bg-purple-dark">
<el-row>
<el-col :span="10">
<span style="font-weight:bold;">{{ $t('organization.message.test_plan_task_notification') }}</span>
</el-col>
<el-col :span="10">
<el-button type="text" icon="el-icon-plus" size="mini" @click="handleAddTaskModel('testPlanTask')">
{{ $t('organization.message.create_new_notification') }}
</el-button>
</el-col>
</el-row>
</div>
<el-table
:data="form.testCasePlanTask"
class="tb-edit"
border
size="mini"
:cell-style="rowClass"
:header-cell-style="headClass"
>
<el-table-column :label="$t('schedule.event')" min-width="20%" prop="events">
<template slot-scope="scope">
<el-select v-model="scope.row.event" :placeholder="$t('organization.message.select_events')"
@change="handleTestPlanReceivers(scope.row)"
prop="events" :disabled="!scope.row.isSet">
<el-option
v-for="item in otherEventOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiver')" prop="userIds" min-width="20%">
<template v-slot:default="{row}">
<el-select v-model="row.userIds" filterable multiple
:placeholder="$t('commons.please_select')" style="width: 100%;" :disabled="!row.isSet">
<el-option
v-for="item in row.testPlanReceiverOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiving_mode')" min-width="20%" prop="type">
<template slot-scope="scope">
<el-select v-model="scope.row.type" :placeholder="$t('organization.message.select_receiving_method')"
:disabled="!scope.row.isSet" @change="handleEdit(scope.$index, scope.row)">
<el-option
v-for="item in receiveTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="webhook" min-width="20%" prop="webhook">
<template v-slot:default="scope">
<el-input v-model="scope.row.webhook" placeholder="webhook地址"
:disabled="!scope.row.isSet||!scope.row.isReadOnly"></el-input>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')" min-width="20%" prop="result">
<template v-slot:default="scope">
<el-button
type="primary"
size="mini"
v-show="scope.row.isSet"
@click="handleAddTask(scope.$index,scope.row)"
>{{ $t('commons.add') }}
</el-button>
<el-button
size="mini"
v-show="scope.row.isSet"
@click.native.prevent="removeRowTask(scope.$index,form.testCasePlanTask)"
>{{ $t('commons.cancel') }}
</el-button>
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
v-show="!scope.row.isSet"
@click.native.prevent="deleteRowTask(scope.$index,scope.row)"
></el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
<el-row class="row">
<el-col :span="24">
<div class="grid-content bg-purple-dark">
<el-row>
<el-col :span="10">
<span style="font-weight:bold;">{{ $t('organization.message.test_review_task_notice') }}</span>
</el-col>
<el-col :span="10">
<el-button type="text" icon="el-icon-plus" size="mini" @click="handleAddTaskModel('reviewTask')">
{{ $t('organization.message.create_new_notification') }}
</el-button>
</el-col>
</el-row>
</div>
<el-table
:data="form.reviewTask"
class="tb-edit"
border
size="mini"
:cell-style="rowClass"
:header-cell-style="headClass"
>
<el-table-column :label="$t('schedule.event')" min-width="20%" prop="events">
<template slot-scope="scope">
<el-select v-model="scope.row.event" :placeholder="$t('organization.message.select_events')"
@change="handleReviewReceivers(scope.row)"
prop="event" :disabled="!scope.row.isSet">
<el-option
v-for="item in reviewTaskEventOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiver')" prop="receiver" min-width="20%">
<template v-slot:default="{row}">
<el-select v-model="row.userIds" filterable multiple
:placeholder="$t('commons.please_select')"
style="width: 100%;" :disabled="!row.isSet">
<el-option
v-for="item in row.reviewReceiverOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiving_mode')" min-width="20%" prop="type">
<template slot-scope="scope">
<el-select v-model="scope.row.type" :placeholder="$t('organization.message.select_receiving_method')"
:disabled="!scope.row.isSet" @change="handleEdit(scope.$index, scope.row)">
<el-option
v-for="item in receiveTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="webhook" min-width="20%" prop="webhook">
<template v-slot:default="scope">
<el-input v-model="scope.row.webhook" placeholder="webhook地址"
:disabled="!scope.row.isSet||!scope.row.isReadOnly"></el-input>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')" min-width="20%" prop="result">
<template v-slot:default="scope">
<el-button
type="primary"
size="mini"
v-show="scope.row.isSet"
@click="handleAddTask(scope.$index,scope.row)"
>{{ $t('commons.add') }}
</el-button>
<el-button
size="mini"
v-show="scope.row.isSet"
@click.native.prevent="removeRowTask(scope.$index,form.reviewTask)"
>{{ $t('commons.cancel') }}
</el-button>
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
v-show="!scope.row.isSet"
@click.native.prevent="deleteRowTask(scope.$index,scope.row)"
></el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
<el-row class="row">
<el-col :span="24">
<div class="grid-content bg-purple-dark">
<el-row>
<el-col :span="10">
<span style="font-weight:bold;">{{ $t('organization.message.defect_task_notification') }}</span>
</el-col>
<el-col :span="10">
<el-button type="text" icon="el-icon-plus" size="mini" @click="handleAddTaskModel('defectTask')">
{{ $t('organization.message.create_new_notification') }}
</el-button>
</el-col>
</el-row>
</div>
<el-table
:data="form.defectTask"
class="tb-edit"
border
size="mini"
:cell-style="rowClass"
:header-cell-style="headClass"
>
<el-table-column :label="$t('schedule.event')" min-width="20%" prop="events">
<template slot-scope="scope">
<el-select v-model="scope.row.event" :placeholder="$t('organization.message.select_events')"
prop="event" :disabled="!scope.row.isSet">
<el-option
v-for="item in defectEventOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiver')" prop="receiver" min-width="20%">
<template v-slot:default="{row}">
<el-select v-model="row.userIds" filterable multiple
:placeholder="$t('commons.please_select')"
style="width: 100%;" :disabled="!row.isSet">
<el-option
v-for="item in defectReceiverOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiving_mode')" min-width="20%" prop="type">
<template slot-scope="scope">
<el-select v-model="scope.row.type" :placeholder="$t('organization.message.select_receiving_method')"
:disabled="!scope.row.isSet" @change="handleEdit(scope.$index, scope.row)">
<el-option
v-for="item in receiveTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="webhook" min-width="20%" prop="webhook">
<template v-slot:default="scope">
<el-input v-model="scope.row.webhook" placeholder="webhook地址"
:disabled="!scope.row.isSet||!scope.row.isReadOnly"></el-input>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')" min-width="20%" prop="result">
<template v-slot:default="scope">
<el-button
type="primary"
size="mini"
v-show="scope.row.isSet"
@click="handleAddTask(scope.$index,scope.row)"
>{{ $t('commons.add') }}
</el-button>
<el-button
size="mini"
v-show="scope.row.isSet"
@click.native.prevent="removeRowTask(scope.$index,form.defectTask)"
>{{ $t('commons.cancel') }}
</el-button>
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
v-show="!scope.row.isSet"
@click="deleteRowTask(scope.$index,scope.row)"
></el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
</el-form>
</div>
</template> </template>
<script> <script>
import {getCurrentUser} from "@/common/js/utils"; import {getCurrentUser} from "@/common/js/utils";
import JenkinsNotification from "@/business/components/settings/organization/components/JenkinsNotification";
import TestPlanTaskNotification from "@/business/components/settings/organization/components/TestPlanTaskNotification";
import TestReviewNotification from "@/business/components/settings/organization/components/TestReviewNotification";
import DefectTaskNotification from "@/business/components/settings/organization/components/DefectTaskNotification";
import MsContainer from "@/business/components/common/components/MsContainer";
import MsMainContainer from "@/business/components/common/components/MsMainContainer";
export default { export default {
name: "TaskNotification", name: "TaskNotification",
components: {
DefectTaskNotification, TestReviewNotification, TestPlanTaskNotification, JenkinsNotification, MsContainer,
MsMainContainer,
},
data() { data() {
return { return {
form: {
jenkinsTask: [{
taskType: "jenkinsTask",
event: "",
userIds: [],
type: [],
webhook: "",
isSet: true,
identification: "",
isReadOnly: false,
}],
testCasePlanTask: [{
taskType: "testPlanTask",
event: "",
userIds: [],
type: [],
webhook: "",
isSet: true,
identification: "",
isReadOnly: false,
}],
reviewTask: [{
taskType: "reviewTask",
event: "",
userIds: [],
type: [],
webhook: "",
isSet: true,
identification: "",
isReadOnly: false,
}],
defectTask: [{
taskType: "defectTask",
event: "",
userIds: [],
type: [],
webhook: "",
isSet: true,
identification: "",
isReadOnly: false,
}],
},
jenkinsEventOptions: [
{value: 'EXECUTE_SUCCESSFUL', label: this.$t('schedule.event_success')},
{value: 'EXECUTE_FAILED', label: this.$t('schedule.event_failed')}
],
jenkinsReceiverOptions: [], jenkinsReceiverOptions: [],
receiveTypeOptions: [
{value: 'EMAIL', label: this.$t('organization.message.mail')},
{value: 'NAIL_ROBOT', label: this.$t('organization.message.nail_robot')},
{value: 'WECHAT_ROBOT', label: this.$t('organization.message.enterprise_wechat_robot')}
],
otherEventOptions: [
{value: 'CREATE', label: this.$t('commons.create')},
{value: 'UPDATE', label: this.$t('commons.update')},
{value: 'DELETE', label: this.$t('commons.delete')}
],
reviewTaskEventOptions: [
{value: 'CREATE', label: this.$t('commons.create')},
{value: 'UPDATE', label: this.$t('commons.update')},
{value: 'DELETE', label: this.$t('commons.delete')},
{value: 'COMMENT', label: this.$t('commons.comment')}
],
defectEventOptions: [
{value: 'CREATE', label: this.$t('commons.create')},
],
// //
testPlanReceiverOptions: [], testPlanReceiverOptions: [],
// //
reviewReceiverOptions: [], reviewReceiverOptions: [],
// //
defectReceiverOptions: [], defectReceiverOptions: [],
rules: {},
} }
}, },
activated() { activated() {
this.initUserList(() => { this.initUserList();
this.initForm()
});
}, },
methods: { methods: {
handleEdit(index, data) { handleEdit(index, data) {
@ -494,20 +54,8 @@ export default {
currentUser: () => { currentUser: () => {
return getCurrentUser(); return getCurrentUser();
}, },
initForm() {
this.result = this.$get('/notice/search/message', response => {
this.form = response.data
this.form.testCasePlanTask.forEach(planTask => { initUserList() {
this.handleTestPlanReceivers(planTask);
});
this.form.reviewTask.forEach(planTask => {
this.handleReviewReceivers(planTask);
});
})
},
initUserList(after) {
let param = { let param = {
name: '', name: '',
organizationId: this.currentUser().lastOrganizationId organizationId: this.currentUser().lastOrganizationId
@ -517,115 +65,7 @@ export default {
this.reviewReceiverOptions = response.data this.reviewReceiverOptions = response.data
this.defectReceiverOptions = response.data this.defectReceiverOptions = response.data
this.testPlanReceiverOptions = response.data this.testPlanReceiverOptions = response.data
after();
}); });
},
handleAddTaskModel(type) {
let Task = {};
Task.event = [];
Task.userIds = [];
Task.type = "";
Task.webhook = "";
Task.isSet = true;
Task.identification = "";
if (type === 'jenkinsTask') {
Task.taskType = 'JENKINS_TASK'
this.form.jenkinsTask.push(Task)
}
if (type === 'testPlanTask') {
Task.taskType = 'TEST_PLAN_TASK'
this.form.testCasePlanTask.push(Task)
}
if (type === 'reviewTask') {
Task.taskType = 'REVIEW_TASK'
this.form.reviewTask.push(Task)
}
if (type === 'defectTask') {
Task.taskType = 'DEFECT_TASK'
this.form.defectTask.push(Task)
}
},
handleAddTask(index, data) {
if (data.event && data.userIds.length > 0 && data.type) {
console.log(data.type)
if (data.type === 'NAIL_ROBOT' || data.type === 'WECHAT_ROBOT') {
if (!data.webhook) {
this.$warning(this.$t('organization.message.message_webhook'));
} else {
this.addTask(data)
}
} else {
this.addTask(data)
}
} else {
this.$warning(this.$t('organization.message.message'));
}
},
addTask(data) {
let list = []
data.isSet = false
list.push(data)
let param = {};
param.messageDetail = list
this.result = this.$post("/notice/save/message/task", param, () => {
this.initForm()
this.$success(this.$t('commons.save_success'));
})
},
removeRowTask(index, data) { //
data.splice(index, 1)
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {
this.$success(this.$t('commons.delete_success'));
this.initForm()
})
},
rowClass() {
return "text-align:center"
},
headClass() {
return "text-align:center;background:'#ededed'"
},
handleTestPlanReceivers(row) {
let testPlanReceivers = JSON.parse(JSON.stringify(this.testPlanReceiverOptions));
switch (row.event) {
case "CREATE":
testPlanReceivers.unshift({id: 'EXECUTOR', name: this.$t('test_track.plan_view.executor')})
break;
case "UPDATE":
case "DELETE":
case "COMMENT":
testPlanReceivers.unshift({id: 'FOUNDER', name: this.$t('api_test.creator')});
break;
default:
break;
}
row.testPlanReceiverOptions = testPlanReceivers;
},
handleReviewReceivers(row) {
let reviewReceiverOptions = JSON.parse(JSON.stringify(this.reviewReceiverOptions));
switch (row.event) {
case "CREATE":
reviewReceiverOptions.unshift({id: 'EXECUTOR', name: this.$t('test_track.review.reviewer')})
break;
case "UPDATE":
reviewReceiverOptions.unshift({id: 'FOUNDER', name: this.$t('test_track.review.review_creator')})
break;
case "DELETE":
reviewReceiverOptions.unshift({id: 'FOUNDER', name: this.$t('test_track.review.review_creator')})
break;
case "COMMENT":
reviewReceiverOptions.unshift({id: 'MAINTAINER', name: this.$t('test_track.case.maintainer')})
break;
default:
break;
}
row.reviewReceiverOptions = reviewReceiverOptions;
} }
} }
} }
@ -637,10 +77,5 @@ export default {
overflow: auto; overflow: auto;
}*/ }*/
.row {
margin-bottom: 30px;
}
</style> </style>

View File

@ -0,0 +1,220 @@
<template>
<div>
<el-row>
<el-col :span="10">
<h3>{{ $t('organization.message.defect_task_notification') }}</h3>
<el-button icon="el-icon-circle-plus-outline" plain size="mini" @click="handleAddTaskModel('defectTask')">
{{ $t('organization.message.create_new_notification') }}
</el-button>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-table
:data="form.defectTask"
class="tb-edit"
border
size="mini"
:cell-style="rowClass"
:header-cell-style="headClass"
>
<el-table-column :label="$t('schedule.event')" min-width="20%" prop="events">
<template slot-scope="scope">
<el-select v-model="scope.row.event" :placeholder="$t('organization.message.select_events')"
prop="event" :disabled="!scope.row.isSet">
<el-option
v-for="item in defectEventOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiver')" prop="receiver" min-width="20%">
<template v-slot:default="{row}">
<el-select v-model="row.userIds" filterable multiple
:placeholder="$t('commons.please_select')"
style="width: 100%;" :disabled="!row.isSet">
<el-option
v-for="item in defectReceiverOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiving_mode')" min-width="20%" prop="type">
<template slot-scope="scope">
<el-select v-model="scope.row.type" :placeholder="$t('organization.message.select_receiving_method')"
:disabled="!scope.row.isSet" @change="handleEdit(scope.$index, scope.row)">
<el-option
v-for="item in receiveTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="webhook" min-width="20%" prop="webhook">
<template v-slot:default="scope">
<el-input v-model="scope.row.webhook" placeholder="webhook地址"
:disabled="!scope.row.isSet||!scope.row.isReadOnly"></el-input>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')" min-width="20%" prop="result">
<template v-slot:default="scope">
<el-button
type="primary"
size="mini"
v-show="scope.row.isSet"
@click="handleAddTask(scope.$index,scope.row)"
>{{ $t('commons.add') }}
</el-button>
<el-button
size="mini"
v-show="scope.row.isSet"
@click.native.prevent="removeRowTask(scope.$index,form.defectTask)"
>{{ $t('commons.cancel') }}
</el-button>
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
v-show="!scope.row.isSet"
@click="deleteRowTask(scope.$index,scope.row)"
></el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: "DefectTaskNotification",
props: {
defectReceiverOptions: {
type: Array
}
},
data() {
return {
form: {
defectTask: [{
taskType: "defectTask",
event: "",
userIds: [],
type: [],
webhook: "",
isSet: true,
identification: "",
isReadOnly: false,
}],
},
defectEventOptions: [
{value: 'CREATE', label: this.$t('commons.create')},
],
receiveTypeOptions: [
{value: 'EMAIL', label: this.$t('organization.message.mail')},
{value: 'NAIL_ROBOT', label: this.$t('organization.message.nail_robot')},
{value: 'WECHAT_ROBOT', label: this.$t('organization.message.enterprise_wechat_robot')}
],
};
},
activated() {
this.initForm()
},
methods: {
initForm() {
this.result = this.$get('/notice/search/message', response => {
this.form.defectTask = response.data.defectTask;
})
},
handleEdit(index, data) {
data.isReadOnly = true;
if (data.type === 'EMAIL') {
data.isReadOnly = !data.isReadOnly
}
},
handleAddTaskModel(type) {
let Task = {};
Task.event = [];
Task.userIds = [];
Task.type = "";
Task.webhook = "";
Task.isSet = true;
Task.identification = "";
if (type === 'jenkinsTask') {
Task.taskType = 'JENKINS_TASK'
this.form.jenkinsTask.push(Task)
}
if (type === 'testPlanTask') {
Task.taskType = 'TEST_PLAN_TASK'
this.form.testCasePlanTask.push(Task)
}
if (type === 'reviewTask') {
Task.taskType = 'REVIEW_TASK'
this.form.reviewTask.push(Task)
}
if (type === 'defectTask') {
Task.taskType = 'DEFECT_TASK'
this.form.defectTask.push(Task)
}
},
handleAddTask(index, data) {
if (data.event && data.userIds.length > 0 && data.type) {
console.log(data.type)
if (data.type === 'NAIL_ROBOT' || data.type === 'WECHAT_ROBOT') {
if (!data.webhook) {
this.$warning(this.$t('organization.message.message_webhook'));
} else {
this.addTask(data)
}
} else {
this.addTask(data)
}
} else {
this.$warning(this.$t('organization.message.message'));
}
},
addTask(data) {
let list = []
data.isSet = false
list.push(data)
let param = {};
param.messageDetail = list
this.result = this.$post("/notice/save/message/task", param, () => {
this.initForm()
this.$success(this.$t('commons.save_success'));
})
},
removeRowTask(index, data) { //
data.splice(index, 1)
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {
this.$success(this.$t('commons.delete_success'));
this.initForm()
})
},
rowClass() {
return "text-align:center"
},
headClass() {
return "text-align:center;background:'#ededed'"
}
}
}
</script>
<style scoped>
.el-row {
margin-bottom: 10px;
}
</style>

View File

@ -0,0 +1,221 @@
<template>
<div>
<el-row>
<el-col :span="10">
<h3>{{ $t('organization.message.jenkins_task_notification') }}</h3>
<el-button icon="el-icon-circle-plus-outline" plain size="mini" @click="handleAddTaskModel('jenkinsTask')">
{{ $t('organization.message.create_new_notification') }}
</el-button>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-table
:data="form.jenkinsTask"
class="tb-edit"
border
size="mini"
:cell-style="rowClass"
:header-cell-style="headClass">
<el-table-column :label="$t('schedule.event')" min-width="20%" prop="events">
<template slot-scope="scope">
<el-select v-model="scope.row.event"
:placeholder="$t('organization.message.select_events')"
prop="events" :disabled="!scope.row.isSet">
<el-option
v-for="item in jenkinsEventOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiver')" prop="userIds" min-width="20%">
<template v-slot:default="{row}">
<el-select v-model="row.userIds" filterable multiple
:placeholder="$t('commons.please_select')" style="width: 100%;" :disabled="!row.isSet">
<el-option
v-for="item in jenkinsReceiverOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiving_mode')" min-width="20%" prop="type">
<template slot-scope="scope">
<el-select v-model="scope.row.type" :placeholder="$t('organization.message.select_receiving_method')"
:disabled="!scope.row.isSet" @change="handleEdit(scope.$index, scope.row)"
>
<el-option
v-for="item in receiveTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="webhook" min-width="20%" prop="webhook">
<template v-slot:default="scope">
<el-input v-model="scope.row.webhook" placeholder="webhook地址"
:disabled="!scope.row.isSet||!scope.row.isReadOnly"></el-input>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')" min-width="20%" prop="result">
<template v-slot:default="scope">
<el-button
type="primary"
size="mini"
v-show="scope.row.isSet"
@click="handleAddTask(scope.$index,scope.row)"
>{{ $t('commons.add') }}
</el-button>
<el-button
size="mini"
v-show="scope.row.isSet"
@click.native.prevent="removeRowTask(scope.$index,form.jenkinsTask)"
>{{ $t('commons.cancel') }}
</el-button>
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
v-show="!scope.row.isSet"
@click.native.prevent="deleteRowTask(scope.$index,scope.row)"
></el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: "JenkinsNotification",
props: {
jenkinsReceiverOptions: {
type: Array
}
},
data() {
return {
form: {
jenkinsTask: [{
taskType: "jenkinsTask",
event: "",
userIds: [],
type: [],
webhook: "",
isSet: true,
identification: "",
isReadOnly: false,
}],
},
jenkinsEventOptions: [
{value: 'EXECUTE_SUCCESSFUL', label: this.$t('schedule.event_success')},
{value: 'EXECUTE_FAILED', label: this.$t('schedule.event_failed')}
],
receiveTypeOptions: [
{value: 'EMAIL', label: this.$t('organization.message.mail')},
{value: 'NAIL_ROBOT', label: this.$t('organization.message.nail_robot')},
{value: 'WECHAT_ROBOT', label: this.$t('organization.message.enterprise_wechat_robot')}
],
}
},
activated() {
this.initForm();
},
methods: {
initForm() {
this.result = this.$get('/notice/search/message', response => {
this.form.jenkinsTask = response.data.jenkinsTask;
})
},
handleEdit(index, data) {
data.isReadOnly = true;
if (data.type === 'EMAIL') {
data.isReadOnly = !data.isReadOnly
}
},
handleAddTaskModel(type) {
let Task = {};
Task.event = [];
Task.userIds = [];
Task.type = "";
Task.webhook = "";
Task.isSet = true;
Task.identification = "";
if (type === 'jenkinsTask') {
Task.taskType = 'JENKINS_TASK'
this.form.jenkinsTask.push(Task)
}
if (type === 'testPlanTask') {
Task.taskType = 'TEST_PLAN_TASK'
this.form.testCasePlanTask.push(Task)
}
if (type === 'reviewTask') {
Task.taskType = 'REVIEW_TASK'
this.form.reviewTask.push(Task)
}
if (type === 'defectTask') {
Task.taskType = 'DEFECT_TASK'
this.form.defectTask.push(Task)
}
},
handleAddTask(index, data) {
if (data.event && data.userIds.length > 0 && data.type) {
console.log(data.type)
if (data.type === 'NAIL_ROBOT' || data.type === 'WECHAT_ROBOT') {
if (!data.webhook) {
this.$warning(this.$t('organization.message.message_webhook'));
} else {
this.addTask(data)
}
} else {
this.addTask(data)
}
} else {
this.$warning(this.$t('organization.message.message'));
}
},
addTask(data) {
let list = []
data.isSet = false
list.push(data)
let param = {};
param.messageDetail = list
this.result = this.$post("/notice/save/message/task", param, () => {
this.initForm()
this.$success(this.$t('commons.save_success'));
})
},
removeRowTask(index, data) { //
data.splice(index, 1)
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {
this.$success(this.$t('commons.delete_success'));
this.initForm()
})
},
rowClass() {
return "text-align:center"
},
headClass() {
return "text-align:center;background:'#ededed'"
},
}
}
</script>
<style scoped>
.el-row {
margin-bottom: 10px;
}
</style>

View File

@ -0,0 +1,242 @@
<template>
<div>
<el-row>
<el-col :span="10">
<h3>{{ $t('organization.message.test_plan_task_notification') }}</h3>
<el-button icon="el-icon-circle-plus-outline" plain size="mini" @click="handleAddTaskModel('testPlanTask')">
{{ $t('organization.message.create_new_notification') }}
</el-button>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-table
:data="form.testCasePlanTask"
class="tb-edit"
border
size="mini"
:cell-style="rowClass"
:header-cell-style="headClass"
>
<el-table-column :label="$t('schedule.event')" min-width="20%" prop="events">
<template slot-scope="scope">
<el-select v-model="scope.row.event" :placeholder="$t('organization.message.select_events')"
@change="handleTestPlanReceivers(scope.row)"
prop="events" :disabled="!scope.row.isSet">
<el-option
v-for="item in otherEventOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiver')" prop="userIds" min-width="20%">
<template v-slot:default="{row}">
<el-select v-model="row.userIds" filterable multiple
:placeholder="$t('commons.please_select')" style="width: 100%;" :disabled="!row.isSet">
<el-option
v-for="item in row.testPlanReceiverOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiving_mode')" min-width="20%" prop="type">
<template slot-scope="scope">
<el-select v-model="scope.row.type" :placeholder="$t('organization.message.select_receiving_method')"
:disabled="!scope.row.isSet" @change="handleEdit(scope.$index, scope.row)">
<el-option
v-for="item in receiveTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="webhook" min-width="20%" prop="webhook">
<template v-slot:default="scope">
<el-input v-model="scope.row.webhook" placeholder="webhook地址"
:disabled="!scope.row.isSet||!scope.row.isReadOnly"></el-input>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')" min-width="20%" prop="result">
<template v-slot:default="scope">
<el-button
type="primary"
size="mini"
v-show="scope.row.isSet"
@click="handleAddTask(scope.$index,scope.row)"
>{{ $t('commons.add') }}
</el-button>
<el-button
size="mini"
v-show="scope.row.isSet"
@click.native.prevent="removeRowTask(scope.$index,form.testCasePlanTask)"
>{{ $t('commons.cancel') }}
</el-button>
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
v-show="!scope.row.isSet"
@click.native.prevent="deleteRowTask(scope.$index,scope.row)"
></el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: "TestPlanTaskNotification",
props: {
testPlanReceiverOptions: {
type: Array
}
},
data() {
return {
form: {
testCasePlanTask: [{
taskType: "testPlanTask",
event: "",
userIds: [],
type: [],
webhook: "",
isSet: true,
identification: "",
isReadOnly: false,
}],
},
otherEventOptions: [
{value: 'CREATE', label: this.$t('commons.create')},
{value: 'UPDATE', label: this.$t('commons.update')},
{value: 'DELETE', label: this.$t('commons.delete')}
],
receiveTypeOptions: [
{value: 'EMAIL', label: this.$t('organization.message.mail')},
{value: 'NAIL_ROBOT', label: this.$t('organization.message.nail_robot')},
{value: 'WECHAT_ROBOT', label: this.$t('organization.message.enterprise_wechat_robot')}
],
};
},
activated() {
this.initForm()
},
methods: {
initForm() {
this.result = this.$get('/notice/search/message', response => {
this.form.testCasePlanTask = response.data.testCasePlanTask;
this.form.testCasePlanTask.forEach(planTask => {
this.handleTestPlanReceivers(planTask);
});
})
},
handleEdit(index, data) {
data.isReadOnly = true;
if (data.type === 'EMAIL') {
data.isReadOnly = !data.isReadOnly
}
},
handleAddTaskModel(type) {
let Task = {};
Task.event = [];
Task.userIds = [];
Task.type = "";
Task.webhook = "";
Task.isSet = true;
Task.identification = "";
if (type === 'jenkinsTask') {
Task.taskType = 'JENKINS_TASK'
this.form.jenkinsTask.push(Task)
}
if (type === 'testPlanTask') {
Task.taskType = 'TEST_PLAN_TASK'
this.form.testCasePlanTask.push(Task)
}
if (type === 'reviewTask') {
Task.taskType = 'REVIEW_TASK'
this.form.reviewTask.push(Task)
}
if (type === 'defectTask') {
Task.taskType = 'DEFECT_TASK'
this.form.defectTask.push(Task)
}
},
handleAddTask(index, data) {
if (data.event && data.userIds.length > 0 && data.type) {
console.log(data.type)
if (data.type === 'NAIL_ROBOT' || data.type === 'WECHAT_ROBOT') {
if (!data.webhook) {
this.$warning(this.$t('organization.message.message_webhook'));
} else {
this.addTask(data)
}
} else {
this.addTask(data)
}
} else {
this.$warning(this.$t('organization.message.message'));
}
},
addTask(data) {
let list = []
data.isSet = false
list.push(data)
let param = {};
param.messageDetail = list
this.result = this.$post("/notice/save/message/task", param, () => {
this.initForm()
this.$success(this.$t('commons.save_success'));
})
},
removeRowTask(index, data) { //
data.splice(index, 1)
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {
this.$success(this.$t('commons.delete_success'));
this.initForm()
})
},
rowClass() {
return "text-align:center"
},
headClass() {
return "text-align:center;background:'#ededed'"
},
handleTestPlanReceivers(row) {
let testPlanReceivers = JSON.parse(JSON.stringify(this.testPlanReceiverOptions));
switch (row.event) {
case "CREATE":
testPlanReceivers.unshift({id: 'EXECUTOR', name: this.$t('test_track.plan_view.executor')})
break;
case "UPDATE":
case "DELETE":
case "COMMENT":
testPlanReceivers.unshift({id: 'FOUNDER', name: this.$t('api_test.creator')});
break;
default:
break;
}
row.testPlanReceiverOptions = testPlanReceivers;
},
}
}
</script>
<style scoped>
.el-row {
margin-bottom: 10px;
}
</style>

View File

@ -0,0 +1,248 @@
<template>
<div>
<el-row>
<el-col :span="10">
<h3>{{ $t('organization.message.test_review_task_notice') }}</h3>
<el-button icon="el-icon-circle-plus-outline" plain size="mini" @click="handleAddTaskModel('reviewTask')">
{{ $t('organization.message.create_new_notification') }}
</el-button>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-table
:data="form.reviewTask"
class="tb-edit"
border
size="mini"
:cell-style="rowClass"
:header-cell-style="headClass"
>
<el-table-column :label="$t('schedule.event')" min-width="20%" prop="events">
<template slot-scope="scope">
<el-select v-model="scope.row.event" :placeholder="$t('organization.message.select_events')"
@change="handleReviewReceivers(scope.row)"
prop="event" :disabled="!scope.row.isSet">
<el-option
v-for="item in reviewTaskEventOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiver')" prop="receiver" min-width="20%">
<template v-slot:default="{row}">
<el-select v-model="row.userIds" filterable multiple
:placeholder="$t('commons.please_select')"
style="width: 100%;" :disabled="!row.isSet">
<el-option
v-for="item in row.reviewReceiverOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.receiving_mode')" min-width="20%" prop="type">
<template slot-scope="scope">
<el-select v-model="scope.row.type" :placeholder="$t('organization.message.select_receiving_method')"
:disabled="!scope.row.isSet" @change="handleEdit(scope.$index, scope.row)">
<el-option
v-for="item in receiveTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
</el-table-column>
<el-table-column label="webhook" min-width="20%" prop="webhook">
<template v-slot:default="scope">
<el-input v-model="scope.row.webhook" placeholder="webhook地址"
:disabled="!scope.row.isSet||!scope.row.isReadOnly"></el-input>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')" min-width="20%" prop="result">
<template v-slot:default="scope">
<el-button
type="primary"
size="mini"
v-show="scope.row.isSet"
@click="handleAddTask(scope.$index,scope.row)"
>{{ $t('commons.add') }}
</el-button>
<el-button
size="mini"
v-show="scope.row.isSet"
@click.native.prevent="removeRowTask(scope.$index,form.reviewTask)"
>{{ $t('commons.cancel') }}
</el-button>
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
v-show="!scope.row.isSet"
@click.native.prevent="deleteRowTask(scope.$index,scope.row)"
></el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: "TestReviewNotification",
props: {
reviewReceiverOptions: {
type: Array
}
},
data() {
return {
form: {
reviewTask: [{
taskType: "reviewTask",
event: "",
userIds: [],
type: [],
webhook: "",
isSet: true,
identification: "",
isReadOnly: false,
}],
},
reviewTaskEventOptions: [
{value: 'CREATE', label: this.$t('commons.create')},
{value: 'UPDATE', label: this.$t('commons.update')},
{value: 'DELETE', label: this.$t('commons.delete')},
{value: 'COMMENT', label: this.$t('commons.comment')}
],
receiveTypeOptions: [
{value: 'EMAIL', label: this.$t('organization.message.mail')},
{value: 'NAIL_ROBOT', label: this.$t('organization.message.nail_robot')},
{value: 'WECHAT_ROBOT', label: this.$t('organization.message.enterprise_wechat_robot')}
],
};
},
activated() {
this.initForm()
},
methods: {
initForm() {
this.result = this.$get('/notice/search/message', response => {
this.form.reviewTask = response.data.reviewTask;
this.form.reviewTask.forEach(planTask => {
this.handleReviewReceivers(planTask);
});
})
},
handleEdit(index, data) {
data.isReadOnly = true;
if (data.type === 'EMAIL') {
data.isReadOnly = !data.isReadOnly
}
},
handleAddTaskModel(type) {
let Task = {};
Task.event = [];
Task.userIds = [];
Task.type = "";
Task.webhook = "";
Task.isSet = true;
Task.identification = "";
if (type === 'jenkinsTask') {
Task.taskType = 'JENKINS_TASK'
this.form.jenkinsTask.push(Task)
}
if (type === 'testPlanTask') {
Task.taskType = 'TEST_PLAN_TASK'
this.form.testCasePlanTask.push(Task)
}
if (type === 'reviewTask') {
Task.taskType = 'REVIEW_TASK'
this.form.reviewTask.push(Task)
}
if (type === 'defectTask') {
Task.taskType = 'DEFECT_TASK'
this.form.defectTask.push(Task)
}
},
handleAddTask(index, data) {
if (data.event && data.userIds.length > 0 && data.type) {
console.log(data.type)
if (data.type === 'NAIL_ROBOT' || data.type === 'WECHAT_ROBOT') {
if (!data.webhook) {
this.$warning(this.$t('organization.message.message_webhook'));
} else {
this.addTask(data)
}
} else {
this.addTask(data)
}
} else {
this.$warning(this.$t('organization.message.message'));
}
},
addTask(data) {
let list = []
data.isSet = false
list.push(data)
let param = {};
param.messageDetail = list
this.result = this.$post("/notice/save/message/task", param, () => {
this.initForm()
this.$success(this.$t('commons.save_success'));
})
},
removeRowTask(index, data) { //
data.splice(index, 1)
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {
this.$success(this.$t('commons.delete_success'));
this.initForm()
})
},
rowClass() {
return "text-align:center"
},
headClass() {
return "text-align:center;background:'#ededed'"
},
handleReviewReceivers(row) {
let reviewReceiverOptions = JSON.parse(JSON.stringify(this.reviewReceiverOptions));
switch (row.event) {
case "CREATE":
reviewReceiverOptions.unshift({id: 'EXECUTOR', name: this.$t('test_track.review.reviewer')})
break;
case "UPDATE":
reviewReceiverOptions.unshift({id: 'FOUNDER', name: this.$t('test_track.review.review_creator')})
break;
case "DELETE":
reviewReceiverOptions.unshift({id: 'FOUNDER', name: this.$t('test_track.review.review_creator')})
break;
case "COMMENT":
reviewReceiverOptions.unshift({id: 'MAINTAINER', name: this.$t('test_track.case.maintainer')})
break;
default:
break;
}
row.reviewReceiverOptions = reviewReceiverOptions;
}
}
}
</script>
<style scoped>
.el-row {
margin-bottom: 10px;
}
</style>

View File

@ -3,18 +3,17 @@
<ms-main-container> <ms-main-container>
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="15"> <el-col :span="15">
<related-test-plan-list ref="relatedTestPlanList"/> <el-row>
<related-test-plan-list ref="relatedTestPlanList"/>
</el-row>
<el-row>
<review-list title="我的评审" ref="caseReviewList"/>
</el-row>
</el-col> </el-col>
<el-col :span="9"> <el-col :span="9">
<test-case-side-list :title="$t('test_track.home.recent_test')" ref="testCaseRecentList"/> <test-case-side-list :title="$t('test_track.home.recent_test')" ref="testCaseRecentList"/>
</el-col> </el-col>
</el-row> </el-row>
<div style="margin-top: 10px"/>
<el-row :gutter="20">
<el-col :span="15">
<review-list title="我的评审" ref="caseReviewList"/>
</el-col>
</el-row>
</ms-main-container> </ms-main-container>
</ms-container> </ms-container>
</template> </template>
@ -52,4 +51,7 @@ export default {
cursor: pointer; cursor: pointer;
} }
.el-row {
padding-bottom: 20px;
}
</style> </style>

View File

@ -101,7 +101,7 @@
<el-row> <el-row>
<el-col :offset="1"> <el-col :offset="1">
<span class="cast_label">{{ $t('test_track.case.prerequisite') }}</span> <span class="cast_label">{{ $t('test_track.case.prerequisite') }}</span>
<span class="cast_item">{{ testCase.prerequisite }}</span> <span class="cast_item"><p>{{ testCase.prerequisite }}</p></span>
</el-col> </el-col>
</el-row> </el-row>
@ -684,4 +684,9 @@ export default {
.el-switch >>> .el-switch__label.is-active { .el-switch >>> .el-switch__label.is-active {
color: #409EFF; color: #409EFF;
} }
p {
white-space: pre-line;
line-height: 20px;
}
</style> </style>

View File

@ -1,7 +1,10 @@
<template> <template>
<div v-loading="result.loading"> <div v-loading="result.loading">
<div class="comment-list"> <div class="comment-list">
<review-comment-item v-for="(comment,index) in comments" :key="index" :comment="comment"/> <review-comment-item v-for="(comment,index) in comments"
:key="index"
:comment="comment"
@refresh="refresh"/>
<div v-if="comments.length === 0" style="text-align: center"> <div v-if="comments.length === 0" style="text-align: center">
<i class="el-icon-chat-line-square" style="font-size: 15px;color: #8a8b8d;"> <i class="el-icon-chat-line-square" style="font-size: 15px;color: #8a8b8d;">
<span style="font-size: 15px; color: #8a8b8d;"> <span style="font-size: 15px; color: #8a8b8d;">
@ -56,10 +59,13 @@ export default {
} }
this.result = this.$post('/test/case/comment/save', comment, () => { this.result = this.$post('/test/case/comment/save', comment, () => {
this.$success(this.$t('test_track.comment.send_success')); this.$success(this.$t('test_track.comment.send_success'));
this.$emit('getComments'); this.refresh();
this.textarea = ''; this.textarea = '';
}); });
}, },
refresh() {
this.$emit('getComments');
},
} }
} }
</script> </script>

View File

@ -2,30 +2,88 @@
<div class="main"> <div class="main">
<div class="comment-left"> <div class="comment-left">
<div class="icon-title"> <div class="icon-title">
{{ comment.author.substring(0, 1) }} {{ comment.authorName.substring(0, 1) }}
</div> </div>
</div> </div>
<div class="comment-right"> <div class="comment-right">
<span style="font-size: 14px;color: #909399;font-weight: bold">{{ comment.author }}</span> <span style="font-size: 14px;color: #909399;font-weight: bold">{{ comment.authorName }}</span>
<span style="color: #8a8b8d; margin-left: 8px; font-size: 12px"> <span style="color: #8a8b8d; margin-left: 8px; font-size: 12px">
{{ comment.createTime | timestampFormatDate }} {{ comment.createTime | timestampFormatDate }}
</span> </span>
<span class="comment-delete">
<i class="el-icon-edit" style="font-size: 9px;margin-right: 6px;" @click="openEdit"/>
<i class="el-icon-close" @click="deleteComment"/>
</span>
<br/> <br/>
<div class="comment-desc" style="font-size: 10px;color: #303133"> <div class="comment-desc" style="font-size: 10px;color: #303133">
<pre>{{ comment.description }}</pre> <pre>{{ comment.description }}</pre>
</div> </div>
</div> </div>
<el-dialog
:title="$t('commons.edit')"
:visible.sync="visible"
width="30%"
:destroy-on-close="true"
:append-to-body="true"
:close-on-click-modal="false"
show-close>
<el-input
type="textarea"
:rows="5"
v-model="description">
</el-input>
<span slot="footer" class="dialog-footer">
<ms-dialog-footer
@cancel="visible = false"
@confirm="editComment"/>
</span>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
import {getCurrentUser} from "@/common/js/utils";
export default { export default {
name: "ReviewCommentItem", name: "ReviewCommentItem",
components: {MsDialogFooter},
props: { props: {
comment: Object comment: Object
}, },
data() { data() {
return {} return {
visible: false,
description: ""
}
},
methods: {
deleteComment() {
if (getCurrentUser().id !== this.comment.author) {
this.$warning(this.$t('test_track.comment.cannot_delete'));
return;
}
this.$parent.result = this.$get("/test/case/comment/delete/" + this.comment.id, () => {
this.$success(this.$t('commons.delete_success'));
this.$emit("refresh");
});
},
openEdit() {
if (getCurrentUser().id !== this.comment.author) {
this.$warning(this.$t('test_track.comment.cannot_edit'));
return;
}
this.description = this.comment.description;
this.visible = true;
},
editComment() {
this.$post("/test/case/comment/edit", {id: this.comment.id, description: this.description}, () => {
this.visible = false;
this.$success(this.$t('commons.modify_success'));
this.$emit("refresh");
});
}
} }
} }
</script> </script>
@ -72,4 +130,10 @@ export default {
pre { pre {
margin: 0 0; margin: 0 0;
} }
.comment-delete {
float: right;
margin-right: 5px;
cursor: pointer;
}
</style> </style>

View File

@ -365,7 +365,7 @@ export default {
}, },
getTestReviewById() { getTestReviewById() {
if (this.reviewId) { if (this.reviewId) {
this.$post('/test/case/review/get/' + this.reviewId, {}, response => { this.$get('/test/case/review/get/' + this.reviewId, response => {
this.testReview = response.data; this.testReview = response.data;
this.refreshTestReviewRecent(); this.refreshTestReviewRecent();
}); });

View File

@ -848,6 +848,8 @@ export default {
relevance_case: "Relevance Case", relevance_case: "Relevance Case",
last_page: "It's the end", last_page: "It's the end",
execute_result: "Result", execute_result: "Result",
cannot_edit: "Cannot edit this comment",
cannot_delete: "Cannot delete this comment",
}, },
module: { module: {
search: "Search module", search: "Search module",

View File

@ -841,6 +841,8 @@ export default {
send: "发送", send: "发送",
description_is_null: "评论内容不能为空!", description_is_null: "评论内容不能为空!",
send_success: "评论成功!", send_success: "评论成功!",
cannot_edit: "无法编辑此评论!",
cannot_delete: "无法删除此评论!",
}, },
review_view: { review_view: {
review: "评审", review: "评审",

View File

@ -846,6 +846,8 @@ export default {
send: "發送", send: "發送",
description_is_null: "評論內容不能為空!", description_is_null: "評論內容不能為空!",
send_success: "評論成功!", send_success: "評論成功!",
cannot_edit: "無法編輯此評論!",
cannot_delete: "無法刪除此評論!",
}, },
review_view: { review_view: {
review: "評審", review: "評審",