交通管理局公文项目
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

756 lines
22 KiB

2 months ago
  1. /*!
  2. @Title: layui
  3. @Description:经典模块化前端 UI 框架
  4. @Site: www.layui.com
  5. @Author: 贤心
  6. @License:MIT
  7. */
  8. !(function (win) {
  9. 'use strict';
  10. var doc = document;
  11. var config = {
  12. modules: {}, // 记录模块物理路径
  13. status: {}, // 记录模块加载状态
  14. timeout: 10, // 符合规范的模块请求最长等待秒数
  15. event: {}, // 记录模块自定义事件
  16. version: '30233', // 系统版本号 为了解决浏览器的缓存问题; 系统中的js、html文件路径都会拼上此版本号
  17. };
  18. var Layui = function () {
  19. this.v = '2.5.5'; // 版本号
  20. };
  21. // 获取layui所在目录
  22. var getPath = (function () {
  23. var jsPath = doc.currentScript
  24. ? doc.currentScript.src
  25. : (function () {
  26. var js = doc.scripts;
  27. var last = js.length - 1;
  28. var src;
  29. for (var i = last; i > 0; i--) {
  30. if (js[i].readyState === 'interactive') {
  31. src = js[i].src;
  32. break;
  33. }
  34. }
  35. return src || js[last].src;
  36. })();
  37. return jsPath.substring(0, jsPath.lastIndexOf('/') + 1);
  38. })();
  39. // 异常提示
  40. var error = function (msg) {
  41. win.console && console.error && console.error('Layui hint: ' + msg);
  42. };
  43. var isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]';
  44. // 内置模块
  45. var modules = {
  46. layer: 'modules/layer', // 弹层
  47. laydate: 'modules/laydate', // 日期
  48. laypage: 'modules/laypage', // 分页
  49. laytpl: 'modules/laytpl', // 模板引擎
  50. layim: 'modules/layim', // web通讯
  51. layedit: 'modules/layedit', // 富文本编辑器
  52. form: 'modules/form', // 表单集
  53. upload: 'modules/upload', // 上传
  54. transfer: 'modules/transfer', // 上传
  55. tree: 'modules/tree', // 树结构
  56. table: 'modules/table', // 表格
  57. element: 'modules/element', // 常用元素操作
  58. rate: 'modules/rate', // 评分组件
  59. colorpicker: 'modules/colorpicker', // 颜色选择器
  60. slider: 'modules/slider', // 滑块
  61. carousel: 'modules/carousel', // 轮播
  62. flow: 'modules/flow', // 流加载
  63. util: 'modules/util', // 工具块
  64. code: 'modules/code', // 代码修饰器
  65. jquery: 'modules/jquery', // DOM库(第三方)
  66. mobile: 'modules/mobile', // 移动大模块 | 若当前为开发目录,则为移动模块入口,否则为移动模块集合
  67. 'layui.all': '../layui.all', // PC模块合并版
  68. };
  69. // 记录基础数据
  70. Layui.prototype.cache = config;
  71. // 定义模块
  72. Layui.prototype.define = function (deps, factory) {
  73. var that = this;
  74. var type = typeof deps === 'function';
  75. var callback = function () {
  76. var setApp = function (app, exports) {
  77. layui[app] = exports;
  78. config.status[app] = true;
  79. };
  80. typeof factory === 'function' &&
  81. factory(function (app, exports) {
  82. setApp(app, exports);
  83. config.callback[app] = function () {
  84. factory(setApp);
  85. };
  86. });
  87. return this;
  88. };
  89. type && ((factory = deps), (deps = []));
  90. if (!layui['layui.all'] && layui['layui.mobile']) {
  91. return callback.call(that);
  92. }
  93. that.use(deps, callback);
  94. return that;
  95. };
  96. // 使用特定模块
  97. Layui.prototype.use = function (apps, callback, exports) {
  98. var that = this;
  99. var dir = (config.dir = config.dir ? config.dir : getPath);
  100. var head = doc.getElementsByTagName('head')[0];
  101. apps = typeof apps === 'string' ? [apps] : apps;
  102. // 如果页面已经存在jQuery1.7+库且所定义的模块依赖jQuery,则不加载内部jquery模块
  103. if (window.jQuery && jQuery.fn.on) {
  104. that.each(apps, function (index, item) {
  105. if (item === 'jquery') {
  106. apps.splice(index, 1);
  107. }
  108. });
  109. layui.jquery = layui.$ = jQuery;
  110. }
  111. var item = apps[0];
  112. var timeout = 0;
  113. exports = exports || [];
  114. // 静态资源host
  115. config.host = config.host || (dir.match(/\/\/([\s\S]+?)\//) || ['//' + location.host + '/'])[0];
  116. // 加载完毕
  117. function onScriptLoad (e, url) {
  118. var readyRegExp =
  119. navigator.platform === 'PLaySTATION 3' ? /^complete$/ : /^(complete|loaded)$/;
  120. if (e.type === 'load' || readyRegExp.test((e.currentTarget || e.srcElement).readyState)) {
  121. config.modules[item] = url;
  122. head.removeChild(node);
  123. (function poll () {
  124. if (++timeout > (config.timeout * 1000) / 4) {
  125. return error(item + ' is not a valid module');
  126. }
  127. config.status[item] ? onCallback() : setTimeout(poll, 4);
  128. })();
  129. }
  130. }
  131. // 回调
  132. function onCallback () {
  133. exports.push(layui[item]);
  134. apps.length > 1
  135. ? that.use(apps.slice(1), callback, exports)
  136. : typeof callback === 'function' && callback.apply(layui, exports);
  137. }
  138. // 如果引入了完整库(layui.all.js),内置的模块则不必再加载
  139. if (
  140. apps.length === 0 ||
  141. (layui['layui.all'] && modules[item]) ||
  142. (!layui['layui.all'] && layui['layui.mobile'] && modules[item])
  143. ) {
  144. return onCallback(), that;
  145. }
  146. // 首次加载模块
  147. if (!config.modules[item]) {
  148. var node = doc.createElement('script');
  149. // 如果是内置模块,则按照 dir 参数拼接模块路径
  150. // 如果是扩展模块,则判断模块路径值是否为 {/} 开头,
  151. // 如果路径值是 {/} 开头,则模块路径即为后面紧跟的字符。
  152. // 否则,则按照 base 参数拼接模块路径
  153. var url =
  154. (modules[item]
  155. ? dir + 'lay/'
  156. : /^\{\/\}/.test(that.modules[item])
  157. ? ''
  158. : config.base || '') +
  159. (that.modules[item] || item) +
  160. '.js';
  161. url = url.replace(/^\{\/\}/, '');
  162. node.async = true;
  163. node.charset = 'utf-8';
  164. node.src =
  165. url +
  166. (function () {
  167. var version =
  168. config.version === true ? config.v || new Date().getTime() : config.version || '';
  169. return version ? '?v=' + version : '';
  170. })();
  171. head.appendChild(node);
  172. if (
  173. node.attachEvent &&
  174. !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) &&
  175. !isOpera
  176. ) {
  177. node.attachEvent('onreadystatechange', function (e) {
  178. onScriptLoad(e, url);
  179. });
  180. } else {
  181. node.addEventListener(
  182. 'load',
  183. function (e) {
  184. onScriptLoad(e, url);
  185. },
  186. false
  187. );
  188. }
  189. config.modules[item] = url;
  190. } else {
  191. // 缓存
  192. (function poll () {
  193. if (++timeout > (config.timeout * 1000) / 4) {
  194. return error(item + ' is not a valid module');
  195. }
  196. typeof config.modules[item] === 'string' && config.status[item]
  197. ? onCallback()
  198. : setTimeout(poll, 4);
  199. })();
  200. }
  201. return that;
  202. };
  203. // 获取节点的style属性值
  204. Layui.prototype.getStyle = function (node, name) {
  205. var style = node.currentStyle ? node.currentStyle : win.getComputedStyle(node, null);
  206. return style[style.getPropertyValue ? 'getPropertyValue' : 'getAttribute'](name);
  207. };
  208. // css外部加载器
  209. Layui.prototype.link = function (href, fn, cssname) {
  210. var that = this;
  211. var link = doc.createElement('link');
  212. var head = doc.getElementsByTagName('head')[0];
  213. if (typeof fn === 'string') {
  214. cssname = fn;
  215. }
  216. var app = (cssname || href).replace(/\.|\//g, '');
  217. var id = (link.id = 'layuicss-' + app);
  218. var timeout = 0;
  219. link.rel = 'stylesheet';
  220. link.href = href + (config.debug ? '?v=' + new Date().getTime() : '');
  221. link.media = 'all';
  222. if (!doc.getElementById(id)) {
  223. head.appendChild(link);
  224. }
  225. if (typeof fn !== 'function') {
  226. return that;
  227. }
  228. // 轮询css是否加载完毕
  229. (function poll () {
  230. if (++timeout > (config.timeout * 1000) / 100) {
  231. return error(href + ' timeout');
  232. }
  233. parseInt(that.getStyle(doc.getElementById(id), 'width')) === 1989
  234. ? (function () {
  235. fn();
  236. })()
  237. : setTimeout(poll, 100);
  238. })();
  239. return that;
  240. };
  241. // 存储模块的回调
  242. config.callback = {};
  243. // 重新执行模块的工厂函数
  244. Layui.prototype.factory = function (modName) {
  245. if (layui[modName]) {
  246. return typeof config.callback[modName] === 'function' ? config.callback[modName] : null;
  247. }
  248. };
  249. // css内部加载器
  250. Layui.prototype.addcss = function (firename, fn, cssname) {
  251. return layui.link(config.dir + 'css/' + firename, fn, cssname);
  252. };
  253. // 图片预加载
  254. Layui.prototype.img = function (url, callback, error) {
  255. var img = new Image();
  256. img.src = url;
  257. if (img.complete) {
  258. return callback(img);
  259. }
  260. img.onload = function () {
  261. img.onload = null;
  262. typeof callback === 'function' && callback(img);
  263. };
  264. img.onerror = function (e) {
  265. img.onerror = null;
  266. typeof error === 'function' && error(e);
  267. };
  268. };
  269. // 全局配置
  270. Layui.prototype.config = function (options) {
  271. options = options || {};
  272. for (var key in options) {
  273. config[key] = options[key];
  274. }
  275. return this;
  276. };
  277. // 记录全部模块
  278. Layui.prototype.modules = (function () {
  279. var clone = {};
  280. for (var o in modules) {
  281. clone[o] = modules[o];
  282. }
  283. return clone;
  284. })();
  285. // 拓展模块
  286. Layui.prototype.extend = function (options) {
  287. var that = this;
  288. // 验证模块是否被占用
  289. options = options || {};
  290. for (var o in options) {
  291. if (that[o] || that.modules[o]) {
  292. error('\u6A21\u5757\u540D ' + o + ' \u5DF2\u88AB\u5360\u7528');
  293. } else {
  294. that.modules[o] = options[o];
  295. }
  296. }
  297. return that;
  298. };
  299. // 路由解析
  300. Layui.prototype.router = function (hash) {
  301. var that = this;
  302. var hash = hash || location.hash;
  303. var data = {
  304. path: [],
  305. search: {},
  306. hash: (hash.match(/[^#](#.*$)/) || [])[1] || '',
  307. };
  308. if (!/^#\//.test(hash)) {
  309. return data;
  310. } // 禁止非路由规范
  311. hash = hash.replace(/^#\//, '');
  312. data.href = '/' + hash;
  313. hash = hash.replace(/([^#])(#.*$)/, '$1').split('/') || [];
  314. // 提取Hash结构
  315. that.each(hash, function (index, item) {
  316. /^\w+=/.test(item)
  317. ? (function () {
  318. item = item.split('=');
  319. data.search[item[0]] = item[1];
  320. })()
  321. : data.path.push(item);
  322. });
  323. return data;
  324. };
  325. // 本地持久性存储
  326. Layui.prototype.data = function (table, settings, storage) {
  327. table = table || 'layui';
  328. storage = storage || localStorage;
  329. if (!win.JSON || !win.JSON.parse) {
  330. return;
  331. }
  332. // 如果settings为null,则删除表
  333. if (settings === null) {
  334. return delete storage[table];
  335. }
  336. settings = typeof settings === 'object' ? settings : { key: settings };
  337. try {
  338. var data = JSON.parse(storage[table]);
  339. } catch (e) {
  340. var data = {};
  341. }
  342. if ('value' in settings) {
  343. data[settings.key] = settings.value;
  344. }
  345. if (settings.remove) {
  346. delete data[settings.key];
  347. }
  348. storage[table] = JSON.stringify(data);
  349. return settings.key ? data[settings.key] : data;
  350. };
  351. // 本地会话性存储
  352. Layui.prototype.sessionData = function (table, settings) {
  353. return this.data(table, settings, sessionStorage);
  354. };
  355. // 设备信息
  356. Layui.prototype.device = function (key) {
  357. var agent = navigator.userAgent.toLowerCase();
  358. // 获取版本号
  359. var getVersion = function (label) {
  360. var exp = new RegExp(label + '/([^\\s\\_\\-]+)');
  361. label = (agent.match(exp) || [])[1];
  362. return label || false;
  363. };
  364. // 返回结果集
  365. var result = {
  366. os: (function () {
  367. // 底层操作系统
  368. if (/windows/.test(agent)) {
  369. return 'windows';
  370. } else if (/linux/.test(agent)) {
  371. return 'linux';
  372. } else if (/iphone|ipod|ipad|ios/.test(agent)) {
  373. return 'ios';
  374. } else if (/mac/.test(agent)) {
  375. return 'mac';
  376. }
  377. })(),
  378. ie: (function () {
  379. // ie版本
  380. return !!win.ActiveXObject || 'ActiveXObject' in win
  381. ? (agent.match(/msie\s(\d+)/) || [])[1] || '11' // 由于ie11并没有msie的标识
  382. : false;
  383. })(),
  384. weixin: getVersion('micromessenger'), // 是否微信
  385. };
  386. // 任意的key
  387. if (key && !result[key]) {
  388. result[key] = getVersion(key);
  389. }
  390. // 移动设备
  391. result.android = /android/.test(agent);
  392. result.ios = result.os === 'ios';
  393. return result;
  394. };
  395. // 提示
  396. Layui.prototype.hint = function () {
  397. return {
  398. error: error,
  399. };
  400. };
  401. // 遍历
  402. Layui.prototype.each = function (obj, fn) {
  403. var key;
  404. var that = this;
  405. if (typeof fn !== 'function') {
  406. return that;
  407. }
  408. obj = obj || [];
  409. if (obj.constructor === Object) {
  410. for (key in obj) {
  411. if (fn.call(obj[key], key, obj[key])) {
  412. break;
  413. }
  414. }
  415. } else {
  416. for (key = 0; key < obj.length; key++) {
  417. if (fn.call(obj[key], key, obj[key])) {
  418. break;
  419. }
  420. }
  421. }
  422. return that;
  423. };
  424. // 将数组中的对象按其某个成员排序
  425. Layui.prototype.sort = function (obj, key, desc) {
  426. var clone = JSON.parse(JSON.stringify(obj || []));
  427. if (!key) {
  428. return clone;
  429. }
  430. // 如果是数字,按大小排序,如果是非数字,按字典序排序
  431. clone.sort(function (o1, o2) {
  432. var isNum = /^-?\d+$/;
  433. var v1 = o1[key];
  434. var v2 = o2[key];
  435. if (isNum.test(v1)) {
  436. v1 = parseFloat(v1);
  437. }
  438. if (isNum.test(v2)) {
  439. v2 = parseFloat(v2);
  440. }
  441. if (v1 && !v2) {
  442. return 1;
  443. } else if (!v1 && v2) {
  444. return -1;
  445. }
  446. if (v1 > v2) {
  447. return 1;
  448. } else if (v1 < v2) {
  449. return -1;
  450. } else {
  451. return 0;
  452. }
  453. });
  454. desc && clone.reverse(); // 倒序
  455. return clone;
  456. };
  457. // 阻止事件冒泡
  458. Layui.prototype.stope = function (thisEvent) {
  459. thisEvent = thisEvent || win.event;
  460. try {
  461. thisEvent.stopPropagation();
  462. } catch (e) {
  463. thisEvent.cancelBubble = true;
  464. }
  465. };
  466. // 自定义模块事件
  467. Layui.prototype.onevent = function (modName, events, callback) {
  468. if (typeof modName !== 'string' || typeof callback !== 'function') {
  469. return this;
  470. }
  471. return Layui.event(modName, events, null, callback);
  472. };
  473. // 执行自定义模块事件
  474. Layui.prototype.event = Layui.event = function (modName, events, params, fn) {
  475. var that = this;
  476. var result = null;
  477. var filter = events.match(/\((.*)\)$/) || []; // 提取事件过滤器字符结构,如:select(xxx)
  478. var eventName = (modName + '.' + events).replace(filter[0], ''); // 获取事件名称,如:form.select
  479. var filterName = filter[1] || ''; // 获取过滤器名称,,如:xxx
  480. var callback = function (_, item) {
  481. var res = item && item.call(that, params);
  482. res === false && result === null && (result = false);
  483. };
  484. // 添加事件
  485. if (fn) {
  486. config.event[eventName] = config.event[eventName] || {};
  487. // 这里不再对多次事件监听做支持,避免更多麻烦
  488. // config.event[eventName][filterName] ? config.event[eventName][filterName].push(fn) :
  489. config.event[eventName][filterName] = [fn];
  490. return this;
  491. }
  492. // 执行事件回调
  493. layui.each(config.event[eventName], function (key, item) {
  494. // 执行当前模块的全部事件
  495. if (filterName === '{*}') {
  496. layui.each(item, callback);
  497. return;
  498. }
  499. // 执行指定事件
  500. key === '' && layui.each(item, callback);
  501. filterName && key === filterName && layui.each(item, callback);
  502. });
  503. return result;
  504. };
  505. // 加载自定义事件
  506. NodeList.prototype.forEach = Array.prototype.forEach;
  507. window.customEvent = {
  508. rootScope: {
  509. name: 'document',
  510. handleEvent: {},
  511. events: {},
  512. children: [],
  513. },
  514. /**
  515. * @description: 用于处理模板解析
  516. * @param {String} template 传递已经被latpl编译的模板
  517. * @param {String} locationId id
  518. * @return {String} 返回已经被latpl编译的模板
  519. */
  520. parse: function (template, locationId) {
  521. var $ = layui.$;
  522. // 查找父级组件
  523. var that = this;
  524. var currentScope = null;
  525. if (!locationId) {
  526. // 根节点初始化Scope
  527. currentScope = that.rootScope;
  528. } else {
  529. var parentId = $("*[edoc='" + locationId + "']")
  530. .parents('*[edoc]')
  531. .first()
  532. .attr('edoc');
  533. var parentScope = that.findScope(parentId, that.rootScope) || that.rootScope;
  534. var currentScope = that.findScope(locationId, that.rootScope);
  535. if (!currentScope) {
  536. currentScope = {
  537. name: locationId,
  538. handleEvent: {},
  539. events: {},
  540. };
  541. parentScope.children
  542. ? parentScope.children.push(currentScope)
  543. : (parentScope.children = [currentScope]);
  544. }
  545. }
  546. // 匹配模板内部存在 @ 绑定事件的DOM
  547. return template.replace(/<[^<]*(@\w*=\w*(\(\S*\))?\s*)+[^>]*>/gi, function (domStr) {
  548. var randomFlag = 'data-ident=' + Math.floor(Math.random() * 1000);
  549. while (currentScope.handleEvent[randomFlag]) {
  550. randomFlag = 'data-ident=' + Math.floor(Math.random() * 1000);
  551. }
  552. // 初始化handleEvent
  553. currentScope.handleEvent[randomFlag] = {};
  554. // 替换生成唯一标记 然后匹配DOM上的绑定事件
  555. // (\((?<=\()[^\)]+\))?
  556. return domStr
  557. .replace('>', ' ' + randomFlag + ' >')
  558. .replace(/@\w*=\w*(\([^\)]+\))?\s*/gi, function (subStr) {
  559. // 生成绑定事件的格式
  560. var event = subStr.replace('@', '').trim().split('=');
  561. currentScope.handleEvent[randomFlag][event[0]] = event[1];
  562. return '';
  563. });
  564. });
  565. },
  566. /**
  567. * @description: 用于绑定监听事件,并触发相关的回调
  568. * @param {Object} data data
  569. * @param {String} locationId id
  570. * @return {Object} null
  571. */
  572. bindEvent: function (data, locationId) {
  573. var $ = layui.$;
  574. var currentScope = this.findScope(locationId, this.rootScope) || this.rootScope;
  575. var filter = $(locationId ? '*[edoc="' + currentScope.name + '"]' : 'body');
  576. try {
  577. Object.keys(currentScope.handleEvent).forEach(function (event) {
  578. if (currentScope.handleEvent[event].bindStatus) {return null;}
  579. var _EventDOM = filter.find('*[' + event + ']');
  580. var _Events = currentScope.handleEvent[event.trim()];
  581. Object.keys(_Events).forEach(function (_EventName) {
  582. var _FnTemp = _Events[_EventName].split('(');
  583. var _FnName = _FnTemp[0];
  584. var _FnArgs = _FnTemp[1] ? _FnTemp[1].replace(')', '') : data;
  585. try {
  586. if (typeof _FnArgs === 'string' && isNaN(_FnArgs)) {
  587. _FnArgs = JSON.parse(_FnArgs);
  588. }
  589. } catch (e) {
  590. console.error(_FnArgs, '参数无法转化为对象', e);
  591. }
  592. _EventDOM.on(_EventName, function (event) {
  593. currentScope.events[_FnName](_FnArgs, event.currentTarget);
  594. });
  595. Object.defineProperty(currentScope.handleEvent[event], 'bindStatus', {
  596. value: true,
  597. });
  598. });
  599. });
  600. } catch (error) {
  601. console.error(error);
  602. }
  603. },
  604. /**
  605. * @description: 用于提供回调以挂载给绑定事件调用
  606. * @param {String} name 回调函数名称
  607. * @param {Function} callback 回调函数
  608. * @return {Object} null
  609. */
  610. toggleEvent: function (name, callback) {
  611. var customEvent = window.customEvent;
  612. var currentScope =
  613. this.constructor.name === 'Object'
  614. ? customEvent.rootScope
  615. : customEvent.findScope(this.id, customEvent.rootScope);
  616. if (currentScope && currentScope.events) {currentScope.events[name] = callback;}
  617. },
  618. //
  619. refresh:function(locationId){
  620. var that=this
  621. try {
  622. var node=that.findScope(locationId,that.rootScope)
  623. if(node){
  624. node.handleEvent={},
  625. node.events={}
  626. }
  627. } catch (error) {
  628. console.log(error);
  629. }
  630. },
  631. // 依赖方法
  632. /**
  633. * @description:数组去重
  634. * @param {Array} arr 重复数组
  635. * @return {Array} 去重数组
  636. */
  637. unique: function (arr) {
  638. var obj = {};
  639. var newArr = [];
  640. for (var i = 0; i < arr.length; i++) {
  641. if (obj[arr[i]] === undefined) {
  642. newArr.push(arr[i]);
  643. obj[arr[i]] = 1;
  644. }
  645. }
  646. return newArr;
  647. },
  648. /**
  649. * @description: 查找作用域(不查根节点)
  650. * @param {String} scopeName 作用域名词
  651. * @param {Object} scopeTree 作用域树
  652. * @return {Object} scope
  653. */
  654. findScope: function (scopeName, scopeTree) {
  655. try {
  656. var childScopes = scopeTree.children;
  657. for (var index = 0; index < childScopes.length; index++) {
  658. var scope = childScopes[index];
  659. if (scope.name === scopeName) {
  660. return scope;
  661. }
  662. if (scope.children) {
  663. var result = window.customEvent.findScope(scopeName, scope);
  664. if (result) {
  665. return result;
  666. }
  667. }
  668. }
  669. return null;
  670. } catch (error) {
  671. console.log(error);
  672. }
  673. },
  674. };
  675. win.layui = new Layui();
  676. })(window);