目录
前言
在某些场景下用户需要自己配置字段并添加数据的时候,我们可以做一个根据不同模块实现字段自定义配置的功能以供不同系统的需求。
效果图
图一
图二
图三
一、后端java层
1.枚举类
因为模块的类型是自己定义的,所以需要加一个类型枚举类,我这里是oa系统的模块,你们可以根据自己系统进行定义。
代码如下:
/**
* <p>
* 自定义字段类型枚举类
* </p>
*
* @author 刘易彦
* @custom.date 2024/9/20 14:46
*/
public class CustomTypeConstants {
/**
* <p>
* 私有化构造方法
* </p>
*
* @author 刘易彦
* @custom.date 2024/6/2 14:46
*/
private CustomTypeConstants() {
}
/**
* 角色管理
*/
public static final String ROLE = "role";
/**
* 岗位信息
*/
public static final String POST = "post";
/**
* 用人需求
*/
public static final String EMPLOYMENT = "employment";
/**
* 招聘面试
*/
public static final String INTERVIEW = "interview";
/**
* 人才资源池
*/
public static final String HUMAN_RESOURCES = "human_resources";
/**
* 本省同行公司
*/
public static final String SAME_PEER = "same_peer";
/**
* 外省同行公司
*/
public static final String NOT_SAME_PEER = "not_same_peer";
/**
* 汽车行业公司
*/
public static final String CAR_PEER = "car_peer";
/**
* 入职申请
*/
public static final String ENTRY = "entry";
/**
* 转正申请
*/
public static final String REGULAR = "regular";
/**
* 员工档案
*/
public static final String ARCHIVES = "archives";
/**
* 合同管理
*/
public static final String CONTRACT = "contract";
/**
* 培训需求
*/
public static final String TRAIN_NEEDS = "trainNeeds";
/**
* 社保
*/
public static final String SOCIAL_SECURITY = "socialSecurity";
/**
* 培训执行
*/
public static final String TRAIN_SCHEDULE = "trainSchedule";
/**
* 其他培训考试
*/
public static final String TRAIN_OTHER_TEST = "trainOtherTest";
2.服务实现类
实现分为两层进行展示,第一层为自定义字段的管理,第二层为模块内对自定义字段管理value值。
1.自定义字段管理
对应效果图中的图一。
代码如下:
@Override
public LayUiAdminResultVo addCustomField(CustomFields customFields) {
customFields.setPossibleValues(customFields.getPossibleValues().replaceAll(",",","));
int insert = this.customFieldsMapper.insert(customFields);
if (insert == 1) {
return LayUiAdminResultVo.ok(WebResponseConstants.SUCCESS);
}
return LayUiAdminResultVo.ok(WebResponseConstants.FAIL);
}
@Override
public LayUiAdminResultVo editCustomField(CustomFields customFields) {
customFields.setPossibleValues(customFields.getPossibleValues().replaceAll(",",","));
int update = this.customFieldsMapper.updateById(customFields);
if (update == 1) {
return LayUiAdminResultVo.ok(WebResponseConstants.SUCCESS);
}
return LayUiAdminResultVo.ok(WebResponseConstants.FAIL);
}
@Override
public LayUiAdminResultVo delCustomField(String id,String type) {
LambdaQueryWrapper<CustomValues> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(CustomValues::getCustomFieldId, id);
lambdaQueryWrapper.eq(CustomValues::getCustomizedType, type);
int delete = this.customValuesMapper.delete(lambdaQueryWrapper);
int delete1 = customFieldsMapper.deleteById(id);
if (delete1 == 1) {
return LayUiAdminResultVo.ok(WebResponseConstants.SUCCESS);
}
return LayUiAdminResultVo.ok(WebResponseConstants.FAIL);
}
@Override
public IPage<CustomFields> getCustomFieldList(int current, int size, String keyWords) {
LambdaQueryWrapper<CustomFields> lambdaQueryWrapper = new LambdaQueryWrapper<>();
IPage<CustomFields> ipage = new Page<>(current, size);
if (StringUtils.isNotBlank(keyWords)) {
lambdaQueryWrapper.like(CustomFields::getName, keyWords);
}
lambdaQueryWrapper.orderByAsc(CustomFields::getType);
return this.customFieldsMapper.selectPage(ipage, lambdaQueryWrapper);
}
2.自定义字段value值管理
对应效果图中的图二。
代码如下:
@Override
public LayUiAdminResultVo addCustomValue(JSONObject jsonObject) {
JSONObject jsonObject1 = jsonObject.getJSONObject("value");
for (String key : jsonObject1.keySet()) {
String value = jsonObject1.get(key).toString();
LambdaQueryWrapper<CustomValues> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(CustomValues::getCustomizedType, jsonObject.getString("customizedType"));
lambdaQueryWrapper.eq(CustomValues::getCustomizedId,jsonObject.getLong("customizedId"));
lambdaQueryWrapper.eq(CustomValues::getCustomFieldId,key);
CustomValues customValues = customValuesMapper.selectOne(lambdaQueryWrapper);
if(customValues!=null){
customValues.setValue(value);
int update = customValuesMapper.updateById(customValues);
}else{
CustomValues customValues2 = new CustomValues();
customValues2.setCustomFieldId(Long.parseLong(key));
customValues2.setValue(value);
customValues2.setCustomizedType(jsonObject.getString("customizedType"));
customValues2.setCustomizedId(jsonObject.getLong("customizedId"));
int insert = customValuesMapper.insert(customValues2);
}
}
return LayUiAdminResultVo.ok(WebResponseConstants.SUCCESS);
}
@Override
public LayUiAdminResultVo delCustomValue(String id,String type) {
LambdaQueryWrapper<CustomValues> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(CustomValues::getCustomizedId, id);
lambdaQueryWrapper.eq(CustomValues::getCustomizedType, type);
int delete = this.customValuesMapper.delete(lambdaQueryWrapper);
if (delete > 0) {
return LayUiAdminResultVo.ok(WebResponseConstants.SUCCESS);
}
return LayUiAdminResultVo.ok(WebResponseConstants.FAIL);
}
二、数据访问层
1.表结构
自定义字段表:
自定义字段value表
2.sql语句
根据模块类型和业务id联表查询自定义字段和对应的value值。
代码如下:
<select id="getCustomFieldValue" resultType="java.util.Map">
select * from custom_fields a left join
(select * from custom_values b where b.customized_id = #{id} and b.customized_type = #{type}) c
on a.id = c.custom_field_id where a.type = #{type} order by a.id asc
</select>
三、前端展示层
1.配置自定义字段
这里以添加为例,编辑雷同就不展示了(我这里的js代码都是以layui为例的,不同框架写法可能有所不同,请注意)。
html页面代码如下:
<div class="layui-form" lay-filter="layuiadmin-form-useradmin" id="layuiadmin-form-useradmin"
style="padding: 20px 0 0 0;">
<div class="title"><span>配置信息</span>
<hr/>
</div>
<div class="layui-form-item">
<label class="layui-form-label" style="width: 100px;"><span
style="color: red;font-weight: bold">*</span>格式</label>
<div class="layui-input-inline">
<select name="fieldFormat" lay-verify="required" lay-filter="fieldFormat">
<option value="">请选择格式</option>
<option value="list">列表</option>
<option value="string">字符串</option>
<option value="int">数字</option>
<option value="text">文本</option>
<option value="date">日期</option>
<option value="phone">手机号</option>
<option value="email">邮箱</option>
<option value="url">网址</option>
<option value="identity">身份证</option>
</select>
</div>
<label class="layui-form-label" style="width: 100px;"><span
style="color: red;font-weight: bold">*</span>模块</label>
<div class="layui-input-inline">
<select name="type" lay-verify="required" lay-filter="type">
<option value="">请选择模块</option>
<option value="role">模块自己定义</option>
</select>
<input type="text" id="typeName" name="typeName" placeholder="请输入字段名称" autocomplete="off" class="layui-input" lay-verify="required"
style="display: none;">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" style="width: 100px;"><span
style="color: red;font-weight: bold">*</span>字段名称</label>
<div class="layui-input-inline">
<input type="text" name="name" placeholder="请输入字段名称" autocomplete="off" class="layui-input" lay-verify="required"
style="width: 520px;">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" style="width: 100px;">可能的值</label>
<div class="layui-input-inline">
<textarea type="text" id="possibleValues" name="possibleValues" placeholder="注:用逗号分隔" autocomplete="off" class="layui-input auto-height-textarea"
style="width: 520px;"></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" style="width: 100px;">默认值</label>
<div class="layui-input-inline" id="defaultDiv">
<input type="text" id="defaultValue" name="defaultValue" autocomplete="off" class="layui-input"
style="width: 300px;">
</div>
</div>
<div class="layui-form-item layui-hide">
<input type="button" lay-submit lay-filter="lay-submit" id="lay-submit" value="确认">
</div>
</div>
js代码如下:
// 监听select选择
form.on('select(fieldFormat)', function(data){
let value = data.value;
common.verifyDefaultValue('defaultDiv','defaultValue','possibleValues',value);
});
// 监听select选择
form.on('select(type)', function(data){
let selectedText = data.elem[data.elem.selectedIndex].text; // 获取选中的文本值
$("#typeName").val(selectedText);
});
verifyDefaultValue: function (defaultDiv, defaultValue,possibleValues,value) {
if(value==='list'){
$("#"+possibleValues).attr("disabled", false);
$("#"+possibleValues).attr("lay-verify", 'required');
}else {
$("#"+possibleValues).attr("disabled", true);
$("#"+possibleValues).attr("lay-verify", '');
}
if(value !== 'date'){
$("#"+defaultDiv).empty();
$("#"+defaultDiv).append('<input type="text" id="defaultValue" name="defaultValue" autocomplete="off" class="layui-input" style="width: 300px;">');
}
if(value==='int'){
$("#"+defaultValue).attr('lay-verify', 'number');
$("#"+defaultValue).attr('placeholder', '请输入默认值(数字)');
}else if(value==='date'){
$("#"+defaultValue).attr('lay-verify', '');
$("#"+defaultValue).attr('placeholder', '请输入默认值(日期)');
laydate.render({
elem: '#'+defaultValue
, type: 'date'
});
}else if(value==='phone'){
$("#"+defaultValue).attr('lay-verify', 'phone');
$("#"+defaultValue).attr('placeholder', '请输入默认值(手机号)');
}else if(value==='email'){
$("#"+defaultValue).attr('lay-verify', 'email');
$("#"+defaultValue).attr('placeholder', '请输入默认值(邮箱)');
}else if(value==='url'){
$("#"+defaultValue).attr('lay-verify', 'url');
$("#"+defaultValue).attr('placeholder', '请输入默认值(url)');
}else if(value==='identity'){
$("#"+defaultValue).attr('lay-verify', 'identity');
$("#"+defaultValue).attr('placeholder', '请输入默认值(身份证)');
}else{
$("#"+defaultValue).attr('lay-verify', '');
$("#"+defaultValue).attr('placeholder', '请输入默认值');
}
form.render();
},
注意事项:html模块类型的value值要和后端枚举类的值一致!!!
2.展示自定义字段(编辑则展示字段和value值)
进入模块管理页面前首先我们通过getCustomFieldValue这个方法获取到当前模块的自定义字段和value值(编辑页面需要获取到value值,新增页面不需要,所以新增页面不传业务id),这里以新增为例。
代码如下:
List<Map<String,Object>> customFieldValue = this.customFieldsService.getCustomFieldValue("",CustomTypeConstants.模块类型的值);
mv.addObject("customFieldValue", customFieldValue);
然后因为不同页面的宽高是不一样的,所以自定义字段的展示我这里根据不同的尺寸进行了通用的封装。
css代码如下:
.head{
padding: 10px;
width: 90%;
margin: auto;
border-radius: 10px;
box-shadow: 0 4px 6px #0c1f500a, 0 0 0 1px #e6eaf0;
background: #FFFFFF;
}
html代码如下:
<div th:if="${customFieldValue.size > 0}">
<div class="title"><span>自定义字段</span>
<hr/>
</div>
<div class="head" style="margin-bottom: 5px;">
<div id="custom">
</div>
</div>
</div>
js代码如下:
/**
* 展示自定义字段
* @param elemId 容器id
* @param customFieldValue 自定义字段列表
* @param showFlag 是否可编辑
*/
showCustomFieldValue: function (elemId, customFieldValue,showFlag) {
let pageWidth = window.innerWidth;
let pageLength;
let width;
if(obj.isEmpty(showFlag)){
showFlag = '';
}
if(pageWidth>1200){
pageLength = pageWidth*0.2;
width = 500;
}else if(pageWidth>900&&pageWidth<=1200){
pageLength = pageWidth*0.15;
width = 350;
}else{
pageLength = pageWidth*0.1;
width = 300;
}
for (let i = 0; i < customFieldValue.length; i++) {
let customValue;
if(obj.isNotEmpty(customFieldValue[i].value)){
customValue = customFieldValue[i].value;
}else{
customValue = customFieldValue[i].default_value;
}
if(customFieldValue[i].type==='archives'){
pageLength = 0;
width = 200;
}
let str = '';
if (customFieldValue[i].field_format === 'list') {
let options = '<option value="">请选择</option>';
let array = customFieldValue[i].possible_values.split(',');
for (let j = 0; j < array.length; j++) {
if (array[j] === customValue) {
options += '<option value="' + array[j] + '" selected>' + array[j] + '</option>'
} else {
options += '<option value="' + array[j] + '">' + array[j] + '</option>'
}
}
str = '<select '+showFlag+'>' +
options +
'</select>';
} else if (customFieldValue[i].field_format === 'int') {
str = '<input type="text" value="' + customValue + '" placeholder="请输入整数类型" autocomplete="off" class="layui-input" lay-verify="number" '+showFlag+'>';
} else if (customFieldValue[i].field_format === 'date') {
str = '<input id="date' + i + '" value="' + customValue + '" placeholder="请选择日期" type="text" autocomplete="off" class="layui-input" lay-verify="date" '+showFlag+'>';
} else if (customFieldValue[i].field_format === 'phone') {
str = '<input type="text" value="' + customValue + '" placeholder="请输入手机号码" autocomplete="off" class="layui-input" lay-verify="phone" '+showFlag+'>';
} else if (customFieldValue[i].field_format === 'email') {
str = '<input type="text" value="' + customValue + '" placeholder="请输入邮箱" autocomplete="off" class="layui-input" lay-verify="email" '+showFlag+'>';
} else if (customFieldValue[i].field_format === 'url') {
str = '<input type="text" value="' + customValue + '" placeholder="请输入网址" autocomplete="off" class="layui-input" lay-verify="url" '+showFlag+'>';
} else if (customFieldValue[i].field_format === 'identity') {
str = '<input type="text" value="' + customValue + '" placeholder="请输入身份证" autocomplete="off" class="layui-input" lay-verify="identity" '+showFlag+'>';
} else if (customFieldValue[i].field_format === 'text') {
str = '<textarea id="texts' + i + '" type="text" autocomplete="off" class="layui-input auto-height-textarea" style="border: 1px solid #d2d2d2;" '+showFlag+'>'+customValue+'</textarea>';
} else {
str ='<input type="text" value="' + customValue + '" placeholder="请输入字符串类型" autocomplete="off" class="layui-input" '+showFlag+'>';
}
let iHtml = ' <div class="layui-form-item msg-list">' +
'<label class="layui-form-label" style="width: 120px;margin-left: '+pageLength+'px"><span value="' + customFieldValue[i].id + '">' + customFieldValue[i].name + '</span></label>' +
' <div class="layui-input-inline" style="margin-left: 20px;width: '+width+'px;">' + str +
' </div>' +
' </div>';
$("#" + elemId).append(iHtml);
if (customFieldValue[i].field_format === 'date') {
laydate.render({
elem: '#date' + i //或 elem: document.getElementById('test')、elem: lay('#test') 等
, type: 'date'
});
}
}
form.render('select');
},
3.添加自定义字段value值
首先从html中获取到id为custom的div元素,然后对里面的字段和value进行解析,最后通过ajax传到后端进行保存。
代码如下:
/**
* 添加自定义字段的value
* @param elem 容器元素
* @param type 模块类型
* @param customizedId 业务数据id
* @returns {boolean}
*/
addCustomValue: function (elem, type, customizedId) {
let value = {};
let field = {};
elem.find('label').each(function () {
value[$(this).find('span').attr("value")] = $(this).siblings().children().val();
});
field.value = value;
field.customizedType = type;
field.customizedId = customizedId;
admin.req({
type: "post",
url: ctxPath + 'custom/addCustomValue',
data: JSON.stringify(field),
dataType: "json",
contentType: 'application/json;charset=utf-8',
success: function (result) {
let data = result.data;
if (data === obj.webConst.SUCCESS) {
//layer.msg('添加成功!', {icon: 6});
} else {
layer.msg('添加自定义字段value值失败!', {icon: 5, shift: 6});
}
}, error: function () {
layer.alert('请求失败');
}
});
},
总结
此功能实现了可在不同页面实现自定义字段,字段的格式可以根据需要自行配置,并提供了列表格式输入可能的值,会以下拉框的形式展示可能的值,也提供了默认值的配置,极大的方便了用户个性化的需求。