resource-selector.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. (function (w) {
  2. let NONE = '';
  3. function ResourceSelector(options) {
  4. let Dcat = w.Dcat;
  5. options = $.extend({
  6. title: '选择', // 弹窗标题
  7. selector: '', // 选择按钮选择器
  8. column: '', // 字段名称
  9. source: '', // 资源地址
  10. maxItem: 1, // 最大选项数量,0为不限制
  11. area: ['80%', '90%'],
  12. queryName: '_resource_',
  13. items: {}, // 默认选中项,key => value 键值对
  14. placeholder: '', // input placeholder
  15. showCloseButton: false,
  16. lang: {
  17. close: Dcat.lang.close || '关闭',
  18. exceed_max_item: Dcat.lang.exceed_max_item || '已超出最大可选择的数量',
  19. selected_options: Dcat.lang.selected_options || '已选中:num个选项',
  20. },
  21. displayerContainer: null, // 选项展示容器dom对象
  22. hiddenInput: null, // 隐藏表单dom对象
  23. displayer: null, // 自定义选中项渲染方法
  24. disabled: false,
  25. clearAllClass: '',
  26. clearOneClass: '',
  27. window: null,
  28. }, options);
  29. options.window = options.window || (top || w);
  30. let self = ResourceSelector,
  31. column = options.column,
  32. cls = column.replace(/[\[\]]*/g, '') + (Math.random().toString(36).substr(2)),
  33. layer = options.window.layer,
  34. $input = getQueryDomObject(options.displayerContainer) || $(options.selector).parents('.select-resource').find('div[name="' + column + '"]'),
  35. $hidden = getQueryDomObject(options.hiddenInput) || $input.parents('.input-group').find('input[type="hidden"]'),
  36. tagClearClass = options.clearOneClass || (cls + '-tag-clear-button'),
  37. clearClass = options.clearAllClass || (cls + '-clear-button'),
  38. maxItem = options.maxItem,
  39. originalItems = options.items,
  40. iframeWin,
  41. layerIdx,
  42. $layerWin;
  43. options.clearOneClass = tagClearClass;
  44. options.clearAllClass = clearClass;
  45. $(options.selector).click(function () {
  46. if (options.disabled) return;
  47. if (layerIdx) {
  48. $layerWin.show();
  49. clickCheckedItems();
  50. return;
  51. }
  52. $(document).one('pjax:complete', function () {// 跳转新页面时移除弹窗
  53. layer.close(layerIdx);
  54. $layerWin.remove();
  55. layerIdx = $layerWin = null;
  56. });
  57. if (options.source.indexOf('?') === -1) {
  58. options.source += '?'
  59. }
  60. layerIdx = layer.open({
  61. type: 2,
  62. title: options.title,
  63. shadeClose: true,
  64. maxmin: false,
  65. resize: false,
  66. shade: false,
  67. scrollbar: false,
  68. skin: 'select-resource',
  69. area: formatArea(options.area),
  70. content: `${options.source}${options.queryName}=1`,
  71. btn: options.showCloseButton ? [options.closeButtonText] : null,
  72. success: function (layero) {
  73. iframeWin = options.window[layero.find('iframe')[0]['name']];
  74. // 解决页面无法滚动问题
  75. $('html').css({'overflow': 'auto'});
  76. // 绑定勾选默认选项事件
  77. bindCheckedDefaultEvent(iframeWin);
  78. },
  79. yes: function () {
  80. $layerWin.hide();
  81. return false;
  82. },
  83. cancel: function () {
  84. $layerWin.hide();
  85. return false;
  86. }
  87. });
  88. $layerWin = options.window.$('#layui-layer' + layerIdx);
  89. });
  90. function getQueryDomObject(value) {
  91. if (! value) {
  92. return;
  93. }
  94. return typeof value === 'object' ? value : $(value);
  95. }
  96. /**
  97. * 多选
  98. */
  99. function multipleSelect($this) {
  100. let id = $this.data('id'),
  101. label = $this.data('label') || id,
  102. exist = Dcat.helpers.isset(originalItems, id);
  103. if ($this.prop('checked')) {
  104. if (!exist) {
  105. originalItems[id] = label;
  106. }
  107. } else if (exist) {
  108. delete originalItems[id];
  109. }
  110. if (maxItem > 0 && Dcat.helpers.len(originalItems) > maxItem) {
  111. unchecked($this);
  112. delete originalItems[id];
  113. // 多选项判断最大长度
  114. return Dcat.warning(options.lang.exceed_max_item);
  115. }
  116. renderTags(originalItems);
  117. }
  118. // 单选
  119. function select($this) {
  120. let id = $this.data('id'),
  121. label = $this.data('label') || id;
  122. getAllCheckboxes().each(function () {
  123. if ($(this).data('id') != id) {
  124. unchecked($(this));
  125. }
  126. });
  127. originalItems = {};
  128. if ($this.prop('checked')) {
  129. originalItems[id] = label;
  130. }
  131. renderTags(originalItems);
  132. }
  133. /**
  134. * 显示选项内容
  135. *
  136. * @param items
  137. */
  138. function renderTags(items) {
  139. let ids = [];
  140. for (let id in items) {
  141. ids.push(id);
  142. }
  143. // 显示勾选的选项内容
  144. displayInputDiv(items);
  145. setSelectedId(ids);
  146. // 绑定清除事件
  147. $('.' + clearClass).click(clearAllTags);
  148. $('.' + tagClearClass).click(clearTag);
  149. }
  150. function setSelectedId(ids) {
  151. $hidden.val(ids.length ? ids.join(',') : NONE);
  152. }
  153. /**
  154. * 显示勾选的选项内容
  155. */
  156. function displayInputDiv(tag) {
  157. if (options.displayer) {
  158. if (typeof options.displayer == 'string' && Dcat.helpers.isset(self.displayers, options.displayer)) {
  159. return self.displayers[options.displayer](tag, $input, options);
  160. }
  161. // 自定义选中内容渲染
  162. return options.displayer(tag, $input, options);
  163. }
  164. return self.displayers.default(tag, $input, options);
  165. }
  166. function bindCheckedDefaultEvent(iframeWin) {
  167. Dcat.ready(function () {
  168. let $selectAll = $(layer.getChildFrame('.checkbox-grid .select-all', layerIdx));
  169. clickCheckedItems();
  170. if (maxItem != 1) {
  171. // 解决多选模式全选框无效问题
  172. $selectAll.on('change', function () {
  173. getAllCheckboxes().trigger('change');
  174. });
  175. }
  176. getAllCheckboxes().on('change', function () {
  177. if (maxItem == 1) {
  178. select($(this));
  179. } else {
  180. multipleSelect($(this));
  181. }
  182. });
  183. if (maxItem == 1) {
  184. // 单选模式禁用全选按钮
  185. $selectAll.on('click', function () {
  186. return false;
  187. });
  188. }
  189. }, iframeWin);
  190. }
  191. function unchecked($ckb) {
  192. $ckb.parents('tr').css('background-color', '');
  193. $ckb.prop('checked', false);
  194. }
  195. // 勾选默认选项
  196. function clickCheckedItems() {
  197. setTimeout(function () {
  198. let ckb = layer.getChildFrame('tbody .checkbox-grid input[type="checkbox"]:checked', layerIdx);
  199. unchecked(ckb);
  200. for (let id in originalItems) {
  201. layer.getChildFrame('.checkbox-grid input[data-id="'+id+'"]', layerIdx).click();
  202. }
  203. }, 10);
  204. }
  205. function getAllCheckboxes() {
  206. return $(layer.getChildFrame('.checkbox-grid input[type="checkbox"]:not(.select-all)', layerIdx));
  207. }
  208. /**
  209. * 清除所有选项
  210. */
  211. function clearTag() {
  212. delete originalItems[$(this).data('id')];
  213. renderTags(originalItems);
  214. }
  215. /**
  216. * 清除所有选项
  217. */
  218. function clearAllTags() {
  219. originalItems = {};
  220. renderTags(originalItems);
  221. }
  222. function formatArea(area) {
  223. if (w.screen.width <= 750) {
  224. return ['100%', '100%'];
  225. }
  226. return area;
  227. }
  228. renderTags(originalItems);
  229. }
  230. ResourceSelector.displayers = {
  231. default: function (tag, $input, opts) {
  232. let place = '<span class="default-text" style="opacity:0.75">' + (opts.placeholder || $input.attr('placeholder')) + '</span>',
  233. maxItem = opts.maxItem;
  234. function init() {
  235. if (! Dcat.helpers.len(tag)) {
  236. return $input.html(place);
  237. }
  238. if (maxItem == 1) {
  239. return $input.html(buildOne(tag[Object.keys(tag)[0]]));
  240. }
  241. $input.html(buildMany(tag));
  242. }
  243. function buildMany(tag) {
  244. let html = [];
  245. for (let i in tag) {
  246. if (maxItem > 2 || !maxItem) {
  247. let strVar = "";
  248. strVar += "<li class=\"select2-selection__choice\" >";
  249. strVar += tag[i] + " <span data-id=\"" + i + "\" class=\"select2-selection__choice__remove ";
  250. strVar += opts.clearOneClass +"\" role=\"presentation\"> ×</span>";
  251. strVar += "</li>";
  252. html.push(strVar);
  253. } else {
  254. html.push(
  255. "<a class='label label-primary'>" + tag[i] + " " +
  256. "<span data-id=" + i + " class='" + opts.clearOneClass +
  257. "' style='font-weight:bold;cursor:pointer;font-size:14px'>×</span></a>"
  258. )
  259. }
  260. }
  261. if (!(maxItem > 2 || !maxItem)) {
  262. return buildOne(html.join('&nbsp;'));
  263. }
  264. html.unshift('<span class="select2-selection__clear '+opts.clearAllClass+'">×</span>');
  265. html = '<ul class="select2-selection__rendered">' + html.join('') + '</ul>';
  266. return html;
  267. }
  268. /**
  269. * 单个选项样式
  270. *
  271. * @param tag
  272. * @returns {string}
  273. */
  274. function buildOne(tag) {
  275. let clearButton = "<div class='pull-right "+opts.clearAllClass+"' style='font-weight:bold;cursor:pointer'>×</div>";
  276. return ""+tag+""+clearButton;
  277. }
  278. init();
  279. },
  280. // list模式
  281. navList: function (tag, $input, opts) {
  282. let place = '<span style="opacity:0.75">' + (opts.placeholder || $input.attr('placeholder')) + '</span>',
  283. maxItem = opts.maxItem;
  284. function init() {
  285. let $app = $(opts.selector).parents('.select-resource').find('app');
  286. $app.html('');
  287. if (! Dcat.helpers.len(tag)) {
  288. return $input.html(place);
  289. }
  290. if (maxItem == 1) {
  291. return $input.html(buildOne(tag[Object.keys(tag)[0]]));
  292. }
  293. $input.html(buildOne(opts.lang.selected_options.replace(':num', Dcat.helpers.len(tag))));
  294. $app.html(buildMany(tag));
  295. }
  296. function buildMany(tag) {
  297. let html = [];
  298. for (let i in tag) {
  299. let strVar = "";
  300. strVar += "<li>";
  301. strVar += "<a class='pull-left'>" + tag[i] + "</a><a data-id='" + i + "' class='pull-right red text-danger ";
  302. strVar += opts.clearOneClass +"' ><i class='fa fa-close'></i></a>";
  303. strVar += "<span class='clearfix'></span></li>";
  304. html.push(strVar);
  305. }
  306. html = '<ul class="nav nav-pills nav-stacked" >' + html.join('') + '</ul>';
  307. return html;
  308. }
  309. function buildOne(tag) {
  310. let clearButton = "<div class='pull-right "+opts.clearAllClass+"' style='font-weight:bold;cursor:pointer'>×</div>";
  311. return tag + clearButton;
  312. }
  313. init();
  314. }
  315. };
  316. Dcat.ResourceSelector = ResourceSelector;
  317. })(window);