feed 信息流

解释: 信息流组件,可配置下拉刷新、列表加载、上滑加载功能,适用于列表信息展示,并可放置在页面的任何部分;组件包含手势下拉以及 api 调用两种使用方式。在信息流列表页中,可根据不同场景配置刷新形式:下拉刷新适用于在页面中浏览过程中有内容更新时,手动触发;自动刷新适用于返回页面后有内容更新时,自动触发。也可在局部模块配置刷新能力。详情查看 刷新 加载

# 属性说明

属性名 类型 必填 默认值 说明
theme String 主题配置,默认浅色;深色主题请指定 dark
loadingHeight Number 64 加载、话术区域高度,会根据屏幕宽度适配,适配基于组件内方法 import {upx2dpx} from '@smt-ui/component/src/common/utils/px';(单位为 px)
pullToRefresh Boolean false 是否开启手势下拉刷新; 默认只能通过组件 api 调起
lowerThreshold Number 150 触发 scrolltolower 事件的阈值(单位 px)
text String 建议最多显示 18 个汉字,超出内容截断 加载成功时的展示话术
refresh EventHandle 手势滑动触发加载时,响应该 onRefresh 事件; 通过调用 api 加载,不会触发该事件
startRefresh EventHandle 手动调用该 api,触发加载
stopRefresh EventHandle 手动调用该 api,停止加载,并弹出加载提示(对应属性 text);可使用 await 等待关闭动画结束
closeLoading EventHandle 手动调用该 api,立即关闭加载,不弹出加载提示;例如接口异常,建义直接关闭加载(小球交替一次大约为 500ms,调用前可加延时避免关闭太快)
smt-feed-container externalClass 组件整体 class 名
smt-feed-loading externalClass 加载区域 class 名
smt-feed-content externalClass false 滚动区域 class 名,用于设置 ios 回弹背景
smt-refresh-circle-left externalClass 加载中左侧小球 class 名
smt-refresh-circle-right externalClass 加载中右侧小球 class 名
ext-cls-content externalClass 滚动区域 class 名
smt-refresh-result-container externalClass 加载话术外框 class 名
ext-cls-result-text externalClass 加载话术文字 class 名

# 示例

扫码体验
重新加载
请使用百度APP扫码

# 代码示例 1: 下拉刷新

    <smt-feed
        class="smt-feed pull-down-refresh"
        pull-to-refresh
        bind:refresh="onRefresh"
        bind:scrolltolower="scrollToLower"
        text="{{PullText}}"
    >
        <view class="list">
            <view class="{{'list-item ' + (val === 1 ? 'first' : '')}}"
                s-for="val in list"
                style="border-bottom: solid 1px #e0e0e0;"
                key="{{val}}"
            >
                <view class="left">
                    <view class="row begin"></view>
                    <view class="row center"></view>
                    <view class="row end"></view>
                </view>
                <view class="right"></view>
            </view>
        </view>
        <smt-spin status="{{status}}" bind:tap="reload"></smt-spin>
    </smt-feed>
    
    {
        "component": true,
        "usingComponents": {
            "smt-feed": "@smt-ui/component/src/feed",
            "smt-spin": "@smt-ui/component/src/spin"
        }
    }
    
    import {selComponent, syncSetData} from '@smt-ui/component/src/common/utils';
    
    Component({
        properties: {
            first: {
                type: Boolean,
                value: true
            }
        },
    
        data: {
            status: 1,
            text: '不超过18个字',
            list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
            count: 0
        },
    
        methods: {
            fetchData(ms = 1200) {
                const data = {
                    code: 0,
                    data: Array.from({length: Math.random() * 10 + 10 | 0}, (_, i) => i)
                };
                return new Promise(r => setTimeout(() => r(data), ms));
            },
            async previewRefresh() {
                const refresh = await selComponent(this, '.pull-down-refresh');
                refresh.startRefresh();
                const {data: list} = await this.fetchData();
                await new Promise(r => setTimeout(r, 300));
                await syncSetData(this, {
                    status: 1,
                    list: list || this.data.list,
                    text: list ? `为你推荐${list.length}条更新` : '暂时没有更新,休息一下'
                });
                refresh.stopRefresh();
            },
    
            async onRefresh() {
                const refresh = await selComponent(this, '.pull-down-refresh');
                const {data: list} = await this.fetchData();
                await new Promise(r => setTimeout(r, 300));
                await syncSetData(this, {
                    status: 1,
                    count: 0,
                    list: list || this.data.list,
                    text: list ? `为你推荐${list.length}条更新` : '暂时没有更新,休息一下'
                });
                refresh.stopRefresh();
            },
    
            async scrollToLower() {
                const {data: list} = await this.fetchData();
                const fail = this.data.count === 3;
                const end = this.data.count === 5;
                if (fail || end) {
                    this.setData({
                        status: fail ? 3 : 2
                    });
                    return;
                }
                await syncSetData(this, {
                    list: list.concat(this.data.list || []),
                    count: ++this.data.count
                });
            },
    
            async reload() {
                if (this.data.status !== 0 && this.data.status !== 3) {
                    return;
                }
                await syncSetData(this, {status: 1, count: ++this.data.count});
                this.scrollToLower();
            }
        },
    
        ready() {
            this.data.first && this.previewRefresh();
            this.triggerEvent('previewend');
        }
    });
    
    .smt-feed {
        height: 100%;
        display: block;
        background: #fff;
    }
    
    .feed-container {
        box-sizing: border-box;
        height: calc(100vh - 0.65rem);
    }
    .list-item {
        background: #eee;
        height: 60.39rpx;
        margin-bottom: 12.08rpx;
    }
    

    # 代码示例 2: 自动刷新

      <smt-feed
          class="smt-feed auto-refresh"
          text="{{text}}"
      >
          <view class="list">
              <view class="{{'list-item ' + (val === 1 ? 'first' : '')}}"
                  s-for="val in list"
                  style="border-bottom: solid 1px #e0e0e0;"
                  key="{{val}}"
              >
                  <view class="left">
                      <view class="row begin"></view>
                      <view class="row center"></view>
                      <view class="row end"></view>
                  </view>
                  <view class="right"></view>
              </view>
          </view>
          <smt-spin
              status="{{status}}"
              bind:tap="clickToLoad"
          >
          </smt-spin>
      </smt-feed>
      
      {
          "component": true,
          "usingComponents": {
              "smt-feed": "@smt-ui/component/src/feed",
              "smt-spin": "@smt-ui/component/src/spin"
          }
      }
      
      import {selComponent, syncSetData} from '@smt-ui/component/src/common/utils';
      
      Component({
          data: {
              status: 1,
              text: '不超过18个字',
              list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
              count: 0
          },
      
          methods: {
              fetchData(ms = 1200) {
                  const data = {
                      code: 0,
                      data: Array.from({length: Math.random() * 10 + 10 | 0}, (_, i) => i)
                  };
                  return new Promise(r => setTimeout(() => r(data), ms));
              },
              async autoRefresh() {
                  const clkRefresh = await selComponent(this, '.auto-refresh');
                  const autoLoad = async () => {
                      clkRefresh.startRefresh();
                      const {code, data: list} = await this.fetchData();
                      if (code !== 0) {
                          swan.showToast({title: '网络错误', mask: true, icon: 'none'});
                          clkRefresh.closeLoading();
                          return;
                      }
                      await new Promise(r => setTimeout(r, 300));
                      await syncSetData(this, {
                          status: 0,
                          list: list || this.data.list,
                          text: list ? `为你推荐${list.length}条更新` : '暂时没有更新,休息一下'
                      });
                      await clkRefresh.stopRefresh();
                      await new Promise(r => setTimeout(r, 500)); // 加载完一轮等500ms
                  };
                  autoLoad();
              },
      
              async clickToLoad() {
                  if (this.data.status === 2) {
                      return;
                  }
                  await syncSetData(this, {status: 1});
                  const {data: list} = await this.fetchData();
                  await new Promise(r => setTimeout(r, 300));
                  const fail = this.data.count === 3;
                  const end = this.data.count === 5;
                  if (fail || end) {
                      this.setData({
                          count: ++this.data.count,
                          status: fail ? 3 : 2
                      });
                      return;
                  }
                  await syncSetData(this, {
                      list: list.concat(this.data.list || []),
                      status: 0,
                      count: ++this.data.count
                  });
              }
          },
      
          ready() {
              this.autoRefresh();
          }
      });
      
      
      .smt-feed {
          height: 100%;
          display: block;
          background: #fff;
      }
      
      .feed-container {
          box-sizing: border-box;
          height: calc(100vh - 0.65rem);
      }
      .list-item {
          background: #eee;
          height: 60.39rpx;
          margin-bottom: 12.08rpx;
      }
      

      # 代码示例 3: 局部刷新

        <view class="placeholder"></view>
        <smt-feed
            class="smt-feed pull-down-refresh"
            pull-to-refresh
            bind:refresh="onRefresh"
            bind:scrolltolower="scrollToLower"
            text="{{text}}"
        >
            <view class="list">
                <view class="{{'list-item ' + (val === 1 ? 'first' : '')}}"
                    s-for="val in list"
                    style="border-bottom: solid 1px #e0e0e0;"
                    key="{{val}}"
                >
                    <view class="left">
                        <view class="row begin"></view>
                        <view class="row center"></view>
                        <view class="row end"></view>
                    </view>
                    <view class="right"></view>
                </view>
            </view>
            <smt-spin status="{{status}}" bind:tap="reload"></smt-spin>
        </smt-feed>
        
        {
            "component": true,
            "usingComponents": {
                "smt-feed": "@smt-ui/component/src/feed",
                "smt-spin": "@smt-ui/component/src/spin"
            }
        }
        
        import {selComponent, syncSetData} from '@smt-ui/component/src/common/utils';
        
        Component({
            properties: {
                first: {
                    type: Boolean,
                    value: true
                }
            },
        
            data: {
                status: 1,
                text: '不超过18个字',
                list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
                count: 0
            },
        
            methods: {
                fetchData(ms = 1200) {
                    const data = {
                        code: 0,
                        data: Array.from({length: Math.random() * 10 + 10 | 0}, (_, i) => i)
                    };
                    return new Promise(r => setTimeout(() => r(data), ms));
                },
                async previewRefresh() {
                    const refresh = await selComponent(this, '.pull-down-refresh');
                    refresh.startRefresh();
                    const {data: list} = await this.fetchData();
                    await new Promise(r => setTimeout(r, 300));
                    await syncSetData(this, {
                        status: 1,
                        list: list || this.data.list,
                        text: list ? `为你推荐${list.length}条更新` : '暂时没有更新,休息一下'
                    });
                    refresh.stopRefresh();
                },
        
                async onRefresh() {
                    const refresh = await selComponent(this, '.pull-down-refresh');
                    const {code, data: list} = await this.fetchData();
                    if (code !== 0) {
                        swan.showToast({title: '网络错误', mask: true, icon: 'none'});
                        refresh.closeLoading();
                        return;
                    }
                    await new Promise(r => setTimeout(r, 300));
                    await syncSetData(this, {
                        status: 1,
                        count: 0,
                        list: list || this.data.list,
                        text: list ? `为你推荐${list.length}条更新` : '暂时没有更新,休息一下'
                    });
                    refresh.stopRefresh();
                },
        
                async autoRefresh() {
                    if (this.data.activeName === '自动刷新') {
                        const clkRefresh = await selComponent(this, '.auto-refresh');
                        const autoLoad = async () => {
                            clkRefresh.startRefresh();
                            const {code, data: list} = await this.fetchData();
                            if (code !== 0) {
                                swan.showToast({title: '网络错误', mask: true, icon: 'none'});
                                clkRefresh.closeLoading();
                                return;
                            }
                            await syncSetData(this, {
                                status: 0,
                                list: list || this.data.list,
                                text: list ? `为你推荐${list.length}条更新` : '暂时没有更新,休息一下'
                            });
                            await new Promise(r => setTimeout(r, 300)); // 1500ms 之后再关闭: 手动关闭不需要等小球转3圈
                            await clkRefresh.stopRefresh();
                            await new Promise(r => setTimeout(r, 500)); // 加载完一轮等500ms
                            this.data.activeName === '自动刷新' && await autoLoad();
                        };
                        autoLoad();
                    }
                },
        
                async scrollToLower() {
                    const {data: list} = await this.fetchData();
                    const fail = this.data.count === 3;
                    const end = this.data.count === 5;
                    if (fail || end) {
                        this.setData({
                            status: fail ? 3 : 2
                        });
                        return;
                    }
                    await syncSetData(this, {
                        list: list.concat(this.data.list || []),
                        count: ++this.data.count
                    });
                },
        
                async reload() {
                    if (this.data.status !== 0 && this.data.status !== 3) {
                        return;
                    }
                    await syncSetData(this, {status: 1, count: ++this.data.count});
                    this.scrollToLower();
                }
            },
        
            ready() {
                this.data.first && this.previewRefresh();
                this.triggerEvent('previewend');
            }
        });
        
        .smt-feed {
            display: block;
            height: calc(100% - 1.52rem);
            background: #fff;
        }
        
        .feed-container {
            box-sizing: border-box;
            height: calc(100vh - 0.65rem);
        }
        .placeholder {
            display: flex;
            align-items: center;
            justify-content: center;
            height: 1.52rem;
            padding: .1rem .17rem;
            background-color: #fff;
        }
        
        .placeholder::before {
            content: "";
            flex: 1;
            height: 100%;
            border-radius: .03rem;
            background-color: #e0e0e0;
        }
        .list-item {
            background: #eee;
            height: 60.39rpx;
            margin-bottom: 12.08rpx;
        }
        

        # Bug&Tip

        • Tip:和 scroll-view 一样,信息流组件作为局部滚动组件,必须在它的父级或本身指定高度。
        • Tip:当同时启用下拉刷新和上滑加载且请求不稳定时,可使用 CancelToken 取消先前的请求。