feat(系统设置): 补充模板管理系统字段

This commit is contained in:
song-cc-rock 2024-03-28 17:12:40 +08:00 committed by Craftsman
parent 34be4b864c
commit c4bcc25c5a
28 changed files with 341 additions and 73 deletions

View File

@ -1,12 +1,16 @@
package io.metersphere.system.domain;
import io.metersphere.validation.groups.*;
import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import lombok.Data;
@Data
public class CustomFieldOption implements Serializable {
@ -29,13 +33,18 @@ public class CustomFieldOption implements Serializable {
@NotNull(message = "{custom_field_option.internal.not_blank}", groups = {Created.class})
private Boolean internal;
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "{custom_field_option.pos.not_blank}", groups = {Created.class})
private Long pos;
private static final long serialVersionUID = 1L;
public enum Column {
fieldId("field_id", "fieldId", "VARCHAR", false),
value("value", "value", "VARCHAR", true),
text("text", "text", "VARCHAR", true),
internal("internal", "internal", "BIT", false);
internal("internal", "internal", "BIT", false),
pos("pos", "pos", "BIGINT", false);
private static final String BEGINNING_DELIMITER = "`";

View File

@ -373,6 +373,66 @@ public class CustomFieldOptionExample {
addCriterion("internal not between", value1, value2, "internal");
return (Criteria) this;
}
public Criteria andPosIsNull() {
addCriterion("pos is null");
return (Criteria) this;
}
public Criteria andPosIsNotNull() {
addCriterion("pos is not null");
return (Criteria) this;
}
public Criteria andPosEqualTo(Long value) {
addCriterion("pos =", value, "pos");
return (Criteria) this;
}
public Criteria andPosNotEqualTo(Long value) {
addCriterion("pos <>", value, "pos");
return (Criteria) this;
}
public Criteria andPosGreaterThan(Long value) {
addCriterion("pos >", value, "pos");
return (Criteria) this;
}
public Criteria andPosGreaterThanOrEqualTo(Long value) {
addCriterion("pos >=", value, "pos");
return (Criteria) this;
}
public Criteria andPosLessThan(Long value) {
addCriterion("pos <", value, "pos");
return (Criteria) this;
}
public Criteria andPosLessThanOrEqualTo(Long value) {
addCriterion("pos <=", value, "pos");
return (Criteria) this;
}
public Criteria andPosIn(List<Long> values) {
addCriterion("pos in", values, "pos");
return (Criteria) this;
}
public Criteria andPosNotIn(List<Long> values) {
addCriterion("pos not in", values, "pos");
return (Criteria) this;
}
public Criteria andPosBetween(Long value1, Long value2) {
addCriterion("pos between", value1, value2, "pos");
return (Criteria) this;
}
public Criteria andPosNotBetween(Long value1, Long value2) {
addCriterion("pos not between", value1, value2, "pos");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {

View File

@ -6,6 +6,7 @@
<id column="value" jdbcType="VARCHAR" property="value" />
<result column="text" jdbcType="VARCHAR" property="text" />
<result column="internal" jdbcType="BIT" property="internal" />
<result column="pos" jdbcType="BIGINT" property="pos" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -66,7 +67,7 @@
</where>
</sql>
<sql id="Base_Column_List">
field_id, `value`, `text`, internal
field_id, `value`, `text`, internal, pos
</sql>
<select id="selectByExample" parameterType="io.metersphere.system.domain.CustomFieldOptionExample" resultMap="BaseResultMap">
select
@ -102,9 +103,9 @@
</delete>
<insert id="insert" parameterType="io.metersphere.system.domain.CustomFieldOption">
insert into custom_field_option (field_id, `value`, `text`,
internal)
internal, pos)
values (#{fieldId,jdbcType=VARCHAR}, #{value,jdbcType=VARCHAR}, #{text,jdbcType=VARCHAR},
#{internal,jdbcType=BIT})
#{internal,jdbcType=BIT}, #{pos,jdbcType=BIGINT})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.system.domain.CustomFieldOption">
insert into custom_field_option
@ -121,6 +122,9 @@
<if test="internal != null">
internal,
</if>
<if test="pos != null">
pos,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="fieldId != null">
@ -135,6 +139,9 @@
<if test="internal != null">
#{internal,jdbcType=BIT},
</if>
<if test="pos != null">
#{pos,jdbcType=BIGINT},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.system.domain.CustomFieldOptionExample" resultType="java.lang.Long">
@ -158,6 +165,9 @@
<if test="record.internal != null">
internal = #{record.internal,jdbcType=BIT},
</if>
<if test="record.pos != null">
pos = #{record.pos,jdbcType=BIGINT},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
@ -168,7 +178,8 @@
set field_id = #{record.fieldId,jdbcType=VARCHAR},
`value` = #{record.value,jdbcType=VARCHAR},
`text` = #{record.text,jdbcType=VARCHAR},
internal = #{record.internal,jdbcType=BIT}
internal = #{record.internal,jdbcType=BIT},
pos = #{record.pos,jdbcType=BIGINT}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -182,6 +193,9 @@
<if test="internal != null">
internal = #{internal,jdbcType=BIT},
</if>
<if test="pos != null">
pos = #{pos,jdbcType=BIGINT},
</if>
</set>
where field_id = #{fieldId,jdbcType=VARCHAR}
and `value` = #{value,jdbcType=VARCHAR}
@ -189,17 +203,18 @@
<update id="updateByPrimaryKey" parameterType="io.metersphere.system.domain.CustomFieldOption">
update custom_field_option
set `text` = #{text,jdbcType=VARCHAR},
internal = #{internal,jdbcType=BIT}
internal = #{internal,jdbcType=BIT},
pos = #{pos,jdbcType=BIGINT}
where field_id = #{fieldId,jdbcType=VARCHAR}
and `value` = #{value,jdbcType=VARCHAR}
</update>
<insert id="batchInsert" parameterType="map">
insert into custom_field_option
(field_id, `value`, `text`, internal)
(field_id, `value`, `text`, internal, pos)
values
<foreach collection="list" item="item" separator=",">
(#{item.fieldId,jdbcType=VARCHAR}, #{item.value,jdbcType=VARCHAR}, #{item.text,jdbcType=VARCHAR},
#{item.internal,jdbcType=BIT})
#{item.internal,jdbcType=BIT}, #{item.pos,jdbcType=BIGINT})
</foreach>
</insert>
<insert id="batchInsertSelective" parameterType="map">
@ -224,6 +239,9 @@
<if test="'internal'.toString() == column.value">
#{item.internal,jdbcType=BIT}
</if>
<if test="'pos'.toString() == column.value">
#{item.pos,jdbcType=BIGINT}
</if>
</foreach>
)
</foreach>

View File

@ -373,6 +373,7 @@ CREATE TABLE IF NOT EXISTS custom_field_option(
`value` VARCHAR(50) NOT NULL COMMENT '选项值' ,
`text` VARCHAR(255) NOT NULL COMMENT '选项值名称' ,
`internal` BIT NOT NULL DEFAULT 0 COMMENT '是否内置' ,
`pos` BIGINT NOT NULL COMMENT '自定义排序间隔5000' ,
PRIMARY KEY (field_id,value)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4

View File

@ -274,17 +274,17 @@ INSERT INTO test_resource_pool_blob (id, configuration) VALUES ((select id from
INSERT INTO project_test_resource_pool (project_id, test_resource_pool_id) VALUES ('100001100001', (SELECT id FROM test_resource_pool WHERE name = '默认资源池'));
-- 初始化组织功能用例字段
INSERT INTO custom_field(id, name, scene, `type`, remark, internal, scope_type, create_time, update_time, create_user, scope_id) VALUES(UUID_SHORT(), 'functional_priority', 'FUNCTIONAL', 'SELECT', '', 1, 'ORGANIZATION', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', '100001');
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'functional_priority'), 'P0', 'P0', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'functional_priority'), 'P1', 'P1', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'functional_priority'), 'P2', 'P2', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'functional_priority'), 'P3', 'P3', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'functional_priority'), 'P0', 'P0', 1, 5000);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'functional_priority'), 'P1', 'P1', 1, 10000);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'functional_priority'), 'P2', 'P2', 1, 15000);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'functional_priority'), 'P3', 'P3', 1, 20000);
-- 初始化组织缺陷严重程度
INSERT INTO custom_field(id, name, scene, `type`, remark, internal, scope_type, create_time, update_time, create_user, scope_id) VALUES(UUID_SHORT(), 'bug_degree', 'BUG', 'SELECT', '', 1, 'ORGANIZATION', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', '100001');
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'bug_degree'), UUID_SHORT(), '提示', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'bug_degree'), UUID_SHORT(), '一般', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'bug_degree'), UUID_SHORT(), '严重', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'bug_degree'), UUID_SHORT(), '致命', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'bug_degree'), UUID_SHORT(), '提示', 1, 5000);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'bug_degree'), UUID_SHORT(), '一般', 1, 10000);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'bug_degree'), UUID_SHORT(), '严重', 1, 15000);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'bug_degree'), UUID_SHORT(), '致命', 1, 20000);
-- 初始化组织功能用例默认模板, 缺陷默认模板
INSERT INTO template (id,name,remark,internal,update_time,create_time,create_user,scope_type,scope_id,enable_third_part, scene) VALUES (UUID_SHORT(), 'functional_default', '', 1, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', 'ORGANIZATION', '100001', 0, 'FUNCTIONAL');
@ -299,17 +299,17 @@ INSERT INTO project_version (id, project_id, name, description, status, latest,
-- 初始化项目功能用例字段
INSERT INTO custom_field(id, name, scene, `type`, remark, internal, scope_type, create_time, update_time, create_user, scope_id, ref_id) VALUES(UUID_SHORT(), 'functional_priority', 'FUNCTIONAL', 'SELECT', '', 1, 'PROJECT', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', '100001100001', (SELECT id FROM (SELECT * FROM custom_field) t where name = 'functional_priority'));
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'functional_priority' and scope_id = '100001100001'), 'P0', 'P0', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'functional_priority' and scope_id = '100001100001'), 'P1', 'P1', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'functional_priority' and scope_id = '100001100001'), 'P2', 'P2', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'functional_priority' and scope_id = '100001100001'), 'P3', 'P3', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'functional_priority' and scope_id = '100001100001'), 'P0', 'P0', 1, 5000);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'functional_priority' and scope_id = '100001100001'), 'P1', 'P1', 1, 10000);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'functional_priority' and scope_id = '100001100001'), 'P2', 'P2', 1, 15000);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'functional_priority' and scope_id = '100001100001'), 'P3', 'P3', 1, 20000);
-- 初始化项目缺陷严重程度
INSERT INTO custom_field(id, name, scene, `type`, remark, internal, scope_type, create_time, update_time, create_user, scope_id, ref_id) VALUES(UUID_SHORT(), 'bug_degree', 'BUG', 'SELECT', '', 1, 'PROJECT', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', '100001100001', (SELECT id FROM (SELECT * FROM custom_field) t where name = 'bug_degree'));
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'bug_degree' and scope_id = '100001100001'), UUID_SHORT(), '提示', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'bug_degree' and scope_id = '100001100001'), UUID_SHORT(), '一般', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'bug_degree' and scope_id = '100001100001'), UUID_SHORT(), '严重', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal) VALUES ((select id from custom_field where name = 'bug_degree' and scope_id = '100001100001'), UUID_SHORT(), '致命', 1);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'bug_degree' and scope_id = '100001100001'), UUID_SHORT(), '提示', 1, 5000);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'bug_degree' and scope_id = '100001100001'), UUID_SHORT(), '一般', 1, 10000);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'bug_degree' and scope_id = '100001100001'), UUID_SHORT(), '严重', 1, 15000);
INSERT INTO custom_field_option (field_id,value,`text`,internal, pos) VALUES ((select id from custom_field where name = 'bug_degree' and scope_id = '100001100001'), UUID_SHORT(), '致命', 1, 20000);
-- 初始化项目功能用例默认模板, 缺陷默认模板
INSERT INTO template (id,name,remark,internal,update_time,create_time,create_user,scope_type,scope_id,enable_third_part, scene, ref_id) VALUES (UUID_SHORT(), 'functional_default', '', 1, UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, 'admin', 'PROJECT', '100001100001', 0, 'FUNCTIONAL', (SELECT id FROM (SELECT * FROM template) t where name = 'functional_default'));

View File

@ -227,6 +227,8 @@ custom_field_option.value.not_blank=选项值不能为空
custom_field_option.value.length_range=选项值长度必须在{min}和{max}之间
custom_field_option.text.not_blank=选项值名称不能为空
custom_field_option.text.length_range=选项值名称长度必须在{min}和{max}之间
custom_field_option.internal.not_blank=选项值是否内置不能为空
custom_field_option.pos.not_blank=选项值顺序不能为空
# permission
permission.system_plugin.name=插件

View File

@ -229,6 +229,8 @@ custom_field_option.value.not_blank=value cannot be empty
custom_field_option.value.length_range=value length must be between {min} and {max}
custom_field_option.text.not_blank=text cannot be empty
custom_field_option.text.length_range=text length must be between {min} and {max}
custom_field_option.internal.not_blank=option_value_internal_cannot_be_null
custom_field_option.pos.not_blank=option_value_pos_cannot_be_empty
# permission
permission.system_plugin.name=Plugin
permission.system_organization_project.name=Organization Project

View File

@ -229,6 +229,8 @@ custom_field_option.value.not_blank=选项值不能为空
custom_field_option.value.length_range=选项值长度必须在{min}和{max}之间
custom_field_option.text.not_blank=选项值名称不能为空
custom_field_option.text.length_range=选项值名称长度必须在{min}和{max}之间
custom_field_option.internal.not_blank=选项值是否内置不能为空
custom_field_option.pos.not_blank=选项值顺序不能为空
# permission
permission.system_plugin.name=插件

View File

@ -228,6 +228,8 @@ custom_field_option.value.not_blank=選項值不能為空
custom_field_option.value.length_range=選項值長度必須在{min}和{max}之間
custom_field_option.text.not_blank=選項值名稱不能為空
custom_field_option.text.length_range=選項值名稱長度必須在{min}和{max}之間
custom_field_option.internal.not_blank=選項值是否內置不能爲空
custom_field_option.pos.not_blank=選項值順序不能爲空
# permission
permission.system_plugin.name=插件

View File

@ -2,13 +2,13 @@ package io.metersphere.project.controller;
import io.metersphere.project.service.ProjectCustomFieldLogService;
import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.param.CustomFieldUpdateRequestDefinition;
import io.metersphere.system.domain.*;
import io.metersphere.system.dto.sdk.CustomFieldDTO;
import io.metersphere.system.dto.sdk.request.CustomFieldOptionRequest;
import io.metersphere.system.dto.sdk.request.CustomFieldUpdateRequest;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.param.CustomFieldUpdateRequestDefinition;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.mapper.CustomFieldMapper;
@ -92,6 +92,7 @@ public class ProjectCustomFieldControllerTests extends BaseTest {
CustomFieldOptionRequest customFieldOptionRequest = new CustomFieldOptionRequest();
customFieldOptionRequest.setValue("1111");
customFieldOptionRequest.setText("test");
customFieldOptionRequest.setPos(5000L);
request.setEnableOptionKey(true);
List<CustomFieldOptionRequest> optionRequests = Arrays.asList(customFieldOptionRequest);
request.setOptions(optionRequests);
@ -165,6 +166,7 @@ public class ProjectCustomFieldControllerTests extends BaseTest {
CustomFieldOptionRequest customFieldOptionRequest = new CustomFieldOptionRequest();
customFieldOptionRequest.setValue("11112");
customFieldOptionRequest.setText("test1");
customFieldOptionRequest.setPos(5000L);
List<CustomFieldOptionRequest> optionRequests = Arrays.asList(customFieldOptionRequest);
request.setOptions(optionRequests);
this.requestPostWithOk(DEFAULT_UPDATE, request);

View File

@ -18,10 +18,10 @@ public enum DefaultBugCustomField {
*/
DEGREE("bug_degree", CustomFieldType.SELECT,
Arrays.asList(
getNewOption(IDGenerator.nextStr(), "提示"),
getNewOption(IDGenerator.nextStr(), "一般"),
getNewOption(IDGenerator.nextStr(), "严重"),
getNewOption(IDGenerator.nextStr(), "致命")
getNewOption(IDGenerator.nextStr(), "提示", 5000L),
getNewOption(IDGenerator.nextStr(), "一般", 10000L),
getNewOption(IDGenerator.nextStr(), "严重", 15000L),
getNewOption(IDGenerator.nextStr(), "致命", 20000L)
)
);
@ -47,10 +47,11 @@ public enum DefaultBugCustomField {
return options;
}
private static CustomFieldOption getNewOption(String value, String text) {
private static CustomFieldOption getNewOption(String value, String text, Long pos) {
CustomFieldOption customFieldOption = new CustomFieldOption();
customFieldOption.setValue(value);
customFieldOption.setText(text);
customFieldOption.setPos(pos);
customFieldOption.setInternal(true);
return customFieldOption;
}

View File

@ -14,10 +14,10 @@ public enum DefaultFunctionalCustomField {
PRIORITY("functional_priority", CustomFieldType.SELECT,
Arrays.asList(
getNewOption("P0", "P0"),
getNewOption("P1", "P1"),
getNewOption("P2", "P2"),
getNewOption("P3", "P3")
getNewOption("P0", "P0", 5000L),
getNewOption("P1", "P1", 10000L),
getNewOption("P2", "P2", 15000L),
getNewOption("P3", "P3", 20000L)
)
);
@ -43,10 +43,11 @@ public enum DefaultFunctionalCustomField {
return options;
}
private static CustomFieldOption getNewOption(String value, String text) {
private static CustomFieldOption getNewOption(String value, String text, Long pos) {
CustomFieldOption customFieldOption = new CustomFieldOption();
customFieldOption.setValue(value);
customFieldOption.setText(text);
customFieldOption.setPos(pos);
customFieldOption.setInternal(true);
return customFieldOption;
}

View File

@ -4,6 +4,7 @@ import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
@ -18,4 +19,8 @@ public class CustomFieldOptionRequest {
@NotBlank(message = "{custom_field_option.text.not_blank}", groups = {Created.class, Updated.class})
@Size(min = 1, max = 255, message = "{custom_field_option.text.length_range}", groups = {Created.class, Updated.class})
private String text;
@Schema(title = "选项值顺序", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "{custom_field_option.pos.not_blank}", groups = {Created.class})
private Long pos;
}

View File

@ -1,9 +1,9 @@
package io.metersphere.system.service;
import io.metersphere.system.dto.sdk.request.CustomFieldOptionRequest;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.system.domain.CustomFieldOption;
import io.metersphere.system.domain.CustomFieldOptionExample;
import io.metersphere.system.dto.sdk.request.CustomFieldOptionRequest;
import io.metersphere.system.mapper.CustomFieldOptionMapper;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
@ -12,6 +12,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@ -44,7 +45,11 @@ public class BaseCustomFieldOptionService {
public List<CustomFieldOption> getByFieldId(String fieldId) {
CustomFieldOptionExample example = new CustomFieldOptionExample();
example.createCriteria().andFieldIdEqualTo(fieldId);
return customFieldOptionMapper.selectByExample(example);
List<CustomFieldOption> options = customFieldOptionMapper.selectByExample(example);
if (CollectionUtils.isNotEmpty(options)) {
options.sort(Comparator.comparing(CustomFieldOption::getPos));
}
return options;
}
public void addByFieldId(String fieldId, List<CustomFieldOption> customFieldOptions) {
@ -90,6 +95,10 @@ public class BaseCustomFieldOptionService {
}
CustomFieldOptionExample example = new CustomFieldOptionExample();
example.createCriteria().andFieldIdIn(fieldIds);
return customFieldOptionMapper.selectByExample(example);
List<CustomFieldOption> options = customFieldOptionMapper.selectByExample(example);
if (CollectionUtils.isNotEmpty(options)) {
options.sort(Comparator.comparing(CustomFieldOption::getPos));
}
return options;
}
}

View File

@ -95,14 +95,14 @@
<!-- <table tableName="organization"/>-->
<!-- <table tableName="custom_field"/>-->
<!-- <table tableName="custom_field_option"/>-->
<table tableName="custom_field_option"/>
<!-- <table tableName="template"/>-->
<!-- <table tableName="template_custom_field"/>-->
<!-- <table tableName="status_item"/>-->
<!-- <table tableName="status_definition"/>-->
<!-- <table tableName="status_flow"/>-->
<!-- <table tableName="organization_parameter"/>-->
<table tableName="user_extend"/>
<!-- <table tableName="user_extend"/>-->
<!-- 要忽略的字段-->
<!-- <table tableName="test_case">

View File

@ -3,11 +3,11 @@ package io.metersphere.system.base;
import io.metersphere.sdk.constants.CustomFieldType;
import io.metersphere.sdk.constants.InternalUser;
import io.metersphere.sdk.constants.TemplateScene;
import io.metersphere.system.dto.sdk.request.CustomFieldOptionRequest;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.system.domain.CustomField;
import io.metersphere.system.dto.CustomFieldDao;
import io.metersphere.system.dto.sdk.request.CustomFieldOptionRequest;
import io.metersphere.system.resolver.field.AbstractCustomFieldResolver;
import io.metersphere.system.resolver.field.CustomFieldResolverFactory;
import io.metersphere.system.service.OrganizationCustomFieldService;
@ -48,6 +48,7 @@ public class BaseCustomFieldTestService {
CustomFieldOptionRequest customFieldOptionRequest = new CustomFieldOptionRequest();
customFieldOptionRequest.setValue(OPTION_VALUE);
customFieldOptionRequest.setText("test");
customFieldOptionRequest.setPos(5000L);
List<CustomFieldOptionRequest> optionRequests = Arrays.asList(customFieldOptionRequest);
CustomField customField = new CustomField();
customField.setType(customFieldType.name());

View File

@ -4,13 +4,13 @@ import io.metersphere.project.domain.Project;
import io.metersphere.project.domain.ProjectExample;
import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.param.CustomFieldUpdateRequestDefinition;
import io.metersphere.system.domain.*;
import io.metersphere.system.dto.sdk.CustomFieldDTO;
import io.metersphere.system.dto.sdk.request.CustomFieldOptionRequest;
import io.metersphere.system.dto.sdk.request.CustomFieldUpdateRequest;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.param.CustomFieldUpdateRequestDefinition;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.mapper.CustomFieldMapper;
@ -26,7 +26,10 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MvcResult;
import java.util.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import static io.metersphere.sdk.constants.InternalUserRole.ADMIN;
import static io.metersphere.system.controller.handler.result.CommonResultCode.*;
@ -92,6 +95,7 @@ public class OrganizationCustomFieldControllerTests extends BaseTest {
CustomFieldOptionRequest customFieldOptionRequest = new CustomFieldOptionRequest();
customFieldOptionRequest.setValue("1111");
customFieldOptionRequest.setText("test");
customFieldOptionRequest.setPos(5000L);
List<CustomFieldOptionRequest> optionRequests = Arrays.asList(customFieldOptionRequest);
request.setOptions(optionRequests);
@ -169,6 +173,7 @@ public class OrganizationCustomFieldControllerTests extends BaseTest {
CustomFieldOptionRequest customFieldOptionRequest = new CustomFieldOptionRequest();
customFieldOptionRequest.setValue("11112");
customFieldOptionRequest.setText("test1");
customFieldOptionRequest.setPos(5000L);
List<CustomFieldOptionRequest> optionRequests = Arrays.asList(customFieldOptionRequest);
request.setOptions(optionRequests);
this.requestPostWithOk(DEFAULT_UPDATE, request);

View File

@ -561,9 +561,11 @@ public class SystemProjectControllerTests extends BaseTest {
CustomFieldOptionRequest customFieldOptionRequest1 = new CustomFieldOptionRequest();
customFieldOptionRequest1.setValue("1");
customFieldOptionRequest1.setText("test1");
customFieldOptionRequest1.setPos(5000L);
CustomFieldOptionRequest customFieldOptionRequest2 = new CustomFieldOptionRequest();
customFieldOptionRequest2.setValue("2");
customFieldOptionRequest2.setText("test2");
customFieldOptionRequest2.setPos(10000L);
customField.setCreateUser("admin");
List<CustomFieldOptionRequest> optionRequests = Arrays.asList(customFieldOptionRequest1, customFieldOptionRequest2);
organizationCustomFieldService.add(customField, optionRequests);

View File

@ -28,6 +28,7 @@ export interface FieldOptions {
value: any;
text: string;
internal?: boolean; // 是否是内置模板
pos: number; // 排序字段
}
// 自定义字段

View File

@ -26,7 +26,7 @@
<div>
<a-checkbox-group v-model="selectSystemIds" class="checkboxContainer">
<div v-for="field in systemField" :key="field.id" class="item checkbox">
<a-checkbox :value="field.id" :disabled="field.internal"
<a-checkbox :value="field.id" :disabled="field.internal && field.name == t('case.caseLevel')"
><a-tooltip :content="field.name">
<div>{{ field.name }}</div></a-tooltip
></a-checkbox

View File

@ -60,21 +60,21 @@
<CaseTemplateLeftContent v-else />
</div>
<div class="preview-right px-4">
<!-- 系统内置的字段 {处理人, 状态...} -->
<DefectTemplateRightSystemField v-if="route.query.type === 'BUG'" />
<CaseTemplateRightSystemField v-else />
<!-- 自定义字段开始 -->
<VueDraggable v-model="selectData" handle=".form" ghost-class="ghost" @change="changeDrag">
<div v-for="(formItem, index) of selectData" :key="formItem.id" class="customWrapper">
<div class="action">
<span class="required">
<a-checkbox
v-if="!formItem.internal"
v-model="formItem.required"
class="mr-1"
@change="(value) => changeState(value, formItem)"
>{{ t('system.orgTemplate.required') }}</a-checkbox
>
<a-checkbox v-else v-model="formItem.required" class="mr-1" disabled>{{
t('system.orgTemplate.required')
}}</a-checkbox>
</span>
<div class="actionList">
<a-tooltip :content="t('system.orgTemplate.toTop')">
@ -94,7 +94,11 @@
@click="moveField(formItem as DefinedFieldItem, 'bottom')"
/>
</a-tooltip>
<a-divider v-if="!formItem.internal" direction="vertical" class="!m-0 !mx-2" />
<a-divider
v-if="!formItem.internal || formItem.fieldName != t('case.caseLevel')"
direction="vertical"
class="!m-0 !mx-2"
/>
<a-tooltip :content="t('common.edit')">
<MsIcon
v-if="!formItem.internal"
@ -103,14 +107,10 @@
@click="editField(formItem as DefinedFieldItem)"
/>
</a-tooltip>
<a-divider
v-if="!formItem.required && formItem.name != t('case.caseLevel')"
direction="vertical"
class="!m-0 !mx-2"
/>
<a-divider v-if="!formItem.internal" direction="vertical" class="!m-0 !mx-2" />
<a-tooltip :content="t('common.delete')">
<MsIcon
v-if="!formItem.required && formItem.name != t('case.caseLevel')"
v-if="formItem.fieldName != t('case.caseLevel')"
type="icon-icon_delete-trash_outlined"
size="16"
@click="deleteSelectedField(formItem as DefinedFieldItem)"
@ -158,6 +158,17 @@
</div>
</VueDraggable>
<!-- 自定义字段结束 -->
<!-- 标签字段开始 -->
<div class="tagWrapper">
<a-form layout="vertical" :model="defectForm">
<a-form-item field="tags" :label="t('system.orgTemplate.tags')" asterisk-position="end">
<a-input :disabled="true" :placeholder="t('system.orgTemplate.noDefaultPlaceholder')" />
</a-form-item>
</a-form>
</div>
<!-- 标签字段结束 -->
<div class="flex items-center">
<a-button class="mr-1 mt-1 px-0" type="text" @click="associatedField">
<template #icon>
@ -222,6 +233,8 @@
import CaseTemplateLeftContent from './caseTemplateLeftContent.vue';
import DefectTemplateLeftContent from './defectTemplateLeftContent.vue';
import EditFieldDrawer from './editFieldDrawer.vue';
import CaseTemplateRightSystemField from '@/views/setting/organization/template/components/caseTemplateRightSystemField.vue';
import DefectTemplateRightSystemField from '@/views/setting/organization/template/components/defectTemplateRightSystemField.vue';
import {
createOrganizeTemplateInfo,
@ -239,7 +252,7 @@
import { sleep } from '@/utils';
import { scrollIntoView } from '@/utils/dom';
import type { ActionTemplateManage, CustomField, DefinedFieldItem, SeneType } from '@/models/setting/template';
import type { ActionTemplateManage, CustomField, DefinedFieldItem } from '@/models/setting/template';
import { ProjectManagementRouteEnum, SettingRouteEnum } from '@/enums/routeEnum';
import { getTemplateName, getTotalFieldOptionList } from './fieldSetting';
@ -274,6 +287,7 @@
const initBugForm = {
name: '',
description: '',
tags: '',
};
const defectForm = ref({ ...initBugForm });
@ -794,4 +808,30 @@
:deep(.apiFieldIdClass) {
margin-bottom: 0;
}
:deep(.systemFieldWrapper) {
.arco-form-item {
margin-bottom: 0;
}
padding: 8px;
border: 1px solid transparent;
border-radius: 6px;
&:hover {
border: 1px solid var(--color-text-n8);
background: var(--color-text-n9);
}
}
.tagWrapper {
.arco-form-item {
margin-bottom: 0;
}
padding: 8px;
border: 1px solid transparent;
border-radius: 6px;
&:hover {
border: 1px solid var(--color-text-n8);
background: var(--color-text-n9);
}
}
</style>

View File

@ -0,0 +1,32 @@
<template>
<a-form ref="viewFormRef" layout="vertical" :model="systemForm">
<div class="systemFieldWrapper">
<a-form-item
field="caseModule"
:required="true"
:label="t('system.orgTemplate.caseModule')"
asterisk-position="end"
>
<a-select :disabled="true" :placeholder="t('system.orgTemplate.noDefaultPlaceholder')"></a-select>
</a-form-item>
</div>
</a-form>
</template>
<script setup lang="ts">
/**
* @description 系统管理-组织-模板管理-用例模板左侧顶部系统字段内容
*/
import { ref } from 'vue';
import { useI18n } from '@/hooks/useI18n';
const { t } = useI18n();
const systemForm = ref<Record<string, any>>({
module: '',
});
</script>
<style scoped></style>

View File

@ -0,0 +1,43 @@
<template>
<a-form ref="viewFormRef" layout="vertical" :model="systemForm">
<div class="systemFieldWrapper">
<a-form-item
field="defectHandleUser"
:required="true"
:label="t('system.orgTemplate.defectHandleUser')"
asterisk-position="end"
>
<a-select :disabled="true" :placeholder="t('system.orgTemplate.noDefaultPlaceholder')"></a-select>
</a-form-item>
</div>
<div class="systemFieldWrapper">
<a-form-item
field="defectStatus"
:required="true"
:label="t('system.orgTemplate.defectStatus')"
asterisk-position="end"
>
<a-select :disabled="true" :placeholder="t('system.orgTemplate.noDefaultPlaceholder')"></a-select>
</a-form-item>
</div>
</a-form>
</template>
<script setup lang="ts">
/**
* @description 系统管理-组织/项目-模板管理-缺陷模板右侧顶部系统字段内容
*/
import { ref } from 'vue';
import { useI18n } from '@/hooks/useI18n';
const { t } = useI18n();
const systemForm = ref<Record<string, any>>({
handleUser: '',
status: '',
});
</script>
<style scoped></style>

View File

@ -163,7 +163,7 @@
FieldOptions,
} from '@/models/setting/template';
import { dateOptions, fieldIconAndName, getFieldRequestApi, getFieldType, numberTypeOptions } from './fieldSetting';
import { dateOptions, fieldIconAndName, getFieldRequestApi, numberTypeOptions } from './fieldSetting';
const { t } = useI18n();
const route = useRoute();
@ -273,6 +273,7 @@
const confirmHandler = async (isContinue = false) => {
try {
drawerLoading.value = true;
console.log(fieldForm.value);
const formCopy = cloneDeep(fieldForm.value);
formCopy.scene = route.query.type;
@ -334,18 +335,21 @@
drawerLoading.value = true;
fieldDefaultValues.value = [...list];
if (showOptionsSelect.value) {
let startPos = 5000;
fieldForm.value.options = (batchFormRef.value?.getFormResult() || []).map((item: any) => {
const currentItem: FieldOptions = {
text: item.text,
value: item.value ? item.value : getGenerateId(),
pos: startPos,
};
if (item.fieldId) {
currentItem.fieldId = item.fieldId;
}
startPos += 5000;
return currentItem;
});
}
console.log(fieldForm.value.options);
await cb();
} catch (error) {
// eslint-disable-next-line no-console

View File

@ -52,11 +52,7 @@
/>
<a-tooltip :content="record.name">
<div
class="ellipsis max-w-[200px]"
:class="{
'text-[rgb(var(--primary-5))]': props.mode === 'project',
'cursor-pointer': props.mode === 'project',
}"
class="ellipsis max-w-[200px] cursor-pointer text-[rgb(var(--primary-5))]"
@click="showDetail(record)"
>{{ record.name }}</div
>
@ -379,8 +375,6 @@
//
const showDetail = (record: AddOrUpdateField) => {
if (props.mode === 'organization') return;
showDetailVisible.value = true;
let fieldType;
if (record.type === 'MEMBER') {

View File

@ -5,6 +5,11 @@
<CaseTemplateLeftContent v-else />
</div>
<div class="preview-right px-4">
<!-- 系统内置的字段 {处理人, 状态...} -->
<DefectTemplateRightSystemField v-if="props.templateType === 'BUG'" />
<CaseTemplateRightSystemField v-else />
<!-- 自定义字段开始 -->
<MsFormCreate
v-if="formRules.length"
ref="formCreateRef"
@ -12,7 +17,16 @@
v-model:form-item="formItem"
:form-rule="formRules"
/>
<a-empty v-else />
<!-- 标签字段开始 -->
<div class="tagWrapper">
<a-form ref="viewFormRef" layout="vertical" :model="viewForm">
<a-form-item field="tags" :label="t('system.orgTemplate.tags')" asterisk-position="end">
<a-input :disabled="true" :placeholder="t('system.orgTemplate.noDefaultPlaceholder')" />
</a-form-item>
</a-form>
</div>
<!-- 标签字段结束 -->
</div>
</div>
</template>
@ -27,9 +41,15 @@
import type { FormItem, FormRuleItem } from '@/components/pure/ms-form-create/types';
import CaseTemplateLeftContent from './caseTemplateLeftContent.vue';
import DefectTemplateLeftContent from './defectTemplateLeftContent.vue';
import CaseTemplateRightSystemField from '@/views/setting/organization/template/components/caseTemplateRightSystemField.vue';
import DefectTemplateRightSystemField from '@/views/setting/organization/template/components/defectTemplateRightSystemField.vue';
import { useI18n } from '@/hooks/useI18n';
import type { DefinedFieldItem, SeneType } from '@/models/setting/template';
const { t } = useI18n();
const props = defineProps<{
templateType: SeneType; //
selectField: DefinedFieldItem[]; //
@ -42,6 +62,10 @@
const fApi = ref(null);
const formCreateRef = ref();
const viewForm = ref<Record<string, any>>({
tags: '',
});
//
const getFormRules = () => {
formRuleField.value = [];

View File

@ -90,6 +90,7 @@ export default {
'system.orgTemplate.caseName': 'Use case name',
'system.orgTemplate.caseNamePlaceholder': 'Please enter a use case name',
'system.orgTemplate.defectNamePlaceholder': 'Please enter a use defect name',
'system.orgTemplate.noDefaultPlaceholder': 'Default value are not supported',
'system.orgTemplate.precondition': 'precondition',
'system.orgTemplate.stepDescription': 'Step description',
'system.orgTemplate.changeType': 'Change type',
@ -164,6 +165,9 @@ export default {
'system.orgTemplate.custom': 'custom',
'system.orgTemplate.defectName': 'Defect Name',
'system.orgTemplate.defectContent': 'Defect Content',
'system.orgTemplate.defectHandleUser': 'AssignUser',
'system.orgTemplate.defectStatus': 'Status',
'system.orgTemplate.caseModule': 'Module',
'system.orgTemplate.defectNameTip':
'You can set a default value for the defect name, which is used uniformly when creating it',
'system.orgTemplate.defectContentTip':

View File

@ -90,6 +90,7 @@ export default {
'system.orgTemplate.caseName': '用例名称',
'system.orgTemplate.caseNamePlaceholder': '请输入用例名称',
'system.orgTemplate.defectNamePlaceholder': '请输入缺陷名称',
'system.orgTemplate.noDefaultPlaceholder': '暂不支持默认值',
'system.orgTemplate.precondition': '前置条件',
'system.orgTemplate.stepDescription': '步骤描述',
'system.orgTemplate.changeType': '更改类型',
@ -156,6 +157,9 @@ export default {
'system.orgTemplate.custom': '自定义',
'system.orgTemplate.defectName': '缺陷名称',
'system.orgTemplate.defectContent': '缺陷内容',
'system.orgTemplate.defectHandleUser': '处理人',
'system.orgTemplate.caseModule': '所属模块',
'system.orgTemplate.defectStatus': '状态',
'system.orgTemplate.defectNameTip': '可为缺陷名称设置默认值,创建时统一使用',
'system.orgTemplate.defectContentTip': '可为缺陷内容设置默认值,创建时统一使用',
'system.orgTemplate.templateNameRules': '请输入模板名称',