EKP前端二次开发公用方法集 - 明细表操作(PC)

/***********************************************
 该文件提供明细表相关函数
 jsp片段中引入publicMethod.js即可,无需引入该文件
 约束:
 1、明细表每个单元格最好只放一个控件且控件类型为xformflag(即控件最外层标签为xformflag),
 若存在多个控件可能导致数据获取和设置错误和某些未知错误(仅能操作到第一个xformflag控件)
 快速查看哪些是xformflag控件:https://app.yinxiang.com/fx/a8d35fd0-c11d-4713-b000-2fe0ec9b707b
 2、无法获取和设置只读控件的值(官方提供的函数GetXFormFieldValueById和SetXFormFieldValueById导致),
 故若需获取或设置完整数据,需在节点处开明细表编辑权限(之后会做优化)
 3、若要在表单一打开就操作明细表,则需将相关操作放置在window.onload里,否则无法获取明细表对象
 4、明细表的初始行数[必须]等于1【重要】
 5、则需将相关操作放置在window.onload里【重要】
 ***********************************************/

/**[移动端已适配部分函数]仅适配移动端展现模式:移动端
 * 获取明细表JS对象
 * @author liquid
 * @date 2022年5月10日
 * 对象内函数仅适配xform控件,即带有xformflag标签的控件
 * @param {string} detailListId 明细表flagid
 * @return {object}
 */
function getDetailObj(detailListId) {
    const TABLE_DL_ID = "TABLE_DL_" + detailListId;
    const table = window[TABLE_DL_ID];// 明细表<table></table>
    const table_selector = "#" + TABLE_DL_ID + " ";
    const tbody_selector = table_selector + " > tbody ";
    if (isEmpty(table)){// 明细表未加载完,或不存在该表
        console.error(detailListId + ":明细表未加载完或不存在该表;请检查表id");
        return null;
    }

    /**[已适配移动端]
     * 是否带有统计行
     * @return {boolean}
     */
    function hasStatisticRow() {
        const statisticRow = $(tbody_selector).find("[type='statisticRow']");
        return statisticRow.length > 0;
    }

    /**[已适配移动端]
     * 获取本明细表id
     * @return {string}
     */
    function getDetailListId() {
        return detailListId;
    }

    /**
     * 获取明细标题
     * @return {string}
     */
    function getDetailListLabel() {
        let label = "";
        const properties = Xform_ObjectInfo.properties;
        for (const property of properties) {
            if (property.name === detailListId) {
                label = property.label;
                break;
            }
        }
        return label;
    }

    /**
     * 判断明细表[只读][编辑:可增删行]
     * @return {string} "onlyRead":只读  "edit":可编辑
     */
    function getDetailListStatus() {
        const addBtn = getDetailBtnObj("bottom_add");
        return isEmpty(addBtn) ?  "onlyRead" : "edit"
    }

    /**[已适配移动端]
     * 获取每列id
     * @param {number} [rowIndex] (可选)行下标
     * 不传入则返回控件id(不含明细表id),传入则返回DetailListId.rowIndex.fieldId(含明细表id前缀+行下标)
     * @return {array}
     */
    function getFieldIds(rowIndex) {
        let isAddRow = getRowLength() === 0 && typeof rowIndex === "undefined";
        if (isAddRow) {// 一行控件都没有是无法获取控件id的,故需添加一行并隐藏之;最后再将该行删除
            DocList_AddRow(TABLE_DL_ID);
            $(tbody_selector + " > tr:nth-child(2)").hide();
        }
        let idArr = [];
        const fieldObjs = getRowFieldObjs(rowIndex);// undefined相当于不传入rowIndex
        for (const obj of fieldObjs) {
            let flagid = $(obj).attr("flagid");
            if (typeof window[detailListId + "fieldId"] === "undefined") window[detailListId + "fieldId"] = [];
            window[detailListId + "fieldId"].push(flagid);
            if (typeof rowIndex === "undefined") {
                const reg = new RegExp(detailListId + "\\.\\d{1,10}\\.");// 去掉明细表id.index.
                flagid = flagid.replace(reg, "");
            }
            idArr.push(flagid);
        }
        if (isAddRow) {
            setTimeout(function () {
                // 当存在图片上传控件时,在该控件未加载完就调用该函数删除的话会引起swf_attachment.js报错
                // Uncaught TypeError: a.connectRuntime is not a function
                // 解决办法:延迟500ms执行删除行,并在addRow之后将添加的行隐藏
                deleteAllRows();
            }, 500)
        }
        return idArr;
    }

    /**
     * 获取列控件标题
     * @return {array}
     */
    function getFieldLabels() {
        const labelArr = [];
        const tdArr = $(tbody_selector +" > tr.tr_normal_title > td");
        for(const td of tdArr) {
            const label = $(td).find(".xform_label");
            if (label.length > 0) {
                labelArr.push(label[0].innerText);
            }
        }
        return labelArr;
    }

    /**[已适配移动端]
     * 获取行数
     * 注意:
     * 1、在编辑模式下,若表的初始行数大于1,在页面刚加载完成时只能获取到1行
     * 因为除第一行外,其余行均是页面加载完后克隆出来的(可用setTimeout获取)
     * 2、在只读模式下页面刚加载时,返回的行数会比实际大1(页面刚加载时莫名其妙多出一行tr,随后被去除)
     * @returns {number}
     */
    function getRowLength() {
        const isEdit = getDetailBtnObj("bottom_add") != null;// 编辑状态
        if (isEdit) {
            const rowCount = $(tbody_selector +" > tr").length;
            return hasStatisticRow() ? rowCount - 3 : rowCount - 2;
        }
        else {
            return $(tbody_selector).find('[kmss_iscontentrow]').length;
        }
    }

    /**
     * 获取列数
     * @param {boolean} isAll=false 是否获取全部列数(包含非数据列)
     * @return {number}
     */
    function getColumnLength(isAll) {
        isAll = isAll || false;
        let length = 0;
        const td = $(tbody_selector + " > tr:nth-child(1) > td")// 明细表标题行
        if (isAll){
            length = td.length;
            return length;
        }
        td.each(function(){
            if ($(this).find(".xform_label").length > 0) length++;
        })
        return length;
    }

    /**[已适配移动端]
     * 获取【行】数据控件对象数组
     * @param {number} rowIndex=0 行下标
     * @return {array[object]}
     */
    function getRowFieldObjs(rowIndex) {
        rowIndex = rowIndex || 0;
        const objs = [];
        const tdArr = $(tbody_selector + " > tr:nth-child("+ (rowIndex + 2) +") > td");// 第rowIndex行数据行
        for(const td of tdArr) {
            const dataTd = $(td).find("xformflag[flagid]");
            if (dataTd.length > 0) {
                objs.push(dataTd[0]);
            }
        }
        return objs;
    }

    /**
     * 获取行对象(整行,包含操作)
     * @param {number|array} rowIndex 行下标或行下标范围[startIndex,endIndex]
     * @return {array|null|undefined}
     */
    function getRowObj(rowIndex) {
        let rows = $(tbody_selector + " > tr");// 全部行
        rows = rows.slice(1, getRowLength() + 1);// 只取数据行
        let rtn = null;
        const isArr = typeof rowIndex === "object" && rowIndex instanceof Array;
        if (isArr) {// 参数是数组类型
            rtn = rows.slice(rowIndex[0], rowIndex[1] + 1);
        }
        else if (typeof rowIndex === "number") {// 参数是数字类型
            rtn = rows.slice(rowIndex, rowIndex + 1);
        }
        return rtn;
    }

    /**
     * 获取【列】数据控件对象数组
     * @param {string} columnId 列控件id(不含明细表id)
     * @return {array[object]}
     */
    function getColumnFieldObjs(columnId) {
        const objs = [];
        for (let i = 0; i < getRowLength(); i++) {
            const obj = $("[flagid='"+ getFlagidInDetailList(i, columnId) +"']")[0];
            objs.push(obj);
        }
        return objs;
    }

    /**
     * 通过列控件id获取列控件下标
     * @param columnId 列控件id(不含明细表id)
     * @return {number}
     * @private
     */
    function _getColumnIndexByColumnId(columnId) {
        const idArr = getFieldIds();
        return idArr.indexOf(getFlagidInDetailList(0, columnId));
    }

    /**[已适配移动端]
     * 获取在明细表的flagid(含明细表id)
     * @param {number} rowIndex
     * @param {string} columnId 列控件id(不含明细表id)
     * @return {string}
     * @private
     */
    function getFlagidInDetailList(rowIndex, columnId) {
        return detailListId + "." + rowIndex +"." + columnId;
    }

    /**[已适配移动端]
     * 获取明细表内单个控件的值 - (依赖于publicMethod.getValueById)
     * @param {number} rowIndex 行下标
     * @param {string} columnId 列控件id(不含明细表id)
     * @return {string|array}
     */
    function getDataSingle(rowIndex, columnId) {
        const flagid = getFlagidInDetailList(rowIndex, columnId);
        return getValueById(flagid);
    }

    /**[已适配移动端]
     * 获取数据 - 全部
     * 该函数可能会导致后续维护困难,正常情况不建议使用该函数
     * 建议使用getData2Json/getDataByColumnId
     * @return {array[]}
     */
    function getData() {
        let dataArr = [];
        for (let i = 0; i < getRowLength(); i++) {
            dataArr.push(getDataByRowIndex(i));
        }
        return dataArr;
    }

    /**
     * 获取数据 - 全部(json格式)
     * @return {*[]}
     */
    function getData2Json() {
        let dataArr = [];
        for (let i = 0; i < getRowLength(); i++) {
            dataArr.push(getDataByRowIndex2Json(i));
        }
        return dataArr;
    }

    /**[已适配移动端]
     * 获取数据 - 某行
     * @param {number} rowIndex 行下标
     * @return {array[string|array]}
     */
    function getDataByRowIndex(rowIndex) {
        const fieldIds = getFieldIds(rowIndex);
        const dataArr = [];
        for (const id of fieldIds) {
            dataArr.push(getValueById(id));
        }
        return dataArr;
    }

    /**
     * 获取数据 - 某行
     * @param {number} rowIndex 行下标
     * @return {array[string|array]}
     */
    function getDataByRowIndex2Json(rowIndex) {
        let rtnJSON = {};
        const fieldIdArr = getFieldIds();
        let i = 0;
        let arr = getDataByRowIndex(rowIndex);
        for (let value of arr) {
            rtnJSON[fieldIdArr[i++]] = value;
        }
        return rtnJSON;
    }

    /**[已适配移动端]
     * 获取数据 - 某列
     * @param {string} columnId 列id(控件id)
     * @return {array[string|array]}
     */
    function getDataByColumnId(columnId) {
        const dataArr = [];
        for (let i = 0; i < getRowLength(); i++) {
            dataArr.push(getDataSingle(i, columnId));
        }
        return dataArr;
    }

    /**
     * 获取同行某控件的数据
     * @param {object} domElemt 控件对象(用于定位某行) 例如触发事件的domElemt 或 event.target
     * @param {string} columnId 目标控件id(获取该控件的值)
     * @return {string|array}
     */
    function getDataSameRow(domElemt, columnId) {
        const rowIndex = getRowIndexByDom(domElemt);
        return getDataSingle(rowIndex, columnId);
    }

    /**
     * 设置数据 - 全部
     * @param {array[]} dataArr  数据数组,数据结构看publicMethod.setValueById的注释;
     * getData返回的数据结构即setData的传入数据结构
     * @return {void}
     */
    function setData(dataArr) {
        // × 不使用删除策略,避免明细表重加载时的闪烁效果
        // deleteAllRows();// 首先删除全部行
        // for (let i = 0; i < dataArr.length; i++) {
        //     addRow(dataArr[i]);
        // }
        // √ 重新赋值已存在的行,若超过已存在的行数则添加行并设置数据
        let rowLength = getRowLength();// 当前行数
        for (let i = 0; i < dataArr.length; i++) {
            if (i < rowLength) {
                setDataByRowIndex(i, dataArr[i]);
                continue;
            }
            addRow(dataArr[i]);
        }
    }

    /**
     * 设置数据 - 某行
     * @param {number} rowIndex 行下标
     * @param {array} dataArr 数据数组,数据结构看publicMethod.setValueById()的注释
     * @return {void}
     */
    function setDataByRowIndex(rowIndex, dataArr) {
        const fieldIds = getFieldIds();
        for (let i = 0; i < dataArr.length; i++) {
            setDataSingle(rowIndex, fieldIds[i], dataArr[i]);
        }
    }

    /**
     * 设置数据 - 某列
     * @param {string} columnId
     * @param {array} dataArr 数据数组,数据结构看publicMethod.setValueById()的注释
     * @return {void}
     */
    function setDataByColumnId(columnId, dataArr) {
        for (let i = 0; i < getRowLength(); i++) {
            setDataSingle(i, columnId, dataArr[i]);
        }
    }

    /**
     * 把统一设置某列的全部值为dom所在行的值
     * @author liquid
     * @date 2023年2月25日
     * @param columnIds 多个用分号分隔
     * @param dom 元素对象
     * @return {void}
     */
    function setColumnDataByDom(columnIds, dom) {
        let targetRowIndex = getRowIndexByDom(dom);
        for (const columnId of columnIds.split(";")) {
            if (isEmpty(columnId)) {
                continue;
            }
            let targetValue = getDataSingle(targetRowIndex, columnId);
            for (let i = 0; i < getRowLength(); i++) {
                if (i === targetRowIndex) {
                    continue
                }
                setDataSingle(i, columnId, targetValue);
            }
        }
    }

    /**
     * 把统一设置某列dom所在行下一行的值为dom所在行的值
     * @author liquid
     * @date 2023年2月25日
     * @param columnIds 多个用分号分隔
     * @param dom 元素对象
     * @return {void}
     */
    function setColumnNextDataByDom(columnIds, dom) {
        let targetRowIndex = getRowIndexByDom(dom);
        if (targetRowIndex + 1 >= getRowLength()) {
            return;
        }
        for (const columnId of columnIds.split(";")) {
            if (isEmpty(columnId)) {
                continue;
            }
            let targetValue = getDataSingle(targetRowIndex, columnId);
            setDataSingle(targetRowIndex + 1, columnId, targetValue);
        }
    }

    /**
     * 添加行(可带数据)
     * @param {array} [dataArr] 可选,行数据,每个元素为各个控件的value,数据结构看publicMethod.setValueById()的注释
     * ,不传入dataArr或为null则添加空行
     * @return {number} 添加行的行索引
     */
    function addRow(dataArr) {
        DocList_AddRow(TABLE_DL_ID);
        let addedRowIndex = getRowLength() - 1;
        if (typeof dataArr !== "undefined" && dataArr !== null) {
            setDataByRowIndex(addedRowIndex, dataArr);// 设置最后一行的数据
        }
        return addedRowIndex;


        // if (typeof dataArr === "undefined" || dataArr === null) {
        //     DocList_AddRow(TABLE_DL_ID);
        //     return getRowLength();
        // }
        // DocList_AddRow(TABLE_DL_ID);
        // const fieldIds = getFieldIds(0);
        // let valueJson = {};
        // for (let i = 0; i < fieldIds.length; i++) {
        //     const fdIdInList = fieldIds[i];
        //     const flagType = getXFormFlagType(fdIdInList);
        //     const fdId = fdIdInList.substr(fdIdInList.lastIndexOf(".") + 1);
        //     const value = formatValue(dataArr[i], flagType);
        //     if (value === null || value === undefined || isEmpty(value)) {
        //         continue;
        //     }
        //     if ("xform_address;xform_new_address".includes(flagType)) {
        //         valueJson["extendDataFormInfo.value("+ detailListId +".!{index}."+ fdId +".id)"] = value[0];
        //         valueJson["extendDataFormInfo.value("+ detailListId +".!{index}."+ fdId +".name)"] = value[1];
        //     } else if ("xform_relation_select;xform_relation_checkbox;xform_relation_radio;xform_relation_choose".includes(flagType)) {
        //         valueJson["extendDataFormInfo.value("+ detailListId +".!{index}."+ fdId +")"] = value[0];
        //         valueJson["extendDataFormInfo.value("+ detailListId +".!{index}."+ fdId +"_text)"] = value[1];
        //     } else {
        //         valueJson["extendDataFormInfo.value("+ detailListId +".!{index}."+ fdId +")"] = value;
        //     }
        // }
        // let newOptTB = DocList_AddRow(TABLE_DL_ID, null, valueJson);
    }

    /**
     * 设置明细表内单个控件的值 - (依赖于publicMethod.setValueById)
     * @param {number} rowIndex 行下标
     * @param {string} columnId 列控件id(不含明细表id)
     * @param {string|array} value 控件值,数据结构看publicMethod.setValueById()的注释
     * @return {void}
     */
    function setDataSingle(rowIndex, columnId, value) {
        const flagid = getFlagidInDetailList(rowIndex, columnId);
        setValueById(flagid, value);
    }

    /**
     * 清空明细表内单个控件
     * @param {number} rowIndex 行下标
     * @param {string} columnId 列控件id(列控件id)
     * @return {void}
     */
    function clearSingle(rowIndex, columnId) {
        const flagid = getFlagidInDetailList(rowIndex, columnId);
        clearField(flagid);
    }

    /**
     * 清空某行
     * @param {number} rowIndex 行下标
     * @return {void}
     */
    function clearRow(rowIndex) {
        for (const columnId of getFieldIds()) {
            clearSingle(rowIndex, columnId);
        }
    }

    /**
     * 清空某列
     * @param {string} columnId 列控件id(列控件id)
     * @return {void}
     */
    function clearColumn(columnId) {
        for (let i = 0; i < getRowLength(); i++) {
            clearSingle(i, columnId);
        }
    }

    /**
     * 清空全部
     */
    function clearAll() {
        for (let i = 0; i < getRowLength(); i++) {
            clearRow(i);
        }
    }

    /**
     * 删除行
     * @param {number} rowIndex 行下标
     * @return {void}
     */
    function deleteRow(rowIndex) {
        DocList_DeleteRow(getRowObj(rowIndex)[0]);
    }

    /**
     * 删除全部行
     * @return {void}
     */
    function deleteAllRows() {
        let optTB = DocList_TableInfo[TABLE_DL_ID].DOMElement;
        // 全选
        $(optTB).find("input[name='DocList_Selected']").each(function() {
            this.checked = true;
        });
        let docList_Selected = $("input[name='DocList_Selected']:checked",optTB);
        // 全删除
        for(let i = docList_Selected.size() - 1;i >= 0 ;i--){
            let _optTR = docList_Selected.eq(i).closest('tr');
            if(_optTR && _optTR.size() > 0){
                DocList_DeleteRow_ClearLast(_optTR[0]);
            }
        }
    }

    /**
     * 隐藏某行
     * @param {number} rowIndex 行下标
     * @return {void}
     */
    function hideRow(rowIndex) {
        _showOrHideRow(getRowObj(rowIndex), true );
    }

    /**
     * 隐藏全部行
     * @return {void}
     */
    function hideAllRows() {
        const rows = getRowObj([0, getRowLength()]);
        _showOrHideRow(rows, true);
    }

    /**
     * 显示某行
     * @param rowIndex 行下标
     * @return {void}
     */
    function showRow(rowIndex) {
        _showOrHideRow(getRowObj(rowIndex), false);
    }

    /**
     * 显示全部行
     * @return {void}
     */
    function showAllRows() {
        const rows = getRowObj([0, getRowLength()]);
        _showOrHideRow(rows, false)
    }

    /**
     * 隐藏或显示某行(多行)
     * @param {array} rowObjArr 行对象数组
     * @param {boolean} isHide 隐藏否
     * @return {void}
     * @private
     */
    function _showOrHideRow(rowObjArr, isHide) {
        for (const row of rowObjArr) {
            if (isHide) $(row).hide();
            else $(row).show();
        }
        _resetFrontRowNum();
    }

    /**
     * 隐藏行时会导致序号错乱,需矫正
     * @return {void}
     * @private
     */
    function _resetFrontRowNum() {
        const isEdit = getDetailBtnObj("bottom_add") != null;// 编辑状态
        const orderDom = $(tbody_selector + " > tr.tr_normal_title > td[colType='noTitle']")[0];
        if (!orderDom || !("序号;Seq".includes(orderDom.innerText))) {// 不存在序号列,不执行序号刷新
            return;
        }
        let count = 1;
        const rows = getRowObj([0, getRowLength() -1]);
        for (const row of rows) {
            if ($(row).css("display") !== "none"){
                $(row).find("td:nth-child("+ (isEdit ? 2 : 1) +")")[0].innerText = count++;
            }
        }
    }

    /**
     * 在某些流程节点中隐藏一些行
     * @param {array} rowIndexArr 行下标数组
     * @param {array} nodeIdArr 流程节点名数组(lbpm.nowNodeId为当前流程节点名)
     * @return {void}
     */
    function lbpm_hideRowInNodes(rowIndexArr, nodeIdArr) {
        if (nodeIdArr.indexOf(lbpm.nowNodeId) > -1) {//
            for (let i = 0; i < rowIndexArr.length; i++) {
                hideRow(rowIndexArr[i]);
            }
        }
    }

    /**
     * 获取明细按钮对象或对象数组
     * @param {string} icon
     * "bottom_add":下方添加行 "bottom_delete":下方删除行
     * "right_copy":右边复制行 "right_delete":右边删除行
     * @param {number} [rowIndex]  行下标(可选,当获取某行的按钮时需提供)
     * @return {object|array|null}
     * @private
     */
    function getDetailBtnObj(icon, rowIndex) {
        let obj = null;
        switch (icon) {
            case "bottom_add": {
                obj = $(tbody_selector +" > tr.tr_normal_opt > td > div > div.tr_normal_opt_c > span:nth-child(1)")[0];
            }break;
            case "bottom_delete": {
                obj = $(tbody_selector +" > tr.tr_normal_opt > td > div > div.tr_normal_opt_l > span")[0];
            }break;

            case "right_copy": {
                if (typeof rowIndex !== "undefined") {// 返回某行的复制按钮
                    return $(tbody_selector + " > tr:nth-child(" + (rowIndex + 2) +
                        ") > td:nth-child("+ getColumnLength(true) +
                        ") > nobr > span.optStyle.opt_copy_style")[0];
                }
                else {// 返回当前已存在的全部复制按钮
                    obj = [];
                    for (let i = 0; i < getRowLength(); i++) {
                        obj.push(getDetailBtnObj("right_copy", i));
                    }
                }
            }break;

            case "right_delete": {
                if (typeof rowIndex !== "undefined") {
                    return $(tbody_selector +" > tr:nth-child(" + (rowIndex + 2) +
                        ") > td:nth-child("+ getColumnLength(true) +
                        ") > nobr > span.optStyle.opt_del_style")[0];
                }
                else {
                    obj = [];
                    for (let i = 0; i < getRowLength(); i++) {
                        obj.push(getDetailBtnObj("right_delete", i));
                    }
                }
            }break;
        }
        // console.log("getDetailBtnObj: ", {obj});
        return obj;
    }

    /**
     * 将明细表flagid从  id.0.id 转成 id.!{idex}.id
     * @param flagid 控件flagid
     * @return {string}
     * @private
     */
    function _transformFlagid2Index(flagid) {
        return flagid.replace(/\.\d{1,10}\./, ".!{index}.");// 返回未渲染完成前的id格式:id.!{index}.id
    }

    /**
     * 栏位值变化事件
     * 提示:
     * 1、明细表初始行数需 = 1
     * 2、使用该函数后会自动隐藏右边的复制行按钮(复制行和添加行有紧密的关系,去掉以降低bug率)
     * 建议有使用该函数的需要时在设计表单时将复制行功能去掉
     * @param {string} fd_id  栏位fd_id(不需加明细表id)
     * @param {function} func  触发函数
     * 1、取值
     * func可带event参数
     * 1) event.target.value(多选框checkBox只能拿到被点击的那个框框的值)【不建议】,此value仅是被点击节点的值,多选框checkbox仅能拿到被点击的那个框框的值
     * 2) event.data.flagid 为触发控件的flagid,【建议】在触发函数中通过该id获取触发值(publicMethod.getValueById)
     * 3) 当目标控件是地址本时,func第一个参数为值,第二个参数为控件对象【重要】
     * @return {void}
     */
    function listenChange(fd_id, func) {
        removeCopy();
        if (getRowLength() > 0) {// 将初始行全部添加触发事件
            for (let i = 0; i < getRowLength(); i++) {
                const id_index_id = getFlagidInDetailList(i, fd_id);
                AttachChangeEventById(id_index_id, func);
            }
        }
        listenAddRowEvent(function () {// 新增行 → 给最后一行添加触发事件
            const id_index_id = getFlagidInDetailList(getRowLength() - 1, fd_id);
            // 先移除上一个事件(因为当行数为0时点击添加行会将之前的func也clone出来 → 导致触发两次)
            $(document).off("change", "[flagid='"+ id_index_id +"']", func);
            AttachChangeEventById(id_index_id, func);
        })
    }

    /**
     * 去掉复制行按钮
     */
    function removeCopy() {
        for (let i = 0; i < getRowLength(); i++) {
            $(getDetailBtnObj("right_copy", i)).remove();
        }
    }
	
	/**
     * 去掉删除行按钮
     */
    function removeDelete() {
        for (let i = 0; i < getRowLength(); i++) {
            $(getDetailBtnObj("right_delete", i)).remove();
        }
        $(getDetailBtnObj("bottom_delete")).remove();
    }

    /**
     * 去掉底部添加按钮
     */
    function removeAdd() {
        let bottomAdd = getDetailBtnObj("bottom_add");
        if (bottomAdd) {
            $(bottomAdd).remove();
        }
    }

	/**
     * 去掉明细表的最后一列,复制行及删除行按钮一并去除
     */
    function removeLastColumn() {
		let gridTr = $(tbody_selector).children("tr")
		let gridCells = null;
		//remove数据区域每一行tr的td
		for (var i = 0 ; i < gridTr.length -1; i ++) {
			gridCells = gridTr[i].cells;
			if(gridCells[gridCells.length -1].innerHTML.indexOf("复制行") > -1 || 
			   gridCells[gridCells.length -1].innerHTML.indexOf("删除行") > -1){
				$(gridCells[gridCells.length -1]).hide();
			}
		}
		//隐藏标题行最后的td
		$(gridTr[0].cells[gridTr[0].cells.length -1]).hide();
		
    }

    /**
     * 明细表添加行事件(下面)
     * @param {function} func  触发函数,可带event参数
     * @return {void}
     */
    function listenAddRowEvent(func) {
        const addBtnSelector = getDetailBtnObj("bottom_add");
        $(addBtnSelector).bind("click", func);
    }

    /**
     * 设置某列为(非)必填
     * @param {string} columnId 列控件id(不加明细表id)
     * @param {boolean} validate true:设为必填,false:取消必填
     * @return {void}
     */
    function setValidateByColumnId(columnId, validate) {
        // 给初始行设置(非)必填
        for (let i = 0; i < getRowLength(); i++) {
            const id_index_id = getFlagidInDetailList(i, columnId);
            setValidateById(id_index_id, validate);
        }
        // 添加行时需要给最后一行设置(非)必填
        listenAddRowEvent(function () {
            const id_index_id = getFlagidInDetailList(getRowLength() - 1, columnId);
            setValidateById(id_index_id, validate);
        })
    }

    /**
     * 获取某控件所属行的下标
     * @param {object} domElemt 控件对象(用于定位某行) 例如触发事件的domElemt 或 event.target
     * @return {number}
     */
    function getRowIndexByDom(domElemt) {
        return $(domElemt).closest("tr").index() - 1;
    }

    /**
     * 设置同行某控件(非)必填
     * 提示:首先控件一开就需要是必填的,否则无法取消或设置小红点
     * @param domElemt 同行触发事件的event.target 或 控件对象
     * @param columnId 同行目标列id
     * @param {boolean} validate true:设为必填,false:取消必填
     */
    function setValidateSameRow(domElemt, columnId, validate) {
        const rowIndex = getRowIndexByDom(domElemt);
        const id_index_id = getFlagidInDetailList(rowIndex, columnId);
        setValidateById(id_index_id, validate);
    }

    /**
     * 设置明细表地址本选择范围:
     * 将scopeColumnId的值作为范围赋给targetColumnIds
     * @param scopeColumnId 范围列id
     * @param targetColumnIds 待设置选择范围的列id(多个用分号分隔)
     * @param [index] (可选)行下标,不传入则取最后一行
     * @return {void}
     */
    function setAddressScopeSameRow(scopeColumnId, targetColumnIds, index) {
        index = index === undefined ? getRowLength() - 1 : index;
        let scope = getDataSingle(index, scopeColumnId);
        if (scope[0]) {
            for (const targetColumnId of targetColumnIds.split(";")) {
                let flagid = getFlagidInDetailList(index, targetColumnId);
                setAddressScope(flagid, scope[0]);
            }
        }
    }

    /**
     * 设置明细表全部行的地址本选择范围:
     * 将scopeColumnId的值作为范围赋给targetColumnIds
     * scopeColumnId的值修改后依然生效(scopeColumnId值变化也会自动清空targetColumnIds)
     * @param scopeColumnId 范围列id
     * @param targetColumnIds 待设置选择范围的列id(多个用分号分隔)
     * @return {void}
     */
    function setAddressScopeAllAndChange(scopeColumnId, targetColumnIds) {
        // 全部设置范围
        // let interval = setInterval(() => {
        //     for (let i = 0; i < getRowLength(); i++) {
        //         setAddressScopeSameRow(scopeColumnId, targetColumnIds, i);
        //     }
        // }, 1000);
        // setTimeout(() => {
        //     clearInterval(interval);
        // }, 1000 * 10);
        for (let i = 0; i < getRowLength(); i++) {
            setAddressScopeSameRow(scopeColumnId, targetColumnIds, i);
        }

        // 范围列值变化时设置范围,并清空targetColumnIds
        AttachChangeEventById("DL_" + scopeColumnId, (a, b) => {
            let rowIndex = ChangeEventDetailListRowIndex(a, b);
            setAddressScopeSameRow(scopeColumnId, targetColumnIds, rowIndex);
            for (const targetColumnId of targetColumnIds.split(";")) {
                let flagid = getFlagidInDetailList(rowIndex, targetColumnId);
                clearField(flagid);
            }
        })
    }

    /**
     * 以低代码无流程表单为辅助,开窗填写明细表某行数据
     * @deprecated 函数已过期,请使用自定义控件:XP-明细表开窗
     * @param {string} dbTableName 低代码无流程表单数据库表名
     * @param {object} target 触发开窗的控件target
     * @param {number} width 开窗宽度(小数)
     * @param {number} height 开窗高度(小数)
     * @param {function(data)} callback 回调函数
     * @return {void}
     */
    function detailOpenWin(dbTableName, target, width, height, callback) {
        let rowIndex = getRowIndexByDom(target);
        let flagidArr = getFieldIds();
        let data4SubpageKey = "detailOpenWinData4SubpageKey" + new Date().getTime();
        window[data4SubpageKey] = {
            dataJson: {},// 控件值 {flagid: 值}
            statusJson : {}// 控件状态 {flagid: 0=隐藏,1=只读,2=非必填,3=必填}
        }
        for (const flagid of flagidArr) {
            let flagidInDetail = getFlagidInDetailList(rowIndex, flagid);
            window[data4SubpageKey].dataJson[flagid] = getValueById(flagidInDetail);
            window[data4SubpageKey].statusJson[flagid] = getEditLevel(flagidInDetail);
        }
        modelingSimpleMainOpenWin_add(dbTableName, width, height, false, {data4SubpageKey:data4SubpageKey}, callback);
    }

    /**
     * 以低代码无流程表单为辅助,开窗填写明细表某行数据 - 填写数据后的设置函数
     * @deprecated 函数已过期,请使用自定义控件:XP-明细表开窗
     * @param {object} data 数据{fd_id:值}
     * @param {object} target 触发开窗的控件target
     * @return {void}
     */
    function detailOpenWin_reSetData(data, target) {
        let rowIndex = getRowIndexByDom(target);
        for (const key in data) {setValueById(getFlagidInDetailList(rowIndex, key), data[key]);}
    }

    /**
     * 令某列执行校验器校验
     * @param columnId 列id
     * @return {void}
     */
    function exeValidationByColumnId(columnId) {
        for (let i = 0; i < getRowLength(); i++) {
            let flagid = getFlagidInDetailList(i, "fd_erp_no");
            exeValidation(flagid);
        }
    }

    /**
     * 设置某行某列的[编辑等级]
     * @author liquid
     * @date 2023年4月14日
     * @param {number} rowIndex 行下标
     * @param {string} columnId 列id
     * @param {number} editLevel 0=隐藏,1=只读,2=非必填,3=必填
     */
    function setEditLevelSingle(rowIndex, columnId, editLevel) {
        const flagid = getFlagidInDetailList(rowIndex, columnId);
        setEditLevel(flagid, editLevel);
    }

    /**
     * 设置整列的[编辑等级]
     * @author liquid
     * @date 2023年4月14日
     * @param {string} columnId 列id
     * @param {number} editLevel 0=隐藏,1=只读,2=非必填,3=必填
     */
    function setEditLevelByColumnId(columnId, editLevel) {
        for (let i = 0; i < getRowLength(); i++) {
            setEditLevelSingle(i, columnId, editLevel);
        }
    }

    // 私有函数不返回
    return {
        createdTime: new Date().getTime(),
        hasStatisticRow: hasStatisticRow,
        getDetailListId: getDetailListId, getDetailListLabel: getDetailListLabel,
        getDetailListStatus: getDetailListStatus,
        getFieldIds: getFieldIds, getFieldLabels: getFieldLabels,
        getRowLength: getRowLength, getColumnLength: getColumnLength,
        getRowFieldObjs: getRowFieldObjs, getRowObj: getRowObj,getColumnFieldObjs: getColumnFieldObjs,
        getFlagidInDetailList: getFlagidInDetailList,
        getDataSingle: getDataSingle,
        getData: getData, getData2Json: getData2Json,
        getDataByRowIndex: getDataByRowIndex, getDataByRowIndex2Json: getDataByRowIndex2Json, getDataByColumnId: getDataByColumnId, getDataSameRow: getDataSameRow,
        setData: setData,
        setDataByRowIndex: setDataByRowIndex, setDataByColumnId: setDataByColumnId, setColumnDataByDom: setColumnDataByDom, setColumnNextDataByDom: setColumnNextDataByDom,
        addRow: addRow,
        setDataSingle: setDataSingle,
        clearSingle: clearSingle,
        clearRow: clearRow, clearColumn: clearColumn,
        clearAll: clearAll,
        deleteRow: deleteRow, deleteAllRows: deleteAllRows,
        hideRow: hideRow, hideAllRows: hideAllRows, showRow: showRow, showAllRows: showAllRows,
        lbpm_hideRowInNodes: lbpm_hideRowInNodes, getDetailBtnObj: getDetailBtnObj,
        listenChange: listenChange, removeCopy: removeCopy, removeAdd:removeAdd, removeDelete:removeDelete, removeLastColumn:removeLastColumn,
        listenAddRowEvent: listenAddRowEvent,
        setValidateByColumnId: setValidateByColumnId,
        getRowIndexByDom: getRowIndexByDom,
        setValidateSameRow: setValidateSameRow,
        setAddressScopeSameRow: setAddressScopeSameRow, setAddressScopeAllAndChange: setAddressScopeAllAndChange,
        detailOpenWin: detailOpenWin, detailOpenWin_reSetData: detailOpenWin_reSetData,
        exeValidationByColumnId: exeValidationByColumnId,
        setEditLevelSingle: setEditLevelSingle, setEditLevelByColumnId: setEditLevelByColumnId,
    }
}

/**[无需适配移动端]
 * 简化图片上传(明细表外)
 * @author liquid
 * @date 2022年5月11日
 * @param {string} ids 图片上传id(多个用;分隔)
 * @return {void}
 */
function simplifyImgUpload(ids) {
    const idArr = ids.split(";");
    for (const id of idArr) {
        $("#uploader_"+ id +" .lui_upload_tip.tip_info").hide();// 隐藏上传成功后的提示
    }
}

/**[无需适配移动端]
 * 简化图片上传(明细表内)
 * @author liquid
 * @date 2022年5月10日
 * @param {string} detailListId 明细表id
 * @param {string} columnId 列控件id(不含明细表id)
 * @return {void}
 */
function simplifyImgUpload_detailList(detailListId, columnId) {
    const dlObj = getDetailObj(detailListId);
    // 简化初始行
    for (let i = 0; i < dlObj.getRowLength(); i++) {
        __simplify(i);
    }

    // 明细表复制行事件
    // dlObj.listenCopyRowEvent_right(function () {
    //     setTimeout(function (){// 等待1s,相关组件可能还没加载完
    //         __simplify(dlObj.getRowLength() - 1);
    //     }, 1000)
    // })

    // 明细表添加行事件
    dlObj.listenAddRowEvent(function () {
        setTimeout(function (){
            __simplify(dlObj.getRowLength() - 1);
        }, 1000)
    })

    // 简化某行
    function __simplify(index) {
        const columnObj = $("#_xform_extendDataFormInfo\\.value\\(" + detailListId +"\\."+ index +"\\." + columnId + "\\)");
        columnObj.find(".lui_upload_tip.tip_info").hide();// 隐藏【上传成功提示】
    }
}

// /**[无需适配移动端]
//  * 为明细表添加行打开功能
//  * 1、使用该函数后会自动隐藏右边的复制行按钮(复制行和添加行有紧密的关系,去掉以降低bug率)
//  * 建议有使用该函数的需要时在设计表单时将复制行功能去掉
//  * @deprecated
//  * @author liquid
//  * @date 2022年5月16日
//  * @param {string|null} outerDivId 若存在外层div,需将其宽度设置为1200px以保证最佳效果;不存在传入null
//  * @param {string} detailListId 明细表id
//  * @param {boolean} hasOrderNo 是否设有序号(重要)
//  * @return {void}
//  */
// const openedDetailsList = {};// 存放已打开行的表
function addOpenRow2DetailList(outerDivId, detailListId, hasOrderNo) {
    // setTimeout(function () {
    //     _addOpenRow2DetailList(outerDivId, detailListId, hasOrderNo);
    // }, 2000)
}
// function _addOpenRow2DetailList(outerDivId, detailListId, hasOrderNo) {
//     const dlobj = getDetailObj(detailListId);
//     const TABLE_DL_ID = "TABLE_DL_" + detailListId;
//     const table = window[TABLE_DL_ID];
//     const table_selector = "#" + TABLE_DL_ID + " ";
//     const tbody_selector = table_selector + " > tbody ";
//     if (isEmpty(table)){// 明细表未加载完,或不存在该表
//         console.error(detailListId + ":明细表未加载完或不存在该表;请检查表id");
//         return;
//     }
//     dlobj.removeCopy();// 去掉明细表复制行按钮
//     const tableStatus = dlobj.getDetailListStatus();
//     // console.log("明细表" + detailListId + "["+ (tableStatus === "edit" ? "编辑" : "只读") +"模式]");
//
//     // 1 [编辑]模式
//     if (tableStatus === "edit") {// [编辑模式-编辑按钮]
//         // 添加右边编辑按钮
//         for (let i = 0; i < dlobj.getRowLength(); i++) {
//             $(tbody_selector + " > tr:nth-child("+ (i + 2) +") > td.freeze_right_col > img").remove();
//             __addEditBtn2right(i);
//         }
//         // [添加行]触发
//         dlobj.listenAddRowEvent(function () {
//             const rowIndex = dlobj.getRowLength() - 1;
//             const tds = $(tbody_selector + " > tr:nth-child("+ (rowIndex + 2) +") > td");
//             $(tds[tds.length - 1]).find("img").remove();// 先去掉自动clone出来的编辑按钮
//             __addEditBtn2right(rowIndex);// 添加编辑按钮
//         })
//     }
//
//     /**
//      * bug!!!!!!!!!有时候点击编辑按钮会触发两次以上行打开!
//      * 1.1 可编辑明细表 - 向最后列的操作框里添加[编辑按钮]
//      * @param {number} rowIndex 目标行下标
//      * @return {void}
//      * @private
//      */
//     function __addEditBtn2right(rowIndex) {
//         const td = dlobj.getRowObj(rowIndex).find("td").slice(-1)[0];// 操作列单元格
//         const copyBtn = $(td).find("span[class='optStyle opt_del_style']");
//         if (copyBtn.length < 1) { // 非编辑模式
//             return;
//         }
//
//         // 添加编辑按钮
//         const editBtn = document.createElement("img");
//         const editBtnWidth = $(td).find("span").css("width");
//         editBtn.title = xp_lang("0002_edit_row");
//         $(td).append(editBtn);
//         editBtn.src = Com_Parameter.ContextPath + "resource/js/xpUtil/icons/edit01.png";
//         editBtn.id = detailListId + "_editBtn_" + new Date().getTime();// 需定义唯一id,避免与其它行冲突
//         editBtn.className = "optStyle";
//         editBtn.style.width = editBtnWidth;
//         editBtn.style.height = editBtnWidth;
//         $(editBtn).css("cursor", "pointer");
//
//         $(document).on("click", "#" + editBtn.id, function (e) {// click事件
//             const rowIndex = __getClickedEditBtnIndex(editBtn.id)
//             openDetailListRow(outerDivId, detailListId, hasOrderNo, rowIndex, "clickRightEdit");
//         })
//         $(document).on("mouseenter", "#" + editBtn.id, function () {// 鼠标进入按钮事件
//             // $(editBtn)[0].setAttribute("src", Com_Parameter.ContextPath + "resource/js/xpUtil/icons/edit02.png")// 不起作用
//             $("#" + editBtn.id)[0].setAttribute("src", Com_Parameter.ContextPath + "resource/js/xpUtil/icons/edit02.png")
//         })
//         $(document).on("mouseout", "#" + editBtn.id, function () {// 鼠标离开按钮事件
//             $("#" + editBtn.id)[0].setAttribute("src", Com_Parameter.ContextPath + "resource/js/xpUtil/icons/edit01.png")
//         })
//         // 此方法无法触发append组件的事件
//         // $("#" + editBtn.id).bind("click", function (){})
//     }
//     function __getClickedEditBtnIndex(editBtnId) {// 获取相应编辑按钮当前所在行
//         const trs = $(tbody_selector +" > tr");
//         let editBtnIndex = 0;
//         for (let i = 0; i < trs.length; i++) {
//             const tr = trs[i];
//             const editbtn = $(tr).find("#" + editBtnId);
//             if (editbtn.length > 0) {
//                 editBtnIndex = i;
//                 break;
//             }
//         }
//         return editBtnIndex - 1;
//     }
//
//
//     // 2 [只读]模式
//     if (tableStatus === "onlyRead") {// [只读模式-点击行打开 & 点击查看打开]
//         // 添加一格标题td
//         const titleTr = $(tbody_selector +" > tr.tr_normal_title");
//         titleTr.append("<td class='td_normal_title freeze_right_col' row='0' coltype='blankTitleCol' style='width:40px;text-align:center;right:0'></td>");
//         setTimeout(() =>{// 系统会自动给我的列添加上冻结按钮,需去掉
//             $(tbody_selector + " > tr.tr_normal_title > td.td_normal_title.freeze_right_col > div > span").remove();
//         }, 1000)
//         // 若存在统计行,则统计行也要加上一个空的td
//         if (dlobj.hasStatisticRow()) {
//             const nullTr = $(tbody_selector + " > tr").slice(-2)[0];
//             $(nullTr).append("<td class='freeze_right_col' row='0' coltype='blankTitleCol' style='width:40px;text-align:center;right:0'></td>");
//         }
//         // 双击行事件 && 添加查看按钮
//         for (let i = 0; i < dlobj.getRowLength(); i++) {
//             // __dbclickRowEvent(i);// [只读]添加行双击事件 - 取消该方式(有bug)
//             __addCheckBtn(i);// [只读]添加左边查看按钮
//         }
//     }
//
//     /**
//      * 2.1、只读模式 - 双击点击行打开
//      * @param {number} rowIndex 目标行下标
//      * @return {void}
//      * @private
//      */
//     function __dbclickRowEvent(rowIndex) {
//         // dbclick无效???
//         // dlobj.getRowObj(rowIndex).bind("dbclick", function () {})
//         // 手动搓一个双击触发事件
//         let clickTime = 0;
//         dlobj.getRowObj(rowIndex).bind("click", function () {
//             if (openedDetailsList[detailListId] === "yes"){// 已打开某行,阻止重复打开
//                 return;
//             }
//             const nowTime = new Date().getTime();
//             if (Math.abs(nowTime - clickTime) < 300) {// 双击之间间隔300ms
//                 // console.log("double-click: ", clickTime);
//                 // console.log("double-click-rowIndex: ", rowIndex);
//                 openedDetailsList[detailListId] = "yes";// 已该表已打开某行
//                 openDetailListRow(outerDivId, detailListId, hasOrderNo, rowIndex - 1, "dbclickRow");
//             }
//             clickTime = nowTime;
//         })
//     }
//
//     /**
//      * 2.2 只读模式 - 添加查看按钮
//      * @param {number} rowIndex 目标行下标
//      * @return {void}
//      * @private
//      */
//     function __addCheckBtn(rowIndex) {
//         // 添加查看按钮
//         const dataTr = dlobj.getRowObj(rowIndex);
//         console.log(dataTr)
//         const tdid = detailListId + "_" + rowIndex + "_" + new Date().getTime();
//         const div_style = "width:40px;height:15px;text-align:center;line-height:100%";
//         const imgSrc = Com_Parameter.ContextPath + "resource/js/xpUtil/icons/search01.png";
//         const div = "<div style='"+ div_style +"'><img style='height:100%;' src='"+ imgSrc +"' alt='"+ xp_lang('0002_check') +"'></div>";
//         const checkTd = "<td id='"+ tdid +"' class='freeze_right_col' style='width:40px;cursor:pointer;right:0' title='"+ xp_lang('0002_check') +"'>"+ div +"</td>"
//         $(dataTr).append(checkTd);
//         // 给td加触发事件
//         $(document).on("click", "#" + tdid, function () {
//             // const currentRowIndex = __getClickedCheckBtnIndex(tdid);
//             if (openedDetailsList[detailListId] === "yes") return;// 已打开某行,阻止重复打开
//             openedDetailsList[detailListId] = "yes";
//             openDetailListRow(outerDivId, detailListId, hasOrderNo, rowIndex, "clickCheck");
//         })
//     }
//     function __getClickedCheckBtnIndex(checkBtnId) {}// 只读模式不涉及行删除,无需获取当前实际行标
//
// }
//
// /**[无需适配移动端]
//  * 打开明细表行
//  * @author liquid
//  * @date 2022年5月15日
//  * @param outerDivId 若明细表外包div,则传入该div的id;若无,传入null
//  * @param detailListId 明细表id
//  * @param hasOrderNo 明细表是否含有序号
//  * @param rowIndex 需要打开的行下标(从0开始,第一行数据行的下标为0)
//  * @param operation "clickAdd":添加行触发 "clickRightEdit":点击右边编辑触发 "dbclickRow":双击行触发 "clickCheck":点击查看按钮 ;无则传入null
//  * @return {void}
//  */
// function openDetailListRow(outerDivId, detailListId, hasOrderNo, rowIndex, operation) {
//     // 1、获取相关对象和信息
//     const dlobj = getDetailObj(detailListId);
//     const TABLE_EL_ID = "TABLE_DL_" + detailListId;
//     const table = window[TABLE_EL_ID];
//     const table_selector = "#" + TABLE_EL_ID + " ";
//     const tbody_selector = table_selector + "> tbody"
//     const tr = dlobj.getRowObj(rowIndex);// 目标行对象
//     const labels = dlobj.getFieldLabels();// 标题数组
//     const originalRowData = dlobj.getDataByRowIndex(rowIndex);
//
//     // 2、需先将所有必填提示remove,否则会导致行高出问题
//     tr.find(".validation-advice").remove();
//
//     // 3、隐藏行
//     $(tbody_selector + " > tr.tr_normal_title").hide();// 隐藏标题行
//     for (let i = 0; i < dlobj.getRowLength(); i++) {
//         if (i === rowIndex) continue;
//         dlobj.hideRow(i);
//     }
//     if (dlobj.hasStatisticRow()){// 隐藏统计行
//         $(tbody_selector + " > tr:nth-child("+ (dlobj.getRowLength() + 2) +")").hide();
//     }
//     $(tbody_selector + " > tr.tr_normal_opt > td > div > div").hide();// 隐藏最后一行的操作按钮
//     $("#" + outerDivId).css("overflow", "");// 去掉滚动
//
//     // 4、隐藏目标行多余的列
//     const tds = tr.find("td");
//     if (hasOrderNo) {
//         const orderIndex = "dbclickRow;clickCheck".includes(operation) ? 0 : 1;
//         $(tds[orderIndex]).hide();// 序号列
//     }
//     if ("clickAdd;clickRightEdit;".indexOf(operation) > -1){
//         $(tds[0]).hide();// 选择列
//         $(tds[tds.length - 1]).hide();// 右边复制行、删除行
//     }
//
//     // 5、设置目标行的内容溢出自动换行
//     // Webkit内核的浏览器(如Safari),必须加上-webkit前缀(干脆默认加上,各个内核均可用)
//     // console.log("tr", tr);
//     tr.css("display", "-webkit-flex");
//     tr.css("flex-direction", "row");
//     tr.css("flex-wrap", "wrap");
//     tr.css("width", "101%");
//     // tr.css("justify-content", "space-between");
//
//     // 6、设置目标行单元格样式
//     let labelIndex = 0;
//     for (let i = 0; i < tds.length; i++) {
//         const td = tds[i];
//         $(td).css("width", "32.1%");// 宽度
//         $(td).css("text-align", "left");// 单元格内左对齐
//         $(td).css("line-height", $(td).css("height"));// 单元格内垂直居中
//         $(td).css("border", "none");// 去掉原本的边界
//         $(td).css("border-right", "dashed 1px #d5d5d5");
//         $(td).css("border-bottom", "dashed 1px #d5d5d5");
//         // 加标题
//         if ("dbclickRow;clickCheck".includes(operation)) {// 只读模式的双击行和查看
//             if (hasOrderNo && i === 0) continue;
//             $(td).prepend("<lable class='xp_detailOpen_label' style='color:#1b83d8;'> "+ labels[labelIndex++] +" </lable>");
//         }
//         else if(operation === "clickAdd" || operation === "clickRightEdit"){
//             if (i === 0 || (hasOrderNo && i === 1)) continue;// i为0是选择列 || 有序号列i为1是序号列 → 不加标题
//             $(td).prepend("<lable class='xp_detailOpen_label' style='color:#1b83d8;'> "+ labels[labelIndex++] +" </lable>");
//         }
//     }
//
//     // 7、添加确认按钮
//     const appendDiv_comfirm_id = detailListId +"_appendDiv_comfirm";
//     const appendDiv_cancel_id = detailListId +"_appendDiv_cancel";
//     const appendDiv_comfirm = "<span id='"+ appendDiv_comfirm_id +"' style='width:300px;height:100%;cursor:pointer;font-size:15px'>" + xp_lang("0002_confirm") + " </span>";
//     const appendDiv_cancel = "<span id='"+ appendDiv_cancel_id +"' style='width:300px;height:100%;cursor:pointer;font-size:15px;justify-self: center'> " + xp_lang("0002_cancel") + "</span>";
//     const operationDiv = $(tbody_selector +" > tr.tr_normal_opt > td > div");
//     operationDiv.append(appendDiv_comfirm);
//     operationDiv.append(appendDiv_cancel);
//     $("#" + appendDiv_comfirm_id).bind("click", __openDetailListRow_confirm);// 确认
//     $("#" + appendDiv_cancel_id).bind("click", function () {// 取消
//         const modified = JSON.stringify(originalRowData) !== JSON.stringify(dlobj.getDataByRowIndex(rowIndex));
//         // console.log(JSON.stringify(originalRowData));
//         // console.log(JSON.stringify(dlobj.getDataByRowIndex(rowIndex)))
//         // console.log({modified})
//         const msg_1 = xp_lang("0003_key");
//         // 若是【点击编辑】触发的行打开且进行了修改,则询问是否保存已修改的数据,不保存 → 还原数据
//         if (operation === "clickRightEdit" && modified && !confirm(msg_1)) {
//             dlobj.setDataByRowIndex(rowIndex, originalRowData);// 恢复数据
//         }
//         // 若是【添加行】触发的,不保存 → 删除该行
//         if (operation === "clickAdd") {
//             let CONFIRM = false;
//             if (modified && !(CONFIRM = confirm(msg_1))) {// 若数据已修改,则询问是否保存,若取消则删除该行
//                 dlobj.deleteRow(rowIndex);
//             }
//             else if (!CONFIRM){// 数据未修改不询问是否保存,则CONFIRM一定是false,故可删除该行
//                 dlobj.deleteRow(rowIndex);
//             }
//         }
//         __openDetailListRow_confirm();// 恢复行样式
//     });
//
//     // 8、若是只读模式双击&查看行进来 - 添加关闭按钮 && 隐藏最后一列查看按钮td
//     if ("dbclickRow;clickCheck".includes(operation)) {
//         // 添加关闭按钮
//         const id = detailListId + "_closeBtn";
//         const style_closeBtn = "width:98.5%;height:100%;border:none;" +
//             "font-size:25px;text-align:center;cursor:pointer;font-weight:500;color:#9d9d9d";
//         const closeBtn = "<td id='"+ id +"' style='"+ style_closeBtn +"' title='"+ xp_lang("0002_close") +"'> × </td>"
//         $(tbody_selector + " > tr:nth-child("+ (rowIndex + 2) +")").append(closeBtn);
//         const selector = "#" + id;
//         $(document).on("click", selector, function (e) {// click事件
//             __openDetailListRow_confirm();// 恢复
//         })
//         $(document).on("mouseenter", selector, function (e) {
//             $(selector).css("color", "#1b83d8")
//         })
//         $(document).on("mouseout", selector, function (e) {
//             $(selector).css("color", "#9d9d9d")
//         })
//         // 隐藏最后一列查看按钮td
//         const checkTd = $(dlobj.getRowObj(rowIndex)[0]).find("td").slice(-2)[0];// 倒数第2个是查看按钮(倒数第1个是关闭按钮)
//         $(checkTd).hide();
//     }
//
//     // 9、打开行,必须先移除冻结列的class='freeze_left_col'(当冻结的数据列超过3时会出现样式错乱)
//     const freeze_left_tds = $(tr).find(".freeze_left_col");
//     freeze_left_tds.removeClass("freeze_left_col");
//
//
//     /**
//      * 确认
//      */
//     function __openDetailListRow_confirm() {
//         // 3、隐藏行 - 逆操作
//         $(tbody_selector + " > tr.tr_normal_title").show();// 隐藏标题行
//         for (let i = 0; i < dlobj.getRowLength(); i++) {
//             if (i === rowIndex) continue;
//             dlobj.showRow(i);
//         }
//         if (dlobj.hasStatisticRow()){// 隐藏统计行
//             $(tbody_selector + " > tr:nth-child("+ (dlobj.getRowLength() + 2) +")").show();
//         }
//         $(tbody_selector + " > tr.tr_normal_opt > td > div > div").show();// 隐藏最后一行的操作按钮
//         $("#" + outerDivId).css("overflow", "scroll");// 去掉滚动
//
//         // 4、隐藏目标行多余的列 - 逆操作
//         const tds = tr.find("td");
//         if (hasOrderNo) {
//             const orderIndex = "dbclickRow;clickCheck".includes(operation) ? 0 : 1;
//             $(tds[orderIndex]).show();// 序号列
//         }
//         if ("clickAdd;clickRightEdit;".indexOf(operation) > -1){
//             $(tds[0]).show();// 选择列
//             $(tds[tds.length - 1]).show();// 右边复制行、删除行
//         }
//
//         // 5、设置目标行的内容溢出自动换行 - 逆操作
//         // Webkit内核的浏览器(如Safari),必须加上-webkit前缀(干脆默认加上,各个内核均可用)
//         tr.css("display", "");
//         tr.css("flex-direction", "");
//         tr.css("flex-wrap", "");
//         tr.css("width", "");
//         // tr.css("justify-content", "space-between");
//
//         // 6、设置目标行单元格样式 - 逆操作
//         for (const td of tds) {
//             $(td).css("width", "");// 宽度
//             $(td).css("text-align", "");// 单元格内左对齐
//             $(td).css("line-height", "");// 单元格内垂直居中
//             $(td).css("border", "none");// 去掉原本的边界
//             $(td).css("border-right", "");
//             $(td).css("border-bottom", "");
//             $(td).find(".xp_detailOpen_label").remove();// 含有标题的列,给其加上标题
//         }
//
//         // 7、添加确认按钮 - 逆操作
//         $("#"+ detailListId +"_appendDiv_comfirm").remove();
//         $("#"+ detailListId +"_appendDiv_cancel").remove();
//
//         // 8、若是只读模式[双击行]或[点击查看]进来 - 移除关闭按钮 && 解除已打开行标志 && 重新装入最后一列查看按钮td
//         if ("dbclickRow;clickCheck".includes(operation)) {
//             // 移除关闭按钮
//             $("#" + detailListId + "_closeBtn").remove();
//             openedDetailsList[detailListId] = "no";// 解除已打开行标志
//
//             // 移除并重新装入最后一列查看按钮td(直接show出来会导致td高度错乱)
//             const lastCheckTd = $(dlobj.getRowObj(rowIndex)).find("td").slice(-1)[0];
//             $(lastCheckTd).remove();
//             const tdid = detailListId + "_" + rowIndex + "_" + new Date().getTime();
//             const div_style = "width:40px;height:15px;text-align:center;line-height:100%";
//             const imgSrc = Com_Parameter.ContextPath + "resource/js/xpUtil/icons/search01.png";
//             const div = "<div style='"+ div_style +"'><img style='height:100%;' src='"+ imgSrc +"' alt='"+xp_lang("0002_check")+"'></div>";
//             const checkTd = "<td id='"+ tdid +"' class='freeze_right_col' style='width:40px;cursor:pointer;right:0' title='"+xp_lang("0002_check")+"'>"+ div +"</td>"
//             $(dlobj.getRowObj(rowIndex)[0]).append(checkTd);
//             $(document).on("click", "#" + tdid, function () {// 给td加触发事件
//                 openedDetailsList[detailListId] = "yes";
//                 openDetailListRow(outerDivId, detailListId, hasOrderNo, rowIndex, "clickCheck");
//             })
//         }
//
//         // 9、打开行,恢复移除的冻结列的class='freeze_left_col'
//         freeze_left_tds.addClass( "freeze_left_col");
//
//     }
// }

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Liquid-Li

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值