2026-02-20 13:56:29 +08:00
|
|
|
|
<!--
|
|
|
|
|
|
PopupContainer V2 - 通用底部弹窗组件(采用 TransactionDetailSheet 样式风格)
|
|
|
|
|
|
|
|
|
|
|
|
## 与 V1 的区别
|
|
|
|
|
|
- V1 (PopupContainer.vue): 使用 Vant 主题变量,标准化布局,默认高度 80%
|
|
|
|
|
|
- V2 (PopupContainerV2.vue): 使用 Inter 字体,16px 圆角,纯白背景,更现代化的视觉风格
|
|
|
|
|
|
|
|
|
|
|
|
## 基础用法
|
|
|
|
|
|
<PopupContainerV2 v-model:show="show" title="标题">
|
|
|
|
|
|
<div class="content">内容区域</div>
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<van-button type="primary">确定</van-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</PopupContainerV2>
|
|
|
|
|
|
|
|
|
|
|
|
## Props
|
2026-02-20 14:57:19 +08:00
|
|
|
|
- show (Boolean, required): 控制弹窗显示/隐藏
|
2026-02-20 13:56:29 +08:00
|
|
|
|
- title (String, required): 标题文本
|
|
|
|
|
|
- height (String, default: 'auto'): 弹窗高度,支持 'auto', '80%', '500px' 等
|
|
|
|
|
|
- maxHeight (String, default: '85%'): 最大高度
|
|
|
|
|
|
|
|
|
|
|
|
## Slots
|
|
|
|
|
|
- default: 可滚动的内容区域(不提供默认 padding,由使用方控制)
|
|
|
|
|
|
- footer: 固定底部区域(操作按钮等)
|
|
|
|
|
|
|
|
|
|
|
|
## Events
|
2026-02-20 14:57:19 +08:00
|
|
|
|
- update:show: 弹窗显示/隐藏状态变更
|
2026-02-20 13:56:29 +08:00
|
|
|
|
-->
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<van-popup
|
|
|
|
|
|
v-model:show="visible"
|
|
|
|
|
|
position="bottom"
|
|
|
|
|
|
:style="{
|
|
|
|
|
|
height: height === 'auto' ? maxHeight : height,
|
|
|
|
|
|
borderTopLeftRadius: '16px',
|
|
|
|
|
|
borderTopRightRadius: '16px'
|
|
|
|
|
|
}"
|
|
|
|
|
|
teleport="body"
|
|
|
|
|
|
@close="handleClose"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="popup-container-v2">
|
|
|
|
|
|
<!-- 固定头部 -->
|
|
|
|
|
|
<div class="popup-header">
|
|
|
|
|
|
<h3 class="popup-title">
|
|
|
|
|
|
{{ title }}
|
|
|
|
|
|
</h3>
|
|
|
|
|
|
<van-icon
|
|
|
|
|
|
name="cross"
|
|
|
|
|
|
class="popup-close"
|
|
|
|
|
|
@click="handleClose"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 可滚动内容区域 -->
|
|
|
|
|
|
<div class="popup-content">
|
|
|
|
|
|
<slot />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 固定底部 -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-if="hasFooter"
|
|
|
|
|
|
class="popup-footer"
|
|
|
|
|
|
>
|
|
|
|
|
|
<slot name="footer" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</van-popup>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import { computed, useSlots } from 'vue'
|
|
|
|
|
|
|
|
|
|
|
|
const props = defineProps({
|
2026-02-20 14:57:19 +08:00
|
|
|
|
show: {
|
2026-02-20 13:56:29 +08:00
|
|
|
|
type: Boolean,
|
|
|
|
|
|
required: true
|
|
|
|
|
|
},
|
|
|
|
|
|
title: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
required: true
|
|
|
|
|
|
},
|
|
|
|
|
|
height: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: 'auto'
|
|
|
|
|
|
},
|
|
|
|
|
|
maxHeight: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: '85%'
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-02-20 14:57:19 +08:00
|
|
|
|
const emit = defineEmits(['update:show'])
|
2026-02-20 13:56:29 +08:00
|
|
|
|
|
|
|
|
|
|
const slots = useSlots()
|
|
|
|
|
|
|
|
|
|
|
|
// 双向绑定
|
|
|
|
|
|
const visible = computed({
|
2026-02-20 14:57:19 +08:00
|
|
|
|
get: () => props.show,
|
|
|
|
|
|
set: (value) => emit('update:show', value)
|
2026-02-20 13:56:29 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 判断是否有 footer 插槽
|
|
|
|
|
|
const hasFooter = computed(() => !!slots.footer)
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭弹窗
|
|
|
|
|
|
const handleClose = () => {
|
|
|
|
|
|
visible.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
|
.popup-container-v2 {
|
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 固定头部
|
|
|
|
|
|
.popup-header {
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
padding: 24px;
|
|
|
|
|
|
padding-bottom: 16px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
|
|
|
|
.popup-title {
|
|
|
|
|
|
font-family: Inter, sans-serif;
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #09090b;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.popup-close {
|
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
|
color: #71717a;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: opacity 0.2s;
|
|
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 可滚动内容区域
|
|
|
|
|
|
.popup-content {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
overflow-x: hidden;
|
|
|
|
|
|
-webkit-overflow-scrolling: touch;
|
|
|
|
|
|
// 不提供默认 padding,由使用方控制
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 固定底部
|
|
|
|
|
|
.popup-footer {
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
padding: 24px;
|
|
|
|
|
|
padding-top: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 暗色模式
|
|
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
|
|
|
|
.popup-container-v2 {
|
|
|
|
|
|
background: #18181b;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.popup-header {
|
|
|
|
|
|
.popup-title {
|
|
|
|
|
|
color: #fafafa;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.popup-close {
|
|
|
|
|
|
color: #a1a1aa;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|