更新记录

1.1.3(2023-06-16)

  • 文档修改

1.1.2(2023-06-16)

  • 文档完善

1.1.1(2023-06-16)

  • 文档修复
查看更多

平台兼容性

Vue2 Vue3
×
App 快应用 微信小程序 支付宝小程序 百度小程序 字节小程序 QQ小程序
HBuilderX 3.7.3 app-vue × × × × ×
钉钉小程序 快手小程序 飞书小程序 京东小程序
× × × ×
H5-Safari Android Browser 微信浏览器(Android) QQ浏览器(Android) Chrome IE Edge Firefox PC-Safari

custom-bottom-sheet-dialog

手势滑动关闭的弹窗

提示: 使用该插件前确保你已经导入 uni-popup 插件。

有问题可以加 QQ 群:297080738(已满),新群:460688316

Props

属性名 类型 默认值 说明
animation Boolean ture 是否开启弹窗动画
is-mask-click Boolean true 点击遮罩关闭弹窗
mask-background-color String rgba(0,0,0,0.4) 蒙版颜色,建议使用 rgba 颜色值
background-color String none 主窗口背景色
safe-area Boolean true 是否适配底部安全区
contentHeight String auto 设置弹窗高度,例如 auto 或者 300px
threshold String | Number 0 设置滑动阈值,滑动多少距离之后触发自动关闭,设置超过可视区域或弹窗高度则会取消自动关闭
speed Number 0.8 设置滑动速度,范围 0 - 1,没有滑动指定距离,但是滑动很快,达到速度阈值同样会触发自动关闭。如果设置为 0 ,只要有下滑操作都会自动关闭弹窗
beforeClose Function - 关闭弹窗前的回调,使用方法见下方示例。

Events

事件名称 说明 返回值
touchstart 按下滑动区域触发 e
touchmove 滑动时触发 e
touchend 松开手触发 e
popupStateChange 弹窗打开和关闭时触发 e={show: true | false,type:当前模式}
maskClick 点击遮罩 -
clickTouchBar 点击滑动区域时触发,可以执行内部滚动区域返回顶部操作 -

Slot

名称 说明
- 默认插槽,弹窗内容展示

最佳实践

此示例模仿实际业务中查看详情信息等情况,实现数据展示、上拉加载、下拉刷新、返回顶部等功能,复制代码即可运行

<template>
  <view class="content">
    <button @click="handleOpenPopup">打开弹窗</button>

    <custom-bottom-sheet-dialog
      ref="popup"
      :contentHeight="contentHeight"
      :threshold="contentHeight"
      :speed="0.2"
      @clickTouchBar="goTop"
      @close="handleClosePullToRefresh"
    >
      <template>
        <scroll-view
          class="scroll-view"
          scroll-y="true"
          :scroll-top="scrollTop"
          :refresher-enabled="true"
          :refresher-triggered="refresherTriggered"
          @touchmove.stop
          @scrolltolower="getList('next')"
          @refresherrefresh="getList('refresh')"
        >
          <uni-list :border="false">
            <uni-list-item
              thumb-size="lg"
              v-for="item in list"
              :key="item.id"
              :title="item.title"
              :note="item.note"
              :thumb="item.thumb"
              :rightText="item.rightText"
            ></uni-list-item>
          </uni-list>
          <uni-load-more
            v-show="!refresherTriggered"
            :status="loadMoreStatus"
            @clickLoadMore="clickLoadMore"
          ></uni-load-more>
        </scroll-view>
      </template>
    </custom-bottom-sheet-dialog>
  </view>
</template>

<script>
export default {
  data() {
    return {
      contentHeight: '',
      scrollTop: 0,
      limit: 10,
      offset: 1,
      listCount: 0,
      refresherTriggered: false,
      loadMoreStatus: 'more',
      list: []
    }
  },
  created() {
    this.contentHeight = `${Math.floor(
      uni.getSystemInfoSync().screenHeight * 0.7
    )}px`
  },
  methods: {
    handleOpenPopup() {
      this.$refs.popup.open()
      this.getList()
    },
    // 列表返回顶部
    goTop() {
      this.scrollTop = 10
      this.$nextTick(() => {
        this.scrollTop = 0
        uni.showToast({
          title: '已返回顶部',
          icon: 'none',
          duration: 1000,
          position: 'bottom'
        })
      })
    },
    // 打开底部弹窗时禁用原生下拉刷新, app生效
    handleClosePullToRefresh(support = true) {
      // #ifdef APP-PLUS
      const pages = getCurrentPages()
      const page = pages[pages.length - 1]
      const currentWebview = page.$getAppWebview()
      currentWebview.setStyle({
        pullToRefresh: {
          support,
          style: plus.os.name === 'Android' ? 'circle' : 'default'
        }
      })
      // #endif
    },
    // 手动点击加载更多
    clickLoadMore() {
      if (this.loadMoreStatus === 'noMore') return
      // 获取下一页数据
      this.getList('next')
    },
    // 获取列表数据
    async getList(type) {
      if (type === 'next') {
        if (this.loadMoreStatus === 'noMore') return
        this.offset++
      }

      if (!type || type === 'refresh') {
        this.listCount = 0
        this.offset = 1
        this.list = []
      }

      if (type === 'refresh') {
        this.refresherTriggered = true
        if (Math.random() > 0.8) {
          this.loadMoreStatus = 'noMore'
          setTimeout(() => {
            this.refresherTriggered = false
          }, 0)
          return
        }
      }

      this.loadMoreStatus = 'loading'

      // 发送请求获取列表数据...
      const { data } = await this.queryList({
        limit: this.limit,
        offset: this.offset
      })

      if (!data.length) {
        this.loadMoreStatus = 'noMore'
      } else {
        this.loadMoreStatus = 'more'
      }

      this.refresherTriggered = false

      this.list.push(...data)
    },
    queryList({ limit, offset }) {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve({
            code: 200,
            data:
              offset >= 3
                ? []
                : new Array(limit).fill(0).map(() => ({
                    id: this.listCount++,
                    title: `这是第${this.listCount}项`,
                    note: `描述信息`,
                    thumb:
                      'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/unicloudlogo.png'
                  }))
          })
        }, 500)
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.content {
  padding: 4px 5px 0;
}

.scroll-view {
  touch-action: none;
  flex: 1;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}
</style>

基础使用示例

<template>
  <view class="content">
    <button type="primary" @click="$refs.popupEmpty.open()">
      打开空白弹窗
    </button>
    <button style="margin-top: 8px" type="primary" @click="handleOpenCol">
      打开弹窗纵向
    </button>
    <button style="margin-top: 8px" type="primary" @click="handleOpenRow">
      打开弹窗横向
    </button>
    <custom-bottom-sheet-dialog ref="popupEmpty">
      <template>
        <view style="padding: 8px; text-align: center">
          <text>这里是插槽</text>
        </view>
      </template>
    </custom-bottom-sheet-dialog>
    <custom-bottom-sheet-dialog ref="popupCol" threshold="200">
      <template>
        <scroll-view class="scroll-view-box" scroll-y="true" @touchmove.stop>
          <view class="col-list">
            <view
              class="col-list-item"
              v-for="item in 15"
              :key="item"
              @click="handleLiClick(item)"
            >
              {{ item }}
            </view>
          </view>
        </scroll-view>
      </template>
    </custom-bottom-sheet-dialog>
    <custom-bottom-sheet-dialog ref="popupRow" threshold="50">
      <template>
        <scroll-view
          class="scroll-view-box row"
          scroll-x="true"
          @touchmove.stop
        >
          <view class="row-list">
            <view
              class="row-list-item"
              v-for="item in 15"
              :key="item"
              @click="handleLiClick(item)"
            >
              <image
                class="logo"
                src="/static/logo.png"
                mode="aspectFit"
                :draggable="false"
              ></image>
              <view class="name">
                <text>菜单</text>
                <text>{{ item }}</text>
              </view>
            </view>
          </view>
          <view class="row-list">
            <view
              class="row-list-item"
              v-for="item in 15"
              :key="item"
              @click="handleLiClick(item)"
            >
              <image
                class="logo"
                src="/static/logo.png"
                mode="aspectFit"
                :draggable="false"
              ></image>
              <view class="name">
                <text>菜单</text>
                <text>{{ item }}</text>
              </view>
            </view>
          </view>
        </scroll-view>
      </template>
    </custom-bottom-sheet-dialog>
  </view>
</template>
<script>
export default {
  data() {
    return {}
  },
  methods: {
    handleOpenCol() {
      this.$refs.popupCol.open()
    },
    handleOpenRow() {
      this.$refs.popupRow.open()
    },
    handleLiClick(value) {
      uni.showToast({
        title: `点击了第${value}个`,
        icon: 'none',
        duration: 1000
      })
      console.log(value)
    },
    beforeClose(done) {
      uni.showModal({
        title: '提示',
        content: '确认关闭底部弹窗?',
        cancelText: '再想想',
        confirmText: '关闭',
        success: function (res) {
          if (res.confirm) {
            done()
          } else if (res.cancel) {
            // 用户点击取消
          }
        }
      })
    }
  }
}
</script>
<style lang="scss" scoped>
.content {
  padding: 8px 10px 0;
}
.scroll-view-box {
  max-height: 300px;
  width: auto;

  &.row {
    display: flex;
  }
}

.col-list-item {
  padding: 8px 10px;
  text-align: center;
  font-size: 16px;
}

.row-list {
  display: flex;

  .row-list-item {
    padding: 8px 10px 8px 0;

    &:first-child {
      margin-left: 10px;
    }
    .logo {
      width: 40px;
      height: 40px;
    }

    .name {
      font-size: 12px;
      text-align: center;
    }
  }
}
</style>

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议

暂无用户评论。

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问