Pr@v1.10@tfs (#5244)
* feat:azuredevops add issue * feat:azuredevops add issue * feat:azuredevops取消集成 * feat:azuredevops支持自定义字段 * feat:azuredevops add issue feat:azuredevops add issue feat:azuredevops取消集成 feat:azuredevops支持自定义字段 feat:azuredevops add issue feat:azuredevops add issue feat:azuredevops取消集成 feat:azuredevops支持自定义字段 fix:fix flyway version
This commit is contained in:
parent
cf9507da4b
commit
4dd976cd2e
|
@ -1,7 +1,3 @@
|
|||
[submodule "backend/src/main/java/io/metersphere/xpack"]
|
||||
path = backend/src/main/java/io/metersphere/xpack
|
||||
url = git@github.com:metersphere/xpack-backend.git
|
||||
branch = master
|
||||
[submodule "frontend/src/business/components/xpack"]
|
||||
path = frontend/src/business/components/xpack
|
||||
url = git@github.com:metersphere/xpack-frontend.git
|
||||
|
|
|
@ -23,6 +23,8 @@ public class Project implements Serializable {
|
|||
|
||||
private String zentaoId;
|
||||
|
||||
private String azureDevopsId;
|
||||
|
||||
private Boolean repeatable;
|
||||
|
||||
private String caseTemplateId;
|
||||
|
|
|
@ -714,6 +714,76 @@ public class ProjectExample {
|
|||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andAzureDevopsIdIsNull() {
|
||||
addCriterion("azure_devops_id is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andAzureDevopsIdIsNotNull() {
|
||||
addCriterion("azure_devops_id is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andAzureDevopsIdEqualTo(String value) {
|
||||
addCriterion("azure_devops_id =", value, "azureDevopsId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andAzureDevopsIdNotEqualTo(String value) {
|
||||
addCriterion("azure_devops_id <>", value, "azureDevopsId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andAzureDevopsIdGreaterThan(String value) {
|
||||
addCriterion("azure_devops_id >", value, "azureDevopsId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andAzureDevopsIdGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("azure_devops_id >=", value, "azureDevopsId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andAzureDevopsIdLessThan(String value) {
|
||||
addCriterion("azure_devops_id <", value, "azureDevopsId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andAzureDevopsIdLessThanOrEqualTo(String value) {
|
||||
addCriterion("azure_devops_id <=", value, "azureDevopsId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andAzureDevopsIdLike(String value) {
|
||||
addCriterion("azure_devops_id like", value, "azureDevopsId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andAzureDevopsIdNotLike(String value) {
|
||||
addCriterion("azure_devops_id not like", value, "azureDevopsId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andAzureDevopsIdIn(List<String> values) {
|
||||
addCriterion("azure_devops_id in", values, "azureDevopsId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andAzureDevopsIdNotIn(List<String> values) {
|
||||
addCriterion("azure_devops_id not in", values, "azureDevopsId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andAzureDevopsIdBetween(String value1, String value2) {
|
||||
addCriterion("azure_devops_id between", value1, value2, "azureDevopsId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andAzureDevopsIdNotBetween(String value1, String value2) {
|
||||
addCriterion("azure_devops_id not between", value1, value2, "azureDevopsId");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andRepeatableIsNull() {
|
||||
addCriterion("`repeatable` is null");
|
||||
return (Criteria) this;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<result column="tapd_id" jdbcType="VARCHAR" property="tapdId" />
|
||||
<result column="jira_key" jdbcType="VARCHAR" property="jiraKey" />
|
||||
<result column="zentao_id" jdbcType="VARCHAR" property="zentaoId" />
|
||||
<result column="azure_devops_id" jdbcType="VARCHAR" property="azureDevopsId" />
|
||||
<result column="repeatable" jdbcType="BIT" property="repeatable" />
|
||||
<result column="case_template_id" jdbcType="VARCHAR" property="caseTemplateId" />
|
||||
<result column="issue_template_id" jdbcType="VARCHAR" property="issueTemplateId" />
|
||||
|
@ -79,8 +80,8 @@
|
|||
</sql>
|
||||
<sql id="Base_Column_List">
|
||||
id, workspace_id, `name`, description, create_time, update_time, tapd_id, jira_key,
|
||||
zentao_id, `repeatable`, case_template_id, issue_template_id, custom_num, scenario_custom_num,
|
||||
create_user, system_id
|
||||
zentao_id, azure_devops_id, `repeatable`, case_template_id, issue_template_id, custom_num,
|
||||
scenario_custom_num, create_user, system_id
|
||||
</sql>
|
||||
<select id="selectByExample" parameterType="io.metersphere.base.domain.ProjectExample" resultMap="BaseResultMap">
|
||||
select
|
||||
|
@ -116,15 +117,15 @@
|
|||
insert into project (id, workspace_id, `name`,
|
||||
description, create_time, update_time,
|
||||
tapd_id, jira_key, zentao_id,
|
||||
`repeatable`, case_template_id, issue_template_id,
|
||||
custom_num, scenario_custom_num, create_user,
|
||||
system_id)
|
||||
azure_devops_id, `repeatable`, case_template_id,
|
||||
issue_template_id, custom_num, scenario_custom_num,
|
||||
create_user, system_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},
|
||||
#{repeatable,jdbcType=BIT}, #{caseTemplateId,jdbcType=VARCHAR}, #{issueTemplateId,jdbcType=VARCHAR},
|
||||
#{customNum,jdbcType=BIT}, #{scenarioCustomNum,jdbcType=BIT}, #{createUser,jdbcType=VARCHAR},
|
||||
#{systemId,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})
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.Project">
|
||||
insert into project
|
||||
|
@ -156,6 +157,9 @@
|
|||
<if test="zentaoId != null">
|
||||
zentao_id,
|
||||
</if>
|
||||
<if test="azureDevopsId != null">
|
||||
azure_devops_id,
|
||||
</if>
|
||||
<if test="repeatable != null">
|
||||
`repeatable`,
|
||||
</if>
|
||||
|
@ -206,6 +210,9 @@
|
|||
<if test="zentaoId != null">
|
||||
#{zentaoId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="azureDevopsId != null">
|
||||
#{azureDevopsId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="repeatable != null">
|
||||
#{repeatable,jdbcType=BIT},
|
||||
</if>
|
||||
|
@ -265,6 +272,9 @@
|
|||
<if test="record.zentaoId != null">
|
||||
zentao_id = #{record.zentaoId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.azureDevopsId != null">
|
||||
azure_devops_id = #{record.azureDevopsId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.repeatable != null">
|
||||
`repeatable` = #{record.repeatable,jdbcType=BIT},
|
||||
</if>
|
||||
|
@ -302,6 +312,7 @@
|
|||
tapd_id = #{record.tapdId,jdbcType=VARCHAR},
|
||||
jira_key = #{record.jiraKey,jdbcType=VARCHAR},
|
||||
zentao_id = #{record.zentaoId,jdbcType=VARCHAR},
|
||||
azure_devops_id = #{record.azureDevopsId,jdbcType=VARCHAR},
|
||||
`repeatable` = #{record.repeatable,jdbcType=BIT},
|
||||
case_template_id = #{record.caseTemplateId,jdbcType=VARCHAR},
|
||||
issue_template_id = #{record.issueTemplateId,jdbcType=VARCHAR},
|
||||
|
@ -340,6 +351,9 @@
|
|||
<if test="zentaoId != null">
|
||||
zentao_id = #{zentaoId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="azureDevopsId != null">
|
||||
azure_devops_id = #{azureDevopsId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="repeatable != null">
|
||||
`repeatable` = #{repeatable,jdbcType=BIT},
|
||||
</if>
|
||||
|
@ -374,6 +388,7 @@
|
|||
tapd_id = #{tapdId,jdbcType=VARCHAR},
|
||||
jira_key = #{jiraKey,jdbcType=VARCHAR},
|
||||
zentao_id = #{zentaoId,jdbcType=VARCHAR},
|
||||
azure_devops_id = #{azureDevopsId,jdbcType=VARCHAR},
|
||||
`repeatable` = #{repeatable,jdbcType=BIT},
|
||||
case_template_id = #{caseTemplateId,jdbcType=VARCHAR},
|
||||
issue_template_id = #{issueTemplateId,jdbcType=VARCHAR},
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
<select id="getProjectWithWorkspace" resultType="io.metersphere.dto.ProjectDTO">
|
||||
select p.id, p.workspace_id, p.name, p.description, p.update_time, p.issue_template_id, p.case_template_id,
|
||||
p.create_time, w.id as workspaceId, w.name as workspaceName, p.tapd_id, p.jira_key, p.zentao_id,p.repeatable, p.custom_num,
|
||||
p.create_time, w.id as workspaceId, w.name as workspaceName, p.tapd_id, p.jira_key, p.zentao_id,p.azure_devops_id,p.repeatable, p.custom_num,
|
||||
user.name as createUserName,
|
||||
p.scenario_custom_num
|
||||
from project p
|
||||
|
@ -131,6 +131,9 @@
|
|||
<if test="platform == 'Zentao'">
|
||||
zentao_id = null
|
||||
</if>
|
||||
<if test="platform == 'AzureDevops'">
|
||||
azure_devops_id = null
|
||||
</if>
|
||||
</set>
|
||||
where project.id in (select id from (select id
|
||||
from project
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package io.metersphere.commons.constants;
|
||||
|
||||
public enum IssuesManagePlatform {
|
||||
Tapd, Jira, Local, Zentao
|
||||
Tapd, Jira, Local, Zentao, AzureDevops
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ public class UserDTO extends User {
|
|||
private String tapdUserName;
|
||||
private String zentaoUserName;
|
||||
private String zentaoPassword;
|
||||
private String azureDevopsPat;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform {
|
|||
*
|
||||
* @return 其他平台和本地项目绑定的属性值
|
||||
*/
|
||||
abstract String getProjectId(String projectId);
|
||||
public abstract String getProjectId(String projectId);
|
||||
|
||||
protected boolean isIntegratedPlatform(String orgId, String platform) {
|
||||
IntegrationRequest request = new IntegrationRequest();
|
||||
|
|
|
@ -4,10 +4,9 @@ import io.metersphere.commons.constants.IssuesManagePlatform;
|
|||
import io.metersphere.track.request.testcase.IssuesRequest;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
|
||||
public class IssueFactory {
|
||||
public static AbstractIssuePlatform createPlatform(String platform, IssuesRequest addIssueRequest) {
|
||||
|
@ -17,6 +16,24 @@ public class IssueFactory {
|
|||
return new JiraPlatform(addIssueRequest);
|
||||
} else if (StringUtils.equals(IssuesManagePlatform.Zentao.toString(), platform)) {
|
||||
return new ZentaoPlatform(addIssueRequest);
|
||||
} else if (StringUtils.equals(IssuesManagePlatform.AzureDevops.toString(), platform)) {
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
Class clazz = loader.loadClass("io.metersphere.xpack.issue.azureDevops.AzureDevopsPlatform");
|
||||
Constructor cons = clazz.getDeclaredConstructor(new Class[] { IssuesRequest.class });
|
||||
AbstractIssuePlatform azureDevopsPlatform = (AbstractIssuePlatform) cons.newInstance(addIssueRequest);
|
||||
return azureDevopsPlatform;
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else if (StringUtils.equalsIgnoreCase(IssuesManagePlatform.Local.toString(), platform)) {
|
||||
return new LocalPlatform(addIssueRequest);
|
||||
}
|
||||
|
|
|
@ -298,7 +298,7 @@ public class JiraPlatform extends AbstractIssuePlatform {
|
|||
}
|
||||
|
||||
@Override
|
||||
String getProjectId(String projectId) {
|
||||
public String getProjectId(String projectId) {
|
||||
if (StringUtils.isNotBlank(projectId)) {
|
||||
return projectService.getProjectById(projectId).getJiraKey();
|
||||
}
|
||||
|
|
|
@ -25,5 +25,5 @@ public abstract class LocalAbstractPlatform extends AbstractIssuePlatform {
|
|||
public void syncIssues(Project project, List<IssuesDao> tapdIssues) {}
|
||||
|
||||
@Override
|
||||
String getProjectId(String projectId) { return null; }
|
||||
public String getProjectId(String projectId) { return null; }
|
||||
}
|
||||
|
|
|
@ -236,7 +236,7 @@ public class TapdPlatform extends AbstractIssuePlatform {
|
|||
}
|
||||
|
||||
@Override
|
||||
String getProjectId(String projectId) {
|
||||
public String getProjectId(String projectId) {
|
||||
if (StringUtils.isNotBlank(projectId)) {
|
||||
return projectService.getProjectById(projectId).getTapdId();
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ public class ZentaoPlatform extends AbstractIssuePlatform {
|
|||
}
|
||||
|
||||
@Override
|
||||
String getProjectId(String projectId) {
|
||||
public String getProjectId(String projectId) {
|
||||
if (StringUtils.isNotBlank(projectId)) {
|
||||
return projectService.getProjectById(projectId).getZentaoId();
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ public class DemandService {
|
|||
boolean tapd = issuesService.isIntegratedPlatform(orgId, IssuesManagePlatform.Tapd.toString());
|
||||
boolean jira = issuesService.isIntegratedPlatform(orgId, IssuesManagePlatform.Jira.toString());
|
||||
boolean zentao = issuesService.isIntegratedPlatform(orgId, IssuesManagePlatform.Zentao.toString());
|
||||
boolean azureDevops = issuesService.isIntegratedPlatform(orgId, IssuesManagePlatform.AzureDevops.toString());
|
||||
List<DemandDTO> list = new ArrayList<>();
|
||||
List<String> platforms = new ArrayList<>();
|
||||
IssuesRequest issueRequest = new IssuesRequest();
|
||||
|
@ -59,6 +60,13 @@ public class DemandService {
|
|||
}
|
||||
}
|
||||
|
||||
if (azureDevops) {
|
||||
String azureDevopsId = project.getAzureDevopsId();
|
||||
if (StringUtils.isNotBlank(azureDevopsId)) {
|
||||
platforms.add(IssuesManagePlatform.AzureDevops.name());
|
||||
}
|
||||
}
|
||||
|
||||
issueRequest.setOrganizationId(orgId);
|
||||
List<AbstractIssuePlatform> platformList = IssueFactory.createPlatforms(platforms, issueRequest);
|
||||
platformList.forEach(platform -> {
|
||||
|
|
|
@ -40,6 +40,8 @@ import org.springframework.stereotype.Service;
|
|||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -88,6 +90,8 @@ public class IssuesService {
|
|||
abstractPlatform.testAuth();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void addIssues(IssuesUpdateRequest issuesRequest) {
|
||||
List<AbstractIssuePlatform> platformList = getUpdatePlatforms(issuesRequest);
|
||||
platformList.forEach(platform -> {
|
||||
|
@ -453,6 +457,9 @@ public class IssuesService {
|
|||
List<IssuesDao> zentaoIssues = issues.stream()
|
||||
.filter(item -> item.getPlatform().equals(IssuesManagePlatform.Zentao.name()))
|
||||
.collect(Collectors.toList());
|
||||
List<IssuesDao> azureDevopsIssues = issues.stream()
|
||||
.filter(item -> item.getPlatform().equals(IssuesManagePlatform.AzureDevops.name()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
IssuesRequest issuesRequest = new IssuesRequest();
|
||||
issuesRequest.setProjectId(projectId);
|
||||
|
@ -469,6 +476,25 @@ public class IssuesService {
|
|||
ZentaoPlatform zentaoPlatform = new ZentaoPlatform(issuesRequest);
|
||||
syncThirdPartyIssues(zentaoPlatform::syncIssues, project, zentaoIssues);
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(azureDevopsIssues)) {
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
Class clazz = loader.loadClass("io.metersphere.xpack.issue.azureDevops.AzureDevopsPlatform");
|
||||
Constructor cons = clazz.getDeclaredConstructor(new Class[] { IssuesRequest.class });
|
||||
AbstractIssuePlatform azureDevopsPlatform = (AbstractIssuePlatform) cons.newInstance(issuesRequest);
|
||||
syncThirdPartyIssues(azureDevopsPlatform::syncIssues, project, azureDevopsIssues);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 52e757ba127c9b33e2715c02bc618aa315192ceb
|
|
@ -0,0 +1,2 @@
|
|||
alter table project add azure_devops_id varchar(50) null after zentao_id;
|
||||
alter table custom_field_template modify default_value varchar(100) null ;
|
|
@ -64,7 +64,7 @@
|
|||
|
||||
<!--要生成的数据库表 -->
|
||||
|
||||
<table tableName="test_plan_test_case"/>
|
||||
<!--<table tableName="test_plan_test_case"/>-->
|
||||
<!--<table tableName="swagger_url_project"/>
|
||||
<table tableName="user_header"/>-->
|
||||
<!--<table tableName="test_plan_api_scenario"/>-->
|
||||
|
@ -78,16 +78,21 @@
|
|||
<table tableName="test_plan"/>
|
||||
<table tableName="test_case_test"/>-->
|
||||
<!-- <table tableName="api_test_environment"></table>-->
|
||||
<!--
|
||||
<table tableName="project"></table>
|
||||
-->
|
||||
<!-- <table tableName="custom_field"></table>-->
|
||||
<!-- <table tableName="test_case"></table>-->
|
||||
<!-- <table tableName="test_case"></table>-->
|
||||
<<<<<<< HEAD
|
||||
<table tableName="test_plan_report_resource"></table>
|
||||
<!-- <table tableName="test_case"></table>-->
|
||||
<!-- <table tableName="api_test_case"></table>-->
|
||||
<!-- <table tableName="api_definition"></table>-->
|
||||
=======
|
||||
<!-- <table tableName="api_scenario"></table>
|
||||
<table tableName="test_case"></table>
|
||||
<table tableName="api_test_case"></table>
|
||||
<table tableName="api_definition"></table>-->
|
||||
>>>>>>> feat:azuredevops add issue
|
||||
|
||||
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
|
@ -12,13 +12,16 @@
|
|||
<el-radio label="Zentao">
|
||||
<img class="zentao_platform" src="../../../../assets/zentao.jpg" alt="Zentao"/>
|
||||
</el-radio>
|
||||
<el-radio label="AzureDevops" v-xpack>
|
||||
<img class="platform" src="../../../../assets/AzureDevops.png" alt="AzureDevops"/>
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
|
||||
<tapd-setting v-if="tapdEnable" ref="tapdSetting"/>
|
||||
<jira-setting v-if="jiraEnable" ref="jiraSetting"/>
|
||||
<zentao-setting v-if="zentaoEnable" ref="zentaoSetting"/>
|
||||
|
||||
<azuredevops-setting v-if="azuredevopsEnable" ref="azureDevopsSetting"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -26,16 +29,18 @@
|
|||
import TapdSetting from "@/business/components/settings/organization/components/TapdSetting";
|
||||
import JiraSetting from "@/business/components/settings/organization/components/JiraSetting";
|
||||
import ZentaoSetting from "@/business/components/settings/organization/components/ZentaoSetting";
|
||||
import {JIRA, TAPD, ZEN_TAO} from "@/common/js/constants";
|
||||
import AzuredevopsSetting from "@/business/components/settings/organization/components/AzureDevopsSetting";
|
||||
import {JIRA, TAPD, ZEN_TAO, AZURE_DEVOPS} from "@/common/js/constants";
|
||||
|
||||
export default {
|
||||
name: "BugManagement",
|
||||
components: {TapdSetting, JiraSetting, ZentaoSetting},
|
||||
components: {TapdSetting, JiraSetting, ZentaoSetting, AzuredevopsSetting},
|
||||
data() {
|
||||
return {
|
||||
tapdEnable: true,
|
||||
jiraEnable: false,
|
||||
zentaoEnable: false,
|
||||
azuredevopsEnable:false,
|
||||
result: {},
|
||||
platform: TAPD
|
||||
}
|
||||
|
@ -46,14 +51,22 @@ export default {
|
|||
this.tapdEnable = true;
|
||||
this.jiraEnable = false;
|
||||
this.zentaoEnable = false;
|
||||
this.azuredevopsEnable = false;
|
||||
} else if (platform === JIRA) {
|
||||
this.tapdEnable = false;
|
||||
this.jiraEnable = true;
|
||||
this.zentaoEnable = false;
|
||||
this.azuredevopsEnable = false;
|
||||
} else if (platform === ZEN_TAO) {
|
||||
this.tapdEnable = false;
|
||||
this.jiraEnable = false;
|
||||
this.zentaoEnable = true;
|
||||
this.azuredevopsEnable = false;
|
||||
} else if (platform === AZURE_DEVOPS) {
|
||||
this.tapdEnable = false;
|
||||
this.jiraEnable = false;
|
||||
this.zentaoEnable = false;
|
||||
this.azuredevopsEnable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,230 @@
|
|||
<template>
|
||||
<div>
|
||||
<div style="width: 500px">
|
||||
<div style="margin-top: 20px;margin-bottom: 10px">{{ $t('organization.integration.basic_auth_info') }}</div>
|
||||
<el-form :model="form" ref="form" label-width="155px" size="small" :disabled="show" :rules="rules">
|
||||
<el-form-item :label="$t('organization.integration.azure_pat')" prop="pat">
|
||||
<el-input v-model="form.pat" auto-complete="new-password" v-if="showInput"
|
||||
:placeholder="$t('organization.integration.input_azure_pat')" show-password/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('organization.integration.azure_devops_url')" prop="url">
|
||||
<el-input v-model="form.url" :placeholder="$t('organization.integration.input_azure_url')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('organization.integration.azure_organization_id')" prop="organization">
|
||||
<el-input v-model="form.organization" :placeholder="$t('organization.integration.input_azure_organization_id')"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('organization.integration.azure_issuetype')" prop="issuetype">
|
||||
<el-input v-model="form.issuetype" :placeholder="$t('organization.integration.input_azure_issuetype')"/>
|
||||
<ms-instructions-icon effect="light">
|
||||
<template>
|
||||
<img class="jira-image" src="../../../../../assets/azureDevops-type.png"/>
|
||||
</template>
|
||||
</ms-instructions-icon>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('organization.integration.azure_storytype')" prop="storytype">
|
||||
<el-input v-model="form.storytype" :placeholder="$t('organization.integration.input_azure_storytype')"/>
|
||||
<ms-instructions-icon effect="light">
|
||||
<template>
|
||||
<img class="jira-image" src="../../../../../assets/azureDevops-type.png"/>
|
||||
</template>
|
||||
</ms-instructions-icon>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<bug-manage-btn @save="save"
|
||||
@init="init"
|
||||
:edit-permission="['ORGANIZATION_SERVICE:READ+EDIT']"
|
||||
@testConnection="testConnection"
|
||||
@cancelIntegration="cancelIntegration"
|
||||
@reloadPassInput="reloadPassInput"
|
||||
:form="form"
|
||||
:show.sync="show"
|
||||
ref="bugBtn"/>
|
||||
|
||||
<div class="defect-tip">
|
||||
<div>{{ $t('organization.integration.use_tip') }}</div>
|
||||
<div>
|
||||
1. {{ $t('organization.integration.use_tip_azure') }}
|
||||
</div>
|
||||
<div>
|
||||
2. {{ $t('organization.integration.use_tip_two') }}
|
||||
<router-link to="/setting/project/all" style="margin-left: 5px">
|
||||
{{ $t('organization.integration.link_the_project_now') }}
|
||||
</router-link>
|
||||
</div>
|
||||
<div>
|
||||
3. {{ $t('organization.integration.use_tip_three') }}
|
||||
<router-link :to="{name: 'PersonSetting', params: { open: true }}" style="margin-left: 5px">
|
||||
{{ $t('organization.integration.link_the_info_now') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BugManageBtn from "@/business/components/settings/organization/components/BugManageBtn";
|
||||
import {getCurrentOrganizationId, getCurrentUser} from "@/common/js/utils";
|
||||
import {AZURE_DEVOPS} from "@/common/js/constants";
|
||||
import MsInstructionsIcon from "@/business/components/common/components/MsInstructionsIcon";
|
||||
|
||||
export default {
|
||||
name: "AzureDevopsSetting",
|
||||
components: {MsInstructionsIcon, BugManageBtn},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: true,
|
||||
showInput: true,
|
||||
form: {},
|
||||
rules: {
|
||||
pat: {
|
||||
required: true,
|
||||
message: this.$t('organization.integration.input_azure_pat'),
|
||||
trigger: ['change', 'blur']
|
||||
},
|
||||
url: {
|
||||
required: true,
|
||||
message: this.$t('organization.integration.input_azure_url'),
|
||||
trigger: ['change', 'blur']
|
||||
},
|
||||
organization: {
|
||||
required: true,
|
||||
message: this.$t('organization.integration.input_azure_organization_id'),
|
||||
trigger: ['change', 'blur']
|
||||
},
|
||||
issuetype: {
|
||||
required: true,
|
||||
message: this.$t('organization.integration.input_azure_issuetype'),
|
||||
trigger: ['change', 'blur']
|
||||
},
|
||||
storytype: {
|
||||
required: true,
|
||||
message: this.$t('organization.integration.input_azure_storytype'),
|
||||
trigger: ['change', 'blur']
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
const {lastOrganizationId} = getCurrentUser();
|
||||
let param = {};
|
||||
param.platform = AZURE_DEVOPS;
|
||||
param.orgId = lastOrganizationId;
|
||||
this.$parent.result = this.$post("service/integration/type", param, response => {
|
||||
let data = response.data;
|
||||
if (data.configuration) {
|
||||
let config = JSON.parse(data.configuration);
|
||||
this.$set(this.form, 'pat', config.pat);
|
||||
this.$set(this.form, 'url', config.url);
|
||||
this.$set(this.form, 'organization', config.organization);
|
||||
this.$set(this.form, 'issuetype', config.issuetype);
|
||||
this.$set(this.form, 'storytype', config.storytype);
|
||||
} else {
|
||||
this.clear();
|
||||
}
|
||||
})
|
||||
},
|
||||
save() {
|
||||
this.$refs['form'].validate(valid => {
|
||||
if (valid) {
|
||||
let formatUrl = this.form.url.trim();
|
||||
if (!formatUrl.endsWith('/')) {
|
||||
formatUrl = formatUrl + '/';
|
||||
}
|
||||
let param = {};
|
||||
let auth = {
|
||||
pat: this.form.pat,
|
||||
url: formatUrl,
|
||||
organization:this.form.organization,
|
||||
issuetype: this.form.issuetype,
|
||||
storytype: this.form.storytype
|
||||
};
|
||||
const {lastOrganizationId} = getCurrentUser();
|
||||
param.organizationId = lastOrganizationId;
|
||||
param.platform = AZURE_DEVOPS;
|
||||
param.configuration = JSON.stringify(auth);
|
||||
this.$parent.result = this.$post("service/integration/save", param, () => {
|
||||
this.show = true;
|
||||
this.$refs.bugBtn.showEdit = true;
|
||||
this.$refs.bugBtn.showSave = false;
|
||||
this.$refs.bugBtn.showCancel = false;
|
||||
this.reloadPassInput();
|
||||
this.init();
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
},
|
||||
clear() {
|
||||
this.$set(this.form, 'pat', '');
|
||||
this.$set(this.form, 'url', '');
|
||||
this.$set(this.form, 'organization', '');
|
||||
this.$set(this.form, 'issuetype', '');
|
||||
this.$set(this.form, 'storytype', '');
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate();
|
||||
}
|
||||
});
|
||||
},
|
||||
testConnection() {
|
||||
if (this.form.pat) {
|
||||
this.$parent.result = this.$get("issues/auth/" + getCurrentOrganizationId() + '/' + AZURE_DEVOPS, () => {
|
||||
this.$success(this.$t('organization.integration.verified'));
|
||||
});
|
||||
} else {
|
||||
this.$warning(this.$t('organization.integration.not_integrated'));
|
||||
return false;
|
||||
}
|
||||
},
|
||||
cancelIntegration() {
|
||||
if (this.form.url && this.form.pat) {
|
||||
this.$alert(this.$t('organization.integration.cancel_confirm') + AZURE_DEVOPS + "?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
const {lastOrganizationId} = getCurrentUser();
|
||||
let param = {};
|
||||
param.orgId = lastOrganizationId;
|
||||
param.platform = AZURE_DEVOPS;
|
||||
this.$parent.result = this.$post("service/integration/delete", param, () => {
|
||||
this.$success(this.$t('organization.integration.successful_operation'));
|
||||
this.init('');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.$warning(this.$t('organization.integration.not_integrated'));
|
||||
}
|
||||
},
|
||||
reloadPassInput() {
|
||||
this.showInput = false;
|
||||
this.$nextTick(function () {
|
||||
this.showInput = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.defect-tip {
|
||||
background: #EDEDED;
|
||||
border: solid #E1E1E1 1px;
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.el-input {
|
||||
width: 80%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,30 @@
|
|||
<template>
|
||||
<el-form label-position="right" label-width="160px" size="small">
|
||||
<el-form-item :label="'AzureDevops 信息'">
|
||||
<ms-instructions-icon size="10" :content="'该信息为通过Azure Devops提交缺陷的用户令牌信息,若未填写,则使用组织中配置的默认信息'"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('organization.integration.azure_pat')" prop="pat">
|
||||
<el-input v-model="data.azureDevopsPat" :placeholder="$t('organization.integration.input_azure_pat')"/>
|
||||
</el-form-item>
|
||||
<el-form-item >
|
||||
<el-button type="primary" style="float: right" @click="$emit('auth', 'AzureDevops')" size="mini">
|
||||
{{$t('commons.validate')}}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsInstructionsIcon from "@/business/components/common/components/MsInstructionsIcon";
|
||||
export default {
|
||||
name: "AzureDevopsUserInfo",
|
||||
components: {MsInstructionsIcon},
|
||||
props: ['data'],
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.instructions-icon {
|
||||
margin-left: -5px;
|
||||
}
|
||||
</style>
|
|
@ -54,6 +54,7 @@
|
|||
<jira-user-info @auth="handleAuth" v-if="hasJira" :data="currentPlatformInfo"/>
|
||||
<tapd-user-info @auth="handleAuth" v-if="hasTapd" :data="currentPlatformInfo"/>
|
||||
<zentao-user-info @auth="handleAuth" v-if="hasZentao" :data="currentPlatformInfo"/>
|
||||
<azure-devops-user-info @auth="handleAuth" v-if="hasAzure" :data="currentPlatformInfo"/>
|
||||
<template v-slot:footer>
|
||||
<ms-dialog-footer
|
||||
@cancel="updateVisible = false"
|
||||
|
@ -100,10 +101,11 @@ import JiraUserInfo from "@/business/components/settings/personal/JiraUserInfo";
|
|||
import TapdUserInfo from "@/business/components/settings/personal/TapdUserInfo";
|
||||
import {getIntegrationService} from "@/network/organization";
|
||||
import ZentaoUserInfo from "@/business/components/settings/personal/ZentaoUserInfo";
|
||||
import AzureDevopsUserInfo from "@/business/components/settings/personal/AzureDevopsUserInfo";
|
||||
|
||||
export default {
|
||||
name: "MsPersonSetting",
|
||||
components: {ZentaoUserInfo, TapdUserInfo, JiraUserInfo, MsDialogFooter, MsTableOperatorButton},
|
||||
components: {ZentaoUserInfo, TapdUserInfo, JiraUserInfo,AzureDevopsUserInfo, MsDialogFooter, MsTableOperatorButton},
|
||||
inject: [
|
||||
'reload'
|
||||
],
|
||||
|
@ -122,12 +124,14 @@ export default {
|
|||
jiraPassword: '',
|
||||
tapdUserName: '',
|
||||
zentaoUserName: '',
|
||||
zentaoPassword: ''
|
||||
zentaoPassword: '',
|
||||
azureDevopsPat:''
|
||||
},
|
||||
ruleForm: {},
|
||||
hasJira: false,
|
||||
hasTapd: false,
|
||||
hasZentao: false,
|
||||
hasAzure:false,
|
||||
rule: {
|
||||
name: [
|
||||
{required: true, message: this.$t('member.input_name'), trigger: 'blur'},
|
||||
|
@ -217,6 +221,9 @@ export default {
|
|||
if (platforms.indexOf("Zentao") !== -1) {
|
||||
this.hasZentao = true;
|
||||
}
|
||||
if (platforms.indexOf("AzureDevops") !== -1) {
|
||||
this.hasAzure = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
editPassword(row) {
|
||||
|
|
|
@ -122,6 +122,9 @@
|
|||
</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.repeatable')" prop="repeatable">
|
||||
<el-switch v-model="form.repeatable"></el-switch>
|
||||
</el-form-item>
|
||||
|
@ -206,6 +209,7 @@ export default {
|
|||
tapd: false,
|
||||
jira: false,
|
||||
zentao: false,
|
||||
azuredevops: false,
|
||||
form: {},
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
|
@ -299,6 +303,9 @@ export default {
|
|||
if (platforms.indexOf("Zentao") !== -1) {
|
||||
this.zentao = true;
|
||||
}
|
||||
if (platforms.indexOf("AzureDevops") !== -1) {
|
||||
this.azuredevops = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
submit(formName) {
|
||||
|
@ -359,6 +366,7 @@ export default {
|
|||
this.tapd = false;
|
||||
this.jira = false;
|
||||
this.zentao = false;
|
||||
this.azuredevops = false;
|
||||
},
|
||||
search() {
|
||||
this.list();
|
||||
|
|
|
@ -134,6 +134,9 @@
|
|||
</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.repeatable')" prop="repeatable">
|
||||
<el-switch v-model="form.repeatable"></el-switch>
|
||||
</el-form-item>
|
||||
|
@ -300,6 +303,7 @@ export default {
|
|||
tapd: false,
|
||||
jira: false,
|
||||
zentao: false,
|
||||
azuredevops: false,
|
||||
form: {},
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
|
@ -430,6 +434,9 @@ export default {
|
|||
if (platforms.indexOf("Zentao") !== -1) {
|
||||
this.zentao = true;
|
||||
}
|
||||
if (platforms.indexOf("AzureDevops") !== -1) {
|
||||
this.azuredevops = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
submit(formName) {
|
||||
|
@ -494,6 +501,7 @@ export default {
|
|||
this.tapd = false;
|
||||
this.jira = false;
|
||||
this.zentao = false;
|
||||
this.azuredevops = false;
|
||||
},
|
||||
search() {
|
||||
this.list();
|
||||
|
|
|
@ -109,6 +109,7 @@ export default {
|
|||
Jira: 'JIRA',
|
||||
Tapd: 'Tapd',
|
||||
Zentao: '禅道',
|
||||
AzureDevops: 'Azure Devops',
|
||||
},
|
||||
operators: [
|
||||
{
|
||||
|
|
|
@ -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 AZURE_DEVOPS = 'AzureDevops';
|
||||
|
||||
export const GROUP_SYSTEM = 'SYSTEM';
|
||||
export const GROUP_ORGANIZATION = 'ORGANIZATION';
|
||||
|
|
|
@ -29,6 +29,7 @@ export const ISSUE_PLATFORM_OPTION = [
|
|||
{value: 'Jira',text: 'JIRA'},
|
||||
{value: 'Tapd',text: 'Tapd'},
|
||||
{value: 'Zentao',text: '禅道'},
|
||||
{value: 'AzureDevops',text: 'Azure Devops'},
|
||||
];
|
||||
|
||||
export const FIELD_TYPE_MAP = {
|
||||
|
|
|
@ -424,7 +424,17 @@ export default {
|
|||
successful_operation: 'Successful operation',
|
||||
not_integrated: 'The platform is not integrated',
|
||||
choose_platform: 'Please choose an integrated platform',
|
||||
verified: 'Verified'
|
||||
azure_issuetype: 'Azure issue type',
|
||||
azure_storytype: 'Azure story type',
|
||||
input_azure_issuetype: 'Please enter the issue type',
|
||||
input_azure_storytype: 'Please enter the story type',
|
||||
azure_pat:'PersonalAccessTokens',
|
||||
azure_devops_url:'Azure Devops url',
|
||||
azure_organization_id:'Azure Organization ID',
|
||||
input_azure_pat:'Please enter Personal Access Token',
|
||||
input_azure_url:'Please enter Azure Devops Url',
|
||||
input_azure_id:'Please enter Azure Organization ID',
|
||||
use_tip_azure: 'Azure Devops URL+PersonalAccessTokens(User Settings-Personal Access Tokens-New Token)',
|
||||
}
|
||||
},
|
||||
project: {
|
||||
|
@ -445,6 +455,7 @@ export default {
|
|||
tapd_id: 'TAPD Project ID',
|
||||
jira_key: 'JIRA Project key',
|
||||
zentao_id: 'Zentao Project ID',
|
||||
azureDevops_id: 'AzureDevops Project ID',
|
||||
manager: 'Manager',
|
||||
no_data: 'No Data',
|
||||
select: 'Select',
|
||||
|
|
|
@ -423,7 +423,18 @@ export default {
|
|||
successful_operation: '操作成功',
|
||||
not_integrated: '未集成该平台',
|
||||
choose_platform: '请选择集成的平台',
|
||||
verified: '验证通过'
|
||||
verified: '验证通过',
|
||||
azure_issuetype: '问题类型',
|
||||
azure_storytype: '需求类型',
|
||||
input_azure_issuetype: '请输入问题类型',
|
||||
input_azure_storytype: '请输入需求类型',
|
||||
azure_pat:'PersonalAccessTokens',
|
||||
azure_devops_url:'Azure Devops 地址',
|
||||
azure_organization_id:'Azure 组织ID',
|
||||
input_azure_pat:'请输入 Personal Access Token',
|
||||
input_azure_url:'请输入 Azure Devops 地址',
|
||||
input_azure_organization_id:'请输入 Azure 组织ID',
|
||||
use_tip_azure: 'Azure Devops 地址+令牌(账户设置-个人访问令牌-创建令牌)',
|
||||
}
|
||||
},
|
||||
project: {
|
||||
|
@ -444,6 +455,7 @@ export default {
|
|||
tapd_id: 'TAPD项目ID',
|
||||
jira_key: 'JIRA项目key',
|
||||
zentao_id: 'Zentao项目ID',
|
||||
azureDevops_id: 'AzureDevops项目ID',
|
||||
manager: '项目管理',
|
||||
no_data: '无数据',
|
||||
select: '选择项目',
|
||||
|
|
|
@ -402,6 +402,7 @@ export default {
|
|||
jira_storytype: '需求類型',
|
||||
input_api_account: '請輸入賬號',
|
||||
input_api_password: '請輸入密碼',
|
||||
input_api_pat:'請输入 Personal Access Token',
|
||||
input_jira_url: '請輸入Jira地址,例:https://metersphere.atlassian.net/',
|
||||
input_jira_issuetype: '請輸入問題類型',
|
||||
input_jira_storytype: '請輸入需求類型',
|
||||
|
@ -423,7 +424,18 @@ export default {
|
|||
successful_operation: '操作成功',
|
||||
not_integrated: '未集成該平臺',
|
||||
choose_platform: '請選擇集成的平臺',
|
||||
verified: '驗證通過'
|
||||
verified: '驗證通過',
|
||||
azure_issuetype: '問題類型',
|
||||
azure_storytype: '需求類型',
|
||||
input_azure_issuetype: '請輸入問題類型',
|
||||
input_azure_storytype: '請輸入需求類型',
|
||||
azure_pat:'PersonalAccessTokens',
|
||||
azure_devops_url:'Azure Devops 地址',
|
||||
azure_organization_id:'Azure 組織ID',
|
||||
input_azure_pat:'請輸入 Personal Access Token',
|
||||
input_azure_url:'請輸入 Azure Devops 地址',
|
||||
input_azure_id:'請輸入 Azure 組織ID',
|
||||
use_tip_azure: 'Azure Devops 地址+令牌(賬戶設置-個人訪問令牌-創建令牌)',
|
||||
}
|
||||
},
|
||||
project: {
|
||||
|
@ -444,6 +456,7 @@ export default {
|
|||
tapd_id: 'TAPD項目ID',
|
||||
jira_key: 'JIRA項目key',
|
||||
zentao_id: 'Zentao項目ID',
|
||||
azureDevops_id: 'AzureDevops項目ID',
|
||||
manager: '項目管理',
|
||||
no_data: '無數據',
|
||||
select: '選擇項目',
|
||||
|
|
Loading…
Reference in New Issue