邹景立 568fb5d274 refactor(editor): 统一富文本编辑器实现
将后台富文本模块统一切换为 wangEditor 加载入口,移除 CKEditor4/CKEditor5 的动态选择与运行时编辑器驱动配置,降低前端资源体积和配置复杂度。

重写 createEditor 封装,支持初始化复用、内容同步、销毁恢复、上传配置透传和图片/视频自定义上传,并在表单提交前通过 data-editor-source 统一回写编辑器内容。

同步更新系统配置页面、接口脚本、初始化数据、语言包、测试用例和业务表单引用,将原 ckeditor 模块调用改为 editor 模块,避免遗留编辑器入口失效。
2026-05-20 23:17:43 +08:00

1512 lines
75 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// +----------------------------------------------------------------------
// | Static Plugin for ThinkAdmin
// +----------------------------------------------------------------------
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
// +----------------------------------------------------------------------
// | 官方网站: https://thinkadmin.top
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// | 免责声明 ( https://thinkadmin.top/disclaimer )
// +----------------------------------------------------------------------
// | gitee 代码仓库https://gitee.com/zoujingli/think-plugs-static
// | github 代码仓库https://github.com/zoujingli/think-plugs-static
// +----------------------------------------------------------------------
/*! 应用根路径,静态插件库路径,动态插件库路径 */
let srcs = document.scripts[document.scripts.length - 1].src.split('/');
window.appRoot = srcs.slice(0, -2).join('/') + '/';
window.baseRoot = srcs.slice(0, -1).join('/') + '/';
window.twebRoot = window.taSystem || window.appRoot + "system";
window.tapiRoot = window.taSystemApi || window.appRoot + (window.taApiPrefix || 'api') + "/system";
window.storageRoot = window.taStorage || window.appRoot + "storage";
window.storageApiRoot = window.taStorageApi || window.appRoot + (window.taApiPrefix || 'api') + "/storage";
/*! 挂载 layui & jquery 对象 */
layui.config({base: baseRoot + 'plugs/layui_exts/'});
window.form = layui.form, window.layer = layui.layer;
window.laytpl = layui.laytpl, window.laydate = layui.laydate;
window.jQuery = window.$ = window.jQuery || window.$ || layui.$;
window.jQuery.fn.size || (window.jQuery.fn.size = () => this.length);
window.jQuery.auth = window.jQuery.auth || new function () {
this.storage = 'ta-system-token';
this.header = function () {
return window.taTokenHeader || 'Authorization';
};
this.scheme = function () {
return window.taTokenScheme || 'Bearer';
};
this.normalize = function (token) {
token = $.trim(String(token || ''));
if (!token.length) return '';
let matched = token.match(/^Bearer\s+(.+)$/i);
return $.trim(matched ? matched[1] : token);
};
this.getStorage = function () {
try {
return window.localStorage.getItem(this.storage) || '';
} catch (e) {
return '';
}
};
this.setStorage = function (value) {
try {
window.localStorage.setItem(this.storage, value);
} catch (e) {
}
return value;
};
this.delStorage = function () {
try {
window.localStorage.removeItem(this.storage);
} catch (e) {
}
};
this.get = function () {
return this.normalize(this.getStorage());
};
this.getSeed = function () {
let token = this.normalize(window.taTokenValue || '');
window.taTokenValue = '';
return token;
};
this.set = function (token) {
token = this.normalize(token);
if (!token.length) return this.clear();
return this.setStorage(token);
};
this.clear = function () {
this.delStorage();
return '';
};
this.apply = function () {
let token = this.getSeed() || this.get();
return token ? this.set(token) : this.clear();
};
this.value = function () {
let token = this.get(), scheme = $.trim(String(this.scheme() || ''));
if (!token.length) return '';
return scheme ? scheme + ' ' + token : token;
};
this.getStatus = function (ret) {
if (!ret || typeof ret !== 'object') return 0;
let value = ret.code;
if (value === undefined || value === null || value === '') value = ret.status;
value = parseInt(value, 10);
return Number.isFinite(value) ? value : 0;
};
this.parseResponse = function (payload) {
if (payload && typeof payload === 'object' && !payload.responseText) return payload;
let text = typeof payload === 'string' ? payload : (payload && typeof payload.responseText === 'string' ? payload.responseText : '');
text = $.trim(String(text || ''));
if (!text.length || !/^[\[{]/.test(text)) return null;
try {
return JSON.parse(text);
} catch (e) {
return null;
}
};
this.getError = function (ret) {
return $.trim(String(ret && ret.error || ''));
};
this.isUnauthorized = function (ret) {
return this.getError(ret) === 'auth.unauthorized' || this.getStatus(ret) === 401;
};
this.isForbidden = function (ret) {
return this.getError(ret) === 'auth.forbidden' || this.getStatus(ret) === 403;
};
this.isAuthError = function (ret) {
return this.isUnauthorized(ret) || this.isForbidden(ret);
};
this.isSuccess = function (ret) {
return !!(ret && typeof ret === 'object') && this.getStatus(ret) === 200;
};
this.accept = function (ret) {
if (!ret || typeof ret !== 'object') return this.get();
if (Object.prototype.hasOwnProperty.call(ret, 'token')) {
return typeof ret.token === 'string' && ret.token.length ? this.set(ret.token) : this.clear();
}
if (this.isUnauthorized(ret)) {
return this.clear();
}
return this.get();
};
};
window.$.auth = window.jQuery.auth;
window.jQuery.ajaxPrefilter(function (options) {
if (options.crossDomain) return;
options.headers = options.headers || {};
let header = $.auth.header(), token = $.auth.value();
if (token && typeof options.headers[header] === 'undefined') {
options.headers[header] = token;
}
});
window.jQuery(document).ajaxError(function (event, xhr, options) {
if (options && options.global === false) return;
if (!xhr || xhr.taHandled) return;
let ret = $.auth.parseResponse(xhr);
if (!ret || typeof ret !== 'object') return;
xhr.taHandled = true;
$.auth.accept(ret);
$.msg.auto(ret, ret.wait || undefined);
});
/*! 配置标准模块加载 */
layui.extend({
excel: '{/}' + baseRoot + 'plugs/layui_exts/excel',
taExcel: '{/}' + baseRoot + 'plugs/system/excel',
taQueue: '{/}' + baseRoot + 'plugs/system/queue',
taValidate: '{/}' + baseRoot + 'plugs/system/validate',
});
window.$.module = window.jQuery.module || new function () {
let that = this, scriptCache = {}, styleCache = {}, moduleCache = {};
this.registry = {
jquery: {api: function () { return layui.$; }},
md5: {src: baseRoot + 'plugs/jquery/md5.min.js', api: function () { return window.SparkMD5; }},
notify: {
css: baseRoot + 'plugs/notify/theme.css',
src: baseRoot + 'plugs/notify/notify.min.js',
api: function () {
return window.Notify || (window.Notify = window.GrowlNotification);
}
},
vue: {src: baseRoot + 'plugs/vue/vue.min.js', api: 'Vue'},
angular: {src: baseRoot + 'plugs/angular/angular.min.js', api: 'angular'},
echarts: {src: baseRoot + 'plugs/echarts/echarts.min.js', api: 'echarts'},
artplayer: {src: baseRoot + 'plugs/jquery/artplayer.min.js', api: 'Artplayer'},
compressor: {src: baseRoot + 'plugs/jquery/compressor.min.js', api: 'Compressor'},
pcasunzips: {src: baseRoot + 'plugs/jquery/pcasunzips.js', api: 'PCAS'},
sortablejs: {src: baseRoot + 'plugs/sortable/sortable.min.js', api: 'Sortable'},
'vue.sortable': {
deps: ['vue', 'sortablejs'],
src: baseRoot + 'plugs/sortable/vue.draggable.min.js',
api: function () {
return window.vuedraggable || window.VueDraggable || window.draggable;
}
},
'jquery.autocompleter': {
css: baseRoot + 'plugs/jquery/autocompleter.css',
src: baseRoot + 'plugs/jquery/autocompleter.min.js',
api: function () { return layui.$; }
},
'jquery.ztree': {
css: baseRoot + 'plugs/ztree/zTreeStyle/zTreeStyle.css',
src: baseRoot + 'plugs/ztree/ztree.all.min.js',
api: function () { return layui.$.fn.zTree || layui.$; }
},
'jquery.masonry': {
src: baseRoot + 'plugs/jquery/masonry.min.js',
api: function () { return window.Masonry || layui.$; }
},
upload: {
deps: ['md5', 'notify'],
loader: function () {
return that.loadScript(storageApiRoot + '/upload/index?', 'upload').then(function () {
return window.taUploadModule || window.SystemUploadAdapter;
});
}
},
editor: {
deps: ['upload'],
css: baseRoot + 'plugs/editor/css/style.css',
src: [baseRoot + 'plugs/editor/index.js', baseRoot + 'plugs/editor/create.js'],
api: function () { return window.createEditor; }
},
excel: {layui: 'taExcel'},
queue: {layui: 'taQueue'},
validate: {layui: 'taValidate'},
};
this.toArray = function (value) {
if (!value) return [];
return Array.isArray(value) ? value : [value];
};
this.toStyleId = function (url) {
return 'ta-style-' + String(url).replace(/[^a-z0-9]+/ig, '-').replace(/^-+|-+$/g, '').toLowerCase();
};
this.loadStyle = function (url) {
if (!url) return Promise.resolve(null);
if (styleCache[url]) return styleCache[url];
styleCache[url] = new Promise(function (resolve) {
layui.link(url, function () {
resolve(url);
}, that.toStyleId(url));
});
return styleCache[url];
};
this.loadScript = function (url, key) {
key = key || url;
if (scriptCache[key]) return scriptCache[key];
scriptCache[key] = new Promise(function (resolve, reject) {
let script = document.createElement('script');
script.async = true;
script.charset = 'utf-8';
script.src = url;
script.onerror = function () {
reject(new Error('Module script load failed: ' + url));
};
script.onload = function () {
resolve(url);
};
document.head.appendChild(script);
});
return scriptCache[key];
};
this.loadSources = function (sources, key) {
return this.toArray(sources).reduce(function (promise, src, idx) {
return promise.then(function () {
return that.loadScript(src, (key || 'module') + '-' + idx);
});
}, Promise.resolve());
};
this.getExport = function (config, value) {
if (typeof config.api === 'function') return config.api(value);
if (typeof config.api === 'string') return window[config.api];
return value;
};
this.useLayui = function (name) {
return new Promise(function (resolve, reject) {
try {
layui.use(name, function () {
resolve(arguments[0]);
});
} catch (e) {
reject(e);
}
});
};
this.load = function (name) {
if (moduleCache[name]) return moduleCache[name];
if (!this.registry[name]) return Promise.reject(new Error('Unsupported module: ' + name));
moduleCache[name] = Promise.all(this.toArray(this.registry[name].deps).map(function (dep) {
return that.load(dep);
}).concat(this.toArray(this.registry[name].css).map(function (css) {
return that.loadStyle(css);
}))).then(function () {
let config = that.registry[name];
if (config.layui) {
return that.useLayui(config.layui);
} else if (typeof config.loader === 'function') {
return config.loader();
} else if (config.src) {
return that.loadSources(config.src, name).then(function () {
return that.getExport(config);
});
} else {
return that.getExport(config);
}
});
return moduleCache[name];
};
this.use = function (names, done) {
names = this.toArray(names);
return Promise.all(names.map(function (name) {
return that.load(name);
})).then(function (mods) {
typeof done === 'function' && done.apply(window, mods);
return mods;
});
};
};
window.jQuery.module = window.$.module;
$.module.use(['editor']);
$(function () {
window.$body = $('body');
if ($body.find('form[data-login-form]').length) {
$.auth.clear();
} else {
$.auth.apply();
}
/*! 基础函数工具 */
$.base = new function () {
/*! 注册单次事件 */
this.onEvent = function (event, select, callable) {
return $body.off(event, select).on(event, select, callable);
};
/*! 注册确认回调 */
this.onConfirm = function (confirm, callable) {
return confirm ? $.msg.confirm(confirm, callable) : callable();
};
/*! 获取加载回调 */
this.onConfirm.getLoadCallable = function (tabldId, callable) {
typeof callable === 'function' && callable();
return tabldId ? function (ret, time) {
if ($.auth.isAuthError(ret)) {
$.msg.auto(ret, time);
return false;
}
// 单独处理 javascript: 返回内容处理
if (typeof ret.data === 'string' && ret.data.indexOf('javascript:') === 0) {
$.msg.goto(ret.data)
}
if (!$.auth.isSuccess(ret)) return true;
time === 'false' ? $.layTable.reload(tabldId) : $.msg.success(ret.info, time, function () {
$.layTable.reload(tabldId);
});
return false;
} : false;
};
/*! 读取 data-value & data-rule 并应用到 callable */
this.applyRuleValue = function (elem, data, callabel) {
// 新 tableId 规则兼容处理
if (elem.dataset.tableId && elem.dataset.rule) {
let idx1, idx2, temp, regx, field, rule = {};
let json = layui.table.checkStatus(elem.dataset.tableId).data;
layui.each(elem.dataset.rule.split(';'), function (idx, item, attr) {
attr = item.split('#', 2), rule[attr[0]] = attr[1];
});
for (idx1 in rule) {
temp = [], regx = new RegExp(/^{(.*?)}$/);
if (regx.test(rule[idx1]) && (field = rule[idx1].replace(regx, '$1'))) {
for (idx2 in json) if (json[idx2][field]) temp.push(json[idx2][field]);
if (temp.length < 1) return $.msg.tips('请选择需要更改的数据!'), false;
data[idx1] = temp.join(',');
} else {
data[idx1] = rule[idx1];
}
}
return $.base.onConfirm(elem.dataset.confirm, function () {
return callabel.call(elem, data, elem, elem.dataset || {});
});
} else if (elem.dataset.value || elem.dataset.rule) {
let value = elem.dataset.value || (function (rule, array) {
$(elem.dataset.target || 'input[type=checkbox].list-check-box').map(function () {
this.checked && array.push(this.value);
});
return array.length > 0 ? rule.replace('{key}', array.join(',')) : '';
})(elem.dataset.rule || '', []) || '';
if (value.length < 1) return $.msg.tips('请选择需要更改的数据!'), false;
value.split(';').forEach(function (item) {
data[item.split('#')[0]] = item.split('#')[1];
});
return $.base.onConfirm(elem.dataset.confirm, function () {
return callabel.call(elem, data, elem, elem.dataset || {});
});
} else {
return $.base.onConfirm(elem.dataset.confirm, function () {
return callabel.call(elem, data, elem, elem.dataset || {});
});
}
}
};
/*! Builder 模块运行时 */
$.builder = new function () {
let that = this;
this.registry = {};
this.define = function (name, handler, deps) {
name = $.trim(String(name || ''));
if (!name.length) return this;
if (typeof handler === 'function') {
this.registry[name] = {init: handler, deps: this.normalizeNames(deps)};
} else if (handler && typeof handler === 'object') {
this.registry[name] = handler;
}
return this;
};
this.normalizeNames = function (value) {
if (!value) return [];
return (Array.isArray(value) ? value : [value]).map(function (item) {
return $.trim(String(item || ''));
}).filter(function (item) {
return item.length > 0;
});
};
this.parseSchemaNode = function (node) {
let content = $.trim($(node).html());
if (!content.length) return null;
try {
return JSON.parse(content);
} catch (e) {
console.error('Builder schema parse failed.', e);
return null;
}
};
this.parseModules = function (value) {
if (typeof value !== 'string' || !value.length) return [];
try {
value = JSON.parse(value);
} catch (e) {
console.error('Builder modules parse failed.', e);
return [];
}
return $.map(Array.isArray(value) ? value : [value], function (item) {
if (!item || typeof item !== 'object') return null;
let name = $.trim(String(item.name || ''));
if (!name.length) return null;
return {name: name, config: item.config || {}};
});
};
this.getScope = function ($node) {
let $scope = $node.is('[data-builder-scope]') ? $node : $node.closest('[data-builder-scope]');
return $scope.length ? $scope.eq(0) : $();
};
this.getScopeMeta = function ($scope) {
if (!$scope.length) return {type: '', schemas: {}, schema: null};
let cached = $scope.data('builder.scope.meta');
if (cached) return cached;
let meta = {
type: $.trim(String($scope.attr('data-builder-scope') || '')),
schemas: {
page: that.parseSchemaNode($scope.find('script.page-builder-schema:first')),
form: that.parseSchemaNode($scope.find('script.form-builder-schema:first')),
},
schema: null,
};
meta.schema = meta.type && meta.schemas[meta.type] ? meta.schemas[meta.type] : (meta.schemas.page || meta.schemas.form || null);
$scope.data('builder.scope.meta', meta);
return meta;
};
this.resolveEntry = function (name) {
return this.registry[name] && typeof this.registry[name] === 'object' ? this.registry[name] : null;
};
this.loadEntry = function (name) {
let entry = this.resolveEntry(name), deps = [];
if (entry && entry.module) deps = this.normalizeNames(entry.module);
else if (entry && entry.deps) deps = this.normalizeNames(entry.deps);
else if ($.module.registry && $.module.registry[name]) deps = [name];
if (deps.length < 1) {
return Promise.resolve({entry: entry, module: null, modules: []});
}
return $.module.use(deps).then(function (mods) {
return {entry: entry, module: mods[0] || null, modules: mods};
});
};
this.resolveInitializer = function (name, entry, module) {
if (entry && typeof entry.init === 'function') return entry.init;
if (typeof module === 'function') return module;
if (module && typeof module.builderInit === 'function') return module.builderInit;
if (module && typeof module.init === 'function') return module.init;
return null;
};
this.initNode = function (node) {
let $node = $(node), modules = this.parseModules($node.attr('data-builder-modules'));
if (modules.length < 1) return Promise.resolve([]);
let state = $node.data('builder.modules.state') || {};
let $scope = this.getScope($node), meta = this.getScopeMeta($scope);
return Promise.all(modules.map(function (item) {
if (state[item.name] === 'pending' || state[item.name] === 'done') {
return Promise.resolve();
}
state[item.name] = 'pending';
$node.data('builder.modules.state', state);
return that.loadEntry(item.name).then(function (loaded) {
let callable = that.resolveInitializer(item.name, loaded.entry, loaded.module);
if (typeof callable !== 'function') {
state[item.name] = 'missed';
$node.data('builder.modules.state', state);
return null;
}
let context = {
name: item.name,
config: item.config || {},
node: node,
$node: $node,
scope: $scope.get(0) || null,
$scope: $scope,
type: meta.type,
schema: meta.schema,
pageSchema: meta.schemas.page || null,
formSchema: meta.schemas.form || null,
module: loaded.module,
modules: loaded.modules,
};
return Promise.resolve(callable.call(node, context, that)).then(function (result) {
state[item.name] = 'done';
$node.data('builder.modules.state', state);
return result;
});
}).catch(function (e) {
state[item.name] = 'error';
$node.data('builder.modules.state', state);
console.error('Builder module init failed: ' + item.name, e);
return null;
});
}));
};
this.init = function ($dom) {
let $root = $($dom || $body);
let $nodes = $root.find('[data-builder-modules]');
$nodes = $nodes.add($root.filter('[data-builder-modules]'));
return Promise.all($nodes.get().map(function (node) {
return that.initNode(node);
}));
};
};
/*! 消息组件实例 */
$.msg = new function () {
this.idx = [];
this.mdx = [];
this.shade = [0.02, '#000000'];
/*! 关闭元素所在窗口 */
this.closeThisModal = function (element) {
layer.close($(element).parents('div.layui-layer-page').attr('times'));
};
/*! 关闭顶层最新窗口 */
this.closeLastModal = function () {
while ($.msg.mdx.length > 0 && (this.tdx = $.msg.mdx.pop()) > 0) {
if ($('#layui-layer' + this.tdx).length) return layer.close(this.tdx);
}
};
/*! 关闭消息框 */
this.close = function (idx) {
if (idx !== null) return layer.close(idx);
for (let i in this.idx) $.msg.close(this.idx[i]);
return (this.idx = []) !== false;
};
/*! 弹出警告框 */
this.alert = function (msg, call) {
let idx = layer.alert(msg, {end: call, scrollbar: false});
return $.msg.idx.push(idx), idx;
};
/*! 显示成功类型的消息 */
this.success = function (msg, time, call) {
let idx = layer.msg(msg, {icon: 1, shade: this.shade, scrollbar: false, end: call, time: (time || 2) * 1000, shadeClose: true});
return $.msg.idx.push(idx), idx;
};
/*! 显示失败类型的消息 */
this.error = function (msg, time, call) {
let idx = layer.msg(msg, {icon: 2, shade: this.shade, scrollbar: false, time: (time || 3) * 1000, end: call, shadeClose: true});
return $.msg.idx.push(idx), idx;
};
/*! 状态消息提示 */
this.tips = function (msg, time, call) {
let idx = layer.msg(msg, {time: (time || 3) * 1000, shade: this.shade, end: call, shadeClose: true});
return $.msg.idx.push(idx), idx;
};
/*! 显示加载提示 */
this.loading = function (msg, call) {
let idx = msg ? layer.msg(msg, {icon: 16, scrollbar: false, shade: this.shade, time: 0, end: call}) : layer.load(0, {time: 0, scrollbar: false, shade: this.shade, end: call});
return $.msg.idx.push(idx), idx;
};
/*! Notify 调用入口 */
// https://www.jq22.com/demo/jquerygrowl-notification202104021049
this.notify = function (title, message, time, option) {
$.module.use(['notify'], function (Notify) {
Notify.notify(Object.assign({title: title || '', description: message || '', position: 'top-right', closeTimeout: time || 3000, width: '400px'}, option || {}));
});
};
/*! 页面加载层 */
this.page = new function () {
this.$body = $('body>.think-page-loader');
this.$main = $('.think-page-body+.think-page-loader');
this.stat = function () {
return this.$body.is(':visible');
}, this.done = function () {
return $.msg.page.$body.fadeOut();
}, this.show = function () {
this.stat() || this.$main.removeClass('layui-hide').show();
}, this.hide = function () {
if (this.time) clearTimeout(this.time);
this.time = setTimeout(function () {
($.msg.page.time = 0) || $.msg.page.$main.fadeOut();
}, 200);
};
};
/*! 确认对话框 */
this.confirm = function (msg, ok, no) {
return layer.confirm(msg, {title: '操作确认', btn: ['确认', '取消']}, function (idx) {
(typeof ok === 'function' && ok.call(this, idx)), $.msg.close(idx);
}, function (idx) {
(typeof no === 'function' && no.call(this, idx)), $.msg.close(idx);
});
};
/*! 自动处理JSON数据 */
this.auto = function (ret, time) {
$.auth.accept(ret);
let status = $.auth.getStatus(ret);
let url = ret.url || (typeof ret.data === 'string' ? ret.data : '');
let msg = ret.msg || (typeof ret.info === 'string' ? ret.info : '');
if ($.auth.isUnauthorized(ret)) {
return url ? this.error(msg, 3, function () {
$.form.goto(url);
}) : this.error(msg, 3);
}
if ($.auth.isForbidden(ret)) {
return this.error(msg, 3);
}
if (status === 200 && time === 'false') {
return url ? $.form.goto(url) : $.form.reload();
} else return status === 200 ? this.success(msg, time, function () {
$.msg.closeLastModal(url ? $.form.goto(url) : $.form.reload());
}) : this.error(msg, 3, function () {
$.form.goto(url);
});
};
};
/*! 表单自动化组件 */
$.form = new function () {
/*! 内容区选择器 */
this.selecter = '.layui-layout-admin>.layui-body>.think-page-body';
/*! 刷新当前页面 */
this.reload = function (force) {
if (force) return top.location.reload();
if (self !== top) return location.reload();
return $.menu.href(location.hash);
};
/*! 内容区域动态加载后初始化 */
this.reInit = function ($dom) {
$dom = $dom || $(this.selecter);
layui.form.render() && layui.element.render() && $(window).trigger('scroll');
$.vali.listen($dom);
$dom.find('[required]').map(function () {
this.$parent = $(this).parent();
if (this.$parent.is('label')) this.$parent.addClass('label-required-prev'); else this.$parent.prevAll('label.layui-form-label').addClass('label-required-next');
});
$dom.find('[data-lazy-src]:not([data-lazy-loaded])').map(function () {
if (this.dataset.lazyLoaded === 'true') return; else this.dataset.lazyLoaded = 'true';
if (this.nodeName === 'IMG') this.src = this.dataset.lazySrc; else this.style.backgroundImage = 'url(' + this.dataset.lazySrc + ')';
});
$dom.find('input[data-date-range]').map(function () {
this.setAttribute('autocomplete', 'off'), laydate.render({
type: this.dataset.dateRange || 'date', range: true, elem: this, done: function (value) {
$(this.elem).val(value).trigger('change');
}
});
});
$dom.find('input[data-date-input]').map(function () {
this.setAttribute('autocomplete', 'off'), laydate.render({
type: this.dataset.dateInput || 'date', range: false, elem: this, done: function (value) {
$(this.elem).val(value).trigger('change');
}
});
});
$.builder.init($dom);
$body.trigger('reInit', $dom);
return $dom;
};
/*! 在内容区显示视图 */
this.show = function (html) {
$.form.reInit($(this.selecter).html(html));
};
/*! 异步加载的数据 */
this.load = function (url, data, method, callable, loading, tips, time, headers) {
// 如果主页面 loader 显示中,绝对不显示 loading 图标
loading = $('.layui-page-loader').is(':visible') ? false : loading;
let defer = jQuery.Deferred(), loadidx = loading !== false ? $.msg.loading(tips) : 0;
$.ajax({
global: false,
data: data || {}, type: method || 'GET', url: $.menu.parseUri(url), beforeSend: function (xhr, i) {
if (typeof Pace === 'object' && loading !== false) Pace.restart();
if (typeof headers === 'object') for (i in headers) xhr.setRequestHeader(i, headers[i]);
}, error: function (XMLHttpRequest, $dialog, layIdx, iframe) {
let ret = $.auth.parseResponse(XMLHttpRequest);
if (typeof ret === 'object') {
XMLHttpRequest.taHandled = true;
$.auth.accept(ret);
time = time || ret.wait || undefined;
if (defer.notify('load.error', ret, XMLHttpRequest) && typeof callable === 'function' && callable.allowHttpError === true && callable.call($.form, ret, time, defer) === false) {
return false;
}
return $.msg.auto(ret, time);
}
// 异常消息显示处理
if (defer.notify('load.error') && parseInt(XMLHttpRequest.status) !== 200 && XMLHttpRequest.responseText.indexOf('Call Stack') > -1) try {
layIdx = layer.open({title: XMLHttpRequest.status + ' - ' + XMLHttpRequest.statusText, type: 2, move: false, content: 'javascript:;'});
layer.full(layIdx), $dialog = $('#layui-layer' + layIdx), iframe = $dialog.find('iframe').get(0);
(iframe.contentDocument || iframe.contentWindow.document).write(XMLHttpRequest.responseText);
iframe.winClose = {width: '30px', height: '30px', lineHeight: '30px', fontSize: '30px', marginLeft: 0};
iframe.winTitle = {color: 'red', height: '60px', lineHeight: '60px', fontSize: '20px', textAlign: 'center', fontWeight: 700};
$dialog.find('.layui-layer-title').css(iframe.winTitle) && $dialog.find('.layui-layer-setwin').css(iframe.winClose).find('span').css(iframe.winClose);
setTimeout(function () {
$(iframe).height($dialog.height() - 60);
}, 100);
} catch (e) {
layer.close(layIdx);
}
layer.closeAll('loading');
if (parseInt(XMLHttpRequest.status) !== 200) {
$.msg.tips('E' + XMLHttpRequest.status + ' - 服务器繁忙,请稍候再试!');
} else {
this.success(XMLHttpRequest.responseText);
}
}, success: function (res) {
$.auth.accept(res);
if (typeof res === 'object' && $.auth.isAuthError(res)) {
time = time || res.wait || undefined;
return $.msg.auto(res, time);
}
defer.notify('load.success', res) && (time = time || res.wait || undefined);
if (typeof callable === 'function' && callable.call($.form, res, time, defer) === false) return false;
return typeof res === 'object' ? $.msg.auto(res, time) : $.form.show(res);
}, complete: function () {
defer.notify('load.complete') && $.msg.page.done() && $.msg.close(loadidx);
}
});
return defer;
};
/*! 兼容跳转与执行 */
this.goto = function (url) {
if (typeof url !== 'string' || url.length < 1) return;
if (url.toLowerCase().indexOf('javascript:') === 0) {
return eval($.trim(url.substring(11)));
} else {
return location.href = url;
}
};
/*! 以 HASH 打开新网页 */
this.href = function (url, elem, hash, base, current, parser) {
this.isMenu = !!(elem && elem.dataset.menuNode);
if (this.isMenu) layui.sessionData('pages', null);
if (typeof url !== 'string' || url === '#' || url === '') {
return this.isMenu && $('[data-menu-node^="' + elem.dataset.menuNode + '-"]:first').trigger('click');
}
hash = hash || $.menu.parseUri(url, elem);
base = url.indexOf('#') > -1 ? url.split('#', 2)[0] : '';
current = location.pathname + location.search;
if (/^(https?:)?(\/\/|\\\\)/i.test(base)) {
parser = document.createElement('a');
parser.href = base;
base = parser.pathname + parser.search;
}
this.isRedirect = base.length > 0 && base !== current;
this.isRedirect ? location.href = url.split('#', 2)[0] + '#' + hash : location.hash = hash;
};
/*! 加载 HTML 到 BODY 位置 */
this.open = function (url, data, call, load, tips) {
this.load(url, data, 'get', function (ret) {
return (typeof ret === 'object' ? $.msg.auto(ret) : $.form.show(ret)), false;
}, load, tips);
};
/*! 打开 IFRAME 窗口 */
this.iframe = function (url, name, area, offset, destroy, success, isfull, maxmin) {
if (typeof area === 'string' && area.indexOf('[') === 0) area = eval('(' + area + ')');
this.idx = layer.open({title: name || '窗口', type: 2, area: area || ['800px', '580px'], end: destroy || null, offset: offset, fixed: true, maxmin: maxmin || false, content: url, success: success});
return isfull && layer.full(this.idx), this.idx;
};
/*! 加载 HTML 到弹出层,返回 refer 对象 */
this.modal = function (url, data, name, call, load, tips, area, offset, isfull, maxmin) {
return this.load(url, data, 'GET', function (res, time, defer) {
if (typeof area === 'string' && area.indexOf('[') === 0) area = eval('(' + area + ')');
return typeof res === 'object' ? $.msg.auto(res) : $.msg.mdx.push(this.idx = layer.open({
type: 1, btn: false, area: area || '800px', offset: offset || 'auto', resize: false, content: res, maxmin: maxmin,
title: name === 'false' ? '' : name, end: () => defer.notify('modal.close'), success: function ($dom, idx) {
defer.notify('modal.success', $dom) && typeof call === 'function' && call.call($.form, $dom);
$.form.reInit($dom.off('click', '[data-close]').on('click', '[data-close]', function () {
$.base.onConfirm(this.dataset.confirm, () => layer.close(idx));
}));
}
})) && isfull && layer.full(this.idx), false;
}, load, tips);
};
};
/*! 后台菜单辅助插件 */
$.menu = new function () {
/*! 计算 URL 地址中有效的 URI */
this.getUri = function (uri) {
uri = uri || location.href;
uri = uri.indexOf(location.host) > -1 ? uri.split(location.host)[1] : uri;
return (uri.indexOf('#') > -1 ? uri.split('#')[1] : uri).split('?')[0];
};
/*! 通过 URI 查询最佳菜单 NODE */
this.queryNode = function (uri, node) {
// 如果该节点存在直接返回 Node 值
if (/^m-/.test(node = node || location.href.replace(/.*spm=([\d\-m]+).*/ig, '$1'))) {
if ($('[data-menu-node="' + node + '"]').length) return node;
}
let path = uri.replace(/\.html$/ig, '');
// 尝试通过 URI 查询节点值
let $menu = $('[data-menu-node][data-open*="' + path + '"]');
if ($menu.length) return $menu.get(0).dataset.menuNode;
// 尝试通过 URL 查询节点值
$menu = $('[data-menu-node][data-open~="#' + path + '"]');
return $menu.length ? $menu.get(0).dataset.menuNode : (/^m-/.test(node || '') ? node : '');
};
/*! 完整 URL 转 URI 地址 */
this.parseUri = function (uri, elem, vars, temp, attrs, target) {
vars = {}, attrs = [], elem = elem || document.createElement('a');
target = typeof uri === 'string' ? uri : '';
target = target.indexOf('#') > -1 ? target.split('#', 2)[1] : target;
if (target.indexOf('?') > -1) target.split('?')[1].split('&').forEach(function (item) {
if (item.indexOf('=') > -1 && (temp = item.split('=')) && typeof temp[0] === 'string' && temp[0].length > 0) {
vars[temp[0]] = encodeURIComponent(decodeURIComponent(temp[1].replace(/%2B/ig, '%20')));
}
});
uri = this.getUri(uri);
if (typeof vars.spm !== 'string') vars.spm = elem.dataset.menuNode || this.queryNode(uri) || '';
if (typeof vars.spm !== 'string' || vars.spm.length < 1) delete vars.spm;
for (let i in vars) attrs.push(i + '=' + vars[i]);
return uri + (attrs.length > 0 ? '?' + attrs.join('&') : '');
};
/*! 后台菜单动作初始化 */
this.listen = function () {
let layout = $('.layui-layout-admin'), mini = 'layui-layout-left-mini';
/*! 菜单切及MiniTips处理 */
$.base.onEvent('click', '[data-target-menu-type]', function () {
layui.data('SystemMenuType', {key: 'mini', value: layout.toggleClass(mini).hasClass(mini)});
}).on('click', '[data-submenu-layout]>a', function () {
setTimeout("$.menu.sync(1)", 100);
}).on('mouseenter', '[data-target-tips]', function (evt) {
if (!layout.hasClass(mini) || !this.dataset.targetTips) return;
evt.idx = layer.tips(this.dataset.targetTips, this, {time: 0});
$(this).mouseleave(() => layer.close(evt.idx));
});
/*! 监听窗口大小及HASH切换 */
return $(window).on('resize', function () {
(layui.data('SystemMenuType')['mini'] || $body.width() < 1000) ? layout.addClass(mini) : layout.removeClass(mini);
}).trigger('resize').on('hashchange', function () {
if (/^#(https?:)?(\/\/|\\\\)/.test(location.hash)) return $.msg.tips('禁止访问外部链接!');
return location.hash.length < 1 ? $body.find('[data-menu-node]:first').trigger('click') : $.menu.href(location.hash);
}).trigger('hashchange');
};
/*! 同步二级菜单展示状态(1同步缓存,2同步展示) */
this.sync = function (mode) {
$('[data-submenu-layout]').map(function () {
let node = this.dataset.submenuLayout;
if (mode === 1) layui.data('SystemMenuState', {key: node, value: $(this).hasClass('layui-nav-itemed') ? 2 : 1});
if (mode === 2) (layui.data('SystemMenuState')[node] || 0) === 2 && $(this).addClass('layui-nav-itemed');
});
};
/*! 页面 LOCATION-HASH 跳转 */
this.href = function (hash, node) {
if ((hash || '').length < 1) return $('[data-menu-node]:first').trigger('click');
$.form.load(hash, {}, 'get', false, !$.msg.page.stat()), $.menu.sync(2);
// 菜单选择切换
if (/^m-/.test(node = node || $.menu.queryNode($.menu.getUri()))) {
let arr = node.split('-'), tmp = arr.shift(), all = $('a[data-menu-node]').parent('.layui-this');
while (arr.length > 0) {
tmp = tmp + '-' + arr.shift();
all = all.not($('a[data-menu-node="' + tmp + '"]').parent().addClass('layui-this'));
}
all.removeClass('layui-this');
// 菜单模式切换
if (node.split('-').length > 2) {
let tmp = node.split('-'), pnode = tmp.slice(0, 2).join('-'), snode = tmp.slice(0, 3).join('-')
$('[data-menu-layout]').not($('[data-menu-layout="' + pnode + '"]').removeClass('layui-hide')).addClass('layui-hide');
$('[data-submenu-layout="' + snode + '"]').addClass('layui-nav-itemed');
$('.layui-layout-admin').removeClass('layui-layout-left-hide');
} else {
$('.layui-layout-admin').addClass('layui-layout-left-hide');
}
setTimeout("$.menu.sync(1);", 100);
}
};
};
/*! 表单转JSON */
$.fn.formToJson = function () {
let self = this, data = {}, push = {};
let rules = {key: /\w+|(?=\[])/g, push: /^$/, fixed: /^\d+$/, named: /^\w+$/};
this.build = function (base, key, value) {
return (base[key] = value), base;
}, this.pushCounter = function (name) {
if (push[name] === undefined) push[name] = 0;
return push[name]++;
}, $.each($(this).serializeArray(), function () {
let key, keys = this.name.match(rules.key), merge = this.value, name = this.name;
while ((key = keys.pop()) !== undefined) {
name = name.replace(new RegExp("\\[" + key + "\\]$"), '');
if (key.match(rules.push)) merge = self.build([], self.pushCounter(name), merge);
else if (key.match(rules.fixed)) merge = self.build([], key, merge);
else if (key.match(rules.named)) merge = self.build({}, key, merge);
}
data = $.extend(true, data, merge);
});
return data;
};
/*! 全局文件上传 */
$.fn.uploadFile = function (callable, initialize) {
return this.each(function (idx, elem) {
if (elem.dataset.inited) return false; else elem.dataset.inited = 'true';
elem.dataset.multiple = '|one|btn|'.indexOf(elem.dataset.file || 'one') > -1 ? '0' : '1';
$.module.use(['upload'], function (apply) {
apply(elem, callable) && setTimeout(function () {
typeof initialize === 'function' && initialize.call(elem, elem);
}, 100);
});
});
};
/*! 上传单个视频 */
$.fn.uploadOneVideo = function () {
return this.each(function () {
if (this.dataset.inited) return; else this.dataset.inited = 'true';
let $bt = $('<div class="uploadimage uploadvideo"><span><a data-file class="layui-icon layui-icon-upload-drag"></a><i class="layui-icon layui-icon-search"></i><i class="layui-icon layui-icon-close"></i></span><span data-file></span></div>');
let $in = $(this).on('change', function () {
if (this.value) $bt.css('backgroundImage', 'url("")').find('span[data-file]').html('<video width="100%" height="100%" autoplay loop muted><source src="' + encodeURI(this.value) + '" type="video/mp4"></video>');
}).after($bt).trigger('change');
$bt.on('click', 'i.layui-icon-search', function (event) {
event.stopPropagation(), $in.val() && $.form.iframe(encodeURI($in.val()), '视频预览');
}).on('click', 'i.layui-icon-close', function (event) {
event.stopPropagation(), $bt.attr('style', '').find('span[data-file]').html('') && $in.val('').trigger('change');
}).find('[data-file]').data('input', this).attr({
'data-path': $in.data('path') || '', 'data-size': $in.data('size') || 0, 'data-type': $in.data('type') || 'mp4',
});
});
};
/*! 上传单张图片 */
$.fn.uploadOneImage = function () {
return this.each(function () {
if (this.dataset.inited) return; else this.dataset.inited = 'true';
let previewUploadOnly = $(this).data('uploadDisplay') === 'preview';
let $bt = $('<div class="uploadimage"><span><a data-file class="layui-icon layui-icon-upload-drag"></a><i class="layui-icon layui-icon-search"></i><i class="layui-icon layui-icon-close"></i></span><span data-file="image"></span></div>');
let $in = $(this).on('change', function () {
if (this.value) $bt.css('backgroundImage', 'url(' + encodeURI(this.value) + ')');
}).after($bt).trigger('change');
if (previewUploadOnly) $bt.find('a[data-file]').remove();
$bt.on('click', 'i.layui-icon-search', function (event) {
event.stopPropagation(), $in.val() && $.previewImage(encodeURI($in.val()));
}).on('click', 'i.layui-icon-close', function (event) {
event.stopPropagation(), $bt.attr('style', '') && $in.val('').trigger('change');
}).find('[data-file]').data('input', this).attr({
'data-path': $in.data('path') || '', 'data-size': $in.data('size') || 0, 'data-type': $in.data('type') || 'gif,png,jpg,jpeg',
'data-max-width': $in.data('max-width') || 0, 'data-max-height': $in.data('max-height') || 0,
'data-cut-width': $in.data('cut-width') || 0, 'data-cut-height': $in.data('cut-height') || 0,
});
});
};
/*! 上传多张图片 */
$.fn.uploadMultipleImage = function () {
return this.each(function () {
if (this.dataset.inited) return; else this.dataset.inited = 'true';
let $bt = $('<div class="uploadimage"><span><a data-file="mul" class="layui-icon layui-icon-upload-drag"></a></span><span data-file="images"></span></div>');
let ims = this.value ? this.value.split('|') : [], $in = $(this).after($bt);
$bt.find('[data-file]').attr({
'data-path': $in.data('path') || '', 'data-size': $in.data('size') || 0, 'data-type': $in.data('type') || 'gif,png,jpg,jpeg',
'data-max-width': $in.data('max-width') || 0, 'data-max-height': $in.data('max-height') || 0,
'data-cut-width': $in.data('cut-width') || 0, 'data-cut-height': $in.data('cut-height') || 0,
}).on('push', function (evt, src) {
ims.push(src), $in.val(ims.join('|')).trigger('change'), showImageContainer([src]);
}) && (ims.length > 0 && showImageContainer(ims));
function showImageContainer(srcs) {
$(srcs).each(function (idx, src, $img) {
$img = $('<div class="uploadimage uploadimagemtl"><div><a class="layui-icon">&#xe603;</a><a class="layui-icon">&#x1006;</a><a class="layui-icon">&#xe602;</a></div></div>');
$img.attr('data-tips-image', encodeURI(src)).css('backgroundImage', 'url(' + encodeURI(src) + ')').on('click', 'a', function (event, index, prevs, $item) {
event.stopPropagation(), $item = $(this).parent().parent(), index = $(this).index();
if (index === 2 && $item.index() !== $bt.prevAll('div.uploadimage').length) $item.next().after($item);
else if (index === 0 && $item.index() > 1) $item.prev().before($item); else if (index === 1) $item.remove();
ims = [], $bt.prevAll('.uploadimage').map(function () {
ims.push($(this).attr('data-tips-image'));
});
ims.reverse(), $in.val(ims.join('|')).trigger('change');
}), $bt.before($img);
});
}
});
};
/*! 标签输入插件 */
$.fn.initTagInput = function () {
return this.each(function () {
let $this = $(this), tags = this.value ? this.value.split(',') : [];
let $text = $('<textarea class="layui-input layui-input-inline layui-tag-input"></textarea>');
let $tags = $('<div class="layui-tags"></div>').append($text);
$this.parent().append($tags) && $text.off('keydown blur') && (tags.length > 0 && showTags(tags));
$text.on('blur keydown', function (event, value) {
if (event.keyCode === 13 || event.type === 'blur') {
event.preventDefault(), (value = $text.val().replace(/^\s*|\s*$/g, ''));
if (tags.indexOf($(this).val()) > -1) return $.msg.notify('温馨提示', '该标签已经存在!', 3000, {type: 'error', width: 280});
else if (value.length > 0) tags.push(value), $this.val(tags.join(',')), showTags([value]), this.focus(), $text.val('');
}
});
function showTags(tagsArr) {
$(tagsArr).each(function (idx, text) {
$('<div class="layui-tag"></div>').data('value', text).on('click', 'i', function () {
tags.splice(tags.indexOf($(this).parent().data('value')), 1);
$this.val(tags.join(',')) && $(this).parent().remove();
}).insertBefore($text).html(text + '<i class="layui-icon">&#x1006;</i>');
});
}
});
};
/*! 文本框插入内容 */
$.fn.insertAtCursor = function (value) {
return this.each(function () {
this.focus();
if (document.selection) {
let selection = document.selection.createRange();
(selection.text = value), selection.select(), selection.unselect();
} else if (this.selectionStart || this.selectionStart === 0) {
let spos = this.selectionStart, apos = this.selectionEnd || spos;
this.value = this.value.substring(0, spos) + value + this.value.substring(apos);
this.selectionEnd = this.selectionStart = spos + value.length;
} else {
this.value += value;
}
this.focus();
});
};
/*! 组件 layui.table 封装 */
$.fn.layTable = function (params) {
return this.each(function () {
$.layTable.create(this, params);
});
};
$.layTable = new function () {
this.showImage = function (image, circle, size, title) {
if (typeof image !== 'string' || image.length < 5) {
return '<span class="color-desc">-</span>' + (title ? laytpl('<span class="ml5">{{d.title}}</span>').render({title: title}) : '');
}
return laytpl('<div class="headimg {{d.class}} headimg-{{d.size}}" data-tips-image data-tips-hover data-lazy-src="{{d.image}}" style="{{d.style}}"></div>').render({
size: size || 'ss', class: circle ? 'shadow-inset' : 'headimg-no', image: image, style: 'background-image:url(' + image + ');margin-right:0'
}) + (title ? laytpl('<span class="ml5">{{d.title}}</span>').render({title: title}) : '');
}, this.render = function (tabldId) {
return this.reload(tabldId, true);
}, this.reload = function (tabldId, force) {
return typeof tabldId === 'string' ? tabldId.split(',').map(function (tableid) {
$('#' + tableid).trigger(force ? 'render' : 'reload')
}) : $.form.reload();
}, this.create = function (table, params) {
// 动态初始化表格
table.id = table.id || 't' + Math.random().toString().replace('.', '');
let $table = $(table).attr('lay-filter', table.dataset.id = table.getAttribute('lay-filter') || table.id);
// 插件初始化参数
let option = params || {}, data = option.where || {}, sort = option.initSort || option.sort || {};
option.id = table.id, option.elem = table, option.url = params.url || table.dataset.url || location.href;
option.limit = params.limit || 20, option.loading = params.loading !== false, option.autoSort = params.autoSort === true;
option.page = params.page !== false ? (params.page || true) : false, option.cols = params.cols || [[]], option.success = params.done || '';
option.cellExpandedMode = option.cellExpandedMode || 'tips';
option.response = $.extend({statusCode: 200}, option.response || {});
// 默认动态设置页数, 动态设置最大高度
if (option.page === true) option.page = {curr: layui.sessionData('pages')[option.id] || 1};
if (option.width === 'full') option.width = $table.parent().width();
if (option.height === 'full') if ($table.parents('.iframe-pagination').length) {
$table.parents('.iframe-pagination').addClass('not-footer');
option.height = $(window).height() - $table.removeClass('layui-hide').offset().top - 20;
} else if ($table.parents('.laytable-pagination').length) {
option.height = $table.parents('.laytable-pagination').height() - $table.removeClass('layui-hide').position().top - 20;
} else {
option.height = $(window).height() - $table.removeClass('layui-hide').offset().top - 35;
}
// 初始化不显示头部
let cls = ['.layui-table-header', '.layui-table-fixed', '.layui-table-body', '.layui-table-page'];
option.css = (typeof option.height === 'number' ? '{height:' + option.height + 'px}' : '') + (option.css || '') + cls.concat(['']).join('{opacity:0}');
// 动态计算最大页数
option.done = function (res, curr, count) {
layui.sessionData('pages', {key: table.id, value: this.page.curr || 1});
typeof option.success === 'function' && option.success.call(this, res, curr, count);
$.form.reInit($table.next()).find('[data-open],[data-load][data-time!="false"],[data-action][data-time!="false"],[data-queue],[data-iframe]').not('[data-table-id]').attr('data-table-id', table.id);
(option.loading = this.loading = true) && $table.data('next', this).next().find(cls.join(',')).animate({opacity: 1});
setTimeout(() => layui.table.resize(table.id), 10);
}, option.parseData = function (res) {
if ($.auth.isAuthError(res) || !$.auth.isSuccess(res)) {
$.msg.auto(res);
return {code: 200, count: 0, data: []};
}
if (typeof params.filter === 'function') {
res.data = params.filter(res.data, res);
}
if (!this.page || !this.page.curr) return res;
let curp = this.page.curr, maxp = Math.ceil(res.count / (this.page.limit || option.limit));
if (curp > maxp && maxp > 1) $table.trigger('reload', {page: {curr: maxp}});
return res;
};
// 关联搜索表单
let sform, search = params.search || table.dataset.targetSearch || 'form[data-table-id="' + table.id + '"] [data-form-export]';
if (search) (sform = $body.find(search)).map(function () {
$(this).attr('data-table-id', table.id);
});
// 关联绑定选择项
let checked = params.checked || table.dataset.targetChecked;
if (checked) $body.find(checked).map(function () {
$(this).attr('data-table-id', table.id);
});
// 实例并绑定事件
$table.data('this', layui.table.render(bindData(option)));
$table.bind('reload render reloadData', function (evt, opts) {
if (option.page === false) (opts || {}).page = false;
data = $.extend({}, data, (opts || {}).where || {});
opts = bindData($.extend({}, opts || {}, {loading: true}));
table.id.split(',').map(function (tableid) {
if (evt.type.indexOf('reload') > -1) {
layui.table.reloadData(tableid, opts);
} else {
layui.table.render(tableid, opts);
}
})
}).bind('row sort tool edit radio toolbar checkbox rowDouble', function (evt, call) {
table.id.split(',').map(function (tableid) {
layui.table.on(evt.type + '(' + tableid + ')', call)
})
}).bind('setFullHeight', function () {
$table.trigger('render', {height: $(window).height() - $table.next().offset().top - 35})
}).trigger('sort', function (rets) {
(sort = rets), $table.trigger('reload')
}).trigger('rowDouble', function (event) {
$(event.tr[0]).find('[data-event-dbclick]').map(function () {
$(this).trigger(this.dataset.eventDbclick || 'click', event);
});
});
return $table;
// 生成初始化参数
function bindData(options) {
data['output'] = 'layui.table';
if (sort.field && sort.type) {
data['_order_'] = sort.type, data['_field_'] = sort.field;
options.initSort = {type: sort.type.split(',')[0].split(' ')[0], field: sort.field.split(',')[0].split(' ')[0]};
if (sform) $(sform).find('[data-form-export]').attr({'data-sort-type': sort.type, 'data-sort-field': sort.field});
}
if (options.page === false) options.limit = '';
return (options['where'] = data), options;
}
};
};
/*!格式化文件大小 */
$.formatFileSize = function (size, fixed, units) {
let unit;
units = units || ['B', 'K', 'M', 'G', 'TB'];
while ((unit = units.shift()) && size > 1024) size = size / 1024;
return (unit === 'B' ? size : size.toFixed(fixed === undefined ? 2 : fixed)) + unit;
}
/*! 弹出图片层 */
$.previewImage = function (src, area) {
let img = new Image(), defer = $.Deferred(), loaded = $.msg.loading();
img.style.background = '#FFF', img.referrerPolicy = 'no-referrer';
img.style.height = 'auto', img.style.width = area || '100%', img.style.display = 'none';
return document.body.appendChild(img), img.onerror = function () {
$.msg.close(loaded) && defer.reject();
}, img.src = src, img.onload = function () {
layer.open({
type: 1, title: false, shadeClose: true, content: $(img), success: function ($elem, idx) {
$.msg.close(loaded) && defer.notify($elem, idx);
}, area: area || '480px', skin: 'layui-layer-nobg', closeBtn: 1, end: function () {
document.body.removeChild(img) && defer.resolve()
}
});
}, defer.promise();
};
/*! 以手机模式显示内容 */
$.previewPhonePage = function (href, title) {
let template = '<div class="mobile-preview"><div class="mobile-header">{{d.title}}</div><div class="mobile-body"><iframe src="{{d.url}}"></iframe></div></div>';
layer.style(layer.open({type: true, resize: false, scrollbar: false, area: ['320px', '600px'], title: false, closeBtn: true, shadeClose: false, skin: 'layui-layer-nobg', content: laytpl(template).render({title: title || '公众号', url: href})}), {boxShadow: 'none'});
};
/*! 显示任务进度 */
$.loadQueue = function (code, doScript, element) {
$.module.use(['queue'], function (Queue) {
return new Queue(code, doScript, element);
});
};
/*! 注册JqFn函数 */
$.fn.vali = function (done, init) {
return this.each(function () {
$.vali(this, done, init);
});
};
/*! 创建表单验证 */
$.vali = function (form, done, init) {
$.module.use(['validate'], function (Validate) {
/** @type {import("./plugs/system/validate")|Validate}*/
let vali = $(form).data('validate') || new Validate(form);
typeof init === 'function' && init.call(vali, $(form).formToJson(), vali);
typeof done === 'function' && vali.addDoneEvent(done);
});
};
/*! 自动监听表单 */
$.vali.listen = function ($dom) {
let $els = $($dom || $body).find('form[data-auto]');
$dom && $($dom).filter('form[data-auto]') && $els.add($dom);
return $els.map(function (idx, form) {
$(this).vali(function (data) {
let type = form.getAttribute('method') || 'POST', href = form.getAttribute('action') || location.href;
let dset = form.dataset, tips = dset.tips || undefined, time = dset.time || undefined, taid = dset.tableId || false;
let call = window[dset.callable || '_default_callable'] || (taid ? function (ret) {
if (typeof ret === 'object' && $.auth.isSuccess(ret) && $('#' + taid).length > 0) {
return $.msg.success(ret.info, 3, function () {
$.msg.closeLastModal();
(typeof ret.data === 'string' && ret.data) ? $.form.goto(ret.data) : $.layTable.reload(taid);
}) && false;
}
} : undefined);
$.base.onConfirm(dset.confirm, function () {
$.form.load(href, data, type, call, true, tips, time);
});
});
});
};
/*! 注册 data-search 表单搜索行为 */
$.base.onEvent('submit', 'form.form-search', function () {
if (this.dataset.tableId) {
let data = $(this).formToJson();
return this.dataset.tableId.split(',').map(function (tableid) {
$('table#' + tableid).trigger('reload', {page: {curr: 1}, where: data});
});
}
let url = $(this).attr('action').replace(/&?page=\d+/g, '');
if ((this.method || 'get').toLowerCase() === 'get') {
let split = url.indexOf('?') > -1 ? '&' : '?', stype = location.href.indexOf('spm=') > -1 ? '#' : '';
$.form.goto(stype + $.menu.parseUri(url + split + $(this).serialize().replace(/\+/g, ' ')));
} else {
$.form.load(url, this, 'post');
}
});
/*! 注册 data-file 事件行为 */
$.base.onEvent('mousedown', '.input-right-icon', function (event) {
event.preventDefault();
event.stopPropagation();
});
$.base.onEvent('touchstart', '.input-right-icon', function (event) {
event.preventDefault();
event.stopPropagation();
});
$.base.onEvent('click', '[data-file]', function () {
this.id = this.dataset.id = this.id || (function (date) {
return (date + Math.random()).replace('0.', '');
})(layui.util.toDateString(Date.now(), 'yyyyMMddHHmmss-'));
/*! 查找表单元素, 如果没有找到将不会自动写值 */
if (!(this.$elem = $(this)).data('input') && this.$elem.data('field')) {
let $input = $('input[name="' + this.$elem.data('field') + '"]:not([type=file])');
this.$elem.data('input', $input.length > 0 ? $input.get(0) : null);
}
// 单图或多图选择器 ( image|images )
if (typeof this.dataset.file === 'string' && /^images?$/.test(this.dataset.file)) {
return $.form.modal(storageApiRoot + '/upload/image', this.dataset, '图片选择器')
}
// 其他文件上传处理
this.dataset.inited || $(this).uploadFile(undefined, function () {
$(this).trigger('upload.start');
});
});
/*! 注册 data-load 事件行为 */
$.base.onEvent('click', '[data-load]', function () {
$.base.applyRuleValue(this, {}, function (data, elem, dset) {
$.form.load(dset.load, data, 'get', $.base.onConfirm.getLoadCallable(dset.tableId), true, dset.tips, dset.time);
});
});
/*! 注册 data-reload 事件行为 */
$.base.onEvent('click', '[data-reload]', function () {
$.layTable.reload(this.dataset.tableId || true);
});
/*! 注册 data-dbclick 事件行为 */
$.base.onEvent('dblclick', '[data-dbclick]', function () {
$(this).find(this.dataset.dbclick || '[data-dbclick]').trigger('click');
});
/*! 注册 data-check 事件行为 */
$.base.onEvent('click', '[data-check-target]', function () {
let target = this;
$(this.dataset.checkTarget).map(function () {
(this.checked = !!target.checked), $(this).trigger('change');
});
});
/*! 表单元素失去焦点时数字 */
$.base.onEvent('blur', '[data-blur-number]', function () {
let set = this.dataset, value = parseFloat(this.value) || 0;
let min = $.isNumeric(set.valueMin) ? set.valueMin : this.min;
let max = $.isNumeric(set.valueMax) ? set.valueMax : this.max;
if ($.isNumeric(min) && value < min) value = parseFloat(min) || 0;
if ($.isNumeric(max) && value > max) value = parseFloat(max) || 0;
this.value = value.toFixed(parseInt(set.blurNumber) || 0);
});
/*! 表单元素失焦时提交 */
$.base.onEvent('blur', '[data-action-blur],[data-blur-action]', function () {
let that = $(this), dset = this.dataset, data = {};
let attrs = (dset.value || '').replace('{value}', that.val()).split(';');
for (let i in attrs) data[attrs[i].split('#')[0]] = attrs[i].split('#')[1];
$.base.onConfirm(dset.confirm, function () {
$.form.load(dset.actionBlur || dset.blurAction, data, dset.method || 'post', function (ret) {
return that.css('border', $.auth.isSuccess(ret) ? '1px solid #e6e6e6' : '1px solid red') && false;
}, dset.loading !== 'false', dset.loading, dset.time);
});
});
/*! 注册 data-href 事件行为 */
$.base.onEvent('click', '[data-href]', function () {
if (this.dataset.href && this.dataset.href.indexOf('#') !== 0) {
$.form.goto(this.dataset.href);
}
});
/*! 注册 data-open 事件行为 */
$.base.onEvent('click', '[data-open]', function () {
// 仅记录当前表格分页
let page = 0, tbid = this.dataset.tableId || null;
if (tbid) page = layui.sessionData('pages')[tbid] || 0;
layui.sessionData('pages', null);
if (page > 0) layui.sessionData('pages', {key: tbid, value: page})
// 根据链接类型跳转页面
if (this.dataset.open.match(/^https?:/)) {
$.form.goto(this.dataset.open);
} else {
$.form.href(this.dataset.open, this);
}
});
/*! 注册 data-action 事件行为 */
$.base.onEvent('click', '[data-action]', function () {
$.base.applyRuleValue(this, {}, function (data, elem, dset) {
let load = dset.loading !== 'false', tips = typeof load === 'string' ? load : undefined;
$.form.load(dset.action, data, dset.method || 'post', $.base.onConfirm.getLoadCallable(dset.tableId), load, tips, dset.time)
});
});
/*! 注册 data-modal 事件行为 */
$.base.onEvent('click', '[data-modal]', function () {
$.base.applyRuleValue(this, {open_type: 'modal'}, function (data, elem, dset) {
let defer = $.form.modal(dset.modal, data, dset.title || this.innerText || '编辑', undefined, undefined, undefined, dset.area || dset.width || '800px', dset.offset || 'auto', dset.full !== undefined, dset.maxmin || false);
defer.progress((type) => type === 'modal.close' && dset.closeRefresh && $.layTable.reload(dset.closeRefresh));
});
});
/*! 注册 data-iframe 事件行为 */
$.base.onEvent('click', '[data-iframe]', function () {
$.base.applyRuleValue(this, {open_type: 'iframe'}, function (data, elem, dset) {
let name = dset.title || this.innerText || 'IFRAME 窗口';
let area = dset.area || [dset.width || '800px', dset.height || '580px'];
let frame = dset.iframe + (dset.iframe.indexOf('?') > -1 ? '&' : '?') + $.param(data);
$(this).attr('data-index', $.form.iframe(frame + '&' + $.param(data), name, area, dset.offset || 'auto', function () {
typeof dset.refresh !== 'undefined' && $.layTable.reload(dset.tableId || true);
}, undefined, dset.full !== undefined, dset.maxmin || false));
})
});
/*! 注册 data-video-player 事件行为 */
$.base.onEvent('click', '[data-video-player]', function () {
let idx = $.msg.loading(), url = this.dataset.videoPlayer, name = this.dataset.title || '媒体播放器', payer;
$.module.use(['artplayer'], () => layer.open({
title: name, type: 1, fixed: true, maxmin: false,
content: '<div class="data-play-video" style="width:800px;height:450px"></div>',
end: () => payer.destroy(), success: $ele => payer = new Artplayer({
url: url, container: $ele.selector + ' .data-play-video', controls: [
{html: '全屏播放', position: 'right', click: () => payer.fullscreen = !payer.fullscreen},
]
}, art => art.play(), $.msg.close(idx))
}));
});
/*! 注册 data-icon 事件行为 */
$.base.onEvent('click', '[data-icon]', function () {
let location = tapiRoot + '/plugs/icon', field = this.dataset.icon || this.dataset.field || 'icon';
$.form.iframe(location + (location.indexOf('?') > -1 ? '&' : '?') + 'field=' + field, '图标选择', ['900px', '700px']);
});
/*! 注册 data-copy 事件行为 */
$.base.onEvent('click', '[data-copy]', function () {
layui.lay.clipboard.writeText({
text: this.dataset.copy || this.innerText,
done: () => $.msg.tips('已复制到剪贴板!'),
error: () => $.msg.tips('请使用鼠标复制!')
})
});
/*! 异步任务状态监听与展示 */
$.base.onEvent('click', '[data-queue]', function () {
$.base.applyRuleValue(this, {}, function (data, elem, dset) {
$.form.load(dset.queue, data, 'post', function (ret) {
if (typeof ret.data === 'string' && ret.data.indexOf('Q') === 0) {
return $.loadQueue(ret.data, true, elem), false;
}
});
});
});
/*! 注册 data-tips-text 事件行为 */
$.base.onEvent('mouseenter', '[data-tips-text]', function () {
let opts = {tips: [$(this).attr('data-tips-type') || 3, '#78BA32'], time: 0};
let layidx = layer.tips($(this).attr('data-tips-text') || this.innerText, this, opts);
$(this).off('mouseleave').on('mouseleave', function () {
setTimeout("layer.close('" + layidx + "')", 100);
});
});
/*! 注册 data-tips-hover 事件行为 */
$.base.onEvent('mouseenter', '[data-tips-image][data-tips-hover],[data-tips-image-hover]', function () {
let $el = $(this), img = new Image();
if ((img.src = this.dataset.tipsImage || this.dataset.lazySrc || this.src || this.value)) {
img.layopt = {anim: 5, tips: 3, time: 0, skin: 'layui-layer-image', isOutAnim: false, scrollbar: false};
img.referrerPolicy = 'no-referrer', img.style.maxWidth = '260px', img.style.maxHeight = '260px';
$el.data('layidx', layer.tips(img.outerHTML, this, img.layopt)).off('mouseleave').on('mouseleave', function () {
$el.off('mousemove') && layer.close($el.data('layidx'));
});
let $lay = $('#layui-layer' + $el.data('layidx'));
$el.on('mousemove', e => $lay.css({top: e.pageY + 20, left: e.pageX - 20}));
}
});
/*! 注册 data-tips-image 事件行为 */
$.base.onEvent('click', '[data-tips-image]', function (event) {
(event.items = [], event.$imgs = $(this).parent().find('[data-tips-image]')).map(function () {
(src => src && event.items.push({src: src}))(this.dataset.tipsImage || this.dataset.lazySrc || this.src || this.value);
}) && event.items.length > 0 && layer.photos({
anim: 5, closeBtn: 1, photos: {start: event.$imgs.index(this), data: event.items}, tab: function (pic, $ele) {
$ele.find('img').attr('referrerpolicy', 'no-referrer');
$ele.find('.layui-layer-close').css({top: '20px', right: '20px', position: 'fixed'});
}
});
});
/*! 注册 data-phone-view 事件行为 */
$.base.onEvent('click', '[data-phone-view]', function () {
$.previewPhonePage(this.dataset.phoneView || this.href);
});
/*! 注册 data-target-submit 事件行为 */
$.base.onEvent('click', '[data-target-submit]', function () {
$(this.dataset.targetSubmit || 'form:last').submit();
});
/*! 表单编辑返回操作 */
$.base.onEvent('click', '[data-target-backup],[data-history-back]', function () {
$.base.onConfirm(this.dataset.historyBack || this.dataset.targetBackup || '确定要返回上个页面吗?', function () {
history.back();
});
});
/*! 图片加载异常处理 */
document.addEventListener('error', function (event) {
if (event.target.nodeName !== 'IMG') return;
event.target.src = baseRoot + 'theme/img/404_icon.png';
}, true);
/*! 初始化系统菜单及表单验证 */
$.menu.listen() && $.form.reInit($body);
});