- __otcDiag 双源对象拆为 __diag(版本元数据, 仅供 banner 展示) + __debug(调试开关布尔值, 唯一真相来源), 消除 __私有对象 与 otcDebug.API 大小写混搭。 - __debug 解析顺序: URL ?otcdebug=1(命中即写回 localStorage) -> localStorage -> 兜底 __diag.debug。同源父子 frame 共享 localStorage, 使 _InfoLayout iframe 弹窗(新增互换交易)自动继承父页面开过的调试开关, 修复弹窗内无调试日志。
852 lines
28 KiB
JavaScript
852 lines
28 KiB
JavaScript
//依赖类库:lodash.js + jquery + jquery.validate + bootstrap.emodal.js
|
||
|
||
// 统一调试日志工具:仅在 otcdebug 开启时输出,默认静默(生产零噪音)。
|
||
// 开启:URL 加 ?otcdebug=1(会自动写入 localStorage,同源 iframe/弹窗自动继承);
|
||
// 或控制台 localStorage.setItem('otcdebug','1')。关闭:localStorage.removeItem('otcdebug')。
|
||
// 业务脚本用 otcDebug.banner / otcDebug.log 替代裸 console.log(见 swapTradeEdit.js / fastVue.base.js)。
|
||
//
|
||
// 命名约定(避免 __私有对象 + otcDebug.API 大小写混搭):
|
||
// __diag —— 版本元数据对象(仅 _MainLayout 灌入 ylotc.__diag 时存在,仅供 banner 展示,不参与开关判定)
|
||
// __debug —— 调试开关布尔值(唯一真相来源:localStorage 优先 → URL 参数 → 兜底 __diag.debug)
|
||
var __diag = (window.ylotc && window.ylotc.__diag) || {};
|
||
var __debug = (function () {
|
||
try {
|
||
// 1) URL 参数优先(排查最直观);命中即写入 localStorage,供同源 iframe/弹窗继承
|
||
if (/[?&]otcdebug=1/.test(location.search)) { localStorage.setItem('otcdebug', '1'); return true; }
|
||
// 2) 已持久化的开关(父页面开过一次,_InfoLayout 弹窗自动生效)
|
||
if (localStorage.getItem('otcdebug') === '1') return true;
|
||
} catch (e) { /* localStorage 不可用时忽略,继续走兜底 */ }
|
||
// 3) 兜底:layout 灌入的 ylotc.__diag.debug(仅 _MainLayout 有)
|
||
return !!__diag.debug;
|
||
})();
|
||
window.otcDebug = {
|
||
// 模块加载横幅:F12 一眼看到模块名+版本,用于排查"是不是加载了旧代码/旧缓存"
|
||
banner: function (name, ver) {
|
||
if (!__debug || !window.console) return;
|
||
var git = (__diag.git || '').slice(0, 7);
|
||
console.log('%c[' + name + '] v' + ver + ' (bundle=' + __diag.jsVersion + ', git=' + git + ', built=' + __diag.built + ')',
|
||
'color:#06c;font-weight:bold');
|
||
},
|
||
// 普通调试日志:透传参数,仅 debug 开启时输出
|
||
log: function () {
|
||
if (!__debug || !window.console) return;
|
||
console.log.apply(console, arguments);
|
||
}
|
||
};
|
||
|
||
var main = window.main || { version: "1.0" };
|
||
|
||
main.extend = $.extend;
|
||
|
||
//数字格式化
|
||
|
||
/**
|
||
* 构建数字格式化函数(依赖lodash.js)
|
||
* @see Intl.NumberFormat
|
||
* @param {any} precision - 小数位精度,默认2,如果是对象类,则当做带precision属性的options处理
|
||
* @param {object} options - 格式化选项
|
||
* @param {boolean} options.rounded - 是否四舍五入,明确传入false时禁止四舍五入
|
||
* @param {boolean} options.grouping - 是否使用千位分隔符,默认false
|
||
* @param {boolean} options.percent - 是否百分比格式,默认false
|
||
* @param {boolean} options.trimTailZeros - 是否去除小数点后的尾部的0,默认false
|
||
* @param {any} options.prefmt
|
||
* - 预先格式化,支持多种形式处理
|
||
* - 如果传入数组类型的参数值,则循环执行数组中的函数,如果执行结果为string或number类型,则prefmt值取此结果并跳出循环
|
||
* - 如果传入函数类型的参数值,prefmt值取函数执行结果
|
||
* - 如果prefmt值为数字类型,则格式化prefmt值
|
||
* - 如果prefmt值不为undefined|null|false,则返回prefmt值,否则忽略prefmt
|
||
* @param {string} options.nanfmt - 输入值解析成数字为NAN时的格式化处理,类型为字符串,默认返回0的格式化数值
|
||
* @return {function} - 返回带fromat函数的格式化对象
|
||
*/
|
||
main.numberFormat = function (precision, options) {
|
||
if (typeof precision === 'object') {
|
||
options = precision;
|
||
precision = options.precision;
|
||
}
|
||
else if (!options || typeof options !== 'object') {
|
||
options = {};
|
||
}
|
||
|
||
//解析小数位精度
|
||
precision = parseInt(precision);
|
||
if (isNaN(precision)) {
|
||
precision = 2;
|
||
} else if (precision < 0) {
|
||
precision = 0;
|
||
} else if (precision > 10) {
|
||
precision = 10;
|
||
}
|
||
|
||
//构建Intl.NumberFormat
|
||
const fmt = new Intl.NumberFormat('en', {
|
||
useGrouping: !!options.grouping,
|
||
minimumFractionDigits: precision,
|
||
maximumFractionDigits: precision,
|
||
style: options.percent ? 'percent' : 'decimal'
|
||
});
|
||
|
||
//百分比格式特殊精度位处理
|
||
if (options.percent) precision += 2;
|
||
|
||
//预格式化处理
|
||
function preformat(prefmt, fmtArgs) {
|
||
|
||
//如果是数组类型,则以管道形式执行函数
|
||
if (Array.isArray(prefmt)) {
|
||
var result = false;
|
||
_.each(prefmt, x => {
|
||
let match = typeof x === 'function';
|
||
while (match) {
|
||
x = x.apply(this, fmtArgs);
|
||
switch (typeof x) {
|
||
case 'function':
|
||
continue;
|
||
case 'number':
|
||
case 'string':
|
||
result = x;
|
||
return false;
|
||
default: return true;
|
||
}
|
||
}
|
||
});
|
||
return result;
|
||
}
|
||
|
||
//执行函数处理
|
||
while (typeof prefmt === 'function') {
|
||
prefmt = prefmt.apply(this, fmtArgs);
|
||
}
|
||
|
||
return prefmt;
|
||
}
|
||
|
||
function _format(val) {
|
||
//预格式化处理
|
||
let prefmt = preformat(options.prefmt, arguments);
|
||
let type = typeof prefmt;
|
||
if (type === 'number') {
|
||
val = prefmt;
|
||
}
|
||
else if (type !== 'undefined' && prefmt !== null && prefmt !== false) {
|
||
return prefmt;
|
||
}
|
||
if (precision <= 4) {
|
||
val = _.round(val, precision + 4);
|
||
}
|
||
//数值格式化
|
||
if (options.rounded !== false) {
|
||
let negative = val < 0;
|
||
val = _.round(negative ? -val : val, precision);
|
||
negative && (val = -val);
|
||
} else {
|
||
val = _.floor(val, precision);
|
||
}
|
||
if (isNaN(val)) {
|
||
return typeof options.nanfmt === 'string' ? options.nanfmt : fmt.format(0);
|
||
}
|
||
return fmt.format(Math.abs(val) < 1e-6 ? 0 : val);
|
||
}
|
||
|
||
function format(val) {
|
||
let str = _format(val);
|
||
return options.trimTailZeros && str.endsWith("0") && precision > 0 ? str.replace(/\.?0+$/, '') : str;
|
||
}
|
||
|
||
format.precision = precision;
|
||
|
||
return format;
|
||
};
|
||
|
||
/**
|
||
* 格式化数字(依赖lodash.js)
|
||
* @param {object} number - 数字数值
|
||
* @param {any} precision - 小数位精度,默认2,如果是对象类,则当做带precision属性的options处理
|
||
* @param {object} options - 格式化选项
|
||
* @param {boolean} options.rounded - 是否四舍五入,类型为布尔值,默认true
|
||
* @param {boolean} options.grouping - 是否使用千位分隔符,类型为布尔值,默认false
|
||
* @param {boolean} options.percent - 是否百分比格式,类型为布尔值,默认false
|
||
* @param {string} options.nanfmt - 输入值解析成数字为NAN时的格式化处理,类型为字符串,默认返回0的格式化数值
|
||
* @param {any} options.prefmt - 参考main.numberFormat
|
||
* @return {function} - 返回带fromat函数的格式化对象
|
||
*/
|
||
main.formatNumber = function (number, precision, options) {
|
||
return new main.numberFormat(precision, options)(number);
|
||
};
|
||
|
||
//各种常用处理
|
||
|
||
const __alert = function (message) {
|
||
window.alert(message);
|
||
return false;
|
||
};
|
||
|
||
/**
|
||
* 显示页面加载组件,等待操作执行完成
|
||
* @param {string} method -- 'show'||'hide'
|
||
* @returns {jQuery} -- jQuery对象
|
||
*/
|
||
$.fn.waitMe = function (method) {
|
||
return this.each(function () {
|
||
if (this.showCount === undefined || this.showCount === null) {
|
||
this.showCount = 0;
|
||
}
|
||
var isBody = $(this).is('body');
|
||
var $waitme = $(this).children(".yl-waitme");
|
||
if (method === "show") {
|
||
if (!$waitme.length) {
|
||
!isBody && $(this).css("position") === "static" && $(this).css("position", "relative");
|
||
$waitme = $('<div class="yl-waitme"></div>');
|
||
$waitme.appendTo($(this));
|
||
}
|
||
$waitme.show();
|
||
this.showCount++;
|
||
} else if (this.showCount > 0) {
|
||
(this.showCount -= 1) === 0 && $waitme.remove();
|
||
}
|
||
});
|
||
};
|
||
|
||
main.logError = function (data) {
|
||
if (!data) return;
|
||
if (console && typeof console.error === "function") {
|
||
console.error(data);
|
||
} else if ($.isPlainObject(data) || data.length) {
|
||
alert(JSON.stringify(data));
|
||
} else {
|
||
alert(data);
|
||
}
|
||
};
|
||
|
||
const _systemmsg = [];
|
||
|
||
/**
|
||
* 显示通知消息
|
||
* @param {any} message -- 消息文本
|
||
* @param {any} time -- 显示时间
|
||
* @returns {boolean} -- false
|
||
*/
|
||
main.message = function (message, time) {
|
||
main.toast(message, time);
|
||
try {
|
||
|
||
if (_systemmsg.length >= 3) {
|
||
_systemmsg.splice(0, 1);
|
||
}
|
||
_systemmsg.push(message);
|
||
|
||
$("#systemspanmsg").html(_systemmsg.join(";"));
|
||
|
||
} catch (e) {
|
||
console.log("[ERROR:main.message]" + e);
|
||
}
|
||
return false;
|
||
};
|
||
|
||
main.toast = function (message, delay) {
|
||
|
||
if (!$('#toast-container').length) {
|
||
$('<div id="toast-container" class="toast-container">').appendTo('body');
|
||
}
|
||
|
||
var $t = $(`
|
||
<div class="toast toast-main">
|
||
<div class="toast-header pb-0 pt-3">
|
||
<span class="mr-auto">提示</span>
|
||
<button type="button" class="close" data-dismiss="toast"><strong>×</strong></button>
|
||
</div>
|
||
<div class="toast-body">${message}</div>
|
||
</div>`);
|
||
$t.appendTo('#toast-container').toast({ autohide: true, delay: delay || 3000 }).toast('show')
|
||
.on('hidden.bs.toast', function () { $t.remove(); }).on('click', function () {
|
||
$(this).toast('hide');
|
||
});
|
||
return false;
|
||
};
|
||
|
||
/**
|
||
* 显示警告信息
|
||
* @param {any} message -- 消息文本
|
||
* @returns {boolean} -- false
|
||
*/
|
||
main.warn = function (message) {
|
||
return main.message(message, 60 * 60 * 1000);
|
||
};
|
||
|
||
/**
|
||
* 显示警告弹窗
|
||
* @param {any} message -- 消息文本
|
||
* @returns {boolean} -- false
|
||
*/
|
||
main.alert = function (message, okCall) {
|
||
$.alert({
|
||
title: '提示',
|
||
content: (message || '').replace('\n', '<br>'),
|
||
buttons: {
|
||
ok: { text: '确定', action: okCall }
|
||
},
|
||
boxWidth: '450px',
|
||
useBootstrap: false,
|
||
animateFromElement: false
|
||
});
|
||
return false;
|
||
};
|
||
|
||
/**
|
||
* 显示确认弹窗
|
||
* @param {any} message -- 确认消息
|
||
* @param {any} okCallback -- 确认执行回调
|
||
* @param {any} cancelCallback -- 取消后回调
|
||
* @param {any} title -- 弹窗标题
|
||
*/
|
||
main.confirm = function (message, okCallback, cancelCallback, title) {
|
||
$.confirm({
|
||
title: title || '确认?',
|
||
content: message,
|
||
type: 'red',
|
||
buttons: {
|
||
ok: {
|
||
text: "确认",
|
||
btnClass: 'btn-primary',
|
||
keys: ['enter'],
|
||
action() {
|
||
if (okCallback) {
|
||
var tempCall = okCallback;
|
||
//确保只执行一次
|
||
okCallback = null;
|
||
tempCall();
|
||
}
|
||
}
|
||
},
|
||
cancel: {
|
||
text: "取消",
|
||
action() {
|
||
cancelCallback && cancelCallback();
|
||
}
|
||
}
|
||
},
|
||
useBootstrap: false,
|
||
boxWidth: '450px',
|
||
animateFromElement: false
|
||
});
|
||
};
|
||
|
||
//使用$.Deferred对象处理
|
||
main.confirmV2 = function (message, title) {
|
||
var deferred = $.Deferred();
|
||
$.confirm({
|
||
title: title || '确认?',
|
||
content: message,
|
||
type: 'red',
|
||
buttons: {
|
||
ok: {
|
||
text: "确认",
|
||
btnClass: 'btn-primary',
|
||
keys: ['enter'],
|
||
action: deferred.resolve
|
||
},
|
||
cancel: {
|
||
text: "取消",
|
||
action: deferred.reject
|
||
}
|
||
},
|
||
useBootstrap: false,
|
||
boxWidth: '450px',
|
||
animateFromElement: false
|
||
});
|
||
return deferred.promise();
|
||
};
|
||
|
||
main.waitMe = function (opt) {
|
||
if (typeof opt === 'boolean') {
|
||
opt = opt ? 'show' : 'hide';
|
||
} else {
|
||
opt = opt || "show";
|
||
}
|
||
$(document.body).waitMe(opt);
|
||
};
|
||
|
||
function __getWaitMeFunc(options) {
|
||
|
||
var waitMeFunc = main.waitMe;
|
||
|
||
if (typeof options !== 'undefined') {
|
||
var m = options.waitMe;
|
||
if (typeof m === 'undefined') {
|
||
m = options;
|
||
}
|
||
if (!m) {
|
||
waitMeFunc = function () { };
|
||
} else if (typeof m === 'function') {
|
||
waitMeFunc = m;
|
||
}
|
||
}
|
||
|
||
return waitMeFunc;
|
||
}
|
||
|
||
/**
|
||
* ⚠️【全局陷阱 / 接手必读】__post 的成败路由:
|
||
* - 成功(resp.success 为真 且 非 errcode) -> deferred.resolve(resp) (.done 触发)
|
||
* - 业务错误(resp.success===false / errcode) -> deferred.reject(resp) (.fail 触发,.done 不触发!)
|
||
* - 网络异常(ajax error) -> deferred.reject(resp) (.fail 触发)
|
||
* 即:业务错误走的是 reject,不是 resolve。调用方若只在 .done(...) 里写「业务失败/算不出来」的处理,
|
||
* 业务失败时那段代码根本不会执行 —— 这是 bug 静默失效的高发地。
|
||
* 典型实例:swapTradeEdit.js 的 calcBondForItem「约定2补充(计算器算不出→清另两字段+清标识)」曾因
|
||
* applyBondCalcFailure 只写在 .done 内、长期不生效;后改到 .fail 才修复(见该文件注释)。
|
||
* 凡要区分「成功 / 业务失败 / 网络异常」的逻辑,务必在 .done 之外再挂 .fail。
|
||
*/
|
||
function __post(url, data, deferred, options) {
|
||
|
||
if (!url) throw "post url 不能为空";
|
||
|
||
var waitMeFunc = __getWaitMeFunc(options);
|
||
|
||
var ajaxOptions = {
|
||
type: "POST",
|
||
data: data,
|
||
dataType: "json",
|
||
cache: false,
|
||
success(resp) {
|
||
if (options && options.dataType && options.dataType !== "json") {
|
||
deferred.resolve(resp);
|
||
return;
|
||
}
|
||
if (resp.success === false || resp.errcode) {
|
||
let alert = options && options.alertFn ? options.alertFn : main.alert;
|
||
let errmsg = (resp.msg || resp.errmsg || "错误").replace(/\n/g, '<p></p>');
|
||
alert(errmsg);
|
||
deferred.reject(resp);
|
||
} else {
|
||
resp.msg && main.message(resp.msg);
|
||
deferred.resolve(resp);
|
||
}
|
||
},
|
||
beforeSend(jqXHR) {
|
||
waitMeFunc(true);
|
||
},
|
||
complete(jqXHR, textStatus) {
|
||
waitMeFunc(false);
|
||
},
|
||
error(jqXHR, textStatus, errorThrown) {
|
||
options && options.suppressError || main.alert('请求失败');
|
||
console.error("请求失败,url:" + url + ",status:" + textStatus + ",error:" + errorThrown);
|
||
}
|
||
};
|
||
|
||
if (data instanceof FormData) {
|
||
ajaxOptions.processData = false;
|
||
ajaxOptions.contentType = false;
|
||
}
|
||
|
||
ajaxOptions = $.extend(ajaxOptions, options);
|
||
|
||
$.ajax(url, ajaxOptions).fail(deferred.reject);
|
||
}
|
||
|
||
/**
|
||
* 封装$.ajax方法
|
||
* @param {any} options
|
||
* -- boolean:是否显示加载等待
|
||
* -- function:自定义加载等待调用
|
||
* -- object:{waitMe,suppressError}
|
||
* @param {boolean} options.waitMe -- 是否显示加载等待
|
||
* @param {boolean} options.suppressError -- 禁止出错时提示
|
||
* @returns {jQuery} -- jquery延迟对象Deferred
|
||
*/
|
||
main.ajax = function (options) {
|
||
var deferred = $.Deferred();
|
||
var waitMeFunc = __getWaitMeFunc(options);
|
||
var ajaxOptions = {
|
||
type: "POST",
|
||
dataType: "json",
|
||
success(resp) {
|
||
if (resp.success === false) {
|
||
main.alert(resp.msg || "错误");
|
||
deferred.reject(resp);
|
||
} else {
|
||
main.message(resp.msg || "成功");
|
||
deferred.resolve(resp);
|
||
}
|
||
},
|
||
beforeSend: function () {
|
||
waitMeFunc(true);
|
||
},
|
||
complete: function () {
|
||
waitMeFunc(false);
|
||
},
|
||
error(jqXHR, textStatus, errorThrown) {
|
||
let error = "请求失败,url:" + url + ",status:" + textStatus + ",error:" + errorThrown;
|
||
(options.suppressError ? console.error : main.alert)(error);
|
||
}
|
||
};
|
||
|
||
ajaxOptions = $.extend(ajaxOptions, options);
|
||
|
||
$.ajax(ajaxOptions).fail(deferred.reject);
|
||
|
||
return deferred.promise();
|
||
};
|
||
|
||
/**
|
||
* 封装$.Post
|
||
* @param {any} url -- post url
|
||
* @param {any} data -- post data
|
||
* @param {any} options
|
||
* -- boolean:是否显示加载等待
|
||
* -- function:自定义加载等待调用
|
||
* -- object:{waitMe,suppressError}
|
||
* @param {boolean} options.waitMe -- 是否显示加载等待
|
||
* @param {boolean} options.suppressError -- 禁止出错时提示
|
||
* @returns {jQuery} -- jquery延迟对象Deferred
|
||
*/
|
||
// ⚠️ 见上方 __post 说明:本方法业务错误/网络异常走 reject,调用方需 .fail 才能捕获(只 .done 会漏掉失败分支)。
|
||
main.post = function (url, data, options) {
|
||
var d = $.Deferred();
|
||
__post(url, data, d, options);
|
||
return d.promise();
|
||
};
|
||
|
||
/**
|
||
* main.postSafe — 安全版 main.post,返回标准 Promise 而非 jQuery Deferred。
|
||
* ============================================================================
|
||
* 解决 main.post 的「业务错误走 reject、.done 不触发」陷阱(见 __post 注释)。
|
||
*
|
||
* 用法:
|
||
* main.postSafe('/api/calc', { price: 100 })
|
||
* .then(function(resp) { /* 成功(含 resp.success !== false)*\/ })
|
||
* .catch(function(resp) { /* 业务错误 / 网络异常 *\/ });
|
||
*
|
||
* 或者 async/await:
|
||
* try {
|
||
* var resp = await main.postSafe('/api/calc', { price: 100 });
|
||
* // 成功
|
||
* } catch (resp) {
|
||
* // 业务错误 / 网络异常(resp 可能是 { success:false, msg:'...' } 或 Error)
|
||
* }
|
||
*
|
||
* 与 main.post 的区别:
|
||
* - main.post 返回 jQuery Deferred:.done = 成功, .fail = 失败
|
||
* - main.postSafe 返回 Promise: .then = 成功, .catch = 失败
|
||
* - 行为一致,但 Promise 更符合标准,async/await 更自然
|
||
*
|
||
* @param {string} url -- post url
|
||
* @param {any} data -- post data
|
||
* @param {any} options -- 同 main.post
|
||
* @returns {Promise} -- 成功 resolve(resp),失败 reject(resp)
|
||
* ============================================================================
|
||
*/
|
||
main.postSafe = function (url, data, options) {
|
||
return new Promise(function (resolve, reject) {
|
||
main.post(url, data, options)
|
||
.done(function (resp) { resolve(resp); })
|
||
.fail(function (resp) { reject(resp); });
|
||
});
|
||
};
|
||
|
||
//确认提交
|
||
main.confirmPost = function (confirmText, url, data, options) {
|
||
var d = $.Deferred();
|
||
var okCallback = function () {
|
||
__post(url, data, d, options);
|
||
};
|
||
var cancelCallback = function () {
|
||
d.reject({ cancel: true });
|
||
};
|
||
main.confirm(confirmText, okCallback, cancelCallback, "提示");
|
||
return d.promise();
|
||
};
|
||
|
||
//以提交表单的方式打开新页面
|
||
main.formOpenNewPage = function (url, postData) {
|
||
var $f = $('<form target="_blank" method="post" action="' + url + '">')
|
||
_.each(postData, (val, key) => {
|
||
if ($.isArray(val)) {
|
||
_.each(val, (v, i) => {
|
||
$f.append('<input type="hidden" name="' + key + '[' + i + ']" value="' + v + '" />');
|
||
})
|
||
} else {
|
||
$f.append('<input type="hidden" name="' + key + '" value="' + val + '" />');
|
||
}
|
||
});
|
||
$f.appendTo("body").submit();
|
||
$f.remove();
|
||
};
|
||
|
||
//转换为string
|
||
main.toString = function (val) {
|
||
if (typeof val === 'undefined' || val === null) {
|
||
return "";
|
||
}
|
||
return val.toString();
|
||
}
|
||
|
||
/**
|
||
* 封装表单操作(表单验证+表单提交)
|
||
* @param {any} options -- 选项,可以直接传入form对象
|
||
* @param {any} options.el -- form元素或者jquery对象或者jquery选择器
|
||
* @param {any} options.submit -- 默认true,内部自动生成ajax post提交处理,如果为false则不自动提交
|
||
* 自定义提交选项:
|
||
* {
|
||
* url: string,提交地址,默认选用form元素的action属性或window.location.href,
|
||
* before: function,提交前的回调,返回false则中止提交
|
||
* after: function,提交成功后的回调
|
||
* postData: function,获取提交数据,如果未赋值则使用默认的表单序列化函数
|
||
* }
|
||
* @param {any} options.validate -- 默认true,内部自动附加验证器,false则不验证或者传入function对象使用自定义验证
|
||
* @returns {any} --
|
||
*/
|
||
main.form = function (options) {
|
||
|
||
if (!options) {
|
||
return __alert('[错误][main.form]缺少选项');
|
||
}
|
||
|
||
!$.isPlainObject(options) && (options = { el: options });
|
||
|
||
var $form, _hasSaved = 0;
|
||
|
||
function __init() {
|
||
|
||
//jquery form
|
||
$form = options.el instanceof jQuery ? options.el : $(options.el);
|
||
if (!$form.is("form")) {
|
||
$form = $form.find("form:first");
|
||
if (!$form.length) {
|
||
return __alert("[错误][main.form]没有找到form元素");
|
||
}
|
||
}
|
||
|
||
let submit = options.submit;
|
||
let validate = options.validate;
|
||
let sumbitHandler = __submitHandle;
|
||
|
||
if (submit === false) {
|
||
sumbitHandler = function () { return true; };
|
||
}
|
||
else {
|
||
!$.isPlainObject(submit) && (submit = {});
|
||
!$.trim(submit.url) && (submit.url = $.trim($form.attr('action')) || window.location.href);
|
||
options.submit = submit;
|
||
}
|
||
|
||
//设置表单验证
|
||
|
||
if ($form.attr('novalidate') !== undefined || validate === false) {
|
||
return;
|
||
}
|
||
|
||
//自定义表单验证
|
||
if (typeof validate === "function") {
|
||
$form.attr("novalidate", true);
|
||
$form.on("submit.validate", function () {
|
||
try {
|
||
validate($form) && sumbitHandler();
|
||
} finally {
|
||
return false;
|
||
}
|
||
});
|
||
return;
|
||
}
|
||
|
||
//jquery.validate表单验证
|
||
if (typeof validate === "object") {
|
||
validate = $.extend({ submitHandler: sumbitHandler }, validate);
|
||
} else {
|
||
validate = { submitHandler: sumbitHandler };
|
||
}
|
||
main.formValidate($form, validate);
|
||
}
|
||
|
||
//表单提交处理
|
||
function __submitHandle(form) {
|
||
|
||
var submit = options.submit;
|
||
|
||
try {
|
||
|
||
if (typeof submit.before === "function" && submit.before(form) === false) {
|
||
return false;
|
||
}
|
||
|
||
if (!submit.url) {
|
||
return __alert("[错误][main.form]submit.url未配置");
|
||
}
|
||
|
||
let data = typeof submit.postData === 'function' ? submit.postData() : $form.serialize();
|
||
|
||
main.post(submit.url, data).done(function (resp) {
|
||
_hasSaved += 1;
|
||
submit.after && submit.after(resp, _hasSaved);
|
||
});
|
||
|
||
} catch (e) {
|
||
console.log(e);
|
||
} finally {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
$(__init);
|
||
|
||
return {
|
||
submit() { $form.submit(); }
|
||
};
|
||
};
|
||
|
||
/**
|
||
* 表单验证
|
||
* @param {any} form -- 表单元素或者jquery对象或者jquery元素选择器
|
||
* @param {any} options --jquery validate选项或者submitHandler函数
|
||
* @returns {any} --
|
||
*/
|
||
main.formValidate = function (form, options) {
|
||
|
||
if (typeof options === "function") {
|
||
options = { submitHandler: options };
|
||
}
|
||
|
||
if (!form) {
|
||
return __alert("[main.formValidate]参数错误:form");
|
||
}
|
||
|
||
if (!(form instanceof jQuery)) {
|
||
form = $(form);
|
||
}
|
||
|
||
//添加自定义验证规则,格式:$method:$param[message]$message[+]$method:$param[message]$message...
|
||
function __addCustomRule($el) {
|
||
var rulestr = $.trim($el.data("validate"));
|
||
if (!rulestr) return;
|
||
var ruleArr = rulestr.split("[+]");
|
||
ruleArr.forEach(function (rulePart) {
|
||
rulePart = rulePart.trim();
|
||
if (!rulePart) return;
|
||
if (!/^[a-zA-Z]/.test(rulePart)) {
|
||
console.error("不是正确的验证表达式:" + rulePart);
|
||
return;
|
||
}
|
||
let message, param, method;
|
||
let index1 = rulePart.indexOf(':');
|
||
method = rulePart.substring(0, index2).trim();
|
||
if (!method) {
|
||
console.error("不是有效的验证表达式(缺少method):" + rulePart);
|
||
return;
|
||
}
|
||
let index2 = rulePart.indexOf('[message]');
|
||
if (index2 > 0) {
|
||
message = rulePart.substring(index2 + 9).trim();
|
||
}
|
||
if (index1 > 0) {
|
||
param = rulePart.substring(index1, index2).trim();
|
||
}
|
||
let rule = {};
|
||
rule[method] = param || true;
|
||
if (message) {
|
||
rule.messages = {};
|
||
rule.messages[method] = decodeURIComponent(message);
|
||
}
|
||
$el.rules("add", rule);
|
||
});
|
||
}
|
||
|
||
var validator = form.validate(options);
|
||
|
||
form.find('input').each(function () {
|
||
var type = $(this).attr("type");
|
||
if (type === "text" || type === "password") {
|
||
__addCustomRule($(this));
|
||
}
|
||
});
|
||
|
||
return validator;
|
||
};
|
||
|
||
/**
|
||
* 解码HTML
|
||
* @param {any} inputStr
|
||
* @returns
|
||
*/
|
||
main.decodeHtmlEntity = function (inputStr) {
|
||
if (typeof inputStr !== 'string') return inputStr;
|
||
if (!inputStr) return '';
|
||
var textarea = document.createElement("textarea");
|
||
textarea.innerHTML = inputStr;
|
||
return textarea.value;
|
||
};
|
||
|
||
//jquery.validator
|
||
(function (factory) {
|
||
if (typeof define === "function" && define.amd) {
|
||
define(["jquery", "jquery.validate"], factory);
|
||
} else if (typeof module === "object" && module.exports) {
|
||
module.exports = factory(require("jquery"));
|
||
} else {
|
||
factory(jQuery);
|
||
}
|
||
}(function ($) {
|
||
if (!$.validator) {
|
||
return $;
|
||
}
|
||
$.validator.setDefaults({
|
||
errorElement: "span",
|
||
submitHandler: function () {
|
||
return true;
|
||
},
|
||
errorPlacement: function (error, element) {
|
||
error.appendTo(element.parent());
|
||
}
|
||
});
|
||
|
||
$.validator.addMethod("pattern", function (val, element, param) {
|
||
if (this.optional(element) || !param) {
|
||
return true;
|
||
}
|
||
val = val.replace(/(^\s*)|(\s*$)/g, "");
|
||
return new RegExp("^" + param + "$").test(val);
|
||
}, "输入不符合要求");
|
||
|
||
$.extend($.validator.messages, {
|
||
required: "必填",
|
||
remote: "请修正此字段",
|
||
email: "请输入有效的电子邮件地址",
|
||
url: "请输入有效的网址",
|
||
date: "请输入有效的日期",
|
||
dateISO: "请输入有效的日期 (YYYY-MM-DD)",
|
||
number: "请输入有效的数字",
|
||
digits: "只能输入数字",
|
||
creditcard: "请输入有效的信用卡号码",
|
||
equalTo: "你的输入不相同",
|
||
extension: "请输入有效的后缀",
|
||
maxlength: $.validator.format("最多可以输入 {0} 个字符"),
|
||
minlength: $.validator.format("最少要输入 {0} 个字符"),
|
||
rangelength: $.validator.format("请输入长度在 {0} 到 {1} 之间的字符串"),
|
||
range: $.validator.format("请输入范围在 {0} 到 {1} 之间的数值"),
|
||
step: $.validator.format("请输入 {0} 的整数倍值"),
|
||
max: $.validator.format("请输入不大于 {0} 的数值"),
|
||
min: $.validator.format("请输入不小于 {0} 的数值")
|
||
});
|
||
|
||
return $;
|
||
}));
|
||
|
||
//例:$('#form1').serializeObject
|
||
jQuery.prototype.serializeObject = function () {
|
||
var obj = new Object();
|
||
$.each(this.serializeArray(),
|
||
function (index, param) {
|
||
if (param.name in obj) {
|
||
obj[param.name] = [].concat(obj[param.name], param.value);
|
||
} else {
|
||
obj[param.name] = param.value;
|
||
}
|
||
});
|
||
return obj;
|
||
};
|
||
|
||
if (!String.prototype.replaceAll) {
|
||
String.prototype.replaceAll = function (oldString, newString) {
|
||
oldString = _.escapeRegExp(oldString);
|
||
return this.replace(new RegExp(oldString, "gm"), newString)
|
||
}
|
||
}
|