require-form.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, Upload, Validator) {
  2. var Form = {
  3. config: {
  4. fieldlisttpl: '<dd class="form-inline"><input type="text" name="<%=name%>[<%=index%>][key]" class="form-control" value="<%=row.key%>" size="10" /> <input type="text" name="<%=name%>[<%=index%>][value]" class="form-control" value="<%=row.value%>" size="40" /> <span class="btn btn-sm btn-danger btn-remove"><i class="fa fa-times"></i></span> <span class="btn btn-sm btn-primary btn-dragsort"><i class="fa fa-arrows"></i></span></dd>'
  5. },
  6. events: {
  7. validator: function (form, success, error, submit) {
  8. if (!form.is("form"))
  9. return;
  10. //绑定表单事件
  11. form.validator($.extend({
  12. validClass: 'has-success',
  13. invalidClass: 'has-error',
  14. bindClassTo: '.form-group',
  15. formClass: 'n-default n-bootstrap',
  16. msgClass: 'n-right',
  17. stopOnError: true,
  18. display: function (elem) {
  19. return $(elem).closest('.form-group').find(".control-label").text().replace(/\:/, '');
  20. },
  21. dataFilter: function (data) {
  22. if (data.code === 1) {
  23. return "";
  24. } else {
  25. return data.msg;
  26. }
  27. },
  28. target: function (input) {
  29. var $formitem = $(input).closest('.form-group'),
  30. $msgbox = $formitem.find('span.msg-box');
  31. if (!$msgbox.length) {
  32. return [];
  33. }
  34. return $msgbox;
  35. },
  36. valid: function (ret) {
  37. var that = this, submitBtn = $(".layer-footer [type=submit]", form);
  38. that.holdSubmit();
  39. $(".layer-footer [type=submit]", form).addClass("disabled");
  40. //验证通过提交表单
  41. Form.api.submit($(ret), function (data, ret) {
  42. that.holdSubmit(false);
  43. submitBtn.removeClass("disabled");
  44. if (false === $(this).triggerHandler("success.form", [data, ret])) {
  45. return false;
  46. }
  47. if (typeof success === 'function') {
  48. if (false === success.call($(this), data, ret)) {
  49. return false;
  50. }
  51. }
  52. //提示及关闭当前窗口
  53. var msg = ret.hasOwnProperty("msg") && ret.msg !== "" ? ret.msg : __('Operation completed');
  54. parent.Toastr.success(msg);
  55. parent.$(".btn-refresh").trigger("click");
  56. var index = parent.Layer.getFrameIndex(window.name);
  57. parent.Layer.close(index);
  58. return false;
  59. }, function (data, ret) {
  60. that.holdSubmit(false);
  61. if (false === $(this).triggerHandler("error.form", [data, ret])) {
  62. return false;
  63. }
  64. submitBtn.removeClass("disabled");
  65. if (typeof error === 'function') {
  66. if (false === error.call($(this), data, ret)) {
  67. return false;
  68. }
  69. }
  70. }, submit);
  71. return false;
  72. }
  73. }, form.data("validator-options") || {}));
  74. //移除提交按钮的disabled类
  75. $(".layer-footer [type=submit],.fixed-footer [type=submit],.normal-footer [type=submit]", form).removeClass("disabled");
  76. },
  77. selectpicker: function (form) {
  78. //绑定select元素事件
  79. if ($(".selectpicker", form).size() > 0) {
  80. require(['bootstrap-select', 'bootstrap-select-lang'], function () {
  81. $('.selectpicker', form).selectpicker();
  82. });
  83. }
  84. },
  85. selectpage: function (form) {
  86. //绑定selectpage元素事件
  87. if ($(".selectpage", form).size() > 0) {
  88. require(['selectpage'], function () {
  89. $('.selectpage', form).selectPage({
  90. eAjaxSuccess: function (data) {
  91. data.list = typeof data.rows !== 'undefined' ? data.rows : (typeof data.list !== 'undefined' ? data.list : []);
  92. data.totalRow = typeof data.total !== 'undefined' ? data.total : (typeof data.totalRow !== 'undefined' ? data.totalRow : data.list.length);
  93. return data;
  94. }
  95. });
  96. });
  97. //给隐藏的元素添加上validate验证触发事件
  98. $(document).on("change", ".sp_hidden", function () {
  99. $(this).trigger("validate");
  100. });
  101. $(document).on("change", ".sp_input", function () {
  102. $(this).closest(".sp_container").find(".sp_hidden").trigger("change");
  103. });
  104. $(form).on("reset", function () {
  105. setTimeout(function () {
  106. $('.selectpage', form).selectPageClear();
  107. }, 1);
  108. });
  109. }
  110. },
  111. cxselect: function (form) {
  112. //绑定cxselect元素事件
  113. if ($("[data-toggle='cxselect']", form).size() > 0) {
  114. require(['cxselect'], function () {
  115. $.cxSelect.defaults.jsonName = 'name';
  116. $.cxSelect.defaults.jsonValue = 'value';
  117. $.cxSelect.defaults.jsonSpace = 'data';
  118. $("[data-toggle='cxselect']", form).cxSelect();
  119. });
  120. }
  121. },
  122. citypicker: function (form) {
  123. //绑定城市远程插件
  124. if ($("[data-toggle='city-picker']", form).size() > 0) {
  125. require(['citypicker'], function () {
  126. });
  127. }
  128. },
  129. datetimepicker: function (form) {
  130. //绑定日期时间元素事件
  131. if ($(".datetimepicker", form).size() > 0) {
  132. require(['bootstrap-datetimepicker'], function () {
  133. var options = {
  134. format: 'YYYY-MM-DD HH:mm:ss',
  135. icons: {
  136. time: 'fa fa-clock-o',
  137. date: 'fa fa-calendar',
  138. up: 'fa fa-chevron-up',
  139. down: 'fa fa-chevron-down',
  140. previous: 'fa fa-chevron-left',
  141. next: 'fa fa-chevron-right',
  142. today: 'fa fa-history',
  143. clear: 'fa fa-trash',
  144. close: 'fa fa-remove'
  145. },
  146. showTodayButton: true,
  147. showClose: true
  148. };
  149. $('.datetimepicker', form).parent().css('position', 'relative');
  150. $('.datetimepicker', form).datetimepicker(options);
  151. });
  152. }
  153. },
  154. daterangepicker: function (form) {
  155. //绑定日期时间元素事件
  156. if ($(".datetimerange", form).size() > 0) {
  157. require(['bootstrap-daterangepicker'], function () {
  158. var ranges = {};
  159. ranges[__('Today')] = [Moment().startOf('day'), Moment().endOf('day')];
  160. ranges[__('Yesterday')] = [Moment().subtract(1, 'days').startOf('day'), Moment().subtract(1, 'days').endOf('day')];
  161. ranges[__('Last 7 Days')] = [Moment().subtract(6, 'days').startOf('day'), Moment().endOf('day')];
  162. ranges[__('Last 30 Days')] = [Moment().subtract(29, 'days').startOf('day'), Moment().endOf('day')];
  163. ranges[__('This Month')] = [Moment().startOf('month'), Moment().endOf('month')];
  164. ranges[__('Last Month')] = [Moment().subtract(1, 'month').startOf('month'), Moment().subtract(1, 'month').endOf('month')];
  165. var options = {
  166. timePicker: false,
  167. autoUpdateInput: false,
  168. timePickerSeconds: true,
  169. timePicker24Hour: true,
  170. autoApply: true,
  171. locale: {
  172. format: 'YYYY-MM-DD HH:mm:ss',
  173. customRangeLabel: __("Custom Range"),
  174. applyLabel: __("Apply"),
  175. cancelLabel: __("Clear"),
  176. },
  177. ranges: ranges,
  178. };
  179. var origincallback = function (start, end) {
  180. $(this.element).val(start.format(options.locale.format) + " - " + end.format(options.locale.format));
  181. $(this.element).trigger('blur');
  182. };
  183. $(".datetimerange", form).each(function () {
  184. var callback = typeof $(this).data('callback') == 'function' ? $(this).data('callback') : origincallback;
  185. $(this).on('apply.daterangepicker', function (ev, picker) {
  186. callback.call(picker, picker.startDate, picker.endDate);
  187. });
  188. $(this).on('cancel.daterangepicker', function (ev, picker) {
  189. $(this).val('').trigger('blur');
  190. });
  191. $(this).daterangepicker($.extend({}, options, $(this).data()), callback);
  192. });
  193. });
  194. }
  195. },
  196. plupload: function (form) {
  197. //绑定plupload上传元素事件
  198. if ($(".plupload", form).size() > 0) {
  199. Upload.api.plupload($(".plupload", form));
  200. }
  201. },
  202. faselect: function (form) {
  203. //绑定fachoose选择附件事件
  204. if ($(".fachoose", form).size() > 0) {
  205. $(".fachoose", form).on('click', function () {
  206. var that = this;
  207. var multiple = $(this).data("multiple") ? $(this).data("multiple") : false;
  208. var mimetype = $(this).data("mimetype") ? $(this).data("mimetype") : '';
  209. parent.Fast.api.open("general/attachment/select?element_id=" + $(this).attr("id") + "&multiple=" + multiple + "&mimetype=" + mimetype, __('Choose'), {
  210. callback: function (data) {
  211. var button = $("#" + $(that).attr("id"));
  212. var maxcount = $(button).data("maxcount");
  213. var input_id = $(button).data("input-id") ? $(button).data("input-id") : "";
  214. maxcount = typeof maxcount !== "undefined" ? maxcount : 0;
  215. if (input_id && data.multiple) {
  216. var urlArr = [];
  217. var inputObj = $("#" + input_id);
  218. var value = $.trim(inputObj.val());
  219. if (value !== "") {
  220. urlArr.push(inputObj.val());
  221. }
  222. urlArr.push(data.url)
  223. var result = urlArr.join(",");
  224. if (maxcount > 0) {
  225. var nums = value === '' ? 0 : value.split(/\,/).length;
  226. var files = data.url !== "" ? data.url.split(/\,/) : [];
  227. var remains = maxcount - nums;
  228. if (files.length > remains) {
  229. Toastr.error(__('You can choose up to %d file%s', remains));
  230. return false;
  231. }
  232. }
  233. inputObj.val(result).trigger("change");
  234. } else {
  235. $("#" + input_id).val(data.url).trigger("change");
  236. }
  237. }
  238. });
  239. return false;
  240. });
  241. }
  242. },
  243. fieldlist: function (form) {
  244. //绑定fieldlist
  245. if ($(".fieldlist", form).size() > 0) {
  246. require(['dragsort', 'template'], function (undefined, Template) {
  247. //刷新隐藏textarea的值
  248. var refresh = function (name) {
  249. var data = {};
  250. var textarea = $("textarea[name='" + name + "']", form);
  251. var container = textarea.closest("dl");
  252. var template = container.data("template");
  253. console.log(name, container);
  254. $.each($("input,select", container).serializeArray(), function (i, j) {
  255. var reg = /\[(\w+)\]\[(\w+)\]$/g;
  256. var match = reg.exec(j.name);
  257. if (!match)
  258. return true;
  259. match[1] = "x" + parseInt(match[1]);
  260. if (typeof data[match[1]] == 'undefined') {
  261. data[match[1]] = {};
  262. }
  263. data[match[1]][match[2]] = j.value;
  264. });
  265. var result = template ? [] : {};
  266. $.each(data, function (i, j) {
  267. if (j) {
  268. if (!template) {
  269. if (j.key != '') {
  270. result[j.key] = j.value;
  271. }
  272. } else {
  273. result.push(j);
  274. }
  275. }
  276. });
  277. textarea.val(JSON.stringify(result));
  278. };
  279. //监听文本框改变事件
  280. $(document).on('change keyup', ".fieldlist input,.fieldlist textarea,.fieldlist select", function () {
  281. refresh($(this).closest("dl").data("name"));
  282. });
  283. //追加控制
  284. $(".fieldlist", form).on("click", ".btn-append", function (e, row) {
  285. var container = $(this).closest("dl");
  286. var index = container.data("index");
  287. var name = container.data("name");
  288. var template = container.data("template");
  289. var data = container.data();
  290. index = index ? parseInt(index) : 0;
  291. container.data("index", index + 1);
  292. var row = row ? row : {};
  293. var vars = {index: index, name: name, data: data, row: row};
  294. var html = template ? Template(template, vars) : Template.render(Form.config.fieldlisttpl, vars);
  295. $(html).insertBefore($(this).closest("dd"));
  296. $(this).trigger("fa.event.appendfieldlist", $(this).closest("dd").prev());
  297. });
  298. //移除控制
  299. $(".fieldlist", form).on("click", "dd .btn-remove", function () {
  300. var container = $(this).closest("dl");
  301. $(this).closest("dd").remove();
  302. refresh(container.data("name"));
  303. });
  304. //拖拽排序
  305. $("dl.fieldlist", form).dragsort({
  306. itemSelector: 'dd',
  307. dragSelector: ".btn-fdragsort",
  308. dragEnd: function () {
  309. refresh($(this).closest("dl").data("name"));
  310. },
  311. placeHolderTemplate: "<dd></dd>"
  312. });
  313. //渲染数据
  314. $(".fieldlist", form).each(function () {
  315. var container = this;
  316. var textarea = $("textarea[name='" + $(this).data("name") + "']", form);
  317. if (textarea.val() == '') {
  318. return true;
  319. }
  320. var template = $(this).data("template");
  321. $.each(JSON.parse(textarea.val()), function (i, j) {
  322. $(".btn-append", container).trigger('click', template ? j : {
  323. key: i,
  324. value: j
  325. });
  326. });
  327. });
  328. });
  329. }
  330. },
  331. bindevent: function (form) {
  332. }
  333. },
  334. api: {
  335. submit: function (form, success, error, submit) {
  336. if (form.size() === 0)
  337. return Toastr.error("表单未初始化完成,无法提交");
  338. if (typeof submit === 'function') {
  339. if (false === submit.call(form)) {
  340. return false;
  341. }
  342. }
  343. var type = form.attr("method").toUpperCase();
  344. type = type && (type === 'GET' || type === 'POST') ? type : 'GET';
  345. url = form.attr("action");
  346. url = url ? url : location.href;
  347. //修复当存在多选项元素时提交的BUG
  348. var params = {};
  349. var multipleList = $("[name$='[]']", form);
  350. if (multipleList.size() > 0) {
  351. var postFields = form.serializeArray().map(function (obj) {
  352. return $(obj).prop("name");
  353. });
  354. $.each(multipleList, function (i, j) {
  355. if (postFields.indexOf($(this).prop("name")) < 0) {
  356. params[$(this).prop("name")] = '';
  357. }
  358. });
  359. }
  360. //调用Ajax请求方法
  361. Fast.api.ajax({
  362. type: type,
  363. url: url,
  364. data: form.serialize() + (Object.keys(params).length > 0 ? '&' + $.param(params) : ''),
  365. dataType: 'json',
  366. complete: function (xhr) {
  367. var token = xhr.getResponseHeader('__token__');
  368. if (token) {
  369. $("input[name='__token__']", form).val(token);
  370. }
  371. }
  372. }, function (data, ret) {
  373. $('.form-group', form).removeClass('has-feedback has-success has-error');
  374. if (data && typeof data === 'object') {
  375. //刷新客户端token
  376. if (typeof data.token !== 'undefined') {
  377. $("input[name='__token__']", form).val(data.token);
  378. }
  379. //调用客户端事件
  380. if (typeof data.callback !== 'undefined' && typeof data.callback === 'function') {
  381. data.callback.call(form, data);
  382. }
  383. }
  384. if (typeof success === 'function') {
  385. if (false === success.call(form, data, ret)) {
  386. return false;
  387. }
  388. }
  389. }, function (data, ret) {
  390. if (data && typeof data === 'object' && typeof data.token !== 'undefined') {
  391. $("input[name='__token__']", form).val(data.token);
  392. }
  393. if (typeof error === 'function') {
  394. if (false === error.call(form, data, ret)) {
  395. return false;
  396. }
  397. }
  398. });
  399. return false;
  400. },
  401. bindevent: function (form, success, error, submit) {
  402. form = typeof form === 'object' ? form : $(form);
  403. var events = Form.events;
  404. events.bindevent(form);
  405. events.validator(form, success, error, submit);
  406. events.selectpicker(form);
  407. events.daterangepicker(form);
  408. events.selectpage(form);
  409. events.cxselect(form);
  410. events.citypicker(form);
  411. events.datetimepicker(form);
  412. events.plupload(form);
  413. events.faselect(form);
  414. events.fieldlist(form);
  415. },
  416. custom: {}
  417. },
  418. };
  419. return Form;
  420. });