erpnext 默认 Web Form 页面提交时很呆,没有 Loading 提示。
于是,为了实现这个功能,发现以下方法不能实现:
frappe.ui.form.on()这个方法只能在 desk 后台调用frappe.web_form.on()这个方法无法触发 hook(如before_save、validate等)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();
});
})();
实现效果: