You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1017 lines
42 KiB

<script setup>
const { $toast } = useNuxtApp();
const notifyAll = () => {
$toast('这是一条默认消息', { type: 'default' });
$toast.info('这是一条 info 消息');
$toast.success('这是一条 success 消息');
$toast.warning('这是一条 warning 消息');
$toast.error('这是一条 error 消息');
$toast.loading('加载中...');
};
const notifyDefault = () => $toast('默认消息 default');
const notifyInfo = () => $toast.info('Info 消息');
const notifySuccess = () => $toast.success('Success 消息');
const notifyWarning = () => $toast.warning('Warning 消息');
const notifyError = () => $toast.error('Error 消息');
// ─── Dialog 测试用例状态 ────────────────────────────
const basicShow = ref(false);
const bottomShow = ref(false);
const topShow = ref(false);
const noMaskCloseShow = ref(false);
const noAnimShow = ref(false);
const animSlideFadeShow = ref(false);
const animBottomFadeShow = ref(false);
const animTopFadeShow = ref(false);
const animLeftFadeShow = ref(false);
const animRightFadeShow = ref(false);
const maskFadeShow = ref(false);
const maskLeftShow = ref(false);
const maskRightShow = ref(false);
const maskTopShow = ref(false);
const maskBottomShow = ref(false);
const destroyOnCloseShow = ref(false);
const noCenterMarginShow = ref(false);
const placeholderShow = ref(false);
const phLoading = ref(false);
const inBoxShow = ref(false);
const parentShow = ref(false);
const childShow = ref(false);
const stopPropShow = ref(false);
const widthFixedShow = ref(false);
const widthPercentShow = ref(false);
const widthMaxMinShow = ref(false);
const widthStringShow = ref(false);
const closeCount = ref(0);
function onBasicClose() {
closeCount.value += 1;
$toast.info(`Dialog 关闭回调触发,累计 ${closeCount.value}`);
}
function onResetCount() {
closeCount.value = 0;
$toast.info('计数器已重置');
}
</script>
<template>
<div class="min-h-screen bg-[#faf9f5] text-[#3d3d3a]">
<div class="mx-auto max-w-5xl px-8 py-12 flex flex-col gap-12">
<!-- 标题区 -->
<header>
<h1 class="text-[36px] leading-[1.15] tracking-[-0.5px] font-normal text-[#141413] font-serif">
bolt-ui 组件测试
</h1>
<p class="mt-3 text-[16px] leading-[1.55] text-[#6c6a64]">
Toast / Dialog 组件行为验证页
</p>
</header>
<!-- Toast 测试 -->
<section class="flex flex-col gap-4">
<h2 class="text-[22px] font-medium text-[#141413]">Toast 测试</h2>
<div class="flex flex-col gap-4 max-w-md">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg hover:bg-[#a9583e] transition-colors w-fit"
@click="notifyAll"
>
全部类型
</button>
<div class="flex gap-2 flex-wrap">
<button
class="px-4 py-2 bg-[#efe9de] border border-[#e6dfd8] rounded-lg hover:bg-[#e8e0d2] transition-colors"
@click="notifyDefault"
>
Default
</button>
<button
class="px-4 py-2 bg-[#5db8a6] text-white rounded-lg hover:opacity-90 transition-opacity"
@click="notifyInfo"
>
Info
</button>
<button
class="px-4 py-2 bg-[#5db872] text-white rounded-lg hover:opacity-90 transition-opacity"
@click="notifySuccess"
>
Success
</button>
<button
class="px-4 py-2 bg-[#e8a55a] text-white rounded-lg hover:opacity-90 transition-opacity"
@click="notifyWarning"
>
Warning
</button>
<button
class="px-4 py-2 bg-[#c64545] text-white rounded-lg hover:opacity-90 transition-opacity"
@click="notifyError"
>
Error
</button>
<button
class="px-4 py-2 bg-[#181715] text-white rounded-lg hover:opacity-90 transition-opacity"
@click="$toast.loading('加载中...')"
>
Loading
</button>
</div>
</div>
</section>
<!-- Dialog 测试 -->
<section class="flex flex-col gap-6">
<div class="flex items-baseline justify-between">
<h2 class="text-[22px] font-medium text-[#141413]">Dialog 测试</h2>
<span class="text-[13px] text-[#6c6a64]">基于 bolt-ui &lt;BoDialog&gt;</span>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- 基础用法 -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">基础用法</h3>
<p class="text-[14px] text-[#6c6a64]">v-model:show 控制显示监听 close 事件</p>
<div class="flex gap-2 pt-1">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg hover:bg-[#a9583e] transition-colors text-[14px]"
@click="basicShow = true"
>
打开基础 Dialog
</button>
</div>
</div>
<!-- 位置模式 -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">位置模式</h3>
<p class="text-[14px] text-[#6c6a64]">center / bottom / top</p>
<div class="flex gap-2 pt-1 flex-wrap">
<button
class="px-4 py-2 bg-[#faf9f5] border border-[#e6dfd8] rounded-lg text-[14px] hover:bg-white"
@click="bottomShow = true"
>
底部弹出
</button>
<button
class="px-4 py-2 bg-[#faf9f5] border border-[#e6dfd8] rounded-lg text-[14px] hover:bg-white"
@click="topShow = true"
>
顶部弹出
</button>
</div>
</div>
<!-- 关闭控制 -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">关闭控制</h3>
<p class="text-[14px] text-[#6c6a64]">maskCanClose=false 禁止点击遮罩关闭</p>
<div class="flex gap-2 pt-1">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg hover:bg-[#a9583e] transition-colors text-[14px]"
@click="noMaskCloseShow = true"
>
强制弹窗
</button>
</div>
</div>
<!-- 关闭回调 -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">关闭回调</h3>
<p class="text-[14px] text-[#6c6a64]">累计触发次数: <span class="text-[#cc785c] font-medium">{{ closeCount }}</span></p>
<div class="flex gap-2 pt-1">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg hover:bg-[#a9583e] transition-colors text-[14px]"
@click="basicShow = true"
>
触发 Dialog
</button>
<button
class="px-4 py-2 bg-[#faf9f5] border border-[#e6dfd8] rounded-lg text-[14px] hover:bg-white"
@click="onResetCount"
>
重置计数
</button>
</div>
</div>
<!-- 动画 -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">动画</h3>
<p class="text-[14px] text-[#6c6a64]">:animation="false" 关闭所有过渡</p>
<div class="flex gap-2 pt-1 flex-wrap">
<button
class="px-4 py-2 bg-[#faf9f5] border border-[#e6dfd8] rounded-lg text-[14px] hover:bg-white"
@click="noAnimShow = true"
>
无动画
</button>
</div>
</div>
<!-- Dialog 动画方向 -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">Dialog 动画方向</h3>
<p class="text-[14px] text-[#6c6a64]">
animation 传入方向名: slide-fade / bottom-fade / top-fade / left-fade / right-fade
</p>
<div class="flex gap-2 pt-1 flex-wrap">
<button
class="px-3 py-1.5 bg-[#faf9f5] border border-[#e6dfd8] rounded text-[13px] hover:bg-white"
@click="animSlideFadeShow = true"
>
slide-fade
</button>
<button
class="px-3 py-1.5 bg-[#faf9f5] border border-[#e6dfd8] rounded text-[13px] hover:bg-white"
@click="animBottomFadeShow = true"
>
bottom-fade
</button>
<button
class="px-3 py-1.5 bg-[#faf9f5] border border-[#e6dfd8] rounded text-[13px] hover:bg-white"
@click="animTopFadeShow = true"
>
top-fade
</button>
<button
class="px-3 py-1.5 bg-[#faf9f5] border border-[#e6dfd8] rounded text-[13px] hover:bg-white"
@click="animLeftFadeShow = true"
>
left-fade
</button>
<button
class="px-3 py-1.5 bg-[#faf9f5] border border-[#e6dfd8] rounded text-[13px] hover:bg-white"
@click="animRightFadeShow = true"
>
right-fade
</button>
</div>
</div>
<!-- Mask 动画方向 -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">Mask 动画方向</h3>
<p class="text-[14px] text-[#6c6a64]">
maskAnimation 传入方向名: fade / left / right / top / bottom
</p>
<div class="flex gap-2 pt-1 flex-wrap">
<button
class="px-3 py-1.5 bg-[#faf9f5] border border-[#e6dfd8] rounded text-[13px] hover:bg-white"
@click="maskFadeShow = true"
>
fade
</button>
<button
class="px-3 py-1.5 bg-[#faf9f5] border border-[#e6dfd8] rounded text-[13px] hover:bg-white"
@click="maskLeftShow = true"
>
mask-left
</button>
<button
class="px-3 py-1.5 bg-[#faf9f5] border border-[#e6dfd8] rounded text-[13px] hover:bg-white"
@click="maskRightShow = true"
>
mask-right
</button>
<button
class="px-3 py-1.5 bg-[#faf9f5] border border-[#e6dfd8] rounded text-[13px] hover:bg-white"
@click="maskTopShow = true"
>
mask-top
</button>
<button
class="px-3 py-1.5 bg-[#faf9f5] border border-[#e6dfd8] rounded text-[13px] hover:bg-white"
@click="maskBottomShow = true"
>
mask-bottom
</button>
</div>
</div>
<!-- 销毁 -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">关闭后销毁</h3>
<p class="text-[14px] text-[#6c6a64]">destoryOnClose=true 关闭后销毁内容</p>
<div class="flex gap-2 pt-1">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg hover:bg-[#a9583e] transition-colors text-[14px]"
@click="destroyOnCloseShow = true"
>
打开可销毁 Dialog
</button>
</div>
</div>
<!-- placeholder 插槽 -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">placeholder 插槽</h3>
<p class="text-[14px] text-[#6c6a64]">
作为 <code class="text-[#cc785c]">bo-dialog__content</code> 的同级锚点
<code class="text-[#cc785c]">&lt;style scoped&gt;</code> 中通过兄弟选择器控制 dialog 内部样式
</p>
<div class="flex gap-2 pt-1">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg hover:bg-[#a9583e] transition-colors text-[14px]"
@click="placeholderShow = true"
>
查看 placeholder 用法
</button>
</div>
</div>
<!-- 容器内 -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">容器内模式 (inBox)</h3>
<p class="text-[14px] text-[#6c6a64]">Dialog 限定在容器范围内</p>
<div
class="relative mt-1 h-48 rounded-lg border border-[#e6dfd8] bg-[#faf9f5] overflow-hidden"
>
<div class="absolute inset-0 flex items-center justify-center">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg hover:bg-[#a9583e] transition-colors text-[14px]"
@click="inBoxShow = true"
>
在容器内打开
</button>
</div>
<BoDialog
v-model:show="inBoxShow"
in-box
mask-animation=""
>
<div class="rounded-lg bg-white p-6 shadow-lg border border-[#e6dfd8]">
<h4 class="text-[16px] font-medium text-[#141413]">内嵌 Dialog</h4>
<p class="mt-2 text-[14px] text-[#6c6a64]">这个 Dialog 被限制在父容器内不会铺满屏幕</p>
<div class="mt-4 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="inBoxShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
</div>
</div>
<!-- 嵌套 -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">嵌套 Dialog</h3>
<p class="text-[14px] text-[#6c6a64]">基于 DialogToken 识别父子父级 teleport 会被禁用</p>
<div class="flex gap-2 pt-1">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg hover:bg-[#a9583e] transition-colors text-[14px]"
@click="parentShow = true"
>
打开父级
</button>
</div>
</div>
<!-- stopPropagation -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">事件冒泡控制</h3>
<p class="text-[14px] text-[#6c6a64]">stopPropagation=false 时点击内容会冒泡到遮罩</p>
<div class="flex gap-2 pt-1">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg hover:bg-[#a9583e] transition-colors text-[14px]"
@click="stopPropShow = true"
>
不阻止冒泡
</button>
</div>
</div>
<!-- 边距控制 -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">居中边距</h3>
<p class="text-[14px] text-[#6c6a64]">noCenterYMargin=true 取消上下内边距</p>
<div class="flex gap-2 pt-1">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg hover:bg-[#a9583e] transition-colors text-[14px]"
@click="noCenterMarginShow = true"
>
无上下边距
</button>
</div>
</div>
<!-- 宽度固定 px -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">宽度 · 固定 px</h3>
<p class="text-[14px] text-[#6c6a64]">:style 对象传入 width: 600px</p>
<div class="flex gap-2 pt-1">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg hover:bg-[#a9583e] transition-colors text-[14px]"
@click="widthFixedShow = true"
>
600px
</button>
</div>
</div>
<!-- 宽度百分比 -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">宽度 · 百分比</h3>
<p class="text-[14px] text-[#6c6a64]">:style="{ width: '80%' }" 撑满父容器</p>
<div class="flex gap-2 pt-1">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg hover:bg-[#a9583e] transition-colors text-[14px]"
@click="widthPercentShow = true"
>
80%
</button>
</div>
</div>
<!-- 宽度max / min -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">宽度 · max / min</h3>
<p class="text-[14px] text-[#6c6a64]">min-width 320px, max-width 800px</p>
<div class="flex gap-2 pt-1">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg hover:bg-[#a9583e] transition-colors text-[14px]"
@click="widthMaxMinShow = true"
>
范围宽度
</button>
</div>
</div>
<!-- 宽度字符串 -->
<div class="rounded-xl bg-[#efe9de] p-6 flex flex-col gap-3">
<h3 class="text-[16px] font-medium text-[#141413]">宽度 · 字符串</h3>
<p class="text-[14px] text-[#6c6a64]">:style 接受 string 形式</p>
<div class="flex gap-2 pt-1">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg hover:bg-[#a9583e] transition-colors text-[14px]"
@click="widthStringShow = true"
>
字符串 style
</button>
</div>
</div>
</div>
</section>
</div>
<!-- Dialog 实例 -->
<!-- 基础 Dialog -->
<BoDialog v-model:show="basicShow" @close="onBasicClose">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8]">
<h3 class="text-[20px] font-medium text-[#141413]">基础 Dialog</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
这是最基础的 Dialog 用法使用 <code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">v-model:show</code> 绑定显示状态
点击遮罩或关闭按钮都可以关闭
</p>
<div class="mt-6 flex justify-end gap-2">
<button
class="px-4 py-2 bg-[#faf9f5] border border-[#e6dfd8] rounded-lg text-[14px] hover:bg-white"
@click="basicShow = false"
>
取消
</button>
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="basicShow = false"
>
确认
</button>
</div>
</div>
</BoDialog>
<!-- 底部弹出 -->
<BoDialog v-model:show="bottomShow" mode="bottom">
<div class="rounded-t-2xl bg-white p-6 shadow-xl border-t border-[#e6dfd8]">
<div class="w-10 h-1 bg-[#e6dfd8] rounded-full mx-auto mb-4" />
<h3 class="text-[20px] font-medium text-[#141413]">底部弹出</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
通过 <code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">mode="bottom"</code> Dialog 从底部弹出常用于移动端 ActionSheet
</p>
<div class="mt-6 flex flex-col gap-2">
<button
v-for="item in ['选项 A', '选项 B', '选项 C']"
:key="item"
class="w-full py-3 bg-[#faf9f5] border border-[#e6dfd8] rounded-lg text-[14px] hover:bg-white"
@click="$toast.info(`选择了 ${item}`); bottomShow = false"
>
{{ item }}
</button>
</div>
<button
class="mt-3 w-full py-3 text-[14px] text-[#6c6a64]"
@click="bottomShow = false"
>
取消
</button>
</div>
</BoDialog>
<!-- 顶部弹出 -->
<BoDialog v-model:show="topShow" mode="top">
<div class="rounded-b-2xl bg-white p-6 shadow-xl border-b border-[#e6dfd8]">
<h3 class="text-[20px] font-medium text-[#141413]">顶部弹出</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
<code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">mode="top"</code> 让 Dialog 从顶部滑入。
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="topShow = false"
>
知道了
</button>
</div>
</div>
</BoDialog>
<!-- 禁止点击遮罩关闭 -->
<BoDialog v-model:show="noMaskCloseShow" :mask-can-close="false">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[420px]">
<h3 class="text-[20px] font-medium text-[#141413]">强制阅读</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
设置 <code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">maskCanClose=false</code>,
点击遮罩不会关闭 Dialog,只能通过显式按钮关闭。
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="noMaskCloseShow = false"
>
我已阅读
</button>
</div>
</div>
</BoDialog>
<!-- 无动画 -->
<BoDialog v-model:show="noAnimShow" :animation="false">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[420px]">
<h3 class="text-[20px] font-medium text-[#141413]">无动画</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
<code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">:animation="false"</code> 关闭所有过渡动画。
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="noAnimShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- Dialog 动画方向 ──────────────────────────── -->
<!-- slide-fade(默认) -->
<BoDialog v-model:show="animSlideFadeShow" animation="slide-fade">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[420px]">
<h3 class="text-[20px] font-medium text-[#141413]">animation="slide-fade"</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
默认动画,<code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">animation=true</code> 时也使用这个。
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="animSlideFadeShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- bottom-fade -->
<BoDialog v-model:show="animBottomFadeShow" animation="bottom-fade">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[420px]">
<h3 class="text-[20px] font-medium text-[#141413]">animation="bottom-fade"</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
从底部上滑 + 淡入。注意:<code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">animation</code> 只影响过渡,
布局仍是 <code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">center</code>,要贴底需用
<code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">mode="bottom"</code>。
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="animBottomFadeShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- top-fade -->
<BoDialog v-model:show="animTopFadeShow" animation="top-fade">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[420px]">
<h3 class="text-[20px] font-medium text-[#141413]">animation="top-fade"</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
从顶部下滑 + 淡入。
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="animTopFadeShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- left-fade -->
<BoDialog v-model:show="animLeftFadeShow" animation="left-fade">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[420px]">
<h3 class="text-[20px] font-medium text-[#141413]">animation="left-fade"</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
从左侧右滑 + 淡入。适合侧边抽屉。
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="animLeftFadeShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- right-fade -->
<BoDialog v-model:show="animRightFadeShow" animation="right-fade">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[420px]">
<h3 class="text-[20px] font-medium text-[#141413]">animation="right-fade"</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
从右侧左滑 + 淡入。
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="animRightFadeShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- Mask 动画方向 ──────────────────────────── -->
<!-- mask-fade(默认) -->
<BoDialog v-model:show="maskFadeShow" mask-animation="mask-fade">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[420px]">
<h3 class="text-[20px] font-medium text-[#141413]">maskAnimation="mask-fade"</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
遮罩默认淡入淡出。
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="maskFadeShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- mask-left -->
<BoDialog v-model:show="maskLeftShow" mask-animation="mask-left">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[420px]">
<h3 class="text-[20px] font-medium text-[#141413]">maskAnimation="mask-left"</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
遮罩从左侧渐入渐出。常与 <code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">animation="left-fade"</code> 组合做抽屉。
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="maskLeftShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- mask-right -->
<BoDialog v-model:show="maskRightShow" mask-animation="mask-right">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[420px]">
<h3 class="text-[20px] font-medium text-[#141413]">maskAnimation="mask-right"</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
遮罩从右侧渐入渐出。
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="maskRightShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- mask-top -->
<BoDialog v-model:show="maskTopShow" mask-animation="mask-top">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[420px]">
<h3 class="text-[20px] font-medium text-[#141413]">maskAnimation="mask-top"</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
遮罩从顶部渐入渐出。
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="maskTopShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- mask-bottom -->
<BoDialog v-model:show="maskBottomShow" mask-animation="mask-bottom">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[420px]">
<h3 class="text-[20px] font-medium text-[#141413]">maskAnimation="mask-bottom"</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
遮罩从底部渐入渐出。
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="maskBottomShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- 关闭后销毁 -->
<BoDialog v-model:show="destroyOnCloseShow" destory-on-close>
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[420px]">
<h3 class="text-[20px] font-medium text-[#141413]">关闭后销毁</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
<code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">destoryOnClose</code> 会在关闭后销毁内容节点,
适合内容包含不可重用状态的场景。
</p>
<div class="mt-3 text-[13px] text-[#6c6a64]">
打开次数: <span class="text-[#cc785c] font-medium">{{ basicShow || destroyOnCloseShow ? '开启中' : '已销毁' }}</span>
</div>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="destroyOnCloseShow = false"
>
关闭并销毁
</button>
</div>
</div>
</BoDialog>
<!-- placeholder 插槽:与 bo-dialog__content 同级,作为 scoped CSS 钩子 -->
<BoDialog v-model:show="placeholderShow">
<template #placeholder>
<!--
placeholder 渲染在 BoDialog 内部的包裹层(position: fixed 全屏),
与 bo-dialog__content 是 DOM 同级,但不在居中的内容卡内。
因此它不适合做"贴在内容上的视觉装饰",正确的用法是:
在 <style scoped> 中作为兄弟选择器钩子,
借助 :deep() 穿透到 bo-dialog__content 内部控制样式。
这里把它收成 0 尺寸,仅保留 DOM 存在与状态类。
-->
<div class="ph-anchor" :class="{ 'is-loading': phLoading }" />
</template>
<div class="ph-body">
<h3 class="text-[20px] font-medium text-[#141413]">placeholder 插槽</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
placeholder 渲染在 <code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">bo-dialog__content</code> 的同级位置。
它本身在 dialog 包裹层(全屏)里而非内容卡里,视觉上难以直接附着。
因此它的正确用法是:作为 scoped CSS 钩子,通过兄弟选择器
<code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">.ph-anchor ~ :deep(.bo-dialog__content)</code>
影响 dialog 内部内容。
</p>
<div class="mt-4 flex items-center gap-3 text-[13px] text-[#6c6a64]">
<span>anchor 状态: <b class="text-[#141413]">{{ phLoading ? 'is-loading' : 'idle' }}</b></span>
<span class="text-[#8e8b82]">|</span>
<span>body 视觉: <b class="text-[#141413]">{{ phLoading ? '灰度 + 不可点' : '正常' }}</b></span>
</div>
<div class="mt-3">
<button
class="px-3 py-1.5 bg-[#faf9f5] border border-[#e6dfd8] rounded text-[13px] hover:bg-white"
@click="phLoading = !phLoading"
>
切换 anchor.is-loading
</button>
</div>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="placeholderShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- 无上下边距 -->
<BoDialog v-model:show="noCenterMarginShow" no-center-y-margin>
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[420px]">
<h3 class="text-[20px] font-medium text-[#141413]">无上下边距</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
<code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">noCenterYMargin</code> 取消居中模式下的 40px 上下内边距,
让 Dialog 紧贴屏幕边缘。
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="noCenterMarginShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- 嵌套 Dialog:父级 -->
<BoDialog v-model:show="parentShow">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[480px]">
<h3 class="text-[20px] font-medium text-[#141413]">父级 Dialog</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
在 Dialog 内部再打开 Dialog,组件会通过 DialogToken 自动识别父子关系:
子级 Dialog 的 teleport 会被禁用,与父级共同渲染。
</p>
<div class="mt-6 flex justify-end gap-2">
<button
class="px-4 py-2 bg-[#faf9f5] border border-[#e6dfd8] rounded-lg text-[14px] hover:bg-white"
@click="parentShow = false"
>
关闭父级
</button>
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="childShow = true"
>
打开子级
</button>
</div>
</div>
<!-- 嵌套子级 -->
<BoDialog v-model:show="childShow">
<div class="rounded-lg bg-[#faf9f5] p-6 shadow-xl border border-[#e6dfd8] w-[400px]">
<h3 class="text-[18px] font-medium text-[#141413]">子级 Dialog</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
我在父级 Dialog 内部父级不会因我打开而消失
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="childShow = false"
>
关闭子级
</button>
</div>
</div>
</BoDialog>
</BoDialog>
<!-- stopPropagation=false点击内容会触发遮罩关闭 -->
<BoDialog v-model:show="stopPropShow" :stop-propagation="false">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8] w-[420px]">
<h3 class="text-[20px] font-medium text-[#141413]">事件冒泡</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
<code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">stopPropagation=false</code>
点击内容区域会冒泡到遮罩触发关闭
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="stopPropShow = false"
>
主动关闭
</button>
</div>
</div>
</BoDialog>
<!-- 宽度固定 px覆盖默认 30% -->
<BoDialog v-model:show="widthFixedShow" :style="{ width: '600px' }">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8]">
<h3 class="text-[20px] font-medium text-[#141413]">固定 600px </h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
通过 <code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">:style="&#123; width: '600px' &#125;"</code>
覆盖默认的 30% 宽度
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="widthFixedShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- 宽度百分比 -->
<BoDialog v-model:show="widthPercentShow" :style="{ width: '80%' }">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8]">
<h3 class="text-[20px] font-medium text-[#141413]">80% </h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
<code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">width: '80%'</code> 撑满视口宽度的 80%
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="widthPercentShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- 宽度min / max -->
<BoDialog
v-model:show="widthMaxMinShow"
:style="{ minWidth: '320px', maxWidth: '800px', width: '100%' }"
>
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8]">
<h3 class="text-[20px] font-medium text-[#141413]">范围宽度</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
<code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">min-width: 320px; max-width: 800px</code>
在窄屏上不会小于 320px宽屏上不会超过 800px
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="widthMaxMinShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
<!-- 宽度字符串 -->
<BoDialog v-model:show="widthStringShow" style="width: 480px; max-width: 90vw;">
<div class="rounded-lg bg-white p-6 shadow-xl border border-[#e6dfd8]">
<h3 class="text-[20px] font-medium text-[#141413]">字符串 style</h3>
<p class="mt-3 text-[14px] text-[#3d3d3a] leading-[1.55]">
<code class="px-1 bg-[#efe9de] rounded text-[#cc785c]">style="width: 480px; max-width: 90vw;"</code>
也可以直接传字符串
</p>
<div class="mt-6 flex justify-end">
<button
class="px-4 py-2 bg-[#cc785c] text-white rounded-lg text-[14px] hover:bg-[#a9583e] transition-colors"
@click="widthStringShow = false"
>
关闭
</button>
</div>
</div>
</BoDialog>
</div>
</template>
<style scoped>
/* ─── placeholder 插槽 scoped 样式演示 ────────────────────
placeholder 元素本身收成 0 尺寸、不可见,
唯一作用是在 DOM 中提供与 .bo-dialog__content 同级的、带父作用域 data-v 的钩子。
*/
/* 1. placeholder 自身:收成 0 尺寸,保留可被 :class 切换的状态钩子 */
.ph-anchor {
position: absolute;
width: 0;
height: 0;
overflow: hidden;
pointer-events: none;
}
/* 2. 兄弟选择器穿透:placeholder 与 .bo-dialog__content 是同级 DOM。
is-loading 状态类触发 :deep 内部的样式,控制 dialog 内容视觉。 */
.ph-anchor.is-loading ~ :deep(.bo-dialog__content) .ph-body {
opacity: 0.5;
pointer-events: none;
filter: grayscale(0.5);
transition: all 0.25s ease;
}
</style>