From f54e7aaaeb0e53398efb859e1db1b48c0c75f47d Mon Sep 17 00:00:00 2001 From: wxg0103 <727495428@qq.com> Date: Fri, 3 Dec 2021 17:26:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=B5=8B=E8=AF=95=E8=B7=9F=E8=B8=AA):=20?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=E6=96=B0=E5=A2=9E=E5=85=AC?= =?UTF-8?q?=E5=85=B1=E7=94=A8=E4=BE=8B=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --story=1004196 --user=王孝刚 功能用例增加公共用例库(X-Pack) https://www.tapd.cn/55049933/s/1080558 --- .../io/metersphere/base/domain/Project.java | 6 +- .../base/domain/ProjectExample.java | 132 ++++- .../io/metersphere/base/domain/TestCase.java | 2 + .../base/domain/TestCaseExample.java | 60 +++ .../metersphere/base/mapper/ProjectMapper.xml | 199 ++++--- .../base/mapper/TestCaseMapper.xml | 497 +++++++++--------- .../base/mapper/ext/ExtTestCaseMapper.java | 13 +- .../base/mapper/ext/ExtTestCaseMapper.xml | 111 +++- .../track/controller/TestCaseController.java | 32 +- .../controller/TestCaseNodeController.java | 5 + .../testcase/QueryTestCaseRequest.java | 1 + .../track/service/TestCaseNodeService.java | 5 + .../track/service/TestCaseService.java | 52 ++ .../db/migration/V102__v1.16_release.sql | 29 +- .../src/main/resources/generatorConfig.xml | 35 +- .../api/definition/ApiDefinition.vue | 10 +- .../components/module/ModulePublicButton.vue | 66 +++ .../project/head/ProjectHeaderMenus.vue | 20 +- .../project/menu/appmanage/AppManage.vue | 176 +++++++ .../src/business/components/project/router.js | 8 +- .../components/track/case/TestCase.vue | 57 +- .../track/case/components/BatchMove.vue | 12 +- .../track/case/components/ShowMoreBtn.vue | 8 + .../track/case/components/TestCaseEdit.vue | 27 +- .../track/case/components/TestCaseList.vue | 194 ++++++- .../track/common/TestCaseNodeTree.vue | 20 +- .../src/common/js/default-table-header.js | 1 + frontend/src/i18n/en-US.js | 30 +- frontend/src/i18n/zh-CN.js | 28 +- frontend/src/i18n/zh-TW.js | 28 +- 30 files changed, 1446 insertions(+), 418 deletions(-) create mode 100644 frontend/src/business/components/api/definition/components/module/ModulePublicButton.vue create mode 100644 frontend/src/business/components/project/menu/appmanage/AppManage.vue 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 57c42ac558..6382be8482 100644 --- a/backend/src/main/java/io/metersphere/base/domain/Project.java +++ b/backend/src/main/java/io/metersphere/base/domain/Project.java @@ -45,9 +45,13 @@ public class Project implements Serializable { private String azureFilterId; + private String apiQuick; + + private Boolean casePublic; + 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 5530581f3d..6ca9b34dd9 100644 --- a/backend/src/main/java/io/metersphere/base/domain/ProjectExample.java +++ b/backend/src/main/java/io/metersphere/base/domain/ProjectExample.java @@ -1434,6 +1434,136 @@ public class ProjectExample { return (Criteria) this; } + public Criteria andApiQuickIsNull() { + addCriterion("api_quick is null"); + return (Criteria) this; + } + + public Criteria andApiQuickIsNotNull() { + addCriterion("api_quick is not null"); + return (Criteria) this; + } + + public Criteria andApiQuickEqualTo(String value) { + addCriterion("api_quick =", value, "apiQuick"); + return (Criteria) this; + } + + public Criteria andApiQuickNotEqualTo(String value) { + addCriterion("api_quick <>", value, "apiQuick"); + return (Criteria) this; + } + + public Criteria andApiQuickGreaterThan(String value) { + addCriterion("api_quick >", value, "apiQuick"); + return (Criteria) this; + } + + public Criteria andApiQuickGreaterThanOrEqualTo(String value) { + addCriterion("api_quick >=", value, "apiQuick"); + return (Criteria) this; + } + + public Criteria andApiQuickLessThan(String value) { + addCriterion("api_quick <", value, "apiQuick"); + return (Criteria) this; + } + + public Criteria andApiQuickLessThanOrEqualTo(String value) { + addCriterion("api_quick <=", value, "apiQuick"); + return (Criteria) this; + } + + public Criteria andApiQuickLike(String value) { + addCriterion("api_quick like", value, "apiQuick"); + return (Criteria) this; + } + + public Criteria andApiQuickNotLike(String value) { + addCriterion("api_quick not like", value, "apiQuick"); + return (Criteria) this; + } + + public Criteria andApiQuickIn(List values) { + addCriterion("api_quick in", values, "apiQuick"); + return (Criteria) this; + } + + public Criteria andApiQuickNotIn(List values) { + addCriterion("api_quick not in", values, "apiQuick"); + return (Criteria) this; + } + + public Criteria andApiQuickBetween(String value1, String value2) { + addCriterion("api_quick between", value1, value2, "apiQuick"); + return (Criteria) this; + } + + public Criteria andApiQuickNotBetween(String value1, String value2) { + addCriterion("api_quick not between", value1, value2, "apiQuick"); + return (Criteria) this; + } + + public Criteria andCasePublicIsNull() { + addCriterion("case_public is null"); + return (Criteria) this; + } + + public Criteria andCasePublicIsNotNull() { + addCriterion("case_public is not null"); + return (Criteria) this; + } + + public Criteria andCasePublicEqualTo(Boolean value) { + addCriterion("case_public =", value, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicNotEqualTo(Boolean value) { + addCriterion("case_public <>", value, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicGreaterThan(Boolean value) { + addCriterion("case_public >", value, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicGreaterThanOrEqualTo(Boolean value) { + addCriterion("case_public >=", value, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicLessThan(Boolean value) { + addCriterion("case_public <", value, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicLessThanOrEqualTo(Boolean value) { + addCriterion("case_public <=", value, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicIn(List values) { + addCriterion("case_public in", values, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicNotIn(List values) { + addCriterion("case_public not in", values, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicBetween(Boolean value1, Boolean value2) { + addCriterion("case_public between", value1, value2, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicNotBetween(Boolean value1, Boolean value2) { + addCriterion("case_public not between", value1, value2, "casePublic"); + return (Criteria) this; + } + public Criteria andPlatformIsNull() { addCriterion("platform is null"); return (Criteria) this; @@ -1657,4 +1787,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/domain/TestCase.java b/backend/src/main/java/io/metersphere/base/domain/TestCase.java index d705696118..57d537f5f5 100644 --- a/backend/src/main/java/io/metersphere/base/domain/TestCase.java +++ b/backend/src/main/java/io/metersphere/base/domain/TestCase.java @@ -59,5 +59,7 @@ public class TestCase implements Serializable { private Long order; + private Boolean casePublic; + private static final long serialVersionUID = 1L; } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/TestCaseExample.java b/backend/src/main/java/io/metersphere/base/domain/TestCaseExample.java index 7722990039..04f60db0f3 100644 --- a/backend/src/main/java/io/metersphere/base/domain/TestCaseExample.java +++ b/backend/src/main/java/io/metersphere/base/domain/TestCaseExample.java @@ -1933,6 +1933,66 @@ public class TestCaseExample { addCriterion("`order` not between", value1, value2, "order"); return (Criteria) this; } + + public Criteria andCasePublicIsNull() { + addCriterion("case_public is null"); + return (Criteria) this; + } + + public Criteria andCasePublicIsNotNull() { + addCriterion("case_public is not null"); + return (Criteria) this; + } + + public Criteria andCasePublicEqualTo(Boolean value) { + addCriterion("case_public =", value, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicNotEqualTo(Boolean value) { + addCriterion("case_public <>", value, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicGreaterThan(Boolean value) { + addCriterion("case_public >", value, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicGreaterThanOrEqualTo(Boolean value) { + addCriterion("case_public >=", value, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicLessThan(Boolean value) { + addCriterion("case_public <", value, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicLessThanOrEqualTo(Boolean value) { + addCriterion("case_public <=", value, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicIn(List values) { + addCriterion("case_public in", values, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicNotIn(List values) { + addCriterion("case_public not in", values, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicBetween(Boolean value1, Boolean value2) { + addCriterion("case_public between", value1, value2, "casePublic"); + return (Criteria) this; + } + + public Criteria andCasePublicNotBetween(Boolean value1, Boolean value2) { + addCriterion("case_public not between", value1, value2, "casePublic"); + return (Criteria) this; + } } public static class Criteria extends GeneratedCriteria { 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 82bab88ff3..99e82a80e8 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ProjectMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ProjectMapper.xml @@ -2,28 +2,30 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + @@ -84,10 +86,11 @@ - 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 + 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, + api_quick, case_public, platform, third_part_template @@ -456,42 +465,45 @@ custom_num = #{record.customNum,jdbcType=VARCHAR}, - - step_model = #{record.stepModel,jdbcType=VARCHAR}, - - - create_user = #{record.createUser,jdbcType=VARCHAR}, - - - original_status = #{record.originalStatus,jdbcType=VARCHAR}, - - - delete_time = #{record.deleteTime,jdbcType=BIGINT}, - - - delete_user_id = #{record.deleteUserId,jdbcType=VARCHAR}, - - - `order` = #{record.order,jdbcType=BIGINT}, - - - prerequisite = #{record.prerequisite,jdbcType=LONGVARCHAR}, - - - remark = #{record.remark,jdbcType=LONGVARCHAR}, - - - steps = #{record.steps,jdbcType=LONGVARCHAR}, - - - step_description = #{record.stepDescription,jdbcType=LONGVARCHAR}, - - - expected_result = #{record.expectedResult,jdbcType=LONGVARCHAR}, - - - custom_fields = #{record.customFields,jdbcType=LONGVARCHAR}, - + + step_model = #{record.stepModel,jdbcType=VARCHAR}, + + + create_user = #{record.createUser,jdbcType=VARCHAR}, + + + original_status = #{record.originalStatus,jdbcType=VARCHAR}, + + + delete_time = #{record.deleteTime,jdbcType=BIGINT}, + + + delete_user_id = #{record.deleteUserId,jdbcType=VARCHAR}, + + + `order` = #{record.order,jdbcType=BIGINT}, + + + case_public = #{record.casePublic,jdbcType=BIT}, + + + prerequisite = #{record.prerequisite,jdbcType=LONGVARCHAR}, + + + remark = #{record.remark,jdbcType=LONGVARCHAR}, + + + steps = #{record.steps,jdbcType=LONGVARCHAR}, + + + step_description = #{record.stepDescription,jdbcType=LONGVARCHAR}, + + + expected_result = #{record.expectedResult,jdbcType=LONGVARCHAR}, + + + custom_fields = #{record.customFields,jdbcType=LONGVARCHAR}, + @@ -526,15 +538,16 @@ delete_time = #{record.deleteTime,jdbcType=BIGINT}, delete_user_id = #{record.deleteUserId,jdbcType=VARCHAR}, `order` = #{record.order,jdbcType=BIGINT}, + case_public = #{record.casePublic,jdbcType=BIT}, prerequisite = #{record.prerequisite,jdbcType=LONGVARCHAR}, remark = #{record.remark,jdbcType=LONGVARCHAR}, steps = #{record.steps,jdbcType=LONGVARCHAR}, step_description = #{record.stepDescription,jdbcType=LONGVARCHAR}, expected_result = #{record.expectedResult,jdbcType=LONGVARCHAR}, custom_fields = #{record.customFields,jdbcType=LONGVARCHAR} - - - + + + update test_case @@ -564,10 +577,11 @@ original_status = #{record.originalStatus,jdbcType=VARCHAR}, delete_time = #{record.deleteTime,jdbcType=BIGINT}, delete_user_id = #{record.deleteUserId,jdbcType=VARCHAR}, - `order` = #{record.order,jdbcType=BIGINT} - - - + `order` = #{record.order,jdbcType=BIGINT}, + case_public = #{record.casePublic,jdbcType=BIT} + + + update test_case @@ -632,109 +646,114 @@ custom_num = #{customNum,jdbcType=VARCHAR}, - - step_model = #{stepModel,jdbcType=VARCHAR}, - - - create_user = #{createUser,jdbcType=VARCHAR}, - - - original_status = #{originalStatus,jdbcType=VARCHAR}, - - - delete_time = #{deleteTime,jdbcType=BIGINT}, - - - delete_user_id = #{deleteUserId,jdbcType=VARCHAR}, - - - `order` = #{order,jdbcType=BIGINT}, - - - prerequisite = #{prerequisite,jdbcType=LONGVARCHAR}, - - - remark = #{remark,jdbcType=LONGVARCHAR}, - - - steps = #{steps,jdbcType=LONGVARCHAR}, - - - step_description = #{stepDescription,jdbcType=LONGVARCHAR}, - - - expected_result = #{expectedResult,jdbcType=LONGVARCHAR}, - - - custom_fields = #{customFields,jdbcType=LONGVARCHAR}, - + + step_model = #{stepModel,jdbcType=VARCHAR}, + + + create_user = #{createUser,jdbcType=VARCHAR}, + + + original_status = #{originalStatus,jdbcType=VARCHAR}, + + + delete_time = #{deleteTime,jdbcType=BIGINT}, + + + delete_user_id = #{deleteUserId,jdbcType=VARCHAR}, + + + `order` = #{order,jdbcType=BIGINT}, + + + case_public = #{casePublic,jdbcType=BIT}, + + + prerequisite = #{prerequisite,jdbcType=LONGVARCHAR}, + + + remark = #{remark,jdbcType=LONGVARCHAR}, + + + steps = #{steps,jdbcType=LONGVARCHAR}, + + + step_description = #{stepDescription,jdbcType=LONGVARCHAR}, + + + expected_result = #{expectedResult,jdbcType=LONGVARCHAR}, + + + custom_fields = #{customFields,jdbcType=LONGVARCHAR}, + where id = #{id,jdbcType=VARCHAR} - update test_case - set node_id = #{nodeId,jdbcType=VARCHAR}, - node_path = #{nodePath,jdbcType=VARCHAR}, - project_id = #{projectId,jdbcType=VARCHAR}, - `name` = #{name,jdbcType=VARCHAR}, - `type` = #{type,jdbcType=VARCHAR}, - maintainer = #{maintainer,jdbcType=VARCHAR}, - priority = #{priority,jdbcType=VARCHAR}, - `method` = #{method,jdbcType=VARCHAR}, - create_time = #{createTime,jdbcType=BIGINT}, - update_time = #{updateTime,jdbcType=BIGINT}, - test_id = #{testId,jdbcType=VARCHAR}, - sort = #{sort,jdbcType=INTEGER}, - num = #{num,jdbcType=INTEGER}, - other_test_name = #{otherTestName,jdbcType=VARCHAR}, - review_status = #{reviewStatus,jdbcType=VARCHAR}, - tags = #{tags,jdbcType=VARCHAR}, - demand_id = #{demandId,jdbcType=VARCHAR}, - demand_name = #{demandName,jdbcType=VARCHAR}, - `status` = #{status,jdbcType=VARCHAR}, - custom_num = #{customNum,jdbcType=VARCHAR}, - step_model = #{stepModel,jdbcType=VARCHAR}, - create_user = #{createUser,jdbcType=VARCHAR}, - original_status = #{originalStatus,jdbcType=VARCHAR}, - delete_time = #{deleteTime,jdbcType=BIGINT}, - delete_user_id = #{deleteUserId,jdbcType=VARCHAR}, - `order` = #{order,jdbcType=BIGINT}, - prerequisite = #{prerequisite,jdbcType=LONGVARCHAR}, - remark = #{remark,jdbcType=LONGVARCHAR}, - steps = #{steps,jdbcType=LONGVARCHAR}, - step_description = #{stepDescription,jdbcType=LONGVARCHAR}, - expected_result = #{expectedResult,jdbcType=LONGVARCHAR}, - custom_fields = #{customFields,jdbcType=LONGVARCHAR} - where id = #{id,jdbcType=VARCHAR} + update test_case + set node_id = #{nodeId,jdbcType=VARCHAR}, + node_path = #{nodePath,jdbcType=VARCHAR}, + project_id = #{projectId,jdbcType=VARCHAR}, + `name` = #{name,jdbcType=VARCHAR}, + `type` = #{type,jdbcType=VARCHAR}, + maintainer = #{maintainer,jdbcType=VARCHAR}, + priority = #{priority,jdbcType=VARCHAR}, + `method` = #{method,jdbcType=VARCHAR}, + create_time = #{createTime,jdbcType=BIGINT}, + update_time = #{updateTime,jdbcType=BIGINT}, + test_id = #{testId,jdbcType=VARCHAR}, + sort = #{sort,jdbcType=INTEGER}, + num = #{num,jdbcType=INTEGER}, + other_test_name = #{otherTestName,jdbcType=VARCHAR}, + review_status = #{reviewStatus,jdbcType=VARCHAR}, + tags = #{tags,jdbcType=VARCHAR}, + demand_id = #{demandId,jdbcType=VARCHAR}, + demand_name = #{demandName,jdbcType=VARCHAR}, + `status` = #{status,jdbcType=VARCHAR}, + custom_num = #{customNum,jdbcType=VARCHAR}, + step_model = #{stepModel,jdbcType=VARCHAR}, + create_user = #{createUser,jdbcType=VARCHAR}, + original_status = #{originalStatus,jdbcType=VARCHAR}, + delete_time = #{deleteTime,jdbcType=BIGINT}, + delete_user_id = #{deleteUserId,jdbcType=VARCHAR}, + `order` = #{order,jdbcType=BIGINT}, + case_public = #{casePublic,jdbcType=BIT}, + prerequisite = #{prerequisite,jdbcType=LONGVARCHAR}, + remark = #{remark,jdbcType=LONGVARCHAR}, + steps = #{steps,jdbcType=LONGVARCHAR}, + step_description = #{stepDescription,jdbcType=LONGVARCHAR}, + expected_result = #{expectedResult,jdbcType=LONGVARCHAR}, + custom_fields = #{customFields,jdbcType=LONGVARCHAR} + where id = #{id,jdbcType=VARCHAR} - update test_case - set node_id = #{nodeId,jdbcType=VARCHAR}, - node_path = #{nodePath,jdbcType=VARCHAR}, - project_id = #{projectId,jdbcType=VARCHAR}, - `name` = #{name,jdbcType=VARCHAR}, - `type` = #{type,jdbcType=VARCHAR}, - maintainer = #{maintainer,jdbcType=VARCHAR}, - priority = #{priority,jdbcType=VARCHAR}, - `method` = #{method,jdbcType=VARCHAR}, - create_time = #{createTime,jdbcType=BIGINT}, - update_time = #{updateTime,jdbcType=BIGINT}, - test_id = #{testId,jdbcType=VARCHAR}, - sort = #{sort,jdbcType=INTEGER}, - num = #{num,jdbcType=INTEGER}, - other_test_name = #{otherTestName,jdbcType=VARCHAR}, - review_status = #{reviewStatus,jdbcType=VARCHAR}, - tags = #{tags,jdbcType=VARCHAR}, - demand_id = #{demandId,jdbcType=VARCHAR}, - demand_name = #{demandName,jdbcType=VARCHAR}, - `status` = #{status,jdbcType=VARCHAR}, - custom_num = #{customNum,jdbcType=VARCHAR}, - step_model = #{stepModel,jdbcType=VARCHAR}, - create_user = #{createUser,jdbcType=VARCHAR}, - original_status = #{originalStatus,jdbcType=VARCHAR}, - delete_time = #{deleteTime,jdbcType=BIGINT}, - delete_user_id = #{deleteUserId,jdbcType=VARCHAR}, - `order` = #{order,jdbcType=BIGINT} - where id = #{id,jdbcType=VARCHAR} + update test_case + set node_id = #{nodeId,jdbcType=VARCHAR}, + node_path = #{nodePath,jdbcType=VARCHAR}, + project_id = #{projectId,jdbcType=VARCHAR}, + `name` = #{name,jdbcType=VARCHAR}, + `type` = #{type,jdbcType=VARCHAR}, + maintainer = #{maintainer,jdbcType=VARCHAR}, + priority = #{priority,jdbcType=VARCHAR}, + `method` = #{method,jdbcType=VARCHAR}, + create_time = #{createTime,jdbcType=BIGINT}, + update_time = #{updateTime,jdbcType=BIGINT}, + test_id = #{testId,jdbcType=VARCHAR}, + sort = #{sort,jdbcType=INTEGER}, + num = #{num,jdbcType=INTEGER}, + other_test_name = #{otherTestName,jdbcType=VARCHAR}, + review_status = #{reviewStatus,jdbcType=VARCHAR}, + tags = #{tags,jdbcType=VARCHAR}, + demand_id = #{demandId,jdbcType=VARCHAR}, + demand_name = #{demandName,jdbcType=VARCHAR}, + `status` = #{status,jdbcType=VARCHAR}, + custom_num = #{customNum,jdbcType=VARCHAR}, + step_model = #{stepModel,jdbcType=VARCHAR}, + create_user = #{createUser,jdbcType=VARCHAR}, + original_status = #{originalStatus,jdbcType=VARCHAR}, + delete_time = #{deleteTime,jdbcType=BIGINT}, + delete_user_id = #{deleteUserId,jdbcType=VARCHAR}, + `order` = #{order,jdbcType=BIGINT}, + case_public = #{casePublic,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/ExtTestCaseMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.java index 749db029bb..d089aafee6 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.java @@ -20,6 +20,8 @@ public interface ExtTestCaseMapper { List list(@Param("request") QueryTestCaseRequest request); + List publicList(@Param("request") QueryTestCaseRequest request); + int moduleCount(@Param("request") QueryTestCaseRequest request); List listIds(@Param("request") QueryTestCaseRequest request); @@ -99,16 +101,19 @@ public interface ExtTestCaseMapper { List selectRelateIdsByQuery(@Param("request") BaseQueryRequest query); - List> moduleCountByCollection(@Param("request")QueryTestCaseRequest request); + List> moduleCountByCollection(@Param("request") QueryTestCaseRequest request); List getCustomFieldsByIds(@Param("ids") List ids); int deleteToGc(@Param("request") TestCase testCase); + + int deletePublic(@Param("request") TestCase testCase); + int reduction(@Param("ids") List ids); void checkOriginalStatusByIds(@Param("ids") List ids); - List selectIdsByNodeIds(@Param("ids")List nodeIds); + List selectIdsByNodeIds(@Param("ids") List nodeIds); TestCaseWithBLOBs getTestCaseStep(@Param("id") String id); @@ -118,7 +123,7 @@ public interface ExtTestCaseMapper { Long getLastOrder(@Param("projectId")String projectId, @Param("baseOrder") Long baseOrder); - Long getPreOrder(@Param("projectId")String projectId, @Param("baseOrder") Long baseOrder); + Long getPreOrder(@Param("projectId") String projectId, @Param("baseOrder") Long baseOrder); List getTestCase(@Param("request") QueryTestCaseRequest request); @@ -127,4 +132,6 @@ public interface ExtTestCaseMapper { int countByIds(@Param("ids") List ids); String getLastExecStatusById(String id); + + int countByWorkSpaceId(String workSpaceId); } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml index 4fa702d073..87fb1140b5 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml @@ -188,6 +188,85 @@ + + + - update test_case set original_status=status, - status = 'Trash', - delete_time = #{request.deleteTime}, - delete_user_id = #{request.deleteUserId} + update test_case + set original_status=status, + status = 'Trash', + delete_time = #{request.deleteTime}, + delete_user_id = #{request.deleteUserId} where id = #{request.id} + + + update test_case + set case_public = false + where id = #{request.id} + + update test_case set @@ -622,7 +709,21 @@ + + diff --git a/backend/src/main/java/io/metersphere/track/controller/TestCaseController.java b/backend/src/main/java/io/metersphere/track/controller/TestCaseController.java index 74f07fa9b9..0390403577 100644 --- a/backend/src/main/java/io/metersphere/track/controller/TestCaseController.java +++ b/backend/src/main/java/io/metersphere/track/controller/TestCaseController.java @@ -65,6 +65,13 @@ public class TestCaseController { return PageUtils.setPageInfo(page, testCaseService.listTestCase(request)); } + @PostMapping("/publicList/{goPage}/{pageSize}") + @RequiresPermissions(PermissionConstants.PROJECT_TRACK_CASE_READ) + public Pager> publicList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryTestCaseRequest request) { + Page page = PageHelper.startPage(goPage, pageSize, true); + return PageUtils.setPageInfo(page, testCaseService.publicListTestCase(request)); + } + @GetMapping("/list/{projectId}") @RequiresPermissions("PROJECT_TRACK_CASE:READ") public List list(@PathVariable String projectId) { @@ -238,6 +245,15 @@ public class TestCaseController { return testCaseService.deleteTestCaseToGc(testCaseId); } + @PostMapping("/deletePublic/{testCaseId}") + @MsAuditLog(module = "track_test_case", type = OperLogConstants.GC, beforeEvent = "#msClass.getLogDetails(#testCaseId)", msClass = TestCaseService.class) + @SendNotice(taskType = NoticeConstants.TaskType.TRACK_TEST_CASE_TASK, event = NoticeConstants.Event.DELETE, target = "#targetClass.getTestCase(#testCaseId)", targetClass = TestCaseService.class, + mailTemplate = "track/TestCaseDelete", subject = "测试用例通知") + public int deletePublic(@PathVariable String testCaseId) { + checkPermissionService.checkTestCaseOwner(testCaseId); + return testCaseService.deleteTestCasePublic(testCaseId); + } + @PostMapping("/import/{projectId}/{userId}/{importType}") @MsAuditLog(module = "track_test_case", type = OperLogConstants.IMPORT, project = "#projectId") @@ -285,18 +301,32 @@ public class TestCaseController { @SendNotice(taskType = NoticeConstants.TaskType.TRACK_TEST_CASE_TASK, target = "#targetClass.findByBatchRequest(#request)", targetClass = TestCaseService.class, event = NoticeConstants.Event.UPDATE, mailTemplate = "track/TestCaseUpdate", subject = "测试用例通知") public void editTestCaseBath(@RequestBody TestCaseBatchRequest request) { + List ids = request.getIds(); + for (String id : ids) { + checkPermissionService.checkTestCaseOwner(id); + } testCaseService.editTestCaseBath(request); } @PostMapping("/batch/copy") @RequiresPermissions(PermissionConstants.PROJECT_TRACK_CASE_READ_EDIT) - @MsAuditLog(module = "track_test_case", type = OperLogConstants.BATCH_ADD, beforeEvent = "#msClass.getLogDetails(#request.ids)", content = "#msClass.getLogDetails(#request.ids)", msClass = TestCaseService.class) + @MsAuditLog(module = "track_test_case", type = OperLogConstants.BATCH_UPDATE, beforeEvent = "#msClass.getLogDetails(#request.ids)", content = "#msClass.getLogDetails(#request.ids)", msClass = TestCaseService.class) @SendNotice(taskType = NoticeConstants.TaskType.TRACK_TEST_CASE_TASK, target = "#targetClass.findByBatchRequest(#request)", targetClass = TestCaseService.class, event = NoticeConstants.Event.CREATE, mailTemplate = "track/TestCaseUpdate", subject = "测试用例通知") public void copyTestCaseBath(@RequestBody TestCaseBatchRequest request) { testCaseService.copyTestCaseBath(request); } + @PostMapping("/batch/copy/public") + @RequiresPermissions(PermissionConstants.PROJECT_TRACK_CASE_READ_EDIT) + @MsAuditLog(module = "track_test_case", type = OperLogConstants.BATCH_ADD, beforeEvent = "#msClass.getLogDetails(#request.ids)", content = "#msClass.getLogDetails(#request.ids)", msClass = TestCaseService.class) + @SendNotice(taskType = NoticeConstants.TaskType.TRACK_TEST_CASE_TASK, target = "#targetClass.findByBatchRequest(#request)", targetClass = TestCaseService.class, + event = NoticeConstants.Event.CREATE, mailTemplate = "track/TestCaseUpdate", subject = "测试用例通知") + public void copyTestCaseBathPublic(@RequestBody TestCaseBatchRequest request) { + testCaseService.copyTestCaseBathPublic(request); + } + + @PostMapping("/batch/delete") @RequiresPermissions(PermissionConstants.PROJECT_TRACK_CASE_READ_DELETE) @MsAuditLog(module = "track_test_case", type = OperLogConstants.BATCH_DEL, beforeEvent = "#msClass.getLogDetails(#request.ids)", msClass = TestCaseService.class) diff --git a/backend/src/main/java/io/metersphere/track/controller/TestCaseNodeController.java b/backend/src/main/java/io/metersphere/track/controller/TestCaseNodeController.java index a9b13e6422..05edaa788e 100644 --- a/backend/src/main/java/io/metersphere/track/controller/TestCaseNodeController.java +++ b/backend/src/main/java/io/metersphere/track/controller/TestCaseNodeController.java @@ -35,6 +35,11 @@ public class TestCaseNodeController { return testCaseNodeService.trashCount(projectId); } + @GetMapping("/publicCount/{workSpaceId}") + public long publicCount(@PathVariable String workSpaceId) { + return testCaseNodeService.publicCount(workSpaceId); + } + /*模块列表列表*/ @PostMapping("/list/all/plan") public List getAllNodeByPlanId(@RequestBody QueryNodeRequest request) { diff --git a/backend/src/main/java/io/metersphere/track/request/testcase/QueryTestCaseRequest.java b/backend/src/main/java/io/metersphere/track/request/testcase/QueryTestCaseRequest.java index 978e2cc733..197ec8bfce 100644 --- a/backend/src/main/java/io/metersphere/track/request/testcase/QueryTestCaseRequest.java +++ b/backend/src/main/java/io/metersphere/track/request/testcase/QueryTestCaseRequest.java @@ -57,4 +57,5 @@ public class QueryTestCaseRequest extends BaseQueryRequest { private String operator; //操作时间 private Long operationTime; + private boolean casePublic; } diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java index 0267104715..79f47aeda7 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java @@ -803,4 +803,9 @@ public class TestCaseNodeService extends NodeTreeService { } } } + + public long publicCount(String workSpaceId) { + + return extTestCaseMapper.countByWorkSpaceId(workSpaceId); + } } diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java index 6eb410e6b0..ce22ab817a 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java @@ -375,6 +375,12 @@ public class TestCaseService { return extTestCaseMapper.deleteToGc(testCase); } + public int deleteTestCasePublic(String testCaseId) { + TestCase testCase = new TestCase(); + testCase.setId(testCaseId); + return extTestCaseMapper.deletePublic(testCase); + } + public List listTestCase(QueryTestCaseRequest request) { this.initRequest(request, true); setDefaultOrder(request); @@ -385,6 +391,18 @@ public class TestCaseService { returnList = this.parseStatus(returnList); return returnList; } + + public List publicListTestCase(QueryTestCaseRequest request) { + this.initRequest(request, true); + setDefaultOrder(request); + if (request.getFilters() != null && !request.getFilters().containsKey("status")) { + request.getFilters().put("status", new ArrayList<>(0)); + } + List returnList = extTestCaseMapper.publicList(request); + returnList = this.parseStatus(returnList); + return returnList; + } + public void setDefaultOrder(QueryTestCaseRequest request) { List orders = ServiceUtils.getDefaultSortOrder(request.getOrders()); OrderRequest order = new OrderRequest(); @@ -1304,6 +1322,40 @@ public class TestCaseService { } } + public void copyTestCaseBathPublic(TestCaseBatchRequest request) { + ServiceUtils.getSelectAllIds(request, request.getCondition(), + (query) -> extTestCaseMapper.selectIds(query)); + List ids = request.getIds(); + if (CollectionUtils.isEmpty(ids)) { + return; + } + TestCaseExample exampleList = new TestCaseExample(); + exampleList.createCriteria().andIdIn(request.getIds()); + List list = testCaseMapper.selectByExampleWithBLOBs(exampleList); + for (TestCaseWithBLOBs item : list) { + TestCaseWithBLOBs batchCopy = new TestCaseWithBLOBs(); + BeanUtils.copyBean(batchCopy, item); + checkTestCaseExist(batchCopy); + batchCopy.setId(UUID.randomUUID().toString()); + batchCopy.setCreateTime(System.currentTimeMillis()); + batchCopy.setUpdateTime(System.currentTimeMillis()); + checkTestCustomNum(batchCopy); + batchCopy.setNum(getNextNum(SessionUtils.getCurrentProjectId())); + if (StringUtils.isBlank(batchCopy.getCustomNum())) { + batchCopy.setCustomNum(batchCopy.getNum().toString()); + } + batchCopy.setCreateUser(SessionUtils.getUserId()); + batchCopy.setMaintainer(SessionUtils.getUserId()); + batchCopy.setReviewStatus(TestCaseReviewStatus.Prepare.name()); + batchCopy.setStatus(TestCaseReviewStatus.Prepare.name()); + batchCopy.setNodePath(request.getNodePath()); + batchCopy.setNodeId(request.getNodeId()); + batchCopy.setProjectId(SessionUtils.getCurrentProjectId()); + batchCopy.setCasePublic(false); + testCaseMapper.insert(batchCopy); + } + } + public void deleteTestCaseBath(TestCaseBatchRequest request) { TestCaseExample example = this.getBatchExample(request); deleteTestPlanTestCaseBath(request.getIds()); 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 04c7b7d9e8..9a49de665d 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,11 +1,28 @@ -- 新增字段 -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 '是否使用第三方平台缺陷模板'; +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'; +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'; + +-- 公共用例库 +ALTER TABLE project + ADD case_public tinyint(1) DEFAULT NULL COMMENT '是否开启用例公共库'; +ALTER TABLE project + ADD api_quick varchar(50) DEFAULT NULL COMMENT 'api定义快捷调试按钮', + +ALTER TABLE test_case + ADD case_public tinyint(1) DEFAULT NULL COMMENT '是否是公共用例'; \ No newline at end of file diff --git a/backend/src/main/resources/generatorConfig.xml b/backend/src/main/resources/generatorConfig.xml index 1894b97876..7ab1c66b16 100644 --- a/backend/src/main/resources/generatorConfig.xml +++ b/backend/src/main/resources/generatorConfig.xml @@ -71,31 +71,30 @@ - -
+
+ - - - - - - - - - + + + + + + + + + - - + diff --git a/frontend/src/business/components/api/definition/ApiDefinition.vue b/frontend/src/business/components/api/definition/ApiDefinition.vue index 0479c23693..d75721e0ad 100644 --- a/frontend/src/business/components/api/definition/ApiDefinition.vue +++ b/frontend/src/business/components/api/definition/ApiDefinition.vue @@ -464,7 +464,15 @@ export default { }, addTab(tab) { if (tab.name === 'add') { - this.handleTabsEdit(this.$t('api_test.definition.request.fast_debug'), "debug"); + this.result = this.$get('/project/get/' + this.projectId, res => { + let projectData = res.data; + if (projectData && projectData.apiQuick === 'api') { + this.handleTabAdd("ADD"); + } else { + this.handleTabsEdit(this.$t('api_test.definition.request.fast_debug'), "debug"); + } + }) + } else if (tab.name === 'trash') { if (this.$refs.trashApiList) { this.$refs.trashApiList.initTable(); diff --git a/frontend/src/business/components/api/definition/components/module/ModulePublicButton.vue b/frontend/src/business/components/api/definition/components/module/ModulePublicButton.vue new file mode 100644 index 0000000000..51f47e2b74 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/module/ModulePublicButton.vue @@ -0,0 +1,66 @@ + + + + + diff --git a/frontend/src/business/components/project/head/ProjectHeaderMenus.vue b/frontend/src/business/components/project/head/ProjectHeaderMenus.vue index cb0a598024..b385cf7908 100644 --- a/frontend/src/business/components/project/head/ProjectHeaderMenus.vue +++ b/frontend/src/business/components/project/head/ProjectHeaderMenus.vue @@ -33,7 +33,7 @@ {{ $t('project.version_manage') }} - + {{ $t('project.app_manage') }} @@ -48,6 +48,7 @@ import MsShowAll from "@/business/components/common/head/ShowAll"; import MsRecentList from "@/business/components/common/head/RecentList"; import MsCreateButton from "@/business/components/common/head/CreateButton"; import ProjectChange from "@/business/components/common/head/ProjectSwitch"; +import {getCurrentProjectID, getCurrentUserId, getCurrentWorkspaceId} from "@/common/js/utils"; export default { name: "ProjectHeaderMenus", @@ -56,6 +57,7 @@ export default { return { currentProject: '', pathName: '', + isProjectAdmin: true }; }, watch: { @@ -66,11 +68,25 @@ export default { } } }, + created() { + this.$get("/user/group/list/project/" + getCurrentProjectID() + "/" + getCurrentUserId(), res => { + let data = res.data; + if (data) { + data.forEach(row => { + if (row.id === 'project_admin') { + this.isProjectAdmin = false; + } + }) + } else { + this.isProjectAdmin = true; + } + }) + }, methods: { clickPlanMenu() { this.$info(this.$t('commons.function_planning')); return false; - } + }, } }; diff --git a/frontend/src/business/components/project/menu/appmanage/AppManage.vue b/frontend/src/business/components/project/menu/appmanage/AppManage.vue new file mode 100644 index 0000000000..a2053d772d --- /dev/null +++ b/frontend/src/business/components/project/menu/appmanage/AppManage.vue @@ -0,0 +1,176 @@ + + + + + + diff --git a/frontend/src/business/components/project/router.js b/frontend/src/business/components/project/router.js index bc215f6b41..3446a7f7ea 100644 --- a/frontend/src/business/components/project/router.js +++ b/frontend/src/business/components/project/router.js @@ -6,6 +6,8 @@ const ProjectLog = () => import('@/business/components/project/menu/Log') const ProjectCodeSegment = () => import('@/business/components/project/menu/function/CustomFunction') const ProjectFileManage = () => import('@/business/components/project/menu/file/FileManage') const ProjectUserGroup = () => import('@/business/components/project/menu/UserGroup') +const ProjectAppManage = () => import('@/business/components/project/menu/appmanage/AppManage') + export default { path: "/project", @@ -47,7 +49,11 @@ export default { { path: 'file/manage', component: ProjectFileManage - } + }, + { + path: 'app', + component: ProjectAppManage + }, ] }; diff --git a/frontend/src/business/components/track/case/TestCase.vue b/frontend/src/business/components/track/case/TestCase.vue index 590e3d2512..3516472803 100644 --- a/frontend/src/business/components/track/case/TestCase.vue +++ b/frontend/src/business/components/track/case/TestCase.vue @@ -12,8 +12,10 @@ @createCase="handleCaseSimpleCreate($event, 'add')" @refreshAll="refreshAll" @enableTrash="enableTrash" + @enablePublic="enablePublic" :type="'edit'" :total='total' + :public-total="publicTotal" ref="nodeTree" /> @@ -31,12 +33,32 @@ @testCaseCopy="copyTestCase" @testCaseDetail="showTestCaseDetail" @getTrashList="getTrashList" + @getPublicList="getPublicList" @refresh="refresh" @refreshAll="refreshAll" @setCondition="setCondition" ref="testCaseTrashList"> + + + + { + getTrashList() { + this.$get("/case/node/trashCount/" + this.projectId, response => { this.total = response.data; }); }, + getPublicList() { + this.$get("/case/node/publicCount/" + getCurrentWorkspaceId(), response => { + this.publicTotal = response.data; + }); + }, updateActiveDom(activeDom) { openMinderConfirm(this, activeDom); }, @@ -386,6 +423,8 @@ export default { nodeChange(node) { this.condition.trashEnable = false; this.trashEnable = false; + this.condition.publicEnable = false; + this.publicEnable = false; this.activeName = "default"; }, refreshTable(data) { @@ -494,7 +533,7 @@ export default { this.$get("/project/get/" + this.projectId, result => { let data = result.data; if (data) { - this.$store.commit('setCurrentProjectIsCustomNum', data.customNum); + this.$store.commit('setCurrentProjectIsCustomNum', data.customNum); } }); }, @@ -502,6 +541,10 @@ export default { this.initApiTableOpretion = "trashEnable"; this.trashEnable = data; }, + enablePublic(data) { + this.initApiTableOpretion = "publicEnable"; + this.publicEnable = data; + }, } }; diff --git a/frontend/src/business/components/track/case/components/BatchMove.vue b/frontend/src/business/components/track/case/components/BatchMove.vue index 327bcd8bec..21c66191a9 100644 --- a/frontend/src/business/components/track/case/components/BatchMove.vue +++ b/frontend/src/business/components/track/case/components/BatchMove.vue @@ -58,6 +58,12 @@ result: {}, } }, + props: { + publicEnable: { + type: Boolean, + default: false, + }, + }, watch: { filterText(val) { this.$refs.tree.filter(val); @@ -85,7 +91,11 @@ }); } param.ids = this.selectIds; - this.$emit('moveSave', param); + if (this.publicEnable) { + this.$emit('copyPublic', param); + } else { + this.$emit('moveSave', param); + } }, refresh() { this.$emit("refresh"); diff --git a/frontend/src/business/components/track/case/components/ShowMoreBtn.vue b/frontend/src/business/components/track/case/components/ShowMoreBtn.vue index e700687439..85ff560faf 100644 --- a/frontend/src/business/components/track/case/components/ShowMoreBtn.vue +++ b/frontend/src/business/components/track/case/components/ShowMoreBtn.vue @@ -52,6 +52,14 @@ import {hasLicense, hasPermissions} from "@/common/js/utils"; } }, isDisable(item) { + if (item.isDisable) { + if (item.isDisable instanceof Function) { + console.log(item.isDisable()); + return item.isDisable(); + } else { + return item.isDisable; + } + } if (item.permissions && item.permissions.length > 0) { return !hasPermissions(...item.permissions); } diff --git a/frontend/src/business/components/track/case/components/TestCaseEdit.vue b/frontend/src/business/components/track/case/components/TestCaseEdit.vue index 322d4e4490..9dc1f7d400 100644 --- a/frontend/src/business/components/track/case/components/TestCaseEdit.vue +++ b/frontend/src/business/components/track/case/components/TestCaseEdit.vue @@ -34,6 +34,10 @@ $t('test_track.case.save_create_continue') }} + {{ + $t('test_track.case.save_add_public') + }} + @@ -142,7 +146,7 @@ import { getCurrentProjectID, getCurrentUser, getNodePath, getUUID, - handleCtrlSEvent, hasPermission, + handleCtrlSEvent, hasLicense, hasPermission, listenGoBack, removeGoBackListener } from "@/common/js/utils"; @@ -194,6 +198,8 @@ export default { return { // sysList: [],//一级选择框的数据 path: "/test/case/add", + publicEnable: false, + isXpack: false, testCaseTemplate: {}, options: REVIEW_STATUS, statuOptions: API_STATUS, @@ -391,7 +397,18 @@ export default { break; } } - }) + }), + this.result = this.$get('/project/get/' + this.projectId, res => { + let data = res.data; + if (data.casePublic) { + this.publicEnable = true; + } + }) + if (hasLicense()) { + this.isXpack = true; + } else { + this.isXpack = false; + } }, methods: { currentUser: () => { @@ -462,6 +479,9 @@ export default { }); } }) + } else if (e === 'ADD_AND_PUBLIC') { + this.form.casePublic = true; + this.saveCase(); } else { this.saveCase(); } @@ -562,6 +582,7 @@ export default { this.getTestCase(this.index); }, initTestCases(testCase) { + this.selectCondition.workspaceId = null; this.result = this.$post('/test/case/list/ids', this.selectCondition, response => { this.testCases = response.data; for (let i = 0; i < this.testCases.length; i++) { @@ -846,7 +867,7 @@ export default { } else { this.showFollow = true; - if(!this.form.follows){ + if (!this.form.follows) { this.form.follows = []; } this.form.follows.push(this.currentUser().id) diff --git a/frontend/src/business/components/track/case/components/TestCaseList.vue b/frontend/src/business/components/track/case/components/TestCaseList.vue index 8a30603d17..76e8dc8e24 100644 --- a/frontend/src/business/components/track/case/components/TestCaseList.vue +++ b/frontend/src/business/components/track/case/components/TestCaseList.vue @@ -129,11 +129,21 @@ + + + @@ -192,7 +202,8 @@ - + @@ -235,7 +246,7 @@ import { } from "@/common/js/tableUtils"; import HeaderLabelOperate from "@/business/components/common/head/HeaderLabelOperate"; import PlanStatusTableItem from "@/business/components/track/common/tableItems/plan/PlanStatusTableItem"; -import {getCurrentProjectID} from "@/common/js/utils"; +import {getCurrentProjectID, getCurrentUserId, getCurrentWorkspaceId} from "@/common/js/utils"; import {getTestTemplate} from "@/network/custom-field-template"; import {getProjectMember} from "@/network/user"; import MsTable from "@/business/components/common/components/table/MsTable"; @@ -279,6 +290,7 @@ export default { }, data() { return { + addPublic: false, projectName: "", type: TEST_CASE_LIST, tableHeaderKey: "TRACK_TEST_CASE", @@ -340,12 +352,29 @@ export default { permissions: ['PROJECT_TRACK_CASE:READ+DELETE'] }, { - name: this.$t('生成依赖关系'), + name: this.$t('test_track.case.generate_dependencies'), isXPack: true, handleClick: this.generateGraph, permissions: ['PROJECT_API_DEFINITION:READ+EDIT_API'] + }, + { + name: this.$t('test_track.case.batch_add_public'), + isXPack: true, + handleClick: this.handleBatchAddPublic, + permissions: ['PROJECT_API_DEFINITION:READ+EDIT_API'], } ], + publicButtons: [ + { + name: this.$t('test_track.case.batch_copy'), + handleClick: this.handleBatchMove, + permissions: ['PROJECT_TRACK_CASE:READ+EDIT'] + }, { + name: this.$t('test_track.case.batch_delete_case'), + handleClick: this.handleDeleteBatchToPublic, + permissions: ['PROJECT_TRACK_CASE:READ+DELETE'], + }, + ], trashButtons: [ { name: this.$t('commons.reduction'), @@ -375,6 +404,25 @@ export default { permissions: ['PROJECT_TRACK_CASE:READ+DELETE'] } ], + publicOperators: [ + { + tip: this.$t('commons.edit'), icon: "el-icon-edit", + exec: this.handleEdit, + permissions: ['PROJECT_TRACK_CASE:READ+EDIT'], + isDisable: this.isPublic + }, + { + tip: this.$t('commons.copy'), icon: "el-icon-copy-document", type: "success", + exec: this.handleCopyPublic, + permissions: ['PROJECT_TRACK_CASE:READ+COPY'] + }, + { + tip: this.$t('commons.delete'), icon: "el-icon-delete", type: "danger", + exec: this.handleDeleteToGc, + permissions: ['PROJECT_TRACK_CASE:READ+DELETE'], + isDisable: this.isPublic + } + ], trashOperators: [ { tip: this.$t('commons.reduction'), @@ -409,6 +457,10 @@ export default { type: Boolean, default: false, }, + publicEnable: { + type: Boolean, + default: false, + }, }, computed: { projectId() { @@ -439,6 +491,9 @@ export default { if (this.trashEnable) { this.operators = this.trashOperators; this.batchButtons = this.trashButtons; + } else if (this.publicEnable) { + this.operators = this.publicOperators; + this.batchButtons = this.publicButtons; } else { this.operators = this.simpleOperators; this.batchButtons = this.simpleButtons; @@ -493,6 +548,21 @@ export default { this.batchButtons = this.simpleButtons; this.condition.filters.status = []; } + }, + publicEnable() { + if (this.publicEnable) { + //更改表格按钮 + this.operators = this.publicOperators; + this.batchButtons = this.publicButtons; + this.condition.moduleIds = []; + initCondition(this.condition, false); + this.initTableData(); + } else { + //更改各种按钮 + this.operators = this.simpleOperators; + this.batchButtons = this.simpleButtons; + this.condition.filters.status = []; + } } }, methods: { @@ -580,7 +650,7 @@ export default { // param.planId = this.planId; this.condition.planId = this.planId; } - if (!this.trashEnable) { + if (!this.trashEnable && !this.publicEnable) { if (this.selectNodeIds && this.selectNodeIds.length > 0) { // param.nodeIds = this.selectNodeIds; this.condition.nodeIds = this.selectNodeIds; @@ -625,25 +695,48 @@ export default { if (this.projectId) { this.condition.projectId = this.projectId; this.$emit('setCondition', this.condition); - this.page.result = this.$post(this.buildPagePath('/test/case/list'), this.condition, response => { - let data = response.data; - this.page.total = data.itemCount; - this.page.data = data.listObject; - this.page.data.forEach(item => { - if (item.customFields) { - item.customFields = JSON.parse(item.customFields); - } - }); - this.page.data.forEach((item) => { - try { - item.tags = JSON.parse(item.tags); - } catch (e) { - item.tags = []; - } + if (this.publicEnable) { + this.condition.casePublic = true; + this.condition.workspaceId = getCurrentWorkspaceId(); + this.page.result = this.$post(this.buildPagePath('/test/case/publicList'), this.condition, response => { + let data = response.data; + this.page.total = data.itemCount; + this.page.data = data.listObject; + this.page.data.forEach(item => { + if (item.customFields) { + item.customFields = JSON.parse(item.customFields); + } + }); + this.page.data.forEach((item) => { + try { + item.tags = JSON.parse(item.tags); + } catch (e) { + item.tags = []; + } + }); + }) + } else { + this.page.result = this.$post(this.buildPagePath('/test/case/list'), this.condition, response => { + let data = response.data; + this.page.total = data.itemCount; + this.page.data = data.listObject; + this.page.data.forEach(item => { + if (item.customFields) { + item.customFields = JSON.parse(item.customFields); + } + }); + this.page.data.forEach((item) => { + try { + item.tags = JSON.parse(item.tags); + } catch (e) { + item.tags = []; + } + }); }); - }); + } this.$emit("getTrashList"); + this.$emit("getPublicList") } }, search() { @@ -664,6 +757,13 @@ export default { } }, + isPublic(testCase) { + if (testCase.maintainer !== getCurrentUserId() || testCase.createUser !== getCurrentUserId()) { + return true; + } else { + return false; + } + }, getCase(id) { this.$refs.testCasePreview.open(); this.rowCaseResult.loading = true; @@ -692,6 +792,10 @@ export default { this.$refs.testCasePreview.setData(this.rowCase); }); }, + handleCopyPublic(testCase) { + this.$refs.table.selectIds.push(testCase.id); + this.$refs.testBatchMove.open(this.treeNodes, this.$refs.table.selectIds, this.moduleOptions); + }, handleCopy(testCase) { this.$get('test/case/get/' + testCase.id, response => { let testCase = response.data; @@ -724,7 +828,11 @@ export default { confirmButtonText: this.$t('commons.confirm'), callback: (action) => { if (action === 'confirm') { - this._handleDeleteToGc(testCase); + if (this.publicEnable) { + this._handleDeletePublic(testCase); + } else { + this._handleDeleteToGc(testCase); + } } } }); @@ -789,6 +897,14 @@ export default { this.$success(this.$t('commons.delete_success')); }); }, + _handleDeletePublic(testCase) { + let testCaseId = testCase.id; + this.$post('/test/case/deletePublic/' + testCaseId, {}, () => { + this.$emit('refreshTable'); + this.initTableData(); + this.$success(this.$t('commons.delete_success')); + }); + }, refresh() { this.$refs.table.clear(); this.$emit('refresh'); @@ -891,6 +1007,34 @@ export default { this.getMaintainerOptions(); this.$refs.batchEdit.open(this.$refs.table.selectRows.size); }, + handleBatchAddPublic() { + this.$get('/project/get/' + getCurrentProjectID(), res => { + let data = res.data; + if (data.casePublic) { + let param = {}; + param.ids = this.$refs.table.selectIds; + param.casePublic = true; + param.condition = this.condition; + this.page.result = this.$post('/test/case/batch/edit', param, () => { + this.$success(this.$t('commons.save_success')); + this.refresh(); + }); + } else { + this.$warning(this.$t('test_track.case.public_warning')) + } + }) + + }, + handleDeleteBatchToPublic() { + let param = {}; + param.ids = this.$refs.table.selectIds; + param.casePublic = false; + param.condition = this.condition; + this.page.result = this.$post('/test/case/batch/edit', param, () => { + this.$success(this.$t('commons.save_success')); + this.refresh(); + }); + }, handleBatchMove() { this.isMoveBatch = true; this.$refs.testBatchMove.open(this.treeNodes, this.$refs.table.selectIds, this.moduleOptions); @@ -914,6 +1058,14 @@ export default { this.$refs.testBatchMove.close(); this.refresh(); }); + }, + copyPublic(param) { + param.condition = this.condition; + this.page.result = this.$post('/test/case/batch/copy/public', param, () => { + this.$success(this.$t('commons.save_success')); + this.$refs.testBatchMove.close(); + this.refresh(); + }); } } }; diff --git a/frontend/src/business/components/track/common/TestCaseNodeTree.vue b/frontend/src/business/components/track/common/TestCaseNodeTree.vue index ee6f91ca4b..1234cf9efa 100644 --- a/frontend/src/business/components/track/common/TestCaseNodeTree.vue +++ b/frontend/src/business/components/track/common/TestCaseNodeTree.vue @@ -24,6 +24,7 @@ :condition="condition" :commands="operators"/> + @@ -49,11 +50,21 @@ import {buildTree} from "../../api/definition/model/NodeTree"; import {buildNodePath} from "@/business/components/api/definition/model/NodeTree"; import {getCurrentProjectID} from "@/common/js/utils"; import ModuleTrashButton from "@/business/components/api/definition/components/module/ModuleTrashButton"; +import ModulePublicButton from "@/business/components/api/definition/components/module/ModulePublicButton"; import {getTestCaseNodes} from "@/network/testCase"; export default { name: "TestCaseNodeTree", - components: {MsSearchBar, TestCaseImport,TestCaseExport, TestCaseCreate, MsNodeTree, NodeEdit,ModuleTrashButton}, + components: { + MsSearchBar, + TestCaseImport, + TestCaseExport, + TestCaseCreate, + MsNodeTree, + NodeEdit, + ModuleTrashButton, + ModulePublicButton + }, data() { return { defaultProps: { @@ -92,6 +103,7 @@ export default { }, showOperator: Boolean, total: Number, + publicTotal: Number, }, watch: { treeNodes() { @@ -132,10 +144,14 @@ export default { refreshAll() { this.$emit('refreshAll'); }, - enableTrash(){ + enableTrash() { this.condition.trashEnable = true; this.$emit('enableTrash', this.condition.trashEnable); }, + enablePublic() { + this.condition.publicEnable = true; + this.$emit('enablePublic', this.condition.publicEnable); + }, list() { if (this.projectId) { this.result = getTestCaseNodes(this.projectId, data => { diff --git a/frontend/src/common/js/default-table-header.js b/frontend/src/common/js/default-table-header.js index dabfc9d66e..4f9a9322d9 100644 --- a/frontend/src/common/js/default-table-header.js +++ b/frontend/src/common/js/default-table-header.js @@ -184,6 +184,7 @@ export let CUSTOM_TABLE_HEADER = { {id: 'reviewStatus', key: '3', label: 'test_track.case.status'}, {id: 'tags', key: '4', label: 'commons.tag'}, {id: 'nodePath', key: '5', label: 'test_track.case.module'}, + {id: 'projectName', key: 'a', label: 'test_track.review.review_project'}, {id: 'updateTime', key: '6', label: 'commons.update_time'}, {id: 'createUser', key: '7', label: 'commons.create_user'}, {id: 'createTime', key: '8', label: 'commons.create_time'}, diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index d64471c3b1..6cbcd95e99 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -87,23 +87,25 @@ export default { show: 'Show', report: 'Report', user: 'User', - personal_password_info:'Same old and new password', + personal_password_info: 'Same old and new password', system: 'System', personal_setting: 'Personal Setting', api_keys: 'API Keys', - third_account:'Third Account', + third_account: 'Third Account', quota: 'Quota', test_resource_pool: 'Resource Pool', system_setting: 'Settings', api: 'API', - my_workstation:'MyWorkstation', + my_workstation: 'MyWorkstation', performance: 'Performance', + enable_settings: 'Enable Settings', + view_settings: 'View Settings', functional: 'Functional test', input_content: 'Please enter content', create: 'Create', edit: 'Edit', copy: 'Copy', - follow:'Follow', + follow: 'Follow', refresh: 'Refresh', remark: 'Remark', delete: 'Delete', @@ -641,12 +643,15 @@ export default { zentao_id: 'Zentao Project ID', azureDevops_id: 'AzureDevops Project ID', azureDevops_filter_id: 'AzureDevops Filter ID', - azureDevops_info:'AzureDevops Information', - azureDevops_tip:'This information is the user token information for submitting defects through Azure Devops. If not filled in, the default information configured in the workspace will be used', + azureDevops_info: 'AzureDevops Information', + azureDevops_tip: 'This information is the user token information for submitting defects through Azure Devops. If not filled in, the default information configured in the workspace will be used', manager: 'Manager', no_data: 'No Data', select: 'Select', repeatable: 'Interface definition URL repeatable', + repeatable_info: 'Interface definition URL repeatable \n When enabled, the interface definition repeatability check will not check the URL', + case_public: 'Common use case library', + public_info: 'Start common use case library \n You can use the public use case library data or add your own use cases to the public use case library', upload_file_again: 'Upload again', code_segment: { code_segment: "Custom Code", @@ -690,7 +695,9 @@ export default { group_desc: 'Add user groups and global configuration', code_segment_desc: 'Custom code snippet', test_case_custom_id: 'Test Case Custom ID', + test_case_custom_id_info: 'Test Case Custom ID \n The Case ID defaults to the system self increment ID', scenario_custom_id: 'Scenario Custom ID', + scenario_custom_id_info: 'Scenario Custom ID \n The scenario Case ID defaults to the system self increment ID', }, member: { create: 'Create', @@ -1020,7 +1027,8 @@ export default { } }, definition: { - id:'Api Definition ID', + api_quick_button: 'Api definition shortcut add button', + id: 'Api Definition ID', api_title: "Api test", case_title: "Test Case", doc_title: "DOC", @@ -1720,6 +1728,7 @@ export default { case_type: "Case Type", name: "Test case name", module: "Module", + project: 'Project', maintainer: "Maintainer", steps: "Steps", number: "Number", @@ -1737,12 +1746,13 @@ export default { delete_confirm: "Confirm delete test case", delete: "Delete case", save_create_continue: "Save and create continue", + save_add_public: "Save and add public", please_create_project: "No project available, please create the project first", create_module_first: "Please create module first", relate_test: "Relate test", relate_issue: "Relate Issue", demand_name_id: "Demand ID/Name", - please_select_relate_test: "请选择要关联的测试", + please_select_relate_test: "Please select the test to associate", relate_test_not_find: 'The associated test does not exist, please check the test case', other_relate_test_not_find: 'Associated test name, please go to the third party platform to execute', batch_handle: 'Batch processing (select {0} item)', @@ -1754,6 +1764,10 @@ export default { please_select_attr_value: 'Please select the value corresponding to the attribute', batch_edit_case: 'Batch editing', batch_move_case: 'Batch move', + batch_copy: 'Batch copy', + batch_add_public: 'Batch add public', + public_warning: 'The public library configuration is not enabled', + generate_dependencies: 'Generate dependencies', batch_delete_case: 'Batch delete', batch_unlink: 'Batch Unlink', unlink: 'Unlink', diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index 5886bd103d..5b52182836 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -84,7 +84,7 @@ export default { group: '用户组', personal_info: '个人信息', api_keys: 'API Keys', - third_account:'第三方平台账号', + third_account: '第三方平台账号', quota: '配额管理', status: '状态', show_all: '显示全部', @@ -93,18 +93,20 @@ export default { user: '用户', system: '系统', personal_setting: '个人设置', - personal_password_info:'新旧密码相同', + personal_password_info: '新旧密码相同', test_resource_pool: '测试资源池', system_setting: '系统设置', api: '接口测试', performance: '性能测试', + enable_settings: '启用设置', + view_settings: '显示设置', functional: '功能测试', - my_workstation:'我的工作台', + my_workstation: '我的工作台', input_content: '请输入内容', create: '新建', edit: '编辑', copy: '复制', - follow:'关注', + follow: '关注', refresh: '刷新', remark: '备注', delete: '删除', @@ -645,14 +647,17 @@ export default { zentao_id: 'Zentao项目ID', azureDevops_id: 'AzureDevops项目ID', azureDevops_filter_id: 'AzureDevops过滤ID', - azureDevops_info:'AzureDevops 信息', - azureDevops_tip:'该信息为通过Azure Devops提交缺陷的用户令牌信息,若未填写,则使用工作空间中配置的默认信息', + azureDevops_info: 'AzureDevops 信息', + azureDevops_tip: '该信息为通过Azure Devops提交缺陷的用户令牌信息,若未填写,则使用工作空间中配置的默认信息', manager: '项目管理', group_permission: '用户组与权限', global: '全局', no_data: '无数据', select: '选择项目', repeatable: '接口定义URL可重复', + repeatable_info: '接口定义URL可重复 \n 启用后接口定义重复性校验将不校验URL', + case_public: '公共用例库', + public_info: '启动公共用例库 \n 可以使用公共用例库数据,也可以自行添加用例至公共用例库', upload_file_again: '重新上传', code_segment: { code_segment: "自定义代码片段", @@ -696,7 +701,9 @@ export default { log_desc: '项目全部操作过程', code_segment_desc: '自定义代码片段', test_case_custom_id: '测试用例自定义ID', + test_case_custom_id_info: '测试用例自定义ID \n 用例ID默认为系统自增ID', scenario_custom_id: '场景自定义ID', + scenario_custom_id_info: '场景自定义ID \n 场景用例ID默认为系统自增ID', }, member: { create: '添加成员', @@ -1029,7 +1036,8 @@ export default { } }, definition: { - id:'接口定义ID', + api_quick_button: '接口定义快捷添加按钮', + id: '接口定义ID', api_title: "接口列表", case_title: "用例列表", doc_title: "文档", @@ -1726,6 +1734,7 @@ export default { case_type: "用例类型", name: "用例名称", module: "所属模块", + project: '所属项目', maintainer: "维护人", steps: "执行步骤", number: "编号", @@ -1743,6 +1752,7 @@ export default { delete_confirm: "确认删除测试用例", delete: "删除用例", save_create_continue: "保存并继续创建", + save_add_public: "保存并添加到公共用例库", please_create_project: "暂无项目,请先创建项目", create_module_first: "请先新建模块", relate_test: "关联测试", @@ -1761,6 +1771,10 @@ export default { batch_edit_case: '批量编辑', batch_move_case: '批量移动', batch_delete_case: '批量删除', + batch_copy: '批量复制', + batch_add_public: '批量添加到公共用例库', + public_warning: '未开启公共库用例配置', + generate_dependencies: '生成依赖关系', batch_unlink: '批量取消关联', unlink: '取消关联', project_name: '所属项目', diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index f634c4ed6a..fa74d8fa24 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -84,7 +84,7 @@ export default { group: '用戶組', personal_info: '個人信息', api_keys: 'API Keys', - third_account:'第三方平台賬號', + third_account: '第三方平台賬號', quota: '配額管理', status: '狀態', show_all: '顯示全部', @@ -93,18 +93,20 @@ export default { user: '用戶', system: '系統', personal_setting: '個人設置', - personal_password_info:'新舊密碼相同', + personal_password_info: '新舊密碼相同', test_resource_pool: '測試資源池', system_setting: '系統設置', api: '接口測試', performance: '性能測試', + enable_settings: '啟用設置', + view_settings: '顯示設置', functional: '功能測試', input_content: '請輸入內容', - my_workstation:'我的工作台', + my_workstation: '我的工作台', create: '新建', edit: '編輯', copy: '復製', - follow:'關注', + follow: '關注', refresh: '刷新', remark: '備註', delete: '刪除', @@ -645,12 +647,15 @@ export default { zentao_id: 'Zentao項目ID', azureDevops_id: 'AzureDevops項目ID', azureDevops_filter_id: 'AzureDevops過濾ID', - azureDevops_info:'AzureDevops 信息', - azureDevops_tip:'該信息為通過Azure Devops提交缺陷的用戶令牌信息,若未填寫,則使用工作空間中配置的默認信息', + azureDevops_info: 'AzureDevops 信息', + azureDevops_tip: '該信息為通過Azure Devops提交缺陷的用戶令牌信息,若未填寫,則使用工作空間中配置的默認信息', manager: '項目管理', no_data: '無數據', select: '選擇項目', repeatable: '接口定義URL可重復', + repeatable_info: '接口定義URL可重復 \n 啟用後介面定義重複性校驗將不校驗URL', + case_public: '公共用例庫', + public_info: '啟動公共用例庫 \n 可以使用公共用例庫數據,也可以自行添加用例至公共用例庫', upload_file_again: '重新上傳', code_segment: { code_segment: "自定義代碼片段", @@ -694,7 +699,9 @@ export default { group_desc: '添加用戶組與許可權以及全局配置', code_segment_desc: '自定義代碼片段', test_case_custom_id: '測試用例自定義ID', + test_case_custom_id_info: '測試用例自定義ID \n 用例ID默認為系統自增ID', scenario_custom_id: '場景自定義ID', + scenario_custom_id_info: '場景自定義ID \n 場景用例ID默認為系統自增ID', }, member: { create: '添加成員', @@ -1026,7 +1033,8 @@ export default { } }, definition: { - id:'接口定義ID', + api_quick_button: '接口定義快捷添加按鈕', + id: '接口定義ID', api_title: "接口列表", case_title: "用例列表", doc_title: "文檔", @@ -1729,6 +1737,7 @@ export default { case_type: "用例類型", name: "用例名稱", module: "所屬模塊", + project: '所屬項目', maintainer: "維護人", steps: "執行步驟", number: "編號", @@ -1746,6 +1755,7 @@ export default { delete_confirm: "確認刪除測試用例", delete: "刪除用例", save_create_continue: "保存並繼續創建", + save_add_public: "保存並添加到公共用例庫", please_create_project: "暫無項目,請先創建項目", create_module_first: "請先新建模塊", relate_test: "關聯測試", @@ -1763,6 +1773,10 @@ export default { please_select_attr_value: '請選擇屬性對應的值', batch_edit_case: '批量編輯', batch_move_case: '批量移動', + batch_copy: '批量複製', + generate_dependencies: '生成依賴關係', + batch_add_public: '批量添加到公共用例庫', + public_warning: '未開啟公共用例庫配寘', batch_delete_case: '批量刪除', batch_unlink: '批量取消關聯', unlink: '取消關聯',