text: import { download, initData } from '@/api/data'
  import { downloadFile, parseTime } from '@/utils/index'
  import Vue from 'vue'
  import i18n from '../../lang'
  
  /**
   * CRUD配置
   * @author moxun
   * @param {*} options <br>
   * @return crud instance.
   * @example
   */
  function CRUD(options) {
    const defaultOptions = {
      // 标题
      title: '',
      // 请求数据的url
      url: '',
      // export excel url
      exportUrl: '',
      // 表格数据
      data: [],
      // 选择项
      selections: [],
      // 待查询的对象
      query: {},
      // 查询数据的参数
      params: {},
      // 是否显示高级搜索按钮,有些场景不需要显示
      showAdSearchBtn: true,
      // 需要高级搜索的字段信息,例如:[{fieldName: 'userName', labelName: '用户名', type: 'text', dists: [{label:'user_status',value: 1 }]}]
      // type 支持text,number,date,datetime,dict
      adSearchFields: [],
      // 高级查询的参数集合,使用Post方法传递的data
      adSearchConditions: [],
      // Form 表单
      form: {},
      // 重置表单
      defaultForm: () => {
      },
      // 排序规则，默认 id 降序， 支持多字段排序 ['id,desc', 'createTime,asc']
      sort: ['createTime,desc'],
      // 等待时间
      time: 50,
      // CRUD Method
      crudMethod: {
        add: (form) => {
        },
        delete: (id) => {
        },
        edit: (form) => {
        },
        get: (id) => {
        }
      },
      // 主页操作栏显示哪些按钮
      optShow: {
        add: true,
        edit: true,
        del: true,
        download: true
      },
      // 自定义一些扩展属性
      props: {},
      // 在主页准备
      queryOnPresenterCreated: true,
      // 调试开关
      debug: false
    }
    options = mergeOptions(defaultOptions, options)
    const data = {
      ...options,
      // 记录数据状态
      dataStatus: {},
      status: {
        add: CRUD.STATUS.NORMAL,
        edit: CRUD.STATUS.NORMAL,
        // 添加或编辑状态
        get cu() {
          if (this.add === CRUD.STATUS.NORMAL && this.edit === CRUD.STATUS.NORMAL) {
            return CRUD.STATUS.NORMAL
          } else if (this.add === CRUD.STATUS.PREPARED || this.edit === CRUD.STATUS.PREPARED) {
            return CRUD.STATUS.PREPARED
          } else if (this.add === CRUD.STATUS.PROCESSING || this.edit === CRUD.STATUS.PROCESSING) {
            return CRUD.STATUS.PROCESSING
          }
          throw new Error('wrong crud\'s cu status')
        },
        // 标题
        get title() {
          return this.add > CRUD.STATUS.NORMAL ? i18n.t('crud.new') + ` ${crud.title}` : this.edit > CRUD.STATUS.NORMAL ? i18n.t('crud.edit') + ` ${crud.title}` : crud.title
        }
      },
      msg: {
        submit: i18n.t('crud.submitSuccess'),
        add: i18n.t('crud.addSuccess'),
        edit: i18n.t('crud.editSuccess'),
        del: i18n.t('crud.delSuccess')
      },
      page: {
        // 页码
        page: 0,
        // 每页数据条数
        size: 10,
        // 总数据条数
        total: 0
      },
      // 高级搜索弹框
      showAdSearchDialog: false,
      // 整体loading
      loading: true,
      // 导出的 Loading
      downloadLoading: false,
      // 删除的 Loading
      delAllLoading: false
    }
    const methods = {
      /**
       * 通用的提示
       */
      submitSuccessNotify() {
        crud.notify(crud.msg.submit, CRUD.NOTIFICATION_TYPE.SUCCESS)
      },
      addSuccessNotify() {
        crud.notify(crud.msg.add, CRUD.NOTIFICATION_TYPE.SUCCESS)
      },
      editSuccessNotify() {
        crud.notify(crud.msg.edit, CRUD.NOTIFICATION_TYPE.SUCCESS)
      },
      delSuccessNotify() {
        crud.notify(crud.msg.del, CRUD.NOTIFICATION_TYPE.SUCCESS)
      },
      // 搜索
      toQuery() {
        crud.page.page = 1
        crud.refresh()
      },
      // 高级搜索
      dialogSearch() {
        // console.log(crud)
        crud.showAdSearchDialog = true // 显示高级搜索弹框
        // console.log(crud.show)
      },
      // 刷新
      refresh() {
        if (!callVmHook(crud, CRUD.HOOK.beforeRefresh)) {
          return
        }
        return new Promise((resolve, reject) => {
          crud.loading = true
          // 判断是否存在高级查询的条件，有则走高级查询的接口
          let method = 'get'
          if (crud.adSearchConditions.length > 0) {
            method = 'post' // 走高级搜索的接口
          }
          // 请求数据,
          initData(crud.url, crud.getQueryParams(), method).then(r => {
            // http通信正常，检查业务逻辑是否正常
            if (r.code === 0) { // 业务逻辑正常
              const data = r.data
              crud.page.total = data.totalCount
              crud.data = data.content
              crud.resetDataStatus()
              // time 毫秒后显示表格
              setTimeout(() => {
                crud.loading = false
                callVmHook(crud, CRUD.HOOK.afterRefresh)
              }, crud.time)
              resolve(data)
            } else { // 业务逻辑不正常
              crud.loading = false
              crud.notify(r.msg, CRUD.NOTIFICATION_TYPE.ERROR)
              console.error(r.toString())
            }
          }).catch(err => { // 此处的错误为Http错误通信码
            crud.loading = false
            reject(err)
          })
        })
      },
      /**
       * 启动添加
       */
      toAdd() {
        if (!(callVmHook(crud, CRUD.HOOK.beforeToAdd, crud.form) && callVmHook(crud, CRUD.HOOK.beforeToCU, crud.form))) {
          return
        }
        crud.status.add = CRUD.STATUS.PREPARED
        callVmHook(crud, CRUD.HOOK.afterToAdd, crud.form)
        callVmHook(crud, CRUD.HOOK.afterToCU, crud.form)
      },
      /**
       * 启动编辑
       * @param {*} data 数据项
       */
      toEdit(data) {
        crud.resetForm(JSON.parse(JSON.stringify(data)))
        if (!(callVmHook(crud, CRUD.HOOK.beforeToEdit, crud.form) && callVmHook(crud, CRUD.HOOK.beforeToCU, crud.form))) {
          return
        }
        crud.status.edit = CRUD.STATUS.PREPARED
        crud.getDataStatus(data.id).edit = CRUD.STATUS.PREPARED
        callVmHook(crud, CRUD.HOOK.afterToEdit, crud.form)
        callVmHook(crud, CRUD.HOOK.afterToCU, crud.form)
      },
      /**
       * 启动删除
       * @param {*} data 数据项
       */
      toDelete(data) {
        crud.getDataStatus(data.id).delete = CRUD.STATUS.PREPARED
      },
      /**
       * 取消删除
       * @param {*} data 数据项
       */
      cancelDelete(data) {
        if (!callVmHook(crud, CRUD.HOOK.beforeDeleteCancel, data)) {
          return
        }
        crud.getDataStatus(data.id).delete = CRUD.STATUS.NORMAL
        callVmHook(crud, CRUD.HOOK.afterDeleteCancel, data)
      },
      /**
       * 取消新增/编辑
       */
      cancelCU() {
        const addStatus = crud.status.add
        const editStatus = crud.status.edit
        if (addStatus === CRUD.STATUS.PREPARED) {
          if (!callVmHook(crud, CRUD.HOOK.beforeAddCancel, crud.form)) {
            return
          }
          crud.status.add = CRUD.STATUS.NORMAL
        }
        if (editStatus === CRUD.STATUS.PREPARED) {
          if (!callVmHook(crud, CRUD.HOOK.beforeEditCancel, crud.form)) {
            return
          }
          crud.status.edit = CRUD.STATUS.NORMAL
          crud.getDataStatus(crud.form.id).edit = CRUD.STATUS.NORMAL
        }
        crud.resetForm()
        if (addStatus === CRUD.STATUS.PREPARED) {
          callVmHook(crud, CRUD.HOOK.afterAddCancel, crud.form)
        }
        if (editStatus === CRUD.STATUS.PREPARED) {
          callVmHook(crud, CRUD.HOOK.afterEditCancel, crud.form)
        }
        // 清除表单验证
        if (crud.findVM('form').$refs['form']) {
          crud.findVM('form').$refs['form'].clearValidate()
        }
      },
      /**
       * 提交新增/编辑
       */
      submitCU() {
        if (!callVmHook(crud, CRUD.HOOK.beforeValidateCU)) {
          return
        }
        crud.findVM('form').$refs['form'].validate(valid => {
          if (!valid) {
            return
          }
          if (!callVmHook(crud, CRUD.HOOK.afterValidateCU)) {
            return
          }
          if (crud.status.add === CRUD.STATUS.PREPARED) {
            crud.doAdd()
          } else if (crud.status.edit === CRUD.STATUS.PREPARED) {
            crud.doEdit()
          }
        })
      },
      /**
       * 执行添加
       */
      doAdd() {
        if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) {
          return
        }
        crud.crudMethod.add(crud.form).then(r => {
          if (r.code === 0) {
            crud.status.add = CRUD.STATUS.NORMAL
            crud.resetForm()
            crud.addSuccessNotify()
            callVmHook(crud, CRUD.HOOK.afterSubmit)
            crud.toQuery()
          } else if (r.code === 1020400) {
            // 显示所有的检查出错的信息
            Object.keys(r.data).forEach(function(key) {
              console.info(key + '-----------' + r.data[key])
              crud.notify(r.data[key], CRUD.NOTIFICATION_TYPE.ERROR)
            })
          } else {
            // 总的校验信息
            crud.notify(r.msg, CRUD.NOTIFICATION_TYPE.ERROR)
          }
        }).catch(() => {
          callVmHook(crud, CRUD.HOOK.afterAddError)
        })
      },
      /**
       * 执行编辑
       */
      doEdit() {
        if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) {
          return
        }
        crud.crudMethod.edit(crud.form).then(r => {
          if (r.code === 0) {
            crud.status.edit = CRUD.STATUS.NORMAL
            crud.getDataStatus(crud.form.id).edit = CRUD.STATUS.NORMAL
            crud.editSuccessNotify()
            crud.resetForm()
            callVmHook(crud, CRUD.HOOK.afterSubmit)
            crud.refresh()
          } else if (r.code === 1020400) {
            // 显示所有的检查出错的信息
            Object.keys(r.data).forEach(function(key) {
              console.info(key + '-----------' + r.data[key])
              crud.notify(r.data[key], CRUD.NOTIFICATION_TYPE.ERROR)
            })
          } else {
            // 总的校验信息
            crud.notify(r.msg, CRUD.NOTIFICATION_TYPE.ERROR)
          }
        }).catch(() => {
          callVmHook(crud, CRUD.HOOK.afterEditError)
        })
      },
      /**
       * 执行删除
       * @param {*} data 数据项
       */
      doDelete(data) {
        let delAll = false
        let dataStatus
        const ids = []
        if (data instanceof Array) {
          delAll = true
          data.forEach(val => {
            ids.push(val.id)
          })
        } else {
          ids.push(data.id)
          dataStatus = crud.getDataStatus(data.id)
        }
        if (!callVmHook(crud, CRUD.HOOK.beforeDelete, data)) {
          return
        }
        if (!delAll) {
          dataStatus.delete = CRUD.STATUS.PROCESSING
        }
        return crud.crudMethod.del(ids).then(r => {
          if (r.code === 0) { // 接口正常处理
            if (delAll) {
              crud.delAllLoading = false
            } else dataStatus.delete = CRUD.STATUS.PREPARED
            crud.dleChangePage(1)
            crud.delSuccessNotify()
            callVmHook(crud, CRUD.HOOK.afterDelete, data)
            crud.refresh()
          } else { // 处理出错！
            // 显示错误信息
            crud.notify(r.msg, CRUD.NOTIFICATION_TYPE.ERROR)
          }
        }).catch(() => {
          if (delAll) {
            crud.delAllLoading = false
          } else dataStatus.delete = CRUD.STATUS.PREPARED
        })
      },
      /**
       * 通用导出
       */
      doExport() {
        crud.downloadLoading = true
        download(crud.exportUrl, crud.getQueryParams()).then(result => {
          downloadFile(result, crud.title + i18n.t('crud.exportData'), 'xlsx')
          crud.downloadLoading = false
        }).catch(() => {
          crud.downloadLoading = false
        })
      },
      /**
       * 获取查询参数
       */
      getQueryParams() {
        return {
          page: crud.page.page - 1,
          size: crud.page.size,
          conditions: crud.adSearchConditions,
          sort: crud.sort,
          ...crud.query,
          ...crud.params
        }
      },
      // 当前页改变
      pageChangeHandler(e) {
        crud.page.page = e
        crud.refresh()
      },
      // 每页条数改变
      sizeChangeHandler(e) {
        crud.page.size = e
        crud.page.page = 1
        crud.refresh()
      },
      // 预防删除第二页最后一条数据时，或者多选删除第二页的数据时，页码错误导致请求无数据
      dleChangePage(size) {
        if (crud.data.length === size && crud.page.page !== 1) {
          crud.page.page -= 1
        }
      },
      // 选择改变
      selectionChangeHandler(val) {
        crud.selections = val
      },
      removeArrayItem(array, val) {
        const index = array.indexOf(val)
        if (index > -1) {
          array.splice(index, 1)
        }
      },
      // 单击列头触发后端排序
      doTitleOrder(column) {
        // 设定排序参数,支持多字段排序
        // if (column.order != null) {
        //   if (column.order.indexOf('descending') > -1) {
        //     crud.sort.push(column.prop + ',desc')
        //     crud.removeArrayItem(crud.sort, column.prop + ',asc')
        //   } else if (column.order.indexOf('ascending') > -1) {
        //     crud.sort.push(column.prop + ',asc')
        //     crud.removeArrayItem(crud.sort, column.prop + ',desc')
        //   }
        // } else {
        //   crud.removeArrayItem(crud.sort, column.prop + ',asc')
        //   crud.removeArrayItem(crud.sort, column.prop + ',desc')
        // }
  
        // 单个字段排序
        // 清除旧排序
        crud.sort = []
        if (column.order != null) {
          if (column.order.indexOf('descending') > -1) {
            crud.sort.push(column.prop + ',desc')
          } else if (column.order.indexOf('ascending') > -1) {
            crud.sort.push(column.prop + ',asc')
          }
        }
        // 调用分页接口
        crud.page.page = 1
        crud.refresh()
      },
      /**
       * 重置查询参数
       * @param {Boolean} toQuery 重置后进行查询操作
       */
      resetQuery(toQuery = true) {
        const defaultQuery = JSON.parse(JSON.stringify(crud.defaultQuery))
        // 清除所有的查询参数
        crud.params = {}
        crud.adSearchConditions = [] // 清空crud里面的保存的高级查询条件！
        const query = crud.query
        Object.keys(query).forEach(key => {
          query[key] = defaultQuery[key]
        })
        if (toQuery) {
          crud.toQuery()
        }
      },
      /**
       * 重置表单
       * @param {Array} data 数据
       */
      resetForm(data) {
        const form = data || (typeof crud.defaultForm === 'object' ? JSON.parse(JSON.stringify(crud.defaultForm)) : crud.defaultForm())
        const crudFrom = crud.form
        for (const key in form) {
          if (crudFrom.hasOwnProperty(key)) {
            crudFrom[key] = form[key]
          } else {
            Vue.set(crudFrom, key, form[key])
          }
        }
      },
      /**
       * 重置数据状态
       */
      resetDataStatus() {
        const dataStatus = {}
  
        function resetStatus(datas) {
          datas.forEach(e => {
            dataStatus[e.id] = {
              delete: 0,
              edit: 0
            }
            if (e.children) {
              resetStatus(e.children)
            }
          })
        }
  
        resetStatus(crud.data)
        crud.dataStatus = dataStatus
      },
      /**
       * 获取数据状态
       * @param {Number | String} id 数据项id
       */
      getDataStatus(id) {
        return crud.dataStatus[id]
      },
      /**
       * 用于树形表格多选, 选中所有
       * @param selection
       */
      selectAllChange(selection) {
        // 如果选中的数目与请求到的数目相同就选中子节点，否则就清空选中
        if (selection && selection.length === crud.data.length) {
          selection.forEach(val => {
            crud.selectChange(selection, val)
          })
        } else {
          crud.findVM('presenter').$refs['table'].clearSelection()
        }
      },
      /**
       * 用于树形表格多选，单选的封装
       * @param selection
       * @param row
       */
      selectChange(selection, row) {
        // 如果selection中存在row代表是选中，否则是取消选中
        if (selection.find(val => {
          return val.id === row.id
        })) {
          if (row.children) {
            row.children.forEach(val => {
              crud.findVM('presenter').$refs['table'].toggleRowSelection(val, true)
              selection.push(val)
              if (val.children) {
                crud.selectChange(selection, val)
              }
            })
          }
        } else {
          crud.toggleRowSelection(selection, row)
        }
      },
      /**
       * 切换选中状态
       * @param selection
       * @param data
       */
      toggleRowSelection(selection, data) {
        if (data.children) {
          data.children.forEach(val => {
            crud.findVM('presenter').$refs['table'].toggleRowSelection(val, false)
            if (val.children) {
              crud.toggleRowSelection(selection, val)
            }
          })
        }
      },
      findVM(type) {
        return crud.vms.find(vm => vm && vm.type === type).vm
      },
      notify(title, type = CRUD.NOTIFICATION_TYPE.INFO) {
        crud.vms[0].vm.$notify({
          title,
          type,
          duration: 2500
        })
      },
      updateProp(name, value) {
        Vue.set(crud.props, name, value)
      }
    }
    const crud = Object.assign({}, data)
    // 可观测化
    Vue.observable(crud)
    // 附加方法
    Object.assign(crud, methods)
    // 记录初始默认的查询参数，后续重置查询时使用
    Object.assign(crud, {
      defaultQuery: JSON.parse(JSON.stringify(data.query)),
      // 预留4位存储：组件 主页、头部、分页、表单，调试查看也方便找
      vms: Array(4),
      /**
       * 注册组件实例
       * @param {String} type 类型
       * @param {*} vm 组件实例
       * @param {Number} index 该参数内部使用
       */
      registerVM(type, vm, index = -1) {
        const vmObj = {
          type,
          vm: vm
        }
        if (index < 0) {
          this.vms.push(vmObj)
          return
        }
        this.vms.length = Math.max(this.vms.length, index)
        this.vms.splice(index, 1, vmObj)
      },
      /**
       * 取消注册组件实例
       * @param {*} vm 组件实例
       */
      unregisterVM(vm) {
        this.vms.splice(this.vms.findIndex(e => e && e.vm === vm), 1)
      }
    })
    // 冻结处理，需要扩展数据的话，使用crud.updateProp(name, value)，以crud.props.name形式访问，这个是响应式的，可以做数据绑定
    Object.freeze(crud)
    return crud
  }
  
  // hook VM
  function callVmHook(crud, hook) {
    if (crud.debug) {
      console.log('callVmHook: ' + hook)
    }
    let ret = true
    const nargs = [crud]
    for (let i = 2; i < arguments.length; ++i) {
      nargs.push(arguments[i])
    }
    // 有些组件扮演了多个角色，调用钩子时，需要去重
    const vmSet = new Set()
    crud.vms.forEach(vm => vm && vmSet.add(vm.vm))
    vmSet.forEach(vm => {
      if (vm[hook]) {
        ret = vm[hook].apply(vm, nargs) !== false && ret
      }
    })
    return ret
  }
  
  function mergeOptions(src, opts) {
    const optsRet = {
      ...src
    }
    for (const key in src) {
      if (opts.hasOwnProperty(key)) {
        optsRet[key] = opts[key]
      }
    }
    return optsRet
  }
  
  /**
   * crud主页
   */
  function presenter(crud) {
    function obColumns(columns) {
      return {
        visible(col) {
          return !columns || !columns[col] ? true : columns[col].visible
        }
      }
    }
  
    return {
      inject: ['crud'],
      beforeCreate() {
        // 由于initInjections在initProvide之前执行，如果该组件自己就需要crud，需要在initInjections前准备好crud
        this._provided = {
          crud,
          'crud.query': crud.query,
          'crud.page': crud.page,
          'crud.form': crud.form
        }
      },
      data() {
        return {
          searchToggle: true,
          columns: obColumns()
        }
      },
      methods: {
        parseTime
      },
      created() {
        this.crud.registerVM('presenter', this, 0)
        if (crud.queryOnPresenterCreated) {
          crud.toQuery()
        }
      },
      beforeDestroy() {
        this.crud.unregisterVM(this)
      },
      mounted() {
        const columns = {}
        this.$refs.table.columns.forEach(e => {
          if (!e.property || e.type !== 'default') {
            return
          }
          columns[e.property] = {
            label: e.label,
            visible: true
          }
        })
        this.columns = obColumns(columns)
        this.crud.updateProp('tableColumns', columns)
      }
    }
  }
  
  /**
   * 头部
   */
  function header() {
    return {
      inject: {
        crud: {
          from: 'crud'
        },
        query: {
          from: 'crud.query'
        }
      },
      created() {
        this.crud.registerVM('header', this, 1)
      },
      beforeDestroy() {
        this.crud.unregisterVM(this)
      }
    }
  }
  
  /**
   * 分页
   */
  function pagination() {
    return {
      inject: {
        crud: {
          from: 'crud'
        },
        page: {
          from: 'crud.page'
        }
      },
      created() {
        this.crud.registerVM('pagination', this, 2)
      },
      beforeDestroy() {
        this.crud.unregisterVM(this)
      }
    }
  }
  
  /**
   * 表单
   */
  function form(defaultForm) {
    return {
      inject: {
        crud: {
          from: 'crud'
        },
        form: {
          from: 'crud.form'
        }
      },
      created() {
        this.crud.registerVM('form', this, 3)
        this.crud.defaultForm = defaultForm
        this.crud.resetForm()
      },
      beforeDestroy() {
        this.crud.unregisterVM(this)
      }
    }
  }
  
  /**
   * crud
   */
  function crud(options = {}) {
    const defaultOptions = {
      type: undefined
    }
    options = mergeOptions(defaultOptions, options)
    return {
      inject: {
        crud: {
          from: 'crud'
        }
      },
      created() {
        this.crud.registerVM(options.type, this)
      },
      beforeDestroy() {
        this.crud.unregisterVM(this)
      }
    }
  }
  
  /**
   * CRUD钩子
   */
  CRUD.HOOK = {
    /** 刷新 - 之前 */
    beforeRefresh: 'beforeCrudRefresh',
    /** 刷新 - 之后 */
    afterRefresh: 'afterCrudRefresh',
    /** 删除 - 之前 */
    beforeDelete: 'beforeCrudDelete',
    /** 删除 - 之后 */
    afterDelete: 'afterCrudDelete',
    /** 删除取消 - 之前 */
    beforeDeleteCancel: 'beforeCrudDeleteCancel',
    /** 删除取消 - 之后 */
    afterDeleteCancel: 'afterCrudDeleteCancel',
    /** 新建 - 之前 */
    beforeToAdd: 'beforeCrudToAdd',
    /** 新建 - 之后 */
    afterToAdd: 'afterCrudToAdd',
    /** 编辑 - 之前 */
    beforeToEdit: 'beforeCrudToEdit',
    /** 编辑 - 之后 */
    afterToEdit: 'afterCrudToEdit',
    /** 开始 "新建/编辑" - 之前 */
    beforeToCU: 'beforeCrudToCU',
    /** 开始 "新建/编辑" - 之后 */
    afterToCU: 'afterCrudToCU',
    /** "新建/编辑" 验证 - 之前 */
    beforeValidateCU: 'beforeCrudValidateCU',
    /** "新建/编辑" 验证 - 之后 */
    afterValidateCU: 'afterCrudValidateCU',
    /** 添加取消 - 之前 */
    beforeAddCancel: 'beforeCrudAddCancel',
    /** 添加取消 - 之后 */
    afterAddCancel: 'afterCrudAddCancel',
    /** 编辑取消 - 之前 */
    beforeEditCancel: 'beforeCrudEditCancel',
    /** 编辑取消 - 之后 */
    afterEditCancel: 'afterCrudEditCancel',
    /** 提交 - 之前 */
    beforeSubmit: 'beforeCrudSubmitCU',
    /** 提交 - 之后 */
    afterSubmit: 'afterCrudSubmitCU',
    afterAddError: 'afterCrudAddError',
    afterEditError: 'afterCrudEditError'
  }
  
  /**
   * CRUD状态
   */
  CRUD.STATUS = {
    NORMAL: 0,
    PREPARED: 1,
    PROCESSING: 2
  }
  
  /**
   * CRUD通知类型
   */
  CRUD.NOTIFICATION_TYPE = {
    SUCCESS: 'success',
    WARNING: 'warning',
    INFO: 'info',
    ERROR: 'error'
  }
  
  export default CRUD
  
  export {
    presenter,
    header,
    form,
    pagination,
    crud
  }
  