美文网首页
vue pc端 纯js实现向下滚动无限加载

vue pc端 纯js实现向下滚动无限加载

作者: 八妹sss | 来源:发表于2022-09-01 13:25 被阅读0次

    代码示例

    <template>
      <div class="template-page"
        v-loading="loading"
          ref="scrollDiv">
        <section class="poster-header">
          <!-- 分类 -->
          <section class="category-filter">
            <div class="label-name">分类:</div>
            <div class="category-content">
              <ul class="category-group"
                :style="isFold ? 'max-height: 32px;' : ''">
                <li class="category-tag"
                  :class="{ active: !categoryIds[0] }"
                  @click="selectCategory()"> 全部 </li>
                <li class="category-tag"
                  v-for="(categoryInfo, i) in categoryList"
                  :key="`category${i}`"
                  :class="{ active : categoryIds[0] === categoryInfo._id }"
                  @click="selectCategory(categoryInfo)">{{categoryInfo.name}}</li>
              </ul>
              <div class="category-more"
                :class="{
                  up: !isFold,
                  down: isFold
                }"
                @click="isFold = !isFold">
                <span class="text">更多</span>
                <span class="icon-fold"></span>
              </div>
            </div>
          </section>
          <!-- 搜索 -->
          <section class="filter-box">
            <div class="fiter-left">
              <search-input
                :defaultValue='searchValue'
                maxWidth='200px'
                height="32px"
                placeholder='请输入海报名称'
                @change='handleSearch'>
              </search-input>
            </div>
            <div class="filter-right"></div>
          </section>
        </section>
        <section class="poster-wrapper">
          <ul class="poster-list"
            v-if="templateList.length">
            <li class="poster-item"
              v-for="(tempInfo, i) in templateList"
              :key="`poster${i}`"
              @click.stop="editPoster(tempInfo, i)">
              <div class="item-header">
                <p class="name">{{tempInfo.name}}</p>
                <el-dropdown
                  @command='handleCommand($event, tempInfo)'
                  class='qa-bottom-dropdown'
                  trigger="hover">
                  <p class="icon"></p>
                  <el-dropdown-menu slot="dropdown" class='handle-menu-card'>
                    <el-dropdown-item command="editor">编辑海报</el-dropdown-item>
                    <el-dropdown-item command="popularize">推广海报</el-dropdown-item>
                  </el-dropdown-menu>
                </el-dropdown>
              </div>
              <div class="item-cont"
                :style="{height: `${(256 / tempInfo.content.width) * tempInfo.content.height}px` }">
                <img :src="tempInfo.content.thumb" alt=""/>
              </div>
              <div class="item-footer">{{formatDate(tempInfo.updatedAt)}}</div>
            </li>
          </ul>
          <!-- 加载中 -->
          <div class="loading-box"
            v-show="loading">加载中...</div>
          <!-- 加载完成 -->
          <div class="load-end"
            v-show="isEnd">已加载完毕</div>
          <div class="default-cont"
            v-show='!templateList.length'>暂无数据</div>
        </section>
      </div>
    </template>
    <script>
    import axios from 'axios'
    import ZhihuiEditor from 'zhihui-editor-sdk'
    
    import searchInput from 'public/search-input'
    export default {
      name: 'matTemplateList',
      components: {
        searchInput
      },
      props: {
        /**
         * 调用腾讯云智绘API所需token
         */
        accessToken: {
          type: String,
          default: ''
        },
        /**
         * 初始化智绘编辑器即iframe时所需token
         */
        editorToken: {
          type: String,
          default: ''
        },
        /**
         * 初始化智绘编辑器即iframe时所需的其他参数
         */
        editorAppInfo: {
          type: Object,
          default () {
            return {
              appid: '',
              appKey: '',
              channel: '',
              stamp: 'testUserId',
              timestamp: '',
              scope: 'all'
            }
          }
        }
      },
      data () {
        return {
          loading: false,
          categoryList: [], // 分类列表
          isFold: true, // 是否折叠分类
          // 获取模板的参数
          searchValue: '',
          currPage: 1,
          pageSize: 20,
          count: 0,
          categoryIds: [], // 选中的分类id
          templateList: [], // 模板列表
          // 模板列表是否加载完毕,默认为false
          isEnd: false,
          // 添加编辑海报相关
          zhihuiEditor: null,
          currTemplateInfo: null,
          currTemplateIndex: -1,
          // 流媒体
          waterFallOptions: {
            containerSelector: '.poster-list',
            cardSelector: '.poster-item',
            paddingLeft: 0,
            paddingRight: 0,
            paddingTop: 0,
            paddingBottom: 20,
            distanceX: 20,
            distanceY: 20,
            cardWidth: 256,
            animation: true
          }
        }
      },
      methods: {
        handleCommand (command, info) {
          console.log('command', command)
          console.log('info', info)
        },
        // 编辑模板
        editPoster (info, index) {
          this.currTemplateInfo = info
          this.currTemplateIndex = index
          this.initEditor()
        },
        initEditor () {
          if (!this.editorToken) {
            return this.showErrMsg('token获取失败,请刷新页面')
          }
          let templateId = this.currTemplateInfo && this.currTemplateInfo._id ? this.currTemplateInfo._id : '6254db7bccc34b01db8666ec'
          let option = {
            appid: this.editorAppInfo.appid,
            channel: this.editorAppInfo.channel,
            stamp: this.editorAppInfo.stamp,
            timestamp: this.editorAppInfo.timestamp,
            scope: this.editorAppInfo.scope, // 注意此处的scope是按照字符串传入
            templateId: templateId,
            from: 'material',
            token: this.editorToken,
            /**
             * loading配置,修改加载时logo的显示
             */
            loadingConfig: {
              logoUrl: 'https://medchat.yuemia.com/storage/b55c/e68c/png/facea3c43fa3dc95a464e83d6f30ad71.png'
            },
            /**
             * 头部配置
             */
            headConfig: {
              logoUrl: 'https://medchat.yuemia.com/storage/b55c/e68c/png/facea3c43fa3dc95a464e83d6f30ad71.png', // 头部的logo
              downloadName: '完成', // 下载按钮的名称
              // 用户点击下载按钮callback
              onClickDownload: res => {
                console.log('res', res)
                this.zhihuiEditor.closeIframe()
              },
              isDownloadImg: false // 点击下载按钮后是否下载图片
            }
          }
          // console.log('option', option)
          this.zhihuiEditor = new ZhihuiEditor(option)
          this.zhihuiEditor.openIframe()
        },
        // 选择分类
        selectCategory (info) {
          if ((this.categoryIds && info && this.categoryIds[0] === info._id) || (!this.categoryIds[0] && !info)) {
            return
          }
          if (info) {
            this.categoryIds = [info._id]
          } else {
            this.categoryIds = []
          }
          this.isEnd = false
          this.currPage = 1
          this.templateList = []
          this.fetchTemplateList()
        },
        // 名称搜索
        handleSearch (val) {
          this.searchValue = val
          this.isEnd = false
          this.currPage = 1
          this.templateList = []
          // 如果选中的是我的,则调用自己服务的接口或者当前用户的记录接口
          this.fetchTemplateList()
        },
        // 获取分类
        fetchCategoryList () {
          if (!this.accessToken) {
            return this.showErrMsg('token获取失败,请刷新页面')
          }
          let url = `获取分类的接口`
          axios.get(url, {
            headers: {
              'Authorization': 'Bearer ' + this.accessToken
            }
          }).then(res => {
            let data = res.data.data
            if (data) {
              this.categoryList = data
            }
          }).catch(err => {
            this.handleError(err)
          })
        },
        // 获取模板列表
        fetchTemplateList () {
          if (!this.accessToken) {
            return this.showErrMsg('token获取失败,请刷新页面')
          }
          let url = `获取模板的接口`
          let params = {
            filter: {state: 'success', type: 'image'}, // 不需要更改
            filterTermArr: this.categoryIds, // 根据检索的分类id进行修改
            hideObjects: true, // 不需要更改
            keyword: this.searchValue || null, // 不需要更改
            pageNumber: this.currPage, // 当前页码
            pageSize: this.pageSize, // 每页数量
            sort: {id: 'desc'} // 排序
          }
          if (this.loading) {
            return
          }
          this.loading = true
          axios.post(url, params, {
            headers: {
              'Authorization': 'Bearer ' + this.accessToken
            }
          }).then(res => {
            this.loading = false
            let data = res.data.data
            if (data) {
              this.count = data.info.total
              let templateList = data.items
              this.templateList = this.templateList.concat(templateList)
              this.$nextTick(() => {
                this.handleWaterFall()
              })
            }
          }).catch(err => {
            this.loading = false
            this.handleError(err)
          })
        },
        // --------------无限加载----------------
        handleScroll (e) {
          // 加定时器进行节流
          setTimeout(() => {
            // 元素内容高度,包括由于溢出导致的视图中不可见内容。
            let boxCcrollHeight = e.target.scrollHeight
            // 滚动距离
            let boxScrollTop = e.target.scrollTop
            // 网页可见区域高度
            let boxClientHeight = e.target.clientHeight
            // 距离底部距离长度
            let bottomLen = boxCcrollHeight - boxScrollTop - boxClientHeight
            if (this.count > this.templateList.length && bottomLen < 80 && !this.loading) {
              // 加载数据
              this.currPage += 1
              this.fetchTemplateList()
              console.log('分页')
            } else if (this.count === this.templateList.length) {
              this.isEnd = true
            }
          }, 500)
        }
      },
      created () {
        this.handleWaterFall = this.waterFall.bind(this, this.waterFallOptions)
        window.addEventListener('resize', this.handleWaterFall)
        this.fetchCategoryList()
        this.fetchTemplateList()
      },
      mounted () {
        this.$refs.scrollDiv.addEventListener('scroll', this.handleScroll, true)
      },
      beforeDestroy () {
        window.removeEventListener('resize', this.handleWaterFall)
        this.$refs.scrollDiv.removeEventListener('scroll', this.handleScroll)
      }
    }
    </script>
    <style lang="stylus" scoped>
    .template-page
      width 100%
      height 100%
      scroll()
      scroll-bar()
      background #fff
      border-radius 10px
      .poster-header
        width 100%
        padded_box(border-box, 20px)
        .category-filter
          display flex
          background #F2F5FA
          padded_box(border-box,15px 20px)
          border-radius: 6px
          .label-name
            margin-right: 16px;
            width: 42px;
            height: 32px;
            line-height: 32px;
            padding: 0;
            color: #303544;
            margin-right: 16px;
          .category-content
            width calc(100% - 58px)
            position relative
            .category-group
              display: flex;
              flex-wrap: wrap;
              align-items: center;
              padding-right: 126px;
              transition: max-height .3s;
              overflow: hidden;
              .category-tag
                display: flex;
                align-items: center;
                line-height: 20px;
                background: #fff
                font-size: 14px;
                color: #444950;
                border: 1px solid #E9E9EB;
                border-radius: 4px;
                padded_box(border-box,5px 12px)
                margin 0 8px 8px 8px
                cursor: pointer;
                &.active
                  color: #2254f4;
                  background: #E2ECFF;
                  border-color #2254f4
            .category-more
              display inline-block
              width 52px
              height 28px
              line-height 20px
              padded_box(border-box, 4px 0)
              cursor pointer
              position absolute
              right 20px
              top 0
              &.up
                .icon-fold
                  transform rotate(180deg)
              .text
                font-size 12px
                color #888B9C
                vertical-align middle
                margin-right 4px
              .icon-fold
                display inline-block
                width 14px
                height 14px
                background url('~assets/img/icon_fold_down@2x.png') no-repeat center/100%
                vertical-align middle
        .filter-box
          width 100%
          display: flex
          justify-content: space-between
          align-items: center
          margin-top 20px
          .fiter-left, .fiter-right
            display flex
            align-items: center
            >>> .search-input-box
              line-height: 32px
      .poster-wrapper
        padded_box(border-box, 0 0 20px 20px)
        .poster-list
          width 100%
          display flex
          flex-wrap wrap
          // .poster-item
          //   width: 252px;
          //   max-height: 448px;
          //   margin:0 30px 30px 0;
          //   cursor: pointer;
          //   display: flex;
          //   justify-content: center;
          //   align-items: center;
          //   img
          //     // width 100%
          //     // height 100%
          //     width 100%
          //     max-height 100%
          //     border none
          //     vertical-align middle
          .poster-item
            width: 256px;
            // max-height: 500px;
            background: #FFFFFF;
            border: 1px solid #D0D0D1;
            border-radius: 4px;
            margin 0 20px 20px 0
            position relative
            .item-header
              display: flex
              justify-content: space-between
              align-items: center
              width 100%
              height 44px
              line-height 20px
              font-size 14px
              padded_box(border-box,12px)
              .name
                width calc(100% - 24px)
                margin-right 8px
              .icon
                width 20px
                height 20px
                background url('~assets/img/icon_more@2x.png') no-repeat center/16px
                cursor pointer
            .item-cont
              width 100%
              img
                width 100%
                // max-height 454px
                height 100%
            .item-footer
              width 100%
              height: 44px;
              line-height 20px
              font-size 14px
              color #fff
              padded_box(border-box, 12px)
              background-image: linear-gradient(180deg, rgba(0,0,0,0.00) 0%, rgba(0,0,0,0.50) 100%);
              position absolute
              left 0
              right 0
              bottom 0
        .loading-box
          width 100%
          height 20px
          line-height 20px
          text-align center
          color #909399
          margin-top 10px
        .load-end
          width 100%
          height 20px
          line-height 20px
          text-align center
          color #909399
          margin-top 10px
        .default-cont
          width 100%
          height 60px
          text-align center
          line-height 20px
          // font-size 12px
          color #909399
          padded_box(border-box, 20px 0)
    </style>
    
    ···

    相关文章

      网友评论

          本文标题:vue pc端 纯js实现向下滚动无限加载

          本文链接:https://www.haomeiwen.com/subject/eyqsnrtx.html