`useDetailLoader` 组合函数封装(支持自动加载 + 页面判断 + 防重复)

🧩 useDetailLoader 组合函数封装(支持自动加载 + 页面判断 + 防重复)

✅ 功能特点

  • ✅ 自动监听 query.id(或指定字段)
  • ✅ 只在指定页面名称时才触发(避免跳转到其他页面时误触发)
  • ✅ 避免同一 id 重复加载
  • ✅ 支持初始加载和浏览器返回场景
  • ✅ 支持动态更新 loading 状态

📦 useDetailLoader.ts

// composables/useDetailLoader.ts
import { watch, ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'

interface UseDetailLoaderOptions {
  routeName: string            // 当前页面 name
  queryKey?: string            // 监听的 query 字段名,默认 'id'
  immediate?: boolean          // 是否立即加载
  fetch: (id: string) => Promise<any>  // 请求函数(返回 Promise)
  onSuccess?: (data: any) => void      // 成功回调
  onError?: (err: any) => void         // 失败回调
}

export function useDetailLoader(options: UseDetailLoaderOptions) {
  const route = useRoute()
  const idKey = options.queryKey || 'id'
  const currentId = ref<string | null>(null)
  const loading = ref(false)

  const load = async (id: string) => {
    if (!id || id === currentId.value) return
    currentId.value = id
    loading.value = true

    try {
      const data = await options.fetch(id)
      options.onSuccess?.(data)
    } catch (err) {
      options.onError?.(err)
      console.error('详情加载失败', err)
    } finally {
      loading.value = false
    }
  }

  // 初始加载
  onMounted(() => {
    const id = route.query[idKey]
    if (options.immediate && typeof id === 'string' && route.name === options.routeName) {
      load(id)
    }
  })

  // 监听 ID 变化
  watch(() => route.query[idKey], (newId) => {
    if (route.name !== options.routeName) return
    if (typeof newId === 'string') {
      load(newId)
    }
  })

  return {
    loading,
    currentId,
    reload: () => {
      const id = route.query[idKey]
      if (typeof id === 'string') {
        load(id)
      }
    }
  }
}

🧪 示例用法:详情页中加载业绩数据

const detailInfo = ref({})
const detailLoading = ref(false)

const { loading, reload } = useDetailLoader({
  routeName: 'PerformanceDetail',
  fetch: async (id) => {
    const res = await getManagerPerformanceInfo({ userId: id })
    return res.data
  },
  onSuccess: (data) => {
    detailInfo.value = data || {}
    currencySymbol.value = data.exchange || '¥'
  },
  onError: () => {
    ElMessage.error('获取详情失败')
  },
  immediate: true
})

🧠 搭配列表请求也可以:

const performanceList = ref([])
const getList = async (id: string) => {
  const res = await getPerformanceOrderList({ userId: id, ...queryParams.value })
  performanceList.value = res.rows || []
}

useDetailLoader({
  routeName: 'PerformanceDetail',
  fetch: async (id) => {
    await getList(id)
    return null // 不需要 onSuccess
  },
  immediate: true
})

🧩 支持的配置项一览

参数类型说明
routeNamestring当前页面的路由名称
queryKeystring监听哪个 query 字段(默认 id
immediateboolean是否在首次进入时立即加载
fetch(id) => Promise<any>数据获取函数
onSuccess(data) => void请求成功时回调
onError(err) => void请求失败时回调

📚 总结

useDetailLoader 是一个用于详情页按需监听参数加载的通用 Hook,具备以下优势:

  • ✅ 自动监听,避免重复加载
  • ✅ 不受非当前页跳转影响
  • ✅ 可读性高,方便团队复用
  • ✅ 易于拓展支持 params、动态参数等
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值