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,
}
}