Java实现自定义字段(不修改表结构)

目录

前言

效果图

一、后端java层

1.枚举类

2.服务实现类

1.自定义字段管理

2.自定义字段value值管理

二、数据访问层

1.表结构

2.sql语句

三、前端展示层

1.配置自定义字段

2.展示自定义字段(编辑则展示字段和value值)

3.添加自定义字段value值

总结


前言

在某些场景下用户需要自己配置字段并添加数据的时候,我们可以做一个根据不同模块实现字段自定义配置的功能以供不同系统的需求。

效果图

图一

图二

图三

一、后端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('请求失败');
                }
            });
        },

总结

此功能实现了可在不同页面实现自定义字段,字段的格式可以根据需要自行配置,并提供了列表格式输入可能的值,会以下拉框的形式展示可能的值,也提供了默认值的配置,极大的方便了用户个性化的需求。

<think>嗯,用户想在VSCode里实现类似IntelliJ IDEA的自动补全功能,他们提到了vscode-intellij-idea-keybindings插件。首先,我需要回忆一下这个插件的功能。根据引用[4],这个插件主要是把IDEA的快捷键移植到VSCode,让用户可以用熟悉的快捷键操作。但自动补全可能只是快捷键的问题,还涉及到代码补全的逻辑。 接下来,用户可能遇到的问题是关于补全当前语句的功能,比如Ctrl+Shift+Enter在IDEA中可以补全分号或大括号,但在VSCode中可能无效。引用[3]提到这是一个已知的bug,因为VSCode对这类补全的支持够,导致插件无法正常工作。所以需要告诉用户如何调整设置或使用其他方法。 根据引用[5],在IDEA中补全分号或大括号是通过Ctrl+Shift+Enter,但在VSCode中可能需要其他方式。可能需要指导用户配置快捷键或者安装其他插件来增强自动补全功能,比如Emmet或者其他代码片段插件。 另外,引用[2]说明这个插件包含了很多功能,比如语法高亮、代码片段、Emmet、代码检查等,但自动补全可能需要结合VSCode自身的智能提示。因此,建议用户确保VSCode的自动补全设置已开启,比如"editor.quickSuggestions"和"editor.suggestOnTriggerCharacters"。 用户可能需要分步骤的教程:安装插件、配置快捷键、调整设置,以及可能的替代方案。同时,要提到已知的问题,比如补全当前语句的bug,并给出临时解决方法,比如手动输入或使用其他扩展。 还要检查是否有其他相关插件可以辅助自动补全,比如IntelliSense或Tabnine,但用户特别问的是IntelliJ IDEA的插件,所以重点还是在Keybindings插件的设置上。 最后,确保回答结构清晰,步骤明确,并引用相关的资料来支持建议,比如引用[3]和[5]提到的补全问题和设置方法。可能还需要提醒用户关注插件的更新,以解决未来的bug。</think>### 如何在VSCode中实现类似IDEA的自动补全功能 #### 步骤1:安装插件 1. **安装 `IntelliJ IDEA Keybindings` 插件** - 在VSCode的扩展商店中搜索 `IntelliJ IDEA Keybindings`,安装后重启VSCode。 - 该插件会将IDEA的快捷键映射到VSCode中,例如`Ctrl+Shift+Enter`补全语句的快捷键[^4]。 2. **补充安装代码补全插件(可选)** - 若需要更强大的自动补全功能,可安装 `Visual Studio IntelliCode` 或 `Tabnine`,这些插件支持AI驱动的智能代码补全。 --- #### 步骤2:配置快捷键和补全行为 1. **验证快捷键冲突** - 打开VSCode设置(`Ctrl+,`),搜索 `keyboard shortcuts`,检查 `Ctrl+Shift+Enter` 是否被其他功能占用。若冲突需手动调整。 2. **自定义补全逻辑(针对分号/大括号)** - 在VSCode设置中添加以下JSON配置,强制在行尾添加分号: ```json "editor.suggest.insertMode": "replace", "editor.autoClosingBrackets": "always", "editor.formatOnType": true ``` - 若需类似IDEA的语句补全,可安装 `Snippets` 插件并自定义代码模板[^2]。 --- #### 步骤3:解决已知问题 1. **`Ctrl+Shift+Enter` 补全失效问题** - 当前插件存在无法补全分号或大括号的bug,可通过以下临时方案解决: - **手动触发补全**:输入分号后按 `Tab` 键自动格式化。 - **使用代码片段**:自定义代码模板(如输入 `if` 自动生成带大括号的语句)[^3][^5]。 2. **调整VSCode的补全触发方式** - 在设置中启用 `editor.suggestOnTriggerCharacters` 和 `editor.quickSuggestions`,确保输入时自动弹出补全建议。 --- #### 步骤4:验证与优化 1. **测试补全功能** - 在代码中输入 `if` 或函数名,观察是否自动补全大括号或参数。 - 使用 `Ctrl+Space` 手动触发补全建议。 2. **调整响应速度** - 若补全延迟较高,可降低插件负载或禁用非必要的扩展。 --- ### 相关问题 1. **如何自定义VSCode的代码片段模板?** 2. **VSCode中有哪些插件可以替代IDEA的智能补全?** 3. **如何解决插件冲突导致的快捷键失效问题?** --- 通过上述步骤,可基本实现接近IDEA的自动补全体验。需注意VSCode与IDEA底层架构差异,部分功能需依赖扩展或手动配置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值