9433cfb9创建于 2025年12月31日历史提交
<template>
  <view class="uni-collapse-item">
    <view class="uni-collapse-item__title" @click="openCollapse(!is_open)">
      <text class="uni-collapse-item__title-text" :class="{'is-disabled':disabled,'open--active':is_open}">{{title}}</text>
      <view class="down_arrow" :class="{'down_arrow--active': is_open}"></view>
    </view>
    <view ref="boxRef" class="uni-collapse-item__content" :class="{'box-open--active':is_open}">
      <view ref="contentRef" class="uni-collapse-item__content-box" :class="{'content-open--active':box_is_open}">
        <slot></slot>
      </view>
    </view>
  </view>
</template>

<script lang="uts" setup>
  import { ItemChildType } from '../uni-collapse/item.type.uts'

  defineOptions({
    name: "UniCollapseItem"
  })

  const props = defineProps({
    title: { type: String, default: '' },
    open: { type: Boolean, default: false },
    disabled: { type: Boolean, default: false }
  })
  let height = 0
  let is_open = ref<boolean>(props.open)
  let box_is_open = ref<boolean>(props.open)

  let boxRef = ref<UniViewElement | null>(null)
  let contentRef = ref<UniViewElement | null>(null)

  let openType = computed(() => props.open)

  // 组件唯一ID
  const elId = ref(`uni_collapse_item_${Math.ceil(Math.random() * 10e5).toString(36)}`)

  const registerChild = inject<((child : ItemChildType) => string) | null>('uni-collapse-register-child', null)
  const collapseToggle = inject<((elId : string) => string) | null>('k-collapse-child-toggle', null)

  function openOrClose(open : boolean) {
    setTimeout(() => {
      box_is_open.value = !box_is_open.value
    }, 10)
    // #ifndef MP-WEIXIN
    const bNode = boxRef.value?.style!;
    const cNode = contentRef.value?.style!;
    let hide = open ? 'flex' : 'none';
    const opacity = open ? "1" : "0"
    let ani_transform = open ? 'translateY(0)' : 'translateY(-100%)';
    bNode.setProperty('display', hide);
    nextTick(() => {
      setTimeout(() => {
        cNode.setProperty('transform', ani_transform);
        cNode.setProperty('opacity', opacity);
      }, 10)
    })
    // #endif
  }

  // 开启或关闭折叠面板
  function openCollapse(open : boolean) {
    if (props.disabled) return
    // 关闭其他已打开
    if (collapseToggle != null) {
      collapseToggle(elId.value)
    }
    is_open.value = open
    openOrClose(open)
  }


  onMounted(() => {
    if (registerChild != null) {
      const child : ItemChildType = {
        is_open,
        elId: elId.value,
        openOrClose
      }
      registerChild(child)
    }
  })

  watch(openType, (value : boolean) => {
    if (boxRef.value != null) {
      openCollapse(value)
    }
  })

  defineExpose({
    is_open,
    openOrClose,
    openCollapse
  })
</script>

<style>
  /* .uni-collapse-item {
    background-color: #fff;
  } */

  .uni-collapse-item .uni-collapse-item__title {
    flex-direction: row;
    align-items: center;
    padding: 12px 18px;
    background-color: #ffffff;
  }

  .uni-collapse-item .down_arrow {
    width: 8px;
    height: 8px;
    transform: rotate(45deg);
    border-right: 1px #999 solid;
    border-bottom: 1px #999 solid;
    margin-top: -3px;
    transition-property: transform;
    transition-duration: 0.2s;
  }

  .uni-collapse-item .down_arrow--active {
    transform: rotate(-135deg);
    margin-top: 0px;
  }

  .uni-collapse-item .uni-collapse-item__title-text {
    flex: 1;
    color: #000;
    font-size: 14px;
    font-weight: 400;
  }

  .uni-collapse-item .open--active {
    /* background-color: #f0f0f0; */
    color: #bbb;
  }

  .uni-collapse-item .is-disabled {
    color: #999;
  }

  .uni-collapse-item .uni-collapse-item__content {
    display: none;
    position: relative;
    overflow: hidden;
  }

  .uni-collapse-item .box-open--active {
    display: flex;
  }

  .uni-collapse-item .uni-collapse-item__content-box {
    width: 100%;
    transition-property: transform, opacity;
    transition-duration: 0.2s;
    transform: translateY(-100%);
    opacity: 0;
  }

  .uni-collapse-item .content-open--active {
    transform: translateY(0%);
    opacity: 1;
  }
</style>