Collapse.vue 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. <template>
  2. <FlexView direction="column">
  3. <slot />
  4. </FlexView>
  5. </template>
  6. <script setup lang="ts">
  7. import { provide, toRef, type Ref } from 'vue';
  8. import { useChildLinkParent } from '../composeabe/ChildItem';
  9. import FlexView from '../layout/FlexView.vue';
  10. import { ArrayUtils } from '@imengyu/imengyu-utils';
  11. export interface CollapseProps {
  12. /**
  13. * 当前展开面板的 name
  14. */
  15. modelValue: (number | string)[];
  16. /**
  17. * 是否开启手风琴模式
  18. * @default false
  19. */
  20. accordion?: boolean;
  21. /**
  22. * 动画时长(ms),为0时禁用动画
  23. * @default 300
  24. */
  25. animDuration?: number;
  26. }
  27. export interface CollapseInstance {
  28. /**
  29. * 展开指定面板
  30. * @param name 面板的 name,为空时表示所有面板
  31. */
  32. open(name?: number | string): void;
  33. /**
  34. * 关闭指定面板
  35. * @param name 面板的 name,为空时表示所有面板
  36. */
  37. close(name?: number | string): void;
  38. /**
  39. * 切换指定面板打开状态
  40. * @param name 面板的 name,为空时表示所有面板
  41. */
  42. toggle(name?: number | string): void;
  43. }
  44. const emit = defineEmits([ 'update:modelValue' ]);
  45. const props = withDefaults(defineProps<CollapseProps>(), {
  46. animDuration: 300,
  47. accordion: false,
  48. });
  49. const childNames: (number | string)[] = [];
  50. const {
  51. getPosition,
  52. } = useChildLinkParent({
  53. onClean: () => {
  54. //ArrayUtils.clear(childNames);
  55. },
  56. });
  57. export interface CollapseContext {
  58. activeName: Ref<(number|string)[]>,
  59. animDuration: Ref<number>,
  60. getPosition: (name?: string|number) => number,
  61. itemClick: (i: number | string) => void;
  62. }
  63. provide<CollapseContext>('CollapseContext', {
  64. activeName: toRef(props, 'modelValue'),
  65. animDuration: toRef(props, 'animDuration'),
  66. getPosition: (name) => {
  67. const position = getPosition().index;
  68. childNames.push(name ?? position);
  69. return position;
  70. },
  71. itemClick: (i) => {
  72. if (props.modelValue.includes(i)) {
  73. emit('update:modelValue', props.modelValue.filter((v) => v !== i));
  74. return;
  75. }
  76. if (props.accordion) {
  77. emit('update:modelValue', [i]);
  78. } else {
  79. emit('update:modelValue', [...props.modelValue, i]);
  80. }
  81. },
  82. });
  83. defineExpose<CollapseInstance>({
  84. open: (name?: number | string) => {
  85. if (name) {
  86. emit('update:modelValue', props.accordion ? [name] : [...props.modelValue, name]);
  87. return;
  88. }
  89. emit('update:modelValue', childNames);
  90. },
  91. close: (name?: number | string) => {
  92. if (name) {
  93. emit('update:modelValue', props.modelValue.filter((v) => v !== name));
  94. return;
  95. }
  96. emit('update:modelValue', []);
  97. },
  98. toggle: (name?: number | string) => {
  99. if (name) {
  100. emit('update:modelValue', props.modelValue.includes(name) ? props.modelValue.filter((v) => v !== name) : [...props.modelValue, name]);
  101. return;
  102. }
  103. emit('update:modelValue', childNames.filter((v) => !props.modelValue.includes(v)));
  104. },
  105. });
  106. </script>