[erpnext] WebForm 提交前添加 Loading

· Python

erpnext 默认 Web Form 页面提交时很呆,没有 Loading 提示。

于是,为了实现这个功能,发现以下方法不能实现:

  1. frappe.ui.form.on() 这个方法只能在 desk 后台调用
  2. frappe.web_form.on() 这个方法无法触发 hook(如 before_savevalidate等)
  3. frappe.web_form.freeze() 无法展示任何元素

发现,frappe.web_form.save() 方法可以被触发,但是 validate() 方法被内嵌(private),并没有暴露出来。于是重写

// 文件路径: your_app/public/js/upload_file_custom.js

(function () {
    // 防止多次加载
    if (window.webform_loading_script_loaded) return;
    window.webform_loading_script_loaded = true;

    document.addEventListener("DOMContentLoaded", function () {
        console.log("Custom Web Form Loading Script Loaded");
        // 显示 Loading
        window.show_loading = function (msg = "Submitting...") {
            let el = document.getElementById("webform-loading");
            if (!el) {
                el = document.createElement("div");
                el.id = "webform-loading";
                el.innerHTML = `
            <div class="loading-overlay">
                <div class="spinner"></div>
                <div class="loading-msg">${msg}</div>
            </div>
        `;
                const style = document.createElement("style");
                style.innerHTML = `
            .loading-overlay {
                position: fixed;
                top: 0; left: 0; right: 0; bottom: 0;
                background: rgba(0,0,0,0.5);
                display: flex;
                flex-direction: column;
                justify-content: center;
                align-items: center;
                z-index: 9999;
            }
            .loading-msg {
                color: #fff;
                margin-top: 16px;
                font-size: 20px;
                text-align: center;
            }
            .spinner {
                width: 30px;
                height: 30px;
                border: 3px solid #aaa;
                border-top: 3px solid #fff;
                border-radius: 50%;
                animation: spin 1s linear infinite;
            }
            @keyframes spin {
                0% { transform: rotate(0deg); }
                100% { transform: rotate(360deg); }
            }
        `;
                document.head.appendChild(style);
                document.body.appendChild(el);
            } else {
                el.querySelector(".loading-msg").innerText = msg;
                el.style.display = "flex";
            }
        };

        // 隐藏 Loading
        window.hide_loading = function () {
            const el = document.getElementById("webform-loading");
            if (el) el.style.display = "none";
        };

        // 覆盖 Web Form save 方法(添加 Loading 效果)
        function hook_webform_save() {
            if (frappe.web_form && frappe.web_form.save) {
                // 替换原有 save 方法,添加 Loading 功能
                frappe.web_form.save = function (...args) {
                    // console.log("Custom Web Form Save Hooked");
                    const is_new = this.is_new;

                    // 调用实例方法获取表单值
                    const doc_values = this.get_values(this.allow_incomplete);

                    // 验证失败或无值,直接返回
                    if (!doc_values || window.saving) return false;

                    // console.log("Web Form Values:", doc_values);

                    // 验证通过 → 显示 Loading
                    show_loading("Submitting...");
                    const btn = document.querySelector(".btn-save");
                    if (btn) btn.disabled = true;

                    window.saving = true;
                    frappe.form_dirty = false;

                    frappe.call({
                        type: "POST",
                        method: "frappe.website.doctype.web_form.web_form.accept",
                        args: { data: doc_values, web_form: this.name, for_payment: false },
                        callback: (response) => {
                            if (!response.exc) this.handle_success(response.message);
                            frappe.web_form.events.trigger("after_save");
                        },
                        always: function () {
                            window.saving = false;
                            hide_loading();
                            if (btn) btn.disabled = false;
                        }
                    });

                    return false;
                };
            } else {
                // 延迟尝试,直到 frappe.web_form 被定义
                setTimeout(hook_webform_save, 50);
            }
        }

        // 执行 Hook 方法
        hook_webform_save();

    });

})();

实现效果:

发表评论