vue3 for循环节点设置属性ref和获取的方式

<template>
  <div class="page_box">
    <section class="main">
      <Scroll
        ref='scrollBox'
        :isEnd='isEnd'
        :list="contentList"
        :isLoading='true'
        :preventDefaultException='{ tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT|AUDIO)$/ }'
        @pullup='loadMore'>
        <div class="search_wrapper">
          <SearchBox
            @search="handleSearch"/>
        </div>
        <div class="tab_wrapper"
          v-if="tabList[0]">
          <CustomTab1
            :tabs="tabList"
            @change="changeTab"/>
        </div>
        <van-collapse
          class="guide_list"
          v-if="contentList.length"
          v-model="activeIds"
          @change="changeCollapse">
          <van-collapse-item
            class="guide_item"
            title-class="item_title"
            v-for="(info, i) in contentList"
            :key="info.id"
            :title="`Q${i + 1}:${info.title}`"
            :name="info.id">
            <div class="content_wrapper">
              <div class="content_box"
                :ref="(el) => setRefMap(el, info)"
                :class="{
                  fold: info.isFold
                }"
                v-html="info.content"></div>
              <div class="btn_more"
                v-show="info.isFold"
                @click="info.isFold = false">
                查看更多
                <van-icon name="arrow-down"/>
              </div>
              <div class="loading_mask"
                v-show="info.isLoading">
                <van-loading type="spinner" />
              </div>
            </div>
          </van-collapse-item>
        </van-collapse>
        <!-- 数据为空 -->
        <NoData
          v-else/>
      </Scroll>
    </section>
  </div>
</template>
<script setup>
import Scroll from '@/components/Scroll.vue'
import SearchBox from '@/components/SearchBox.vue'
import CustomTab1 from '@/components/CustomTab1.vue'
import NoData from '@/components/NoData.vue'

import guideApi from '@/api/guide.js'

import { ref, nextTick, onMounted } from 'vue'
import { toast } from '@/utils/mixin'

const isEnd = ref(true)
const scrollBox = ref()

const keyword = ref('')
const tabList = ref([])
const loading = ref(false)
const moduleKey = ref({})
const currentPage = ref(1)
const activeIds = ref([])
const oldActiveIds = ref([])
const contentList = ref([])

// 动态ref相关
const refMap = ref({})
const setRefMap = (el, info) => {
  if (el) {
    refMap.value[`content${info.id}`] = el
  }
}

// 切换折叠状态(获取详情内容)
const changeCollapse = (val) => {
  if (val[0]) {
    let currId = ''
    val.forEach(item => {
      if (!oldActiveIds.value.includes(item)) {
        currId = item
      }
    })
    let info = contentList.value.find(item => {
      return item.id === currId
    })
    if (info && !info.content) {
      fetchGuideDetail(info)
    }
    oldActiveIds.value = val
  }
}

// 获取指南详情
const fetchGuideDetail = (info) => {
  info.isLoading = true
  guideApi.getGuideDetail(info.id).then(res => {
    info.isLoading = false
    if (res.code === 0 && res.data) {
      let data = res.data
      if (data) {
        info.content = data.content
        // 判断是否折叠内容
        nextTick(() => {
          let dom = refMap.value[`content${info.id}`]
          let height = dom.clientHeight
          info.isFold = height > 110
        })
      }
    } else {
      toast.alert(res.msg)
    }
  })
}

// 搜索
const handleSearch = (val) => {
  keyword.value = val
  currentPage.value = 1
  contentList.value = []
  fetchGuideList(true)
  // 重置展开项
  activeIds.value = []
  oldActiveIds.value = []
}

// 切换顶部tab
const changeTab = (tab) => {
  moduleKey.value = tab
  currentPage.value = 1
  contentList.value = []
  fetchGuideList(true)
  // 重置展开项
  activeIds.value = []
  oldActiveIds.value = []
}

// 加载更多
const loadMore = () => {
  if (!isEnd.value && !loading.value) {
    currentPage.value += 1
    fetchGuideList()
  }
}

// 滑倒底部
function refreshScroll () {
  nextTick(() => {
    if (scrollBox.value) {
      scrollBox.value.refresh()
    }
  })
}

// 获取指南列表
const fetchGuideList = (isReplace) => {
  let params = {
    search: keyword.value || null,
    category_id: moduleKey.value || null,
    page: currentPage.value,
    page_size: 10
  }
  loading.value = true
  guideApi.getGuideList(params).then(res => {
    if (res.code === 0 && res.data) {
      let results = res.data.results || []
      results.map(item => {
        item.isFold = false
        item.isLoading = false
        item.content = ''
      })
      contentList.value = isReplace ? results : contentList.value.concat(results)
      isEnd.value = contentList.value.length >= +res.data.count
    } else {
      isEnd.value = true
    }
    loading.value = false
    refreshScroll()
  })
}
// 获取分类列表
const fetchCategoryList = () => {
  guideApi.getCategoryList().then(res => {
    if (res.code === 0) {
      let data = res.data
      tabList.value = data
    } else {
      tabList.value = []
      toast.alert(res.msg)
    }
    moduleKey.value = tabList.value[0].id || ''
    fetchGuideList(true)
  })
}

onMounted(() => {
  fetchCategoryList()
})
</script>

<style lang="stylus" scoped>
.page_box
  width 100%
  height 100vh
  background: #F4F4F5;
  .main
    height: 100%
    // position relative
    .search_wrapper
      width 100%
      display: flex
      justify-content: space-between
      align-items: center
      padding 12px
      box-sizing: border-box
      >>> .search_box
        width 100%
    .tab_wrapper
      width 100%
      padding 0 12px
      box-sizing: border-box
    .guide_list
      width 100%
      padding 12px
      box-sizing: border-box
      .guide_item
        width 100%
        background: #fff
        border-radius: 8px
        overflow: hidden
        margin-bottom 12px
        >>>.item_title
          span
            font-weight: 600
            font-size 15px
            color: #0082A8
        .content_wrapper
          position relative
          .content_box
            width 100%
            line-height: 20px
            font-size 13px
            color: #63666A
            white-space: break-spaces
            &.fold
              height 100px
              overflow hidden
          .btn_more
            line-height: 20px
            text-align: center
            color #93999F
            margin-top: 10px
          .loading_mask
            width 100%
            height 100%
            display: flex
            justify-content: center
            align-items center
            position absolute
            top 0
            left 0
</style>
目标效果:富文本内容高度超过100px显示“查看更多”
image.png
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容