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-error" color="#fff" size="50rpx"></fs-icon>
  24. </view>
  25. </view>
  26. <fs-mask v-model="modelValue" @close="handleClose" :maskClickable="maskClickable"></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. title: {
  42. type: String,
  43. default: '提示'
  44. },
  45. showTitle: {
  46. type: Boolean,
  47. default: true
  48. },
  49. content: String,
  50. showCancel: {
  51. type: Boolean,
  52. default: true
  53. },
  54. cancelText: {
  55. type: String,
  56. default: '取消'
  57. },
  58. showConfirm: {
  59. type: Boolean,
  60. default: true
  61. },
  62. confirmText: {
  63. type: String,
  64. default: '确定'
  65. },
  66. confirmTextColor: {
  67. type: String
  68. },
  69. confirmTextColorType: {
  70. type: String,
  71. default: 'primary',
  72. validator(value) {
  73. return ['primary', 'success', 'info', 'warning', 'danger'].includes(value)
  74. }
  75. },
  76. showClose: {
  77. type: Boolean,
  78. default: false
  79. },
  80. beforeClose: Function
  81. })
  82. const emits = defineEmits(['update:modelValue','confirm','cancel'])
  83. const loading = ref(false)
  84. const handleClose = () => {
  85. emits('update:modelValue', false)
  86. }
  87. const handleMask = () => {
  88. if(props.maskClickable) {
  89. handleClose()
  90. }
  91. }
  92. const handleCancel = () => {
  93. handleClose()
  94. emits('cancel')
  95. }
  96. const handleConfirm = () => {
  97. if (props.beforeClose) {
  98. loading.value = true
  99. props.beforeClose('confirm', (flag = true) => {
  100. loading.value = false
  101. flag && handleClose()
  102. })
  103. } else{
  104. handleClose()
  105. emits('confirm', false)
  106. }
  107. }
  108. </script>
  109. <style lang="scss" scoped>
  110. .fs-modal {
  111. &-box {
  112. position: fixed;
  113. background-color: #fff;
  114. z-index: 900;
  115. transition: all .2s;
  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>