wd-collapse.vue 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. <template>
  2. <view :class="`wd-collapse ${viewmore ? 'is-viewmore' : ''} ${customClass}`" :style="customStyle">
  3. <!-- 普通或手风琴 -->
  4. <block v-if="!viewmore">
  5. <slot></slot>
  6. </block>
  7. <!-- 查看更多模式 -->
  8. <view v-else>
  9. <view
  10. :class="`wd-collapse__content ${!modelValue ? 'is-retract' : ''} `"
  11. :style="`-webkit-line-clamp: ${contentLineNum}; -webkit-box-orient: vertical`"
  12. >
  13. <slot></slot>
  14. </view>
  15. <view class="wd-collapse__more" @click="handleMore">
  16. <!-- 自定义展开按钮 -->
  17. <view v-if="useMoreSlot" :class="customMoreSlotClass">
  18. <slot name="more"></slot>
  19. </view>
  20. <!-- 显示展开或折叠按钮 -->
  21. <block v-else>
  22. <span class="wd-collapse__more-txt">{{ !modelValue ? translate('expand') : translate('retract') }}</span>
  23. <view :class="`wd-collapse__arrow ${modelValue ? 'is-retract' : ''}`">
  24. <wd-icon name="arrow-down"></wd-icon>
  25. </view>
  26. </block>
  27. </view>
  28. </view>
  29. </view>
  30. </template>
  31. <script lang="ts">
  32. export default {
  33. name: 'wd-collapse',
  34. options: {
  35. addGlobalClass: true,
  36. virtualHost: true,
  37. styleIsolation: 'shared'
  38. }
  39. }
  40. </script>
  41. <script lang="ts" setup>
  42. import wdIcon from '../wd-icon/wd-icon.vue'
  43. import { onBeforeMount, ref, watch } from 'vue'
  44. import { COLLAPSE_KEY, collapseProps, type CollapseExpose, type CollapseToggleAllOptions } from './types'
  45. import { useChildren } from '../composables/useChildren'
  46. import { isArray, isBoolean, isDef } from '../common/util'
  47. import { useTranslate } from '../composables/useTranslate'
  48. const props = defineProps(collapseProps)
  49. const emit = defineEmits(['change', 'update:modelValue'])
  50. const { translate } = useTranslate('collapse')
  51. const contentLineNum = ref<number>(0) // 查看更多的折叠面板,收起时的显示行数
  52. const { linkChildren, children } = useChildren(COLLAPSE_KEY)
  53. linkChildren({ props, toggle })
  54. watch(
  55. () => props.modelValue,
  56. (newVal) => {
  57. const { viewmore, accordion } = props
  58. // 手风琴状态下 value 类型只能为 string
  59. if (accordion && typeof newVal !== 'string') {
  60. console.error('accordion value must be string')
  61. } else if (!accordion && !viewmore && !isArray(newVal)) {
  62. console.error('value must be Array')
  63. }
  64. },
  65. { deep: true }
  66. )
  67. watch(
  68. () => props.lineNum,
  69. (newVal) => {
  70. if (newVal <= 0) {
  71. console.error('lineNum must greater than 0')
  72. }
  73. },
  74. { deep: true, immediate: true }
  75. )
  76. onBeforeMount(() => {
  77. const { lineNum, viewmore, modelValue } = props
  78. contentLineNum.value = viewmore && !modelValue ? lineNum : 0
  79. })
  80. function updateChange(activeNames: string | string[] | boolean) {
  81. emit('update:modelValue', activeNames)
  82. emit('change', {
  83. value: activeNames
  84. })
  85. }
  86. function toggle(name: string, expanded: boolean) {
  87. const { accordion, modelValue } = props
  88. if (accordion) {
  89. updateChange(name === modelValue ? '' : name)
  90. } else if (expanded) {
  91. updateChange((modelValue as string[]).concat(name))
  92. } else {
  93. updateChange((modelValue as string[]).filter((activeName) => activeName !== name))
  94. }
  95. }
  96. /**
  97. * 切换所有面板展开状态,传 true 为全部展开,false 为全部收起,不传参为全部切换
  98. * @param options 面板状态
  99. */
  100. const toggleAll = (options: CollapseToggleAllOptions = {}) => {
  101. if (props.accordion) {
  102. return
  103. }
  104. if (isBoolean(options)) {
  105. options = { expanded: options }
  106. }
  107. const { expanded, skipDisabled } = options
  108. const names: string[] = []
  109. children.forEach((item, index) => {
  110. if (item.disabled && skipDisabled) {
  111. if (item.$.exposed!.getExpanded()) {
  112. names.push(item.name || index)
  113. }
  114. } else if (isDef(expanded) ? expanded : !item.$.exposed!.getExpanded()) {
  115. names.push(item.name || index)
  116. }
  117. })
  118. updateChange(names)
  119. }
  120. /**
  121. * 查看更多点击
  122. */
  123. function handleMore() {
  124. emit('update:modelValue', !props.modelValue)
  125. emit('change', {
  126. value: !props.modelValue
  127. })
  128. }
  129. defineExpose<CollapseExpose>({
  130. toggleAll
  131. })
  132. </script>
  133. <style lang="scss" scoped>
  134. @import './index.scss';
  135. </style>