text: 
  import '../jquery-form/jquery.form.min';
  
  let formCallbacks = {
          before: [], success: [], error: []
      };
  
  class Form {
      constructor(options) {
          let _this = this;
  
          _this.options = $.extend({
              // 表单的 jquery 对象或者css选择器
              form: null,
              // 开启表单验证
              validate: false,
              // 确认弹窗
              confirm: {title: null, content: null},
              // 是否使用Toastr展示字段验证错误信息
              validationErrorToastr: false,
              // 表单错误信息class
              errorClass: 'has-error',
              // 表单错误信息容器选择器
              errorContainerSelector: '.with-errors',
              // 表单组css选择器
              groupSelector: '.form-group,.form-label-group,.form-field',
              // tab表单css选择器
              tabSelector: '.tab-pane',
              // 错误信息模板
              errorTemplate: '<label class="control-label" for="inputError"><i class="feather icon-x-circle"></i> {message}</label><br/>',
              // 是否允许跳转
              redirect: true,
              // 自动移除表单错误信息
              autoRemoveError: true,
              // 表单提交之前事件监听，返回false可以中止表单继续提交
              before: function () {},
              // 表单提交之后事件监听，返回false可以中止后续逻辑
              after: function () {},
              // 成功事件，返回false可以中止后续逻辑
              success: function () {},
              // 失败事件，返回false可以中止后续逻辑
              error: function () {},
          }, options);
  
          _this.originalValues = {};
          _this.$form = $(_this.options.form).first();
          _this._errColumns = {};
  
          _this.init();
      }
  
      init() {
          let _this = this;
          let confirm = _this.options.confirm;
  
          if (! confirm.title) {
              return _this.submit();
          }
  
          Dcat.confirm(confirm.title, confirm.content, function () {
              _this.submit();
          });
      }
  
      submit() {
          let _this = this,
              $form = _this.$form,
              options = _this.options,
              $submitButton = $form.find('[type="submit"],.submit');
  
          // 移除所有错误信息
          _this.removeErrors();
  
          $form.ajaxSubmit({
              data: {_token: Dcat.token},
              beforeSubmit: function (fields, form, _opt) {
                  if (options.before(fields, form, _opt, _this) === false) {
                      return false;
                  }
  
                  // 触发全局事件
                  if (fire(formCallbacks.before, fields, form, _opt, _this) === false) {
                      return false;
                  }
  
                  // 开启表单验证
                  if (options.validate) {
                      $form.validator('validate');
  
                      if ($form.find('.' + options.errorClass).length > 0) {
                          return false;
                      }
                  }
  
                  $submitButton.buttonLoading();
              },
              success: function (response) {
                  setTimeout(function () {
                      $submitButton.buttonLoading(false);
                  }, 700);
  
                  if (options.after(true, response, _this) === false) {
                      return;
                  }
  
                  if (options.success(response, _this) === false) {
                      return;
                  }
  
                  if (fire(formCallbacks.success, response, _this) === false) {
                      return;
                  }
  
                  if (response.redirect === false || ! options.redirect) {
                      if (response.data && response.data.then) {
                          delete response.data['then'];
                          delete response.data['then'];
                          delete response.data['then'];
                      }
                  }
  
                  Dcat.handleJsonResponse(response);
              },
              error: function (response) {
                  $submitButton.buttonLoading(false);
  
                  if (options.after(false, response, _this) === false) {
                      return;
                  }
  
                  if (options.error(response, _this) === false) {
                      return;
                  }
  
                  if (fire(formCallbacks.error, response, _this) === false) {
                      return;
                  }
  
                  try {
                      var error = JSON.parse(response.responseText),
                          key;
  
                      if (response.status != 422 || ! error || ! Dcat.helpers.isset(error, 'errors')) {
                          let json = response.responseJSON;
                          if (json && json.message) {
                              return Dcat.error(json.message);
                          }
  
                          return Dcat.error(response.status + ' ' + response.statusText);
                      }
                      error = error.errors;
  
                      for (key in error) {
                          // 显示错误信息
                          _this._errColumns[key] = _this.showError($form, key, error[key]);
                      }
  
                  } catch (e) {
                      return Dcat.error(response.status + ' ' + response.statusText);
                  }
              }
          });
      }
  
      // 显示错误信息
      showError($form, column, errors) {
          let _this = this,
              $field = _this.queryFieldByName($form, column),
              $group = $field.closest(_this.options.groupSelector),
              render = function (msg) {
                  $group.addClass(_this.options.errorClass);
  
                  if (typeof msg === 'string') {
                      msg = [msg];
                  }
  
                  for (let j in msg) {
                      $group.find(_this.options.errorContainerSelector).first().append(
                          _this.options.errorTemplate.replace('{message}', msg[j])
                      );
                  }
  
                  if (_this.options.validationErrorToastr) {
                      Dcat.error(msg.join('<br/>'));
                  }
              };
  
          queryTabTitleError(_this, $field).removeClass('d-none');
  
          // 保存字段原始数据
          _this.originalValues[column] = _this.getFieldValue($field);
  
          if (! $field) {
              if (Dcat.helpers.len(errors) && errors.length) {
                  Dcat.error(errors.join("  \n  "));
              }
              return;
          }
  
          render(errors);
  
          if (_this.options.autoRemoveError) {
              removeErrorWhenValChanged(_this, $field, column);
          }
  
          return $field;
      }
  
      // 获取字段值
      getFieldValue($field) {
          let vals = [],
              type = $field.attr('type'),
              checker = type === 'checkbox' || type === 'radio',
              i;
  
          for (i = 0; i < $field.length; i++) {
              if (checker) {
                  vals.push($($field[i]).prop('checked'));
                  continue;
              }
  
              vals.push($($field[i]).val());
          }
  
          return vals;
      }
  
      // 判断值是否改变
      isValueChanged($field, column) {
          return ! Dcat.helpers.equal(this.originalValues[column], this.getFieldValue($field));
      }
  
      // 获取字段jq对象
      queryFieldByName($form, column) {
          if (column.indexOf('.') !== -1) {
              column = column.split('.');
  
              let first = column.shift(),
                  i,
                  sub = '';
  
              for (i in column) {
                  sub += '[' + column[i] + ']';
              }
              column = first + sub;
          }
  
          var $c = $form.find('[name="' + column + '"]');
  
          if (!$c.length) $c = $form.find('[name="' + column + '[]"]');
  
          if (!$c.length) {
              $c = $form.find('[name="' + column.replace(/start$/, '') + '"]');
          }
          if (!$c.length) {
              $c = $form.find('[name="' + column.replace(/end$/, '') + '"]');
          }
  
          if (!$c.length) {
              $c = $form.find('[name="' + column.replace(/start\]$/, ']') + '"]');
          }
          if (!$c.length) {
              $c = $form.find('[name="' + column.replace(/end\]$/, ']') + '"]');
          }
  
          return $c;
      }
  
      // 移除给定字段的错误信息
      removeError($field, column) {
          let options = this.options,
              parent = $field.parents(options.groupSelector),
              errorClass = this.errorClass;
  
          parent.removeClass(errorClass);
          parent.find(options.errorContainerSelector).html('');
  
          // tab页下没有错误信息了，隐藏title的错误图标
          let tab;
  
          if (! queryTabByField(this, $field).find('.'+errorClass).length) {
              tab = queryTabTitleError(this, $field);
              if (! tab.hasClass('d-none')) {
                  tab.addClass('d-none');
              }
          }
  
          delete this._errColumns[column];
      }
  
      // 删除所有错误信息
      removeErrors() {
          let _this = this,
              column,
              tab;
  
          // 移除所有字段的错误信息
          _this.$form.find(_this.options.errorContainerSelector).each(function (_, $err) {
              $($err).parents(_this.options.groupSelector).removeClass(_this.options.errorClass);
              $($err).html('');
          });
  
          // 移除tab表单tab标题错误信息
          for (column in _this._errColumns) {
              tab = queryTabTitleError(_this._errColumns[column]);
              if (! tab.hasClass('d-none')) {
                  tab.addClass('d-none');
              }
          }
  
          // 重置
          _this._errColumns = {};
      }
  }
  
  // 监听表单提交事件
  Form.submitting = function (callback) {
      typeof callback == 'function' && (formCallbacks.before.push(callback));
  
      return this
  };
  
  // 监听表单提交完毕事件
  Form.submitted = function (success, error) {
      typeof success == 'function' && (formCallbacks.success.push(success));
      typeof error == 'function' && (formCallbacks.error.push(error));
  
      return this
  };
  
  // 当字段值变化时移除错误信息
  function removeErrorWhenValChanged(form, $field, column) {
      let remove = function () {
          form.removeError($field, column)
      };
  
      $field.one('change', remove);
      $field.off('blur', remove).on('blur', function () {
          if (form.isValueChanged($field, column))  {
              remove();
          }
      });
  
      // 表单值发生变化就移除错误信息
      let interval = function () {
          setTimeout(function () {
              if (! $field.length) {
                  return;
              }
              if (form.isValueChanged($field, column)) {
                  return remove();
              }
  
              interval();
          }, 500);
      };
  
      interval();
  }
  
  
  function getTabId(form, $field) {
      return $field.parents(form.options.tabSelector).attr('id');
  }
  
  function queryTabByField(form, $field)
  {
      let tabId = getTabId(form, $field);
  
      if (! tabId) {
          return $('<none></none>');
      }
  
      return $(`a[href="#${tabId}"]`);
  }
  
  function queryTabTitleError(form, $field) {
      return queryTabByField(form, $field).find('.has-tab-error');
  }
  
  // 触发钩子事件
  function fire(callbacks) {
      let i, j,
          result,
          args = arguments,
          argsArr = [];
  
      delete args[0];
  
      args = args || [];
  
      for (j in args) {
          argsArr.push(args[j]);
      }
  
      for (i in callbacks) {
          result = callbacks[i].apply(callbacks[i], argsArr);
  
          if (result === false) {
              return result; // 返回 false 会代码阻止继续执行
          }
      }
  }
  
  
  // 开启form表单模式
  $.fn.form = function (options) {
      let $this = $(this);
  
      options = $.extend(options, {
          form: $this,
      });
  
      $this.on('submit', function () {
          return false;
      });
  
      $this.find('[type="submit"],.submit').click(function (e) {
          Dcat.Form(options);
  
          return false;
      });
  };
  
  export default Form
  