view.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /**
  2. @Name:layuiAdmin 视图模块
  3. @Author:贤心
  4. @Site:http://www.layui.com/admin/
  5. @License:LPPL
  6. */
  7. layui.define(['laytpl', 'layer'], function(exports){
  8. var $ = layui.jquery
  9. ,laytpl = layui.laytpl
  10. ,layer = layui.layer
  11. ,setter = layui.setter
  12. ,device = layui.device()
  13. ,hint = layui.hint()
  14. //对外接口
  15. ,view = function(id){
  16. return new Class(id);
  17. }
  18. ,SHOW = 'layui-show', LAY_BODY = 'LAY_app_body'
  19. //构造器
  20. ,Class = function(id){
  21. this.id = id;
  22. this.container = $('#'+(id || LAY_BODY));
  23. };
  24. //加载中
  25. view.loading = function(elem){
  26. elem.append(
  27. this.elemLoad = $('<i class="layui-anim layui-anim-rotate layui-anim-loop layui-icon layui-icon-loading layadmin-loading"></i>')
  28. );
  29. };
  30. //移除加载
  31. view.removeLoad = function(){
  32. this.elemLoad && this.elemLoad.remove();
  33. };
  34. //清除 token,并跳转到登入页
  35. view.exit = function(callback){
  36. //清空本地记录的 token
  37. layui.data(setter.tableName, {
  38. key: setter.request.tokenName
  39. ,remove: true
  40. });
  41. //跳转到登入页
  42. parent.location.href = '/admin/login/login.html';
  43. callback && callback();
  44. };
  45. //Ajax请求
  46. view.req = function(options){
  47. var that = this
  48. ,success = options.success
  49. ,error = options.error
  50. ,request = setter.request
  51. ,response = setter.response
  52. ,debug = function(){
  53. return setter.debug
  54. ? '<br><cite>URL:</cite>' + options.url
  55. : '';
  56. };
  57. options.data = options.data || {};
  58. options.headers = options.headers || {};
  59. if(request.tokenName){
  60. /*
  61. //自动给参数传入默认 token
  62. options.data[request.tokenName] = request.tokenName in options.data
  63. ? options.data[request.tokenName]
  64. : (layui.data(setter.tableName)[request.tokenName] || '');
  65. */
  66. //自动给 Request Headers 传入 token
  67. options.headers[request.tokenName] = request.tokenName in options.headers
  68. ? options.headers[request.tokenName]
  69. : (layui.data(setter.tableName)[request.tokenName] || '');
  70. }
  71. delete options.success;
  72. delete options.error;
  73. return $.ajax($.extend({
  74. type: 'get'
  75. ,dataType: 'json'
  76. ,success: function(res){
  77. var statusCode = response.statusCode;
  78. //只有 response 的 code 一切正常才执行 done
  79. if(res[response.statusName] == statusCode.ok) {
  80. typeof options.done === 'function' && options.done(res);
  81. }
  82. //登录状态失效,清除本地 access_token,并强制跳转到登入页
  83. else if(res[response.statusName] == statusCode.logout){
  84. view.exit();
  85. }
  86. //其它异常
  87. else {
  88. var error = [
  89. '<cite>Error:</cite> ' + (res[response.msgName] || '返回状态码异常')
  90. ,debug()
  91. ].join('');
  92. view.error(error);
  93. }
  94. //只要 http 状态码正常,无论 response 的 code 是否正常都执行 success
  95. typeof success === 'function' && success(res);
  96. }
  97. ,error: function(e, code){
  98. var error = [
  99. '请求异常,请重试<br><cite>错误信息:</cite>'+ code
  100. ,debug()
  101. ].join('');
  102. view.error(error);
  103. typeof error === 'function' && error(res);
  104. }
  105. }, options));
  106. };
  107. view.download = function(url){
  108. $('<form action="'+url+'" method="GET"></form>').appendTo('body').submit().remove();
  109. };
  110. view.admindownload = function(url){
  111. const xhr = new XMLHttpRequest()
  112. xhr.open('GET', url)
  113. xhr.responseType = 'blob'
  114. xhr.onload = function () {
  115. const disposition = xhr.getResponseHeader("Content-Disposition");
  116. let fileName = '';
  117. if (disposition && disposition.indexOf('attachment') !== -1) {
  118. const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(disposition);
  119. if (matches != null && matches[1]) {
  120. fileName = matches[1].replace(/['"]/g, '').trim();
  121. fileName = decodeURIComponent(fileName); // 解码文件名
  122. }
  123. }
  124. const url = window.URL.createObjectURL(xhr.response);
  125. const link = document.createElement('a');
  126. link.href = url;
  127. link.download = fileName;
  128. document.body.appendChild(link);
  129. link.click();
  130. document.body.removeChild(link); // 清理下载链接对象
  131. window.URL.revokeObjectURL(url);
  132. }
  133. // 传递 token
  134. xhr.setRequestHeader('Authorization', layui.data(setter.tableName)[setter.request.tokenName] || '');
  135. xhr.send();
  136. // $.ajax({
  137. // url: url,
  138. // method: "get",
  139. // beforeSend: function (request) {
  140. // request.setRequestHeader("Authorization", layui.data(setter.tableName)[setter.request.tokenName] || '');
  141. // },
  142. // xhrFields: {
  143. // responseType: 'blob'
  144. // },
  145. // success: function (result, state, xhr) {
  146. // // let fileName = decodeURIComponent(xhr.getResponseHeader('Content-Disposition').split(';')[1].split('=')[1].replace(/\"/g, ''));
  147. // // let type = xhr.getResponseHeader("content-type")
  148. // // const url = window.URL.createObjectURL(result);
  149. // // const link = document.createElement('a');
  150. // // link.href = url;
  151. // // link.setAttribute('download', 'example.docx');
  152. // // document.body.appendChild(link);
  153. // // link.click();
  154. // // document.body.removeChild(link); // 清理下载链接对象
  155. // },
  156. // })
  157. };
  158. //弹窗
  159. view.popup = function(options){
  160. var success = options.success
  161. ,skin = options.skin;
  162. delete options.success;
  163. delete options.skin;
  164. return layer.open($.extend({
  165. type: 1
  166. ,title: '提示'
  167. ,content: ''
  168. ,id: 'LAY-system-view-popup'
  169. ,skin: 'layui-layer-admin' + (skin ? ' ' + skin : '')
  170. ,shadeClose: true
  171. ,closeBtn: false
  172. ,success: function(layero, index){
  173. var elemClose = $('<i class="layui-icon" close>&#x1006;</i>');
  174. layero.append(elemClose);
  175. elemClose.on('click', function(){
  176. layer.close(index);
  177. });
  178. typeof success === 'function' && success.apply(this, arguments);
  179. }
  180. }, options))
  181. };
  182. //异常提示
  183. view.error = function(content, options){
  184. return view.popup($.extend({
  185. content: content
  186. ,maxWidth: 300
  187. //,shade: 0.01
  188. //,offset: 't'
  189. ,anim: 6
  190. ,id: 'LAY_adminError'
  191. }, options))
  192. };
  193. //请求模板文件渲染
  194. Class.prototype.render = function(views, params){
  195. var that = this, router = layui.router();
  196. views = setter.views + views + setter.engine;
  197. $('#'+ LAY_BODY).children('.layadmin-loading').remove();
  198. view.loading(that.container); //loading
  199. //请求模板
  200. $.ajax({
  201. url: views
  202. ,type: 'get'
  203. ,dataType: 'html'
  204. ,data: {
  205. v: layui.cache.version
  206. }
  207. ,success: function(html){
  208. html = '<div>' + html + '</div>';
  209. var elemTitle = $(html).find('title')
  210. ,title = elemTitle.text() || (html.match(/\<title\>([\s\S]*)\<\/title>/)||[])[1];
  211. var res = {
  212. title: title
  213. ,body: html
  214. };
  215. elemTitle.remove();
  216. that.params = params || {}; //获取参数
  217. if(that.then){
  218. that.then(res);
  219. delete that.then;
  220. }
  221. that.parse(html);
  222. view.removeLoad();
  223. if(that.done){
  224. that.done(res);
  225. delete that.done;
  226. }
  227. }
  228. ,error: function(e){
  229. view.removeLoad();
  230. if(that.render.isError){
  231. return view.error('请求视图文件异常,状态:'+ e.status);
  232. };
  233. if(e.status === 404){
  234. that.render('template/tips/404');
  235. } else {
  236. that.render('template/tips/error');
  237. }
  238. that.render.isError = true;
  239. }
  240. });
  241. return that;
  242. };
  243. //解析模板
  244. Class.prototype.parse = function(html, refresh, callback){
  245. var that = this
  246. ,isScriptTpl = typeof html === 'object' //是否模板元素
  247. ,elem = isScriptTpl ? html : $(html)
  248. ,elemTemp = isScriptTpl ? html : elem.find('*[template]')
  249. ,fn = function(options){
  250. var tpl = laytpl(options.dataElem.html());
  251. options.dataElem.after(tpl.render($.extend({
  252. params: router.params
  253. }, options.res)));
  254. typeof callback === 'function' && callback();
  255. try {
  256. options.done && new Function('d', options.done)(options.res);
  257. } catch(e){
  258. console.error(options.dataElem[0], '\n存在错误回调脚本\n\n', e)
  259. }
  260. }
  261. ,router = layui.router();
  262. elem.find('title').remove();
  263. that.container[refresh ? 'after' : 'html'](elem.children());
  264. router.params = that.params || {};
  265. //遍历模板区块
  266. for(var i = elemTemp.length; i > 0; i--){
  267. (function(){
  268. var dataElem = elemTemp.eq(i - 1)
  269. ,layDone = dataElem.attr('lay-done') || dataElem.attr('lay-then') //获取回调
  270. ,url = laytpl(dataElem.attr('lay-url')|| '').render(router) //接口 url
  271. ,data = laytpl(dataElem.attr('lay-data')|| '').render(router) //接口参数
  272. ,headers = laytpl(dataElem.attr('lay-headers')|| '').render(router); //接口请求的头信息
  273. try {
  274. data = new Function('return '+ data + ';')();
  275. } catch(e) {
  276. hint.error('lay-data: ' + e.message);
  277. data = {};
  278. };
  279. try {
  280. headers = new Function('return '+ headers + ';')();
  281. } catch(e) {
  282. hint.error('lay-headers: ' + e.message);
  283. headers = headers || {}
  284. };
  285. if(url){
  286. view.req({
  287. type: dataElem.attr('lay-type') || 'get'
  288. ,url: url
  289. ,data: data
  290. ,dataType: 'json'
  291. ,headers: headers
  292. ,success: function(res){
  293. fn({
  294. dataElem: dataElem
  295. ,res: res
  296. ,done: layDone
  297. });
  298. }
  299. });
  300. } else {
  301. fn({
  302. dataElem: dataElem
  303. ,done: layDone
  304. });
  305. }
  306. }());
  307. }
  308. return that;
  309. };
  310. //自动渲染数据模板
  311. Class.prototype.autoRender = function(id, callback){
  312. var that = this;
  313. $(id || 'body').find('*[template]').each(function(index, item){
  314. var othis = $(this);
  315. that.container = othis;
  316. that.parse(othis, 'refresh');
  317. });
  318. };
  319. //直接渲染字符
  320. Class.prototype.send = function(views, data){
  321. var tpl = laytpl(views || this.container.html()).render(data || {});
  322. this.container.html(tpl);
  323. return this;
  324. };
  325. //局部刷新模板
  326. Class.prototype.refresh = function(callback){
  327. var that = this
  328. ,next = that.container.next()
  329. ,templateid = next.attr('lay-templateid');
  330. if(that.id != templateid) return that;
  331. that.parse(that.container, 'refresh', function(){
  332. that.container.siblings('[lay-templateid="'+ that.id +'"]:last').remove();
  333. typeof callback === 'function' && callback();
  334. });
  335. return that;
  336. };
  337. //视图请求成功后的回调
  338. Class.prototype.then = function(callback){
  339. this.then = callback;
  340. return this;
  341. };
  342. //视图渲染完毕后的回调
  343. Class.prototype.done = function(callback){
  344. this.done = callback;
  345. return this;
  346. };
  347. //Ajax请求setup
  348. view.reqSetup = function(){
  349. var request=setter.request;
  350. if(request.tokenName){
  351. $.ajaxSetup({
  352. headers:{
  353. [request.tokenName]:layui.data(setter.tableName)[request.tokenName] || '',
  354. [request.userId]:layui.data(setter.tableName)[request.userId] || ''
  355. },
  356. dataFilter: function (responseText) {
  357. var res;
  358. try{
  359. res = eval('('+responseText+')');
  360. }catch (e) {
  361. return responseText;
  362. }
  363. if(res && res.code == setter.response.statusCode.logout)
  364. view.exit();
  365. else
  366. return responseText;
  367. }
  368. });
  369. }
  370. };
  371. view.getParameterByName = function (name) {
  372. name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
  373. var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
  374. results = regex.exec(location.search);
  375. return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
  376. }
  377. //对外接口
  378. exports('view', view);
  379. });