fs-modal.vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <template>
  2. <view class="fs-modal">
  3. <view class="fs-modal-box" :class="{ show: modelValue }" :style="{ width }">
  4. <view class="fs-modal-title title" v-if="showTitle">{{ title }}</view>
  5. <view class="fs-modal-content">
  6. <slot>{{ content }}</slot>
  7. </view>
  8. <view class="fs-modal-ft">
  9. <view class="fs-modal-ft-btn fs-modal-ft-cancel" v-if="showCancel" @click="handleCancel">{{ cancelText }}</view>
  10. <view
  11. v-if="showConfirm"
  12. class="fs-modal-ft-btn fs-modal-ft-confirm"
  13. :class="[confirmTextColorType]"
  14. :style="{ color: confirmTextColor }"
  15. @click="handleConfirm"
  16. >
  17. <view class="fs-loader" v-if="loading"></view>
  18. <template v-else>
  19. {{ confirmText }}
  20. </template>
  21. </view>
  22. </view>
  23. <view class="fs-modal-close" v-if="showClose" @click="handleClose">
  24. <fs-icon type="icon-close-circle" color="#fff" size="50rpx"></fs-icon>
  25. </view>
  26. </view>
  27. <fs-mask
  28. :modelValue="modelValue"
  29. @close="handleClose"
  30. :maskClickable="maskClickable"
  31. :blurable="blurable"
  32. ></fs-mask>
  33. </view>
  34. </template>
  35. <script>
  36. /**
  37. * 模态框组件
  38. * @description 模态框组件
  39. * @property {String} width 模态框宽度
  40. * @property {String} title 标题
  41. * @property {Boolean} showTitle 是否显示标题
  42. * @property {Boolean} maskClickable 点击遮罩是否可关闭
  43. * @property {String} content 内容
  44. * @property {Boolean} blurable 毛玻璃效果
  45. * @property {Boolean} showClose 是否显示关闭
  46. * @property {Function} beforeClose 异步关闭
  47. * @property {String} cancelText 取消按钮文字
  48. * @property {Boolean} showCancel 是否显示取消按钮
  49. * @property {String} confirmText 确定按钮文字
  50. * @property {Boolean} showConfirm 是否显示确定按钮
  51. * @property {String} confirmTextColor 确定按钮文字颜色
  52. * @property {Boolean} confirmTextColorType = [primary | danger | warning | info | success] 确定按钮文字颜色类型
  53. * @event {Function} confirm 确定事件
  54. * @event {Function} cancel 取消事件
  55. */
  56. export default {
  57. name: 'fs-model'
  58. }
  59. </script>
  60. <script setup>
  61. import { ref } from 'vue'
  62. const props = defineProps({
  63. modelValue: Boolean,
  64. width: {
  65. type: String,
  66. default: '80vw'
  67. },
  68. maskClickable: {
  69. type: Boolean,
  70. default: true
  71. },
  72. blurable: Boolean,
  73. title: {
  74. type: String,
  75. default: '提示'
  76. },
  77. showTitle: {
  78. type: Boolean,
  79. default: true
  80. },
  81. content: String,
  82. showCancel: {
  83. type: Boolean,
  84. default: true
  85. },
  86. cancelText: {
  87. type: String,
  88. default: '取消'
  89. },
  90. showConfirm: {
  91. type: Boolean,
  92. default: true
  93. },
  94. confirmText: {
  95. type: String,
  96. default: '确定'
  97. },
  98. confirmTextColor: {
  99. type: String
  100. },
  101. confirmTextColorType: {
  102. type: String,
  103. default: 'primary',
  104. validator(value) {
  105. return ['primary', 'success', 'info', 'warning', 'danger'].includes(value)
  106. }
  107. },
  108. showClose: {
  109. type: Boolean,
  110. default: false
  111. },
  112. beforeClose: Function
  113. })
  114. const emits = defineEmits(['update:modelValue', 'confirm', 'cancel'])
  115. const loading = ref(false)
  116. const handleClose = () => {
  117. emits('update:modelValue', false)
  118. }
  119. const handleMask = () => {
  120. if (props.maskClickable) {
  121. handleClose()
  122. }
  123. }
  124. const handleCancel = () => {
  125. handleClose()
  126. emits('cancel')
  127. }
  128. const handleConfirm = () => {
  129. if (props.beforeClose) {
  130. loading.value = true
  131. props.beforeClose('confirm', (flag = true) => {
  132. loading.value = false
  133. flag && handleClose()
  134. })
  135. } else {
  136. handleClose()
  137. emits('confirm', false)
  138. }
  139. }
  140. </script>
  141. <style lang="scss" scoped>
  142. .fs-modal {
  143. &-box {
  144. position: fixed;
  145. background-color: #fff;
  146. z-index: 900;
  147. top: 50%;
  148. left: 50%;
  149. transform: translate(-50%, -50%) scale(0);
  150. border-radius: var(--radius);
  151. &.show {
  152. transform: translate(-50%, -50%) scale(1);
  153. }
  154. }
  155. &-title {
  156. padding: 20rpx;
  157. text-align: center;
  158. }
  159. &-content {
  160. padding: 20rpx 20rpx 40rpx;
  161. text-align: center;
  162. }
  163. &-ft {
  164. display: flex;
  165. &-btn {
  166. flex: 1;
  167. height: 80rpx;
  168. line-height: 80rpx;
  169. text-align: center;
  170. border-top: 2rpx solid var(--border-color);
  171. cursor: pointer;
  172. & + & {
  173. border-left: 2rpx solid var(--border-color);
  174. }
  175. }
  176. }
  177. &-close {
  178. position: absolute;
  179. top: -110rpx;
  180. left: 50%;
  181. transform: translateX(-50%);
  182. &::before {
  183. position: absolute;
  184. bottom: 0;
  185. left: 50%;
  186. transform: translate(-50%, 100%);
  187. height: 60rpx;
  188. width: 2rpx;
  189. background-color: #fff;
  190. content: '';
  191. }
  192. }
  193. }
  194. .fs-loader {
  195. display: inline-block;
  196. width: 40rpx;
  197. height: 40rpx;
  198. color: inherit;
  199. vertical-align: middle;
  200. border: 4rpx solid currentcolor;
  201. border-bottom-color: transparent;
  202. border-radius: 50%;
  203. animation: 1s loader linear infinite;
  204. position: relative;
  205. }
  206. @keyframes loader {
  207. 0% {
  208. transform: rotate(0deg);
  209. }
  210. 100% {
  211. transform: rotate(360deg);
  212. }
  213. }
  214. </style>