封装一个弹窗容器

117 阅读2分钟

以下是弹窗组件

<template>
  <uni-popup
    ref="PopupRef"
    :safe-area="false"
    type="bottom"
    @maskClick="cancel()"
  >
    <view class="popBox">
      <view class="popup">
        <view class="box">
          <view class="popup_header">
            <slot name="title">
              <view class="titleBox"
                ><text class="title">{{ options.title }}</text></view
              >
            </slot>
          </view>
          <view class="content">
            <slot name="content"> </slot>
          </view>
        </view>
        <view class="foot">
          <slot name="remark">
            <view class="remark" v-if="options.remark"
              >{{ options.remark }}</view
            >
          </slot>
          <view
            style="
              display: flex;
              justify-content: space-between;
              margin-top: 20rpx;
            "
          >
            <view class="btn ca" @click="cancel">{{ options.cancelText }}</view>
            <view
              class="btn su"
              @click="sure"
              :class="[{ disabled: props.onConfirmDisable }]"
              >{{ options.confirmText }}</view
            >
          </view>
        </view>
      </view>
    </view>
  </uni-popup>
</template>

<script setup>
import { ref, computed, defineProps, nextTick } from "vue";
const emit = defineEmits(["sure", "cancel"]);
const props = defineProps({
  onConfirmDisable: {
    //标识,需要显示的字段
    type: Boolean,
    default: false,
  },
});
const PopupRef = ref(null);
const options = ref({
  title: "",
  cancelText: "取消",
  confirmText: "确定",
  remark: "",
  onConfirm: null, // Function
  onClose: null, // Function
  onOpen: null, // Function
});
function open(opt = {}) {
  options.value = { ...options.value, ...opt };
  PopupRef.value.open();
  nextTick(() => {
    if (options.value.onOpen) {
      options.value.onOpen();
    }
  });
}
function cancel() {
  if (options.value.onClose) {
    options.value.onClose();
  }
  PopupRef.value.close();
}
function sure() {
  if (props.onConfirmDisable) {
    return;
  }
  if (options.value.onConfirm) {
    options.value.onConfirm();
  }
}
defineExpose({
  open,
  close:cancel,
});
</script>

<style scoped lang="scss">
.popBox {
  background-color: #fff;
  border-top-left-radius: 20rpx;
  border-top-right-radius: 20rpx;
}
.popup {
  // flex: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  overflow: auto;
  justify-content: space-between;
  max-height: 70vh;

  .box {
    flex: 1;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    // background-color: #fff;
    .popup_header {
      border-radius: 20rpx;
      display: flex;
      align-items: center;
      padding: 26rpx 40rpx 26rpx;
      background-color: #fff;
      position: sticky;
      top: 0px;
      z-index: 9;
      .titleBox {
        width: 100%;
        display: flex;
        justify-content: center;
        .title {
          font-size: 34.35rpx;
          font-weight: 600;
        }
      }
    }
    .content {
      flex: 1;
      overflow: auto;
      padding: 0 40rpx;
    }
  }
  .foot {
    // background: 0,linear-gradient(0deg, rgba(51, 124, 250, 0.1), transparent 15rpx), 0;
    background-color: #fff;
    padding: 0 40rpx 30rpx;
    border-top: 1px solid rgba(240, 240, 240, 1);
    margin-top: 20rpx;
    .btn {
      width: 310rpx;
      height: 80rpx;
      border-radius: 120rpx;
      display: flex;
      align-items: center;
      justify-content: center;

      &.ca {
        background: rgba(242, 243, 245, 1);
      }

      &.su {
        background: rgba(51, 124, 250, 1);
        color: #fff;
      }
      &.disabled {
        opacity: 0.6;
      }
    }
    .remark {
      display: flex;
      justify-content: center;
      align-items: center;
      color: rgba(255, 61, 77, 1);
      padding-top: 20rpx;
      // margin-top: 20rpx;
    }
  }
}

.type {
  font-size: 30.53rpx;
  font-weight: 600;
  margin-bottom: 40rpx;
}
.select {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  .item {
    margin-bottom: 30rpx;
    width: 300rpx;
    height: 70rpx;
    border-radius: 20rpx;
    background: rgba(243, 245, 249, 1);
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 26.71rpx;
    border: 1px solid rgba(243, 245, 249, 1);
    &.active {
      border: 1px solid rgba(51, 124, 250, 1);
      background-color: rgba(217, 230, 255, 1);
      color: #337CFA;
    }
  }
}
</style>

使用如下及其效果图

image.png

<template>
    <view @click="open()">打开弹窗</view>
    <popupBox ref="Popup" :onConfirmDisable="onConfirmDisable">
        <!-- 本代码里是传入的头部的值(title),也可以如下自定义顶端头部 -->
        <!-- <template #title>
            自定义顶端内容
        </template> -->
        <template #content>
            中间内容
        </template>
        <!-- 本代码里是传入的备注的值(remark),也可以如下自定义备注内容 -->
        <!-- <template #remark>
            自定义备注内容
        </template> -->
    </popupBox>
</template>
  
<script setup>
import popupBox from "./components/popupBox";
import { ref, computed, onMounted, watch, defineEmits } from "vue";
import { onLoad } from "@dcloudio/uni-app";
const Popup = ref(null);
// 是否禁用确认按钮
const onConfirmDisable = computed(() => {
    return true
})
function open() {
    //打开弹窗
    Popup.value.open({
        cancelText: "取消",
        confirmText: "确认",
        remark: "这是一段备注",
        title: "这是头部",
        onOpen: function () {
            console.log("触发了弹窗《开启》事件");
        },
        onConfirm: function () {
            console.log("触发了弹窗《确定》事件");
            Popup.value.close()//关闭弹窗
        },
        onClose: function () {
            console.log("触发了弹窗《关闭》事件");
        },
    });
}
</script>
<style lang="scss" scoped></style>