mirror of
https://gitee.com/fit2cloud-feizhiyun/MeterSphere.git
synced 2024-12-05 05:29:29 +08:00
feat: jira支持自动获取模板
This commit is contained in:
parent
bc5381a7c4
commit
a3ccff0649
@ -45,5 +45,9 @@ public class Project implements Serializable {
|
||||
|
||||
private String azureFilterId;
|
||||
|
||||
private String platform;
|
||||
|
||||
private Boolean thirdPartTemplate;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
}
|
||||
|
@ -1433,6 +1433,136 @@ public class ProjectExample {
|
||||
addCriterion("azure_filter_id not between", value1, value2, "azureFilterId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andPlatformIsNull() {
|
||||
addCriterion("platform is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andPlatformIsNotNull() {
|
||||
addCriterion("platform is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andPlatformEqualTo(String value) {
|
||||
addCriterion("platform =", value, "platform");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andPlatformNotEqualTo(String value) {
|
||||
addCriterion("platform <>", value, "platform");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andPlatformGreaterThan(String value) {
|
||||
addCriterion("platform >", value, "platform");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andPlatformGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("platform >=", value, "platform");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andPlatformLessThan(String value) {
|
||||
addCriterion("platform <", value, "platform");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andPlatformLessThanOrEqualTo(String value) {
|
||||
addCriterion("platform <=", value, "platform");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andPlatformLike(String value) {
|
||||
addCriterion("platform like", value, "platform");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andPlatformNotLike(String value) {
|
||||
addCriterion("platform not like", value, "platform");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andPlatformIn(List<String> values) {
|
||||
addCriterion("platform in", values, "platform");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andPlatformNotIn(List<String> values) {
|
||||
addCriterion("platform not in", values, "platform");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andPlatformBetween(String value1, String value2) {
|
||||
addCriterion("platform between", value1, value2, "platform");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andPlatformNotBetween(String value1, String value2) {
|
||||
addCriterion("platform not between", value1, value2, "platform");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andThirdPartTemplateIsNull() {
|
||||
addCriterion("third_part_template is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andThirdPartTemplateIsNotNull() {
|
||||
addCriterion("third_part_template is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andThirdPartTemplateEqualTo(Boolean value) {
|
||||
addCriterion("third_part_template =", value, "thirdPartTemplate");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andThirdPartTemplateNotEqualTo(Boolean value) {
|
||||
addCriterion("third_part_template <>", value, "thirdPartTemplate");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andThirdPartTemplateGreaterThan(Boolean value) {
|
||||
addCriterion("third_part_template >", value, "thirdPartTemplate");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andThirdPartTemplateGreaterThanOrEqualTo(Boolean value) {
|
||||
addCriterion("third_part_template >=", value, "thirdPartTemplate");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andThirdPartTemplateLessThan(Boolean value) {
|
||||
addCriterion("third_part_template <", value, "thirdPartTemplate");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andThirdPartTemplateLessThanOrEqualTo(Boolean value) {
|
||||
addCriterion("third_part_template <=", value, "thirdPartTemplate");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andThirdPartTemplateIn(List<Boolean> values) {
|
||||
addCriterion("third_part_template in", values, "thirdPartTemplate");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andThirdPartTemplateNotIn(List<Boolean> values) {
|
||||
addCriterion("third_part_template not in", values, "thirdPartTemplate");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andThirdPartTemplateBetween(Boolean value1, Boolean value2) {
|
||||
addCriterion("third_part_template between", value1, value2, "thirdPartTemplate");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andThirdPartTemplateNotBetween(Boolean value1, Boolean value2) {
|
||||
addCriterion("third_part_template not between", value1, value2, "thirdPartTemplate");
|
||||
return (Criteria) this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Criteria extends GeneratedCriteria {
|
||||
@ -1527,4 +1657,4 @@ public class ProjectExample {
|
||||
this(condition, value, secondValue, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,8 @@
|
||||
<result column="mock_tcp_port" jdbcType="INTEGER" property="mockTcpPort" />
|
||||
<result column="is_mock_tcp_open" jdbcType="BIT" property="isMockTcpOpen" />
|
||||
<result column="azure_filter_id" jdbcType="VARCHAR" property="azureFilterId" />
|
||||
<result column="platform" jdbcType="VARCHAR" property="platform" />
|
||||
<result column="third_part_template" jdbcType="BIT" property="thirdPartTemplate" />
|
||||
</resultMap>
|
||||
<sql id="Example_Where_Clause">
|
||||
<where>
|
||||
@ -82,9 +84,10 @@
|
||||
</where>
|
||||
</sql>
|
||||
<sql id="Base_Column_List">
|
||||
id, workspace_id, `name`, description, create_time, update_time, tapd_id, jira_key,
|
||||
zentao_id, azure_devops_id, `repeatable`, case_template_id, issue_template_id, custom_num,
|
||||
scenario_custom_num, create_user, system_id, mock_tcp_port, is_mock_tcp_open, azure_filter_id
|
||||
id, workspace_id, `name`, description, create_time, update_time, tapd_id, jira_key,
|
||||
zentao_id, azure_devops_id, `repeatable`, case_template_id, issue_template_id, custom_num,
|
||||
scenario_custom_num, create_user, system_id, mock_tcp_port, is_mock_tcp_open, azure_filter_id,
|
||||
platform, third_part_template
|
||||
</sql>
|
||||
<select id="selectByExample" parameterType="io.metersphere.base.domain.ProjectExample" resultMap="BaseResultMap">
|
||||
select
|
||||
@ -101,7 +104,7 @@
|
||||
</if>
|
||||
</select>
|
||||
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
|
||||
select
|
||||
select
|
||||
<include refid="Base_Column_List" />
|
||||
from project
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
@ -117,20 +120,22 @@
|
||||
</if>
|
||||
</delete>
|
||||
<insert id="insert" parameterType="io.metersphere.base.domain.Project">
|
||||
insert into project (id, workspace_id, `name`,
|
||||
description, create_time, update_time,
|
||||
tapd_id, jira_key, zentao_id,
|
||||
azure_devops_id, `repeatable`, case_template_id,
|
||||
issue_template_id, custom_num, scenario_custom_num,
|
||||
create_user, system_id, mock_tcp_port,
|
||||
is_mock_tcp_open, azure_filter_id)
|
||||
values (#{id,jdbcType=VARCHAR}, #{workspaceId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
|
||||
#{description,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
|
||||
#{tapdId,jdbcType=VARCHAR}, #{jiraKey,jdbcType=VARCHAR}, #{zentaoId,jdbcType=VARCHAR},
|
||||
#{azureDevopsId,jdbcType=VARCHAR}, #{repeatable,jdbcType=BIT}, #{caseTemplateId,jdbcType=VARCHAR},
|
||||
#{issueTemplateId,jdbcType=VARCHAR}, #{customNum,jdbcType=BIT}, #{scenarioCustomNum,jdbcType=BIT},
|
||||
#{createUser,jdbcType=VARCHAR}, #{systemId,jdbcType=VARCHAR}, #{mockTcpPort,jdbcType=INTEGER},
|
||||
#{isMockTcpOpen,jdbcType=BIT}, #{azureFilterId,jdbcType=VARCHAR})
|
||||
insert into project (id, workspace_id, `name`,
|
||||
description, create_time, update_time,
|
||||
tapd_id, jira_key, zentao_id,
|
||||
azure_devops_id, `repeatable`, case_template_id,
|
||||
issue_template_id, custom_num, scenario_custom_num,
|
||||
create_user, system_id, mock_tcp_port,
|
||||
is_mock_tcp_open, azure_filter_id, platform,
|
||||
third_part_template)
|
||||
values (#{id,jdbcType=VARCHAR}, #{workspaceId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
|
||||
#{description,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
|
||||
#{tapdId,jdbcType=VARCHAR}, #{jiraKey,jdbcType=VARCHAR}, #{zentaoId,jdbcType=VARCHAR},
|
||||
#{azureDevopsId,jdbcType=VARCHAR}, #{repeatable,jdbcType=BIT}, #{caseTemplateId,jdbcType=VARCHAR},
|
||||
#{issueTemplateId,jdbcType=VARCHAR}, #{customNum,jdbcType=BIT}, #{scenarioCustomNum,jdbcType=BIT},
|
||||
#{createUser,jdbcType=VARCHAR}, #{systemId,jdbcType=VARCHAR}, #{mockTcpPort,jdbcType=INTEGER},
|
||||
#{isMockTcpOpen,jdbcType=BIT}, #{azureFilterId,jdbcType=VARCHAR}, #{platform,jdbcType=VARCHAR},
|
||||
#{thirdPartTemplate,jdbcType=BIT})
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.Project">
|
||||
insert into project
|
||||
@ -195,6 +200,12 @@
|
||||
<if test="azureFilterId != null">
|
||||
azure_filter_id,
|
||||
</if>
|
||||
<if test="platform != null">
|
||||
platform,
|
||||
</if>
|
||||
<if test="thirdPartTemplate != null">
|
||||
third_part_template,
|
||||
</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">
|
||||
@ -257,6 +268,12 @@
|
||||
<if test="azureFilterId != null">
|
||||
#{azureFilterId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="platform != null">
|
||||
#{platform,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="thirdPartTemplate != null">
|
||||
#{thirdPartTemplate,jdbcType=BIT},
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
<select id="countByExample" parameterType="io.metersphere.base.domain.ProjectExample" resultType="java.lang.Long">
|
||||
@ -328,6 +345,12 @@
|
||||
<if test="record.azureFilterId != null">
|
||||
azure_filter_id = #{record.azureFilterId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.platform != null">
|
||||
platform = #{record.platform,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.thirdPartTemplate != null">
|
||||
third_part_template = #{record.thirdPartTemplate,jdbcType=BIT},
|
||||
</if>
|
||||
</set>
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
@ -354,7 +377,9 @@
|
||||
system_id = #{record.systemId,jdbcType=VARCHAR},
|
||||
mock_tcp_port = #{record.mockTcpPort,jdbcType=INTEGER},
|
||||
is_mock_tcp_open = #{record.isMockTcpOpen,jdbcType=BIT},
|
||||
azure_filter_id = #{record.azureFilterId,jdbcType=VARCHAR}
|
||||
azure_filter_id = #{record.azureFilterId,jdbcType=VARCHAR},
|
||||
platform = #{record.platform,jdbcType=VARCHAR},
|
||||
third_part_template = #{record.thirdPartTemplate,jdbcType=BIT}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
@ -419,6 +444,12 @@
|
||||
<if test="azureFilterId != null">
|
||||
azure_filter_id = #{azureFilterId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="platform != null">
|
||||
platform = #{platform,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="thirdPartTemplate != null">
|
||||
third_part_template = #{thirdPartTemplate,jdbcType=BIT},
|
||||
</if>
|
||||
</set>
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
@ -442,7 +473,9 @@
|
||||
system_id = #{systemId,jdbcType=VARCHAR},
|
||||
mock_tcp_port = #{mockTcpPort,jdbcType=INTEGER},
|
||||
is_mock_tcp_open = #{isMockTcpOpen,jdbcType=BIT},
|
||||
azure_filter_id = #{azureFilterId,jdbcType=VARCHAR}
|
||||
azure_filter_id = #{azureFilterId,jdbcType=VARCHAR},
|
||||
platform = #{platform,jdbcType=VARCHAR},
|
||||
third_part_template = #{thirdPartTemplate,jdbcType=BIT}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
</mapper>
|
||||
</mapper>
|
||||
|
@ -59,10 +59,6 @@
|
||||
from issues
|
||||
left join
|
||||
test_case_issues on issues.id = test_case_issues.issues_id
|
||||
<if test="request.projectId != null||request.workspaceId != null">
|
||||
left join
|
||||
project on issues.project_id = project.id
|
||||
</if>
|
||||
<include refid="queryWhereCondition"/>
|
||||
and (test_case_issues.test_case_id is null or test_case_issues.test_case_id != #{request.caseId})
|
||||
and (issues.platform_status != 'delete' or issues.platform_status is NULL)
|
||||
|
@ -52,7 +52,8 @@
|
||||
user.name AS createUserName,
|
||||
p.mock_tcp_port AS mockTcpPort,
|
||||
p.is_mock_tcp_open AS isMockTcpOpen,
|
||||
p.scenario_custom_num
|
||||
p.scenario_custom_num,
|
||||
p.platform, p.third_part_template
|
||||
FROM project p
|
||||
JOIN workspace w ON p.workspace_id = w.id
|
||||
LEFT JOIN user ON user.id = p.create_user
|
||||
|
@ -12,6 +12,7 @@ import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.*;
|
||||
import io.metersphere.controller.request.IntegrationRequest;
|
||||
import io.metersphere.dto.CustomFieldItemDTO;
|
||||
import io.metersphere.dto.IssueTemplateDao;
|
||||
import io.metersphere.dto.UserDTO;
|
||||
import io.metersphere.service.*;
|
||||
import io.metersphere.track.request.testcase.IssuesRequest;
|
||||
@ -363,7 +364,6 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
protected String syncIssueCustomField(String customFieldsStr, JSONObject issue) {
|
||||
List<CustomFieldItemDTO> customFields = CustomFieldService.getCustomFields(customFieldsStr);
|
||||
customFields.forEach(item -> {
|
||||
@ -395,6 +395,9 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform {
|
||||
@Override
|
||||
public void syncAllIssues(Project project) {}
|
||||
|
||||
@Override
|
||||
public IssueTemplateDao getThirdPartTemplate() {return null;}
|
||||
|
||||
protected List<IssuesWithBLOBs> getIssuesByPlatformIds(List<String> platformIds) {
|
||||
IssuesService issuesService = CommonBeanFactory.getBean(IssuesService.class);
|
||||
return issuesService.getIssuesByPlatformIds(platformIds, projectId);
|
||||
|
@ -3,6 +3,7 @@ package io.metersphere.track.issue;
|
||||
import io.metersphere.base.domain.IssuesDao;
|
||||
import io.metersphere.base.domain.IssuesWithBLOBs;
|
||||
import io.metersphere.base.domain.Project;
|
||||
import io.metersphere.dto.IssueTemplateDao;
|
||||
import io.metersphere.dto.UserDTO;
|
||||
import io.metersphere.track.dto.DemandDTO;
|
||||
import io.metersphere.track.issue.domain.PlatformUser;
|
||||
@ -71,4 +72,10 @@ public interface IssuesPlatform {
|
||||
* @param project
|
||||
*/
|
||||
void syncAllIssues(Project project);
|
||||
|
||||
/**
|
||||
* 获取第三方平台缺陷模板
|
||||
* @return
|
||||
*/
|
||||
IssueTemplateDao getThirdPartTemplate();
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import io.metersphere.track.issue.domain.jira.JiraConfig;
|
||||
import io.metersphere.track.issue.domain.jira.JiraIssue;
|
||||
import io.metersphere.track.request.testcase.IssuesRequest;
|
||||
import io.metersphere.track.request.testcase.IssuesUpdateRequest;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.commonmark.node.Node;
|
||||
import org.commonmark.parser.Parser;
|
||||
@ -148,9 +149,7 @@ public class JiraPlatform extends AbstractIssuePlatform {
|
||||
|
||||
List<File> imageFiles = getImageFiles(issuesRequest.getDescription());
|
||||
|
||||
imageFiles.forEach(img -> {
|
||||
jiraClientV2.uploadAttachment(result.getKey(), img);
|
||||
});
|
||||
imageFiles.forEach(img -> jiraClientV2.uploadAttachment(result.getKey(), img));
|
||||
|
||||
String status = getStatus(issues.getFields());
|
||||
issuesRequest.setPlatformStatus(status);
|
||||
@ -184,50 +183,78 @@ public class JiraPlatform extends AbstractIssuePlatform {
|
||||
|
||||
JSONObject issuetype = new JSONObject();
|
||||
issuetype.put("name", issuetypeStr);
|
||||
|
||||
fields.put("summary", issuesRequest.getTitle());
|
||||
// fields.put("description", new JiraIssueDescription(desc));
|
||||
fields.put("description", desc);
|
||||
fields.put("issuetype", issuetype);
|
||||
|
||||
JSONObject addJiraIssueParam = new JSONObject();
|
||||
addJiraIssueParam.put("fields", fields);
|
||||
|
||||
if (issuesRequest.isThirdPartPlatform()) {
|
||||
parseCustomFiled(issuesRequest, fields);
|
||||
issuesRequest.setTitle(fields.getString("summary"));
|
||||
} else {
|
||||
fields.put("summary", issuesRequest.getTitle());
|
||||
fields.put("description", desc);
|
||||
parseCustomFiled(issuesRequest, fields);
|
||||
}
|
||||
|
||||
return addJiraIssueParam;
|
||||
}
|
||||
|
||||
private void parseCustomFiled(IssuesUpdateRequest issuesRequest, JSONObject fields) {
|
||||
List<CustomFieldItemDTO> customFields = CustomFieldService.getCustomFields(issuesRequest.getCustomFields());
|
||||
|
||||
customFields.forEach(item -> {
|
||||
String fieldName = item.getCustomData();
|
||||
if (StringUtils.isNotBlank(fieldName)) {
|
||||
if (item.getValue() != null) {
|
||||
if (StringUtils.isNotBlank(item.getType()) &&
|
||||
StringUtils.equalsAny(item.getType(), "select", "radio", "member")) {
|
||||
JSONObject param = new JSONObject();
|
||||
if (fieldName.equals("assignee") || fieldName.equals("reporter")) {
|
||||
param.put("name", item.getValue());
|
||||
if (StringUtils.isNotBlank(item.getType())) {
|
||||
if (StringUtils.equalsAny(item.getType(), "select", "radio", "member")) {
|
||||
JSONObject param = new JSONObject();
|
||||
if (fieldName.equals("assignee") || fieldName.equals("reporter")) {
|
||||
if (issuesRequest.isThirdPartPlatform()) {
|
||||
param.put("id", item.getValue());
|
||||
} else {
|
||||
param.put("name", item.getValue());
|
||||
}
|
||||
} else {
|
||||
param.put("id", item.getValue());
|
||||
}
|
||||
fields.put(fieldName, param);
|
||||
} else if (StringUtils.equalsAny(item.getType(), "multipleSelect", "checkbox", "multipleMember")) {
|
||||
JSONArray attrs = new JSONArray();
|
||||
if (item.getValue() != null) {
|
||||
JSONArray values = (JSONArray)item.getValue();
|
||||
values.forEach(v -> {
|
||||
JSONObject param = new JSONObject();
|
||||
param.put("id", v);
|
||||
attrs.add(param);
|
||||
});
|
||||
}
|
||||
fields.put(fieldName, attrs);
|
||||
} else if (StringUtils.equalsAny(item.getType(), "cascadingSelect")) {
|
||||
if (item.getValue() != null) {
|
||||
JSONArray values = (JSONArray)item.getValue();
|
||||
JSONObject attr = new JSONObject();
|
||||
if (CollectionUtils.isNotEmpty(values)) {
|
||||
if (values.size() > 0) {
|
||||
attr.put("id", values.get(0));
|
||||
}
|
||||
if (values.size() > 1) {
|
||||
JSONObject param = new JSONObject();
|
||||
param.put("id", values.get(1));
|
||||
attr.put("child", param);
|
||||
}
|
||||
}
|
||||
fields.put(fieldName, attr);
|
||||
}
|
||||
} else {
|
||||
param.put("id", item.getValue());
|
||||
fields.put(fieldName, item.getValue());
|
||||
}
|
||||
fields.put(fieldName, param);
|
||||
} else if (StringUtils.isNotBlank(item.getType()) &&
|
||||
StringUtils.equalsAny(item.getType(), "multipleSelect", "checkbox", "multipleMember")) {
|
||||
JSONArray attrs = new JSONArray();
|
||||
if (item.getValue() != null) {
|
||||
JSONArray values = (JSONArray)item.getValue();
|
||||
values.forEach(v -> {
|
||||
JSONObject param = new JSONObject();
|
||||
param.put("id", v);
|
||||
attrs.add(param);
|
||||
});
|
||||
}
|
||||
fields.put(fieldName, attrs);
|
||||
} else {
|
||||
fields.put(fieldName, item.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return addJiraIssueParam;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -4,11 +4,7 @@ import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.track.issue.domain.jira.JiraAddIssueResponse;
|
||||
import io.metersphere.track.issue.domain.jira.JiraConfig;
|
||||
import io.metersphere.track.issue.domain.jira.JiraField;
|
||||
import io.metersphere.track.issue.domain.jira.JiraIssue;
|
||||
import io.metersphere.track.issue.domain.jira.JiraIssueListResponse;
|
||||
import io.metersphere.track.issue.domain.jira.*;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.*;
|
||||
@ -16,7 +12,9 @@ import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class JiraAbstractClient extends BaseClient {
|
||||
|
||||
@ -35,6 +33,34 @@ public abstract class JiraAbstractClient extends BaseClient {
|
||||
return (JiraIssue) getResultForObject(JiraIssue.class, responseEntity);
|
||||
}
|
||||
|
||||
public Map<String, JiraCreateMetadataResponse.Field> getCreateMetadata(String projectKey, String issueType) {
|
||||
String url = getBaseUrl() + "/issue/createmeta?projectKeys={1}&issuetypeNames={2}&expand=projects.issuetypes.fields";
|
||||
ResponseEntity<String> response = null;
|
||||
try {
|
||||
response = restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), String.class, projectKey, issueType);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
Map<String, JiraCreateMetadataResponse.Field> fields = ((JiraCreateMetadataResponse) getResultForObject(JiraCreateMetadataResponse.class, response))
|
||||
.getProjects().get(0).getIssuetypes().get(0).getFields();
|
||||
fields.remove("project");
|
||||
fields.remove("issuetype");
|
||||
return fields;
|
||||
}
|
||||
|
||||
public List<JiraUser> getAssignableUser(String projectKey) {
|
||||
String url = getBaseUrl() + "/user/assignable/search?project={1}";
|
||||
ResponseEntity<String> response = null;
|
||||
try {
|
||||
response = restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), String.class, projectKey);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
return (List<JiraUser>) getResultForList(JiraUser.class, response);
|
||||
}
|
||||
|
||||
public JSONArray getDemands(String projectKey, String issueType, int startAt, int maxResults) {
|
||||
String jql = getBaseUrl() + "/search?jql=project=" + projectKey + "+AND+issuetype=" + issueType
|
||||
+ "&maxResults=" + maxResults + "&startAt=" + startAt + "&fields=summary,issuetype";
|
||||
|
@ -0,0 +1,61 @@
|
||||
package io.metersphere.track.issue.domain.jira;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class JiraCreateMetadataResponse {
|
||||
private List<Projects> projects;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class Projects {
|
||||
private List<Issuetypes> issuetypes;
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class Issuetypes {
|
||||
private Map<String, Field> fields;
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class Field {
|
||||
private boolean required;
|
||||
private Schema schema;
|
||||
private String name;
|
||||
private String key;
|
||||
private String autoCompleteUrl;
|
||||
private boolean hasDefaultValue;
|
||||
private Object defaultValue;
|
||||
private List<AllowedValues> allowedValues;
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class Schema {
|
||||
private String type;
|
||||
private String items;
|
||||
private String custom;
|
||||
private int customId;
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class AllowedValues {
|
||||
private String self;
|
||||
private String id;
|
||||
private String description;
|
||||
private String name;
|
||||
private String value;
|
||||
private boolean subtask;
|
||||
private int avatarId;
|
||||
private int hierarchyLevel;
|
||||
private List<AllowedValues> children;
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package io.metersphere.track.issue.domain.jira;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class JiraUser {
|
||||
private String accountId;
|
||||
private String displayName;
|
||||
private String emailAddress;
|
||||
private Boolean active;
|
||||
}
|
@ -25,5 +25,7 @@ public class IssuesUpdateRequest extends IssuesWithBLOBs {
|
||||
private List<String> zentaoBuilds;
|
||||
private List<String> testCaseIds;
|
||||
|
||||
private boolean thirdPartPlatform;
|
||||
|
||||
private List<String> follows;
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 269d23d5004ee49a9e82a977027ca72757d3056a
|
||||
Subproject commit a99681fe94107d42ff340f4e8dcdca95195cf884
|
@ -1,2 +1,11 @@
|
||||
-- 新增字段
|
||||
ALTER TABLE `swagger_url_project` ADD COLUMN `config` longtext COMMENT '鉴权配置信息' AFTER `mode_id`;
|
||||
ALTER TABLE `swagger_url_project` ADD COLUMN `config` longtext COMMENT '鉴权配置信息' AFTER `mode_id`;
|
||||
|
||||
-- 第三方平台模板
|
||||
ALTER TABLE project ADD platform varchar(20) DEFAULT 'Local' NOT NULL COMMENT '项目使用哪个平台的模板';
|
||||
ALTER TABLE project ADD third_part_template tinyint(1) DEFAULT 0 NULL COMMENT '是否使用第三方平台缺陷模板';
|
||||
|
||||
-- 处理历史数据
|
||||
UPDATE issue_template SET platform = 'Local' WHERE platform = 'metersphere';
|
||||
UPDATE project p JOIN issue_template it on p.issue_template_id = it.id SET p.platform = it.platform;
|
||||
UPDATE custom_field SET `type` = 'date' WHERE `type` = 'data';
|
||||
|
@ -7,11 +7,23 @@
|
||||
<el-input v-model="form.name" autocomplete="off"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="platformOptions.length > 1" :label-width="labelWidth" :label="$t('集成第三方平台')" prop="platform">
|
||||
<el-select filterable v-model="form.platform">
|
||||
<el-option v-for="item in platformOptions" :key="item.value" :label="item.text" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label-width="labelWidth" :label="$t('workspace.case_template_manage')" prop="caseTemplateId">
|
||||
<template-select :data="form" scene="API_CASE" prop="caseTemplateId" ref="caseTemplate"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label-width="labelWidth" :label="$t('workspace.issue_template_manage')" prop="issueTemplateId">
|
||||
<template-select :data="form" scene="ISSUE" prop="issueTemplateId" ref="issueTemplate"/>
|
||||
|
||||
<el-form-item v-if="xpackEable" :label-width="labelWidth" :label="$t('使用第三方平台模板')" prop="scenarioCustomNum">
|
||||
<el-switch v-model="form.thirdPartTemplate"></el-switch>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="!xpackEable || !form.thirdPartTemplate" :label-width="labelWidth" :label="$t('workspace.issue_template_manage')" prop="issueTemplateId">
|
||||
<template-select :platform="form.platform" :data="form" scene="ISSUE" prop="issueTemplateId" ref="issueTemplate"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label-width="labelWidth" label="TCP Mock Port">
|
||||
@ -79,12 +91,12 @@
|
||||
import {
|
||||
getCurrentProjectID,
|
||||
getCurrentUser, getCurrentUserId,
|
||||
getCurrentWorkspaceId,
|
||||
getCurrentWorkspaceId, hasLicense,
|
||||
listenGoBack,
|
||||
removeGoBackListener
|
||||
} from "@/common/js/utils";
|
||||
|
||||
import {PROJECT_ID} from "@/common/js/constants";
|
||||
import {AZURE_DEVOPS, JIRA, PROJECT_ID, TAPD, ZEN_TAO} from "@/common/js/constants";
|
||||
import {PROJECT_CONFIGS} from "@/business/components/common/components/search/search-components";
|
||||
import MsInstructionsIcon from "@/business/components/common/components/MsInstructionsIcon";
|
||||
import TemplateSelect from "@/business/components/settings/workspace/template/TemplateSelect";
|
||||
@ -102,9 +114,10 @@ import MsTableOperator from "@/business/components/common/components/MsTableOper
|
||||
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
|
||||
import MsTableHeader from "@/business/components/common/components/MsTableHeader";
|
||||
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
|
||||
import {ISSUE_PLATFORM_OPTION} from "@/common/js/table-constants";
|
||||
|
||||
export default {
|
||||
name: "MsProject",
|
||||
name: "EditProject",
|
||||
components: {
|
||||
MsInstructionsIcon,
|
||||
TemplateSelect,
|
||||
@ -126,10 +139,6 @@ export default {
|
||||
title: this.$t('project.create'),
|
||||
condition: {components: PROJECT_CONFIGS},
|
||||
items: [],
|
||||
tapd: false,
|
||||
jira: false,
|
||||
zentao: false,
|
||||
azuredevops: false,
|
||||
form: {},
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
@ -147,7 +156,9 @@ export default {
|
||||
// issueTemplateId: [{required: true}],
|
||||
},
|
||||
screenHeight: 'calc(100vh - 195px)',
|
||||
labelWidth: '150px'
|
||||
labelWidth: '150px',
|
||||
platformOptions: [],
|
||||
xpackEable: false
|
||||
};
|
||||
},
|
||||
props: {
|
||||
@ -161,11 +172,24 @@ export default {
|
||||
this.create();
|
||||
this.$router.replace('/setting/project/all');
|
||||
}
|
||||
this.xpackEable = hasLicense();
|
||||
},
|
||||
computed: {
|
||||
currentUser: () => {
|
||||
return getCurrentUser();
|
||||
}
|
||||
},
|
||||
tapd() {
|
||||
return this.form.platform === TAPD && this.platformOptions.map(i => i.value).indexOf(TAPD) > -1;
|
||||
},
|
||||
jira() {
|
||||
return this.form.platform === JIRA && this.platformOptions.map(i => i.value).indexOf(JIRA) > -1;
|
||||
},
|
||||
zentao() {
|
||||
return this.form.platform === ZEN_TAO && this.platformOptions.map(i => i.value).indexOf(ZEN_TAO) > -1;
|
||||
},
|
||||
azuredevops() {
|
||||
return this.form.platform === AZURE_DEVOPS && this.platformOptions.map(i => i.value).indexOf(AZURE_DEVOPS) > -1;
|
||||
},
|
||||
},
|
||||
inject: [
|
||||
'reload'
|
||||
@ -187,24 +211,32 @@ export default {
|
||||
this.getOptions();
|
||||
this.createVisible = true;
|
||||
listenGoBack(this.handleClose);
|
||||
this.form = Object.assign({}, row);
|
||||
if (row) {
|
||||
this.form = Object.assign({}, row);
|
||||
} else {
|
||||
this.form = {};
|
||||
}
|
||||
this.platformOptions = [];
|
||||
this.platformOptions.push(...ISSUE_PLATFORM_OPTION);
|
||||
this.$get("/service/integration/all/" + getCurrentUser().lastWorkspaceId, 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;
|
||||
}
|
||||
if (platforms.indexOf("Zentao") !== -1) {
|
||||
this.zentao = true;
|
||||
}
|
||||
if (platforms.indexOf("AzureDevops") !== -1) {
|
||||
this.azuredevops = true;
|
||||
}
|
||||
this.filterPlatformOptions(platforms, TAPD);
|
||||
this.filterPlatformOptions(platforms, JIRA);
|
||||
this.filterPlatformOptions(platforms, ZEN_TAO);
|
||||
this.filterPlatformOptions(platforms, AZURE_DEVOPS);
|
||||
});
|
||||
},
|
||||
filterPlatformOptions(platforms, platform) {
|
||||
if (platforms.indexOf(platform) === -1) {
|
||||
for (let i = 0; i < this.platformOptions.length; i++) {
|
||||
if (this.platformOptions[i].value === platform) {
|
||||
this.platformOptions.splice(1, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
submit(formName) {
|
||||
this.$refs[formName].validate((valid) => {
|
||||
if (valid) {
|
||||
@ -254,10 +286,6 @@ export default {
|
||||
handleClose() {
|
||||
removeGoBackListener(this.handleClose);
|
||||
this.createVisible = false;
|
||||
this.tapd = false;
|
||||
this.jira = false;
|
||||
this.zentao = false;
|
||||
this.azuredevops = false;
|
||||
},
|
||||
chengeMockTcpSwitch(value){
|
||||
if(value && this.form.mockTcpPort === 0){
|
||||
|
@ -93,77 +93,7 @@
|
||||
:total="total"/>
|
||||
</el-card>
|
||||
|
||||
<el-dialog :close-on-click-modal="false" :title="title" :visible.sync="createVisible" destroy-on-close
|
||||
@close="handleClose">
|
||||
<el-form :model="form" :rules="rules" ref="form" label-position="right" label-width="80px" size="small">
|
||||
<el-form-item :label-width="labelWidth" :label="$t('commons.name')" prop="name">
|
||||
<el-input v-model="form.name" autocomplete="off"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item :label-width="labelWidth" :label="$t('用例模板')" prop="caseTemplateId">
|
||||
<template-select :data="form" scene="API_CASE" prop="caseTemplateId" ref="caseTemplate"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label-width="labelWidth" :label="$t('缺陷模板')" prop="issueTemplateId">
|
||||
<template-select :data="form" scene="ISSUE" prop="issueTemplateId" ref="issueTemplate"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label-width="labelWidth" label="TCP Mock Port">
|
||||
<el-input-number v-model="form.mockTcpPort" :controls="false"
|
||||
style="width: 37%;margin-right: 30px"></el-input-number>
|
||||
<el-switch v-model="form.isMockTcpOpen" @change="chengeMockTcpSwitch"></el-switch>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label-width="labelWidth" :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-width="labelWidth" :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-width="labelWidth" :label="$t('project.jira_key')" v-if="jira">
|
||||
<el-input v-model="form.jiraKey" autocomplete="off" />
|
||||
<ms-instructions-icon effect="light">
|
||||
<template>
|
||||
<img class="jira-image" src="../../../../assets/jira-key.png"/>
|
||||
</template>
|
||||
</ms-instructions-icon>
|
||||
</el-form-item>
|
||||
<el-form-item :label-width="labelWidth" :label="$t('project.zentao_id')" v-if="zentao">
|
||||
<el-input v-model="form.zentaoId" autocomplete="off" ></el-input>
|
||||
<ms-instructions-icon effect="light">
|
||||
<template>
|
||||
禅道流程:产品-项目 | 产品-迭代 | 产品-冲刺 | 项目-迭代 | 项目-冲刺 <br/><br/>
|
||||
根据 "后台 -> 自定义 -> 流程" 查看对应流程,根据流程填写ID <br/><br/>
|
||||
产品-项目 | 产品-迭代 | 产品-冲刺 需要填写产品ID <br/><br/>
|
||||
项目-迭代 | 项目-冲刺 需要填写项目ID
|
||||
</template>
|
||||
</ms-instructions-icon>
|
||||
</el-form-item>
|
||||
<el-form-item :label-width="labelWidth" :label="$t('project.azureDevops_id')" v-if="azuredevops">
|
||||
<el-input v-model="form.azureDevopsId" autocomplete="off" ></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label-width="labelWidth" :label="$t('project.azureDevops_filter_id')" v-if="azuredevops">
|
||||
<el-input v-model="form.azureFilterId" autocomplete="off"/>
|
||||
<ms-instructions-icon content="非必填项,用例关联需求时,可以只筛选出,所填的 workItem 下的选项" effect="light"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label-width="labelWidth" :label="$t('project.repeatable')" prop="repeatable">
|
||||
<el-switch v-model="form.repeatable"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item :label-width="labelWidth" :label="$t('project.test_case_custom_id')" prop="customNum">
|
||||
<el-switch v-model="form.customNum"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item :label-width="labelWidth" :label="$t('project.scenario_custom_id')" prop="scenarioCustomNum">
|
||||
<el-switch v-model="form.scenarioCustomNum"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template v-slot:footer>
|
||||
<div class="dialog-footer">
|
||||
<ms-dialog-footer
|
||||
@cancel="createVisible = false"
|
||||
@confirm="submit('form')"/>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<edit-project ref="editProject"/>
|
||||
|
||||
<el-dialog :close-on-click-modal="false" :visible.sync="memberVisible" width="70%" :destroy-on-close="true"
|
||||
@close="close"
|
||||
@ -280,10 +210,12 @@ import {PROJECT_CONFIGS} from "@/business/components/common/components/search/se
|
||||
import MsRolesTag from "@/business/components/common/components/MsRolesTag";
|
||||
import AddMember from "@/business/components/settings/common/AddMember";
|
||||
import MsInstructionsIcon from "@/business/components/common/components/MsInstructionsIcon";
|
||||
import EditProject from "@/business/components/project/menu/EditProject";
|
||||
|
||||
export default {
|
||||
name: "MsProject",
|
||||
components: {
|
||||
EditProject,
|
||||
MsInstructionsIcon,
|
||||
TemplateSelect,
|
||||
MsResourceFiles,
|
||||
@ -302,7 +234,6 @@ export default {
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
createVisible: false,
|
||||
updateVisible: false,
|
||||
dialogMemberVisible: false,
|
||||
result: {},
|
||||
@ -310,10 +241,6 @@ export default {
|
||||
title: this.$t('project.create'),
|
||||
condition: {components: PROJECT_CONFIGS},
|
||||
items: [],
|
||||
tapd: false,
|
||||
jira: false,
|
||||
zentao: false,
|
||||
azuredevops: false,
|
||||
form: {},
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
@ -371,9 +298,6 @@ export default {
|
||||
return getCurrentWorkspaceId();
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
this.createVisible = false;
|
||||
},
|
||||
methods: {
|
||||
jumpPage(row) {
|
||||
this.currentWorkspaceRow = row;
|
||||
@ -383,7 +307,6 @@ export default {
|
||||
projectId: row.id
|
||||
};
|
||||
this.wsId = row.id;
|
||||
let path = "/user/project/member/list";
|
||||
this.result = this.$post("/user/project/member/list", param, res => {
|
||||
let data = res.data;
|
||||
this.memberLineData = data;
|
||||
@ -398,7 +321,6 @@ export default {
|
||||
|
||||
},
|
||||
getMaintainerOptions() {
|
||||
let workspaceId = getCurrentWorkspaceId();
|
||||
this.$post('/user/project/member/tester/list', {projectId: getCurrentProjectID()}, response => {
|
||||
this.userFilters = response.data.map(u => {
|
||||
return {text: u.name, value: u.id};
|
||||
@ -407,73 +329,17 @@ export default {
|
||||
},
|
||||
create() {
|
||||
let workspaceId = getCurrentWorkspaceId();
|
||||
this.getOptions();
|
||||
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 = {};
|
||||
},
|
||||
getOptions() {
|
||||
|
||||
if (this.$refs.issueTemplate) {
|
||||
this.$refs.issueTemplate.getTemplateOptions();
|
||||
}
|
||||
if (this.$refs.caseTemplate) {
|
||||
this.$refs.caseTemplate.getTemplateOptions();
|
||||
}
|
||||
this.$refs.editProject.edit();
|
||||
},
|
||||
edit(row) {
|
||||
this.title = this.$t('project.edit');
|
||||
this.getOptions();
|
||||
this.createVisible = true;
|
||||
listenGoBack(this.handleClose);
|
||||
this.form = Object.assign({}, row);
|
||||
this.$get("/service/integration/all/" + getCurrentWorkspaceId(), 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;
|
||||
}
|
||||
if (platforms.indexOf("Zentao") !== -1) {
|
||||
this.zentao = true;
|
||||
}
|
||||
if (platforms.indexOf("AzureDevops") !== -1) {
|
||||
this.azuredevops = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
submit(formName) {
|
||||
this.$refs[formName].validate((valid) => {
|
||||
if (valid) {
|
||||
let saveType = "add";
|
||||
if (this.form.id) {
|
||||
saveType = "update";
|
||||
}
|
||||
var protocol = document.location.protocol;
|
||||
protocol = protocol.substring(0, protocol.indexOf(":"));
|
||||
this.form.protocal = protocol;
|
||||
this.form.workspaceId = getCurrentWorkspaceId();
|
||||
this.form.createUser = getCurrentUserId();
|
||||
this.result = this.$post("/project/" + saveType, this.form, () => {
|
||||
this.createVisible = false;
|
||||
Message.success(this.$t('commons.save_success'));
|
||||
if (saveType === 'add') {
|
||||
this.reloadTopMenus();
|
||||
} else {
|
||||
this.list();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
this.$refs.editProject.edit(row);
|
||||
},
|
||||
openJarConfig() {
|
||||
this.$refs.jarConfig.open();
|
||||
@ -507,11 +373,6 @@ export default {
|
||||
},
|
||||
handleClose() {
|
||||
removeGoBackListener(this.handleClose);
|
||||
this.createVisible = false;
|
||||
this.tapd = false;
|
||||
this.jira = false;
|
||||
this.zentao = false;
|
||||
this.azuredevops = false;
|
||||
},
|
||||
search() {
|
||||
this.list();
|
||||
|
@ -14,6 +14,14 @@
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
<el-cascader
|
||||
v-else-if="data.type === 'cascadingSelect'"
|
||||
expand-trigger="hover"
|
||||
@change="handleChange"
|
||||
:options="data.options"
|
||||
v-model="data[prop]">
|
||||
</el-cascader>
|
||||
|
||||
<el-input
|
||||
v-else-if="data.type === 'textarea'"
|
||||
type="textarea"
|
||||
@ -52,21 +60,21 @@
|
||||
v-else-if="data.type === 'int'"
|
||||
v-model="data[prop]"
|
||||
:disabled="disabled"
|
||||
@change="handleChange"></el-input-number>
|
||||
@change="handleChange"/>
|
||||
|
||||
<el-input-number
|
||||
v-else-if="data.type === 'float'"
|
||||
:disabled="disabled"
|
||||
@change="handleChange"
|
||||
v-model="data[prop]" :precision="2" :step="0.1"></el-input-number>
|
||||
v-model="data[prop]" :precision="2" :step="0.1"/>
|
||||
|
||||
<el-date-picker
|
||||
class="custom-with"
|
||||
@change="handleChange"
|
||||
v-else-if="data.type === 'data'"
|
||||
v-else-if="data.type === 'date' || data.type === 'datetime'"
|
||||
:disabled="disabled"
|
||||
v-model="data[prop]"
|
||||
type="date"
|
||||
:type="data.type === 'date' ? 'date' : 'datetime'"
|
||||
:placeholder="$t('commons.select_date')">
|
||||
</el-date-picker>
|
||||
|
||||
@ -84,10 +92,16 @@
|
||||
</el-select>
|
||||
|
||||
<ms-input-tag v-else-if="data.type === 'multipleInput'"
|
||||
@input="handleChange"
|
||||
:read-only="disabled" :currentScenario="data" :prop="prop"/>
|
||||
|
||||
<ms-mark-down-text v-else-if="data.type === 'richText'"
|
||||
:prop="prop"
|
||||
@change="handleChange"
|
||||
:data="data" :disabled="disabled"/>
|
||||
|
||||
<el-input class="custom-with"
|
||||
@change="handleChange"
|
||||
@input="handleChange"
|
||||
:disabled="disabled"
|
||||
v-else v-model="data[prop]"/>
|
||||
|
||||
@ -97,11 +111,12 @@
|
||||
|
||||
<script>
|
||||
import MsTableColumn from "@/business/components/common/components/table/MsTableColumn";
|
||||
import {getCurrentProjectID, getCurrentWorkspaceId} from "@/common/js/utils";
|
||||
import {getCurrentProjectID} from "@/common/js/utils";
|
||||
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
|
||||
import MsMarkDownText from "@/business/components/track/case/components/MsMarkDownText";
|
||||
export default {
|
||||
name: "CustomFiledComponent",
|
||||
components: {MsInputTag, MsTableColumn},
|
||||
components: {MsMarkDownText, MsInputTag, MsTableColumn},
|
||||
props: [
|
||||
'data',
|
||||
'prop',
|
||||
@ -113,6 +128,9 @@ export default {
|
||||
memberOptions: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initOption(this.data.options);
|
||||
},
|
||||
mounted() {
|
||||
this.$post('/user/project/member/tester/list', {projectId: getCurrentProjectID()}, response => {
|
||||
this.memberOptions = response.data;
|
||||
@ -125,7 +143,15 @@ export default {
|
||||
handleChange() {
|
||||
if (this.form) {
|
||||
this.$set(this.form, this.data.name, this.data[this.prop]);
|
||||
this.$emit('reload');
|
||||
}
|
||||
},
|
||||
initOption(options) {
|
||||
if (options) {
|
||||
options.forEach(i => {
|
||||
i.label = i.text;
|
||||
this.$set(i, 'label', i.text);
|
||||
this.initOption(i.children);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ import CustomFieldFormList from "@/business/components/settings/workspace/templa
|
||||
import CustomFieldRelateList from "@/business/components/settings/workspace/template/CustomFieldRelateList";
|
||||
import FieldTemplateEdit from "@/business/components/settings/workspace/template/FieldTemplateEdit";
|
||||
import FormRIchTextItem from "@/business/components/track/case/components/FormRichTextItem";
|
||||
import {LOCAL} from "@/common/js/constants";
|
||||
|
||||
export default {
|
||||
name: "IssueTemplateEdit",
|
||||
@ -64,7 +65,7 @@ export default {
|
||||
showDialog: false,
|
||||
form: {
|
||||
name: "",
|
||||
platform: 'metersphere',
|
||||
platform: LOCAL,
|
||||
description: '',
|
||||
title: '',
|
||||
content: '',
|
||||
@ -106,7 +107,7 @@ export default {
|
||||
this.form = {
|
||||
id: "",
|
||||
name: "",
|
||||
platform: 'metersphere',
|
||||
platform: LOCAL,
|
||||
description: '',
|
||||
title: '',
|
||||
content: '',
|
||||
|
@ -105,7 +105,7 @@ export default {
|
||||
currentPage: 1,
|
||||
result: {},
|
||||
issuePlatformMap:{
|
||||
metersphere: 'Metersphere',
|
||||
Local: 'Metersphere',
|
||||
Jira: 'JIRA',
|
||||
Tapd: 'Tapd',
|
||||
Zentao: '禅道',
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<el-select filterable v-model="data[prop]">
|
||||
<el-option
|
||||
v-for="(item, index) in templateOptions"
|
||||
v-for="(item, index) in templateFilterOptions"
|
||||
:key="index"
|
||||
:label="item.name"
|
||||
:value="item.id">
|
||||
@ -17,6 +17,7 @@ export default {
|
||||
props: {
|
||||
scene: String,
|
||||
prop: String,
|
||||
platform: String,
|
||||
data: {
|
||||
type: Object,
|
||||
default() {
|
||||
@ -29,12 +30,18 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
templateOptions: []
|
||||
templateOptions: [],
|
||||
templateFilterOptions: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getTemplateOptions();
|
||||
},
|
||||
watch: {
|
||||
platform() {
|
||||
this.filter();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getTemplateOptions() {
|
||||
let url = 'field/template/case/option/';
|
||||
@ -43,6 +50,7 @@ export default {
|
||||
}
|
||||
this.$get(url + getCurrentWorkspaceId(), (response) => {
|
||||
this.templateOptions = response.data;
|
||||
this.templateFilterOptions = this.templateOptions;
|
||||
if (!this.data[this.prop]) {
|
||||
for (let item of this.templateOptions) {
|
||||
if (this.scene !== 'ISSUE') {
|
||||
@ -59,7 +67,25 @@ export default {
|
||||
|
||||
}
|
||||
}
|
||||
this.filter();
|
||||
});
|
||||
},
|
||||
filter() {
|
||||
if (this.platform) {
|
||||
let hasTemplate = false;
|
||||
this.templateFilterOptions = [];
|
||||
this.templateOptions.forEach(i => {
|
||||
if (i.platform === this.platform) {
|
||||
this.templateFilterOptions.push(i);
|
||||
if (i.id === this.data[this.prop])
|
||||
hasTemplate = true;
|
||||
}
|
||||
});
|
||||
if (!hasTemplate)
|
||||
this.data[this.prop] = null;
|
||||
} else {
|
||||
this.templateFilterOptions = this.templateOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,138 +1,17 @@
|
||||
<template>
|
||||
<el-form-item v-loading="result.loading" :disable="true" :label="title" :prop="prop" :label-width="labelWidth">
|
||||
<mavon-editor :id="id" v-if="active" :editable="!disabled" @imgAdd="imgAdd" :default-open="defaultOpen" class="mavon-editor"
|
||||
:xss-options="xssOptions"
|
||||
:subfield="false" :toolbars="toolbars" :language="language" :toolbarsFlag="disabled ? false : true" @imgDel="imgDel" v-model="data[prop]" ref="md"/>
|
||||
<el-form-item :disable="true" :label="title" :prop="prop" :label-width="labelWidth">
|
||||
<ms-mark-down-text :prop="prop" :data="data" :disabled="disabled"/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getCurrentUser, getUUID} from "@/common/js/utils";
|
||||
import MsMarkDownText from "@/business/components/track/case/components/MsMarkDownText";
|
||||
export default {
|
||||
name: "FormRichTextItem",
|
||||
components: {},
|
||||
props: ['data', 'title', 'prop', 'disabled', 'labelWidth'],
|
||||
data() {
|
||||
return {
|
||||
result: {loading: false},
|
||||
id: getUUID(),
|
||||
xssOptions: {
|
||||
whiteList: {
|
||||
img: ["src", "alt", "width", "height"],
|
||||
},
|
||||
stripIgnoreTagBody: true
|
||||
},
|
||||
defaultOpen: 'preview',
|
||||
toolbars: {
|
||||
bold: true, // 粗体
|
||||
italic: true, // 斜体
|
||||
header: true, // 标题
|
||||
underline: true, // 下划线
|
||||
strikethrough: true, // 中划线
|
||||
mark: true, // 标记
|
||||
superscript: true, // 上角标
|
||||
subscript: true, // 下角标
|
||||
quote: true, // 引用
|
||||
ol: true, // 有序列表
|
||||
ul: true, // 无序列表
|
||||
link: true, // 链接
|
||||
imagelink: true, // 图片链接
|
||||
code: true, // code
|
||||
table: true, // 表格
|
||||
fullscreen: true, // 全屏编辑
|
||||
readmodel: true, // 沉浸式阅读
|
||||
htmlcode: true, // 展示html源码
|
||||
help: true, // 帮助
|
||||
/* 1.3.5 */
|
||||
undo: true, // 上一步
|
||||
redo: true, // 下一步
|
||||
trash: true, // 清空
|
||||
save: false, // 保存(触发events中的save事件)
|
||||
/* 1.4.2 */
|
||||
navigation: true, // 导航目录
|
||||
/* 2.1.8 */
|
||||
alignleft: true, // 左对齐
|
||||
aligncenter: true, // 居中
|
||||
alignright: true, // 右对齐
|
||||
/* 2.2.1 */
|
||||
subfield: true, // 单双栏模式
|
||||
preview: true, // 预览
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
active() {
|
||||
if (this.data[this.prop] !== undefined) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
language() {
|
||||
const user = getCurrentUser();
|
||||
const language = user.language;
|
||||
switch (language) {
|
||||
case 'zh_CN':
|
||||
return 'zh-CN';
|
||||
case 'zh_TW':
|
||||
return 'zh-TW';
|
||||
case 'en_US':
|
||||
return 'en';
|
||||
default:
|
||||
return 'zh-CN';
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 点击编辑,失去焦点展示
|
||||
let el = document.getElementById(this.id);
|
||||
if (el) {
|
||||
el.addEventListener('click', () => {
|
||||
let imagePreview = el.getElementsByClassName('v-note-img-wrapper');
|
||||
if (imagePreview.length > 0) { // 图片预览的时候不切换到编辑模式
|
||||
this.defaultOpen = 'preview';
|
||||
} else {
|
||||
this.defaultOpen = null;
|
||||
}
|
||||
});
|
||||
let input = el.getElementsByClassName('auto-textarea-input');
|
||||
input[0].addEventListener('blur', () => {
|
||||
this.defaultOpen = 'preview';
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
imgAdd(pos, file){
|
||||
let param = {
|
||||
id: getUUID().substring(0, 8)
|
||||
};
|
||||
file.prefix = param.id;
|
||||
this.result.loading = true;
|
||||
// 带括号和空格,可能无法展示
|
||||
param.fileName = file.name.replace("(", "").replace(")", "").replace(" ", "");
|
||||
this.$fileUpload('/resource/md/upload', file, null, param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.$refs.md.$img2Url(pos, '/resource/md/get/' + param.id + '_' + param.fileName);
|
||||
this.result.loading = false;
|
||||
});
|
||||
this.$emit('imgAdd', file);
|
||||
},
|
||||
imgDel(file) {
|
||||
if (file) {
|
||||
this.$get('/resource/md/delete/' + file[1].prefix + "_" + file[1].name);
|
||||
}
|
||||
},
|
||||
}
|
||||
components: {MsMarkDownText},
|
||||
props: ['data', 'title', 'prop', 'disabled', 'labelWidth']
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.mavon-editor {
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
/deep/ .v-note-wrapper {
|
||||
position: initial;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -73,6 +73,7 @@ import MsTablePagination from "@/business/components/common/pagination/TablePagi
|
||||
import {getPageInfo} from "@/common/js/tableUtils";
|
||||
import {getCurrentProjectID} from "@/common/js/utils";
|
||||
import {getIssueTemplate} from "../../../../../network/custom-field-template";
|
||||
import {LOCAL} from "@/common/js/constants";
|
||||
export default {
|
||||
name: "IssueRelateList",
|
||||
components: {MsTablePagination, IssueDescriptionTableItem, MsTableColumn, MsTable, MsEditDialog},
|
||||
@ -95,7 +96,7 @@ export default {
|
||||
created() {
|
||||
getIssueTemplate()
|
||||
.then((template) => {
|
||||
if (template.platform === 'metersphere') {
|
||||
if (template.platform === LOCAL) {
|
||||
this.isThirdPart = false;
|
||||
} else {
|
||||
this.isThirdPart = true;
|
||||
|
@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<mavon-editor :id="id" v-if="active" :editable="!disabled" @imgAdd="imgAdd" :default-open="defaultOpen" class="mavon-editor"
|
||||
:xss-options="xssOptions"
|
||||
@change="$emit('change')"
|
||||
:subfield="false" :toolbars="toolbars" :language="language" :toolbarsFlag="disabled ? false : true" @imgDel="imgDel" v-model="data[prop]" ref="md"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getCurrentUser, getUUID} from "@/common/js/utils";
|
||||
export default {
|
||||
name: "MsMarkDownText",
|
||||
components: {},
|
||||
props: ['data', 'prop', 'disabled'],
|
||||
data() {
|
||||
return {
|
||||
result: {loading: false},
|
||||
id: getUUID(),
|
||||
xssOptions: {
|
||||
whiteList: {
|
||||
img: ["src", "alt", "width", "height"],
|
||||
},
|
||||
stripIgnoreTagBody: true
|
||||
},
|
||||
defaultOpen: 'preview',
|
||||
toolbars: {
|
||||
bold: true, // 粗体
|
||||
italic: true, // 斜体
|
||||
header: true, // 标题
|
||||
underline: true, // 下划线
|
||||
strikethrough: true, // 中划线
|
||||
mark: true, // 标记
|
||||
superscript: true, // 上角标
|
||||
subscript: true, // 下角标
|
||||
quote: true, // 引用
|
||||
ol: true, // 有序列表
|
||||
ul: true, // 无序列表
|
||||
link: true, // 链接
|
||||
imagelink: true, // 图片链接
|
||||
code: true, // code
|
||||
table: true, // 表格
|
||||
fullscreen: true, // 全屏编辑
|
||||
readmodel: true, // 沉浸式阅读
|
||||
htmlcode: true, // 展示html源码
|
||||
help: true, // 帮助
|
||||
/* 1.3.5 */
|
||||
undo: true, // 上一步
|
||||
redo: true, // 下一步
|
||||
trash: true, // 清空
|
||||
save: false, // 保存(触发events中的save事件)
|
||||
/* 1.4.2 */
|
||||
navigation: true, // 导航目录
|
||||
/* 2.1.8 */
|
||||
alignleft: true, // 左对齐
|
||||
aligncenter: true, // 居中
|
||||
alignright: true, // 右对齐
|
||||
/* 2.2.1 */
|
||||
subfield: true, // 单双栏模式
|
||||
preview: true, // 预览
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
active() {
|
||||
if (this.data[this.prop] !== undefined) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
language() {
|
||||
const user = getCurrentUser();
|
||||
const language = user.language;
|
||||
switch (language) {
|
||||
case 'zh_CN':
|
||||
return 'zh-CN';
|
||||
case 'zh_TW':
|
||||
return 'zh-TW';
|
||||
case 'en_US':
|
||||
return 'en';
|
||||
default:
|
||||
return 'zh-CN';
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 点击编辑,失去焦点展示
|
||||
let el = document.getElementById(this.id);
|
||||
if (el) {
|
||||
el.addEventListener('click', () => {
|
||||
let imagePreview = el.getElementsByClassName('v-note-img-wrapper');
|
||||
if (imagePreview.length > 0) { // 图片预览的时候不切换到编辑模式
|
||||
this.defaultOpen = 'preview';
|
||||
} else {
|
||||
this.defaultOpen = null;
|
||||
}
|
||||
});
|
||||
let input = el.getElementsByClassName('auto-textarea-input');
|
||||
input[0].addEventListener('blur', () => {
|
||||
this.defaultOpen = 'preview';
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
imgAdd(pos, file){
|
||||
let param = {
|
||||
id: getUUID().substring(0, 8)
|
||||
};
|
||||
file.prefix = param.id;
|
||||
this.result.loading = true;
|
||||
// 带括号和空格,可能无法展示
|
||||
param.fileName = file.name.replace("(", "").replace(")", "").replace(" ", "");
|
||||
this.$fileUpload('/resource/md/upload', file, null, param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.$refs.md.$img2Url(pos, '/resource/md/get/' + param.id + '_' + param.fileName);
|
||||
this.result.loading = false;
|
||||
});
|
||||
this.$emit('imgAdd', file);
|
||||
},
|
||||
imgDel(file) {
|
||||
if (file) {
|
||||
this.$get('/resource/md/delete/' + file[1].prefix + "_" + file[1].name);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.mavon-editor {
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
/deep/ .v-note-wrapper {
|
||||
position: initial;
|
||||
}
|
||||
|
||||
</style>
|
@ -260,7 +260,7 @@ export default {
|
||||
// remark: [{max: 1000, message: this.$t('test_track.length_less_than') + '1000', trigger: 'blur'}]
|
||||
},
|
||||
customFieldRules: {},
|
||||
customFieldForm: {},
|
||||
customFieldForm: null,
|
||||
formLabelWidth: "100px",
|
||||
operationType: '',
|
||||
isCreateContinue: false,
|
||||
@ -433,7 +433,7 @@ export default {
|
||||
}
|
||||
if (this.type === 'add') {
|
||||
//设置自定义熟悉默认值
|
||||
parseCustomField(this.form, this.testCaseTemplate, this.customFieldForm, this.customFieldRules);
|
||||
this.customFieldForm = parseCustomField(this.form, this.testCaseTemplate, this.customFieldRules);
|
||||
this.form.name = this.testCaseTemplate.caseName;
|
||||
this.form.stepDescription = this.testCaseTemplate.stepDescription;
|
||||
this.form.expectedResult = this.testCaseTemplate.expectedResult;
|
||||
@ -539,7 +539,7 @@ export default {
|
||||
this.setTestCaseExtInfo(testCase);
|
||||
this.getSelectOptions();
|
||||
//设置自定义熟悉默认值
|
||||
parseCustomField(this.form, this.testCaseTemplate, this.customFieldForm, this.customFieldRules, buildTestCaseOldFields(this.form));
|
||||
this.customFieldForm = parseCustomField(this.form, this.testCaseTemplate, this.customFieldRules, buildTestCaseOldFields(this.form));
|
||||
this.reload();
|
||||
} else {
|
||||
this.initTestCases(testCase);
|
||||
@ -559,7 +559,7 @@ export default {
|
||||
this.form.maintainer = user.id;
|
||||
this.form.tags = [];
|
||||
this.getSelectOptions();
|
||||
parseCustomField(this.form, this.testCaseTemplate, this.customFieldForm, this.customFieldRules);
|
||||
this.customFieldForm = parseCustomField(this.form, this.testCaseTemplate, this.customFieldRules);
|
||||
this.reload();
|
||||
}
|
||||
},
|
||||
@ -634,7 +634,7 @@ export default {
|
||||
}
|
||||
this.form.module = testCase.nodeId;
|
||||
//设置自定义熟悉默认值
|
||||
parseCustomField(this.form, this.testCaseTemplate, this.customFieldForm, this.customFieldRules, testCase ? buildTestCaseOldFields(this.form) : null);
|
||||
this.customFieldForm = parseCustomField(this.form, this.testCaseTemplate, this.customFieldRules, testCase ? buildTestCaseOldFields(this.form) : null);
|
||||
this.setDefaultValue();
|
||||
// 重新渲染,显示自定义字段的必填校验
|
||||
this.reloadForm();
|
||||
|
@ -100,6 +100,7 @@ import IssueRelateList from "@/business/components/track/case/components/IssueRe
|
||||
import {deleteIssueRelate, getIssuesByCaseId} from "@/network/Issue";
|
||||
import {getIssueTemplate} from "@/network/custom-field-template";
|
||||
import {getCustomFieldValue, getTableHeaderWithCustomFields} from "@/common/js/tableUtils";
|
||||
import {LOCAL} from "@/common/js/constants";
|
||||
export default {
|
||||
name: "TestCaseIssueRelate",
|
||||
components: {IssueRelateList, IssueDescriptionTableItem, MsTableColumn, MsTable, TestPlanIssueEdit},
|
||||
@ -134,7 +135,7 @@ export default {
|
||||
getIssueTemplate()
|
||||
.then((template) => {
|
||||
this.issueTemplate = template;
|
||||
if (this.issueTemplate.platform === 'metersphere') {
|
||||
if (this.issueTemplate.platform === LOCAL) {
|
||||
this.isThirdPart = false;
|
||||
} else {
|
||||
this.isThirdPart = true;
|
||||
|
@ -3,7 +3,7 @@
|
||||
<el-scrollbar>
|
||||
<el-form :model="form" :rules="rules" label-position="right" label-width="80px" ref="form">
|
||||
|
||||
<el-form-item :label="$t('commons.title')" prop="title">
|
||||
<el-form-item v-if="!enableThirdPartTemplate" :label="$t('commons.title')" prop="title">
|
||||
<el-input v-model="form.title" autocomplete="off" class="top-input-class"></el-input>
|
||||
<el-tooltip :content="$t('commons.follow')" placement="bottom" effect="dark" v-if="!showFollow">
|
||||
<i class="el-icon-star-off" style="color: #783987; font-size: 25px; margin-left: 15px;cursor: pointer;position: relative;top: 5px" @click="saveFollow" />
|
||||
@ -14,21 +14,31 @@
|
||||
</el-form-item>
|
||||
|
||||
<!-- 自定义字段 -->
|
||||
<el-form v-if="isFormAlive" :model="customFieldForm" :rules="customFieldRules" ref="customFieldForm"
|
||||
<el-form :model="customFieldForm" :rules="customFieldRules" ref="customFieldForm"
|
||||
class="case-form">
|
||||
<el-row class="custom-field-row">
|
||||
<el-col :span="8" v-for="(item, index) in issueTemplate.customFields" :key="index">
|
||||
<el-form-item :label="item.system ? $t(systemNameMap[item.name]) : item.name" :prop="item.name"
|
||||
:label-width="formLabelWidth">
|
||||
<custom-filed-component @reload="reloadForm" :data="item" :form="customFieldForm" prop="defaultValue"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<span class="custom-item" v-for="(item, index) in issueTemplate.customFields" :key="index">
|
||||
<el-col :span="8" v-if="item.type !== 'richText'">
|
||||
<el-form-item :label="item.system ? $t(systemNameMap[item.name]) : item.name" :prop="item.name"
|
||||
:label-width="formLabelWidth">
|
||||
<custom-filed-component :data="item" :form="customFieldForm" prop="defaultValue"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<div v-else>
|
||||
<el-col :span="24">
|
||||
<el-form-item :label="item.system ? $t(systemNameMap[item.name]) : item.name" :prop="item.name"
|
||||
:label-width="formLabelWidth">
|
||||
<custom-filed-component :data="item" :form="customFieldForm" prop="defaultValue"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</div>
|
||||
</span>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<form-rich-text-item :title="$t('custom_field.issue_content')" :data="form" prop="description"/>
|
||||
<form-rich-text-item v-if="!enableThirdPartTemplate" :title="$t('custom_field.issue_content')" :data="form" prop="description"/>
|
||||
|
||||
<el-row class="custom-field-row">
|
||||
<el-row v-if="!enableThirdPartTemplate" class="custom-field-row">
|
||||
<el-col :span="8" v-if="hasTapdId">
|
||||
<el-form-item :label-width="formLabelWidth" :label="$t('test_track.issue.tapd_current_owner')"
|
||||
prop="tapdUsers">
|
||||
@ -85,8 +95,17 @@ import {buildCustomFields, parseCustomField} from "@/common/js/custom_field";
|
||||
import CustomFiledComponent from "@/business/components/settings/workspace/template/CustomFiledComponent";
|
||||
import TestCaseIssueList from "@/business/components/track/issue/TestCaseIssueList";
|
||||
import IssueEditDetail from "@/business/components/track/issue/IssueEditDetail";
|
||||
import {getCurrentProjectID, getCurrentUser, getCurrentUserId, getCurrentWorkspaceId} from "@/common/js/utils";
|
||||
import {
|
||||
getCurrentProjectID,
|
||||
getCurrentUser,
|
||||
getCurrentUserId,
|
||||
getCurrentWorkspaceId,
|
||||
hasLicense
|
||||
} from "@/common/js/utils";
|
||||
import {getIssueTemplate} from "@/network/custom-field-template";
|
||||
import {getIssueThirdPartTemplate} from "@/network/Issue";
|
||||
import {getCurrentProject} from "@/network/project";
|
||||
import {JIRA} from "@/common/js/constants";
|
||||
|
||||
export default {
|
||||
name: "IssueEditDetail",
|
||||
@ -105,11 +124,10 @@ export default {
|
||||
issueId:'',
|
||||
result: {},
|
||||
relateFields: [],
|
||||
isFormAlive: true,
|
||||
showFollow:false,
|
||||
formLabelWidth: "150px",
|
||||
issueTemplate: {},
|
||||
customFieldForm: {},
|
||||
customFieldForm: null,
|
||||
customFieldRules: {},
|
||||
rules: {
|
||||
title: [
|
||||
@ -131,7 +149,8 @@ export default {
|
||||
zentaoUsers: [],
|
||||
Builds: [],
|
||||
hasTapdId: false,
|
||||
hasZentaoId: false
|
||||
hasZentaoId: false,
|
||||
currentProject: null
|
||||
};
|
||||
},
|
||||
props: {
|
||||
@ -153,17 +172,35 @@ export default {
|
||||
},
|
||||
projectId() {
|
||||
return getCurrentProjectID();
|
||||
}
|
||||
},
|
||||
enableThirdPartTemplate() {
|
||||
return hasLicense() && this.currentProject && this.currentProject.thirdPartTemplate && this.currentProject.platform === JIRA;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
open(data) {
|
||||
let initAddFuc = this.initEdit;
|
||||
getIssueTemplate()
|
||||
.then((template) => {
|
||||
this.issueTemplate = template;
|
||||
this.getThirdPartyInfo();
|
||||
initAddFuc(data);
|
||||
getCurrentProject((responseData) => {
|
||||
this.currentProject = responseData;
|
||||
this.$nextTick(() => {
|
||||
if (this.enableThirdPartTemplate) {
|
||||
getIssueThirdPartTemplate()
|
||||
.then((template) => {
|
||||
this.issueTemplate = template;
|
||||
this.getThirdPartyInfo();
|
||||
initAddFuc(data);
|
||||
});
|
||||
} else {
|
||||
getIssueTemplate()
|
||||
.then((template) => {
|
||||
this.issueTemplate = template;
|
||||
this.getThirdPartyInfo();
|
||||
initAddFuc(data);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if(data&&data.id){
|
||||
this.$get('/issues/follow/' + data.id, response => {
|
||||
this.form.follows = response.data;
|
||||
@ -218,7 +255,7 @@ export default {
|
||||
this.form.options = data.options ? JSON.parse(data.options) : [];
|
||||
}
|
||||
if (data.id) {
|
||||
this.issueId = data.id
|
||||
this.issueId = data.id
|
||||
this.url = 'issues/update';
|
||||
} else {
|
||||
//copy
|
||||
@ -238,17 +275,13 @@ export default {
|
||||
this.form.creator = getCurrentUserId();
|
||||
}
|
||||
}
|
||||
parseCustomField(this.form, this.issueTemplate, this.customFieldForm, this.customFieldRules);
|
||||
this.customFieldForm = parseCustomField(this.form, this.issueTemplate, this.customFieldRules);
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.testCaseIssueList) {
|
||||
this.$refs.testCaseIssueList.initTableData();
|
||||
}
|
||||
});
|
||||
},
|
||||
reloadForm() {
|
||||
this.isFormAlive = false;
|
||||
this.$nextTick(() => (this.isFormAlive = true));
|
||||
},
|
||||
save() {
|
||||
let isValidate = true;
|
||||
this.$refs['form'].validate((valid) => {
|
||||
@ -281,6 +314,8 @@ export default {
|
||||
if (this.planId) {
|
||||
param.resourceId = this.planId;
|
||||
}
|
||||
|
||||
param.thirdPartPlatform = this.enableThirdPartTemplate;
|
||||
return param;
|
||||
},
|
||||
_save() {
|
||||
|
@ -168,7 +168,7 @@ import {
|
||||
import MsTableHeader from "@/business/components/common/components/MsTableHeader";
|
||||
import IssueDescriptionTableItem from "@/business/components/track/issue/IssueDescriptionTableItem";
|
||||
import IssueEdit from "@/business/components/track/issue/IssueEdit";
|
||||
import {getIssues, syncIssues} from "@/network/Issue";
|
||||
import {getIssues, getIssueThirdPartTemplate, syncIssues} from "@/network/Issue";
|
||||
import {
|
||||
getCustomFieldValue,
|
||||
getCustomTableWidth,
|
||||
@ -176,9 +176,11 @@ import {
|
||||
} from "@/common/js/tableUtils";
|
||||
import MsContainer from "@/business/components/common/components/MsContainer";
|
||||
import MsMainContainer from "@/business/components/common/components/MsMainContainer";
|
||||
import {getCurrentProjectID, getCurrentWorkspaceId} from "@/common/js/utils";
|
||||
import {getCurrentProjectID, getCurrentWorkspaceId, hasLicense} from "@/common/js/utils";
|
||||
import {getIssueTemplate} from "@/network/custom-field-template";
|
||||
import {getProjectMember} from "@/network/user";
|
||||
import {JIRA, LOCAL} from "@/common/js/constants";
|
||||
import {getCurrentProject} from "@/network/project";
|
||||
|
||||
export default {
|
||||
name: "IssueList",
|
||||
@ -215,7 +217,8 @@ export default {
|
||||
issueTemplate: {},
|
||||
members: [],
|
||||
isThirdPart: false,
|
||||
creatorFilters: []
|
||||
creatorFilters: [],
|
||||
currentProject: null
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@ -229,25 +232,21 @@ export default {
|
||||
getProjectMember((data) => {
|
||||
this.members = data;
|
||||
});
|
||||
getIssueTemplate()
|
||||
.then((template) => {
|
||||
this.issueTemplate = template;
|
||||
if (this.issueTemplate.platform === 'metersphere') {
|
||||
this.isThirdPart = false;
|
||||
} else {
|
||||
this.isThirdPart = true;
|
||||
}
|
||||
this.fields = getTableHeaderWithCustomFields('ISSUE_LIST', this.issueTemplate.customFields);
|
||||
if (!this.isThirdPart) {
|
||||
for (let i = 0; i < this.fields.length; i++) {
|
||||
if (this.fields[i].id === 'platformStatus') {
|
||||
this.fields.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.$refs.table) this.$refs.table.reloadTable();
|
||||
});
|
||||
|
||||
getCurrentProject((responseData) => {
|
||||
this.currentProject = responseData;
|
||||
if (hasLicense() && this.currentProject.thirdPartTemplate && this.currentProject.platform === JIRA) {
|
||||
getIssueThirdPartTemplate()
|
||||
.then((template) => {
|
||||
this.initFields(template);
|
||||
});
|
||||
} else {
|
||||
getIssueTemplate()
|
||||
.then((template) => {
|
||||
this.initFields(template);
|
||||
});
|
||||
}
|
||||
});
|
||||
this.getIssues();
|
||||
},
|
||||
computed: {
|
||||
@ -277,6 +276,24 @@ export default {
|
||||
getCustomFieldValue(row, field) {
|
||||
return getCustomFieldValue(row, field, this.members);
|
||||
},
|
||||
initFields(template) {
|
||||
this.issueTemplate = template;
|
||||
if (this.issueTemplate.platform === LOCAL) {
|
||||
this.isThirdPart = false;
|
||||
} else {
|
||||
this.isThirdPart = true;
|
||||
}
|
||||
this.fields = getTableHeaderWithCustomFields('ISSUE_LIST', this.issueTemplate.customFields);
|
||||
if (!this.isThirdPart) {
|
||||
for (let i = 0; i < this.fields.length; i++) {
|
||||
if (this.fields[i].id === 'platformStatus') {
|
||||
this.fields.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.$refs.table) this.$refs.table.reloadTable();
|
||||
},
|
||||
getIssues() {
|
||||
this.page.condition.projectId = this.projectId;
|
||||
this.page.condition.workspaceId= this.workspaceId;
|
||||
@ -311,9 +328,6 @@ export default {
|
||||
});
|
||||
},
|
||||
btnDisable(row) {
|
||||
if (this.issueTemplate.platform == "metersphere" && row.platform == 'Local') {
|
||||
return false;
|
||||
}
|
||||
if (this.issueTemplate.platform !== row.platform) {
|
||||
return true;
|
||||
}
|
||||
|
@ -391,7 +391,7 @@ export default {
|
||||
}
|
||||
}
|
||||
this.testCase = item;
|
||||
parseCustomField(this.testCase, this.testCaseTemplate, null, null, buildTestCaseOldFields(this.testCase));
|
||||
parseCustomField(this.testCase, this.testCaseTemplate, null, buildTestCaseOldFields(this.testCase));
|
||||
this.isCustomFiledActive = true;
|
||||
if (!this.testCase.actualResult) {
|
||||
// 如果没值,使用模板的默认值
|
||||
|
@ -327,7 +327,7 @@ export default {
|
||||
if (!item.stepModel) {
|
||||
item.stepModel = 'STEP';
|
||||
}
|
||||
parseCustomField(item, this.testCaseTemplate, null, null, buildTestCaseOldFields(item));
|
||||
parseCustomField(item, this.testCaseTemplate, null, buildTestCaseOldFields(item));
|
||||
this.isCustomFiledActive = true;
|
||||
this.testCase = item;
|
||||
if (!this.testCase.actualResult) {
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit abb1eae9aff953ca0be49bfb413454e435d627ee
|
||||
Subproject commit 275aff0cdaf7556e02c0fc479cb19d2f962695c2
|
@ -38,6 +38,7 @@ export const EN_US = 'en_US';
|
||||
export const TAPD = 'Tapd';
|
||||
export const JIRA = 'Jira';
|
||||
export const ZEN_TAO = 'Zentao';
|
||||
export const LOCAL = 'Local';
|
||||
export const AZURE_DEVOPS = 'AzureDevops';
|
||||
|
||||
export const GROUP_SYSTEM = 'SYSTEM';
|
||||
|
@ -14,7 +14,7 @@ function setDefaultValue(item, value) {
|
||||
* @param rules 自定义表单的校验规则
|
||||
* @param oldFields 用于兼容旧版本数据
|
||||
*/
|
||||
export function parseCustomField(data, template, customFieldForm, rules, oldFields) {
|
||||
export function parseCustomField(data, template, rules, oldFields) {
|
||||
let hasOldData = false;
|
||||
if (!data.customFields) {
|
||||
// 旧数据
|
||||
@ -25,6 +25,8 @@ export function parseCustomField(data, template, customFieldForm, rules, oldFiel
|
||||
data.customFields = JSON.parse(data.customFields);
|
||||
}
|
||||
|
||||
let customFieldForm = {};
|
||||
|
||||
// 设置页面显示的默认值
|
||||
template.customFields.forEach(item => {
|
||||
|
||||
@ -82,10 +84,10 @@ export function parseCustomField(data, template, customFieldForm, rules, oldFiel
|
||||
}
|
||||
}
|
||||
|
||||
if (customFieldForm) {
|
||||
customFieldForm[item.name] = item.defaultValue;
|
||||
}
|
||||
customFieldForm[item.name] = item.defaultValue;
|
||||
});
|
||||
|
||||
return customFieldForm;
|
||||
}
|
||||
|
||||
// 将template的属性值设置给customFields
|
||||
|
@ -1,5 +1,6 @@
|
||||
// 模板
|
||||
import i18n from "@/i18n/i18n";
|
||||
import {AZURE_DEVOPS, JIRA, LOCAL, TAPD, ZEN_TAO} from "@/common/js/constants";
|
||||
|
||||
export const CUSTOM_FIELD_TYPE_OPTION = [
|
||||
{value: 'input',text: i18n.t('workspace.custom_filed.input')},
|
||||
@ -10,7 +11,9 @@ export const CUSTOM_FIELD_TYPE_OPTION = [
|
||||
{value: 'checkbox',text: i18n.t('workspace.custom_filed.checkbox')},
|
||||
{value: 'member',text: i18n.t('workspace.custom_filed.member')},
|
||||
{value: 'multipleMember',text: i18n.t('workspace.custom_filed.multipleMember')},
|
||||
{value: 'data',text: i18n.t('workspace.custom_filed.data')},
|
||||
{value: 'date',text: i18n.t('workspace.custom_filed.date')},
|
||||
{value: 'datetime',text: i18n.t('workspace.custom_filed.datetime')},
|
||||
{value: 'richText',text: i18n.t('workspace.custom_filed.richText')},
|
||||
{value: 'int',text: i18n.t('workspace.custom_filed.int')},
|
||||
{value: 'float',text: i18n.t('workspace.custom_filed.float')},
|
||||
{value: 'multipleInput',text: i18n.t('workspace.custom_filed.multipleInput')}
|
||||
@ -26,11 +29,11 @@ export const CASE_TYPE_OPTION = [
|
||||
];
|
||||
|
||||
export const ISSUE_PLATFORM_OPTION = [
|
||||
{value: 'Local',text: 'Metersphere'},
|
||||
{value: 'Jira',text: 'JIRA'},
|
||||
{value: 'Tapd',text: 'Tapd'},
|
||||
{value: 'Zentao',text: '禅道'},
|
||||
{value: 'AzureDevops',text: 'Azure Devops'},
|
||||
{value: LOCAL, text: 'Metersphere'},
|
||||
{value: TAPD, text: 'Tapd'},
|
||||
{value: JIRA, text: 'JIRA'},
|
||||
{value: ZEN_TAO, text: '禅道'},
|
||||
{value: AZURE_DEVOPS, text: 'Azure Devops'},
|
||||
];
|
||||
|
||||
export const FIELD_TYPE_MAP = {
|
||||
@ -42,7 +45,9 @@ export const FIELD_TYPE_MAP = {
|
||||
checkbox: 'workspace.custom_filed.checkbox',
|
||||
member: 'workspace.custom_filed.member',
|
||||
multipleMember: 'workspace.custom_filed.multipleMember',
|
||||
data: 'workspace.custom_filed.data',
|
||||
date: 'workspace.custom_filed.date',
|
||||
datetime: 'workspace.custom_filed.datetime',
|
||||
richText: 'workspace.custom_filed.richText',
|
||||
int: 'workspace.custom_filed.int',
|
||||
float: 'workspace.custom_filed.float',
|
||||
multipleInput: 'workspace.custom_filed.multipleInput'
|
||||
|
@ -459,10 +459,12 @@ export function getCustomFieldValue(row, field, members) {
|
||||
return values;
|
||||
}
|
||||
} else if (['radio', 'select'].indexOf(field.type) > -1) {
|
||||
for (let j = 0; j < field.options.length; j++) {
|
||||
let option = field.options[j];
|
||||
if (option.value === item.value) {
|
||||
return field.system ? i18n.t(option.text) : option.text;
|
||||
if (field.options) {
|
||||
for (let j = 0; j < field.options.length; j++) {
|
||||
let option = field.options[j];
|
||||
if (option.value === item.value) {
|
||||
return field.system ? i18n.t(option.text) : option.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -494,7 +494,9 @@ export default {
|
||||
checkbox: 'Checkbox',
|
||||
member: 'Member',
|
||||
multipleMember: 'MultipleMember',
|
||||
data: 'Data',
|
||||
date: 'DatePicker',
|
||||
datetime: 'DateTimePicker',
|
||||
richText: 'RichText',
|
||||
int: 'Int',
|
||||
float: 'Float',
|
||||
multipleInput: 'MultipleInput'
|
||||
|
@ -496,7 +496,9 @@ export default {
|
||||
checkbox: '多选框',
|
||||
member: '单选成员',
|
||||
multipleMember: '多选成员',
|
||||
data: '日期',
|
||||
date: '日期选择器',
|
||||
datetime: '日期时间选择器',
|
||||
richText: '富文本框',
|
||||
int: '整型',
|
||||
float: '浮点型',
|
||||
multipleInput: '多值输入框'
|
||||
|
@ -495,7 +495,9 @@ export default {
|
||||
checkbox: '多選框',
|
||||
member: '單選成員',
|
||||
multipleMember: '多選成員',
|
||||
data: '日期',
|
||||
date: '日期選擇器',
|
||||
datetime: '日期時間選擇器',
|
||||
richText: '富文本框',
|
||||
int: '整型',
|
||||
float: '浮點型',
|
||||
multipleInput: '多值輸入框'
|
||||
|
@ -92,3 +92,19 @@ export function syncIssues(success) {
|
||||
export function deleteIssueRelate(param, callback) {
|
||||
return basePost('/issues/delete/relate', param, callback);
|
||||
}
|
||||
|
||||
export function getIssueThirdPartTemplate() {
|
||||
return new Promise(resolve => {
|
||||
baseGet('/xpack/issue/template/' + getCurrentProjectID(), (data) => {
|
||||
let template = data;
|
||||
if (template.customFields) {
|
||||
template.customFields.forEach(item => {
|
||||
if (item.options) {
|
||||
item.options = JSON.parse(item.options);
|
||||
}
|
||||
});
|
||||
}
|
||||
resolve(template);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
10
frontend/src/network/project.js
Normal file
10
frontend/src/network/project.js
Normal file
@ -0,0 +1,10 @@
|
||||
import {baseGet} from "@/network/base-network";
|
||||
import {getCurrentProjectID} from "@/common/js/utils";
|
||||
|
||||
export function getProject(projectId, callback) {
|
||||
return projectId ? baseGet('/project/get/' + projectId, callback) : {};
|
||||
}
|
||||
|
||||
export function getCurrentProject(callback) {
|
||||
return getProject(getCurrentProjectID(), callback);
|
||||
}
|
Loading…
Reference in New Issue
Block a user