Modal.vue 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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. <view
  79. class="popup-content"
  80. :style="{
  81. backgroundColor,
  82. width,
  83. height,
  84. }"
  85. >
  86. <view class="title">
  87. <slot name="title">{{ title }}</slot>
  88. <uni-icons type="closeempty" class="icons" @click="close"></uni-icons>
  89. </view>
  90. <view class="content">
  91. <slot></slot>
  92. </view>
  93. <view v-if="footer" class="footer">
  94. <slot name="footer">
  95. <button class="plain" @click="c(onClose)">
  96. {{ submitter.closeText || "取消" }}
  97. </button>
  98. <button class="primary" @click="c(onSubmit)">
  99. {{ submitter.text || "确定" }}
  100. </button>
  101. </slot>
  102. </view>
  103. </view>
  104. </uni-popup>
  105. </template>
  106. <style scoped lang="scss">
  107. @import "@/uni.scss";
  108. .popup-content {
  109. display: flex;
  110. align-items: center;
  111. flex-direction: column;
  112. justify-content: space-between;
  113. gap: 20rpx;
  114. padding: 20rpx;
  115. border-radius: 10rpx;
  116. .title {
  117. font-family: PingFang SC, PingFang SC;
  118. font-weight: bold;
  119. font-size: 36rpx;
  120. color: #000000;
  121. position: relative;
  122. display: flex;
  123. align-items: center;
  124. justify-content: center;
  125. width: 100%;
  126. margin-top: 10rpx;
  127. .icons {
  128. position: absolute;
  129. right: 0;
  130. top: -10rpx;
  131. }
  132. }
  133. .content {
  134. width: 100%;
  135. flex: 1;
  136. }
  137. .footer {
  138. display: flex;
  139. gap: 20rpx;
  140. width: 100%;
  141. padding: 0 30rpx;
  142. box-sizing: border-box;
  143. button {
  144. flex: 1;
  145. font-family: PingFang SC, PingFang SC;
  146. border-radius: 100rpx 100rpx 100rpx 100rpx;
  147. border: 1rpx solid $primary;
  148. background-color: transparent;
  149. font-weight: 500;
  150. font-size: 32rpx;
  151. &::after {
  152. border: 0;
  153. }
  154. }
  155. .plain {
  156. background: $uni-primary-light;
  157. color: $primary;
  158. }
  159. .primary {
  160. background: $primary;
  161. color: #fff;
  162. }
  163. }
  164. }
  165. </style>