当前位置:   article > 正文

Vue3对话框(Dialog)_vue3 dialog

vue3 dialog

Vue2对话框(Dialog)

可自定义设置以下属性:

  • 标题(title),类型:string | slot,默认 '提示'

  • 内容(content),类型:string | slot,默认 ''

  • 宽度(width),类型:number,单位px,默认 540px

  • 高度(height),类型:number|string,默认 'auto',自适应内容高度

  • 是否允许切换全屏(switchFullscreen),允许后右上角会出现一个按钮,类型:boolean,默认 false

  • 取消按钮文字(cancelText),类型:string,默认 '取消'

  • 确认按钮文字(okText),类型:string,默认 '确认'

  • 是否显示底部按钮(footer),类型:boolean,默认 false

  • 是否水平垂直居中(center),类型:boolean,默认 true,(false时是固定高度水平居中)

  • 固定高度水平居中时,距顶部高度(top),类型:number,默认 100px

  • 加载中(loading),类型:boolean,默认 false

  • 对话框 body 样式(bodyStyle),类型:CSSProperties,默认 {}

  • 对话框是否可见(visible),类型:boolean,默认 false

效果如下图:在线预览(整体样式模仿ant-design-vue Modal,同时阴影覆盖浏览器窗口)

其中引入使用了Vue3加载中(Spin)Vue3按钮(Button) 

①创建对话框组件Dialog.vue: 

  1. <script setup lang="ts">
  2. import Spin from '../spin'
  3. import Button from '../button'
  4. import { ref, watch, computed } from 'vue'
  5. import type { CSSProperties } from 'vue'
  6. interface Props {
  7. title?: string // 标题 string | slot
  8. content?: string // 内容 string | slot
  9. width?: number // 宽度,单位px
  10. height?: number|string // 高度,单位px,默认auto,自适应内容高度
  11. switchFullscreen?: boolean // 是否允许切换全屏,允许后右上角会出现一个按钮
  12. cancelText?: string // 取消按钮文字
  13. okText?: string // 确定按钮文字
  14. footer?: boolean // 是否显示底部按钮,默认不显示
  15. center?: boolean // 水平垂直居中:true 固定高度水平居中:false
  16. top?: number // 固定高度水平居中时,距顶部高度
  17. loading?: boolean // 加载中
  18. bodyStyle?: CSSProperties // 对话框 body 样式
  19. visible?: boolean // 是否可见
  20. }
  21. const props = withDefaults(defineProps<Props>(), {
  22. title: '提示',
  23. content: '',
  24. width: 540,
  25. height: 'auto',
  26. switchFullscreen: false,
  27. cancelText: '取消',
  28. okText: '确定',
  29. footer: false,
  30. center: true,
  31. top: 100,
  32. loading: false,
  33. bodyStyle: () => ({}),
  34. visible: false
  35. })
  36. const fullScreen = ref(false)
  37. const dialogHeight = computed(() => {
  38. if (typeof props.height === 'number') {
  39. return props.height + 'px'
  40. } else {
  41. return props.height
  42. }
  43. })
  44. watch(
  45. () => props.visible,
  46. (to) => {
  47. if (to) { // 重置全屏显示
  48. fullScreen.value = false
  49. }
  50. }
  51. )
  52. const emits = defineEmits(['close', 'cancel', 'ok'])
  53. function onBlur () {
  54. if (!props.loading) {
  55. emits('close')
  56. }
  57. }
  58. function onFullScreen () {
  59. fullScreen.value = !fullScreen.value
  60. }
  61. function onClose () {
  62. emits('close')
  63. }
  64. function onCancel () {
  65. emits('cancel')
  66. }
  67. function onConfirm () {
  68. emits('ok')
  69. }
  70. </script>
  71. <template>
  72. <div class="m-dialog-root">
  73. <Transition name="mask">
  74. <div v-show="visible" class="m-dialog-mask"></div>
  75. </Transition>
  76. <Transition>
  77. <div v-show="visible" class="m-dialog-wrap" @click.self="onBlur">
  78. <div
  79. ref="dialog"
  80. :class="['m-dialog', center ? 'relative-hv-center' : 'top-center']"
  81. :style="`width: ${fullScreen ? '100%' : props.width + 'px'}; top: ${center ? '50%' : (fullScreen ? 0 : top + 'px')};`">
  82. <div
  83. class="m-dialog-content"
  84. :class="{loading: loading}"
  85. :style="`--height: ${fullScreen ? '100vh' : dialogHeight}`">
  86. <Spin class="u-spin" :spinning="loading" size="small" />
  87. <div class="m-dialog-header">
  88. <p class="u-head">
  89. <slot name="title">{{ title }}</slot>
  90. </p>
  91. </div>
  92. <span class="m-screen" @click="onFullScreen" v-if="switchFullscreen">
  93. <svg v-show="!fullScreen" class="u-svg" viewBox="64 64 896 896" data-icon="fullscreen" aria-hidden="true" focusable="false"><path d="M290 236.4l43.9-43.9a8.01 8.01 0 0 0-4.7-13.6L169 160c-5.1-.6-9.5 3.7-8.9 8.9L179 329.1c.8 6.6 8.9 9.4 13.6 4.7l43.7-43.7L370 423.7c3.1 3.1 8.2 3.1 11.3 0l42.4-42.3c3.1-3.1 3.1-8.2 0-11.3L290 236.4zm352.7 187.3c3.1 3.1 8.2 3.1 11.3 0l133.7-133.6 43.7 43.7a8.01 8.01 0 0 0 13.6-4.7L863.9 169c.6-5.1-3.7-9.5-8.9-8.9L694.8 179c-6.6.8-9.4 8.9-4.7 13.6l43.9 43.9L600.3 370a8.03 8.03 0 0 0 0 11.3l42.4 42.4zM845 694.9c-.8-6.6-8.9-9.4-13.6-4.7l-43.7 43.7L654 600.3a8.03 8.03 0 0 0-11.3 0l-42.4 42.3a8.03 8.03 0 0 0 0 11.3L734 787.6l-43.9 43.9a8.01 8.01 0 0 0 4.7 13.6L855 864c5.1.6 9.5-3.7 8.9-8.9L845 694.9zm-463.7-94.6a8.03 8.03 0 0 0-11.3 0L236.3 733.9l-43.7-43.7a8.01 8.01 0 0 0-13.6 4.7L160.1 855c-.6 5.1 3.7 9.5 8.9 8.9L329.2 845c6.6-.8 9.4-8.9 4.7-13.6L290 787.6 423.7 654c3.1-3.1 3.1-8.2 0-11.3l-42.4-42.4z"></path></svg>
  94. <svg v-show="fullScreen" class="u-svg" viewBox="64 64 896 896" data-icon="fullscreen-exit" aria-hidden="true" focusable="false"><path d="M391 240.9c-.8-6.6-8.9-9.4-13.6-4.7l-43.7 43.7L200 146.3a8.03 8.03 0 0 0-11.3 0l-42.4 42.3a8.03 8.03 0 0 0 0 11.3L280 333.6l-43.9 43.9a8.01 8.01 0 0 0 4.7 13.6L401 410c5.1.6 9.5-3.7 8.9-8.9L391 240.9zm10.1 373.2L240.8 633c-6.6.8-9.4 8.9-4.7 13.6l43.9 43.9L146.3 824a8.03 8.03 0 0 0 0 11.3l42.4 42.3c3.1 3.1 8.2 3.1 11.3 0L333.7 744l43.7 43.7A8.01 8.01 0 0 0 391 783l18.9-160.1c.6-5.1-3.7-9.4-8.8-8.8zm221.8-204.2L783.2 391c6.6-.8 9.4-8.9 4.7-13.6L744 333.6 877.7 200c3.1-3.1 3.1-8.2 0-11.3l-42.4-42.3a8.03 8.03 0 0 0-11.3 0L690.3 279.9l-43.7-43.7a8.01 8.01 0 0 0-13.6 4.7L614.1 401c-.6 5.2 3.7 9.5 8.8 8.9zM744 690.4l43.9-43.9a8.01 8.01 0 0 0-4.7-13.6L623 614c-5.1-.6-9.5 3.7-8.9 8.9L633 783.1c.8 6.6 8.9 9.4 13.6 4.7l43.7-43.7L824 877.7c3.1 3.1 8.2 3.1 11.3 0l42.4-42.3c3.1-3.1 3.1-8.2 0-11.3L744 690.4z"></path></svg>
  95. </span>
  96. <span class="m-close" @click="onClose">
  97. <svg class="u-svg" viewBox="64 64 896 896" data-icon="close" aria-hidden="true" focusable="false"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg>
  98. </span>
  99. <div class="m-dialog-body" :style="bodyStyle">
  100. <slot>{{ content }}</slot>
  101. </div>
  102. <div class="m-dialog-footer" v-show="footer">
  103. <Button class="mr8" @click="onCancel">{{ cancelText }}</Button>
  104. <Button type="primary" @click="onConfirm">{{ okText }}</Button>
  105. </div>
  106. </div>
  107. </div>
  108. </div>
  109. </Transition>
  110. </div>
  111. </template>
  112. <style lang="less" scoped>
  113. .mask-enter-active, .mask-leave-active {
  114. transition: opacity .25s;
  115. }
  116. .mask-enter-from, .mask-leave-to {
  117. opacity: 0;
  118. }
  119. .v-enter-active, .v-leave-active {
  120. transition: all .25s;
  121. }
  122. .v-enter-from, .v-leave-to {
  123. opacity: 0;
  124. transform: scale(0);
  125. }
  126. .flex-hv-center { // 水平垂直居中方法①:弹性布局,随内容增大高度,并自适应水平垂直居中
  127. display: flex;
  128. justify-content: center;
  129. align-items: center;
  130. }
  131. .relative-hv-center { // 水平垂直居中方法②:相对定位,随内容增大高度,并自适应水平垂直居中
  132. position: relative;
  133. top: 50%;
  134. transform: translateY(-50%);
  135. }
  136. .top-center { // 相对定位,固定高度,始终距离视图顶端100px
  137. position: relative;
  138. // top: 100px;
  139. }
  140. .m-dialog-mask {
  141. position: fixed;
  142. top: 0;
  143. right: 0;
  144. bottom: 0;
  145. left: 0;
  146. width: 100%;
  147. height: 100%;
  148. z-index: 1000;
  149. background: rgba(0, 0, 0, .45);
  150. }
  151. .m-dialog-wrap {
  152. position: fixed;
  153. top: 0;
  154. inset-inline-end: 0;
  155. bottom: 0;
  156. inset-inline-start: 0;
  157. overflow: auto;
  158. outline: 0;
  159. inset: 0;
  160. z-index: 1010;
  161. .m-dialog {
  162. margin: 0 auto;
  163. transition: all .25s;
  164. .loading { // 加载过程背景虚化
  165. background: rgb(248, 248, 248) !important;
  166. pointer-events: none; // 屏蔽鼠标事件
  167. }
  168. .m-dialog-content {
  169. display: flex;
  170. flex-direction: column;
  171. height: var(--height);
  172. position: relative;
  173. background-color: #fff;
  174. border-radius: 8px;
  175. box-shadow: 0 6px 16px 0 rgba(0, 0, 0, .08), 0 3px 6px -4px rgba(0, 0, 0, .12), 0 9px 28px 8px rgba(0, 0, 0, .05);
  176. padding: 20px 24px;
  177. transition: all .25s;
  178. .u-spin {
  179. position: absolute;
  180. inset: 0;
  181. margin: auto;
  182. }
  183. .m-dialog-header {
  184. color: rgba(0, 0, 0, .88);
  185. background: transparent;
  186. border-radius: 8px 8px 0 0;
  187. margin-bottom: 8px;
  188. max-width: calc(100% - 54px);
  189. .u-head {
  190. margin: 0;
  191. color: rgba(0, 0, 0, .88);
  192. font-weight: 600;
  193. font-size: 16px;
  194. line-height: 1.5;
  195. word-break: break-all;
  196. }
  197. }
  198. .m-screen {
  199. .m-close();
  200. inset-inline-end: 48px;
  201. }
  202. .m-close {
  203. position: absolute;
  204. top: 17px;
  205. inset-inline-end: 17px;
  206. z-index: 1010;
  207. font-weight: 600;
  208. line-height: 1;
  209. background: transparent;
  210. border-radius: 4px;
  211. width: 22px;
  212. height: 22px;
  213. cursor: pointer;
  214. transition: background .2s;
  215. display: flex;
  216. align-items: center;
  217. justify-content: center;
  218. .u-svg {
  219. display: inline-block;
  220. width: 16px;
  221. height: 16px;
  222. line-height: 22px;
  223. fill: rgba(0, 0, 0, .45);
  224. cursor: pointer;
  225. transition: fill .2s;
  226. }
  227. &:hover {
  228. background: rgba(0, 0, 0, .06);
  229. .u-svg {
  230. fill: rgba(0, 0, 0, .88);
  231. }
  232. }
  233. }
  234. .m-dialog-body {
  235. flex: 1;
  236. font-size: 14px;
  237. color: rgba(0, 0, 0, .88);
  238. line-height: 1.5714285714285714;
  239. word-break: break-all;
  240. overflow: auto;
  241. transition: all .25s;
  242. }
  243. .m-dialog-footer {
  244. text-align: end;
  245. background: transparent;
  246. margin-top: 12px;
  247. .mr8 {
  248. margin-inline-end: 8px;
  249. }
  250. }
  251. }
  252. }
  253. }
  254. </style>

②在要使用的页面引入:

  1. <script setup lang="ts">
  2. import Dialog from './Dialog.vue'
  3. import { ref } from 'vue'
  4. const visible1 = ref(false)
  5. const visible2 = ref(false)
  6. const visible3 = ref(false)
  7. const visible4 = ref(false)
  8. const visible5 = ref(false)
  9. const visible6 = ref(false)
  10. const loading = ref(false)
  11. function showDialog () {
  12. visible1.value = true
  13. }
  14. function showCustomHeightDialog () {
  15. visible2.value = true
  16. }
  17. function showFooterDialog () {
  18. visible3.value = true
  19. }
  20. function showFixPositionDialog () {
  21. visible4.value = true
  22. }
  23. function showFullScreenDialog () {
  24. visible5.value = true
  25. }
  26. function showCustomHBodyDialog () {
  27. visible6.value = true
  28. }
  29. function onClose () { // 关闭回调
  30. visible1.value = false
  31. visible2.value = false
  32. visible3.value = false
  33. visible4.value = false
  34. visible5.value = false
  35. visible6.value = false
  36. }
  37. function onCancel () { // “取消”按钮回调
  38. visible3.value = false
  39. }
  40. function onConfirm () { // “确定”,“知道了”按钮回调
  41. loading.value = true // 开启加载状态
  42. setTimeout(() => {
  43. visible3.value = false
  44. loading.value = false
  45. }, 500)
  46. }
  47. </script>
  48. <template>
  49. <div>
  50. <h1>Dialog 对话框</h1>
  51. <h2 class="mt30 mb10">基本使用</h2>
  52. <Space :size="16">
  53. <Button type="primary" @click="showDialog">默认对话框</Button>
  54. <Dialog :visible="visible1" @close="onClose">
  55. <template #title>Title</template>
  56. <p>Bla bla ...</p>
  57. <p>Bla bla ...</p>
  58. <p>Bla bla ...</p>
  59. </Dialog>
  60. <Button type="primary" @click="showCustomHeightDialog">内容高度自定义</Button>
  61. <Dialog
  62. :height="360"
  63. @close="onClose"
  64. :visible="visible2">
  65. <template #title>Title</template>
  66. <p>Bla bla ...</p>
  67. <p>Bla bla ...</p>
  68. <p>Bla bla ...</p>
  69. </Dialog>
  70. <Button type="primary" @click="showFooterDialog">有底部按钮</Button>
  71. <Dialog
  72. footer
  73. @close="onClose"
  74. @cancel="onCancel"
  75. @ok="onConfirm"
  76. :loading="loading"
  77. :visible="visible3">
  78. <template #title>Title</template>
  79. <p>Bla bla ...</p>
  80. <p>Bla bla ...</p>
  81. <p>Bla bla ...</p>
  82. </Dialog>
  83. <Button type="primary" @click="showFixPositionDialog">位置高度自定义</Button>
  84. <Dialog
  85. :center="false"
  86. :top="120"
  87. @close="onClose"
  88. :visible="visible4">
  89. <template #title>Title</template>
  90. <p>Bla bla ...</p>
  91. <p>Bla bla ...</p>
  92. <p>Bla bla ...</p>
  93. </Dialog>
  94. <Button type="primary" @click="showFullScreenDialog">允许切换全屏</Button>
  95. <Dialog
  96. switch-fullscreen
  97. @close="onClose"
  98. :visible="visible5">
  99. <template #title>Title</template>
  100. <p>Bla bla ...</p>
  101. <p>Bla bla ...</p>
  102. <p>Bla bla ...</p>
  103. </Dialog>
  104. <Button type="primary" @click="showCustomHBodyDialog">body 样式自定义</Button>
  105. <Dialog
  106. :body-style="{fontSize: '20px', color: '#eb2f96'}"
  107. @close="onClose"
  108. :visible="visible6">
  109. <template #title>Title</template>
  110. <p>Bla bla ...</p>
  111. <p>Bla bla ...</p>
  112. <p>Bla bla ...</p>
  113. </Dialog>
  114. </Space>
  115. </div>
  116. </template>
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/355516
推荐阅读
相关标签
  

闽ICP备14008679号