• 【前端】活动表单


    先看效果

    要求:

    1、能够动态添加多个参数 因为不是每个设备都有对应的参数

    2、同一个设备参数名不能重复 这个在后端很好整 前端需要限制用户的输入操作

    3、保存之后能够随时修改

    环境:

    基于若依4.6.1开发的项目

    前言:

    因为没有现成的东西,值能把旧的代码翻出来找 找到了个类似的

    在还不知道若伊这类管理平台的时候 生成表格、表单的代码都是自己写的

    尤其是刚毕业后工作的两个单位 对前端框架仅仅是bootstrap这种单纯的前端框架

    其实造轮子挺好玩的 就是速度慢一些

    数据库部分:

    按照一对多的模型创建的参数表

    | uuid | 设备型号id | 参数键名 | 参数值 |

    参数键存在数据字典中方便管理

    参数键名为0x01 0x02这样的编号

    参数键标签名为执行标准、公称直径这类业务数据 存在数据字典中

    参数值为业务数据 GB/T 12345这类

    后端略过:数据的增查删改会的都会,不会的学去,最基本的玩意

    前端:

    最麻烦的就是前端 要考虑用户的使用 要考虑到各种zz操作导致的各种bug

    我也不会讲 代码写的不咋样 就是按ruoyi里面有个$.Table这个玩意 仿照着感觉写的

    先是一个小工具 方便创建HTML元素

    /**
     * 创建HTML元素
     * @param tagName html标签名
     * @param parent 父对象
     * @param callback 回调函数 参数为该元素
     * @returns {Element} 返回新建的HTML元素
     */
    function createElement(tagName, parent, callback) {
        let element = document.createElement(tagName);
        if (parent) {
            $(parent).append(element);
        }
        if (callback) {
            callback(element);
        }
        return element;
    }

    能用代码整得就不要拼接字符串 除非我不会

    这两大坨代码仍在工具类的js文件中

    那个$.extend只是为了能够通过$.activeTable调用 没其他意思

    /**
     * 用于管理活动表格的配置信息
     * @type {{config: {}, options: {}, set: activeTable.set, get: activeTable.get}}
     */
    var activeTable = {
        // 配置信息的集合
        config: {},
        // 活动的配置信息
        options: {},
        // 选择活动的配置信息
        set: function (id) {
            if ($.common.getLength(activeTable.config) > 1) {
                let tableId = $.common.isEmpty(id) ?
                    $(event.currentTarget).parents(".active-table").find("table.table").attr("id") : id;
                if ($.common.isNotEmpty(tableId)) {
                    activeTable.options = activeTable.get(tableId);
                }
            }
        },
        // 获取配置信息
        get: function (id) {
            if ($.common.isNotEmpty(id)) {
                return activeTable.config[id];
            } else {
                return activeTable.options;
            }
        }
    };
    
    $.extend({
        /**
         * 活动表格
         */
        activeTable: {
            /**
             * 初始化活动表格 调用后会调用loadData方法填充数据
             * 回调函数接受三个参数{cell:单元格,
             * @param _options 配置信息 {id:唯一识别符,titleList:标题列,fieldList:字段列,dataList:数据列,callback:每个单元格的回调函数}
             *
             * option可以自行添加额外配置 以上内容为最小配置
             * 配置id目的是在同一页面使用多个active-table
             * 我也没试过 谁有兴趣试试看中不中
             */
            init: function (_options) {
                let defaults = {
                    id: "active-table",  // 标识符
                    titleList: [],       // 标题列
                    fieldList: [],       // 字段列
                    dataList: [],        // 数据列
                    callback: null       // 处理每个单元格的回调函数
                };
    
                let options = $.extend(defaults, _options); // 合并配置项 覆盖默认配置
                activeTable.options = options;
                activeTable.config[options.id] = options;
            },
            /**
             * 活动表格填充数据
             * @param data 要填充的数据 为空则使用options中的数据
             */
            load: function (data) {
                let table = $('#' + activeTable.options['id']);
                let options = activeTable.options;
                if (data) {
                    options.dataList = data;
                }
                let titleList = options.titleList;
                let dataList = options.dataList;
                let callback = options.callback;
                // 清空表格内容
                $(table).empty();
                // 创建表格结构
                let thead = createElement('thead', table);
                let tr_head = createElement('tr', thead);
                for (let title of titleList) {
                    createElement('th', tr_head, function (e) {
                        $(e).text(title);
                        $(e).css('text-align', 'center');
                    });
                }
                createElement('tbody', table);
                if (dataList) {
                    // 追加数据
                    $.activeTable.appendData(dataList, callback);
                }
                // 追加空行 做添加行的按钮
                $.activeTable.appendRow(null, callback);
            },
            /**
             * 创建一行
             * @param data 要插入的数据(该行的)
             * @param callback(options, cell, data, field) 回调函数处理每个单元格的数据
             * @returns {Element} 返回创建的行
             */
            createRow: function (data, callback) {
                let fieldList = activeTable.options.fieldList;
                let row = createElement('tr', null);
                for (let field of fieldList) {
                    let cell = createElement('td', row);
                    if (data) {
                        $(cell).text(data[field]);
                    }
                    if (callback) {
                        callback(activeTable.options, cell, data, field);
                    }
                }
                return row;
            },
            /**
             * 追加行
             * @param data 该行的数据
             * @param callback(cell,data,field) 回调函数处理每个单元格的数据
             */
            appendRow: function (data, callback) {
                let tbody = $('#' + activeTable.options['id'] + ' tbody');
                let row = $.activeTable.createRow(data, callback);
                $(tbody).append(row);
            },
            /**
             * 插入行 在最后一行前插入一行数据
             * @param data 该行的数据
             * @param callback(cell,data,field) 回调函数处理每个单元格的数据
             */
            insertRow: function (data, callback) {
                let tbody = $('#' + activeTable.options['id'] + ' tbody');
                let row = $.activeTable.createRow(data, callback);
                $(tbody).find('tr:last').before(row);
            },
            /**
             * 追加数据
             * @param data 数据列
             * @param callback(cell,data,field) 回调函数处理每个单元格的数据
             */
            appendData: function (data, callback) {
                for (let item of data) {
                    $.activeTable.appendRow(item, callback);
                }
            },
            /**
             * 插入数据 在最后一行前插入一行数据
             * @param data 该行的数据
             * @param callback(cell,data,field) 回调函数处理每个单元格的数据
             */
            insertData: function (data, callback) {
                for (let item of data) {
                    $.activeTable.insertRow(item, callback);
                }
            }
        }
    });

    这只是个基本的框架 之前的动态表格就是这么整得 只不过那个只是数据展示 现在要做成表单填写提交数据

    按照业务需要 在业务的公共js文件中编写如下配置 配置比框架代码都多 看着好头大

    主要是处理联动部分写的太多了

    var titleList = ['参数名', '参数值', '操作'];
    var fieldList = ['paramKey', 'paramValue', 'options'];
    
    // 自定义的配置选项 业务是配置设备参数 参数不一定都有 但是唯一 只能出现一次
    // dictKeys 表示可能使用的参数键
    // usedKeys 表示已经使用的参数键
    // addParamKey 是添加行的时候在paramKey的时候调用的方法
    // addParamValue 是添加行的时候在paramValue的时候调用的方法
    var defaultOptions = {
        id: 'device_params',
        titleList: titleList,
        fieldList: fieldList,
        dictKeys: [],
        usedKeys: [],
        addParamKey: function (options, data) {
            // js的this好麻烦 不注意就成window了
            let dictKeys = this.dictKeys;
            let usedKeys = this.usedKeys;
            // 按照业务需求创建下拉框
            let paramKey = document.createElement('select');
            $(paramKey).attr('name', 'paramKeys');
            // 加上ruoyi提供的样式
            $(paramKey).addClass('form-control');
            // ruoyi框架提供了dict转下拉框的option的方法 直接调用了 自己实现也不难 遍历一遍键值创建一堆option填充数据就行
            paramKey.innerHTML = $.common.dictToSelect(dictKeys);
            // 先隐藏已经使用过的键
            for (let usedKey of usedKeys) {
                $(paramKey).find('option[value="' + usedKey + '"]').attr('hidden', 'true');
            }
            // 如果这行数据不空 将这行数据的key置成选中状态 并去掉隐藏属性
            // 建议直接移除hidden属性 而不是置为false
            if (data) {
                $(paramKey).find('option[value="' + data['paramKey'] + '"]').attr('selected', 'true');
                $(paramKey).find('option[value="' + data['paramKey'] + '"]').removeAttr('hidden');
            } else {
                // 如果这是个空行 将第一个未使用的key置为选中状态
                let optionList = $(paramKey).find('option');
                for (let usedKey of usedKeys) {
                    optionList = $(optionList).not('[value="' + usedKey + '"]');
                }
                $(optionList).first().attr('selected', 'true');
                let initKey = $(paramKey).val();
                // 将该表格内其他的该键设置为隐藏 因为这个时候该select还没有添加进表格 所以该行不受影响
                $('#' + options.id).find('option[value=' + initKey + ']').each(function (idx, e) {
                    $(e).attr('hidden', 'true');
                });
            }
            // 将该键放到使用的键集合中
            let selectKey = $(paramKey).val();
            if (usedKeys.indexOf(selectKey) == -1) {
                usedKeys.push(selectKey);
            }
            // 如果所有的键都使用完成 则隐藏添加按钮
            if (dictKeys.length == usedKeys.length) {
                $('#' + options.id).find('tr:last').attr('hidden', 'true');
            }
            // 以下为当某个下拉框发生变动 则联动其他下拉框隐藏和显示键
            let oldKey = null;
            // 当下拉框聚焦的时候获取值作为旧值
            $(paramKey).focus(function (e) {
                oldKey = $(e.target).val();
            }).change(function (e) {
                // 当发生改变的时候 获取新的值
                let newKey = $(e.target).val();
                let oldIdx = usedKeys.indexOf(oldKey);
                usedKeys.splice(oldIdx, 1);
                usedKeys.push(newKey);
                // 显示旧的值
                $('#' + options.id).find('option[value=' + oldKey + ']').each(function (idx, e) {
                    $(e).removeAttr('hidden');
                });
                // 隐藏新的值
                $('#' + options.id).find('option[value=' + newKey + ']').each(function (idx, e) {
                    $(e).attr('hidden', 'true');
                });
                // 旧的值取消选中状态
                $(paramKey).find('option[value=' + oldKey + ']').removeAttr('selected');
                // 该下拉框新的值取消隐藏状态
                $(paramKey).find('option[value=' + newKey + ']').removeAttr('hidden');
                // 该下拉框新的值设置选中状态
                $(paramKey).find('option[value=' + newKey + ']').attr('selected', 'true');
                oldKey = newKey;
            });
    
            return paramKey;
        },
        addParamValue: function (data) {
            // 参数值就是个input框 没啥说的
            let paramValue = document.createElement('input');
            $(paramValue).attr('name', 'paramValues');
            // 加上ruoyi提供的样式
            $(paramValue).addClass('form-control');
            if (data) {
                $(paramValue).val(data['paramValue']);
            }
            $(paramValue).attr('name', 'paramValues');
            return paramValue;
        },
        callback: function (options, element, data, field) {
            // 根据字段分别设置行为
            if (field == 'paramKey') {
                $(element).empty();
                if (data) {
                    let paramKey = options.addParamKey(options, data);
                    element.append(paramKey);
                }
            }
            if (field == 'paramValue') {
                $(element).empty();
                if (data) {
                    let paramValue = options.addParamValue(data);
                    element.append(paramValue);
                }
            }
            if (field == 'options') {
                // 如果有数据 则添加删除按钮
                if (data) {
                    $(element).empty();
                    $(element).css('text-align', 'center');
                    let btn = createElement('a', element, function (e) {
                        $(e).addClass('btn');
                        $(e).addClass('btn-warning');
                        $(e).text('删除');
                    });
                    $(btn).click(function () {
                        let tr = $(element).parent();
                        tr.remove();
                        // 删除后需要联动其他下拉框 更新可用的键
                        let key = $(element).parent().find('select').val();
                        let index = options.usedKeys.indexOf(key);
                        options.usedKeys.splice(index, 1);
                        $('#' + options.id).find('option[value=' + key + ']').each(function (idx, e) {
                            $(e).removeAttr('hidden');
                        });
                        // 如果有可用的键 则显示添加按钮
                        if (options.dictKeys.length > options.usedKeys.length) {
                            $('#' + options.id).find('tr:last').attr('hidden', true);
                        }
                    });
                } else {
                    // 如果没有数据 则增加个添加参数的按钮 这一段只在初始化表格的时候使用
                    let blankLine = $(element).parent();
                    $(blankLine).empty();
                    $(blankLine).append(element);
                    $(element).attr('colspan', '3');
    
                    let btn = createElement('a', element, function (e) {
                        $(e).addClass('btn');
                        $(e).addClass('btn-success');
                        $(e).css('width', '100%');
                        $(e).text('添加');
                    });
                    // 该按钮的行为和上面的回调函数应该相同
                    $(btn).click(function () {
                        // 以下逻辑与上面大致相同 但要注意点击按钮新增的行数据肯定为空 而且只有删除功能
                        $.activeTable.insertRow(null, function (options, cellElement, data, field) {
                            let rowData = data == null ? null : data[field];
                            if (field == 'paramKey') {
                                $(cellElement).empty();
                                let paramKey = options.addParamKey(options, rowData);
                                cellElement.append(paramKey);
                            }
                            if (field == 'paramValue') {
                                $(cellElement).empty();
                                let paramValue = options.addParamValue(rowData);
                                cellElement.append(paramValue);
                            }
                            if (field == 'options') {
                                $(cellElement).empty();
                                $(cellElement).css('text-align', 'center');
                                let btn = createElement('a', cellElement, function (e) {
                                    $(e).addClass('btn');
                                    $(e).addClass('btn-warning');
                                    $(e).text('删除');
                                });
                                $(btn).click(function () {
                                    let tr = $(cellElement).parent();
                                    tr.remove();
                                    let key = $(cellElement).parent().find('select').val();
                                    let index = options.usedKeys.indexOf(key);
                                    options.usedKeys.splice(index, 1);
                                    $('#' + options.id).find('option[value=' + key + ']').each(function (idx, e) {
                                        $(e).removeAttr('hidden');
                                    });
                                    if (options.dictKeys.length > options.usedKeys.length) {
                                        $('#' + options.id).find('tr:last').removeAttr('hidden');
                                    }
                                });
                            }
                        });
                    });
                }
            }
        }
    };

    里面一些class样式是直接用的bootstrap的 大概吧。。。 我对前端不是很熟 不对 是很不熟

    代码也许很烂 但是功能实现了

    用的时候html部分扔一个<table id='active-table'></table>

    id的名称随便整 但是要和options中的一样 因为需要找到这个表格 然后

    let options = objClone(defaultOptions);
    options.id = 'device_params'; // 这个就是表格的id
    options.dataList = deviceParams; // 从后台获取
    options.dictKeys = paramKeys; // 从后台获取
    
    for(let item of deviceParams) {
        options.usedKeys.push(item['paramKey']);
    }
    $.activeTable.init(options);
    $.activeTable.load();

    objClone是个深拷贝对象的方法 去百度能找到不少 反正就是最好不要修改业务默认配置 免得引起什么问题

    到这就结束了

    来自1942年冬季攻势中的中央集团军的037号17吨救援拖车
  • 相关阅读:
    消息队列技术
    NET Core中使用Apworks
    TCP基础
    Oracle停止一个JOB
    如何在Java 8中愉快地处理日期和时间
    mysql字符串区分大小写的问题
    【已解决】javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint
    spring boot 1.4默认使用 hibernate validator
    mysql shell
    android:background="@drawable/home_tab_bg"
  • 原文地址:https://www.cnblogs.com/panther1942/p/15172237.html
Copyright © 2020-2023  润新知