<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