index.vue
3.47 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
<!--
* @Date: 2026-01-31 12:49:11
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2026-02-09 20:22:23
* @FilePath: /manulife-weapp/src/components/PlanPopup/index.vue
* @Description: 文件描述
-->
<template>
<nut-popup
:visible="visible"
position="bottom"
round
:style="{ height: '90%' }"
:close-on-click-overlay="true"
:safe-area-inset-bottom="true"
:catch-move="true"
@update:visible="handleVisibleChange"
>
<div class="h-full flex flex-col bg-gray-50 overflow-hidden rounded-t-2xl">
<!-- 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="childPopupCount === 0"
class="p-4 bg-white border-t border-gray-100 flex gap-3 flex-shrink-0 pb-safe"
>
<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>
</nut-popup>
</template>
<script setup>
/**
* @description 录入计划书弹窗容器组件
* @param {boolean} visible - 控制弹窗显示隐藏
* @param {string} title - 弹窗标题
* @emits update:visible - 更新 visible 状态
* @emits close - 关闭弹窗
* @emits submit - 提交表单
*/
import { defineProps, defineEmits, ref, watch, provide } from 'vue';
import IconFont from '@/components/IconFont.vue';
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
title: {
type: String,
default: '计划书',
},
});
const emit = defineEmits(['update:visible', 'close', 'submit']);
/**
* 子弹窗计数器
* @description 用于跟踪有多少个子弹窗打开,> 0 时隐藏底部按钮
*/
const childPopupCount = ref(0);
/**
* 处理子弹窗打开事件
*/
const handleChildOpen = () => {
childPopupCount.value++;
};
/**
* 处理子弹窗关闭事件
*/
const handleChildClose = () => {
if (childPopupCount.value > 0) {
childPopupCount.value--;
}
};
// Provide 子弹窗控制函数给所有后代组件
provide('popupControl', {
open: handleChildOpen,
close: handleChildClose
})
// 处理 visible 变化事件
const handleVisibleChange = (value) => {
emit('update:visible', value);
if (!value) {
// 重置子弹窗计数器
childPopupCount.value = 0;
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>