25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

374 lines
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 = get_doctype_label(this.opts.new_doctype);
  62. }
  63. }
  64. if(!this.opts.no_result_message) {
  65. this.opts.no_result_message = 'Nothing to show'
  66. }
  67. },
  68. make: function(opts) {
  69. if(opts) {
  70. this.opts = opts;
  71. }
  72. this.prepare_opts();
  73. $.extend(this, this.opts);
  74. $(this.parent).html(repl('\
  75. <div class="wnlist">\
  76. <h3 class="title hide">%(title)s</h3>\
  77. \
  78. <div class="list-filters" style="display: none;">\
  79. <div class="show_filters well">\
  80. <div class="filter_area"></div>\
  81. <div>\
  82. <button class="btn btn-small btn-info search-btn">\
  83. <i class="icon-refresh icon-white"></i> Search</button>\
  84. <button class="btn btn-small add-filter-btn">\
  85. <i class="icon-plus"></i> Add Filter</button>\
  86. </div>\
  87. </div>\
  88. </div>\
  89. \
  90. <div style="margin-bottom:9px" class="list-toolbar-wrapper">\
  91. <div class="list-toolbar" style="display:inline-block; margin-right: 10px;">\
  92. </div>\
  93. <div style="display:inline-block; width: 24px; margin-left: 4px">\
  94. <img src="lib/images/ui/button-load.gif" \
  95. class="img-load"/></div>\
  96. </div><div style="clear:both"></div>\
  97. \
  98. <div class="no-result help hide">\
  99. %(no_result_message)s\
  100. </div>\
  101. \
  102. <div class="result">\
  103. <div class="result-list"></div>\
  104. </div>\
  105. \
  106. <div class="paging-button">\
  107. <button class="btn btn-small btn-more hide">More...</div>\
  108. </div>\
  109. </div>\
  110. ', this.opts));
  111. this.$w = $(this.parent).find('.wnlist');
  112. this.set_events();
  113. if(this.appframe) {
  114. this.$w.find('.list-toolbar-wrapper').toggle(false);
  115. }
  116. if(this.show_filters) {
  117. this.make_filters();
  118. }
  119. },
  120. add_button: function(label, click, icon) {
  121. if(this.appframe) {
  122. return this.appframe.add_button(label, click, icon)
  123. } else {
  124. $button = $('<button class="btn btn-small"></button>')
  125. .appendTo(this.$w.find('.list-toolbar'))
  126. if(icon) {
  127. $('<i>').addClass(icon).appendTo($button);
  128. }
  129. $button.html(label).click(click);
  130. return $button
  131. }
  132. },
  133. show_view: function($btn, $div, $btn_unsel, $div_unsel) {
  134. $btn_unsel.removeClass('btn-info');
  135. $btn_unsel.find('i').removeClass('icon-white');
  136. $div_unsel.toggle(false);
  137. $btn.addClass('btn-info');
  138. $btn.find('i').addClass('icon-white');
  139. $div.toggle(true);
  140. },
  141. set_events: function() {
  142. var me = this;
  143. // next page
  144. this.$w.find('.btn-more').click(function() {
  145. me.run({append: true });
  146. });
  147. // title
  148. if(this.title) {
  149. this.$w.find('h3').html(this.title).toggle(true);
  150. }
  151. // hide-refresh
  152. if(!(this.hide_refresh || this.no_refresh)) {
  153. this.add_button('Refresh', function() {
  154. me.run();
  155. }, 'icon-refresh');
  156. }
  157. // new
  158. if(this.new_doctype) {
  159. this.add_button('New ' + this.new_doctype, function() {
  160. (me.custom_new_doc || me.make_new_doc)(me.new_doctype);
  161. }, 'icon-plus');
  162. }
  163. // hide-filter
  164. if(me.show_filters) {
  165. this.add_button('Show Filters', function() {
  166. me.filter_list.show_filters();
  167. }, 'icon-search').addClass('btn-filter');
  168. }
  169. if(me.no_toolbar || me.hide_toolbar) {
  170. me.$w.find('.list-toolbar-wrapper').toggle(false);
  171. }
  172. },
  173. make_new_doc: function(new_doctype) {
  174. new_doc(new_doctype);
  175. },
  176. make_filters: function() {
  177. this.filter_list = new wn.ui.FilterList({
  178. listobj: this,
  179. $parent: this.$w.find('.list-filters').toggle(true),
  180. doctype: this.doctype,
  181. filter_fields: this.filter_fields
  182. });
  183. },
  184. clear: function() {
  185. this.data = [];
  186. this.$w.find('.result-list').empty();
  187. this.$w.find('.result').toggle(true);
  188. this.$w.find('.no-result').toggle(false);
  189. this.start = 0;
  190. },
  191. run: function() {
  192. // in old - arguments: 0 = callback, 1 = append
  193. var me = this;
  194. var a0 = arguments[0]; var a1 = arguments[1];
  195. if(a0 && typeof a0=='function')
  196. this.onrun = a0;
  197. if(a0 && a0.callback)
  198. this.onrun = a0.callback;
  199. if(!a1 && !(a0 && a0.append))
  200. this.start = 0;
  201. if(!me.opts.no_loading)
  202. me.set_working(true);
  203. wn.call({
  204. method: this.opts.method || 'webnotes.widgets.query_builder.runquery',
  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. });