diff --git a/extend/fast/Form.php b/extend/fast/Form.php index 7d5933cc04a94eba03820cd01cbe0e8a67511ef6..53bc8034e1ff02d62fd364fcc853e6153da1c860 100644 --- a/extend/fast/Form.php +++ b/extend/fast/Form.php @@ -34,6 +34,7 @@ use ArrayAccess; * @method string timerange(string $name, string $value, array $options = []) static 时间区间组件 * @method string datetimerange(string $name, string $value, array $options = []) static 日期时间区间组件 * @method string fieldlist(string $name, string $value, string $title = null, string $template = null, array $options = []) static 字段列表组件 + * @method string tablelist(string $name, string $value, string $title = null, string $template = null, array $options = []) static 字段列表组件 * @method string cxselect(string $url, array $names = [], array $values = [], array $options = []) static 联动组件 * @method string selectRange(string $name, string $begin, string $end, string $selected = null, array $options = []) static 选择数字区间 * @method string selectYear(string $name, string $begin, string $end, string $selected = null, array $options = []) static 选择年 @@ -678,6 +679,43 @@ EOD;
{$append}
+EOD; + return $html; + } + + /** + * 生成动态表格列表组件 + * + * @param string $name + * @param mixed $value + * @param array $title + * @param string $template + * @param array $options + * @return string + */ + public function tablelist($name, $value, $title = null, $template = null, $options = []) + { + $append = __('Append'); + $template = $template ? 'data-template="' . $template . '"' : ''; + $attributes = $this->attributes($options); + if (is_null($title)) { + $title = [__('Key'), __('Value')]; + } + $ins = implode("\n", array_map(function ($value) { + return "{$value}"; + }, $title)); + $value = is_array($value) ? json_encode($value) : $value; + $html = << + + + {$ins} + + +
操作
+ {$append} + + EOD; return $html; } diff --git a/public/assets/js/require-form.js b/public/assets/js/require-form.js index d486148056686ade7e39c71401e1cb504ebc58d3..19f1932c77270cc86429f026b15c935131955e4d 100755 --- a/public/assets/js/require-form.js +++ b/public/assets/js/require-form.js @@ -1,10 +1,11 @@ -define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, Upload, Validator) { +define(['jquery', 'bootstrap', 'upload', 'validator'], function($, undefined, Upload, Validator) { var Form = { config: { - fieldlisttpl: '
' + fieldlisttpl: '
', + tablelisttpl: ' ' }, events: { - validator: function (form, success, error, submit) { + validator: function(form, success, error, submit) { if (!form.is("form")) return; //绑定表单事件 @@ -15,17 +16,17 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U formClass: 'n-default n-bootstrap', msgClass: 'n-right', stopOnError: true, - display: function (elem) { + display: function(elem) { return $(elem).closest('.form-group').find(".control-label").text().replace(/\:/, ''); }, - dataFilter: function (data) { + dataFilter: function(data) { if (data.code === 1) { return data.msg ? { "ok": data.msg } : ''; } else { return data.msg; } }, - target: function (input) { + target: function(input) { var target = $(input).data("target"); if (target && $(target).size() > 0) { return $(target); @@ -37,12 +38,13 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U } return $msgbox; }, - valid: function (ret) { - var that = this, submitBtn = $(".layer-footer [type=submit]", form); + valid: function(ret) { + var that = this, + submitBtn = $(".layer-footer [type=submit]", form); that.holdSubmit(true); submitBtn.addClass("disabled"); //验证通过提交表单 - var submitResult = Form.api.submit($(ret), function (data, ret) { + var submitResult = Form.api.submit($(ret), function(data, ret) { that.holdSubmit(false); submitBtn.removeClass("disabled"); if (false === $(this).triggerHandler("success.form", [data, ret])) { @@ -60,7 +62,7 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U var index = parent.Layer.getFrameIndex(window.name); parent.Layer.close(index); return false; - }, function (data, ret) { + }, function(data, ret) { that.holdSubmit(false); if (false === $(this).triggerHandler("error.form", [data, ret])) { return false; @@ -84,25 +86,25 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U //移除提交按钮的disabled类 $(".layer-footer [type=submit],.fixed-footer [type=submit],.normal-footer [type=submit]", form).removeClass("disabled"); }, - selectpicker: function (form) { + selectpicker: function(form) { //绑定select元素事件 if ($(".selectpicker", form).size() > 0) { - require(['bootstrap-select', 'bootstrap-select-lang'], function () { + require(['bootstrap-select', 'bootstrap-select-lang'], function() { $('.selectpicker', form).selectpicker(); - $(form).on("reset", function () { - setTimeout(function () { + $(form).on("reset", function() { + setTimeout(function() { $('.selectpicker').selectpicker('refresh').trigger("change"); }, 1); }); }); } }, - selectpage: function (form) { + selectpage: function(form) { //绑定selectpage元素事件 if ($(".selectpage", form).size() > 0) { - require(['selectpage'], function () { + require(['selectpage'], function() { $('.selectpage', form).selectPage({ - eAjaxSuccess: function (data) { + eAjaxSuccess: function(data) { data.list = typeof data.rows !== 'undefined' ? data.rows : (typeof data.list !== 'undefined' ? data.list : []); data.totalRow = typeof data.total !== 'undefined' ? data.total : (typeof data.totalRow !== 'undefined' ? data.totalRow : data.list.length); return data; @@ -110,23 +112,23 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U }); }); //给隐藏的元素添加上validate验证触发事件 - $(document).on("change", ".sp_hidden", function () { + $(document).on("change", ".sp_hidden", function() { $(this).trigger("validate"); }); - $(document).on("change", ".sp_input", function () { + $(document).on("change", ".sp_input", function() { $(this).closest(".sp_container").find(".sp_hidden").trigger("change"); }); - $(form).on("reset", function () { - setTimeout(function () { + $(form).on("reset", function() { + setTimeout(function() { $('.selectpage', form).selectPageClear(); }, 1); }); } }, - cxselect: function (form) { + cxselect: function(form) { //绑定cxselect元素事件 if ($("[data-toggle='cxselect']", form).size() > 0) { - require(['cxselect'], function () { + require(['cxselect'], function() { $.cxSelect.defaults.jsonName = 'name'; $.cxSelect.defaults.jsonValue = 'value'; $.cxSelect.defaults.jsonSpace = 'data'; @@ -134,22 +136,22 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U }); } }, - citypicker: function (form) { + citypicker: function(form) { //绑定城市远程插件 if ($("[data-toggle='city-picker']", form).size() > 0) { - require(['citypicker'], function () { - $(form).on("reset", function () { - setTimeout(function () { + require(['citypicker'], function() { + $(form).on("reset", function() { + setTimeout(function() { $("[data-toggle='city-picker']").citypicker('refresh'); }, 1); }); }); } }, - datetimepicker: function (form) { + datetimepicker: function(form) { //绑定日期时间元素事件 if ($(".datetimepicker", form).size() > 0) { - require(['bootstrap-datetimepicker'], function () { + require(['bootstrap-datetimepicker'], function() { var options = { format: 'YYYY-MM-DD HH:mm:ss', icons: { @@ -171,10 +173,10 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U }); } }, - daterangepicker: function (form) { + daterangepicker: function(form) { //绑定日期时间元素事件 if ($(".datetimerange", form).size() > 0) { - require(['bootstrap-daterangepicker'], function () { + require(['bootstrap-daterangepicker'], function() { var ranges = {}; ranges[__('Today')] = [Moment().startOf('day'), Moment().endOf('day')]; ranges[__('Yesterday')] = [Moment().subtract(1, 'days').startOf('day'), Moment().subtract(1, 'days').endOf('day')]; @@ -196,16 +198,16 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U }, ranges: ranges, }; - var origincallback = function (start, end) { + var origincallback = function(start, end) { $(this.element).val(start.format(this.locale.format) + " - " + end.format(this.locale.format)); $(this.element).trigger('blur'); }; - $(".datetimerange", form).each(function () { + $(".datetimerange", form).each(function() { var callback = typeof $(this).data('callback') == 'function' ? $(this).data('callback') : origincallback; - $(this).on('apply.daterangepicker', function (ev, picker) { + $(this).on('apply.daterangepicker', function(ev, picker) { callback.call(picker, picker.startDate, picker.endDate); }); - $(this).on('cancel.daterangepicker', function (ev, picker) { + $(this).on('cancel.daterangepicker', function(ev, picker) { $(this).val('').trigger('blur'); }); $(this).daterangepicker($.extend({}, options, $(this).data()), callback); @@ -213,23 +215,23 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U }); } }, - plupload: function (form) { + plupload: function(form) { //绑定plupload上传元素事件 if ($(".plupload", form).size() > 0) { Upload.api.plupload($(".plupload", form)); } }, - faselect: function (form) { + faselect: function(form) { //绑定fachoose选择附件事件 if ($(".fachoose", form).size() > 0) { - $(".fachoose", form).on('click', function () { + $(".fachoose", form).on('click', function() { var that = this; var multiple = $(this).data("multiple") ? $(this).data("multiple") : false; var mimetype = $(this).data("mimetype") ? $(this).data("mimetype") : ''; var admin_id = $(this).data("admin-id") ? $(this).data("admin-id") : ''; var user_id = $(this).data("user-id") ? $(this).data("user-id") : ''; parent.Fast.api.open("general/attachment/select?element_id=" + $(this).attr("id") + "&multiple=" + multiple + "&mimetype=" + mimetype + "&admin_id=" + admin_id + "&user_id=" + user_id, __('Choose'), { - callback: function (data) { + callback: function(data) { var button = $("#" + $(that).attr("id")); var maxcount = $(button).data("maxcount"); var input_id = $(button).data("input-id") ? $(button).data("input-id") : ""; @@ -262,17 +264,17 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U }); } }, - fieldlist: function (form) { + fieldlist: function(form) { //绑定fieldlist if ($(".fieldlist", form).size() > 0) { - require(['dragsort', 'template'], function (undefined, Template) { + require(['dragsort', 'template'], function(undefined, Template) { //刷新隐藏textarea的值 - var refresh = function (name) { + var refresh = function(name) { var data = {}; var textarea = $("textarea[name='" + name + "']", form); var container = textarea.closest("dl"); var template = container.data("template"); - $.each($("input,select,textarea", container).serializeArray(), function (i, j) { + $.each($("input,select,textarea", container).serializeArray(), function(i, j) { var reg = /\[(\w+)\]\[(\w+)\]$/g; var match = reg.exec(j.name); if (!match) @@ -284,7 +286,7 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U data[match[1]][match[2]] = j.value; }); var result = template ? [] : {}; - $.each(data, function (i, j) { + $.each(data, function(i, j) { if (j) { if (!template) { if (j.key != '') { @@ -298,11 +300,11 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U textarea.val(JSON.stringify(result)); }; //监听文本框改变事件 - $(document).on('change keyup', ".fieldlist input,.fieldlist textarea,.fieldlist select", function () { + $(document).on('change keyup', ".fieldlist input,.fieldlist textarea,.fieldlist select", function() { refresh($(this).closest("dl").data("name")); }); //追加控制 - $(".fieldlist", form).on("click", ".btn-append,.append", function (e, row) { + $(".fieldlist", form).on("click", ".btn-append,.append", function(e, row) { var container = $(this).closest("dl"); var index = container.data("index"); var name = container.data("name"); @@ -311,13 +313,13 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U index = index ? parseInt(index) : 0; container.data("index", index + 1); var row = row ? row : {}; - var vars = {index: index, name: name, data: data, row: row}; + var vars = { index: index, name: name, data: data, row: row }; var html = template ? Template(template, vars) : Template.render(Form.config.fieldlisttpl, vars); $(html).insertBefore($(this).closest("dd")); $(this).trigger("fa.event.appendfieldlist", $(this).closest("dd").prev()); }); //移除控制 - $(".fieldlist", form).on("click", "dd .btn-remove", function () { + $(".fieldlist", form).on("click", "dd .btn-remove", function() { var container = $(this).closest("dl"); $(this).closest("dd").remove(); refresh(container.data("name")); @@ -326,13 +328,13 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U $("dl.fieldlist", form).dragsort({ itemSelector: 'dd', dragSelector: ".btn-dragsort", - dragEnd: function () { + dragEnd: function() { refresh($(this).closest("dl").data("name")); }, placeHolderTemplate: "
" }); //渲染数据 - $(".fieldlist", form).each(function () { + $(".fieldlist", form).each(function() { var container = this; var textarea = $("textarea[name='" + $(this).data("name") + "']", form); if (textarea.val() == '') { @@ -342,9 +344,100 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U var json = {}; try { json = JSON.parse(textarea.val()); - } catch (e) { + } catch (e) {} + $.each(json, function(i, j) { + $(".btn-append,.append", container).trigger('click', template ? j : { + key: i, + value: j + }); + }); + }); + }); + } + }, + tablelist: function(form) { + if ($(".tablelist", form).size() > 0) { + require(['dragsort', 'template'], function(undefined, Template) { + //刷新隐藏textarea的值 + var refresh = function(name) { + var data = {}; + var textarea = $("textarea[name='" + name + "']", form); + var container = textarea.closest("div.tablelist"); + var template = container.data("template"); + $.each($("input,select,textarea", container).serializeArray(), function(i, j) { + var reg = /\[(\w+)\]\[(\w+)\]$/g; + var match = reg.exec(j.name); + if (!match) + return true; + match[1] = "x" + parseInt(match[1]); + if (typeof data[match[1]] == 'undefined') { + data[match[1]] = {}; + } + data[match[1]][match[2]] = j.value; + }); + var result = template ? [] : {}; + $.each(data, function(i, j) { + if (j) { + if (!template) { + if (j.key != '') { + result[j.key] = j.value; + } + } else { + result.push(j); + } + } + }); + textarea.val(JSON.stringify(result)); + }; + //监听文本框改变事件 + $(document).on('blur change keyup', ".tablelist input,.tablelist textarea,.tablelist select", function() { + refresh($(this).closest("div.tablelist").data("name")); + }); + + //追加控制 + $(".tablelist", form).on("click", ".btn-append,.append", function(e, row) { + var container = $(this).closest("div.tablelist"); + var table = $(container).children('table'); + var index = container.data("index"); + var name = container.data("name"); + var template = container.data("template"); + var data = container.data(); + index = index ? parseInt(index) : 0; + container.data("index", index + 1); + var row = row ? row : {}; + var vars = { index: index, name: name, data: data, row: row }; + var html = template ? Template(template, vars) : Template.render(Form.config.tablelisttpl, vars); + $(html).insertAfter($(table).find("tr:last")); + $(this).trigger("fa.event.appendfieldlist", $(table).find("tr:last")); + }); + //移除控制 + $(".tablelist", form).on("click", "tr .btn-remove", function() { + var container = $(this).closest("div.tablelist"); + $(this).closest("tr").remove(); + refresh(container.data("name")); + }); + //拖拽排序 + $("div.tablelist > table", form).dragsort({ + itemSelector: 'tr', + dragSelector: ".btn-dragsort", + dragEnd: function() { + refresh($(this).closest("div.tablelist").data("name")); + }, + placeHolderTemplate: "" + }); + //渲染数据 + $(".tablelist", form).each(function() { + var container = this; + var textarea = $("textarea[name='" + $(this).data("name") + "']", form); + if (textarea.val() == '') { + return true; } - $.each(json, function (i, j) { + var template = $(this).data("template"); + var json = {}; + try { + json = JSON.parse(textarea.val()); + } catch (e) {} + $.each(json, function(i, j) { $(".btn-append,.append", container).trigger('click', template ? j : { key: i, value: j @@ -354,8 +447,8 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U }); } }, - switcher: function (form) { - form.on("click", "[data-toggle='switcher']", function () { + switcher: function(form) { + form.on("click", "[data-toggle='switcher']", function() { if ($(this).hasClass("disabled")) { return false; } @@ -376,15 +469,15 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U return false; }); }, - bindevent: function (form) { + bindevent: function(form) { }, - slider: function (form) { + slider: function(form) { if ($(".slider", form).size() > 0) { - require(['bootstrap-slider'], function () { - $('.slider').removeClass('hidden').css('width', function (index, value) { + require(['bootstrap-slider'], function() { + $('.slider').removeClass('hidden').css('width', function(index, value) { return $(this).parents('.form-control').width(); - }).slider().on('slide', function (ev) { + }).slider().on('slide', function(ev) { var data = $(this).data(); if (typeof data.unit !== 'undefined') { $(this).parents('.form-control').siblings('.value').text(ev.value + data.unit); @@ -395,7 +488,7 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U } }, api: { - submit: function (form, success, error, submit) { + submit: function(form, success, error, submit) { if (form.size() === 0) { Toastr.error("表单未初始化完成,无法提交"); return false; @@ -413,10 +506,10 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U var params = {}; var multipleList = $("[name$='[]']", form); if (multipleList.size() > 0) { - var postFields = form.serializeArray().map(function (obj) { + var postFields = form.serializeArray().map(function(obj) { return $(obj).prop("name"); }); - $.each(multipleList, function (i, j) { + $.each(multipleList, function(i, j) { if (postFields.indexOf($(this).prop("name")) < 0) { params[$(this).prop("name")] = ''; } @@ -428,13 +521,13 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U url: url, data: form.serialize() + (Object.keys(params).length > 0 ? '&' + $.param(params) : ''), dataType: 'json', - complete: function (xhr) { + complete: function(xhr) { var token = xhr.getResponseHeader('__token__'); if (token) { $("input[name='__token__']").val(token); } } - }, function (data, ret) { + }, function(data, ret) { $('.form-group', form).removeClass('has-feedback has-success has-error'); if (data && typeof data === 'object') { //刷新客户端token @@ -451,7 +544,7 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U return false; } } - }, function (data, ret) { + }, function(data, ret) { if (data && typeof data === 'object' && typeof data.token !== 'undefined') { $("input[name='__token__']").val(data.token); } @@ -463,7 +556,7 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U }); return true; }, - bindevent: function (form, success, error, submit) { + bindevent: function(form, success, error, submit) { form = typeof form === 'object' ? form : $(form); @@ -491,6 +584,8 @@ define(['jquery', 'bootstrap', 'upload', 'validator'], function ($, undefined, U events.fieldlist(form); + events.tablelist(form); + events.slider(form); events.switcher(form);