[入門到吐槽系列] Webix 10分鐘入門 二 表單Form的使用

發表於2022-02-22

前言

繼續接著上一篇的webix入門:https://www.cnblogs.com/zc22/p/15912342.html。今天完成剩下兩個最重要的控制元件,表單和表格的使用。掌握了這兩個,整個Webix就入門完成了,就會進入吐槽模式。

 

Webix 表單Form的使用

表單的初始化佈局

本章節介紹表單的獲取、設定、驗證;表單控制元件的資料繫結。

表單和一般控制元件,最大的區別,就是提供了批量操作。包括批量的設定取值、批量輸入驗證。先建立一個表單:

    webix.ui({
        id: 'webix_domasticparts',
        rows: [
            {
                id: 'form', view: 'form', borderless: true,
                elements: [
                    {
                        cols: [
                            { view: 'label', label: '無型號:', autowidth: true, },
                            { id: 'lb_title', name: 'lb_title', view: 'label', label: '0', width: 50, align: 'center', },
                            { id: 'sh_select', name: 'sh_select', view: 'switch', onLabel: "開啟", offLabel: '關閉', width: 120, value: 0 },
                            {},
                        ],
                    }, {
                        cols: [
                            { id: 'txt_input', name: "txt_input", view: 'text', width: 200, placeholder: '商家編碼', },
                            { id: 'btn_update', view: 'button', value: '設定', width: 150, },
                            {},
                        ],
                    },
                ],
            },
        ]
    });

效果演示:

程式碼解釋:

  • 程式碼建立了個基本行佈局,第一行放入一個view:form,表單控制元件。 
  • 表單內容預設是行佈局,使用elements,也可以使用rows,這裡需要吐槽作者,設計API的時候任意亂來,一會element,一會cell,一會rows/columns,一會options。往後這類非常low的問題會越來越多,先預告下。
  • 表單的控制元件,如果需要通過方法返回集合,需要宣告name屬性。

 

表單資料獲取與設定

接下來就是表單繫結事件,實現設定、更新,先看程式碼:

    $$('btn_update').attachEvent('onItemClick', function () {
        var values = $$('form').getValues();
        console.log(values);

        values.lb_title = values.txt_input;
        values.sh_select = 1;
        $$('form').setValues(values, true);
    });

效果顯示:

程式碼解釋:

在輸入框輸入hello,點選設定,標籤會變成同樣內容,開關開啟。

  • 獲取和設定都是通過getValues, setValues控制。
  • 表單的控制元件通過attachEvent繫結點選事件

 

表單輸入校驗

繫結驗證方法很多,我們只分享最乾淨的一種:

    webix.ui({
        id: 'webix_domasticparts',
        rows: [
            {
                id: 'form', view: 'form', borderless: true,
                elements: [
                    {
                        cols: [
                            { view: 'label', label: '無型號:', autowidth: true, },
                            { id: 'lb_title', name: 'lb_title', view: 'label', label: '0', width: 50, align: 'center', },
                            { id: 'sh_select', name: 'sh_select', view: 'switch', onLabel: "開啟", offLabel: '關閉', width: 120, value: 0 },
                            {},
                        ],
                    }, {
                        cols: [
                            { id: 'txt_input', name: "txt_input", view: 'text', width: 200, placeholder: '商家編碼', },
                            { id: 'btn_update', view: 'button', value: '設定', width: 150, },
                            {},
                        ],
                    },
                ],
                rules: {
                    txt_input: webix.rules.isNumber,
                },
            },
        ]
    });

    $$('btn_update').attachEvent('onItemClick', function () {
        if (!$$('form').validate()) {
            webix.alert('輸入錯誤')
            return;
        }

        var values = $$('form').getValues();
        console.log(values);

        values.lb_title = values.txt_input;
        values.sh_select = 1;
        $$('form').setValues(values, true);
    });

效果演示:

程式碼解釋:

  • 在表單的elements節點下方,新增rules節點,裡面鍵值對繫結控制元件的驗證。webix預設內建了:
    • isNotEmpty
    • isNumber
    • isEmail
    • isChecked
    • 更多參考:https://docs.webix.com/desktop__data_validation.html#validationrules
  • 當然,rules節點的配置可以是自定義方法。具體不詳細解釋了。
  • 呼叫form的validate方法,通過返回值判斷輸入是否正確。

 

小結

現在我們可以製作一個比較複雜的表單了,裡面包含了上傳、匯出等操作。先看程式碼:

    <style>
        .warn_label .webix_el_box {
            color: red;
            font-size: 18px;
            font-weight: bold;
        }

        .default_label .webix_el_box {
            font-weight: bold;
        }

        a {
            color: #333;
            text-decoration: none;
        }
    </style>
    <script type="text/javascript" charset="utf-8">

        const aadp_mixmode_cb = [
            { id: 1, value: '自營模式', },
            { id: 2, value: '淘寶國產代發', },
            { id: 3, value: '淘寶全代發', },
            { id: 4, value: '混採模式', },
        ];

        webix.ui({
            id: 'webix_domasticparts',
            rows: [
                {
                    id: 'aadp_form', view: 'form', borderless: true,
                    elements: [
                        {
                            cols: [
                                { view: 'label', label: '無型號:', autowidth: true, },
                                { id: 'aadp_unknownpart', name: 'aadp_unknownpart', view: 'label', label: '0', width: 50, align: 'center', css: 'warn_label', },
                                { view: 'label', label: '無顏色:', autowidth: true, },
                                { id: 'aadp_unknowncolor', name: 'aadp_unknowncolor', view: 'label', label: '0', width: 50, align: 'center', css: 'warn_label', },
                                { view: 'label', label: '無庫存:', autowidth: true, },
                                { id: 'aadp_unknownstock', name: 'aadp_unknownstock', view: 'label', label: '0', width: 50, align: 'center', css: 'warn_label', },

                                { view: 'label', label: '零件總數:', autowidth: true, },
                                { id: 'aadp_totalpart', name: 'aadp_totalpart', view: 'label', label: '0', width: 50, align: 'center', css: 'default_label', },
                                { view: 'label', label: '總價格:', autowidth: true, },
                                { id: 'aadp_totalprice', name: 'aadp_totalprice', view: 'label', label: '0', width: 180, align: 'center', css: 'default_label', },
                                { view: 'label', label: '代發價格:', autowidth: true, },
                                { id: 'aadp_totaldiscount', name: 'aadp_totaldiscount', view: 'label', label: '0', width: 180, align: 'center', css: 'default_label', },

                                { id: 'aadp_unknownpart_cb', name: 'aadp_unknownpart_cb', view: 'switch', onLabel: "未知零件", offLabel: '未知零件', width: 120, value: 0 },
                                { id: 'aadp_unknowncolor_cb', name: 'aadp_unknowncolor_cb', view: 'switch', onLabel: "未知顏色", offLabel: '未知顏色', width: 120, value: 0 },
                                { id: 'aadp_unknownstock_cb', name: 'aadp_unknownstock_cb', view: 'switch', onLabel: "無庫存", offLabel: '無庫存', width: 120, value: 0 },
                                {},
                            ],
                        },
                        {
                            cols: [
                                { id: 'aadp_mixmode_cb', name: "aadp_mixmode_cb", view: 'richselect', label: '商家選擇', width: 250, options: [], },
                                { id: 'aadp_uploadsellercode', name: "aadp_uploadsellercode", view: 'text', width: 200, placeholder: '商家編碼', },
                                { id: 'aadp_reupload', view: 'uploader', value: '上傳零件表', inputName: 'file', width: 150, css: 'webix_normal' },
                                { id: 'aadp_download', view: 'button', value: '匯出零件表', autowidth: true, },
                                { id: 'aadp_createorder', view: 'button', value: '生成訂單', autowidth: true, css: 'webix_primary' },
                                { id: 'aadp_designcode', name: "aadp_designcode", view: 'text', width: 250, label: '零件編碼', labelAlign: 'right', },
                                { id: 'aadp_search', view: 'button', value: '搜尋', autowidth: true, },
                                {},
                            ],
                        },
                    ],
                },
            ]
        });

        // 設定載入中特效
        webix.extend($$("webix_domasticparts"), webix.ProgressBar);

        // 程式碼設定下拉框
        $$('aadp_mixmode_cb').define('options', aadp_mixmode_cb);
        $$('aadp_mixmode_cb').refresh();
        $$('aadp_mixmode_cb').setValue(2);


        // 程式碼設定上傳元件
        $$('aadp_reupload').define('formData', function () {
            var formval = $$('aadp_form').getValues();
            return {
                mixmode: formval.aadp_mixmode_cb,
                sellercode: formval.aadp_uploadsellercode,
            };
        });
        $$('aadp_reupload').define('upload', 'http://localhost:8001/sso/page/upload');
        $$('aadp_reupload').refresh();
        $$('aadp_reupload').attachEvent('onFileUploadError', function (file, err) {
            console.error(file, err);
            $$('webix_domasticparts').hideProgress();
            // 做一些補救措施,然後自動再上傳
            $$('aadp_reupload').send();
        });
        $$('aadp_reupload').attachEvent('onAfterFileAdd', function () {
            $$('webix_domasticparts').showProgress({
                type: 'icon',
                delay: 1000,
            });
        });
        $$('aadp_reupload').attachEvent('onFileUpload', function (file, response) {
            console.log(response);
        });

        // 下載檔案
        $$('aadp_download').attachEvent('onItemClick', function (e) {
            window.open(createRequestUrl('/wxss/moc/analysis/auth_exportbricklinkdomastic?orderNo=' + orderno));
        });


    </script>

服務端程式碼:

    @RequestMapping("/upload")
    @ResponseBody
    public ApiResponseBody upload(String name, @RequestParam("file") MultipartFile file) {
        return ApiResponseBody.success(file.getOriginalFilename().toLowerCase());
    }    // 管理員國產零件匯出
    @GetMapping("/analysis/auth_exportbricklinkdomastic")
    @ResponseBody
    public ApiResponseBody authExportBricklinkDomastic(String orderNo, HttpServletResponse response) throws Exception {
     // 這裡新增下載的資料組裝,filename等
try { response.setHeader("content-type", "application/octet-stream"); response.setContentType("application/octet-stream"); // 下載檔案能正常顯示中文 response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); OutputStream os = response.getOutputStream(); // 資料寫入返回 System.out.println("Download successfully!"); return null; } catch (Exception e) { System.out.println("Download failed!"); return null; } finally { wb.close(); } }

效果如下:

 Label關鍵點說明:

  • 如果要修改label的表現形式,需要增加css的屬性,這是個非常常用的功能,但是這裡有個巨坑,需要按照webix控制元件皮膚去改,改成:.warn_label .webix_el_box,原因是如果沒這個css宣告,會被官方的css覆蓋了,導致一直改不成功。
    • 再分享一個巨坑,一旦用了自己的css屬性,設定了字型的大小等,會導致label的自動調整佈局resize() 全部失效,對不齊。因為官方用了預設的css去計算label的部局尺寸。
    • 從這裡開始,大家應該可以理解到這個webix框架會有多爛。繼續。

 Uploader關鍵點說明:

  • 使用上傳控制元件,宣告一個view:'uploader'的控制元件,他本質和button基本一樣。同時要宣告上傳二進位制的名字inputName: 'file',後臺才能用file去接:@RequestParam("file")
  • 動態繫結上傳地址,需要使用define('upload'這個最原始的方法,再refresh()。又是個無語的地方。
  • 同理,動態修改提交的formData,也需要使用define,然後使用方法返回執行時資料。
  • 上傳元件如果需要程式碼再次提交,使用send()方法。比如上傳的時候,服務端返回登入超時,那麼前端再做一次登入,然後直接再次上傳。
  • 上傳元件幾個關鍵事件:onAfterFileAdd、onFileUploadError、onFileUpload

其他說明:

  • 如果要使用載入遮罩,需要擴充套件控制元件webix.extend,對控制元件增加webix.ProgressBar,然後就可以呼叫對應的showProgress, hideProgress。這是個透明的懸浮窗,可以遮擋使用者操作行為,非常常用,但是webix竟然特麼沒有提供基礎loading方法。
  • 動態修改下拉框的資料,同樣要非常弱智的使用define('options的方式,修改控制元件配置,再refresh。沒有提供一般性的繫結資料介面。
  • 要實現檔案下載,建議使用window.open的方式。不然報錯或者在iframe下,會有各種奇怪不爽問題。

 

由於webix的坑實在多如天上繁星,接下里的datatable的坑數會翻倍。所以我們們新開一篇繼續。今天的分享完畢!

 

本文所有程式碼在這裡可以下載:

連結: https://pan.baidu.com/s/1jRk6Zo6YtIvNza-8I6rOtA

提取碼: eq2k

也歡迎大家關注我們們公眾號:辰同學技術 微信公眾號,同樣會分享各種技術10分鐘從入門到吐槽:

 

相關文章