因为vue2不支持hook写法,但又希望和vue3中类似用hook实现封装公共逻辑,故写了一个class类实现其逻辑,只是没办法在类中使用生命周期和监听。 后期可添加一些导入导出等逻辑的封装
使用方法
data(){
return{
table:{},
options: {
throttleInterval:1000,
loadingKeep:400,
params: () => this.searchData,
onBefore:(params)=>{ xxx },
onSuccess:(data,params)=>{ xxxx },
onError: ()=> {},
onRes: ()=> {}
},
}
}
created() {
this.table = new PaginationTable(getQuerySamplePoolPage, this.options)
// this.table上面挂载了一些数据和方法便可直接使用到组件上 如下
// this.table.run() 查询
// this.table.refresh 查询且pageIndex重置为1
// this.table.data 表格数据
// this.table.loading 可直接用于v-loading="table.loading"
// this.table.pageIndex
// this.table.pageSize
// this.table.total
}
注意每个公司分页接口设置结构不一样,可自行调整 this.total等值
/*
* @fileName: PaginationTable.js
* @author: l
* @date: 2024-07-03 09:24:18
* @version:
* @description:分页接口(支持防抖,节流,轮询,loading,错误重试,自动请求/手动请求)
*/
import Pagination from './pagination.js'
import throttle from 'lodash/throttle'
import debounce from 'lodash/debounce'
import { cloneDeep } from 'lodash'
// interface IOptions {
// ready: Boolean; //(必填)只有当ready为true时,才会发起请求 (有些时候,可能会遇到网络请求相互依赖的情况。例如 B 请求的请求参数,依赖 A 请求的返回结果。这时可以使用 ready 来处理这种依赖关系)
// manual:Boolean, //(必填)默认情况下,在组件挂载时,自动触发绑定的请求。你可以通过传入 manual 选项,来禁止挂载时的默认请求,然后通过 run()
// hidePagination: Boolean, //是否有分页(默认有既然false)
// params?: Object | ((oldParams: R, options: any) => any); //获取接口参数() 例如 params: () => this.searchData
// dataPath?: String; //接口返回数据路径(默认data)
// pollingInterval?: Number //轮询间隔
// debounceInterval?:Number; //防抖模式,延迟的毫秒数
// throttleInterval?: Number; //节流时间间隔,默认开启,赋0就是关闭
// errorRetryCount: Number; //错误重试次数
// errorRetryInterval:Number; //错误重试间隔
// loadingKeep:Number; //loading状态持续时间(如果请求时间少于指定的时间,则最终时间为指定的时间.如果请求时间大于指定的时间,则最终时间为请求的时间) 默认400
// onSuccess?: (data: T, params: any) => void; //既onBefore
// onBefore?: (params: any) => void;
// onError?: (error: Error, params: any) => void;
// onRes?: (res: R, params: any) => any; //接口返回后返回res时候立即回调 用于修改res的回调(需要return res)
// mockRes?:()=>any; //模拟接口返回数据的函数,用于无接口时的模拟,优先级高于实际接口
// }
export class PaginationTable extends Pagination {
constructor(api, options) {
super(options.total || 0, options.pageSize || 20)
this._replenishOptionsDefaultOptions(options)
this.api = api
this.params = {} //接口参数存储
this.options = options
//前端页面使用
this.data = []
this.loading = false
this.polling = false //轮询状态
if (this.options?.manual && this.options?.ready) this.run() //默认加载数据
//轮询 (要手动销毁)
this.pollingInterval = null
if (this.options?.pollingInterval) this.startPolling(this.options.pollingInterval)
// 防抖
if (this.options?.debounceInterval) this.run = debounce(this.run.bind(this), this.options.debounceInterval)
//节流
if (this.options?.throttleInterval) this.run = throttle(this.run.bind(this), this.options.throttleInterval)
// 错误重试(次数)
if (typeof this.options.errorRetryCount == 'number' && this.options.errorRetryCount && this.options.errorRetryCount > 0) this.errorRetryCount = this.options.errorRetryCount
}
// 手动触发请求 extraParams对象
async run(extraParams = {}) {
if (!this.options || !this.options.ready) return // 只有ready为true时,才会发起请求
this.loading = true
const startTime = Date.now()
try {
await this._currentParams() // 生成当前接口的this.params
if (this.options?.onBefore) await this.options.onBefore(this.params, this.options)
// 接口参数(这是分页接口,参数一定是对象)
let tempParams = { ...this.params }
if (!this.options?.hidePagination) {
const page = { pageIndex: this.pageIndex, pageSize: this.pageSize }
tempParams = { ...tempParams, ...page }
}
tempParams = { ...tempParams, ...extraParams }
console.log('接口参数', this.params, tempParams)
let apiPage = this.options?.mockRes ? this.options.mockRes : this.api
let res = await apiPage(tempParams)
console.log('PaginationTable接口返回', res)
if (res?.code !== 0) return console.error(res.msg || '接口异常')
const dataKey = this.options?.dataPath || 'data'
if (this.options?.onRes) res[dataKey] = this.options.onRes(cloneDeep(res), this.options) || res[dataKey]
const data = res[dataKey]
if (!data || !Array.isArray(data)) return console.error('分页数据非数组')
this.data = data
console.log('接口请求成功', this.data)
if (!this.options?.hidePagination) this.total = Number(res?.count) || 0
if (this.options?.onSuccess) this.options.onSuccess(this.data)
if (this.options?.errorRetryCount) this._errorRetryReset() //放在最后
return res
} catch (error) {
if (this.options?.errorRetryCount && this.options?.errorRetryCount > 0) {
await this._errorRetry(error, this.params)
} else {
console.error(`接口请求异常==>${this.api}`, error)
if (this.options?.onError) this.options.onError(error, this.params)
this._resetData()
}
} finally {
if (!this.options?.loadingKeep) {
this.loading = false
} else {
const elapsedTime = Date.now() - startTime
const remainingTime = this.options?.loadingKeep - elapsedTime
if (remainingTime > 0) {
setTimeout(() => {
this.loading = false
}, remainingTime)
} else {
this.loading = false
}
}
}
}
//更新页面 pageIndex = 1
refresh() {
this.pageIndex = 1
this.run()
}
//补充额外的默认参数
_replenishOptionsDefaultOptions(options) {
const optionsKeys = Object.keys(options)
if (!optionsKeys.includes('manual')) options.manual = true
if (!optionsKeys.includes('ready')) options.ready = true
if (!optionsKeys.includes('throttleInterval')) options.throttleInterval = 1000
if (!optionsKeys.includes('loadingKeep')) options.loadingKeep = 400
}
// 重置数据
_resetData() {
this.data = []
this.total = 0
this.pageIndex = 1
}
// 接口报错
async _errorRetry(error, params) {
if (this.errorRetryCount <= 0) {
this._errorRetryReset()
this._resetData()
if (this.options?.onError) this.options.onError(error, this.params)
return console.error('接口请求异常', error)
} else {
console.error(`错误重试剩余次数:${this.errorRetryCount}`)
this.errorRetryCount--
console.log('0000000次数', this.errorRetryCount)
// 增加延迟 错误重试间隔
if (this.options.errorRetryInterval) await new Promise(resolve => setTimeout(resolve, this.options.errorRetryInterval))
return this.run(params)
}
}
// 错误重试次数重置
_errorRetryReset() {
this.errorRetryCount = this.options.errorRetryCount
}
async _currentParams() {
console.log('3333333444', this.options?.params)
if (!this.options?.params) return this.params
if (typeof this.options?.params == 'function') {
this.params = await this.options.params(this.params, this.options)
} else if (typeof this.options?.params == 'object') {
this.params = this.options.params
}
return this.params
}
// 是否可以发起请求状态修改
readyChange(bool) {
//false ==> true时候默认执行一次。true ==> true不执行run (防止run被多次调用)
if (!this.options.ready) {
this.options.ready = bool
if (bool) this.run()
} else {
this.options.ready = bool
}
}
// 开始轮询函数,interval指定轮询间隔,单位毫秒(启用后记得销毁轮询)
startPolling(interval = 5000) {
if (this.polling) return // 避免重复启动轮询
this.polling = true
this.pollingInterval = setInterval(() => {
this.run()
}, interval)
}
// 停止轮询函数
stopPolling() {
this.polling = false
if (this.pollingInterval) {
clearInterval(this.pollingInterval)
this.pollingInterval = null
}
}
}