从零搭建Vue3.0组件库之折叠菜单
一.Collapse组件
<template>
<div class="z-collapse">
<slot></slot>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue";
export default defineComponent({
name: "ZCollapse",
props: {
accordion: Boolean, // 是否是手风琴模式
modelValue: { // 当前展开的值
type: [Array, String] as PropType<string | Array<string>>,
default:()=>[]
},
},
setup(props,{emit}){
return {}
}
});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
二.CollapseItem组件
<template>
<div class="z-collapse-item">
<div class="z-collapse-item__header">
<slot name="title">{{title}}</slot>
</div>
<div class="z-collapse-item__content">
<slot></slot>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "ZCollapseItem",
props: {
title: {
type: String,
default: "",
},
name: {
type: [String, Number],
default: () => Math.floor(Math.random() * 10000),
},
disabled: Boolean,
}
});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
CollapseItem为折叠菜单中的每一项
@import "mixins/mixins";
@import "common/var";
@include b(collapse) {
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
font-size: 13px;
}
@include b(collapse-item) {
@include e(header) {
display: flex;
cursor: pointer;
line-height: 48px;
border-bottom: 1px solid #ccc;
}
@include e(content) {
padding-bottom: 25px;
border-bottom: 1px solid #ccc;
}
&:last-child {
margin-bottom: -1px;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
三.组件间跨级通信
父组件用于收集子组件的状态。 collapse.vue
import mitt from "mitt";
const activeNames = ref([].concat(props.modelValue)); // 当前激活的列表
const collapseMitt = mitt(); // 用于子类触发事件
watch(()=>props.modelValue,()=>{ // 值变化后,更新当前激活的activeNames
activeNames.value = [].concat(props.modelValue)
})
provide("collapse", { // 提供给子组件
activeNames,
collapseMitt
});
const handleItemClick = (name) => {}; // 监听子组件的点击事件
collapseMitt.on("item-click", handleItemClick);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
子组件注入父组件提供的数据
<template>
<div class="z-collapse-item">
<div class="z-collapse-item__header" @click="handleHeaderClick">
<slot name="title">{{ title }}</slot>
</div>
<div class="z-collapse-item__content" v-show="isActive">
<slot></slot>
</div>
</div>
</template>
<script lang="ts">
import mitt, { Emitter } from "mitt";
import { defineComponent, inject, Ref } from "vue";
export default defineComponent({
name: "ZCollapseItem",
props: {
title: {
type: String,
default: "",
},
name: {
type: [String, Number],
default: () => Math.floor(Math.random() * 10000),
},
disabled: Boolean,
},
setup(props) {
const collapse = inject<{ activeNames: Ref; collapseMitt: Emitter }>(
"collapse"
);
const handleHeaderClick = () => { // 点击时触发
if (props.disabled) return;
collapse.collapseMitt.emit("item-click", props.name);
};
const isActive = computed(() => { // 当前是否展开
return collapse.activeNames.value.includes(props.name);
});
return {
handleHeaderClick,
isActive,
};
},
});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
四.手风琴效果
const setValue = (_activeNames) => {
activeNames.value = [].concat(_activeNames);
const value = props.accordion ? activeNames.value[0] : activeNames.value;
emit("update:modelValue", value);
};
const handleItemClick = (name) => {
if (props.accordion) { // 手风琴效果
let v = activeNames.value[0] === name ? "" : name;
setValue(v);
} else { // 默认展开效果
let _activeNames = activeNames.value.slice(0);
const index = _activeNames.indexOf(name);
if (index > -1) {
_activeNames.splice(index, 1);
} else {
_activeNames.push(name);
}
setValue(_activeNames);
}
};
collapseMitt.on("item-click", handleItemClick);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
五.过渡动画组件
<template>
<transition v-on="on">
<slot></slot>
</transition>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
setup() {
return {
on: {
beforeEnter() {
console.log("beforeEnter");
},
enter() {
console.log("enter");
},
afterEnter() {
console.log("afterEnter");
},
beforeLeave() {
console.log("beforeLeave");
},
leave() {
console.log("leave");
},
afterLeave() {
console.log("afterLeave");
},
},
};
},
});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
transition组件通过js控制动画
<ZTransition>
<div v-show="isActive">
<div class="z-collapse-item__content">
<slot></slot>
</div>
</div>
</ZTransition>
1
2
3
4
5
6
7
2
3
4
5
6
7
<template>
<transition v-on="on">
<slot></slot>
</transition>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
setup() {
return {
on: {
beforeEnter(el) { // 进入前 添加过度 设为初始状态
el.classList.add("collapse-transition");
el.style.height = 0;
},
enter(el) { // 设置结束状态
el.style.height = el.scrollHeight + "px";
el.style.overflow = "hidden";
},
afterEnter(el) { // 完成后删除样式
el.classList.remove("collapse-transition");
},
leave(el) { // 离开时 设置离开的目标
el.classList.add("collapse-transition");
el.style.height = 0;
},
afterLeave(el) { // 离开后删除样式即可
el.classList.remove("collapse-transition");
}
}
};
}
});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36