JavaScript通用表单处理与Ajax交互组件实现

作者:小括号

时间:2026-10-29

浏览:265

本文介绍一个基于JavaScript和jQuery的通用表单处理组件。该组件封装了Ajax同步提交、按钮动作注入、复选框状态同步、日期与时间选择器初始化等功能。通过自定义验证效果和拦截器,实现表单数据的自动收集、错误提示及页面局部刷新,适用于需要统一交互逻辑的Web应用。

文章目录

通用表单处理组件概述

这是一个用于处理各类表单、链接及按钮交互的通用JavaScript组件。

具体实现代码如下:

/**
* 通用表单处理 JavaScript 脚本
* 作者:Anthony.chen
 */
Ajax 请求配置与按钮动作处理/**
 * 将按钮动作 [btn_action] 数据推入表单
 * 如果存在预执行脚本,则先运行该脚本
 */
"use strict";
// 默认情况下所有 Ajax 请求同步执行
var ajaxSynchronized = true;
// 默认情况下所有 Ajax 请求完成后自动解除阻塞
var ajaxAutoUnblock = true;
var ajax_action_button = function (e){
 var btn = $(this);
 // 添加预执行脚本
 var pre_script;
 if(pre_script = btn.attr("pre_script")){
 var ret = eval(pre_script);
 if(ret==false){
 return false;
 }
 }
 var btn_action = btn.attr('value');
 if(btn_action){
 $(this).closest('form').data('btn_action',
 { name:'btn_action',value:btn_action }
 );
 }
};
表单数据更新与特殊控件处理/**
 * 使用表单元素、键和值更新 FormElement 中的额外表单数据
 */
var ajax_update_post_data = function(formEle, k, v){
 var form = $(formEle);
 var post_data = form.data('post_data');
 if( post_data == undefined ){
 post_data = {};
 }
if( v == undefined ){
 delete post_data[k];
 }else{
 post_data[k] = v;
 }
 form.data('post_data',post_data);
 return true;
};
/**
 * 布尔复选框是特殊的复选框,需要保持未选中状态
 * 并通过 Ajax 表单提交,该表单位于父级中
 */
var bool_checkbox = function(){
 var ipt = $(this);
 var formEle = ipt.closest("form");
 var _check = ipt.prop("checked");
 if(_check){
 ajax_update_post_data(formEle,ipt.attr('name'));
 }else{
 ajax_update_post_data(formEle,ipt.attr('name'),'f');
 }
};
数字、日期与时间选择器初始化/**
 * 初始化数字旋转器
 */
var spin_number = function(){
 var spin = $(this);
 var config = { 
 lock:true,
 imageBasePath: '{webpath}/css/images/spin/',
 btnCss: null,
 txtCss: null,
 btnClass:'spin_btn',
 };
 var interval = spin.attr('interval');
 if(interval){
 config.interval = interval;
 }else{
 config.interval = 1;
 }
var min = spin.attr('min');
 if( min ){
 config.min = min;
 }
var max = spin.attr('max');
 if( max ){
 config.max = max;
 }
spin.spin(config);
 return true;
};
/**
 * 初始化日期输入框
 */
var date_input = function(){
 var ipt = $(this);
 var config = {
 offset:[4,0],
 selectors:true,
 lang: '{lang}',
 firstDay : 1,
 format: 'yyyy-mm-dd',
 };
 var min = ipt.attr('min');
 if( min ){
 config.min = min;
 }
var min = ipt.attr('min');
 if( min ){
 config.min = min;
 }
ipt.dateinput(config);
 return true;
};
/**
 * 初始化时间选择器
 */
var time_picker = function(){
 var ipt = $(this);
 var config = { };
var step = ipt.attr("step");
 if( step ){
 config.step = step;
 }
ipt.timePicker( config );
 return true;
};
Ajax 表单提交核心逻辑/**
 * 通用 Ajax 表单提交函数
 * 如果设置了 btn_action,则将数据添加到表单
 * 如果设置了 returi,则重定向到 returi
 * 如果设置了 reload,则重新加载
 * 否则显示阻塞消息
 *
 * 表单将被验证。
 */
var ajax_form_post = function(e){
 var form = $(this);
 var pre_script;
 if(pre_script = form.attr("pre_script")){
 var ret = eval(pre_script);
 if(ret==false){
 return false;
 }
 }
 var errHint = form.find(".formError").first();
 if(errHint.size() == 0){
 errHint = $("#pageError");
 }
 errHint.text('').hide();
// 清理页面错误
 if(!e.isDefaultPrevented()){
 // 隐藏所有 .formError
 $.blockUI({ message:"{__('L_PROCESSING')}" });
 var formArray = form.serializeArray();
 var btn_action_data;
 var btn_action;
 if(btn_action_data = form.data('btn_action')){
 formArray.push(btn_action_data);
 form.removeData('btn_action');
 btn_action = btn_action_data.value;
 }else{
 btn_action = '';
 }
console.log('btn action:'+btn_action);
 // 添加额外数据
 var post_data;
 if(post_data = form.data('post_data')){
 for (var k in post_data ){
 //if post_data[k] is array,need more to do
 formArray.push( { name:k ,value: post_data[k] } );
 }
 form.removeData('post_data');
 }
 $.post(form.attr('action'), formArray,function(json){
 if($(window).data('blockUI.isBlocked') == 1){
 $.unblockUI();
 }
 if(json.code === true){
 var returi = "";
 var retData = "{__('L_PROCESSED')}!";
 if(json.data){
 retData = json.data; 
 }
//TODO 添加支持以允许保存并停留
 if(btn_action =='reqonly'){
 if(returi = form.attr('returi')){
 $(window).data('blockUI.returi',returi);
 ajaxAutoUnblock = false;
 }
 $.blockUI({ message:retData, css:{ cursor:'pointer',padding:'4px',border:'3px solid #CC0000',}, overlayCSS:{ cursor:'pointer' } });
 $(".blockUI").addClass("blockwarn");
 } // 如果设置了 returi,则返回到 uri
 else if(returi = form.attr('returi')){
 window.location = returi;
 // 否则如果设置了 reload,则重新加载
 }else if(form.attr('reload')!=undefined){
 window.location.reload();
 }else{
 $.blockUI({ message:retData });
 $.unblockUI();
 }
 }
 else{
 if(typeof (json.data.errmsg) == 'string'){
 errHint.html(json.data).show();
 //$.blockUI({ message:json.data, css:{ cursor:'pointer',padding:'4px',border:'3px solid #CC0000',}, overlayCSS:{ cursor:'pointer' } });
 //$(".blockUI").addClass("blockwarn");
 }else{
 errHint.html("{Html::text(__('E_FORM'))}").show();
 for(var p in json.data){
 var msg = json.data[p];
 //处理隐藏值,None hidden input 应该具有引用隐藏值的 refid
 //显示服务器消息到 ref 
 var ele = form.find("[type=hidden][name="+p+"]");
 if(ele.length){
 delete json.data.p;
 refid = ele.attr("id");
 refname = form.find("[hidden-id="+refid+"]").attr("name");
 json.data[refname]=+msg;
 }
// 多选框 
 var ele = form.find("[type=checkbox][name='"+p+"[]']");
 if(ele.length){
 delete json.data.p;
 refname = p+'[]';
 json.data[refname]=+msg;
 }
 //@END
 }
 }
 /*
 * 检查隐藏值 
 */
 form.data("validator").invalidate(json.data);
 } },'json');
 e.preventDefault();
 }else{
 errHint.html("{Html::text(__('E_FORM'))}").show();
 }
};
隐藏字段验证与重置/***
 * 重置输入
 */
var ajax_post_form_hidden = function(){ 
 var form = $(this);
form.find("[hidden-id]").each(function(){
 // 清除引用消息 
 var input = $(this);
 var refid = input.attr("hidden-id");
 var field = $("#" + refid + ""); 
 // 设置清除 Errmsg
 // 监听 ID 元素上的 change 事件,移除真实输入的错误消息 
 field.change(function(){ 
 // 隐藏输入
 var hinput = $(this);
 // 真实输入
 var rinput = $("[hidden-id="+hinput.attr("id")+"]").first();
 form.data('validator').reset(rinput)
 });
 });
 };
var validate_hidden_id = function(input) {
 var refid = input.attr("hidden-id");
 var field = $("#" + refid); 
 var msg = field.attr('msg');
 if( !msg ){
 msg = "{__('E_NOT_EMPTY')}";
 }
return field.val() ? true : msg; 
};
var data_equals_validate = function(input) {
 var field;
 var name = input.attr("data-equals");
 field = this.getInputs().filter("[name=" + name + "]");
 return input.val() == field.val() ? true : [name];
};
链接与按钮的 Ajax 交互/**
 * 通过链接发送 Ajax 请求
 * 如果设置了 confirm,则在发送请求前确认
 * 支持 returi 和 reload
 * 否则显示阻塞消息
 */
var ajax_link_req = function(){
 var l = $(this);
 var hint = l.attr('hint');
 if(hint){
 var errHint = $(l.attr('hint'));
 errHint.text('').hide();
 }
 // 如果设置了确认消息,则应从客户端确认
 if(l.attr('confirm')){
 if(!confirm(l.attr('confirm'))){
 return false;
 }
 }
$.blockUI({ message:"{__('L_PROCESSING')}" });
var pre_script;
 if(pre_script = l.attr("pre_script")){
 var ret = eval(pre_script);
 if(ret==false){
 return false;
 }
 }
var block = l.attr('block');
 if(block != undefined){
 ajaxAutoUnblock = false ;
 }
$.get(l.attr('href'),function(json){
 if(json.code == true){
 var retData = "{__('L_PROCESSED')}!";
 var returi;
 // 如果成功执行每个函数
 var successFunc = l.attr('success');
 if(successFunc){
 l.each(window[successFunc]);
 }
if(json.data){
 retData = json.data; 
 }
 // 如果需要警告前
 if( l.attr('value') == 'reqonly'){
 alert(retData);
 }else if(returi = form.attr('returi')){
 window.location = returi;
 }
 else if(l.attr('reload')!=undefined){
 window.location.reload();
 }
 else{
 $.blockUI({ message:retData, css:{ cursor:'pointer',padding:'4px',border:'3px solid #CC0000',}, overlayCSS:{ cursor:'pointer' } });
 $(".blockUI").addClass("blockwarn");
 }
 }else{
 //$.unblockUI();
 //仅支持文本 errmsg
 if(hint){
 errHint.text(json.data).show();
 }else{
 alert(json.data);
 }
 }},'json');
 return false;
};
/**
 * 支持基于按钮的导航
 * 仅跳转到新 href
 */
var btn_nav = function(){
 var input = $(this);
 var href = input.attr("href");
 if(href){
 window.location = href;
 }else{
 alert("Href not set");
 }
 return false;
};
/**
 * 支持基于按钮的 Ajax 获取方法请求
 * 支持 returi 和 reload
 */
var btn_req = function(){
 var input = $(this);
 var href = input.attr("href");
var hint = input.attr('hint');
 if(hint){
 var errHint = $(hint).first();
 if(errHint.size() == 0){
 errHint = $("#pageError");
 }
 errHint.text('').hide();
 }
var block = input.attr('block');
 if(block != undefined){
 ajaxAutoUnblock = false 
 }
$.get(href,function(json){
 if(json.code == true){
 var returi;
 if(returi = input.attr('returi')){
 window.location = returi;
 }
 else if(input.attr("reload")!=undefined){
 window.location.reload();
 }else{
 var retData = "{__('L_PROCESSED')}!";
 if(json.data){
 retData = json.data; 
 }
 $.blockUI({ message:retData,css:{ cursor:'pointer' },overlayCSS:{ cursor:'pointer' } });
 }
 }else{
 if(hint){
 $.unblockUI();
 errHint.html(json.data.errmsg).show();
 }else{
 $.blockUI({ message:json.data.errmsg, css:{ cursor:'pointer',padding:'4px',border:'3px solid #CC0000',}, overlayCSS:{ cursor:'pointer' } });
 $(".blockUI").addClass("blockwarn");
 }
 }
 },'json');
 return false;
};
 
复选框与多级选择列表处理/**
 * 通用 Ajax 复选框
 * 默认操作被阻止,并通过 URL 提交真实请求
 */
var ajax_checkbox = function(){
 event.preventDefault();
 var action = $(this);
 var url = action.attr('url');
 var _check = action.prop("checked");
 console.log(_check);
 var op ; 
 if(_check){
 op = "1";
 }else{
 op = "0";
 }
 $.get(url + op ,function(json){
 if(json.code == true){
 if(_check){
 action.prop("checked",true);
 }else{
 action.prop("checked",false);
 }
 return true;
 }else{
 return false;
 }
 },'json');
};
/**
 * 创建根级选择列表 
 */
var picklistinit = function(){
 var _select = $(this);
 var _hidden_id = _select.attr('hidden-id');
 var _un = _select.attr('un');
 var _lovchildren = _select.data('lovtree').c;
 var _rowvalue = _select.data('rowvalue');
$("<OPTION>").append("{__('L_SELECT')}").appendTo(_select);
 for(var _kid in _lovchildren){
 var _lov = _lovchildren[_kid]['lov'];
 $("<OPTION>").val(_lov.lov_id).append(_lov.v).attr('k',_lov.id).attr('is_leaf',_lov.is_leaf).appendTo(_select);
 }
 _select.change(picklistchange);
// 选择列表
 if(_rowvalue){
 _select.find("[value="+_rowvalue[0]+"]").prop("selected",true);
 _select.change();
 }
 return true;
};
/**
 * 选择选择列表
 */
var picklistchange = function (){
 var _select = $(this);
 var _hidden_id = _select.attr('hidden-id');
 var _un = _select.attr('un');
// 移除所有后续项 
 var _lovtree = _select.data('lovtree');
 var _rowvalue = _select.data('rowvalue');
_select.nextAll().remove();
// 当前选择列表的值
 var _selected = _select.find(':selected');
 if(_selected.attr('is_leaf')=="{DB::T}"){
 $("#"+_hidden_id).val(_select.val());
 _select.after("<img src='/s.gif' class='sprite_global successimg'/>");
 }else{
 var _val = _select.val();
 var _k = _selected.attr('k');
// 获取子列表
 if(_lovtree.c[_k].c == undefined){
 return false;
 }
 var _c_lovtree = _lovtree.c[_k];
var _c_select = $('<SELECT>').data('lovtree',_c_lovtree).
 data('rowvalue',_rowvalue).
 attr('hidden-id',_hidden_id).attr('un',_un).
 attr('name',_un+'_'+_k);
 $("<OPTION>").append("{__('L_SELECT')}").appendTo(_c_select);
 // 构建选项列表
 for(var _kid in _c_lovtree.c){
 var _lov = _c_lovtree.c[_kid]['lov'];
 $("<OPTION>").val(_lov.lov_id).append(_lov.v).attr('k',_lov.id).attr('is_leaf',_lov.is_leaf).appendTo(_c_select);
 // 插入之后
 _select.after(_c_select);
 // 更改事件
 }
 _c_select.change(picklistchange);
if(_rowvalue){
 _c_select.find("[value="+_rowvalue[_k]+"]").prop("selected",true);
 _c_select.change();
 }
 };
 
查找窗口与文件上传功能var lookup_new = function(){
 var lookup = $(this);
 var pre_script;
 if(pre_script = lookup.attr("pre_script")){
 var ret = eval(pre_script);
 if(ret==false){
 return false;
 }
 }
 var url = lookup.attr("url");
 if(!url){
 alert('url not set');
 return false;
 }
 var height = lookup.attr('h');
 if(!height){
 height = 600;
 }
 var width = lookup.attr('w');
 if(!width){
 width = 800;
 }
 window.open(url,"pselect","scrollbars=yes,menubar=no,height="+height+",width="+width+",resizable=yes,toolbar=no,location=no,status=no");
 return false;
};
/**
 * 为隐藏值查找新值
 */
var parent_lookup = function(){
 var lookup = $(this);
 var pid = opener.$("#" + lookup.attr('pid'));
 if(!pid.length){
 alert(lookup.attr('pid')+ " not found");
 return false;
 }
 var pname = opener.$( "#" + lookup.attr('pname'));
 if(!pname.length){
 alert(lookup.attr('pname')+ " not found");
 return false;
 }
var aft_script;
 // 运行当前后脚本
 if(aft_script = lookup.attr('aft_script')){
 window.eval(aft_script);
 }
pid.val($(this).attr("refid")); 
 // 仅 opener 操作可触发更改事件
 pid.change();
 pname.val($(this).attr("refvalue")); 
 pname.change();
 // 父级 after_script
 if(aft_script = pname.attr('aft_script')){
 opener.window.eval(aft_script);
 }
 if(aft_script = pid.attr('aft_script')){
 opener.window.eval(aft_script);
 }
 window.close();
};
/**
 * 默认上传完成
 */
//var uploadComplete = function(event, id, fileName, responseJSON) { 
var uploadComplete = function(e, data) { 
 // 将由 jquery 上传器替换
 var _fileUpload = $(this);
 //console.log(_fileUpload);
 //console.log(data.result);
 if(_fileUpload.attr('reload')!=undefined){
 window.location.reload();
 }
};
/**
 * 文件上传函数,以下属性控制上传操作
 * 'endpoint' 作为上传 url
 * 'sid' 作为会话 id
 * 'complete' 可选配置自定义上传完成函数
 */
var genericUpload = function(dom){
 var endpointurl = $(this).attr('endpoint');
 var sid = $(this).attr("sid");
 var completeFunc = 'uploadComplete';
 // 设置自定义完成函数
 var cusComplete = $(this).attr('complete');
 if(cusComplete){
 completeFunc = cusComplete;
 }
$.fileupload({
 url: endpointurl,
 autoUpload:true,
 dataType:'json',
 formData: [{ 'sessionid': sid }],
 paramName: 'Filedata',
 }).bind('fileuploaddone',window[completeFunc]);
};
高级验证效果与事件绑定/**
 * 与输入匹配的错误
 * 仅在此处识别匹配的错误
 */
var advance_validate = function(errors, event) {
 var conf = this.getConf();
 // 循环错误
 $.each(errors, function(index, error) {
 // 将错误类添加到输入 Dom 元素
 var input = error.input;
input.addClass(conf.errorClass);
// 获取错误容器的句柄
 var msg = input.data("msg.el");
// 如果不存在则创建 Error 数据,并为输入添加错误类
 // "msg.el" 数据链接到错误消息 Dom 元素
 if (!msg) { 
 //msg = $(conf.message).addClass(conf.messageClass).insertAfter(input);
 msg = $(conf.message).addClass(conf.messageClass).appendTo(input.parent());
 input.data("msg.el", msg);
 }
// 清空容器 
 msg.css({visibility: 'hidden'}).find("span").remove();
// 填充消息
 $.each(error.messages, function(i, m) { 
 $("<span/>").html(m).appendTo(msg); 
 });
// 确保宽度不是整个主体宽度,以便可以正确定位
 if (msg.outerWidth() == msg.parent().width()) {
 msg.add(msg.find("span")); 
 }
// 插入正确的位置(相对于字段)
msg.css({ visibility: 'visible'}) .fadeIn(conf.speed); 
 msg.parent().addClass("colError");
 });
};
var advance_inputs = function(inputs) {
 var conf = this.getConf(); 
 inputs.removeClass(conf.errorClass).each(function() {
 var msg = $(this).data("msg.el");
 if (msg) {
 msg.hide();
 msg.parent().removeClass("colError");
 }
 });
 if($(".colError").size() == 0){ 
 var form = $(this);
 var errHint = form.find(".formError").first();
 if(errHint.size() == 0){
 errHint = $("#pageError");
 errHint.text('').hide();
 }
 }
};
/**
 * 当 refname 包含被选中时
 */
var checkall = function() { 
 var check = $(this);
 var refname = check.attr('refname');
 if(refname){
 if(check.prop("checked")){
 $("input[name*='"+refname+"']").prop("checked",true);
 }else{
 $("input[name*='"+refname+"']").prop("checked",false);
 }
 }
var refclass = check.attr('refclass');
 if(refclass){
 if(check.prop("checked")){
 $("input."+refclass).prop("checked",true);
 }else{
 $("input."+refclass).prop("checked",false);
 }
 };
/**
 * 设置只读复选框 
 */
var readonlyCheck = function(e){
 e.preventDefault(); 
 return false;
};
/**
 * 选择列表禁用
 */
var readonlySelect = function(){
 $(this).prop("disabled", true); 
};
$(document).ready(function() {
 $(document).ajaxStart(function(){
 // 清理 Ajax 请求页面级错误
 $("#pageError").text('').hide();
 // 清理表单错误
 $(".formError").text('').hide();
 // 阻止所有 ajax 处理
 if(ajaxSynchronized){
 $.blockUI({ message:"{__('L_PROCESSING')}" });
 }
 });
 $(document).ajaxStop(function(){
 if(ajaxSynchronized){
 if($(window).data('blockUI.isBlocked') == 1){
 if(ajaxAutoUnblock){
 $.unblockUI();
 }else{
 ajaxAutoUnblock = true;
 }
 }
 }else{// 从异步模式改回默认同步模式
 ajaxAutoUnblock = true;
 ajaxSynchronized = true;
 }
 });
 $(document).ajaxError(function(event, request, settings){
 alert('Ajax Request Error! URL='+settings.url);
 if(ajaxSynchronized){
 if($(window).data('blockUI.isBlocked') == 1){
 if(ajaxAutoUnblock){
 $.unblockUI();
 }else{
 ajaxAutoUnblock = true;
 }
 }
 }else{
 ajaxAutoUnblock = true;
 ajaxSynchronized = true;
 }
 });
// 强制解除 blockUI
 $(document).click(function(){
 if($(window).data('blockUI.isBlocked') == 1){
 $.unblockUI();
 var returi = $(window).data('blockUI.returi');
 if(returi){
 window.location = returi;
 }
 }});
$.tools.validator.addEffect("advanced", advance_validate,advance_inputs);
 $.tools.validator.fn("[data-equals]", { "{lang}":"{__('E_NOTEQUAL')}" }, data_equals_validate );
 $.tools.validator.fn("[hidden-id]",validate_hidden_id);
$(".ajax_form_post").validator({ lang:'{lang}',effect:'advanced' }) .submit( ajax_form_post );
 $(".ajax_form_post").each(ajax_post_form_hidden);
 $(".spin_number").each(spin_number);
 $(".date_input").each(date_input);
 $(".time_picker").each(time_picker);
$('.ajax_link_req').click(ajax_link_req);
 // 隐藏 ID 的客户端验证
 $(".require_validate").validator({ lang:'{lang}',effect:'advanced' });
$(".btn_nav").click( btn_nav );
 $(".btn_req").click( btn_req );
 $("button.btn_action").click(ajax_action_button);
 $(".lookup_new").click(lookup_new);
 $(".parent_lookup").click(parent_lookup);
 $(".ajax_checkbox").click(ajax_checkbox);
 $(".bool_checkbox").click(bool_checkbox);
 $(".checkall").click(checkall);
 $("img[rel]").overlay();
 $("input[tip]").tooltip({ position:"center right"});
// 最后我们将进行本地化
 $.tools.validator.localize("{lang}", {
 '*' : "{__('E_ALL')}",
 ':email' : "{__('E_EMAIL')}",
 ':number' : "{__('E_DECIMAL')}",
 ':url' : "{__('E_URL')}",
 '[max]' : "{__('E_MAX_LENGTH')}",
 '[min]' : "{__('E_MIN_LENGTH')}",
 '[required]' : "{__('E_NOT_EMPTY')}",
 });
});

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章