PlanPopupNew.vue
5.09 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
204
205
<!--
* @Date: 2026-02-08
* @Description: 计划书弹窗容器组件(支持全局弹窗管理器)
-->
<template>
<nut-popup
:visible="visible"
position="bottom"
round
:style="{ height: '88%' }"
:close-on-click-overlay="true"
:safe-area-inset-bottom="true"
@update:visible="handleVisibleChange"
>
<div :class="containerClasses">
<!-- Header -->
<div class="flex justify-between items-center px-5 py-4 bg-white border-b border-gray-100 flex-shrink-0">
<span class="text-lg font-bold text-gray-900">{{ title }}</span>
<div class="p-2 -mr-2" @click="handleClose">
<IconFont name="close" size="16" color="#9CA3AF" />
</div>
</div>
<!-- Scrollable Content -->
<div class="flex-1 overflow-y-auto p-4">
<div class="bg-white rounded-xl p-5 shadow-sm">
<slot></slot>
</div>
</div>
<!-- Footer Buttons -->
<div
v-show="showFooter"
class="p-4 bg-white border-t border-gray-100 flex-shrink-0 pb-safe"
>
<!-- 未找到模板:只显示关闭按钮 -->
<nut-button
v-if="!hasTemplate"
type="primary"
color="#2563EB"
block
class="w-full !h-[88rpx] !rounded-[16rpx] !text-[30rpx]"
@click="handleClose"
>
关闭
</nut-button>
<!-- 找到模板:显示取消/生成计划书按钮 -->
<div v-else class="flex gap-3 w-full">
<nut-button
plain
type="primary"
class="flex-1 !h-[88rpx] !rounded-[16rpx] !text-[30rpx] !border-blue-600"
@click="handleClose"
>
取消
</nut-button>
<nut-button
type="primary"
color="#2563EB"
class="flex-1 !h-[88rpx] !rounded-[16rpx] !text-[30rpx]"
@click="handleSubmit"
>
生成计划书
</nut-button>
</div>
</div>
</div>
</nut-popup>
</template>
<script setup>
/**
* @description 录入计划书弹窗容器组件(支持全局弹窗管理器)
* @description 自动监听子弹窗状态,隐藏/显示底部按钮
* @param {boolean} visible - 控制弹窗显示隐藏
* @param {string} title - 弹窗标题
* @emits update:visible - 更新 visible 状态
* @emits close - 关闭弹窗
* @emits submit - 提交表单
* @author Claude Code
* @version 2.0.0 - 支持全局弹窗管理器
*/
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
import IconFont from '@/components/icons/IconFont.vue'
import { useParentPopup } from './PlanFields/GlobalPopupManager.js'
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
title: {
type: String,
default: '计划书',
},
/**
* 是否找到模板
* @description 如果未找到模板,只显示"关闭"按钮;否则显示"取消/生成计划书"按钮
* @type {boolean}
*/
hasTemplate: {
type: Boolean,
default: true,
},
})
const emit = defineEmits(['update:visible', 'close', 'submit'])
/**
* 底部按钮显示状态
* @type {Ref<boolean>}
*/
const showFooter = ref(true)
/**
* 容器动态类名
* @description 当有子弹窗时,移除 overflow-hidden 避免裁剪
* @type {ComputedRef<string>}
*/
const containerClasses = computed(() => {
const baseClasses = 'h-full flex flex-col bg-gray-50 rounded-t-2xl'
// 当有活动子弹窗时,使用 overflow-visible 允许子弹窗溢出
return hasActiveChildPopup.value
? `${baseClasses} overflow-visible`
: `${baseClasses} overflow-hidden`
})
/**
* 使用父弹窗管理器
*/
const { registerFooterCallback, hasActiveChildPopup } = useParentPopup()
/**
* 取消注册回调函数
* @type {Function|null}
*/
let unregisterFooterCallback = null
/**
* 组件挂载时注册回调
*/
onMounted(() => {
// 注册回调,当子弹窗打开/关闭时自动隐藏/显示底部按钮
unregisterFooterCallback = registerFooterCallback((shouldShowFooter) => {
showFooter.value = shouldShowFooter
})
// 初始化时检查是否已有活动弹窗
if (hasActiveChildPopup.value) {
showFooter.value = false
}
})
/**
* 组件卸载时取消注册
*/
onUnmounted(() => {
if (unregisterFooterCallback) {
unregisterFooterCallback()
}
})
/**
* 监听全局弹窗状态变化
* 当子弹窗打开/关闭时,自动隐藏/显示底部按钮
*/
watch(hasActiveChildPopup, (isActive) => {
showFooter.value = !isActive
})
// 处理 visible 变化事件
const handleVisibleChange = (value) => {
emit('update:visible', value)
if (!value) {
// 重置底部按钮显示状态
showFooter.value = true
emit('close')
}
}
const handleClose = () => {
emit('update:visible', false)
emit('close')
}
const handleSubmit = () => {
emit('submit')
}
</script>
<style lang="less">
:deep(.nut-popup) {
border-top-left-radius: 16px;
border-top-right-radius: 16px;
background-color: #F9FAFB;
}
/* 适配底部安全区 */
.pb-safe {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
</style>