Files
zszq-trs/YLErpWeb/wwwroot/Scripts/base/main.js
T
hjhan 39fe5a69e6 fix(auth): 解决AJAX请求401未授权处理问题
- 将全局AJAX 401处理逻辑从main.js移至_MainLayout.cshtml
- 移除main.js中的重复401状态检查代码
- 在Cookie认证配置中添加自定义重定向事件处理
- 优化中间件中AJAX请求检测逻辑,支持多种请求类型判断
- 更新依赖注入和服务注册中的命名空间引用
- 添加日志管理器相关引用和配置优化
2026-04-27 11:35:41 +08:00

769 lines
23 KiB
JavaScript

//依赖类库:lodash.js + jquery + jquery.validate + bootstrap.emodal.js
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>&times;</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;
}
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
*/
main.post = function (url, data, options) {
var d = $.Deferred();
__post(url, data, d, options);
return d.promise();
};
//确认提交
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)
}
}