// +---------------------------------------------------------------------- // | 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 = $('
'); let $in = $(this).on('change', function () { if (this.value) $bt.css('backgroundImage', 'url("")').find('span[data-file]').html(''); }).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 = $(''); 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 = $(''); 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 = $(''); $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 = $(''); let $tags = $('').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) { $('').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 + 'ဆ'); }); } }); }; /*! 文本框插入内容 */ $.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 '-' + (title ? laytpl('{{d.title}}').render({title: title}) : ''); } return laytpl('').render({ size: size || 'ss', class: circle ? 'shadow-inset' : 'headimg-no', image: image, style: 'background-image:url(' + image + ');margin-right:0' }) + (title ? laytpl('{{d.title}}').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 = '