441 lines
14 KiB
Vue
441 lines
14 KiB
Vue
<template>
|
||
<div class="dialog-showcase">
|
||
<!-- 介绍区 -->
|
||
<section class="intro">
|
||
<p class="eyebrow">组件展示 · Dialog</p>
|
||
<h1>弹框组件设计指南</h1>
|
||
<p class="subtitle">
|
||
基于 Element Plus 弹框能力的二次封装,支持外层 +
|
||
内层嵌套弹框、统一的视觉样式和交互行为,
|
||
适合在复杂业务中复用,避免每个页面重复堆样式与逻辑。
|
||
</p>
|
||
</section>
|
||
|
||
<!-- 基础示例:外层弹框 -->
|
||
<section class="card-full">
|
||
<el-card shadow="hover" class="demo-card">
|
||
<template #header>
|
||
<div class="card-header">
|
||
<h3>基础弹框</h3>
|
||
<span>最常见的确认 / 信息展示场景</span>
|
||
</div>
|
||
</template>
|
||
|
||
<p class="card-description">
|
||
点击下方按钮打开基础弹框,外层弹框默认居中吸附,带有统一的头部样式、阴影和内容区域滚动处理。
|
||
</p>
|
||
|
||
<div class="demo-actions">
|
||
<ComButton type="primary" size="large" @click="openBaseDialog">
|
||
打开基础弹框
|
||
</ComButton>
|
||
</div>
|
||
|
||
<ComDialog
|
||
:dialog-config="baseDialog"
|
||
@closeDialogOuter="handleCloseBaseOuter"
|
||
@closeDialogInner="handleCloseBaseInner"
|
||
>
|
||
<template #outerContent>
|
||
<div class="dialog-content">
|
||
<h4 class="dialog-title">创建任务</h4>
|
||
<p class="dialog-subtitle">
|
||
在这里展示业务表单或提示文案,内容区域已处理最大高度和滚动,保证在小屏幕下也能完整展示。
|
||
</p>
|
||
<el-form label-width="80px" class="dialog-form">
|
||
<el-form-item label="任务名称">
|
||
<el-input
|
||
v-model="baseForm.name"
|
||
placeholder="请输入任务名称"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="优先级">
|
||
<el-select v-model="baseForm.level" placeholder="请选择">
|
||
<el-option label="普通" value="normal" />
|
||
<el-option label="重要" value="important" />
|
||
<el-option label="紧急" value="urgent" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="备注说明">
|
||
<el-input
|
||
v-model="baseForm.remark"
|
||
type="textarea"
|
||
:rows="3"
|
||
placeholder="可选填写,补充说明信息"
|
||
/>
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<div class="dialog-footer">
|
||
<ComButton plain @click="closeBaseDialog">取消</ComButton>
|
||
<ComButton type="primary" @click="submitBaseDialog">
|
||
确认提交
|
||
</ComButton>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</ComDialog>
|
||
</el-card>
|
||
</section>
|
||
|
||
<!-- 内外层嵌套弹框示例 -->
|
||
<section class="card-full">
|
||
<el-card shadow="hover" class="demo-card">
|
||
<template #header>
|
||
<div class="card-header">
|
||
<h3>内外层嵌套弹框</h3>
|
||
<span>适用于「详情 + 深层操作」的复杂流程</span>
|
||
</div>
|
||
</template>
|
||
|
||
<p class="card-description">
|
||
外层弹框承载主流程(如详情、编辑表单),当需要处理更深入的步骤(如高级设置、二次确认)时,
|
||
可以在外层内部再打开内层弹框,保持上下文不丢失。
|
||
</p>
|
||
|
||
<div class="demo-actions">
|
||
<ComButton type="primary" @click="openNestedOuter"> 打开嵌套弹框 </ComButton>
|
||
</div>
|
||
|
||
<ComDialog
|
||
:dialog-config="nestedDialog"
|
||
@closeDialogOuter="handleCloseNestedOuter"
|
||
@closeDialogInner="handleCloseNestedInner"
|
||
>
|
||
<!-- 外层弹框内容 -->
|
||
<template #outerContent>
|
||
<div class="dialog-content">
|
||
<h4 class="dialog-title">用户详情</h4>
|
||
<p class="dialog-subtitle">
|
||
外层用于展示主要信息和基础操作,点击「高级设置」将打开内层弹框,避免页面跳转打断当前上下文。
|
||
</p>
|
||
|
||
<el-descriptions :column="2" border class="dialog-descriptions">
|
||
<el-descriptions-item label="用户名">
|
||
bns_admin
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="角色">
|
||
超级管理员
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="部门"> 技术中心 </el-descriptions-item>
|
||
<el-descriptions-item label="状态">
|
||
<el-tag type="success">启用</el-tag>
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
|
||
<div class="dialog-footer">
|
||
<ComButton plain @click="closeNestedOuter">关闭</ComButton>
|
||
<ComButton type="primary" @click="openNestedInner">
|
||
打开高级设置
|
||
</ComButton>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- 内层弹框内容 -->
|
||
<template #innerContent>
|
||
<div class="dialog-content">
|
||
<h4 class="dialog-title">高级设置</h4>
|
||
<p class="dialog-subtitle">
|
||
内层弹框通常体量较小,用于配置少量但影响较大的选项,例如权限范围、敏感操作确认等。
|
||
</p>
|
||
|
||
<el-form label-width="90px" class="dialog-form">
|
||
<el-form-item label="数据权限">
|
||
<el-select
|
||
v-model="nestedForm.scope"
|
||
placeholder="请选择数据范围"
|
||
>
|
||
<el-option label="仅本人" value="self" />
|
||
<el-option label="本部门" value="dept" />
|
||
<el-option label="本部门及下级" value="dept_child" />
|
||
<el-option label="全部数据" value="all" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="登录保护">
|
||
<el-switch v-model="nestedForm.mfa" />
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<div class="dialog-footer">
|
||
<ComButton plain @click="closeNestedInner">取消</ComButton>
|
||
<ComButton type="primary" @click="submitNestedInner">
|
||
保存设置
|
||
</ComButton>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</ComDialog>
|
||
</el-card>
|
||
</section>
|
||
|
||
<!-- 使用建议 -->
|
||
<section class="card-full guide-card">
|
||
<el-card shadow="never">
|
||
<template #header>
|
||
<div class="card-header">
|
||
<h3>使用建议</h3>
|
||
<span>保持一致的交互体验与视觉规范</span>
|
||
</div>
|
||
</template>
|
||
<ul class="guide-list">
|
||
<li>尽量保证系统中所有弹框都通过 `ComDialog` 使用,统一头部样式与位置。</li>
|
||
<li>外层弹框承担主要流程,内层弹框仅在确有必要时使用,避免过深的弹框嵌套。</li>
|
||
<li>
|
||
推荐使用 `dialogConfig`
|
||
对象集中管理弹框状态与标题,避免在多个变量间来回切换。
|
||
</li>
|
||
<li>
|
||
关闭弹框时,结合 `closeDialogOuter / closeDialogInner`
|
||
事件重置表单、清理临时状态。
|
||
</li>
|
||
</ul>
|
||
</el-card>
|
||
</section>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup name="ShowDialog">
|
||
import { reactive } from 'vue'
|
||
import ComDialog from '@/components/ComDialog/index.vue'
|
||
import ComButton from '@/components/ComButton/index.vue'
|
||
|
||
// 基础弹框配置
|
||
const baseDialog = reactive({
|
||
outerVisible: false,
|
||
outerTitle: '基础弹框示例',
|
||
outerWidth: '720px',
|
||
minHeight: '320px',
|
||
maxHeight: '70vh',
|
||
// 以下字段用于兼容组件的内层结构(本示例未使用)
|
||
innerVisible: false,
|
||
})
|
||
|
||
const baseForm = reactive({
|
||
name: '',
|
||
level: 'normal',
|
||
remark: '',
|
||
})
|
||
|
||
const openBaseDialog = () => {
|
||
baseDialog.outerVisible = true
|
||
}
|
||
|
||
const closeBaseDialog = () => {
|
||
baseDialog.outerVisible = false
|
||
}
|
||
|
||
const submitBaseDialog = () => {
|
||
// 此处可替换为实际提交逻辑
|
||
console.log('提交基础弹框表单:', { ...baseForm })
|
||
baseDialog.outerVisible = false
|
||
}
|
||
|
||
const handleCloseBaseOuter = () => {
|
||
baseDialog.outerVisible = false
|
||
}
|
||
|
||
const handleCloseBaseInner = () => {
|
||
baseDialog.innerVisible = false
|
||
}
|
||
|
||
// 内外层嵌套弹框配置
|
||
const nestedDialog = reactive({
|
||
outerVisible: false,
|
||
outerTitle: '外层弹框 · 用户详情',
|
||
outerWidth: '760px',
|
||
minHeight: '360px',
|
||
maxHeight: '75vh',
|
||
innerVisible: false,
|
||
innerTitle: '内层弹框 · 高级设置',
|
||
innerWidth: '520px',
|
||
innerMinHeight: '260px',
|
||
innerMaxHeight: '60vh',
|
||
})
|
||
|
||
const nestedForm = reactive({
|
||
scope: 'dept_child',
|
||
mfa: true,
|
||
})
|
||
|
||
const openNestedOuter = () => {
|
||
nestedDialog.outerVisible = true
|
||
}
|
||
|
||
const closeNestedOuter = () => {
|
||
nestedDialog.outerVisible = false
|
||
nestedDialog.innerVisible = false
|
||
}
|
||
|
||
const openNestedInner = () => {
|
||
nestedDialog.innerVisible = true
|
||
}
|
||
|
||
const closeNestedInner = () => {
|
||
nestedDialog.innerVisible = false
|
||
}
|
||
|
||
const submitNestedInner = () => {
|
||
console.log('保存高级设置:', { ...nestedForm })
|
||
nestedDialog.innerVisible = false
|
||
}
|
||
|
||
const handleCloseNestedOuter = () => {
|
||
closeNestedOuter()
|
||
}
|
||
|
||
const handleCloseNestedInner = () => {
|
||
closeNestedInner()
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.dialog-showcase {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 32px;
|
||
padding: 32px 40px 48px;
|
||
background: linear-gradient(160deg, #f6f8ff 0%, #ffffff 55%, #f9fbff 100%);
|
||
min-height: 100%;
|
||
}
|
||
|
||
.intro {
|
||
max-width: 720px;
|
||
text-align: left;
|
||
}
|
||
|
||
.eyebrow {
|
||
font-size: 14px;
|
||
letter-spacing: 0.12em;
|
||
text-transform: uppercase;
|
||
color: #5c6aff;
|
||
margin-bottom: 12px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.intro h1 {
|
||
font-size: 32px;
|
||
font-weight: 700;
|
||
margin: 0 0 12px;
|
||
color: #1f2a56;
|
||
}
|
||
|
||
.subtitle {
|
||
font-size: 16px;
|
||
line-height: 1.7;
|
||
color: #4c5a7a;
|
||
margin: 0;
|
||
}
|
||
|
||
.card-full {
|
||
width: 100%;
|
||
}
|
||
|
||
.demo-card {
|
||
border-radius: 18px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.card-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
gap: 16px;
|
||
}
|
||
|
||
.card-header h3 {
|
||
margin: 0;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #1f2a56;
|
||
}
|
||
|
||
.card-header span {
|
||
font-size: 14px;
|
||
color: #8792b0;
|
||
}
|
||
|
||
.card-description {
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
color: #4c5a7a;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.demo-actions {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 16px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.dialog-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
}
|
||
|
||
.dialog-title {
|
||
margin: 0;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #111827;
|
||
}
|
||
|
||
.dialog-subtitle {
|
||
margin: 0;
|
||
font-size: 13px;
|
||
line-height: 1.6;
|
||
color: #6b7280;
|
||
}
|
||
|
||
.dialog-form {
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.dialog-descriptions {
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.dialog-footer {
|
||
margin-top: 12px;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 12px;
|
||
}
|
||
|
||
.guide-card :deep(.el-card__body) {
|
||
padding: 20px 28px 28px;
|
||
}
|
||
|
||
.guide-list {
|
||
margin: 0;
|
||
padding-left: 18px;
|
||
display: grid;
|
||
gap: 10px;
|
||
color: #4c5a7a;
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.guide-list li {
|
||
list-style: disc;
|
||
}
|
||
|
||
@media (max-width: 1024px) {
|
||
.dialog-showcase {
|
||
padding: 24px 24px 36px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
.intro h1 {
|
||
font-size: 26px;
|
||
}
|
||
|
||
.subtitle {
|
||
font-size: 15px;
|
||
}
|
||
}
|
||
</style>
|