| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- <script setup lang="ts">
- interface Props {
- // 默认展示的行数
- inline?: number
- // 是否默认展开
- defaultExpanded?: boolean
- // 展开按钮文字
- expandText?: string
- // 收起按钮文字
- collapseText?: string
- // 行高(px),用于计算行数
- lineHeight?: number
- }
- const props = withDefaults(defineProps<Props>(), {
- inline: 3,
- defaultExpanded: false,
- expandText: '展开',
- collapseText: '收起',
- lineHeight: 100,
- })
- const emit = defineEmits<{
- (e: 'toggle', isExpanded: boolean): void
- }>()
- const _this = getCurrentInstance()
- // 响应式数据
- const isExpanded = ref(props.defaultExpanded)
- const contentHeight = ref(0)
- const showToggle = ref(false)
- // 计算属性
- const toggleText = computed(() =>
- isExpanded.value ? props.collapseText : props.expandText,
- )
- const contentStyle = computed(() => {
- const style: Record<string, string> = {}
- if (isExpanded.value) {
- // 展开状态,不限制高度
- style.overflow = 'visible'
- }
- else {
- // 收起状态,限制行数
- style.overflow = 'hidden'
- style.maxHeight = `${props.inline * props.lineHeight}rpx`
- }
- return style
- })
- // 方法
- function handleToggle() {
- isExpanded.value = !isExpanded.value
- emit('toggle', isExpanded.value)
- }
- // 获取内容高度
- function getContentHeight() {
- return new Promise<number>((resolve) => {
- const query = uni.createSelectorQuery().in(_this)
- query.select('.collapse-panel__content').boundingClientRect()
- query.exec((res) => {
- if (res && res[0]) {
- resolve(res[0].height)
- }
- else {
- resolve(0)
- }
- })
- })
- }
- // 检查是否需要显示展开按钮
- async function checkNeedToggle() {
- await nextTick()
- try {
- const height = await getContentHeight()
- contentHeight.value = height
- const maxHeightInPx = (props.inline * props.lineHeight) / 2
- showToggle.value = height > maxHeightInPx
- }
- catch (error) {
- console.error('获取内容高度失败:', error)
- }
- }
- // 暴露方法给父组件
- defineExpose({
- expand: () => {
- isExpanded.value = true
- emit('toggle', true)
- },
- collapse: () => {
- isExpanded.value = false
- emit('toggle', false)
- },
- toggle: handleToggle,
- refresh: checkNeedToggle,
- isExpanded: computed(() => isExpanded.value),
- })
- // 监听inline变化,重新检查
- watch(() => props.inline, () => {
- if (!isExpanded.value) {
- checkNeedToggle()
- }
- })
- onMounted(() => {
- // 使用setTimeout确保DOM已渲染
- setTimeout(() => {
- checkNeedToggle()
- }, 100)
- })
- </script>
- <template>
- <view class="collapse-panel">
- <view
- class="collapse-panel__content"
- :class="{
- 'collapse-panel__content--collapsed': !isExpanded,
- 'collapse-panel__content--expanded': isExpanded,
- }"
- :style="contentStyle"
- >
- <slot />
- </view>
- <view
- v-if="showToggle"
- class="collapse-panel__toggle"
- @tap="handleToggle"
- >
- <text class="collapse-panel__toggle-text">
- {{ toggleText }}
- </text>
- <view class="collapse-panel__toggle-icon" :class="{ 'collapse-panel__toggle-icon--expanded': isExpanded }">
- <wd-icon name="arrow-down" size="22px" />
- </view>
- </view>
- </view>
- </template>
- <style lang="scss" scoped>
- .collapse-panel {
- width: 100%;
- &__content {
- overflow: hidden;
- transition: max-height 0.3s ease;
- line-height: 1.5;
- &--collapsed {
- position: relative;
- // 添加渐变遮罩效果(可选)
- &::after {
- content: '';
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- height: 60rpx;
- background: linear-gradient(to bottom, transparent, #ffffff);
- pointer-events: none;
- }
- }
- }
- &__toggle {
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 16rpx 0;
- color: #222222;
- font-size: 28rpx;
- &-text {
- margin-right: 8rpx;
- }
- &-icon {
- transition: transform 0.3s ease;
- font-size: 24rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- width: 32rpx;
- height: 32rpx;
- &--expanded {
- transform: rotate(180deg);
- }
- }
- }
- }
- // 如果在小程序中需要更精确的文本行数控制,可以使用以下样式类
- .text-lines {
- display: -webkit-box;
- -webkit-box-orient: vertical;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- </style>
|