介绍

定制字段拥有高度的自定义功能,用户自身的需求自主开发,系统仅负责保存需要保存的数据。也正因为其自身的自定义特性,要求用户有一定的前端开发能力!

配置

应用配置管理-对象管理-对象编辑-新建字段

参数 必填 说明
值显示 必填 在详情页展示的显示形式
结构 必填 字段根据结构来进行自定义展示
定制PC查看 选填 在PC端查看定制字段时:根据自定义的html、css、js进行查看弹框页面的交互
定制PC编辑 选填 在PC端编辑定制字段时,根据自定义的html、css、js决定编辑弹框页面的交互
定制移动查看 选填 在移动端查看定制字段时:根据自定义的html、css、js进行查看弹框页面的交互
定制移动编辑 选填 在移动端编辑定制字段时,根据自定义的html、css、js决定编辑弹框页面的交互
界面自定义代码-html 选填 页面的html代码
界面自定义代码-css 选填 页面的css代码
界面自定义代码-js 选填 根据默认代码中的注释引导,在相应位置编写自定义逻辑
界面自定义代码-资源 选填 上传图片,文件等资源,使用户可在定制字段界面中引用
通过以上的说明,那么如何配置它呢?

demo

结构:
[{ "id": 1,
    "code":"CN_fruits",
    "name": "fruits1",
    "products": [{
        "id": "1-1",
        "name": "苹果"
    },{
        "id": "1-2",
        "name": "香蕉"
    },{
        "id": "1-3",
        "name": "橘子"
    },{
        "id": "1-4",
        "name": "椰子"
    },{
        "id": "1-5",
        "name": "菠萝"
    },{
        "id": "1-6",
        "name": "西瓜"
    },{
        "id": "1-7",
        "name": "葡萄"
    },{
        "id": "1-8",
        "name": "水蜜桃"
    }]
}]

定制PC查看

/************************* html *****************************/
<div id="udesk-custom-field" class="udesk-custom-field">
    <table class="udesk-custom-field-table">
        <thead>
            <th>
                是否选中
            </th>
            <th>
                分类
            </th>
            <th>
                详情
            </th>
            <th>
                时间1
            </th>
            <th>
                时间2
            </th>
        </thead>
        <tbody id="udesk-custom-field-tbody"></tbody>
    </table>
    <p class="work-time-total-container">
        时间1总计:
        <span id="udesk-work-time-total">0</span>
    </p>
    <p class="on-way-time-total-container">
        时间2总计:
        <span id="udesk-on-way-time-total">0</span>
    </p>
</div>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>


/************************* css *****************************/
.udesk-custom-field {
    position: relative;
}
.udesk-custom-field-table {
    width: 100%;
    min-height: 25px;
    font-size: 13px;
    line-height: 1.5;
    text-align: center;
    border-collapse: collapse;
}
.udesk-custom-field-table tr th,
.udesk-custom-field-table tr td {
    padding: 8px;
    border: 1px solid #ccc;
}
.product-td {
    text-align: left;
}
.product-td label {
    margin: 0 5px;
}
.product-td label > input {
    vertical-align: bottom;
}


/************************* js *****************************/
/*
* initData : 请务必注册此事件,用于数据初始化
* 数据结构 {
* data: 定义该字段结构部分,
* value:界面保存数据
* }
*
* onSavedValueData: 请务必注册此事件,用于数据保存
*
* */


(function (global) {
    var fullDataList = [];
    global.webApiClient.init();
    global.webApiClient.on("init", function () {
        //该事件用于保存数据
        global.webApiClient.initData();
    });

    global.webApiClient.on("initData", function (message) {
        //该事件可接收初始数据
        if (message) {
            dataSource = message.data;
            value = message.value;
        }
        if (value && typeof value === "string" && value !== "") {
            value = JSON.parse(value);
        }

        try {
            fullDataList = buildTableDataList(dataSource, value);
            renderTable(fullDataList);
            renderTotal(fullDataList);
        } catch (error) {
            //数据格式不正确,
        }
    });

    global.webApiClient.on("onDestroyed", function () {
        //该事件用于销毁内存,避免内存泄露
        global.webApiClient.off("init");
        global.webApiClient.off("initData");
        global.webApiClient.off("onDestroyed");
        fullDataList = [];
    });

    function getServerProductsData(list) {
        var newProducs = [];
        for (var index = 0; index < list.length; index++) {
            var listItem = list[index];
            if (listItem.isChecked) {
                newProducs.push(listItem.data.id);
            }
        }
        return newProducs;
    }

    function buildTableDataList(dataList, value) {
        var newDataList = [];
        let values = value.values;
        for (var index = 0; index < dataList.length; index++) {
            var dataItem = dataList[index];
            var targetValue = findTargetValue(values, function (item) {
                return item.id == dataItem.id;
            });

            var newDataItem = {
                data: dataItem,
                isChecked: false,
                products: buildProducts(dataItem.products, targetValue),
                onlineTime: 0,
                onWayTime: 0,
            };

            if (targetValue) {
                newDataItem.isChecked = true;
                newDataItem.onlineTime = targetValue.onlineTime;
                newDataItem.onWayTime = targetValue.onWayTime;
            }
            newDataList.push(newDataItem);
        }

        return newDataList;
    }

    function buildProducts(products, targetValue) {
        var productList = [];
        if (products && products.length > 0) {
            for (var j = 0; j < products.length; j++) {
                var product = products[j];
                var isChecked = false;
                if (targetValue) {
                    var findTarget = findTargetValue(
                        targetValue.products,
                        function (item) {
                            return item == product.id;
                        }
                    );

                    isChecked = findTarget == null ? false : true;
                }
                productList.push({
                    data: product,
                    isChecked: isChecked,
                });
            }
        }
        return productList;
    }

    function renderTotal(fullDataList) {
        var workTimeTotal = 0;
        var onWayTimeTotal = 0;
        for (var index = 0; index < fullDataList.length; index++) {
            let fullData = fullDataList[index];
            workTimeTotal += Number(fullData.onlineTime);
            onWayTimeTotal += Number(fullData.onWayTime);
        }
        global.document.getElementById(
            "udesk-work-time-total"
        ).innerHTML = workTimeTotal;
        global.document.getElementById(
            "udesk-on-way-time-total"
        ).innerHTML = onWayTimeTotal;
    }

    function renderTable(dataList) {
        var tr = "";
        for (var index = 0; index < dataList.length; index++) {
            var dataItem = dataList[index];
            tr +=
                "<tr data-id=" +
                dataItem.data.id +
                "><td><input type='checkbox' value=" +
                dataItem.data.id +
                (dataItem.isChecked ? " checked disabled/>" : " disabled/>") +
                " </td>";

            tr +=
                "<td>" +
                dataItem.data.name +
                "</td><td class='product-td'>";

            $.each(dataItem.products, function (i, product) {
                tr +=
                    "<label><input type='checkbox' value=" +
                    product.data.id +
                    (product.isChecked ? " checked disabled/>" : " disabled/>") +
                    product.data.name +
                    "</label>";
            });

            tr +=
                "</td><td><input type='number' min=0 value=" +
                dataItem.onlineTime +
                " disabled /></td>";

            tr +=
                "</td><td><input type='number' min=0 value=" +
                dataItem.onWayTime +
                " disabled /></td></tr>";
        }

        global.document.getElementById(
            "udesk-custom-field-tbody"
        ).innerHTML = tr;
    }

    function findTargetValue(value, matcherCallback) {
        var findValue = null;
        if (value && value.length > 0) {
            for (var i = 0; i < value.length; i++) {
                var valueItem = value[i];
                if (matcherCallback(valueItem)) {
                    findValue = valueItem;
                    break;
                } else {
                    findValue = null;
                }
            }
        }
        return findValue;
    }
})(window);

定制PC编辑

/************************* html *****************************/
<div id="udesk-custom-field" class="udesk-custom-field">
    <table class="udesk-custom-field-table">
        <thead>
            <tr>
                <th>
                    是否选中
                </th>
                <th>
                    分类
                </th>
                <th>
                    详情
                </th>
                <th>
                    时间1
                </th>
                <th>
                    时间2
                </th>
            </tr>
        </thead>
        <tbody id="udesk-custom-field-tbody"></tbody>
    </table>
    <p class="work-time-total-container">
        时间1总计:
        <span id="udesk-work-time-total">0</span>
    </p>
    <p class="on-way-time-total-container">
        时间2总计:
        <span id="udesk-on-way-time-total">0</span>
    </p>
</div>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>

/************************* css *****************************/
.udesk-custom-field {
    position: relative;
}
.udesk-custom-field-table {
    width: 100%;
    min-height: 25px;
    font-size: 13px;
    line-height: 1.5;
    text-align: center;
    border-collapse: collapse;
}
.udesk-custom-field-table tr th,
.udesk-custom-field-table tr td {
    padding: 8px;
    border: 1px solid #ccc;
}
.product-td {
    text-align: left;
}
.product-td label {
    margin: 0 5px;
}
.product-td label > input {
    vertical-align: bottom;
}


/************************* js *****************************/
/*
* initData : 请务必注册此事件,用于数据初始化
* 数据结构 {
* data: 定义该字段结构部分,
* value:界面保存数据
* }
*
* onSavedValueData: 请务必注册此事件,用于数据保存
*
* */

(function (global) {
    var fullDataList = [];
    var dataSource = [];
    global.webApiClient.init();
    global.webApiClient.on("init", function () {
        //该事件用于保存数据
        global.webApiClient.initData();
    });

    global.webApiClient.on("initData", function (message) {
        //该事件可接收初始数据
        if (message) {
            dataSource = message.data;
            value = message.value;
        }
        if (value && typeof value === "string" && value !== "") {
            value = JSON.parse(value);
        }

        try {
            fullDataList = buildTableDataList(dataSource, value);
            renderTable(fullDataList);
            renderTotal(fullDataList);
        } catch (error) {
            //数据格式不正确,
        }
    });

    global.webApiClient.on("onSavedValueData", function () {
        //该事件用于保存数据
        var data = getServerDataValue();
        if (data && data.errorMessage) {
            alert(data.errorMessage);
        } else {
            global.webApiClient.saveValueData(data.value);
        }
    });

    global.webApiClient.on("onDestroyed", function () {
        //该事件用于销毁内存,避免内存泄露
        global.webApiClient.off("init");
        global.webApiClient.off("initData");
        global.webApiClient.off("onSavedValueData");
        global.webApiClient.off("onDestroyed");

        global.changeCheckBoxValue = null;
        global.changeOnlineTime = null;
        global.changeOnWayTime = null;
        fullDataList = [];
    });

    global.changeCheckBoxValue = function (e, dataItemId, productId) {
        for (var a = 0; a < fullDataList.length; a++) {
            var fullDataItem = fullDataList[a];
            var checkedValue = e.target.checked;

            if (dataItemId == fullDataItem.data.id) {
                if (productId == null) {
                    fullDataItem.isChecked = checkedValue;
                } else {
                    if (checkedValue) {
                        fullDataItem.isChecked = checkedValue;
                    }
                    findTargetValue(fullDataItem.products, function (
                        item
                    ) {
                        if (item.data.id == productId) {
                            item.isChecked = checkedValue;
                            return true;
                        } else {
                            return false;
                        }
                    });
                }
                break;
            }
        }
        renderTable(fullDataList);
    };

    global.changeOnlineTime = function (e, dataItemId) {
        var onlineTimeValue = e.target.value;
        findTargetValue(fullDataList, function (item) {
            if (item.data.id == dataItemId) {
                item.onlineTime = onlineTimeValue;
                return true;
            } else {
                return false;
            }
        });
        renderTotal(fullDataList);
    };

    global.changeOnWayTime = function (e, dataItemId) {
        var onWayTimeValue = e.target.value;
        findTargetValue(fullDataList, function (item) {
            if (item.data.id == dataItemId) {
                item.onWayTime = onWayTimeValue;
                return true;
            } else {
                return false;
            }
        });
        renderTotal(fullDataList);
    };

    function getServerDataValue() {
        var newValues = [];
        var workTimeTotal = 0;
        var onWayTimeTotal = 0;
        var isValid = true;
        var errorMessage = "";
        if (Array.isArray(fullDataList) && fullDataList.length > 0) {
            for (var key = 0; key < fullDataList.length; key++) {
                var dataItem = fullDataList[key];
                if (dataItem.isChecked) {
                    var productsData = getServerProductsData(
                        dataItem.products
                    );

                    var hasProucts =
                        productsData &&
                        Array.isArray(productsData.products) &&
                        productsData.products &&
                        productsData.products.length > 0;

                    if (!hasProucts) {
                        isValid = false;
                        errorMessage =
                            "请选择产品线" +
                            dataItem.data.name +
                            "涉及产品";
                        break;
                    }

                    if (dataItem.onlineTime <= 0) {
                        isValid = false;
                        errorMessage =
                            "请输入产品线" +
                            dataItem.data.name +
                            "的工作时长";
                        break;
                    }

                    newValues.push({
                        id: dataItem.data.id,
                        name: dataItem.data.name,
                        code: dataItem.data.code,
                        onlineTime: dataItem.onlineTime,
                        onWayTime: dataItem.onWayTime,
                        products: productsData.products,
                        productNames: productsData.productNames,
                    });
                    workTimeTotal += Number(dataItem.onlineTime);
                    onWayTimeTotal += Number(dataItem.onWayTime);
                }
            }
        } else {
            return {
                errorMessage: "数据为空",
            };
        }

        if (isValid) {
            if (newValues && newValues.length < 1) {
                return {
                    errorMessage: "保存数据不能为空!",
                };
            } else {
                return {
                    errorMessage: errorMessage,
                    value: JSON.stringify({
                        values: newValues,
                        workTimeTotal: workTimeTotal,
                        onWayTimeTotal: onWayTimeTotal,
                    }),
                };
            }
        } else {
            return {
                errorMessage: errorMessage,
            };
        }
    }

    function getServerProductsData(list) {
        var newProducts = [];
        var newProductNames = [];
        for (var index = 0; index < list.length; index++) {
            var listItem = list[index];
            if (listItem.isChecked) {
                newProducts.push(listItem.data.id);
                newProductNames.push(listItem.data.name);
            }
        }
        return {
            products: newProducts,
            productNames: newProductNames.join(","),
        };
    }

    function buildTableDataList(dataList, value) {
        var newDataList = [];
        let values = value.values;
        for (var i = 0; i < dataList.length; i++) {
            var dataItem = dataList[i];
            var targetValue = findTargetValue(values, function (item) {
                return item.id == dataItem.id;
            });

            var newDataItem = {
                data: dataItem,
                isChecked: false,
                products: buildProducts(dataItem.products, targetValue),
                onlineTime: 0,
                onWayTime: 0,
            };

            if (targetValue) {
                newDataItem.isChecked = true;
                newDataItem.onlineTime = targetValue.onlineTime;
                newDataItem.onWayTime = targetValue.onWayTime;
            }
            newDataList.push(newDataItem);
        }

        return newDataList;
    }

    function buildProducts(products, targetValue) {
        var productList = [];
        if (products && products.length > 0) {
            for (var j = 0; j < products.length; j++) {
                var product = products[j];
                var isChecked = false;
                if (targetValue) {
                    var findTarget = findTargetValue(
                        targetValue.products,
                        function (item) {
                            return item == product.id;
                        }
                    );

                    isChecked = findTarget == null ? false : true;
                }
                productList.push({
                    data: product,
                    isChecked: isChecked,
                });
            }
        }
        return productList;
    }

    function renderTotal(fullDataList) {
        var workTimeTotal = 0;
        var onWayTimeTotal = 0;
        for (var index = 0; index < fullDataList.length; index++) {
            let fullData = fullDataList[index];
            if (fullData.onlineTime) {
                workTimeTotal += Number(fullData.onlineTime);
            }
            if (fullData.onWayTime) {
                onWayTimeTotal += Number(fullData.onWayTime);
            }
        }
        workTimeTotal = !!workTimeTotal ? workTimeTotal.toFixed(1) : 0;
        onWayTimeTotal = !!onWayTimeTotal
            ? onWayTimeTotal.toFixed(1)
            : 0;
        global.document.getElementById(
            "h3c-work-time-total"
        ).innerHTML = workTimeTotal;
        global.document.getElementById(
            "h3c-on-way-time-total"
        ).innerHTML = onWayTimeTotal;
    }

    function renderTable(dataList) {
        var tr = "";
        for (var index = 0; index < dataList.length; index++) {
            var dataItem = dataList[index];
            tr +=
                "<tr data-id=" +
                dataItem.data.id +
                "><td><input type='checkbox' onChange='changeCheckBoxValue(event" +
                ',"' +
                dataItem.data.id +
                "\")' value=" +
                dataItem.data.id +
                (dataItem.isChecked ? " checked/>" : " />") +
                " </td>";

            tr +=
                "<td>" +
                dataItem.data.name +
                "</td><td class='product-td'>";

            $.each(dataItem.products, function (i, product) {
                tr +=
                    "<label><input type='checkbox' onChange='changeCheckBoxValue(event" +
                    "," +
                    dataItem.data.id +
                    ',"' +
                    product.data.id +
                    "\")' value=" +
                    product.data.id +
                    (product.isChecked ? " checked/>" : " />") +
                    product.data.name +
                    "</label>";
            });

            tr +=
                "</td><td><input type='number' min=0 oninput='changeOnlineTime(event" +
                "," +
                dataItem.data.id +
                ")' value=" +
                dataItem.onlineTime +
                " /></td>";

            tr +=
                "</td><td><input type='number' min=0 oninput='changeOnWayTime(event" +
                "," +
                dataItem.data.id +
                ")' value=" +
                dataItem.onWayTime +
                " /></td></tr>";
        }

        global.document.getElementById(
            "h3c-custom-field-tbody"
        ).innerHTML = tr;
    }

    function findTargetValue(value, matcherCallback) {
        var findValue = null;
        if (value && value.length > 0) {
            for (var i = 0; i < value.length; i++) {
                var valueItem = value[i];
                if (matcherCallback(valueItem)) {
                    findValue = valueItem;
                    break;
                } else {
                    findValue = null;
                }
            }
        }
        return findValue;
    }
})(window);

定制移动查看

/************************* html *****************************/
<div id="udesk-custom-field-mobile" class="udesk-custom-field-mobile">
    <ul id="udesk-custom-field-content" class="udesk-custom-field-content"></ul>
    <p class="work-time-total-container">
        时间1总计:
        <span id="udesk-work-time-total">0</span>
    </p>
    <p class="on-way-time-total-container">
        时间2总计:
        <span id="udesk-on-way-time-total">0</span>
    </p>
</div>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>


/************************* css *****************************/
.udesk-custom-field-mobile {
    position: relative;
    font-size: 13px;
    line-height: 1.5;
}

.udesk-custom-field-mobile ul,
.udesk-custom-field-mobile li,
.udesk-custom-field-mobile label,
.udesk-custom-field-mobile input {
    margin: 0;
    padding: 0;
}

.udesk-custom-field-mobile ul {
    list-style: none;
}

.udesk-custom-field-mobile ul > li {
    list-style: none;
}

.udesk-custom-field-mobile .udesk-custom-field-content > li {
    margin-bottom: 20px;
    padding: 10px;
    border: 1px solid #ccc;
}

.udesk-custom-field-mobile .content-item {
    line-height: 32px;
    border-bottom: 1px solid #ccc;
}

.udesk-custom-field-mobile .content-item.productline-header {
    font-weight: 600;
    font-size: 14px;
}

.udesk-custom-field-mobile .content-item label {
    margin-right: 5px;
}

.udesk-custom-field-mobile .content-item .selecte-product-btn {
    float: right;
    font-size: 18px;
    cursor: pointer;
}


/************************* js *****************************/
/*
* initData : 请务必注册此事件,用于数据初始化
* 数据结构 {
* data: 定义该字段结构部分,
* value:界面保存数据
* }
*
* onSavedValueData: 请务必注册此事件,用于数据保存
*
* */

(function (global) {
    var dataSource = [];
    var showValue = [];
    var selectedProductLine = [];
    var selectedProducts = [];

    global.webApiClient.init();
    global.webApiClient.on("initData", function (message) {
        //该事件可接收初始数据

        if (message) {
            dataSource = message.data;
            showValue = message.value.values || [];
        }

        try {
            renderDomData(showValue);
            renderTotal(showValue);
        } catch (error) {
            //数据格式不正确,
        }
    });

    global.webApiClient.on("onDestroyed", function () {
        //该事件用于销毁内存,避免内存泄露
        global.webApiClient.off("init");
        global.webApiClient.off("initData");
        global.webApiClient.off("onDestroyed");

        fullDataList = [];
        dataSource = [];
        showValue = [];
        selectedProductLine = [];
        selectedProducts = [];
    });

    function renderTotal(showValue) {
        var workTimeTotal = 0;
        var onWayTimeTotal = 0;
        for (var index = 0; index < showValue.length; index++) {
            let fullData = showValue[index];
            workTimeTotal += Number(fullData.onlineTime);
            onWayTimeTotal += Number(fullData.onWayTime);
        }
        global.document.getElementById(
            "udesk-work-time-total"
        ).innerHTML = workTimeTotal;
        global.document.getElementById(
            "udesk-on-way-time-total"
        ).innerHTML = onWayTimeTotal;
    }

    function renderDomData(value) {
        var ulData = "";
        for (var index = 0; index < value.length; index++) {
            var valueItem = value[index];

            ulData +=
                "<li data-product-id=" +
                valueItem.id +
                ">" +
                buildItemRenderData(valueItem) +
                "</li>";
        }

        global.document.getElementById(
            "udesk-custom-field-content"
        ).innerHTML = ulData;
    }

    function buildItemRenderData(value) {
        var content = "";
        var targetProductLine = findTargetValue(dataSource, function (
            item
        ) {
            return item.id == value.id;
        });

        if (targetProductLine) {
            let displayProductName = getDisplayProductName(
                targetProductLine.products,
                value.products
            );

            content +=
                "<div class='content-item productline-header'><span>" +
                targetProductLine.name +
                "</span></div>";

            content +=
                "<div class='content-item'><label>详情:</label><span class='content-item-content'>" +
                displayProductName +
                "</span><span class='selecte-product-btn' onclick='onAddProduct(" +
                value.id +
                ")'> > </span></div>";

            content +=
                "<div class='content-item'><label>时间1:</label><input class='content-item-content' type='number' min='0' disabled='disabled' value=" +
                value.onlineTime +
                " /></div>";
            content +=
                "<div class='content-item'><label>时间2:</label><input class='content-item-content' type='number' min='0' disabled='disabled' value=" +
                value.onWayTime +
                " /></div>";
        }
        return content;
    }

    function getDisplayProductName(dataSourceProducts, valueProducts) {
        var valueList = [];
        if (valueProducts && valueProducts.length > 0) {
            for (var index = 0; index < valueProducts.length; index++) {
                var targetProductData = findTargetValue(
                    dataSourceProducts,
                    function (item) {
                        return item.id == valueProducts[index];
                    }
                );
                if (targetProductData) {
                    valueList.push(targetProductData.name);
                }
            }
        }

        if (valueList && valueList.length > 0) {
            return valueList.join(",");
        } else {
            return "- -";
        }
    }

    function findTargetValue(value, matcherCallback) {
        var findValue = null;
        if (value && value.length > 0) {
            for (var i = 0; i < value.length; i++) {
                var valueItem = value[i];
                if (matcherCallback(valueItem)) {
                    findValue = valueItem;
                    break;
                } else {
                    findValue = null;
                }
            }
        }
        return findValue;
    }

})(window);

定制移动编辑

/************************* html *****************************/
<div id="udesk-custom-field-mobile" class="udesk-custom-field-mobile">
    <ul id="udesk-custom-field-content" class="udesk-custom-field-content"></ul>
    <p class="work-time-total-container">
        时间1总计:
        <span id="udesk-work-time-total">0</span>
    </p>
    <p class="on-way-time-total-container">
        时间2总计:
        <span id="udesk-on-way-time-total">0</span>
    </p>
    <button type="button" class="add-product-line-btn" onclick="addProductionLine()">
        新增分类
    </button>
    <div id="add-data-modal-mask" class="add-data-modal-mask">
        <div class="custom-field-modal-wrap">
            <ul id="add-data-container"></ul>
            <div class="actions-content">
                <button type="button" class="save-btn" onClick="saveSelectedData()">
                    确定
                </button>
                <button type="button" class="cancel-btn" onClick="closedModal()">
                    取消
                </button>
            </div>
        </div>
    </div>
</div>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>


/************************* css *****************************/
.udesk-custom-field-mobile {
    position: relative;
    font-size: 13px;
    line-height: 1.5;
}

.udesk-custom-field-mobile ul,
.udesk-custom-field-mobile li,
.udesk-custom-field-mobile label,
.udesk-custom-field-mobile input {
    margin: 0;
    padding: 0;
}

.udesk-custom-field-mobile ul {
    list-style: none;
}

.udesk-custom-field-mobile ul > li {
    list-style: none;
}

.udesk-custom-field-mobile .udesk-custom-field-content > li {
    margin-bottom: 20px;
    padding: 10px;
    border: 1px solid #ccc;
}

.udesk-custom-field-mobile .content-item {
    line-height: 32px;
    border-bottom: 1px solid #ccc;
}

.udesk-custom-field-mobile .content-item.productline-header {
    font-weight: 600;
    font-size: 14px;
}

.udesk-custom-field-mobile
    .content-item.productline-header
    .delete-product-line-btn {
    float: right;
    color: red;
    font-weight: normal;
    cursor: pointer;
}

.udesk-custom-field-mobile .content-item label {
    margin-right: 5px;
}

.udesk-custom-field-mobile .content-item .selecte-product-btn {
    float: right;
    font-size: 18px;
    cursor: pointer;
}

.add-product-line-btn {
    width: 100%;
    margin: 10px 0;
    padding: 7px 0;
    text-align: center;
    border: 1px solid #3c7eff;
    outline: none;
    cursor: pointer;
    background-color: #3c7eff;
    color: #fff;
}

.add-data-modal-mask {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 1020;
    background: rgba(0, 0, 0, 0.1);
    display: none;
}

.custom-field-modal-wrap {
    overflow: hidden;
    overflow-y: auto;
    width: 60%;
    height: 70%;
    max-height: 70%;
    margin: 50px 20%;
    padding: 20px;
    background: #fff;
}

.custom-field-modal-wrap .modal-content-item {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    line-height: 28px;
    border-bottom: 1px solid #ccc;
}

.custom-field-modal-wrap .modal-content-item .modal-content-item-right {
    width: 20px;
    line-height: 28px;
}

.actions-content {
    position: relative;
    width: 130px;
    margin: 20px auto;
    cursor: pointer;
}

.actions-content > .save-btn,
.actions-content > .cancel-btn {
    display: inline-block;
    height: 32px;
    font-size: 14px;
    line-height: 1;
    margin: 0 8px;
    padding: 6px 7px;
    cursor: pointer;
    border-width: 1px;
    outline: none;
    border-radius: 3px;
}

.custom-field-modal-wrap .save-btn {
    background-color: #3c7eff;
    color: #fff;
    border-color: #3c7eff;
}


/************************* js *****************************/
/*
* initData : 请务必注册此事件,用于数据初始化
* 数据结构 {
* data: 定义该字段结构部分,
* value:界面保存数据
* }
*
* onSavedValueData: 请务必注册此事件,用于数据保存
*
* */

(function (global) {
    var dataSource = [];
    var showValue = [];
    var selectedProductLine = [];
    var selectedProducts = {};

    global.webApiClient.init();
    global.webApiClient.on("initData", function (message) {
        //该事件可接收初始数据
        if (message) {
            dataSource = message.data;
            showValue = message.value.values || [];
        }


        try {
            renderDomData(showValue);
            renderTotal(showValue);
        } catch (error) {
            //数据格式不正确,
        }
    });


    global.webApiClient.on("onSavedValueData", function () {
        var workTimeTotal = 0;
        var onWayTimeTotal = 0;
        for (var index = 0; index < showValue.length; index++) {
            var fullDataItem = showValue[index];
            workTimeTotal += Number(fullDataItem.onlineTime);
            onWayTimeTotal += Number(fullDataItem.onWayTime);
        }

        //该事件用于保存数据
        global.webApiClient.saveValueData(JSON.stringify({
            values: [].concat(showValue || []),
            workTimeTotal: workTimeTotal,
            onWayTimeTotal: onWayTimeTotal
        }));
    });

    global.webApiClient.on("onDestroyed", function () {
        //该事件用于销毁内存,避免内存泄露
        global.webApiClient.off("init");
        global.webApiClient.off("initData");
        global.webApiClient.off("onSavedValueData");
        global.webApiClient.off("onDestroyed");

        global.changeOnlineTime = null;
        global.changeOnWayTime = null;
        global.addProductionLine = null;
        global.onAddProduct = null;
        global.deleteProductLine = null;
        fullDataList = [];
    });

    function renderTotal(showValue) {
        var workTimeTotal = 0;
        var onWayTimeTotal = 0;
        for (var index = 0; index < showValue.length; index++) {
            let fullData = showValue[index];
            if (fullData.onlineTime) {
                workTimeTotal += Number(fullData.onlineTime);
            }
            if (fullData.onWayTime) {
                onWayTimeTotal += Number(fullData.onWayTime);
            }
        }
        workTimeTotal = !!workTimeTotal ? workTimeTotal.toFixed(1) : 0;
        onWayTimeTotal = !!onWayTimeTotal ? onWayTimeTotal.toFixed(1) : 0;
        global.document.getElementById(
            "udesk-work-time-total"
        ).innerHTML = workTimeTotal;
        global.document.getElementById(
            "udesk-on-way-time-total"
        ).innerHTML = onWayTimeTotal;
    }

    function renderDomData(value) {
        var ulData = "";
        for (var index = 0; index < value.length; index++) {
            var valueItem = value[index];

            ulData +=
                "<li data-product-id=" +
                valueItem.id +
                ">" +
                buildItemRenderData(valueItem) +
                "</li>";
        }

        global.document.getElementById(
            "udesk-custom-field-content"
        ).innerHTML = ulData;
    }

    function buildItemRenderData(value) {
        var content = "";
        var targetProductLine = findTargetValue(dataSource, function (
            item
        ) {
            return item.id == value.id;
        });

        if (targetProductLine) {
            let displayProductName = getDisplayProductName(
                targetProductLine.products,
                value.products
            );

            content +=
                "<div class='content-item productline-header'><span>" +
                targetProductLine.name +
                "</span><span class='delete-product-line-btn' onclick='deleteProductLine(" +
                value.id +
                ")'>删除</span></div>";

            content +=
                "<div class='content-item'><label>详情:</label><span class='content-item-content'>" +
                displayProductName +
                "</span><span class='selecte-product-btn' onclick='onAddProduct(" +
                value.id +
                ")'> > </span></div>";
            content +=
                "<div class='content-item'><label>时间1:</label><input class='content-item-content' type='number' min='0' oninput='changeOnlineTime(event" +
                "," +
                value.id +
                ")' value=" +
                value.onlineTime +
                " /></div>";

            content +=
                "<div class='content-item'><label>时间2:</label><input class='content-item-content' type='number' min='0' oninput='changeOnWayTime(event" +
                "," +
                value.id +
                ")' value=" +
                value.onWayTime +
                " /></div>";

        }
        return content;
    }

    function getDisplayProductName(dataSourceProducts, valueProducts) {
        var valueList = [];
        if (valueProducts && valueProducts.length > 0) {
            for (var index = 0; index < valueProducts.length; index++) {
                var targetProductData = findTargetValue(
                    dataSourceProducts,
                    function (item) {
                        return item.id == valueProducts[index];
                    }
                );
                if (targetProductData) {
                    valueList.push(targetProductData.name);
                }
            }
        }

        if (valueList && valueList.length > 0) {
            return valueList.join(",");
        } else {
            return "- -";
        }
    }

    function findTargetValue(value, matcherCallback) {
        var findValue = null;
        if (value && value.length > 0) {
            for (var i = 0; i < value.length; i++) {
                var valueItem = value[i];
                if (matcherCallback(valueItem)) {
                    findValue = valueItem;
                    break;
                } else {
                    findValue = null;
                }
            }
        }
        return findValue;
    }

    global.changeOnlineTime = function (e, dataItemId) {
        var onlineTimeValue = e.target.value;
        findTargetValue(showValue, function (item) {
            if (item.id == dataItemId) {
                item.onlineTime = onlineTimeValue;
                return true;
            } else {
                return false;
            }
        });
        renderTotal(showValue);
    };

    global.changeOnWayTime = function (e, dataItemId) {
        var onWayTimeValue = e.target.value;
        findTargetValue(showValue, function (item) {
            if (item.id == dataItemId) {
                item.onWayTime = onWayTimeValue;
                return true;
            } else {
                return false;
            }
        });
        renderTotal(showValue);
    };

    global.addProductionLine = function () {
        var li = "";
        for (var index = 0; index < dataSource.length; index++) {
            var dataItem = dataSource[index];
            var hadProductLine = findTargetValue(showValue, function (
                value
            ) {
                return value.id == dataItem.id;
            });

            li +=
                "<li class='modal-content-item'><div>" +
                dataItem.name +
                "</div><input class='modal-content-item-right' type='radio' name='product-line' onChange='selectedProductLine(event" +
                "," +
                dataItem.id +
                ',"' +
                dataItem.name + '","' +
                dataItem.code +
                "\")' value='" +
                dataItem.id +
                (hadProductLine ? "' disabled/>" : "' />") +
                "</li>";
        }

        global.document.getElementById(
            "add-data-container"
        ).innerHTML = li;

        global.document.getElementById(
            "add-data-modal-mask"
        ).style.display = "block";
    };

    global.onAddProduct = function (productLineId) {
        var targetProductData = findTargetValue(dataSource, function (
            item
        ) {
            return item.id == productLineId;
        });

        if (targetProductData) {
            var showProductLine = findTargetValue(showValue, function (
                value
            ) {
                return value.id == productLineId;
            });

            var hadProducts = [];
            if (showProductLine) {
                if (selectedProducts == null) {
                    selectedProducts = {};
                }

                var productNames = (typeof showProductLine.productNames == "string" && showProductLine.productNames && showProductLine.productNames.split(","));
                selectedProducts[productLineId] = {
                    products: showProductLine.products || [],
                    productNames: productNames || []
                };

                hadProducts = [].concat(
                    selectedProducts[productLineId].products
                );
            }

            var li = "";
            var productNames = [];
            for (
                var index = 0;
                index < targetProductData.products.length;
                index++
            ) {
                var product = targetProductData.products[index];
                var isChecked =
                    hadProducts.indexOf(product.id) > -1 ? true : false;
                if (isChecked) {
                    productNames.push(product.name);
                }

                li +=
                    "<li class='modal-content-item'><div>" +
                    product.name +
                    "</div><input class='modal-content-item-right' type='checkbox' onChange='changeCheckBoxValue(event" +
                    "," +
                    targetProductData.id +
                    ',"' +
                    product.id + '","' +
                    product.name +
                    "\")' value=" +
                    product.id +
                    (isChecked ? " checked/>" : " />") +
                    "</li>";
            }

            global.document.getElementById(
                "add-data-container"
            ).innerHTML = li;
            global.document.getElementById(
                "add-data-modal-mask"
            ).style.display = "block";
            hadProducts = [];
        }
    };

    global.selectedProductLine = function (e, productLineId, productName, productCode) {
        var newValue = {
            id: productLineId,
            code: productCode,
            name: productName,
            products: [],
            productNames: "",
            onlineTime: 0,
            onWayTime: 0,
        };
        if (!Array.isArray(selectedProductLine)) {
            selectedProductLine = [];
        }
        selectedProductLine = [newValue];
    };
    global.changeCheckBoxValue = function (
        e,
        productLineId,
        productId,
        productName
    ) {
        if (selectedProducts == null) {
            selectedProducts = {};
        }

        if (selectedProducts[productLineId] == null) {
            selectedProducts[productLineId] = {
                products: [productId],
                productNames: [productName]
            };
        } else {
            var targetIndex = selectedProducts[productLineId].products.indexOf(
                productId
            );

            if (targetIndex == -1) {
                selectedProducts[productLineId].products.push(productId);
                selectedProducts[productLineId].productNames.push(productName);
            } else {
                selectedProducts[productLineId].products.splice(targetIndex, 1);
                selectedProducts[productLineId].productNames.splice(targetIndex, 1);
            }
        }
    };

    global.deleteProductLine = function (productLineId) {
        var containerElement = global.document.getElementById(
            "udesk-custom-field-content"
        );
        var removeNode = global.document.querySelector(
            "[data-product-id='" + productLineId + "']"
        );
        containerElement.removeChild(removeNode);
        for (var j = 0; j < showValue.length; j++) {
            var showValueItem = showValue[j];
            if (showValueItem.id == productLineId) {
                showValue.splice(j, 1);
                break;
            }
        }
    };

    global.saveSelectedData = function () {
        if (selectedProductLine && selectedProductLine.length > 0) {
            var newValue = selectedProductLine[0];
            var li = buildItemRenderData(newValue);

            var liDom = global.document.createElement("li");
            liDom.setAttribute("data-product-id", newValue.id);
            liDom.innerHTML = buildItemRenderData(newValue);

            global.document
                .getElementById("udesk-custom-field-content")
                .appendChild(liDom);
            if (showValue == null) {
                showValue = [];
            }
            showValue.push(newValue);

            clearModalData();
        } else if (selectedProducts) {
            findTargetValue(showValue, function (item) {
                var targetSelectedProducts = selectedProducts[item.id];
                if (targetSelectedProducts &&
                    targetSelectedProducts.products
                ) {
                    item.products = targetSelectedProducts.products;
                    item.productNames = targetSelectedProducts.productNames && targetSelectedProducts.productNames.length > 0 ? targetSelectedProducts.productNames.join(",") : "";

                    var li =
                        "<li data-product-id=" +
                        item.id +
                        ">" +
                        buildItemRenderData(item) +
                        "</li>";

                    var targetDom = global.document.querySelector(
                        "[data-product-id='" + item.id + "']"
                    );
                    targetDom.outerHTML = li;
                    clearModalData();
                    return true;
                }
                return false;
            });

        } else {
            alert("请先选择");
        }
    };
    global.closedModal = function () {
        clearModalData();
    };
    function clearModalData() {
        global.document.getElementById("add-data-container").innerHTML =
            "";
        global.document.getElementById(
            "add-data-modal-mask"
        ).style.display = "none";

        selectedProductLine = [];
        selectedProducts = null;
    }
})(window);

demo展示