plugs.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. // +----------------------------------------------------------------------
  2. // | Think.Admin
  3. // +----------------------------------------------------------------------
  4. // | 版权所有 2014~2017 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
  5. // +----------------------------------------------------------------------
  6. // | 官方网站: http://think.ctolog.com
  7. // +----------------------------------------------------------------------
  8. // | 开源协议 ( https://mit-license.org )
  9. // +----------------------------------------------------------------------
  10. // | github开源项目:https://github.com/zoujingli/Think.Admin
  11. // +----------------------------------------------------------------------
  12. define(['jquery'], function () {
  13. // jQuery placeholder, fix for IE6,7,8,9
  14. var JPlaceHolder = {
  15. _check: function () {
  16. return 'placeholder' in document.createElement('input');
  17. },
  18. init: function () {
  19. !this._check() && this.fix();
  20. },
  21. fix: function () {
  22. $(':input[placeholder]').map(function () {
  23. var self = $(this), txt = self.attr('placeholder');
  24. self.wrap($('<div></div>').css({zoom: '1', margin: 'none', border: 'none', padding: 'none', background: 'none', position: 'relative'}));
  25. var pos = self.position(), h = self.outerHeight(true), paddingleft = self.css('padding-left');
  26. var holder = $('<span></span>').text(txt).css({position: 'absolute', left: pos.left, top: pos.top, height: h, lineHeight: h + 'px', paddingLeft: paddingleft, color: '#aaa'}).appendTo(self.parent());
  27. self.on('focusin focusout change keyup', function () {
  28. self.val() ? holder.hide() : holder.show();
  29. });
  30. holder.click(function () {
  31. self.get(0).focus();
  32. });
  33. self.val() && holder.hide();
  34. });
  35. }
  36. };
  37. JPlaceHolder.init();
  38. // 消息处理
  39. $.msg = new msg();
  40. function msg() {
  41. var self = this;
  42. this.shade = [0.02, '#000'];
  43. // 关闭消息框
  44. this.close = function () {
  45. return layer.close(this.index);
  46. };
  47. // 弹出警告消息框
  48. this.alert = function (msg, callback) {
  49. return this.index = layer.alert(msg, {end: callback, scrollbar: false});
  50. };
  51. // 确认对话框
  52. this.confirm = function (msg, ok, no) {
  53. var self = this;
  54. return this.index = layer.confirm(msg, {btn: ['确认', '取消']}, function () {
  55. typeof ok === 'function' && ok.call(this);
  56. self.close();
  57. }, function () {
  58. typeof no === 'function' && no.call(this);
  59. self.close();
  60. });
  61. };
  62. // 显示成功类型的消息
  63. this.success = function (msg, time, callback) {
  64. return this.index = layer.msg(msg, {icon: 1, shade: this.shade, scrollbar: false, end: callback, time: (time || 2) * 1000, shadeClose: true});
  65. };
  66. // 显示失败类型的消息
  67. this.error = function (msg, time, callback) {
  68. return this.index = layer.msg(msg, {icon: 2, shade: this.shade, scrollbar: false, time: (time || 3) * 1000, end: callback, shadeClose: true});
  69. };
  70. // 状态消息提示
  71. this.tips = function (msg, time, callback) {
  72. return this.index = layer.msg(msg, {time: (time || 3) * 1000, shade: this.shade, end: callback, shadeClose: true});
  73. };
  74. // 显示正在加载中的提示
  75. this.loading = function (msg, callback) {
  76. return this.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});
  77. };
  78. this.successNeedCloseLayerIndex = [];
  79. // 自动处理显示Think返回的Json数据
  80. this.auto = function (data, time, load) {
  81. if (parseInt(data.code) === 1) {
  82. return self.success(data.msg, time, function () {
  83. !!data.url ? (window.location.href = data.url) : $.form.reload();
  84. self.close();
  85. for (var i in self.successNeedCloseLayerIndex) {
  86. layer.close(self.successNeedCloseLayerIndex[i]);
  87. }
  88. self.successNeedCloseLayerIndex = [];
  89. });
  90. }
  91. return self.error(data.msg, 3, function () {
  92. !!data.url && (window.location.href = data.url);
  93. });
  94. };
  95. }
  96. // 表单构造函数
  97. $.form = new _form();
  98. function _form() {
  99. this.errMsg = '{status}服务器繁忙,请稍候再试!';
  100. // 内容区域动态加载后初始化
  101. this.reInit = function ($container) {
  102. $.validate.listen.call(this), JPlaceHolder.init();
  103. $container.find('[required]').parent().prevAll('label').addClass('label-required');
  104. };
  105. // 以hash打开网页
  106. this.href = function (url, obj) {
  107. window.location.href = '#' + $.menu.parseUri(url, obj);
  108. };
  109. // 在内容区显示视图
  110. this.show = function (html) {
  111. var $container = $('.framework-container').html(html);
  112. reinit.call(this), setTimeout(reinit, 500), setTimeout(reinit, 1000);
  113. function reinit() {
  114. $.form.reInit($container);
  115. }
  116. };
  117. // 关闭FORM框
  118. this.close = function () {
  119. return $(this._modal).modal('hide');
  120. };
  121. // 刷新当前页面
  122. this.reload = function () {
  123. window.onhashchange.call(this);
  124. };
  125. // 异步加载的数据
  126. this.load = function (url, data, type, callback, loading, tips, time) {
  127. var self = this, dialogIndex = 0;
  128. (loading !== false) && (dialogIndex = $.msg.loading(tips));
  129. (typeof Pace === 'object') && Pace.restart();
  130. $.ajax({
  131. type: type || 'GET', url: $.menu.parseUri(url), data: data || {},
  132. statusCode: {
  133. 404: function () {
  134. $.msg.tips(self.errMsg.replace('{status}', 'E404 - '));
  135. },
  136. 500: function () {
  137. $.msg.tips(self.errMsg.replace('{status}', 'E500 - '));
  138. }
  139. },
  140. error: function (XMLHttpRequest, textStatus, errorThrown) {
  141. $.msg.tips(self.errMsg.replace('{status}', 'E' + textStatus + ' - '));
  142. },
  143. success: function (res) {
  144. $.msg.close(dialogIndex);
  145. if (typeof callback === 'function' && callback.call(self, res) === false) {
  146. return false;
  147. }
  148. if (typeof (res) === 'object') {
  149. return $.msg.auto(res, time);
  150. }
  151. self.show(res);
  152. }
  153. });
  154. };
  155. // 加载HTML到目标位置
  156. this.open = function (url, data, callback, loading, tips) {
  157. this.load(url, data, 'get', function (res) {
  158. if (typeof (res) === 'object') {
  159. return $.msg.auto(res);
  160. }
  161. var $container = $('.framework-container').html(res);
  162. reinit.call(this), setTimeout(reinit, 500), setTimeout(reinit, 1000);
  163. return (typeof callback === 'function') && callback.call(this);
  164. function reinit() {
  165. $.form.reInit($container);
  166. }
  167. }, loading, tips);
  168. };
  169. // 打开一个iframe窗口
  170. this.iframe = function (url, title) {
  171. return layer.open({title: title || '窗口', type: 2, area: ['800px', '530px'], fix: true, maxmin: false, content: url});
  172. };
  173. // 加载HTML到弹出层
  174. this.modal = function (url, data, title, callback, loading, tips) {
  175. this.load(url, data, 'GET', function (res) {
  176. if (typeof (res) === 'object') {
  177. return $.msg.auto(res);
  178. }
  179. var layerIndex = layer.open({type: 1, btn: false, area: "800px", content: res, title: title || '', success: function (dom, index) {
  180. $(dom).find('[data-close]').off('click').on('click', function () {
  181. if ($(this).attr('data-confirm')) {
  182. return $.msg.confirm($(this).attr('data-confirm'), function () {
  183. layer.close(index);
  184. });
  185. }
  186. layer.close(index);
  187. });
  188. $.form.reInit($(dom));
  189. }
  190. });
  191. $.msg.successNeedCloseLayerIndex.push(layerIndex);
  192. return (typeof callback === 'function') && callback.call(this);
  193. }, loading, tips);
  194. };
  195. }
  196. // 表单验证
  197. var validate = function () {
  198. var self = this;
  199. // 表单元素
  200. this.tags = 'input,textarea,select';
  201. // 检测元素事件
  202. this.checkEvent = {change: true, blur: true, keyup: false};
  203. // 去除字符串两头的空格
  204. this.trim = function (str) {
  205. return str.replace(/(^\s*)|(\s*$)/g, '');
  206. };
  207. // 标签元素是否可见
  208. this.isVisible = function (ele) {
  209. return $(ele).is(':visible');
  210. };
  211. // 检测属性是否有定义
  212. this.hasProp = function (ele, prop) {
  213. if (typeof prop !== "string") {
  214. return false;
  215. }
  216. var attrProp = ele.getAttribute(prop);
  217. return (typeof attrProp !== 'undefined' && attrProp !== null && attrProp !== false);
  218. };
  219. // 判断表单元素是否为空
  220. this.isEmpty = function (ele, value) {
  221. var trimValue = this.trim(ele.value);
  222. value = value || ele.getAttribute('placeholder');
  223. return (trimValue === "" || trimValue === value);
  224. };
  225. // 正则验证表单元素
  226. this.isRegex = function (ele, regex, params) {
  227. var inputValue = ele.value, dealValue = this.trim(inputValue);
  228. regex = regex || ele.getAttribute('pattern');
  229. if (dealValue === "" || !regex) {
  230. return true;
  231. }
  232. if (dealValue !== inputValue) {
  233. (ele.tagName.toLowerCase() !== "textarea") ? (ele.value = dealValue) : (ele.innerHTML = dealValue);
  234. }
  235. return new RegExp(regex, params || 'i').test(dealValue);
  236. };
  237. // 检侧所的表单元素
  238. this.isAllpass = function (elements, options) {
  239. if (!elements) {
  240. return true;
  241. }
  242. var allpass = true, self = this, params = options || {};
  243. if (elements.size && elements.size() === 1 && elements.get(0).tagName.toLowerCase() === "form") {
  244. elements = $(elements).find(self.tags);
  245. } else if (elements.tagName && elements.tagName.toLowerCase() === "form") {
  246. elements = $(elements).find(self.tags);
  247. }
  248. elements.each(function () {
  249. if (self.checkInput(this, params) === false) {
  250. return $(this).focus(), (allpass = false);
  251. }
  252. });
  253. return allpass;
  254. };
  255. // 验证标志
  256. this.remind = function (input) {
  257. return this.isVisible(input) ? this.errorPlacement(input, this.getErrMsg(input)) : false;
  258. };
  259. // 检测表单单元
  260. this.checkInput = function (input) {
  261. var type = (input.getAttribute("type") + "").replace(/\W+$/, "").toLowerCase();
  262. var tag = input.tagName.toLowerCase(), isRequired = this.hasProp(input, "required");
  263. if (this.hasProp(input, 'data-auto-none') || input.disabled || type === 'submit' || type === 'reset' || type === 'file' || type === 'image' || !this.isVisible(input)) {
  264. return;
  265. }
  266. var allpass = true;
  267. if (type === "radio" && isRequired) {
  268. var radiopass = false, eleRadios = input.name ? $("input[type='radio'][name='" + input.name + "']") : $(input);
  269. eleRadios.each(function () {
  270. (radiopass === false && $(this).is("[checked]")) && (radiopass = true);
  271. });
  272. if (radiopass === false) {
  273. allpass = this.remind(eleRadios.get(0), type, tag);
  274. } else {
  275. this.successPlacement(input);
  276. }
  277. } else if (type === "checkbox" && isRequired && !$(input).is("[checked]")) {
  278. allpass = this.remind(input, type, tag);
  279. } else if (tag === "select" && isRequired && !input.value) {
  280. allpass = this.remind(input, type, tag);
  281. } else if ((isRequired && this.isEmpty(input)) || !(allpass = this.isRegex(input))) {
  282. allpass ? this.remind(input, type, "empty") : this.remind(input, type, tag);
  283. allpass = false;
  284. } else {
  285. this.successPlacement(input);
  286. }
  287. return allpass;
  288. };
  289. // 获取错误提示的内容
  290. this.getErrMsg = function (ele) {
  291. return ele.getAttribute('title') || '';
  292. };
  293. // 错误消息显示
  294. this.errorPlacement = function (ele, content) {
  295. $(ele).addClass('validate-error'), this.insertErrorEle(ele);
  296. $($(ele).data('input-info')).addClass('fadeInRight animated').css({width: 'auto'}).html(content);
  297. };
  298. // 错误消息消除
  299. this.successPlacement = function (ele) {
  300. $(ele).removeClass('validate-error'), this.insertErrorEle(ele);
  301. $($(ele).data('input-info')).removeClass('fadeInRight').css({width: '30px'}).html('');
  302. };
  303. // 错误消息标签插入
  304. this.insertErrorEle = function (ele) {
  305. var $html = $('<span style="-webkit-animation-duration:.2s;animation-duration:.2s;padding-right:20px;color:#a94442;position:absolute;right:0;font-size:12px;z-index:2;display:block;width:34px;text-align:center;pointer-events:none"></span>');
  306. $html.css({top: $(ele).position().top + 'px', paddingBottom: $(ele).css('paddingBottom'), lineHeight: $(ele).css('lineHeight')});
  307. $(ele).data('input-info') || $(ele).data('input-info', $html.insertAfter(ele));
  308. };
  309. // 表单验证入口
  310. this.check = function (form, callback, options) {
  311. var params = $.extend({}, options || {});
  312. $(form).attr("novalidate", "novalidate");
  313. $(form).find(self.tags).map(function () {
  314. for (var i in self.checkEvent) {
  315. if (self.checkEvent[i] === true) {
  316. $(this).off(i, func).on(i, func);
  317. }
  318. }
  319. function func() {
  320. self.checkInput(this);
  321. }
  322. });
  323. $(form).bind("submit", function (event) {
  324. if (self.isAllpass($(this).find(self.tags), params) && typeof callback === 'function') {
  325. callback.call(this, $(form).serialize());
  326. }
  327. return event.preventDefault(), false;
  328. });
  329. return $(form).data('validate', this);
  330. };
  331. };
  332. // 注册对象到JqFn
  333. $.fn.validate = function (callback, options) {
  334. return (new validate()).check(this, callback, options);
  335. };
  336. // 注册对象到Jq
  337. $.validate = function (form, callback, options) {
  338. return (new validate()).check(form, callback, options);
  339. };
  340. // 自动监听规则内表单
  341. $.validate.listen = function () {
  342. $('form[data-auto]').map(function () {
  343. if ($(this).attr('data-listen') !== 'true') {
  344. var callbackname = $(this).attr('data-callback');
  345. $(this).attr('data-listen', 'true').validate(function (data) {
  346. var method = this.getAttribute('method') || 'POST';
  347. var tips = this.getAttribute('data-tips') || undefined;
  348. var url = this.getAttribute('action') || window.location.href;
  349. var callback = window[callbackname || '_default_callback'] || undefined;
  350. $.form.load(url, data, method, callback, true, tips, this.getAttribute('data-time') || undefined);
  351. });
  352. $(this).find('[data-form-loaded]').map(function () {
  353. $(this).html(this.getAttribute('data-form-loaded') || this.innerHTML);
  354. $(this).removeAttr('data-form-loaded').removeClass('layui-disabled');
  355. });
  356. }
  357. });
  358. };
  359. // 后台菜单辅助插件
  360. $.menu = new _menu();
  361. function _menu() {
  362. // 计算URL地址中有效的URI
  363. this.getUri = function (uri) {
  364. uri = uri || window.location.href;
  365. uri = (uri.indexOf(window.location.host) !== -1 ? uri.split(window.location.host)[1] : uri).split('?')[0];
  366. return (uri.indexOf('#') !== -1 ? uri.split('#')[1] : uri);
  367. };
  368. // 通过URI查询最有可能的菜单NODE
  369. this.queryNode = function (url) {
  370. var $menu = $('[data-menu-node][data-open*="_URL_"]'.replace('_URL_', url.replace(/\.html$/ig, '')));
  371. if ($menu.size()) {
  372. return $menu.get(0).getAttribute('data-menu-node');
  373. }
  374. return /^m\-/.test(node = location.href.replace(/.*spm=([\d\-m]+).*/ig, '$1')) ? node : '';
  375. };
  376. // URL转URI
  377. this.parseUri = function (uri, obj) {
  378. var params = {};
  379. if (uri.indexOf('?') !== -1) {
  380. var queryParams = uri.split('?')[1].split('&');
  381. for (var i in queryParams) {
  382. if (queryParams[i].indexOf('=') !== -1) {
  383. var hash = queryParams[i].split('=');
  384. try {
  385. params[hash[0]] = window.decodeURIComponent(window.decodeURIComponent(hash[1].replace(/%2B/ig, ' ')));
  386. } catch (e) {
  387. console.log([e, uri, queryParams, hash]);
  388. }
  389. }
  390. }
  391. }
  392. uri = this.getUri(uri);
  393. params.spm = obj && obj.getAttribute('data-menu-node') || this.queryNode(uri);
  394. if (!params.token) {
  395. var token = window.location.href.replace(/.*token=(\w+).*/ig, '$1');
  396. (/^\w{16}$/.test(token)) && (params.token = token);
  397. }
  398. delete params[""];
  399. var query = '?' + $.param(params);
  400. return uri + (query !== '?' ? query : '');
  401. };
  402. // 后台菜单动作初始化
  403. this.listen = function () {
  404. /*! 左侧菜单状态切换 */
  405. $('ul.sidebar-trans .nav-item a').on('click', function () {
  406. $(this).parents('.sidebar-nav.main-nav').addClass('open').find('ul.sidebar-trans').show();
  407. $('.sidebar-trans .nav-item').not($(this).parent().addClass('active')).removeClass('active');
  408. });
  409. $('body').on('click', '.framework-sidebar-full .sidebar-title', function () {
  410. var $trans = $(this).next('ul.sidebar-trans'), node = $trans.attr('data-menu-node') || false;
  411. node && $.cookie(node, $(this).parent().toggleClass('open').hasClass('open') ? 2 : 1);
  412. $(this).parent().hasClass('open') ? $trans.show() : $trans.hide();
  413. });
  414. $('ul.sidebar-trans').map(function () {
  415. var node = this.getAttribute('data-menu-node') || false;
  416. node && (parseInt($.cookie(node) || 2) === 2) && $(this).show().parent().addClass('open');
  417. });
  418. /*! Mini 菜单模式 Tips 显示*/
  419. $('body').on('mouseenter mouseleave', '.framework-sidebar-mini .sidebar-trans .nav-item,.framework-sidebar-mini .sidebar-title', function (e) {
  420. $(this).tooltip({
  421. template: '<div class="console-sidebar-tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
  422. title: $(this).text(), placement: 'right', container: 'body'
  423. }).tooltip('show'), (e.type === 'mouseleave') && $(this).tooltip('destroy');
  424. });
  425. /*! 切换左侧菜单 */
  426. var $menutarget = $('[data-menu-target]').on('click', function () {
  427. $menutarget.not($(this).addClass('active')).removeClass('active');
  428. var menuNode = $(this).attr('data-menu-target'), $left = $('[data-menu-box=' + menuNode + ']').removeClass('hide');
  429. $("[data-menu-box]").not($left).addClass('hide'), $left.find('[data-open]:first').trigger('click')
  430. });
  431. /*! 左侧菜单样式切换 */
  432. var $targetmenu = $('.sidebar-fold').on('click', function () {
  433. var $body = $('.framework-body').toggleClass('framework-sidebar-mini framework-sidebar-full');
  434. $.cookie('menu-style', $body.hasClass('framework-sidebar-mini') ? 'mini' : 'full');
  435. });
  436. ($.cookie('menu-style') !== 'mini') && $targetmenu.trigger('click');
  437. /*! URI路由处理 */
  438. window.onhashchange = function () {
  439. var hash = (window.location.hash || '').substring(1), node = hash.replace(/.*spm=([\d\-m]+).*/ig, "$1");
  440. if (!/^m\-[\d\-]+$/.test(node)) {
  441. node = $.menu.queryNode($.menu.getUri()) || '';
  442. }
  443. if (hash.length < 1 || node.length < 1) {
  444. return $('.topbar-home-link:first').trigger('click');
  445. }
  446. /* 顶部菜单选中处理 */
  447. var pNode = [node.split('-')[0], node.split('-')[1]].join('-');
  448. $('.topbar-home-link').not($('[data-menu-target="' + pNode + '"]').addClass('active')).removeClass('active');
  449. /* 左则菜单处理 */
  450. var $menu = $('[data-menu-node="' + node + '"]').eq(0);
  451. if ($menu.size() > 0) {
  452. $menu.parents('.main-nav').addClass('open'), $menu.parents('.sidebar-trans').removeClass('hide').show();
  453. var $li = $menu.parent('li').addClass('active');
  454. $li.parents('.framework-sidebar').find('li.active').not($li).removeClass('active');
  455. $menu.parents('[data-menu-box]').removeClass('hide').siblings('[data-menu-box]').addClass('hide');
  456. if (/^m\-\d+$/i.test(node)) {
  457. $('.framework-sidebar').addClass('hide'), $menu.addClass('active');
  458. $('.framework-container').css('left', 0).addClass('framework-sidebar-full');
  459. } else {
  460. $('.framework-sidebar').removeClass('hide');
  461. $('.framework-container').removeAttr('style').addClass('framework-sidebar-full');
  462. }
  463. } else {
  464. $('.framework-sidebar').hide();
  465. $('.framework-container').removeClass('framework-sidebar-full');
  466. }
  467. $.form.open(hash);
  468. };
  469. // URI初始化动作
  470. window.onhashchange.call(this);
  471. };
  472. }
  473. });