define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, Upload, Validator) {
var Form = {
config: {
fieldlisttpl: '
'
},
events: {
validator: function (form, success, error, submit) {
if (!form.is("form"))
return;
//绑定表单事件
form.validator($.extend({
validClass: 'has-success',
invalidClass: 'has-error',
bindClassTo: '.form-group',
formClass: 'n-yellow n-bootstrap',
msgClass: 'n-top',
theme: 'yellow_top',
stopOnError: true,
rules: {
mobile: [/^1[3-9]\d{9}$/, "请填写有效的手机号"],
email: [/^[0-9A-Za-z][\.-_0-9A-Za-z]*@[0-9A-Za-z]+(\.[0-9A-Za-z]+)+$/, "请填写有效的邮箱地址"],
chinese: [/^[\u0391-\uFFE5]+$/, "请填写中文字符"],
money: [/^(?:0|[1-9]\d*)(?:\.\d{1,2})?$/, "请填写有效的金额"],
ip: [/^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})$/i, '请填写有效的 IP 地址'],
// 身份证
idcard: function (element) {
var value = element.value,
isValid = true;
var cityCode = {11: "北京", 12: "天津", 13: "河北", 14: "山西", 15: "内蒙古", 21: "辽宁", 22: "吉林", 23: "黑龙江 ", 31: "上海", 32: "江苏", 33: "浙江", 34: "安徽", 35: "福建", 36: "江西", 37: "山东", 41: "河南", 42: "湖北 ", 43: "湖南", 44: "广东", 45: "广西", 46: "海南", 50: "重庆", 51: "四川", 52: "贵州", 53: "云南", 54: "西藏 ", 61: "陕西", 62: "甘肃", 63: "青海", 64: "宁夏", 65: "新疆", 71: "台湾", 81: "香港", 82: "澳门", 91: "国外 "};
/* 15位校验规则: (dddddd yymmdd xx g) g奇数为男,偶数为女
* 18位校验规则: (dddddd yyyymmdd xxx p) xxx奇数为男,偶数为女,p校验位
校验位公式:C17 = C[ MOD( ∑(Ci*Wi), 11) ]
i----表示号码字符从由至左包括校验码在内的位置序号
Wi 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2 1
Ci 1 0 X 9 8 7 6 5 4 3 2
*/
var rFormat = /^\d{6}(19|20)\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)$|^\d{6}\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}$/; // 格式验证
if (!rFormat.test(value) || !cityCode[value.substr(0, 2)]) {
isValid = false;
}
// 18位身份证需要验证最后一位校验位
else if (value.length === 18) {
var Wi = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1]; // 加权因子
var Ci = "10X98765432"; // 校验字符
// 加权求和
var sum = 0;
for (var i = 0; i < 17; i++) {
sum += value.charAt(i) * Wi[i];
}
// 计算校验值
var C17 = Ci.charAt(sum % 11);
// 与校验位比对
if (C17 !== value.charAt(17)) {
isValid = false;
}
}
return isValid || "请填写正确的身份证号码";
},
// 银行卡(借记卡、贷记卡)
bankcard: function (element) {
var value = element.value.replace(/\s/g, ''),
isValid = true,
rFormat = /^[\d]{12,19}$/;
if (!rFormat.test(value)) {
isValid = false;
} else {
var arr = value.split('').reverse(),
i = arr.length,
temp,
sum = 0;
while (i--) {
if (i % 2 === 0) {
sum += +arr[i];
} else {
temp = +arr[i] * 2;
sum += temp % 10;
if (temp > 9) sum += 1;
}
}
if (sum % 10 !== 0) {
isValid = false;
}
}
return isValid || "请填写有效的银行卡号";
},
// 信用卡
creditcard: function (element, params) {
var value = element.value,
validTypes = 0x0000,
types = {
mastercard: 0x0001,
visa: 0x0002,
amex: 0x0004,
dinersclub: 0x0008,
enroute: 0x0010,
discover: 0x0020,
jcb: 0x0040,
unknown: 0x0080
};
if (/[^0-9\-]+/.test(value)) {
return false;
}
value = value.replace(/\D/g, "");
if (!params) {
validTypes = 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040 | 0x0080;
} else {
for (var i = 0; i < parmas.length; i++) {
validTypes |= types[params[i]];
}
}
if (validTypes & 0x0001 && /^(5[12345])/.test(value)) { //mastercard
return value.length === 16;
}
if (validTypes & 0x0002 && /^(4)/.test(value)) { //visa
return value.length === 16;
}
if (validTypes & 0x0004 && /^(3[47])/.test(value)) { //amex
return value.length === 15;
}
if (validTypes & 0x0008 && /^(3(0[012345]|[68]))/.test(value)) { //dinersclub
return value.length === 14;
}
if (validTypes & 0x0010 && /^(2(014|149))/.test(value)) { //enroute
return value.length === 15;
}
if (validTypes & 0x0020 && /^(6011)/.test(value)) { //discover
return value.length === 16;
}
if (validTypes & 0x0040 && /^(3)/.test(value)) { //jcb
return value.length === 16;
}
if (validTypes & 0x0040 && /^(2131|1800)/.test(value)) { //jcb
return value.length === 15;
}
if (validTypes & 0x0080) { //unknown
return true;
}
return "请填写有效的信用卡号";
},
// 组织机构代码证
orgcode: function (element) {
var value = element.value,
isValid = true,
rFormat = /^[A-Z\d]{8}-[X\d]/;
if (!rFormat.test(value)) {
isValid = false;
} else {
var Wi = [3, 7, 9, 10, 5, 8, 4, 2];
var Ci = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// 加权求和
var sum = 0;
for (var i = 0; i < 8; i++) {
sum += Ci.indexOf(value.charAt(i)) * Wi[i];
}
// 计算校验值: C9 = 11 - MOD ( ∑(Ci*Wi), 11 )
var C9 = 11 - (sum % 11);
if (C9 === 10) C9 = 'X';
else if (C9 === 11) C9 = 0;
C9 = '' + C9;
// 与校验位比对
if (C9 !== value.charAt(9)) {
isValid = false;
}
}
return isValid || "请填写正确的组织机构代码";
},
// 营业执照号
bizcode: function (element) {
var value = element.value,
isValid = true,
rFormat = /^[1-6]\d{14}$/;
// 共15位:6位首次登记机关代码 + 8位顺序码 + 校验位
if (!rFormat.test(value)) {
isValid = false;
} else {
var s = [],
p = [10];
for (var i = 0; i < 15; i++) {
s[i] = (p[i] % 11) + (+value.charAt(i));
p[i + 1] = (s[i] % 10 || 10) * 2;
}
if (1 !== s[14] % 10) {
isValid = false;
}
}
return isValid || "请填写正确的营业执照号";
},
// 统一社会信用代码
unicode: function (element) {
var value = element.value.replace(/^\s*|\s*$/g, ''),
isValid = true,
rFormat = /^[1-9A-GV][1239][1-9]\d{5}[A-Z\d]{8}[X\d][Y\d]/;
if (!rFormat.test(value)) {
isValid = false;
} else {
var code, Wi, Ci, sum, C9, C18;
// 计算组织机构代码校验位:C9 = 11 - MOD ( ∑(Ci*Wi), 11 )
code = value.slice(9, 17);
Wi = [3, 7, 9, 10, 5, 8, 4, 2];
Ci = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// 加权求和
sum = 0;
for (var i = 0; i < Wi.length; i++) {
sum += Ci.indexOf(code.charAt(i)) * Wi[i];
}
C9 = 11 - (sum % 11);
if (C9 === 10) C9 = 'X';
else if (C9 === 11) C9 = 0;
C9 = '' + C9;
// 与校验位比对
if (C9 !== code.charAt(9)) {
isValid = false;
}
if (isValid) {
// 计算最后校验位:C18 = 31 - MOD ( ∑(Ci*Wi), 31 )
code = value.slice(0, 17);
Wi = [1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28];
Ci = "0123456789ABCDEFGHJKLMNPQRTUWXY";
// 加权求和
sum = 0;
for (var i = 0; i < Wi.length; i++) {
sum += Ci.indexOf(code.charAt(i)) * Wi[i];
}
C18 = 31 - (sum % 31);
if (C18 === 30) C18 = 'Y';
else if (C18 === 31) C18 = 0;
C18 = '' + C18;
// 与校验位比对
if (C18 !== code.charAt(18)) {
isValid = false;
}
}
}
return isValid || "请填写正确的统一社会信用代码";
}
},
display: function (elem) {
return $(elem).closest('.form-group').find(".control-label").text().replace(/\:/, '');
},
dataFilter: function (data) {
if (data.code === 1) {
return "";
} else {
return data.msg;
}
},
target: function (input) {
var target = $(input).data("target");
if (target && $(target).size() > 0) {
return $(target);
}
var $formitem = $(input).closest('.form-group'),
$msgbox = $formitem.find('span.msg-box');
if (!$msgbox.length) {
return [];
}
return $msgbox;
},
valid: function (ret) {
var that = this, submitBtn = $(".layer-footer [type=submit]", form);
that.holdSubmit();
$(".layer-footer [type=submit]", form).addClass("disabled");
//验证通过提交表单
Form.api.submit($(ret), function (data, ret) {
that.holdSubmit(false);
submitBtn.removeClass("disabled");
if (false === $(this).triggerHandler("success.form", [data, ret])) {
return false;
}
if (typeof success === 'function') {
if (false === success.call($(this), data, ret)) {
return false;
}
}
//提示及关闭当前窗口
var msg = ret.hasOwnProperty("msg") && ret.msg !== "" ? ret.msg : __('Operation completed');
parent.Toastr.success(msg);
parent.$(".btn-refresh").trigger("click");
var index = parent.Layer.getFrameIndex(window.name);
parent.Layer.close(index);
return false;
}, function (data, ret) {
that.holdSubmit(false);
if (false === $(this).triggerHandler("error.form", [data, ret])) {
return false;
}
submitBtn.removeClass("disabled");
if (typeof error === 'function') {
if (false === error.call($(this), data, ret)) {
return false;
}
}
}, submit);
return false;
}
}, form.data("validator-options") || {}));
//移除提交按钮的disabled类
$(".layer-footer [type=submit],.fixed-footer [type=submit],.normal-footer [type=submit]", form).removeClass("disabled");
},
selectpicker: function (form) {
//绑定select元素事件
if ($(".selectpicker", form).size() > 0) {
require(['bootstrap-select', 'bootstrap-select-lang'], function () {
$('.selectpicker', form).selectpicker();
});
}
},
selectpage: function (form) {
//绑定selectpage元素事件
if ($(".selectpage", form).size() > 0) {
require(['selectpage'], function () {
$('.selectpage', form).selectPage({
eAjaxSuccess: function (data) {
data.list = typeof data.rows !== 'undefined' ? data.rows : (typeof data.list !== 'undefined' ? data.list : []);
data.totalRow = typeof data.total !== 'undefined' ? data.total : (typeof data.totalRow !== 'undefined' ? data.totalRow : data.list.length);
return data;
}
});
});
//给隐藏的元素添加上validate验证触发事件
$(document).on("change", ".sp_hidden", function () {
$(this).trigger("validate");
});
$(document).on("change", ".sp_input", function () {
$(this).closest(".sp_container").find(".sp_hidden").trigger("change");
});
$(form).on("reset", function () {
setTimeout(function () {
$('.selectpage', form).selectPageClear();
}, 1);
});
}
},
cxselect: function (form) {
//绑定cxselect元素事件
if ($("[data-toggle='cxselect']", form).size() > 0) {
require(['cxselect'], function () {
$.cxSelect.defaults.jsonName = 'name';
$.cxSelect.defaults.jsonValue = 'value';
$.cxSelect.defaults.jsonSpace = 'data';
$("[data-toggle='cxselect']", form).cxSelect();
});
}
},
citypicker: function (form) {
//绑定城市远程插件
if ($("[data-toggle='city-picker']", form).size() > 0) {
require(['citypicker'], function () {
});
}
},
datetimepicker: function (form) {
//绑定日期时间元素事件
if ($(".datetimepicker", form).size() > 0) {
require(['bootstrap-datetimepicker'], function () {
var options = {
format: 'YYYY-MM-DD HH:mm:ss',
icons: {
time: 'fa fa-clock-o',
date: 'fa fa-calendar',
up: 'fa fa-chevron-up',
down: 'fa fa-chevron-down',
previous: 'fa fa-chevron-left',
next: 'fa fa-chevron-right',
today: 'fa fa-history',
clear: 'fa fa-trash',
close: 'fa fa-remove'
},
showTodayButton: true,
showClose: true
};
$('.datetimepicker', form).parent().css('position', 'relative');
$('.datetimepicker', form).datetimepicker(options);
});
}
},
daterangepicker: function (form) {
//绑定日期时间元素事件
if ($(".datetimerange", form).size() > 0) {
require(['bootstrap-daterangepicker'], function () {
var ranges = {};
ranges[__('Today')] = [Moment().startOf('day'), Moment().endOf('day')];
ranges[__('Yesterday')] = [Moment().subtract(1, 'days').startOf('day'), Moment().subtract(1, 'days').endOf('day')];
ranges[__('Last 7 Days')] = [Moment().subtract(6, 'days').startOf('day'), Moment().endOf('day')];
ranges[__('Last 30 Days')] = [Moment().subtract(29, 'days').startOf('day'), Moment().endOf('day')];
ranges[__('This Month')] = [Moment().startOf('month'), Moment().endOf('month')];
ranges[__('Last Month')] = [Moment().subtract(1, 'month').startOf('month'), Moment().subtract(1, 'month').endOf('month')];
var options = {
timePicker: false,
autoUpdateInput: false,
timePickerSeconds: true,
timePicker24Hour: true,
autoApply: true,
locale: {
format: 'YYYY-MM-DD HH:mm:ss',
customRangeLabel: __("Custom Range"),
applyLabel: __("Apply"),
cancelLabel: __("Clear"),
},
ranges: ranges,
};
var origincallback = function (start, end) {
$(this.element).val(start.format(options.locale.format) + " - " + end.format(options.locale.format));
$(this.element).trigger('blur');
};
$(".datetimerange", form).each(function () {
var callback = typeof $(this).data('callback') == 'function' ? $(this).data('callback') : origincallback;
$(this).on('apply.daterangepicker', function (ev, picker) {
callback.call(picker, picker.startDate, picker.endDate);
});
$(this).on('cancel.daterangepicker', function (ev, picker) {
$(this).val('').trigger('blur');
});
$(this).daterangepicker($.extend({}, options, $(this).data()), callback);
});
});
}
},
plupload: function (form) {
//绑定plupload上传元素事件
if ($(".plupload", form).size() > 0) {
Upload.api.plupload($(".plupload", form));
}
},
faselect: function (form) {
//绑定fachoose选择附件事件
if ($(".fachoose", form).size() > 0) {
$(".fachoose", form).on('click', function () {
var that = this;
var multiple = $(this).data("multiple") ? $(this).data("multiple") : false;
var mimetype = $(this).data("mimetype") ? $(this).data("mimetype") : '';
parent.Fast.api.open("general/attachment/select?element_id=" + $(this).attr("id") + "&multiple=" + multiple + "&mimetype=" + mimetype, __('Choose'), {
callback: function (data) {
var button = $("#" + $(that).attr("id"));
var maxcount = $(button).data("maxcount");
var input_id = $(button).data("input-id") ? $(button).data("input-id") : "";
maxcount = typeof maxcount !== "undefined" ? maxcount : 0;
if (input_id && data.multiple) {
var urlArr = [];
var inputObj = $("#" + input_id);
var value = $.trim(inputObj.val());
if (value !== "") {
urlArr.push(inputObj.val());
}
urlArr.push(data.url)
var result = urlArr.join(",");
if (maxcount > 0) {
var nums = value === '' ? 0 : value.split(/\,/).length;
var files = data.url !== "" ? data.url.split(/\,/) : [];
var remains = maxcount - nums;
if (files.length > remains) {
Toastr.error(__('You can choose up to %d file%s', remains));
return false;
}
}
inputObj.val(result).trigger("change");
} else {
$("#" + input_id).val(data.url).trigger("change");
}
}
});
return false;
});
}
},
fieldlist: function (form) {
//绑定fieldlist
if ($(".fieldlist", form).size() > 0) {
require(['dragsort', 'template'], function (undefined, Template) {
//刷新隐藏textarea的值
var refresh = function (name) {
var data = {};
var textarea = $("textarea[name='" + name + "']", form);
var container = textarea.closest("dl");
var template = container.data("template");
console.log(name, container);
$.each($("input,select", container).serializeArray(), function (i, j) {
var reg = /\[(\w+)\]\[(\w+)\]$/g;
var match = reg.exec(j.name);
if (!match)
return true;
match[1] = "x" + parseInt(match[1]);
if (typeof data[match[1]] == 'undefined') {
data[match[1]] = {};
}
data[match[1]][match[2]] = j.value;
});
var result = template ? [] : {};
$.each(data, function (i, j) {
if (j) {
if (!template) {
if (j.key != '') {
result[j.key] = j.value;
}
} else {
result.push(j);
}
}
});
textarea.val(JSON.stringify(result));
};
//监听文本框改变事件
$(document).on('change keyup', ".fieldlist input,.fieldlist textarea,.fieldlist select", function () {
refresh($(this).closest("dl").data("name"));
});
//追加控制
$(".fieldlist", form).on("click", ".btn-append,.append", function (e, row) {
var container = $(this).closest("dl");
var index = container.data("index");
var name = container.data("name");
var template = container.data("template");
var data = container.data();
index = index ? parseInt(index) : 0;
container.data("index", index + 1);
var row = row ? row : {};
var vars = {index: index, name: name, data: data, row: row};
var html = template ? Template(template, vars) : Template.render(Form.config.fieldlisttpl, vars);
$(html).insertBefore($(this).closest("dd"));
$(this).trigger("fa.event.appendfieldlist", $(this).closest("dd").prev());
});
//移除控制
$(".fieldlist", form).on("click", "dd .btn-remove", function () {
var container = $(this).closest("dl");
$(this).closest("dd").remove();
refresh(container.data("name"));
});
//拖拽排序
$("dl.fieldlist", form).dragsort({
itemSelector: 'dd',
dragSelector: ".btn-dragsort",
dragEnd: function () {
refresh($(this).closest("dl").data("name"));
},
placeHolderTemplate: ""
});
//渲染数据
$(".fieldlist", form).each(function () {
var container = this;
var textarea = $("textarea[name='" + $(this).data("name") + "']", form);
if (textarea.val() == '') {
return true;
}
var template = $(this).data("template");
var json = {};
try {
json = JSON.parse(textarea.val());
} catch (e) {
}
$.each(json, function (i, j) {
$(".btn-append,.append", container).trigger('click', template ? j : {
key: i,
value: j
});
});
});
});
}
},
bindevent: function (form) {
}
},
api: {
submit: function (form, success, error, submit) {
if (form.size() === 0)
return Toastr.error("表单未初始化完成,无法提交");
if (typeof submit === 'function') {
if (false === submit.call(form)) {
return false;
}
}
var type = form.attr("method").toUpperCase();
type = type && (type === 'GET' || type === 'POST') ? type : 'GET';
url = form.attr("action");
url = url ? url : location.href;
//修复当存在多选项元素时提交的BUG
var params = {};
var multipleList = $("[name$='[]']", form);
if (multipleList.size() > 0) {
var postFields = form.serializeArray().map(function (obj) {
return $(obj).prop("name");
});
$.each(multipleList, function (i, j) {
if (postFields.indexOf($(this).prop("name")) < 0) {
params[$(this).prop("name")] = '';
}
});
}
//调用Ajax请求方法
Fast.api.ajax({
type: type,
url: url,
data: form.serialize() + (Object.keys(params).length > 0 ? '&' + $.param(params) : ''),
dataType: 'json',
complete: function (xhr) {
var token = xhr.getResponseHeader('__token__');
if (token) {
$("input[name='__token__']", form).val(token);
}
}
}, function (data, ret) {
$('.form-group', form).removeClass('has-feedback has-success has-error');
if (data && typeof data === 'object') {
//刷新客户端token
if (typeof data.token !== 'undefined') {
$("input[name='__token__']", form).val(data.token);
}
//调用客户端事件
if (typeof data.callback !== 'undefined' && typeof data.callback === 'function') {
data.callback.call(form, data);
}
}
if (typeof success === 'function') {
if (false === success.call(form, data, ret)) {
return false;
}
}
}, function (data, ret) {
if (data && typeof data === 'object' && typeof data.token !== 'undefined') {
$("input[name='__token__']", form).val(data.token);
}
if (typeof error === 'function') {
if (false === error.call(form, data, ret)) {
return false;
}
}
});
return false;
},
bindevent: function (form, success, error, submit) {
form = typeof form === 'object' ? form : $(form);
var events = Form.events;
events.bindevent(form);
events.validator(form, success, error, submit);
events.selectpicker(form);
events.daterangepicker(form);
events.selectpage(form);
events.cxselect(form);
events.citypicker(form);
events.datetimepicker(form);
events.plupload(form);
events.faselect(form);
events.fieldlist(form);
},
custom: {}
},
};
return Form;
});