fs-modal.vue 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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. <view class="fs-loader" v-if="loading"></view>
  17. <template v-else>
  18. {{confirmText}}
  19. </template>
  20. </view>
  21. </view>
  22. <view class="fs-modal-close" v-if="showClose" @click="handleClose">
  23. <fs-icon type="icon-close-circle" color="#fff" size="50rpx"></fs-icon>
  24. </view>
  25. </view>
  26. <fs-mask :modelValue="modelValue" @close="handleClose" :maskClickable="maskClickable" :blurable="blurable"></fs-mask>
  27. </view>
  28. </template>
  29. <script setup>
  30. import { ref } from 'vue'
  31. const props = defineProps({
  32. modelValue: Boolean,
  33. width: {
  34. type: String,
  35. default: '80vw'
  36. },
  37. maskClickable: {
  38. type: Boolean,
  39. default: true
  40. },
  41. blurable: Boolean,
  42. title: {
  43. type: String,
  44. default: '提示'
  45. },
  46. showTitle: {
  47. type: Boolean,
  48. default: true
  49. },
  50. content: String,
  51. showCancel: {
  52. type: Boolean,
  53. default: true
  54. },
  55. cancelText: {
  56. type: String,
  57. default: '取消'
  58. },
  59. showConfirm: {
  60. type: Boolean,
  61. default: true
  62. },
  63. confirmText: {
  64. type: String,
  65. default: '确定'
  66. },
  67. confirmTextColor: {
  68. type: String
  69. },
  70. confirmTextColorType: {
  71. type: String,
  72. default: 'primary',
  73. validator(value) {
  74. return ['primary', 'success', 'info', 'warning', 'danger'].includes(value)
  75. }
  76. },
  77. showClose: {
  78. type: Boolean,
  79. default: false
  80. },
  81. beforeClose: Function
  82. })
  83. const emits = defineEmits(['update:modelValue','confirm','cancel'])
  84. const loading = ref(false)
  85. const handleClose = () => {
  86. emits('update:modelValue', false)
  87. }
  88. const handleMask = () => {
  89. if(props.maskClickable) {
  90. handleClose()
  91. }
  92. }
  93. const handleCancel = () => {
  94. handleClose()
  95. emits('cancel')
  96. }
  97. const handleConfirm = () => {
  98. if (props.beforeClose) {
  99. loading.value = true
  100. props.beforeClose('confirm', (flag = true) => {
  101. loading.value = false
  102. flag && handleClose()
  103. })
  104. } else{
  105. handleClose()
  106. emits('confirm', false)
  107. }
  108. }
  109. </script>
  110. <style lang="scss" scoped>
  111. .fs-modal {
  112. &-box {
  113. position: fixed;
  114. background-color: #fff;
  115. z-index: 900;
  116. top: 50%;
  117. left: 50%;
  118. transform: translate(-50%, -50%) scale(0);
  119. border-radius: var(--radius);
  120. &.show{
  121. transform: translate(-50%, -50%) scale(1);
  122. }
  123. }
  124. &-title {
  125. padding: 20rpx;
  126. text-align: center;
  127. }
  128. &-content {
  129. padding: 20rpx 20rpx 40rpx;
  130. text-align: center;
  131. }
  132. &-ft{
  133. display: flex;
  134. &-btn{
  135. flex: 1;
  136. height: 80rpx;
  137. line-height: 80rpx;
  138. text-align: center;
  139. border-top: 2rpx solid var(--border-color);
  140. cursor: pointer;
  141. & + &{
  142. border-left: 2rpx solid var(--border-color);
  143. }
  144. }
  145. }
  146. &-close {
  147. position: absolute;
  148. top: -110rpx;
  149. left: 50%;
  150. transform: translateX(-50%);
  151. &::before {
  152. position: absolute;
  153. bottom: 0;
  154. left: 50%;
  155. transform: translate(-50%, 100%);
  156. height: 60rpx;
  157. width: 2rpx;
  158. background-color: #fff;
  159. content: '';
  160. }
  161. }
  162. }
  163. .fs-loader {
  164. display: inline-block;
  165. width: 40rpx;
  166. height: 40rpx;
  167. color: inherit;
  168. vertical-align: middle;
  169. border: 4rpx solid currentcolor;
  170. border-bottom-color: transparent;
  171. border-radius: 50%;
  172. animation: 1s loader linear infinite;
  173. position: relative;
  174. }
  175. @keyframes loader {
  176. 0% {
  177. transform: rotate(0deg);
  178. }
  179. 100% {
  180. transform: rotate(360deg);
  181. }
  182. }
  183. </style>