美文网首页
crud增删改查文件——自定义钩子函数

crud增删改查文件——自定义钩子函数

作者: yichen_china | 来源:发表于2021-12-13 17:55 被阅读0次
import { initData,download} from '@/api/data'
import { parseTime,downloadFile} from '@/utils/index'
import Vue from 'vue'
/** CRUD配置 */
function CRUD(options){ 
    const defaultOptions = { 
        tag:'default',
        //id字段名
        idFiled:'id',
        //标题
        title:'',
        //请求数据的url
        url:'',
        //表格数据
        data:[],
        //选择项
        selections:[],
        //待查询的对象
        query:{ },
        //查询数据的参数
        params:{ },
        //form表单
        form:{ },
        //重置表单
        defaultForm:()=>{ },
        //排序规则,默认id降序
        sort:['id,desc'],
        //等待时间
        time:50,
        //CRUD Method
        crudMethod:{ 
            add:(form)=>{ },
            del:(id)=>{ },
            edit:(form)=>{ },
            get:(id)=>{ }
        },
        //主页操作栏显示哪些按钮
        optShow:{ 
            add:true,
            edit:true,
            del:true,
            download:true,
            reset: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 ? `新增${ crud.title}` : this.edit > CRUD.STATUS.NORMAL ? `编辑${ crud.title}` : crud.title
          }
        },
        msg: { 
          submit: '提交成功',
          add: '新增成功',
          edit: '编辑成功',
          del: '删除成功'
        },
        page: { 
          // 页码
          page: 0,
          // 每页数据条数
          size: 10,
          // 总数据条数
          total: 0
        },
        // 整体loading
        loading: false,
        // 导出的 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() { 
          // console.log('点击了搜索按钮')
          crud.page.page = 1
          crud.refresh()
        },
        // 刷新
        refresh() { 
          if (!callVmHook(crud, CRUD.HOOK.beforeRefresh)) { 
            return
          }
          return new Promise((resolve, reject) => { 
            crud.loading = true
            // 请求数据
            initData(crud.url, crud.getQueryParams()).then(data => { 
              crud.page.total = data.totalElements
              crud.data = data.content || data.data.acticleDtos
              crud.resetDataStatus()
              // time 毫秒后显示表格
              setTimeout(() => { 
                crud.loading = false
                callVmHook(crud, CRUD.HOOK.afterRefresh)
              }, crud.time)
              resolve(data)
            }).catch(err => { 
              crud.loading = false
              reject(err)
            })
          })
        },
        /** * 启动添加 */
        toAdd() { 
          crud.resetForm()
          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(crud.getDataId(data)).edit = CRUD.STATUS.PREPARED
          callVmHook(crud, CRUD.HOOK.afterToEdit, crud.form)
          callVmHook(crud, CRUD.HOOK.afterToCU, crud.form)
        },
        /** * 启动删除 * @param {*} data 数据项 */
        toDelete(data) { 
          crud.getDataStatus(crud.getDataId(data)).delete = CRUD.STATUS.PREPARED
        },
        /** * 取消删除 * @param {*} data 数据项 */
        cancelDelete(data) { 
          if (!callVmHook(crud, CRUD.HOOK.beforeDeleteCancel, data)) { 
            return
          }
          crud.getDataStatus(crud.getDataId(data)).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.getDataId(crud.form)).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.status.add = CRUD.STATUS.PROCESSING
          crud.crudMethod.add(crud.form).then(() => { 
            crud.status.add = CRUD.STATUS.NORMAL
            crud.resetForm()
            crud.addSuccessNotify()
            callVmHook(crud, CRUD.HOOK.afterSubmit)
            crud.toQuery()
          }).catch(() => { 
            crud.status.add = CRUD.STATUS.PREPARED
            callVmHook(crud, CRUD.HOOK.afterAddError)
          })
        },
        /** * 执行编辑 */
        doEdit() { 
          if (!callVmHook(crud, CRUD.HOOK.beforeSubmit)) { 
            return
          }
          crud.status.edit = CRUD.STATUS.PROCESSING
          crud.crudMethod.edit(crud.form).then(() => { 
            crud.status.edit = CRUD.STATUS.NORMAL
            crud.getDataStatus(crud.getDataId(crud.form)).edit = CRUD.STATUS.NORMAL
            crud.editSuccessNotify()
            crud.resetForm()
            callVmHook(crud, CRUD.HOOK.afterSubmit)
            crud.refresh()
          }).catch(() => { 
            crud.status.edit = CRUD.STATUS.PREPARED
            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(this.getDataId(val))
            })
          } else { 
            ids.push(this.getDataId(data))
            dataStatus = crud.getDataStatus(this.getDataId(data))
          }
          if (!callVmHook(crud, CRUD.HOOK.beforeDelete, data)) { 
            return
          }
          if (!delAll) { 
            dataStatus.delete = CRUD.STATUS.PROCESSING
          }
          return crud.crudMethod.del(ids).then(() => { 
            if (delAll) { 
              crud.delAllLoading = false
            } else dataStatus.delete = CRUD.STATUS.PREPARED
            crud.dleChangePage(1)
            crud.delSuccessNotify()
            callVmHook(crud, CRUD.HOOK.afterDelete, data)
            crud.refresh()
          }).catch(() => { 
            if (delAll) { 
              crud.delAllLoading = false
            } else dataStatus.delete = CRUD.STATUS.PREPARED
          })
        },
        /** * 通用导出 */
        doExport() { 
          crud.downloadLoading = true
          download(crud.url + '/download', crud.getQueryParams()).then(result => { 
            downloadFile(result, crud.title + '数据', 'xlsx')
            crud.downloadLoading = false
          }).catch(() => { 
            crud.downloadLoading = false
          })
        },
        /** * 获取查询参数 */
        getQueryParams: function () { 
          // 清除参数无值的情况
          Object.keys(crud.query).length !== 0 && Object.keys(crud.query).forEach(item => { 
            if (crud.query[item] === null || crud.query[item] === '') crud.query[item] = undefined
          })
          Object.keys(crud.params).length !== 0 && Object.keys(crud.params).forEach(item => { 
            if (crud.params[item] === null || crud.params[item] === '') crud.params[item] = undefined
          })
          return { 
            page: crud.page.page - 1,
            size: crud.page.size,
            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
          // console.log(val)
        },
        /** * 重置查询参数 * @param {Boolean} toQuery 重置后进行查询操作 */
        resetQuery(toQuery = true) { 
          const defaultQuery = JSON.parse(JSON.stringify(crud.defaultQuery))
          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.apply(crud.findVM('form')))
          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[crud.getDataId(e)] = { 
                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 this.getDataId(val) === this.getDataId(row) })) { 
            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)
        },
        getDataId(data) { 
          if (!data.hasOwnProperty(this.idField)) { 
            console.error('[CRUD error]: no property [%s] in %o', this.idField, data)
          }
          return data[this.idField]
        },
        attchTable() { 
          const table = this.findVM('presenter').$refs.table
          const columns = []
          table.columns.forEach((e, index) => { 
            if (!e.property || e.type !== 'default') { 
              return
            }
            e.__index = index
            columns.push({ 
              property: e.property,
              index,
              label: e.label,
              visible: true
            })
          })
          this.updateProp('tableColumns', columns)
          this.updateProp('table', table)
        }
      }
      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
          }
          if (index < 4) {  // 内置预留vm数
            this.vms[index] = vmObj
            return
          }
          this.vms.length = Math.max(this.vms.length, index)
          this.vms.splice(index, 1, vmObj)
        },
        /** * 取消注册组件实例 * @param {*} vm 组件实例 */
        unregisterVM(type, vm) { 
          for (let i = this.vms.length - 1; i >= 0; i--) { 
            if (this.vms[i] === undefined) { 
              continue
            }
            if (this.vms[i].type === type && this.vms[i].vm === vm) { 
              if (i < 4) {  // 内置预留vm数
                this.vms[i] = undefined
              } else { 
                this.vms.splice(i, 1)
              }
              break
            }
          }
        }
      })
      // 冻结处理,需要扩展数据的话,使用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)
      }
      const tagHook = crud.tag ? hook + '$' + crud.tag : null
      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
        }
        if (tagHook && vm[tagHook]) { 
          ret = vm[tagHook].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 * @param {string} tag */
    function lookupCrud(vm, tag) { 
      tag = tag || vm.$attrs['crud-tag'] || 'default'
      // function lookupCrud(vm, tag) { 
      if (vm.$crud) { 
        const ret = vm.$crud[tag]
        if (ret) { 
          return ret
        }
      }
      return vm.$parent ? lookupCrud(vm.$parent, tag) : undefined
    }
    /** * crud主页 */
    function presenter(crud) { 
      if (crud) { 
        console.warn('[CRUD warn]: ' + 'please use $options.cruds() { return CRUD(...) or [CRUD(...), ...] }')
      }
      return { 
        data() { 
          // 在data中返回crud,是为了将crud与当前实例关联,组件观测crud相关属性变化
          return { 
            crud: this.crud
          }
        },
        beforeCreate() { 
          this.$crud = this.$crud || { }
          let cruds = this.$options.cruds instanceof Function ? this.$options.cruds() : crud
          if (!(cruds instanceof Array)) { 
            cruds = [cruds]
          }
          cruds.forEach(ele => { 
            if (this.$crud[ele.tag]) { 
              console.error('[CRUD error]: ' + 'crud with tag [' + ele.tag + ' is already exist')
            }
            this.$crud[ele.tag] = ele
            ele.registerVM('presenter', this, 0)
          })
          this.crud = this.$crud['defalut'] || cruds[0]
        },
        methods: { 
          parseTime
        },
        created() { 
          for (const k in this.$crud) { 
            if (this.$crud[k].queryOnPresenterCreated) { 
              this.$crud[k].toQuery()
            }
          }
        },
        destroyed() { 
          for (const k in this.$crud) { 
            this.$crud[k].unregisterVM('presenter', this)
          }
        },
        mounted() { 
          // 如果table未实例化(例如使用了v-if),请稍后在适当时机crud.attchTable刷新table信息
          if (this.$refs.table !== undefined) { 
            this.crud.attchTable()
          }
        }
      }
    }
    /** * 头部 */
    function header() { 
      return { 
        data() { 
          return { 
            crud: this.crud,
            query: this.crud.query
          }
        },
        beforeCreate() { 
          this.crud = lookupCrud(this)
          this.crud.registerVM('header', this, 1)
        },
        destroyed() { 
          this.crud.unregisterVM('header', this)
        }
      }
    }
    /** * 分页 */
    function pagination() { 
      return { 
        data() { 
          return { 
            crud: this.crud,
            page: this.crud.page
          }
        },
        beforeCreate() { 
          this.crud = lookupCrud(this)
          this.crud.registerVM('pagination', this, 2)
        },
        destroyed() { 
          this.crud.unregisterVM('pagination', this)
        }
      }
    }
    /** * 表单 */
    function form(defaultForm) { 
      return { 
        data() { 
          return { 
            crud: this.crud,
            form: this.crud.form
          }
        },
        beforeCreate() { 
          this.crud = lookupCrud(this)
          this.crud.registerVM('form', this, 3)
        },
        created() { 
          this.crud.defaultForm = defaultForm
          this.crud.resetForm()
        },
        destroyed() { 
          this.crud.unregisterVM('form', this)
        }
      }
    }
    /** * crud */
    function crud(options = { }) { 
      const defaultOptions = { 
        type: undefined
      }
      options = mergeOptions(defaultOptions, options)
      return { 
        data() { 
          return { 
            crud: this.crud
          }
        },
        beforeCreate() { 
          this.crud = lookupCrud(this)
          this.crud.registerVM(options.type, this)
        },
        destroyed() { 
          this.crud.unregisterVM(options.type, 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
    }

相关文章

网友评论

      本文标题:crud增删改查文件——自定义钩子函数

      本文链接:https://www.haomeiwen.com/subject/mpfsfrtx.html