diff --git a/backend/src/main/java/io/metersphere/base/domain/Project.java b/backend/src/main/java/io/metersphere/base/domain/Project.java index f85eadf639..57c42ac558 100644 --- a/backend/src/main/java/io/metersphere/base/domain/Project.java +++ b/backend/src/main/java/io/metersphere/base/domain/Project.java @@ -45,5 +45,9 @@ public class Project implements Serializable { private String azureFilterId; + private String platform; + + private Boolean thirdPartTemplate; + private static final long serialVersionUID = 1L; -} \ No newline at end of file +} diff --git a/backend/src/main/java/io/metersphere/base/domain/ProjectExample.java b/backend/src/main/java/io/metersphere/base/domain/ProjectExample.java index 7f42ebe850..5530581f3d 100644 --- a/backend/src/main/java/io/metersphere/base/domain/ProjectExample.java +++ b/backend/src/main/java/io/metersphere/base/domain/ProjectExample.java @@ -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 values) { + addCriterion("platform in", values, "platform"); + return (Criteria) this; + } + + public Criteria andPlatformNotIn(List 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 values) { + addCriterion("third_part_template in", values, "thirdPartTemplate"); + return (Criteria) this; + } + + public Criteria andThirdPartTemplateNotIn(List 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); } } -} \ No newline at end of file +} diff --git a/backend/src/main/java/io/metersphere/base/mapper/ProjectMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ProjectMapper.xml index e02df82cce..82bab88ff3 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ProjectMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ProjectMapper.xml @@ -22,6 +22,8 @@ + + @@ -82,9 +84,10 @@ - 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 @@ -328,6 +345,12 @@ azure_filter_id = #{record.azureFilterId,jdbcType=VARCHAR}, + + platform = #{record.platform,jdbcType=VARCHAR}, + + + third_part_template = #{record.thirdPartTemplate,jdbcType=BIT}, + @@ -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} @@ -419,6 +444,12 @@ azure_filter_id = #{azureFilterId,jdbcType=VARCHAR}, + + platform = #{platform,jdbcType=VARCHAR}, + + + third_part_template = #{thirdPartTemplate,jdbcType=BIT}, + where id = #{id,jdbcType=VARCHAR} @@ -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} - \ No newline at end of file + diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.xml index 744e29c80d..c5a69c1ef2 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.xml @@ -59,10 +59,6 @@ from issues left join test_case_issues on issues.id = test_case_issues.issues_id - - left join - project on issues.project_id = project.id - 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) diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml index 5ea046fa25..b7de3d3e37 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml @@ -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 diff --git a/backend/src/main/java/io/metersphere/track/issue/AbstractIssuePlatform.java b/backend/src/main/java/io/metersphere/track/issue/AbstractIssuePlatform.java index d5682f3d0a..299e29b64d 100644 --- a/backend/src/main/java/io/metersphere/track/issue/AbstractIssuePlatform.java +++ b/backend/src/main/java/io/metersphere/track/issue/AbstractIssuePlatform.java @@ -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 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 getIssuesByPlatformIds(List platformIds) { IssuesService issuesService = CommonBeanFactory.getBean(IssuesService.class); return issuesService.getIssuesByPlatformIds(platformIds, projectId); diff --git a/backend/src/main/java/io/metersphere/track/issue/IssuesPlatform.java b/backend/src/main/java/io/metersphere/track/issue/IssuesPlatform.java index 99248caed4..e25202161f 100644 --- a/backend/src/main/java/io/metersphere/track/issue/IssuesPlatform.java +++ b/backend/src/main/java/io/metersphere/track/issue/IssuesPlatform.java @@ -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(); } diff --git a/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java b/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java index 4933644f23..239eab5eeb 100644 --- a/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java +++ b/backend/src/main/java/io/metersphere/track/issue/JiraPlatform.java @@ -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 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 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 diff --git a/backend/src/main/java/io/metersphere/track/issue/client/JiraAbstractClient.java b/backend/src/main/java/io/metersphere/track/issue/client/JiraAbstractClient.java index 3c810ed40c..4ccf497d3b 100644 --- a/backend/src/main/java/io/metersphere/track/issue/client/JiraAbstractClient.java +++ b/backend/src/main/java/io/metersphere/track/issue/client/JiraAbstractClient.java @@ -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 getCreateMetadata(String projectKey, String issueType) { + String url = getBaseUrl() + "/issue/createmeta?projectKeys={1}&issuetypeNames={2}&expand=projects.issuetypes.fields"; + ResponseEntity 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 fields = ((JiraCreateMetadataResponse) getResultForObject(JiraCreateMetadataResponse.class, response)) + .getProjects().get(0).getIssuetypes().get(0).getFields(); + fields.remove("project"); + fields.remove("issuetype"); + return fields; + } + + public List getAssignableUser(String projectKey) { + String url = getBaseUrl() + "/user/assignable/search?project={1}"; + ResponseEntity 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) 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"; diff --git a/backend/src/main/java/io/metersphere/track/issue/domain/jira/JiraCreateMetadataResponse.java b/backend/src/main/java/io/metersphere/track/issue/domain/jira/JiraCreateMetadataResponse.java new file mode 100644 index 0000000000..f63458398f --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/issue/domain/jira/JiraCreateMetadataResponse.java @@ -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; + + @Setter + @Getter + public static class Projects { + private List issuetypes; + } + + @Setter + @Getter + public static class Issuetypes { + private Map 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; + } + + @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 children; + } +} diff --git a/backend/src/main/java/io/metersphere/track/issue/domain/jira/JiraUser.java b/backend/src/main/java/io/metersphere/track/issue/domain/jira/JiraUser.java new file mode 100644 index 0000000000..84e0dc5f22 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/issue/domain/jira/JiraUser.java @@ -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; +} diff --git a/backend/src/main/java/io/metersphere/track/request/testcase/IssuesUpdateRequest.java b/backend/src/main/java/io/metersphere/track/request/testcase/IssuesUpdateRequest.java index 8c1c145e84..afc96dcafd 100644 --- a/backend/src/main/java/io/metersphere/track/request/testcase/IssuesUpdateRequest.java +++ b/backend/src/main/java/io/metersphere/track/request/testcase/IssuesUpdateRequest.java @@ -25,5 +25,7 @@ public class IssuesUpdateRequest extends IssuesWithBLOBs { private List zentaoBuilds; private List testCaseIds; + private boolean thirdPartPlatform; + private List follows; } diff --git a/backend/src/main/java/io/metersphere/xpack b/backend/src/main/java/io/metersphere/xpack index 269d23d500..a99681fe94 160000 --- a/backend/src/main/java/io/metersphere/xpack +++ b/backend/src/main/java/io/metersphere/xpack @@ -1 +1 @@ -Subproject commit 269d23d5004ee49a9e82a977027ca72757d3056a +Subproject commit a99681fe94107d42ff340f4e8dcdca95195cf884 diff --git a/backend/src/main/resources/db/migration/V102__v1.16_release.sql b/backend/src/main/resources/db/migration/V102__v1.16_release.sql index b2247a93b2..04c7b7d9e8 100644 --- a/backend/src/main/resources/db/migration/V102__v1.16_release.sql +++ b/backend/src/main/resources/db/migration/V102__v1.16_release.sql @@ -1,2 +1,11 @@ -- 新增字段 -ALTER TABLE `swagger_url_project` ADD COLUMN `config` longtext COMMENT '鉴权配置信息' AFTER `mode_id`; \ No newline at end of file +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'; diff --git a/frontend/src/business/components/project/menu/EditProject.vue b/frontend/src/business/components/project/menu/EditProject.vue index b4d579fca1..c80110a96f 100644 --- a/frontend/src/business/components/project/menu/EditProject.vue +++ b/frontend/src/business/components/project/menu/EditProject.vue @@ -7,11 +7,23 @@ + + + + + + + - - + + + + + + + @@ -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){ diff --git a/frontend/src/business/components/settings/workspace/MsProject.vue b/frontend/src/business/components/settings/workspace/MsProject.vue index 9888c42a6d..69ef6e7bc5 100644 --- a/frontend/src/business/components/settings/workspace/MsProject.vue +++ b/frontend/src/business/components/settings/workspace/MsProject.vue @@ -93,77 +93,7 @@ :total="total"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + { 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(); diff --git a/frontend/src/business/components/settings/workspace/template/CustomFiledComponent.vue b/frontend/src/business/components/settings/workspace/template/CustomFiledComponent.vue index f0549057eb..56ed29a447 100644 --- a/frontend/src/business/components/settings/workspace/template/CustomFiledComponent.vue +++ b/frontend/src/business/components/settings/workspace/template/CustomFiledComponent.vue @@ -14,6 +14,14 @@ + + + + @change="handleChange"/> + v-model="data[prop]" :precision="2" :step="0.1"/> @@ -84,10 +92,16 @@ + + @@ -97,11 +111,12 @@ diff --git a/frontend/src/business/components/track/case/components/IssueRelateList.vue b/frontend/src/business/components/track/case/components/IssueRelateList.vue index 3cfa423058..cbd0918781 100644 --- a/frontend/src/business/components/track/case/components/IssueRelateList.vue +++ b/frontend/src/business/components/track/case/components/IssueRelateList.vue @@ -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; diff --git a/frontend/src/business/components/track/case/components/MsMarkDownText.vue b/frontend/src/business/components/track/case/components/MsMarkDownText.vue new file mode 100644 index 0000000000..913246e17b --- /dev/null +++ b/frontend/src/business/components/track/case/components/MsMarkDownText.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/frontend/src/business/components/track/case/components/TestCaseEdit.vue b/frontend/src/business/components/track/case/components/TestCaseEdit.vue index 381c9bca51..7ddb8df6ca 100644 --- a/frontend/src/business/components/track/case/components/TestCaseEdit.vue +++ b/frontend/src/business/components/track/case/components/TestCaseEdit.vue @@ -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(); diff --git a/frontend/src/business/components/track/case/components/TestCaseIssueRelate.vue b/frontend/src/business/components/track/case/components/TestCaseIssueRelate.vue index 25fe61b8a6..2dbf8e7671 100644 --- a/frontend/src/business/components/track/case/components/TestCaseIssueRelate.vue +++ b/frontend/src/business/components/track/case/components/TestCaseIssueRelate.vue @@ -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; diff --git a/frontend/src/business/components/track/issue/IssueEditDetail.vue b/frontend/src/business/components/track/issue/IssueEditDetail.vue index 275121b27a..3637da0e8a 100644 --- a/frontend/src/business/components/track/issue/IssueEditDetail.vue +++ b/frontend/src/business/components/track/issue/IssueEditDetail.vue @@ -3,7 +3,7 @@ - + @@ -14,21 +14,31 @@ - - - - - - + + + + + + +
+ + + + + +
+
- + - + @@ -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() { diff --git a/frontend/src/business/components/track/issue/IssueList.vue b/frontend/src/business/components/track/issue/IssueList.vue index b093fb1935..a16425c551 100644 --- a/frontend/src/business/components/track/issue/IssueList.vue +++ b/frontend/src/business/components/track/issue/IssueList.vue @@ -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; } diff --git a/frontend/src/business/components/track/plan/view/comonents/functional/FunctionalTestCaseEdit.vue b/frontend/src/business/components/track/plan/view/comonents/functional/FunctionalTestCaseEdit.vue index bcf3381cfd..22279e3353 100644 --- a/frontend/src/business/components/track/plan/view/comonents/functional/FunctionalTestCaseEdit.vue +++ b/frontend/src/business/components/track/plan/view/comonents/functional/FunctionalTestCaseEdit.vue @@ -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) { // 如果没值,使用模板的默认值 diff --git a/frontend/src/business/components/track/review/view/components/TestReviewTestCaseEdit.vue b/frontend/src/business/components/track/review/view/components/TestReviewTestCaseEdit.vue index 0da5298af3..aa19e34aa6 100644 --- a/frontend/src/business/components/track/review/view/components/TestReviewTestCaseEdit.vue +++ b/frontend/src/business/components/track/review/view/components/TestReviewTestCaseEdit.vue @@ -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) { diff --git a/frontend/src/business/components/xpack b/frontend/src/business/components/xpack index abb1eae9af..275aff0cda 160000 --- a/frontend/src/business/components/xpack +++ b/frontend/src/business/components/xpack @@ -1 +1 @@ -Subproject commit abb1eae9aff953ca0be49bfb413454e435d627ee +Subproject commit 275aff0cdaf7556e02c0fc479cb19d2f962695c2 diff --git a/frontend/src/common/js/constants.js b/frontend/src/common/js/constants.js index 4a8ac0aa85..626fea123f 100644 --- a/frontend/src/common/js/constants.js +++ b/frontend/src/common/js/constants.js @@ -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'; diff --git a/frontend/src/common/js/custom_field.js b/frontend/src/common/js/custom_field.js index 0df46ad37e..a0d0bdba38 100644 --- a/frontend/src/common/js/custom_field.js +++ b/frontend/src/common/js/custom_field.js @@ -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 diff --git a/frontend/src/common/js/table-constants.js b/frontend/src/common/js/table-constants.js index 319d51dc17..b3811d456c 100644 --- a/frontend/src/common/js/table-constants.js +++ b/frontend/src/common/js/table-constants.js @@ -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' diff --git a/frontend/src/common/js/tableUtils.js b/frontend/src/common/js/tableUtils.js index d0be4a8d67..ec1be24498 100644 --- a/frontend/src/common/js/tableUtils.js +++ b/frontend/src/common/js/tableUtils.js @@ -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; + } } } } diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index ef136c2193..66ee672a26 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -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' diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index a01c89ac05..82ca73fa9c 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -496,7 +496,9 @@ export default { checkbox: '多选框', member: '单选成员', multipleMember: '多选成员', - data: '日期', + date: '日期选择器', + datetime: '日期时间选择器', + richText: '富文本框', int: '整型', float: '浮点型', multipleInput: '多值输入框' diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index 623d699163..560d490f3c 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -495,7 +495,9 @@ export default { checkbox: '多選框', member: '單選成員', multipleMember: '多選成員', - data: '日期', + date: '日期選擇器', + datetime: '日期時間選擇器', + richText: '富文本框', int: '整型', float: '浮點型', multipleInput: '多值輸入框' diff --git a/frontend/src/network/Issue.js b/frontend/src/network/Issue.js index afa802d984..60c58f8645 100644 --- a/frontend/src/network/Issue.js +++ b/frontend/src/network/Issue.js @@ -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); + }) + }); +} diff --git a/frontend/src/network/project.js b/frontend/src/network/project.js new file mode 100644 index 0000000000..e6e4c59b86 --- /dev/null +++ b/frontend/src/network/project.js @@ -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); +}