File size: 3,224 Bytes
8059bf0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/**
 * 导航加载状态组合式函数
 * 管理路由切换时的加载状态,支持防闪烁逻辑
 */
import { ref, readonly, computed } from 'vue'

/**
 * 导航加载状态管理
 *
 * 功能:
 * 1. 在路由切换时显示加载状态
 * 2. 快速导航(< 100ms)不显示加载指示器(防闪烁)
 * 3. 导航取消时正确重置状态
 */
export function useNavigationLoading() {
  // 内部加载状态
  const _isLoading = ref(false)

  // 导航开始时间(用于防闪烁计算)
  let navigationStartTime: number | null = null

  // 防闪烁延迟计时器
  let showLoadingTimer: ReturnType<typeof setTimeout> | null = null

  // 是否应该显示加载指示器(考虑防闪烁逻辑)
  const shouldShowLoading = ref(false)

  // 防闪烁延迟时间(毫秒)
  const ANTI_FLICKER_DELAY = 100

  /**
   * 清理计时器
   */
  const clearTimer = (): void => {
    if (showLoadingTimer !== null) {
      clearTimeout(showLoadingTimer)
      showLoadingTimer = null
    }
  }

  /**
   * 导航开始时调用
   */
  const startNavigation = (): void => {
    navigationStartTime = Date.now()
    _isLoading.value = true

    // 延迟显示加载指示器,实现防闪烁
    clearTimer()
    showLoadingTimer = setTimeout(() => {
      if (_isLoading.value) {
        shouldShowLoading.value = true
      }
    }, ANTI_FLICKER_DELAY)
  }

  /**
   * 导航结束时调用
   */
  const endNavigation = (): void => {
    clearTimer()
    _isLoading.value = false
    shouldShowLoading.value = false
    navigationStartTime = null
  }

  /**
   * 导航取消时调用(比如快速连续点击不同链接)
   */
  const cancelNavigation = (): void => {
    clearTimer()
    // 保持加载状态,因为新的导航会立即开始
    // 但重置导航开始时间
    navigationStartTime = null
  }

  /**
   * 重置所有状态(用于测试)
   */
  const resetState = (): void => {
    clearTimer()
    _isLoading.value = false
    shouldShowLoading.value = false
    navigationStartTime = null
  }

  /**
   * 获取导航持续时间(毫秒)
   */
  const getNavigationDuration = (): number | null => {
    if (navigationStartTime === null) {
      return null
    }
    return Date.now() - navigationStartTime
  }

  // 公开的加载状态(只读)
  const isLoading = computed(() => shouldShowLoading.value)

  // 内部加载状态(用于测试,不考虑防闪烁)
  const isNavigating = readonly(_isLoading)

  return {
    isLoading,
    isNavigating,
    startNavigation,
    endNavigation,
    cancelNavigation,
    resetState,
    getNavigationDuration,
    // 导出常量用于测试
    ANTI_FLICKER_DELAY
  }
}

// 创建单例实例,供全局使用
let navigationLoadingInstance: ReturnType<typeof useNavigationLoading> | null = null

export function useNavigationLoadingState() {
  if (!navigationLoadingInstance) {
    navigationLoadingInstance = useNavigationLoading()
  }
  return navigationLoadingInstance
}

// 导出重置函数(用于测试)
export function _resetNavigationLoadingInstance(): void {
  if (navigationLoadingInstance) {
    navigationLoadingInstance.resetState()
  }
  navigationLoadingInstance = null
}