import type { TopicList, UserListConfig, UserListsInit, UseTabList, UseTabListsInit } from '@/types' import ls from 'store2' export function useGlobal() { const ins = getCurrentInstance()! const ctx = ins.appContext.config.globalProperties const options = ins.type const route = useRoute() const router = useRouter() const globalStore = useGlobalStore() return { ctx, options, route, router, globalStore, } } /** * 竞态锁 * @param fn 回调函数 * @param autoUnlock 是否自动解锁 * @description * ``` * autoUnlock === true 不管 fn 返回什么, 都自动解锁 * autoUnlock === false 不管 fn 返回什么, 都不自动解锁 * autoUnlock === 'auto' 当 fn 返回 false 时, 不自动解锁, 返回其他值时, 自动解锁 * ``` * @example * ``` * const Fn = useLockFn(async (key) => { * console.log(key) * } * *
* ``` */ export function useLockFn(fn: AnyFn, autoUnlock: boolean | 'auto' = 'auto') { const lock = ref(false) return async (...args: any[]) => { if (lock.value) { return } lock.value = true try { const $return: any = await fn(...args) if (autoUnlock === true || (autoUnlock === 'auto' && $return !== false)) { lock.value = false } } catch (e) { lock.value = false throw e } } } export function useSaveScroll() { const ins = getCurrentInstance() const route = useRoute() let name: string | undefined = '' if (ins) { name = ins.type.name } onActivated(() => { if (name) { const body = document.querySelector(`.${name}`) if (body) { const scrollTop = ls.get(route.fullPath) || 0 body.scrollTo(0, scrollTop) ls.remove(route.fullPath) } } }) onBeforeRouteLeave((_to, from, next) => { const body = document.querySelector('.body') if (body) { ls.set(from.fullPath, body.scrollTop || 0) } next() }) } /** * 单列表封装 * @param init { api: 接口封装 } */ export function useLists(init: UserListsInit) { const globalStore = useGlobalStore() const body = $ref()! const res: UserListConfig = reactive({ ...init, timer: null, isLoaded: false, // 列表数据 ==> page: 1, dataList: [], // <==列表数据 config: { // 下拉刷新 ==> isLoading: false, isRefresh: false, // <==下拉刷新 // 滚动加载 ==> loadStatus: 'loadmore', isLock: false, loading: false, error: false, finished: false, // <==滚动加载 }, }) /** * 请求列表接口 */ const getList = async () => { if (res.config.isLock) { return } res.config.isLock = true // 异步更新数据 res.timer = setTimeout(() => { globalStore.$patch({ routerLoading: true }) }, 500) // 第一页时不显示loading if (res.page > 1) { res.config.loading = true } const { data, code } = await $api[init.api.method]>(init.api.url, { ...init.api.config, page: res.page }) // 500毫秒内已经加载完成数据, 则清除定时器, 不再显示路由loading if (res.timer) { clearTimeout(res.timer) } globalStore.$patch({ routerLoading: false }) res.isLoaded = true if (code === 200) { // 如果是下拉刷新 或者是第1页, 则只保留当前数据 if (res.config.isRefresh || res.page === 1) { res.dataList = [...data.list] res.config.isRefresh = false } else { res.dataList = res.dataList.concat(data.list) } await nextTick() // 加载状态结束 res.config.loading = false // 数据全部加载完成 if (!data.hasNext) { res.config.finished = true res.config.loadStatus = 'nomore' } else { res.config.loadStatus = 'loadmore' res.page += 1 } res.config.isLock = false } else { res.config.error = true } } /** * 刷新接口 */ const onRefresh = async () => { res.config.isRefresh = true res.page = 1 await getList() res.config.isLoading = false } /** * 触底回调 */ const reachBottom = () => { if (res.config.loadStatus === 'nomore' || res.config.loadStatus === 'loading') { return } res.config.loadStatus = 'loading' getList() } const lazyLoading = () => { // 滚动到底部,再加载的处理事件 const scrollTop = body.scrollTop const clientHeight = body.clientHeight const scrollHeight = body.scrollHeight if (scrollTop + clientHeight >= scrollHeight - 300) { reachBottom() } } return { ...toRefs(res), body, getList, onRefresh, reachBottom, lazyLoading, } } /** * Tab接口列表 * @param init { api: 接口封装 } */ export function useTabLists(init: UseTabListsInit) { const { options, globalStore } = useGlobal() const body = $ref()! const res: UseTabList = reactive({ ...init, timer: null, // 列表数据 ==> list: Array.from({ length: 5 }, () => '').map(() => ({ page: 1, items: [], refreshing: false, loading: false, error: false, finished: false, })), // <==列表数据 }) const activeIndex = ref(0) const getList = async (index: number) => { const list: TopicList = JSON.parse(JSON.stringify(res.list[index])) if (list.page === 1) { const body = document.querySelector(`.${options.name}`) if (body) body.scrollTo(0, 0) } // 500毫秒数据还没请求完成, 显示路由loading res.timer = setTimeout(() => { globalStore.$patch({ routerLoading: true }) }, 500) // 第一页直接用路由loading if (list.page === 1) { list.loading = false } // 异步更新数据 const { method, url, config } = res.api[index] const { code, data } = await $api[method as Methods]>(url, { ...config, page: list.page }) // 500毫秒内已经加载完成数据, 则清除定时器, 不再显示路由loading if (res.timer) { clearTimeout(res.timer) } globalStore.$patch({ routerLoading: false }) if (code === 200) { // 如果是下拉刷新, 则只保留当前数据 if (list.refreshing) { list.items = [...data.list] list.refreshing = false } else { list.items = list.items.concat(data.list) } await nextTick() // 加载状态结束 list.loading = false // 数据全部加载完成 if (!data.hasNext) { list.finished = true } else { list.page += 1 } } else { list.error = true } res.list.splice(index, 1, list) } const onRefresh = async (index: number) => { res.list[index].refreshing = true res.list[index].page = 1 await getList(index) res.list[index].refreshing = false showMsg('刷新成功') } return { res, body, getList, onRefresh, activeIndex, } }