GlobalPopupManager.js
4.54 KB
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/**
* 全局弹窗管理器
*
* @description 管理嵌套弹窗的层级和显示状态,解决弹窗遮挡问题
* @module GlobalPopupManager
* @author Claude Code
* @version 2.0.0
*/
import { ref, computed } from 'vue'
/**
* 全局状态:当前活动的弹窗列表
* @type {Ref<string[]>}
*/
const activePopups = ref([])
/**
* 是否有活动的子弹窗
* @type {ComputedRef<boolean>}
*/
const hasActiveChildPopup = computed(() => activePopups.value.length > 0)
/**
* 弹窗计数器(用于生成唯一 ID)
* @type {number}
*/
let popupCounter = 0
/**
* 父弹窗回调函数列表(全局共享)
* @type {Function[]}
*/
const parentPopupCallbacks = []
/**
* 注册父弹窗(用于 PlanPopupNew)
*
* @description 提供给父弹窗使用的 composable
* @returns {Object} 父弹窗控制方法
*
* @example
* const { registerFooterCallback, hasActiveChildPopup } = useParentPopup()
*
* // 注册回调,当子弹窗打开时自动隐藏父级 footer
* const unregister = registerFooterCallback((shouldShowFooter) => {
* showFooter.value = shouldShowFooter
* })
*/
export function useParentPopup() {
/**
* 注册底部按钮回调
*
* @param {Function} callback - 回调函数,接收 shouldShowFooter 参数
* @returns {Function} 取消注册函数
*/
const registerFooterCallback = (callback) => {
parentPopupCallbacks.push(callback)
// 返回取消注册函数
return () => {
const index = parentPopupCallbacks.indexOf(callback)
if (index > -1) {
parentPopupCallbacks.splice(index, 1)
}
}
}
/**
* 通知所有回调
*
* @param {boolean} shouldShowFooter - 是否显示底部按钮
*/
const notifyCallbacks = (shouldShowFooter) => {
parentPopupCallbacks.forEach(callback => callback(shouldShowFooter))
}
return {
registerFooterCallback,
hasActiveChildPopup,
notifyCallbacks
}
}
/**
* 注册全局弹窗(用于 DatePickerGlobal, SelectPickerGlobal 等)
*
* @description 提供给需要全局管理的弹窗组件使用
* @returns {Object} 弹窗控制方法
*
* @example
* const { registerPopup, activatePopup, deactivatePopup } = useGlobalPopup()
*
* // 组件挂载时注册
* const popupId = ref(null)
* onMounted(() => {
* popupId.value = registerPopup()
* })
*
* // 弹窗打开时激活
* activatePopup(popupId.value)
*
* // 弹窗关闭时停用
* deactivatePopup(popupId.value)
*/
export function useGlobalPopup() {
/**
* 注册弹窗
*
* @description 生成唯一的弹窗 ID
* @returns {string} 弹窗 ID(格式:popup-1, popup-2, ...)
*/
const registerPopup = () => {
popupCounter++
return `popup-${popupCounter}`
}
/**
* 激活弹窗
*
* @description 将弹窗添加到活动列表,触发父弹窗隐藏底部按钮
* @param {string} popupId - 弹窗 ID
*/
const activatePopup = (popupId) => {
if (!activePopups.value.includes(popupId)) {
activePopups.value.push(popupId)
// 通知所有父弹窗隐藏底部按钮
parentPopupCallbacks.forEach((callback) => {
callback(false)
})
}
}
/**
* 停用弹窗
*
* @description 从活动列表中移除弹窗,触发父弹窗显示底部按钮
* @param {string} popupId - 弹窗 ID
*/
const deactivatePopup = (popupId) => {
const index = activePopups.value.indexOf(popupId)
if (index > -1) {
activePopups.value.splice(index, 1)
// 如果没有其他活动弹窗了,通知所有父弹窗显示底部按钮
if (activePopups.value.length === 0) {
parentPopupCallbacks.forEach((callback) => {
callback(true)
})
}
}
}
return {
registerPopup,
activatePopup,
deactivatePopup,
hasActiveChildPopup
}
}
/**
* 注册子弹窗(旧接口,向后兼容)
*
* @description 提供给子弹窗使用的 composable
* @param {Function} notifyParent - 通知父弹窗的函数
* @returns {Object} 子弹窗控制方法
*/
export function useChildPopup(notifyParent) {
/**
* 子弹窗打开时通知父弹窗
*/
const notifyParentOpen = () => {
if (notifyParent) {
notifyParent(false)
}
}
/**
* 子弹窗关闭时通知父弹窗
*/
const notifyParentClose = () => {
if (notifyParent) {
notifyParent(true)
}
}
return {
notifyParentOpen,
notifyParentClose
}
}
/**
* 重置所有弹窗状态
*
* @description 用于测试或异常情况下的状态重置
*/
export function resetPopupState() {
activePopups.value = []
popupCounter = 0
}