您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

374 行
9.7 KiB

  1. // Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
  2. //
  3. // MIT License (MIT)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a
  6. // copy of this software and associated documentation files (the "Software"),
  7. // to deal in the Software without restriction, including without limitation
  8. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. // and/or sell copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  16. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  17. // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  18. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  19. // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  20. // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. //
  22. // new re-factored Listing object
  23. // uses FieldGroup for rendering filters
  24. // removed rarely used functionality
  25. //
  26. // opts:
  27. // parent
  28. // method (method to call on server)
  29. // args (additional args to method)
  30. // get_args (method to return args as dict)
  31. // show_filters [false]
  32. // doctype
  33. // filter_fields (if given, this list is rendered, else built from doctype)
  34. // query or get_query (will be deprecated)
  35. // query_max
  36. // buttons_in_frame
  37. // no_result_message ("No result")
  38. // page_length (20)
  39. // hide_refresh (False)
  40. // no_toolbar
  41. // new_doctype
  42. // [function] render_row(parent, data)
  43. // [function] onrun
  44. // no_loading (no ajax indicator)
  45. wn.provide('wn.ui');
  46. wn.ui.Listing = Class.extend({
  47. init: function(opts) {
  48. this.opts = opts || {};
  49. this.page_length = 20;
  50. this.start = 0;
  51. this.data = [];
  52. if(opts) {
  53. this.make();
  54. }
  55. },
  56. prepare_opts: function() {
  57. if(this.opts.new_doctype) {
  58. if(wn.boot.profile.can_create.indexOf(this.opts.new_doctype)==-1) {
  59. this.opts.new_doctype = null;
  60. } else {
  61. this.opts.new_doctype = this.opts.new_doctype;
  62. }
  63. }
  64. if(!this.opts.no_result_message) {
  65. this.opts.no_result_message = wn._('Nothing to show');
  66. }
  67. this.opts._more = wn._("More");
  68. },
  69. make: function(opts) {
  70. if(opts) {
  71. this.opts = opts;
  72. }
  73. this.prepare_opts();
  74. $.extend(this, this.opts);
  75. $(this.parent).html(repl('\
  76. <div class="wnlist">\
  77. <h3 class="title hide">%(title)s</h3>\
  78. \
  79. <div class="list-filters" style="display: none;">\
  80. <div class="show_filters well">\
  81. <div class="filter_area"></div>\
  82. <div>\
  83. <button class="btn btn-info search-btn">\
  84. <i class="icon-refresh icon-white"></i> Search</button>\
  85. <button class="btn add-filter-btn">\
  86. <i class="icon-plus"></i> Add Filter</button>\
  87. </div>\
  88. </div>\
  89. </div>\
  90. \
  91. <div style="margin-bottom:9px" class="list-toolbar-wrapper">\
  92. <div class="list-toolbar btn-group" style="display:inline-block; margin-right: 10px;">\
  93. </div>\
  94. <div style="display:inline-block; width: 24px; margin-left: 4px">\
  95. <img src="lib/images/ui/button-load.gif" \
  96. class="img-load"/></div>\
  97. </div><div style="clear:both"></div>\
  98. \
  99. <div class="no-result" style="display: none;">\
  100. %(no_result_message)s\
  101. </div>\
  102. \
  103. <div class="result">\
  104. <div class="result-list"></div>\
  105. </div>\
  106. \
  107. <div class="paging-button">\
  108. <button class="btn btn-more hide">%(_more)s...</div>\
  109. </div>\
  110. </div>\
  111. ', this.opts));
  112. this.$w = $(this.parent).find('.wnlist');
  113. this.set_events();
  114. if(this.appframe) {
  115. this.$w.find('.list-toolbar-wrapper').toggle(false);
  116. }
  117. if(this.show_filters) {
  118. this.make_filters();
  119. }
  120. },
  121. add_button: function(label, click, icon) {
  122. if(this.appframe) {
  123. return this.appframe.add_button(label, click, icon)
  124. } else {
  125. $button = $('<button class="btn"></button>')
  126. .appendTo(this.$w.find('.list-toolbar'))
  127. .html((icon ? ("<i class='"+icon+"'></i> ") : "") + label)
  128. .click(click);
  129. return $button
  130. }
  131. },
  132. show_view: function($btn, $div, $btn_unsel, $div_unsel) {
  133. $btn_unsel.removeClass('btn-info');
  134. $btn_unsel.find('i').removeClass('icon-white');
  135. $div_unsel.toggle(false);
  136. $btn.addClass('btn-info');
  137. $btn.find('i').addClass('icon-white');
  138. $div.toggle(true);
  139. },
  140. set_events: function() {
  141. var me = this;
  142. // next page
  143. this.$w.find('.btn-more').click(function() {
  144. me.run({append: true });
  145. });
  146. // title
  147. if(this.title) {
  148. this.$w.find('h3').html(this.title).toggle(true);
  149. }
  150. // hide-refresh
  151. if(!(this.hide_refresh || this.no_refresh)) {
  152. this.add_button('', function() {
  153. me.run();
  154. }, 'icon-refresh');
  155. }
  156. // new
  157. if(this.new_doctype) {
  158. this.add_button(wn._('New') + ' ' + wn._(this.new_doctype), function() {
  159. (me.custom_new_doc || me.make_new_doc)(me.new_doctype);
  160. }, 'icon-plus');
  161. }
  162. // hide-filter
  163. if(me.show_filters) {
  164. this.add_button(wn._('Show Filters'), function() {
  165. me.filter_list.show_filters();
  166. }, 'icon-search').addClass('btn-filter');
  167. }
  168. if(me.no_toolbar || me.hide_toolbar) {
  169. me.$w.find('.list-toolbar-wrapper').toggle(false);
  170. }
  171. },
  172. make_new_doc: function(new_doctype) {
  173. new_doc(new_doctype);
  174. },
  175. make_filters: function() {
  176. this.filter_list = new wn.ui.FilterList({
  177. listobj: this,
  178. $parent: this.$w.find('.list-filters').toggle(true),
  179. doctype: this.doctype,
  180. filter_fields: this.filter_fields
  181. });
  182. },
  183. clear: function() {
  184. this.data = [];
  185. this.$w.find('.result-list').empty();
  186. this.$w.find('.result').toggle(true);
  187. this.$w.find('.no-result').toggle(false);
  188. this.start = 0;
  189. },
  190. run: function() {
  191. // in old - arguments: 0 = callback, 1 = append
  192. var me = this;
  193. var a0 = arguments[0]; var a1 = arguments[1];
  194. if(a0 && typeof a0=='function')
  195. this.onrun = a0;
  196. if(a0 && a0.callback)
  197. this.onrun = a0.callback;
  198. if(!a1 && !(a0 && a0.append))
  199. this.start = 0;
  200. if(!me.opts.no_loading)
  201. me.set_working(true);
  202. wn.call({
  203. method: this.opts.method || 'webnotes.widgets.query_builder.runquery',
  204. type: "GET",
  205. args: this.get_call_args(a0),
  206. callback: function(r) {
  207. if(!me.opts.no_loading)me.set_working(false);
  208. me.render_results(r)
  209. },
  210. no_spinner: this.opts.no_loading
  211. });
  212. },
  213. set_working: function(flag) {
  214. this.$w.find('.img-load').toggle(flag);
  215. },
  216. get_call_args: function(opts) {
  217. // load query
  218. if(!this.method) {
  219. var query = this.get_query ? this.get_query() : this.query;
  220. query = this.add_limits(query);
  221. var args={
  222. query_max: this.query_max,
  223. as_dict: 1
  224. }
  225. args.simple_query = query;
  226. } else {
  227. var args = {
  228. limit_start: this.start,
  229. limit_page_length: this.page_length
  230. }
  231. }
  232. // append user-defined arguments
  233. if(this.args)
  234. $.extend(args, this.args)
  235. if(this.get_args) {
  236. $.extend(args, this.get_args(opts));
  237. }
  238. return args;
  239. },
  240. render_results: function(r) {
  241. if(this.start==0) this.clear();
  242. this.$w.find('.btn-more').toggle(false);
  243. if(r.message) {
  244. r.values = this.get_values_from_response(r.message);
  245. }
  246. if(r.values && r.values.length) {
  247. this.data = this.data.concat(r.values);
  248. this.render_list(r.values);
  249. this.update_paging(r.values);
  250. } else {
  251. if(this.start==0) {
  252. this.$w.find('.result').toggle(false);
  253. var msg = this.get_no_result_message
  254. ? this.get_no_result_message()
  255. : (this.no_result_message
  256. ? this.no_result_message
  257. : wn._("Nothing to show"));
  258. this.$w.find('.no-result')
  259. .html(msg)
  260. .toggle(true);
  261. }
  262. }
  263. // callbacks
  264. if(this.onrun) this.onrun();
  265. if(this.callback) this.callback(r);
  266. },
  267. get_values_from_response: function(data) {
  268. // make dictionaries from keys and values
  269. if(data.keys) {
  270. var values = [];
  271. $.each(data.values, function(row_idx, row) {
  272. var new_row = {};
  273. $.each(data.keys, function(key_idx, key) {
  274. new_row[key] = row[key_idx];
  275. })
  276. values.push(new_row);
  277. });
  278. return values;
  279. } else {
  280. return data;
  281. }
  282. },
  283. render_list: function(values) {
  284. var m = Math.min(values.length, this.page_length);
  285. this.data = values;
  286. if(this.filter_list)
  287. this.filter_values = this.filter_list.get_filters();
  288. // render the rows
  289. for(var i=0; i < m; i++) {
  290. this.render_row(this.add_row(), values[i], this, i);
  291. }
  292. },
  293. update_paging: function(values) {
  294. if(values.length >= this.page_length) {
  295. this.$w.find('.btn-more').toggle(true);
  296. this.start += this.page_length;
  297. }
  298. },
  299. add_row: function() {
  300. return $('<div class="list-row">').appendTo(this.$w.find('.result-list')).get(0);
  301. },
  302. refresh: function() {
  303. this.run();
  304. },
  305. add_limits: function(query) {
  306. query += ' LIMIT ' + this.start + ',' + (this.page_length+1);
  307. return query
  308. },
  309. set_filter: function(fieldname, label) {
  310. var filter = this.filter_list.get_filter(fieldname);
  311. //this.filter_list.show_filters(true);
  312. if(filter) {
  313. var v = filter.field.get_value();
  314. if(v.indexOf(label)!=-1) {
  315. // already set
  316. return false;
  317. } else {
  318. // second filter set for this field
  319. if(fieldname=='_user_tags') {
  320. // and for tags
  321. this.filter_list.add_filter(this.doctype, fieldname,
  322. 'like', '%' + label);
  323. } else {
  324. // or for rest using "in"
  325. filter.set_values(this.doctype, fieldname, 'in', v + ', ' + label);
  326. }
  327. }
  328. } else {
  329. // no filter for this item,
  330. // setup one
  331. if(fieldname=='_user_tags') {
  332. this.filter_list.add_filter(this.doctype, fieldname,
  333. 'like', '%' + label);
  334. } else {
  335. this.filter_list.add_filter(this.doctype, fieldname, '=', label);
  336. }
  337. }
  338. }
  339. });