Modal.vue 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. <script setup>
  2. import { ref, watchEffect, nextTick } from "vue";
  3. const popup = ref(null);
  4. const props = defineProps({
  5. open: {
  6. type: Boolean,
  7. default: false,
  8. },
  9. backgroundColor: {
  10. type: String,
  11. default: "#fff",
  12. },
  13. width: {
  14. type: String,
  15. default: "600rpx",
  16. },
  17. height: {
  18. type: String,
  19. default: "",
  20. },
  21. title: {
  22. type: String,
  23. default: "",
  24. },
  25. footer: {
  26. type: Boolean,
  27. default: true,
  28. },
  29. onClose: {
  30. type: Function,
  31. default: () => {},
  32. },
  33. onSubmit: {
  34. type: Function,
  35. default: () => {},
  36. },
  37. submitter: {
  38. type: Object,
  39. default: () => ({}),
  40. },
  41. });
  42. const emit = defineEmits(["update:open"]);
  43. const onChange = (e) => {
  44. emit("update:open", e.show);
  45. };
  46. const close = () => {
  47. emit("update:open", false);
  48. };
  49. const c = async (fn) => {
  50. const res = await fn();
  51. if (res) {
  52. close();
  53. }
  54. };
  55. watchEffect(() => {
  56. // 监听这两个函数
  57. popup.value;
  58. props.open;
  59. nextTick(() => {
  60. if (props.open) {
  61. popup.value?.open?.();
  62. } else {
  63. popup.value?.close?.();
  64. }
  65. });
  66. });
  67. defineExpose({
  68. popup,
  69. });
  70. </script>
  71. <template>
  72. <uni-popup
  73. ref="popup"
  74. @change="onChange"
  75. v-bind="$attrs"
  76. background-color="transparent"
  77. >
  78. <slot name="custom">
  79. <view
  80. class="popup-content"
  81. :style="{
  82. backgroundColor,
  83. width,
  84. height,
  85. }"
  86. >
  87. <view class="title">
  88. <slot name="title">{{ title }}</slot>
  89. <uni-icons type="closeempty" class="icons" @click="close"></uni-icons>
  90. </view>
  91. <view class="content">
  92. <slot></slot>
  93. </view>
  94. <view v-if="footer" class="footer">
  95. <slot name="footer">
  96. <button class="plain" @click="c(onClose)">
  97. {{ submitter.closeText || "取消" }}
  98. </button>
  99. <button class="primary" @click="c(onSubmit)">
  100. {{ submitter.text || "确定" }}
  101. </button>
  102. </slot>
  103. </view>
  104. </view>
  105. </slot>
  106. </uni-popup>
  107. </template>
  108. <style scoped lang="scss">
  109. @import "@/uni.scss";
  110. .popup-content {
  111. display: flex;
  112. align-items: center;
  113. flex-direction: column;
  114. justify-content: space-between;
  115. gap: 20rpx;
  116. padding: 20rpx;
  117. border-radius: 10rpx;
  118. .title {
  119. font-family: PingFang SC, PingFang SC;
  120. font-weight: bold;
  121. font-size: 36rpx;
  122. color: #000000;
  123. position: relative;
  124. display: flex;
  125. align-items: center;
  126. justify-content: center;
  127. width: 100%;
  128. margin-top: 10rpx;
  129. .icons {
  130. position: absolute;
  131. right: 0;
  132. top: -10rpx;
  133. }
  134. }
  135. .content {
  136. width: 100%;
  137. flex: 1;
  138. }
  139. .footer {
  140. display: flex;
  141. gap: 20rpx;
  142. width: 100%;
  143. padding: 0 30rpx;
  144. box-sizing: border-box;
  145. button {
  146. flex: 1;
  147. font-family: PingFang SC, PingFang SC;
  148. border-radius: 100rpx 100rpx 100rpx 100rpx;
  149. border: 1rpx solid $primary;
  150. background-color: transparent;
  151. font-weight: 500;
  152. font-size: 32rpx;
  153. &::after {
  154. border: 0;
  155. }
  156. }
  157. .plain {
  158. background: $uni-primary-light;
  159. color: $primary;
  160. }
  161. .primary {
  162. background: $primary;
  163. color: #fff;
  164. }
  165. }
  166. }
  167. </style>