admin.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. // +----------------------------------------------------------------------
  2. // | ThinkAdmin
  3. // +----------------------------------------------------------------------
  4. // | 版权所有 2014~2019 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
  5. // +----------------------------------------------------------------------
  6. // | 官方网站: http://demo.thinkadmin.top
  7. // +----------------------------------------------------------------------
  8. // | 开源协议 ( https://mit-license.org )
  9. // +----------------------------------------------------------------------
  10. // | gitee 代码仓库:https://gitee.com/zoujingli/ThinkAdmin
  11. // | github 代码仓库:https://github.com/zoujingli/ThinkAdmin
  12. // +----------------------------------------------------------------------
  13. // 浏览器兼容提示
  14. (function (w) {
  15. if (!("WebSocket" in w && 2 === w.WebSocket.CLOSING)) {
  16. document.body.innerHTML = '<div class="version-debug">您使用的浏览器已经<strong>过时</strong>,建议使用最新版本的谷歌浏览器。<a target="_blank" href="https://pc.qq.com/detail/1/detail_2661.html" class="layui-btn layui-btn-primary">立即下载</a></div>';
  17. }
  18. }(window));
  19. // Layui & jQuery
  20. if (typeof jQuery === 'undefined') window.$ = window.jQuery = layui.$;
  21. window.form = layui.form, window.layer = layui.layer, window.laydate = layui.laydate;
  22. // 当前资源URL目录
  23. window.baseRoot = (function (src) {
  24. src = document.scripts[document.scripts.length - 1].src;
  25. return src.substring(0, src.lastIndexOf("/") + 1);
  26. })();
  27. // require 配置参数
  28. require.config({
  29. waitSeconds: 60,
  30. baseUrl: baseRoot,
  31. map: {'*': {css: baseRoot + 'plugs/require/css.js'}},
  32. paths: {
  33. 'md5': ['plugs/jquery/md5.min'],
  34. 'spop': ['plugs/spop/spop.min'],
  35. 'json': ['plugs/jquery/json.min'],
  36. 'upload': ['plugs/plupload/build'],
  37. 'base64': ['plugs/jquery/base64.min'],
  38. 'echarts': ['plugs/echarts/echarts.min'],
  39. 'angular': ['plugs/angular/angular.min'],
  40. 'ckeditor': ['plugs/ckeditor/ckeditor'],
  41. 'plupload': ['plugs/plupload/plupload.full.min'],
  42. 'websocket': ['plugs/socket/websocket'],
  43. 'pcasunzips': ['plugs/jquery/pcasunzips'],
  44. 'jquery.ztree': ['plugs/ztree/ztree.all.min'],
  45. 'jquery.masonry': ['plugs/jquery/masonry.min'],
  46. 'jquery.autocompleter': ['plugs/jquery/autocompleter.min'],
  47. },
  48. shim: {
  49. 'spop': {deps: ['css!' + baseRoot + 'plugs/spop/spop.min.css']},
  50. 'websocket': {deps: [baseRoot + 'plugs/socket/swfobject.min.js']},
  51. 'jquery.ztree': {deps: ['jquery', 'css!' + baseRoot + 'plugs/ztree/zTreeStyle/zTreeStyle.css']},
  52. 'jquery.autocompleter': {deps: ['jquery', 'css!' + baseRoot + 'plugs/jquery/autocompleter.css']},
  53. }
  54. });
  55. // 注册jquery到require模块
  56. define('jquery', [], function () {
  57. return layui.$;
  58. });
  59. $(function () {
  60. window.$body = $('body');
  61. /*! 消息组件实例 */
  62. $.msg = new function (that) {
  63. that = this;
  64. this.indexs = [];
  65. this.shade = [0.02, '#000'];
  66. // 关闭消息框
  67. this.close = function (index) {
  68. return layer.close(index);
  69. };
  70. // 弹出警告框
  71. this.alert = function (msg, callback) {
  72. var index = layer.alert(msg, {end: callback, scrollbar: false});
  73. return this.indexs.push(index), index;
  74. };
  75. // 确认对话框
  76. this.confirm = function (msg, ok, no) {
  77. var index = layer.confirm(msg, {title: '操作确认', btn: ['确认', '取消']}, function () {
  78. typeof ok === 'function' && ok.call(this, index);
  79. }, function () {
  80. typeof no === 'function' && no.call(this, index);
  81. that.close(index);
  82. });
  83. return index;
  84. };
  85. // 显示成功类型的消息
  86. this.success = function (msg, time, callback) {
  87. var index = layer.msg(msg, {icon: 1, shade: this.shade, scrollbar: false, end: callback, time: (time || 2) * 1000, shadeClose: true});
  88. return this.indexs.push(index), index;
  89. };
  90. // 显示失败类型的消息
  91. this.error = function (msg, time, callback) {
  92. var index = layer.msg(msg, {icon: 2, shade: this.shade, scrollbar: false, time: (time || 3) * 1000, end: callback, shadeClose: true});
  93. return this.indexs.push(index), index;
  94. };
  95. // 状态消息提示
  96. this.tips = function (msg, time, callback) {
  97. var index = layer.msg(msg, {time: (time || 3) * 1000, shade: this.shade, end: callback, shadeClose: true});
  98. return this.indexs.push(index), index;
  99. };
  100. // 显示正在加载中的提示
  101. this.loading = function (msg, callback) {
  102. var index = msg ? layer.msg(msg, {icon: 16, scrollbar: false, shade: this.shade, time: 0, end: callback}) : layer.load(2, {time: 0, scrollbar: false, shade: this.shade, end: callback});
  103. return this.indexs.push(index), index;
  104. };
  105. // 自动处理显示Think返回的Json数据
  106. this.auto = function (ret, time) {
  107. var url = ret.url || (typeof ret.data === 'string' ? ret.data : '');
  108. var msg = ret.msg || (typeof ret.info === 'string' ? ret.info : '');
  109. if (parseInt(ret.code) === 1 && time === 'false') {
  110. return url ? (window.location.href = url) : $.form.reload();
  111. }
  112. return (parseInt(ret.code) === 1) ? this.success(msg, time, function () {
  113. url ? (window.location.href = url) : $.form.reload();
  114. for (var i in that.indexs) layer.close(that.indexs[i]);
  115. that.indexs = [];
  116. }) : this.error(msg, 3, function () {
  117. url ? window.location.href = url : '';
  118. });
  119. };
  120. };
  121. /*! 表单自动化组件 */
  122. $.form = new function (that) {
  123. that = this;
  124. // 内容区选择器
  125. this.selecter = '.layui-layout-admin>.layui-body';
  126. // 刷新当前页面
  127. this.reload = function () {
  128. window.onhashchange.call(this);
  129. };
  130. // 内容区域动态加载后初始化
  131. this.reInit = function ($dom) {
  132. $.vali.listen(this);
  133. $dom = $dom || $(this.selecter);
  134. $dom.find('[required]').map(function ($parent) {
  135. if (($parent = $(this).parent()) && $parent.is('label')) {
  136. $parent.addClass('label-required-prev');
  137. } else {
  138. $parent.prevAll('label').addClass('label-required-next');
  139. }
  140. });
  141. $dom.find('input[data-date-range]').map(function () {
  142. this.setAttribute('autocomplete', 'off');
  143. laydate.render({
  144. type: this.getAttribute('data-date-range') || 'date',
  145. range: true, elem: this, done: function (value) {
  146. $(this.elem).val(value).trigger('change');
  147. }
  148. });
  149. });
  150. $dom.find('[data-file]:not([data-inited])').map(function (index, elem, $this, field) {
  151. $this = $(elem), field = $this.attr('data-field') || 'file';
  152. if (!$this.data('input')) $this.data('input', $('[name="' + field + '"]').get(0));
  153. $this.uploadFile(function (url) {
  154. $($this.data('input')).val(url).trigger('change');
  155. });
  156. });
  157. };
  158. // 在内容区显示视图
  159. this.show = function (html) {
  160. $(this.selecter).html(html);
  161. this.reInit($(this.selecter));
  162. setTimeout(function () {
  163. that.reInit($(that.selecter));
  164. }, 500);
  165. };
  166. // 以HASH打开新网页
  167. this.href = function (url, obj) {
  168. if (url !== '#') {
  169. window.location.href = '#' + $.menu.parseUri(url, obj);
  170. } else if (obj && obj.getAttribute('data-menu-node')) {
  171. $('[data-menu-node^="' + obj.getAttribute('data-menu-node') + '-"][data-open!="#"]:first').trigger('click');
  172. }
  173. };
  174. // 异步加载的数据
  175. this.load = function (url, data, method, callback, loading, tips, time, headers) {
  176. var index = loading !== false ? $.msg.loading(tips) : 0;
  177. if (typeof data === 'object' && typeof data['_csrf_'] === 'string') {
  178. headers = headers || {};
  179. headers['User-Token-Csrf'] = data['_csrf_'];
  180. delete data['_csrf_'];
  181. }
  182. $.ajax({
  183. data: data || {}, type: method || 'GET', url: $.menu.parseUri(url), beforeSend: function (xhr) {
  184. if (typeof Pace === 'object') Pace.restart();
  185. if (typeof headers === 'object') for (var i in headers) xhr.setRequestHeader(i, headers[i]);
  186. }, error: function (XMLHttpRequest) {
  187. if (parseInt(XMLHttpRequest.status) === 200) this.success(XMLHttpRequest.responseText);
  188. else $.msg.tips('E' + XMLHttpRequest.status + ' - 服务器繁忙,请稍候再试!');
  189. }, success: function (ret) {
  190. if (typeof callback === 'function' && callback.call(that, ret) === false) return false;
  191. return typeof ret === 'object' ? $.msg.auto(ret, time || ret.wait || undefined) : that.show(ret);
  192. }, complete: function () {
  193. $.msg.close(index);
  194. }
  195. });
  196. };
  197. // 加载HTML到目标位置
  198. this.open = function (url, data, callback, loading, tips) {
  199. this.load(url, data, 'get', function (ret) {
  200. return (typeof ret === 'object' ? $.msg.auto(ret) : that.show(ret)), false;
  201. }, loading, tips);
  202. };
  203. // 打开一个iframe窗口
  204. this.iframe = function (url, title, area) {
  205. return layer.open({title: title || '窗口', type: 2, area: area || ['800px', '580px'], fix: true, maxmin: false, content: url});
  206. };
  207. // 加载HTML到弹出层
  208. this.modal = function (url, data, title, callback, loading, tips) {
  209. this.load(url, data, 'GET', function (res, index) {
  210. if (typeof (res) === 'object') return $.msg.auto(res), false;
  211. index = layer.open({
  212. type: 1, btn: false, area: "800px", content: res, title: title || '', success: function (dom, index) {
  213. $(dom).find('[data-close]').off('click').on('click', function () {
  214. if ($(this).attr('data-confirm')) return $.msg.confirm($(this).attr('data-confirm'), function (_index) {
  215. layer.close(_index), layer.close(index);
  216. }), false;
  217. layer.close(index);
  218. });
  219. $.form.reInit($(dom));
  220. }
  221. });
  222. $.msg.indexs.push(index);
  223. return (typeof callback === 'function') && callback.call(that);
  224. }, loading, tips);
  225. };
  226. };
  227. /*! 后台菜单辅助插件 */
  228. $.menu = new function (that) {
  229. that = this;
  230. // 计算URL地址中有效的URI
  231. this.getUri = function (uri) {
  232. uri = uri || window.location.href;
  233. uri = (uri.indexOf(window.location.host) > -1 ? uri.split(window.location.host)[1] : uri);
  234. return (uri.indexOf('#') > -1 ? uri.split('#')[1] : uri).split('?')[0];
  235. };
  236. // 通过URI查询最有可能的菜单NODE
  237. this.queryNode = function (url, node) {
  238. node = node || location.href.replace(/.*spm=([\d\-m]+).*/ig, '$1');
  239. if (!/^m-/.test(node)) {
  240. var $menu = $('[data-menu-node][data-open*="' + url.replace(/\.html$/ig, '') + '"]');
  241. return $menu.size() ? $menu.get(0).getAttribute('data-menu-node') : '';
  242. }
  243. return node;
  244. };
  245. // URL转URI
  246. this.parseUri = function (uri, obj) {
  247. var params = {};
  248. if (uri.indexOf('?') > -1) {
  249. var attrs = uri.split('?')[1].split('&');
  250. for (var i in attrs) if (attrs[i].indexOf('=') > -1) {
  251. var tmp = attrs[i].split('=').slice();
  252. if (typeof tmp[0] === 'string' && tmp[0].length > 0) {
  253. params[tmp[0]] = decodeURIComponent(tmp[1].replace(/%2B/ig, '%20'));
  254. }
  255. }
  256. }
  257. uri = this.getUri(uri);
  258. if (typeof params.spm !== 'string') {
  259. params.spm = obj && obj.getAttribute('data-menu-node') || this.queryNode(uri);
  260. }
  261. if (typeof params.spm !== 'string' || params.spm.length < 1) delete params.spm;
  262. var query = '?' + $.param(params);
  263. return uri + (query === '?' ? '' : query);
  264. };
  265. // 后台菜单动作初始化
  266. this.listen = function () {
  267. // 菜单模式切换
  268. (function ($menu, miniClass) {
  269. // Mini 菜单模式切换及显示
  270. if (layui.data('admin-menu-type')['type-min']) $menu.addClass(miniClass);
  271. $body.on('click', '[data-target-menu-type]', function () {
  272. $menu.toggleClass(miniClass);
  273. layui.data('admin-menu-type', {key: 'type-min', value: $menu.hasClass(miniClass)});
  274. }).on('resize', function () {
  275. if ($body.width() > 1000) {
  276. layui.data('admin-menu-type')['type-min'] ? $menu.addClass(miniClass) : $menu.removeClass(miniClass);
  277. } else {
  278. $menu.addClass(miniClass);
  279. }
  280. }).trigger('resize');
  281. // Mini 菜单模式时TIPS文字显示
  282. $('[data-target-tips]').mouseenter(function () {
  283. if ($menu.hasClass(miniClass)) {
  284. $(this).attr('index', layer.tips($(this).attr('data-target-tips') || '', this));
  285. }
  286. }).mouseleave(function () {
  287. layer.close($(this).attr('index'));
  288. });
  289. })($('.layui-layout-admin'), 'layui-layout-left-mini');
  290. // 左则二级菜单展示
  291. $('[data-submenu-layout]>a').on('click', function () {
  292. that.syncOpenStatus(1);
  293. });
  294. // 同步二级菜单展示状态
  295. this.syncOpenStatus = function (mode) {
  296. $('[data-submenu-layout]').map(function (node) {
  297. node = $(this).attr('data-submenu-layout');
  298. if (mode === 1) layui.data('menu', {key: node, value: $(this).hasClass('layui-nav-itemed') ? 2 : 1});
  299. else if ((layui.data('menu')[node] || 2) === 2) $(this).addClass('layui-nav-itemed');
  300. });
  301. };
  302. window.onhashchange = function (hash, node) {
  303. hash = window.location.hash || '';
  304. if (hash.length < 1) return $('[data-menu-node][data-open!="#"]:first').trigger('click');
  305. $.form.load(hash), that.syncOpenStatus(2);
  306. // 菜单选择切换
  307. node = that.queryNode(that.getUri());
  308. if (/^m-/.test(node)) {
  309. var $all = $('a[data-menu-node]').parent(), tmp = node.split('-'), tmpNode = tmp.shift();
  310. while (tmp.length > 0) {
  311. tmpNode = tmpNode + '-' + tmp.shift();
  312. $all = $all.not($('a[data-menu-node="' + tmpNode + '"]').parent().addClass('layui-this'));
  313. }
  314. $all.removeClass('layui-this');
  315. // 菜单模式切换
  316. if (node.split('-').length > 2) {
  317. var _tmp = node.split('-'), _node = _tmp.shift() + '-' + _tmp.shift();
  318. $('[data-menu-layout]').not($('[data-menu-layout="' + _node + '"]').removeClass('layui-hide')).addClass('layui-hide');
  319. $('[data-menu-node="' + node + '"]').parent().parent().parent().addClass('layui-nav-itemed');
  320. $('.layui-layout-admin').removeClass('layui-layout-left-hide');
  321. } else $('.layui-layout-admin').addClass('layui-layout-left-hide');
  322. that.syncOpenStatus(1);
  323. }
  324. };
  325. // URI初始化动作
  326. window.onhashchange.call(this);
  327. };
  328. };
  329. /*! 注册对象到Jq */
  330. $.vali = function (form, callback, options) {
  331. return (new function (that) {
  332. that = this;
  333. // 表单元素
  334. this.tags = 'input,textarea,select';
  335. // 检测元素事件
  336. this.checkEvent = {change: true, blur: true, keyup: false};
  337. // 去除字符串两头的空格
  338. this.trim = function (str) {
  339. return str.replace(/(^\s*)|(\s*$)/g, '');
  340. };
  341. // 标签元素是否可见
  342. this.isVisible = function (ele) {
  343. return $(ele).is(':visible');
  344. };
  345. // 检测属性是否有定义
  346. this.hasProp = function (ele, prop) {
  347. if (typeof prop !== "string") return false;
  348. var attrProp = ele.getAttribute(prop);
  349. return (typeof attrProp !== 'undefined' && attrProp !== null && attrProp !== false);
  350. };
  351. // 判断表单元素是否为空
  352. this.isEmpty = function (ele, value) {
  353. var trim = this.trim(ele.value);
  354. value = value || ele.getAttribute('placeholder');
  355. return (trim === "" || trim === value);
  356. };
  357. // 正则验证表单元素
  358. this.isRegex = function (ele, regex, params) {
  359. var input = $(ele).val(), real = this.trim(input);
  360. regex = regex || ele.getAttribute('pattern');
  361. if (real === "" || !regex) return true;
  362. return new RegExp(regex, params || 'i').test(real);
  363. };
  364. // 检侧所的表单元素
  365. this.checkAllInput = function () {
  366. var isPass = true;
  367. $(form).find(this.tags).each(function () {
  368. if (that.checkInput(this) === false) return $(this).focus(), isPass = false;
  369. });
  370. return isPass;
  371. };
  372. // 检测表单单元
  373. this.checkInput = function (input) {
  374. var tag = input.tagName.toLowerCase(), need = this.hasProp(input, "required");
  375. var type = (input.getAttribute("type") || '').replace(/\W+/, "").toLowerCase();
  376. if (this.hasProp(input, 'data-auto-none')) return true;
  377. var ingoreTags = ['select'], ingoreType = ['radio', 'checkbox', 'submit', 'reset', 'image', 'file', 'hidden'];
  378. for (var i in ingoreTags) if (tag === ingoreTags[i]) return true;
  379. for (var i in ingoreType) if (type === ingoreType[i]) return true;
  380. if (need && this.isEmpty(input)) return this.remind(input);
  381. return this.isRegex(input) ? (this.hideError(input), true) : this.remind(input);
  382. };
  383. // 验证标志
  384. this.remind = function (input) {
  385. if (!this.isVisible(input)) return true;
  386. this.showError(input, input.getAttribute('title') || input.getAttribute('placeholder') || '输入错误');
  387. return false;
  388. };
  389. // 错误消息显示
  390. this.showError = function (ele, content) {
  391. $(ele).addClass('validate-error'), this.insertError(ele);
  392. $($(ele).data('input-info')).addClass('layui-anim layui-anim-fadein').css({width: 'auto'}).html(content);
  393. };
  394. // 错误消息消除
  395. this.hideError = function (ele) {
  396. $(ele).removeClass('validate-error'), this.insertError(ele);
  397. $($(ele).data('input-info')).removeClass('layui-anim-fadein').css({width: '30px'}).html('');
  398. };
  399. // 错误消息标签插入
  400. this.insertError = function (ele) {
  401. var $html = $('<span style="padding-right:12px;color:#a94442;position:absolute;right:0;font-size:12px;z-index:2;display:block;width:34px;text-align:center;pointer-events:none"></span>');
  402. $html.css({top: $(ele).position().top + 'px', paddingBottom: $(ele).css('paddingBottom'), lineHeight: $(ele).css('height')});
  403. $(ele).data('input-info') || $(ele).data('input-info', $html.insertAfter(ele));
  404. };
  405. // 表单验证入口
  406. this.check = function (form, callback) {
  407. $(form).attr("novalidate", "novalidate");
  408. $(form).find(that.tags).map(function () {
  409. this.bindEventMethod = function () {
  410. that.checkInput(this);
  411. };
  412. for (var e in that.checkEvent) if (that.checkEvent[e] === true) {
  413. $(this).off(e, this.bindEventMethod).on(e, this.bindEventMethod);
  414. }
  415. });
  416. $(form).bind("submit", function (event) {
  417. if (that.checkAllInput() && typeof callback === 'function') {
  418. if (typeof CKEDITOR === 'object' && typeof CKEDITOR.instances === 'object') {
  419. for (var i in CKEDITOR.instances) CKEDITOR.instances[i].updateElement();
  420. }
  421. callback.call(this, $(form).formToJson());
  422. }
  423. return event.preventDefault(), false;
  424. });
  425. $(form).find('[data-form-loaded]').map(function () {
  426. $(this).html(this.getAttribute('data-form-loaded') || this.innerHTML);
  427. $(this).removeAttr('data-form-loaded').removeClass('layui-disabled');
  428. });
  429. return $(form).data('validate', this);
  430. };
  431. }).check(form, callback, options);
  432. };
  433. /*! 自动监听规则内表单 */
  434. $.vali.listen = function () {
  435. $('form[data-auto]').map(function () {
  436. if ($(this).attr('data-listen') !== 'true') $(this).attr('data-listen', 'true').vali(function (data) {
  437. var call = $(this).attr('data-callback') || '_default_callback';
  438. var type = this.getAttribute('method') || 'POST', tips = this.getAttribute('data-tips') || undefined;
  439. var time = this.getAttribute('data-time') || undefined, href = this.getAttribute('action') || window.location.href;
  440. $.form.load(href, data, type, window[call] || undefined, true, tips, time);
  441. });
  442. });
  443. };
  444. /*! 注册对象到JqFn */
  445. $.fn.vali = function (callback, options) {
  446. return $.vali(this, callback, options);
  447. };
  448. /*! 表单转JSON */
  449. $.fn.formToJson = function () {
  450. var self = this, data = {}, pushCounters = {};
  451. var patterns = {"key": /[a-zA-Z0-9_]+|(?=\[\])/g, "push": /^$/, "fixed": /^\d+$/, "named": /^[a-zA-Z0-9_]+$/};
  452. this.build = function (base, key, value) {
  453. base[key] = value;
  454. return base;
  455. };
  456. this.pushCounter = function (name) {
  457. if (pushCounters[name] === undefined) pushCounters[name] = 0;
  458. return pushCounters[name]++;
  459. };
  460. $.each($(this).serializeArray(), function () {
  461. var key, keys = this.name.match(patterns.key), merge = this.value, name = this.name;
  462. while ((key = keys.pop()) !== undefined) {
  463. name = name.replace(new RegExp("\\[" + key + "\\]$"), '');
  464. if (key.match(patterns.push)) { // push
  465. merge = self.build([], self.pushCounter(name), merge);
  466. } else if (key.match(patterns.fixed)) { // fixed
  467. merge = self.build([], key, merge);
  468. } else if (key.match(patterns.named)) { // named
  469. merge = self.build({}, key, merge);
  470. }
  471. }
  472. data = $.extend(true, data, merge);
  473. });
  474. return data;
  475. };
  476. /*! 全局文件上传入口 */
  477. $.fn.uploadFile = function (callback) {
  478. if (this.attr('data-inited')) return false;
  479. var that = this, mode = $(this).attr('data-file') || 'one';
  480. this.attr('data-inited', true).attr('data-multiple', (mode !== 'btn' && mode !== 'one') ? 1 : 0);
  481. require(['upload'], function (apply) {
  482. apply(that, null, callback);
  483. });
  484. };
  485. /*! 上传单个图片 */
  486. $.fn.uploadOneImage = function () {
  487. var name = $(this).attr('name') || 'image', type = $(this).data('type') || 'png,jpg,gif';
  488. var $tpl = $('<a data-file="btn" class="uploadimage"></a>').attr('data-field', name).attr('data-type', type);
  489. $(this).attr('name', name).after($tpl.data('input', this)).on('change', function () {
  490. if (this.value) $tpl.css('backgroundImage', 'url(' + this.value + ')');
  491. }).trigger('change');
  492. };
  493. /*! 上传多个图片 */
  494. $.fn.uploadMultipleImage = function () {
  495. var type = $(this).data('type') || 'png,jpg,gif', name = $(this).attr('name') || 'umt-image';
  496. var $tpl = $('<a class="uploadimage"></a>').attr('data-file', 'mul').attr('data-field', name).attr('data-type', type);
  497. $(this).attr('name', name).after($tpl.data('input', this)).on('change', function () {
  498. var input = this;
  499. this.setImageData = function () {
  500. input.value = input.getImageData().join('|');
  501. };
  502. this.getImageData = function () {
  503. var values = [];
  504. $(input).prevAll('.uploadimage').map(function () {
  505. values.push($(this).attr('data-tips-image'));
  506. });
  507. return values.reverse(), values;
  508. };
  509. var urls = this.getImageData(), srcs = this.value.split('|');
  510. for (var i in srcs) if (srcs[i]) urls.push(srcs[i]);
  511. $(this).prevAll('.uploadimage').remove();
  512. this.value = urls.join('|');
  513. for (var i in urls) {
  514. var tpl = '<div class="uploadimage uploadimagemtl"><a class="layui-icon margin-right-5">&#xe602;</a><a class="layui-icon margin-right-5">&#x1006;</a><a class="layui-icon margin-right-5">&#xe603;</a></div>';
  515. var $tpl = $(tpl).attr('data-tips-image', urls[i]).css('backgroundImage', 'url(' + urls[i] + ')').on('click', 'a', function (e) {
  516. e.stopPropagation();
  517. var $cur = $(this).parent();
  518. switch ($(this).index()) {
  519. case 1:// remove
  520. return $.msg.confirm('确定要移除这张图片吗?', function (index) {
  521. $cur.remove(), input.setImageData(), $.msg.close(index);
  522. });
  523. case 0: // right
  524. var lenght = $cur.siblings('div.uploadimagemtl').length;
  525. if ($cur.index() !== lenght) $cur.next().after($cur);
  526. return input.setImageData();
  527. case 2: // left
  528. if ($cur.index() !== 0) $cur.prev().before($cur);
  529. return input.setImageData();
  530. }
  531. });
  532. $(this).before($tpl);
  533. }
  534. }).trigger('change');
  535. };
  536. /*! 注册 data-load 事件行为 */
  537. $body.on('click', '[data-load]', function () {
  538. var url = $(this).attr('data-load'), tips = $(this).attr('data-tips'), time = $(this).attr('data-time');
  539. if ($(this).attr('data-confirm')) return $.msg.confirm($(this).attr('data-confirm'), function () {
  540. $.form.load(url, {}, 'get', null, true, tips, time);
  541. });
  542. $.form.load(url, {}, 'get', null, true, tips, time);
  543. });
  544. /*! 注册 data-serach 表单搜索行为 */
  545. $body.on('submit', 'form.form-search', function () {
  546. var url = $(this).attr('action').replace(/&?page=\d+/g, ''), split = url.indexOf('?') === -1 ? '?' : '&';
  547. if ((this.method || 'get').toLowerCase() === 'get') {
  548. return window.location.href = '#' + $.menu.parseUri(url + split + $(this).serialize());
  549. }
  550. $.form.load(url, this, 'post');
  551. });
  552. /*! 注册 data-modal 事件行为 */
  553. $body.on('click', '[data-modal]', function () {
  554. return $.form.modal($(this).attr('data-modal'), 'open_type=modal', $(this).attr('data-title') || $(this).text() || '编辑');
  555. });
  556. /*! 注册 data-open 事件行为 */
  557. $body.on('click', '[data-open]', function () {
  558. $.form.href($(this).attr('data-open'), this);
  559. });
  560. /*! 注册 data-dbclick 事件行为 */
  561. $body.on('dblclick', '[data-dbclick]', function () {
  562. $(this).find(this.getAttribute('data-dbclick') || '[data-dbclick]').trigger('click');
  563. });
  564. /*! 注册 data-reload 事件行为 */
  565. $body.on('click', '[data-reload]', function () {
  566. $.form.reload();
  567. });
  568. /*! 注册 data-check 事件行为 */
  569. $body.on('click', '[data-check-target]', function () {
  570. var checked = !!this.checked;
  571. $($(this).attr('data-check-target')).map(function () {
  572. this.checked = checked;
  573. $(this).trigger('change');
  574. });
  575. });
  576. /*! 注册 data-action 事件行为 */
  577. $body.on('click', '[data-action]', function () {
  578. var $this = $(this), data = {}, time = $this.attr('data-time'), action = $this.attr('data-action');
  579. var loading = $this.attr('data-loading'), method = $this.attr('data-method') || 'post';
  580. var rule = $this.attr('data-value') || (function (rule, ids) {
  581. $($this.attr('data-target') || 'input[type=checkbox].list-check-box').map(function () {
  582. (this.checked) && ids.push(this.value);
  583. });
  584. return ids.length > 0 ? rule.replace('{key}', ids.join(',')) : '';
  585. }).call(this, $this.attr('data-rule') || '', []) || '';
  586. if (rule.length < 1) return $.msg.tips('请选择需要更改的数据!');
  587. var rules = rule.split(';');
  588. for (var i in rules) {
  589. if (rules[i].length < 2) return $.msg.tips('异常的数据操作规则,请修改规则!');
  590. data[rules[i].split('#')[0]] = rules[i].split('#')[1];
  591. }
  592. data['_csrf_'] = $this.attr('data-token') || $this.attr('data-csrf') || '--';
  593. var load = loading !== 'false', tips = typeof loading === 'string' ? loading : undefined;
  594. if (!$this.attr('data-confirm')) $.form.load(action, data, method, false, load, tips, time);
  595. else $.msg.confirm($this.attr('data-confirm'), function () {
  596. $.form.load(action, data, method, false, load, tips, time);
  597. });
  598. });
  599. /*! 输入框失焦提交 */
  600. $body.on('blur', '[data-action-blur]', function () {
  601. var data = {}, that = this, $this = $(this), action = $this.attr('data-action-blur');
  602. var time = $this.attr('data-time'), loading = $this.attr('data-loading') || false;
  603. var load = loading !== 'false', tips = typeof loading === 'string' ? loading : undefined;
  604. var method = $this.attr('data-method') || 'post', confirm = $this.attr('data-confirm');
  605. var attrs = $this.attr('data-value').replace('{value}', $this.val()).split(';');
  606. for (var i in attrs) {
  607. if (attrs[i].length < 2) return $.msg.tips('异常的数据操作规则,请修改规则!');
  608. data[attrs[i].split('#')[0]] = attrs[i].split('#')[1];
  609. }
  610. that.callback = function (ret) {
  611. $this.css('border', (ret && ret.code) ? '1px solid #e6e6e6' : '1px solid red');
  612. return false;
  613. };
  614. data['_csrf_'] = $this.attr('data-token') || $this.attr('data-csrf') || '--';
  615. if (!confirm) return $.form.load(action, data, method, that.callback, load, tips, time);
  616. $.msg.confirm(confirm, function () {
  617. $.form.load(action, data, method, that.callback, load, tips, time);
  618. });
  619. });
  620. /*! 注册 data-href 事件行为 */
  621. $body.on('click', '[data-href]', function (href) {
  622. href = $(this).attr('data-href');
  623. if (href && href.indexOf('#') !== 0) window.location.href = href;
  624. });
  625. /*! 注册 data-iframe 事件行为 */
  626. $body.on('click', '[data-iframe]', function (index) {
  627. index = $.form.iframe($(this).attr('data-iframe'), $(this).attr('data-title') || '窗口');
  628. $(this).attr('data-index', index);
  629. });
  630. /*! 注册 data-icon 事件行为 */
  631. $body.on('click', '[data-icon]', function (field, location) {
  632. field = $(this).attr('data-icon') || $(this).attr('data-field') || 'icon';
  633. location = window.ROOT_URL + '?s=admin/api.plugs/icon.html&field=' + field;
  634. $.form.iframe(location, '图标选择');
  635. });
  636. /*! 注册 data-copy 事件行为 */
  637. $body.on('click', '[data-copy]', function () {
  638. $.copyToClipboard(this.getAttribute('data-copy'));
  639. });
  640. $.copyToClipboard = function (content, input) {
  641. input = document.createElement('textarea');
  642. input.style.position = 'absolute', input.style.left = '-100000px';
  643. input.style.width = '1px', input.style.height = '1px', input.innerText = content;
  644. document.body.appendChild(input), input.select(), setTimeout(function () {
  645. document.execCommand('Copy') ? $.msg.tips('复制成功') : $.msg.tips('复制失败,请使用鼠标操作复制!');
  646. document.body.removeChild(input);
  647. }, 100);
  648. };
  649. /*! 注册 data-tips-text 事件行为 */
  650. $body.on('mouseenter', '[data-tips-text]', function () {
  651. $(this).attr('index', layer.tips($(this).attr('data-tips-text'), this, {tips: [$(this).attr('data-tips-type') || 3, '#78BA32']}));
  652. }).on('mouseleave', '[data-tips-text]', function () {
  653. layer.close($(this).attr('index'));
  654. });
  655. /*! 注册 data-tips-image 事件行为 */
  656. $body.on('click', '[data-tips-image]', function () {
  657. $.previewImage(this.getAttribute('data-tips-image') || this.src, this.getAttribute('data-width'));
  658. });
  659. $.previewImage = function (src, area) {
  660. var img = new Image(), index = $.msg.loading();
  661. img.style.background = '#fff', img.style.display = 'none';
  662. img.style.height = 'auto', img.style.width = area || '480px';
  663. document.body.appendChild(img), img.onerror = function () {
  664. $.msg.close(index);
  665. }, img.onload = function () {
  666. layer.open({
  667. type: 1, shadeClose: true, success: img.onerror, content: $(img), title: false,
  668. area: area || '480px', closeBtn: 1, skin: 'layui-layer-nobg', end: function () {
  669. document.body.removeChild(img);
  670. }
  671. });
  672. };
  673. img.src = src;
  674. };
  675. /*! 注册 data-phone-view 事件行为 */
  676. $body.on('click', '[data-phone-view]', function () {
  677. $.previewPhonePage(this.getAttribute('data-phone-view') || this.href);
  678. });
  679. $.previewPhonePage = function (href, title) {
  680. var tpl = '<div><div class="mobile-preview pull-left"><div class="mobile-header">_TITLE_</div><div class="mobile-body"><iframe id="phone-preview" src="_URL_" frameborder="0" marginheight="0" marginwidth="0"></iframe></div></div></div>';
  681. layer.style(layer.open({type: true, scrollbar: false, area: ['320px', '600px'], title: false, closeBtn: true, shadeClose: false, skin: 'layui-layer-nobg', content: $(tpl.replace('_TITLE_', title || '公众号').replace('_URL_', href)).html(),}), {boxShadow: 'none'});
  682. };
  683. /*! 表单编辑返回操作 */
  684. $body.on('click', '[data-history-back]', function (title) {
  685. title = this.getAttribute('data-history-back') || '确定要返回上一页吗?';
  686. $.msg.confirm(title, function (index) {
  687. history.back();
  688. $.msg.close(index);
  689. })
  690. });
  691. /*! 表单元素失去焦点处理 */
  692. $body.on('blur', '[data-blur-number]', function (fiexd) {
  693. fiexd = this.getAttribute('data-blur-number') || 0;
  694. this.value = (parseFloat(this.value) || 0).toFixed(fiexd);
  695. });
  696. /*! 后台加密登录处理 */
  697. $body.find('[data-login-form]').map(function () {
  698. require(["md5"], function (md5) {
  699. $("form").vali(function (data) {
  700. data['password'] = md5.hash(md5.hash(data['password']) + data['skey']);
  701. if (data['skey']) delete data['skey'];
  702. $.form.load(location.href, data, "post", null, null, null, 'false');
  703. });
  704. });
  705. });
  706. /*! 图片加载异常处理 */
  707. document.addEventListener('error', function (e, elem) {
  708. elem = e.target;
  709. if (elem.tagName.toLowerCase() === 'img') {
  710. elem.src = baseRoot + 'theme/img/404_icon.png';
  711. }
  712. }, true);
  713. /*! 初始化事件 */
  714. $.menu.listen();
  715. $.vali.listen();
  716. });