index.vue 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. <template>
  2. <view v-if="visible" class="ss-confirm" catchtouchmove="true" @touchmove.stop.prevent="noop">
  3. <!-- 遮罩层 - 模糊背景 -->
  4. <view class="confirm-mask" catchtouchmove="true" @touchmove.stop.prevent="noop" @click="handleMaskClick"></view>
  5. <!-- 弹窗内容 -->
  6. <view class="confirm-content" :style="contentStyle" catchtouchmove="true" @touchmove.stop.prevent="noop">
  7. <!-- 头部信息 - 可选 -->
  8. <view v-if="headerVisible" class="confirm-header">
  9. <slot name="header">
  10. <view class="header-title">{{ title }}</view>
  11. </slot>
  12. </view>
  13. <view class="header-line" v-if="headerVisible"></view>
  14. <!-- 主要内容区域 - 通过slot传入 -->
  15. <view class="confirm-body">
  16. <slot></slot>
  17. </view>
  18. <!-- 底部按钮 -->
  19. <view class="confirm-bottom">
  20. <SsBottom :buttons="bottomButtons" @button-click="handleBottomClick" />
  21. </view>
  22. </view>
  23. </view>
  24. </template>
  25. <script setup>
  26. import { computed } from 'vue'
  27. import SsBottom from '@/components/SsBottom/index.vue'
  28. // Props定义
  29. const props = defineProps({
  30. // 弹窗显示状态
  31. visible: {
  32. type: Boolean,
  33. default: false
  34. },
  35. // 弹窗标题
  36. title: {
  37. type: String,
  38. default: ''
  39. },
  40. // 是否显示头部
  41. showHeader: {
  42. type: Boolean,
  43. default: true
  44. },
  45. // 弹窗宽度
  46. width: {
  47. type: [String, Number],
  48. default: '600rpx'
  49. },
  50. // 弹窗高度
  51. height: {
  52. type: [String, Number],
  53. default: '500rpx'
  54. },
  55. // 弹窗最大高度
  56. // maxHeight: {
  57. // type: [String, Number],
  58. // default: '500rpx'
  59. // },
  60. // 底部按钮配置
  61. bottomButtons: {
  62. type: Array,
  63. default: () => [
  64. { text: '取消', type: 'default' },
  65. { text: '确认', type: 'primary' }
  66. ]
  67. },
  68. // 点击遮罩是否关闭
  69. maskClosable: {
  70. type: Boolean,
  71. default: true
  72. }
  73. })
  74. // 事件定义
  75. const emit = defineEmits(['update:visible', 'close', 'button-click', 'mask-click'])
  76. const noop = () => {}
  77. const headerVisible = computed(() => {
  78. return props.showHeader && !!String(props.title || '').trim()
  79. })
  80. // 计算弹窗样式
  81. const contentStyle = computed(() => {
  82. const style = {}
  83. // 处理宽度
  84. if (typeof props.width === 'number') {
  85. style.width = props.width + 'rpx'
  86. } else {
  87. style.width = props.width
  88. }
  89. // 处理高度
  90. if (props.height !== 'auto') {
  91. if (typeof props.height === 'number') {
  92. style.height = props.height + 'rpx'
  93. } else {
  94. style.height = props.height
  95. }
  96. }
  97. return style
  98. })
  99. /**
  100. * 处理遮罩点击
  101. */
  102. // const handleMaskClick = () => {
  103. // emit('mask-click')
  104. // if (props.maskClosable) {
  105. // emit('update:visible', false)
  106. // emit('close')
  107. // }
  108. // }
  109. /**
  110. * 处理底部按钮点击
  111. */
  112. const handleBottomClick = (button, index) => {
  113. emit('button-click', button, index)
  114. }
  115. </script>
  116. <style lang="scss" scoped>
  117. .ss-confirm {
  118. position: fixed;
  119. top: 0;
  120. left: 0;
  121. right: 0;
  122. bottom: 0;
  123. z-index: 9999;
  124. display: flex;
  125. flex-direction: column;
  126. }
  127. // 遮罩层
  128. .confirm-mask {
  129. position: absolute;
  130. top: 0;
  131. left: 0;
  132. right: 0;
  133. bottom: 0;
  134. backdrop-filter: blur(4rpx); // 只要模糊效果,不要颜色
  135. -webkit-backdrop-filter: blur(41rpxrpx);
  136. }
  137. // 弹窗内容
  138. .confirm-content {
  139. position: absolute;
  140. top: 50%;
  141. left: 50%;
  142. transform: translate(-50%, -50%);
  143. background: #f2f3f4;
  144. border-radius: 10rpx;
  145. overflow: hidden;
  146. display: flex;
  147. flex-direction: column;
  148. box-shadow: 12rpx 10rpx 5rpx rgba(0, 0, 0, 0.3);
  149. // 宽高通过 :style 动态设置
  150. }
  151. // 头部信息
  152. .confirm-header {
  153. padding-top: 41rpx;
  154. padding-bottom: 20rpx;
  155. border-bottom: 1rpx solid #f2f3f4;
  156. .header-title {
  157. font-size: 42rpx;
  158. color: #333;
  159. text-align: center;
  160. }
  161. }
  162. .header-line {
  163. width: calc(100% - 104rpx);
  164. height: 1rpx;
  165. background: #e6e6e6;
  166. margin: 0 auto;
  167. }
  168. // 主要内容区域
  169. .confirm-body {
  170. flex: 1;
  171. overflow: hidden;
  172. display: flex;
  173. flex-direction: column;
  174. }
  175. // 底部按钮
  176. .confirm-bottom {
  177. position: relative;
  178. border-top: 1rpx solid #eee;
  179. }
  180. </style>