index.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <template>
  2. <view :class="['nut-collapse-item', { 'nut-collapse-item-left': classDirection == 'left' }, { 'nut-collapse-item-icon': icon }]">
  3. <view :class="['collapse-item', { 'item-expanded': openExpanded }, { 'nut-collapse-item-disabled': disabled }]" @click="toggleOpen">
  4. <view class="collapse-title">
  5. <view v-html="title"></view>
  6. </view>
  7. <view v-if="subTitle" v-html="subTitle" class="subTitle"></view>
  8. <i v-if="icon" :class="['collapse-icon', { 'col-expanded': openExpanded }, { 'collapse-icon-disabled': disabled }]" :style="iconStyle"></i>
  9. <i v-else :class="['collapse-icon', { 'col-expanded': openExpanded }, { 'collapse-icon-disabled': disabled }]"></i>
  10. </view>
  11. <view class="collapse-wrapper" ref="wrapperRef">
  12. <view class="collapse-content" ref="contentRef">
  13. <slot></slot>
  14. </view>
  15. </view>
  16. </view>
  17. </template>
  18. <script lang="ts">
  19. import { reactive, toRefs, onMounted, ref, nextTick, computed, watch } from 'vue';
  20. import { createComponent } from '@/utils/create';
  21. import { useParent } from '@/utils/useRelation/useParent';
  22. import { COLLAPSE_KEY } from './../collapse/index.vue';
  23. const { create } = createComponent('collapse-item');
  24. export default create({
  25. props: {
  26. title: {
  27. type: String,
  28. default: ''
  29. },
  30. subTitle: {
  31. type: String,
  32. default: ''
  33. },
  34. disabled: {
  35. type: Boolean,
  36. default: false
  37. },
  38. name: {
  39. type: [Number, String],
  40. default: -1,
  41. required: true
  42. },
  43. collapseRef: {
  44. type: Object
  45. }
  46. },
  47. setup(props) {
  48. const collapse = useParent(COLLAPSE_KEY);
  49. const parent: any = reactive(collapse.parent as any);
  50. const index: any = reactive(collapse.index as any);
  51. const proxyData = reactive({
  52. openExpanded: false,
  53. classDirection: 'right',
  54. iconStyle: {
  55. width: '20px',
  56. height: '20px',
  57. 'background-image': 'url(https://img10.360buyimg.com/imagetools/jfs/t1/111306/10/17422/341/5f58aa0eEe9218dd6/28d76a42db334e31.png)',
  58. 'background-repeat': 'no-repeat',
  59. 'background-size': '100% 100%',
  60. transform: 'rotate(0deg)'
  61. }
  62. });
  63. // 获取 Dom 元素
  64. const wrapperRef: any = ref(null);
  65. const contentRef: any = ref(null);
  66. // 清除 willChange 减少性能浪费
  67. const onTransitionEnd = () => {
  68. const wrapperRefEle: any = document.getElementsByClassName('collapse-wrapper')[0];
  69. wrapperRefEle.style.willChange = 'auto';
  70. };
  71. // 手风琴模式
  72. const animation = () => {
  73. const wrapperRefEle: any = wrapperRef.value;
  74. const contentRefEle: any = contentRef.value;
  75. if (!wrapperRefEle || !contentRefEle) {
  76. return;
  77. }
  78. const offsetHeight = contentRefEle.offsetHeight;
  79. if (offsetHeight) {
  80. const contentHeight = `${offsetHeight}px`;
  81. wrapperRefEle.style.willChange = 'height';
  82. wrapperRefEle.style.height = !proxyData.openExpanded ? 0 : contentHeight;
  83. if (parent.icon && !proxyData.openExpanded) {
  84. proxyData.iconStyle['transform'] = 'rotate(0deg)';
  85. } else {
  86. proxyData.iconStyle['transform'] = 'rotate(' + parent.rotate + 'deg)';
  87. }
  88. }
  89. if (!proxyData.openExpanded) {
  90. onTransitionEnd();
  91. }
  92. };
  93. const open = () => {
  94. proxyData.openExpanded = !proxyData.openExpanded;
  95. animation();
  96. };
  97. const defaultOpen = () => {
  98. open();
  99. if (parent.icon) {
  100. proxyData['iconStyle']['transform'] = 'rotate(' + parent.rotate + 'deg)';
  101. }
  102. };
  103. const currentName = computed(() => props.name ?? index.value);
  104. const toggleOpen = () => {
  105. if (parent.accordion) {
  106. parent.children.forEach((item: any, index: number) => {
  107. if (currentName.value == item.name) {
  108. item.changeOpen(!item.openExpanded);
  109. } else {
  110. item.changeOpen(false);
  111. item.animation();
  112. }
  113. });
  114. nextTick(() => {
  115. parent.changeVal(currentName.value, !proxyData.openExpanded);
  116. animation();
  117. });
  118. } else {
  119. parent.changeValAry(props.name);
  120. open();
  121. }
  122. };
  123. // 更改子组件展示
  124. const changeOpen = (bol: boolean) => {
  125. proxyData.openExpanded = bol;
  126. };
  127. const expanded = computed(() => {
  128. if (parent) {
  129. return parent.isExpanded(props.name);
  130. }
  131. return null;
  132. });
  133. watch(expanded, (value, oldValue) => {
  134. if (value) {
  135. proxyData.openExpanded = true;
  136. }
  137. });
  138. onMounted(() => {
  139. const { name } = props;
  140. const active = parent && parent.value;
  141. if (typeof active == 'number' || typeof active == 'string') {
  142. if (name == active) {
  143. defaultOpen();
  144. }
  145. } else if (Object.values(active) instanceof Array) {
  146. const f = Object.values(active).filter(item => item == name);
  147. if (f.length > 0) {
  148. defaultOpen();
  149. }
  150. }
  151. proxyData.classDirection = parent.expandIconPosition;
  152. if (parent.icon) {
  153. proxyData.iconStyle['background-image'] = 'url(' + parent.icon + ')';
  154. }
  155. if (parent.iconWidth) {
  156. proxyData.iconStyle['width'] = parent.conWidth;
  157. }
  158. if (parent.iconHeght) {
  159. proxyData.iconStyle['height'] = parent.iconHeight;
  160. }
  161. });
  162. return {
  163. ...toRefs(proxyData),
  164. ...toRefs(parent),
  165. wrapperRef,
  166. contentRef,
  167. open,
  168. toggleOpen,
  169. changeOpen,
  170. animation
  171. };
  172. }
  173. });
  174. </script>
  175. <style lang="scss" scoped>
  176. @import './index.scss';
  177. </style>