vue3封装折叠面板函数

1.新建collapseTransition.vue文件,代码如下:

<template>
	<transition
		@before-enter="beforeEnter"
		@enter="enter"
		@leave="leave"
		@after-leave="afterLeave"
		@after-enter="afterEnter"
		@before-leave="beforeLeave"
	>
		<slot></slot>
	</transition>
</template>

<script setup lang="ts">
const beforeEnter = (el: any) => {
	el.classList.add("collapse-transition");
	el.dataset.oldPaddingTop = el.style.paddingTop;
	el.dataset.oldPaddingBottom = el.style.paddingBottom;
	el.dataset.oldOverflow = el.style.overflow;
	el.style.overflow = "hidden";
	el.style.maxHeight = "0";
	el.style.paddingTop = 0;
	el.style.paddingBottom = 0;
};
const enter = (el: any) => {
	el.style.maxHeight = el.scrollHeight + "px";
	el.style.paddingTop = el.dataset.oldPaddingTop;
	el.style.paddingBottom = el.dataset.oldPaddingBottom;
};
const afterEnter = (el: any) => {
	el.classList.remove("collapse-transition");
	el.style.maxHeight = "";
	el.style.overflow = el.dataset.oldOverflow;
};
const beforeLeave = (el: any) => {
	el.dataset.oldPaddingTop = el.style.paddingTop;
	el.dataset.oldPaddingBottom = el.style.paddingBottom;
	el.dataset.oldOverflow = el.style.overflow;
	el.style.maxHeight = el.scrollHeight + "px";
	el.style.overflow = "hidden";
};
const leave = (el: any) => {
	el.classList.add("collapse-transition");
	el.style.maxHeight = 0;
	el.style.paddingTop = 0;
	el.style.paddingBottom = 0;
};
const afterLeave = (el: any) => {
	el.classList.remove("collapse-transition");
	el.style.maxHeight = "";
	el.style.overflow = el.dataset.oldOverflow;
	el.style.paddingTop = el.dataset.oldPaddingTop;
	el.style.paddingBottom = el.dataset.oldPaddingBottom;
};
</script>

<style scoped lang="scss">
.collapse-transition {
	transition: all 0.4s ease-in-out;
}
</style>

2.新建外层容器文件collapseItem.vue,代码如下

<template>
	<div class="collapse-item">
		<div class="collapse-head">
			<div class="collapse-head-right">
				<span v-if="!slots.title" class="collapse-title">{{ attrs.title }}</span>
				<slot name="title"></slot>
			</div>
			<div class="right-arrow flx-align-center ml-10px" @click.stop="handlePanelItemClick">
				<span class="mr-6px font-size-14px">明细</span>
				<el-icon class="caret-down" :class="{ 'caret-open': isCollapse }">
					<ArrowRight />
				</el-icon>
			</div>
		</div>
		<CollapseTransition>
			<div v-show="isCollapse" class="collapse-content">
				<slot name="content"></slot>
			</div>
		</CollapseTransition>
	</div>
</template>

<script setup lang="ts">
import { useSlots, useAttrs, inject, computed } from "vue";
import CollapseTransition from "./collapseTransition.vue";
const slots = useSlots();
const attrs = useAttrs();
const activeNames: any = inject("activeNames");
const handleToggle: any = inject("toggle");

const isCollapse = computed(() => {
	return activeNames["value"].includes(attrs.name);
});

const handlePanelItemClick = () => {
	handleToggle(attrs.name);
};
</script>

<style scoped lang="scss">
.collapse-item {
	display: flex;
	flex-flow: column;
	.collapse-head {
		display: flex;
		flex-flow: row nowrap;
		align-items: center;
		overflow: hidden;
		border-bottom: none;
		border-radius: 4px 4px 0 0;
		.right-arrow {
			color: #2578f2;
			cursor: pointer;
			user-select: none;
			.caret-down {
				margin-right: 6px;
				font-size: 16px;
				transition: transform 0.3s;
				transform-origin: center center;
				&.caret-open {
					transform: rotate(90deg);
				}
			}
		}
		.collapse-head-right {
			flex: 1;
			width: 0;
			.collapse-title {
				font-size: 14px;
				color: #1b1b1b;
			}
		}
	}
}
</style>

3.再新建最外层容器collapsePanel.vue,代码如下

<template>
	<div class="collapse-panel">
		<slot></slot>
	</div>
</template>

<script setup lang="ts">
import { ref, onMounted, provide } from "vue";

const props = defineProps({
	modelValue: {
		type: [String, Array, Number]
	}, // 数据绑定
	accordion: {
		type: Boolean
	} // 是否开启手风琴模式,默认不开启
});
const emits = defineEmits(["update:modelValue", "change"]);

const activeNames = ref<Array<any>>([]);

onMounted(() => {
	setValueLists();
});

// 初始化设置激活项
const setValueLists = () => {
	if (!Array.isArray(props.modelValue)) {
		activeNames["value"] = [props.modelValue];
	} else {
		activeNames["value"] = props.modelValue;
	}
};
// 点击每项处理函数
const toggle = (name: any) => {
	if (activeNames["value"].includes(name)) {
		// 收起时
		activeNames["value"] = activeNames["value"].filter(item => item != name);
	} else {
		// 展开时
		if (props.accordion) {
			activeNames["value"] = [name];
		} else {
			activeNames["value"].push(name);
		}
	}
	emits("update:modelValue", activeNames["value"]);
	emits("change", activeNames["value"]);
};

// 提供父组件指定方法
provide("toggle", toggle);
provide("activeNames", activeNames);
</script>

<style lang="scss" scoped></style>

4.最后代码使用:

<template>
	<div class="table-list">
		<CollapsePanel v-model="activeName" @change="change" :accordion="true">
			<collapse-item :name="1">
				<template #title>
					<!-- 自定义title -->
				</template>
				<template #content>
					<!-- 自定义内容 -->
				</template>
			</collapse-item>
		</CollapsePanel>
	</div>
</template>

<script setup lang="ts">
import { ref } from "vue";
import CollapsePanel from "./collapsePanel.vue";
import CollapseItem from "./collapseItem.vue";
const activeName = ref([1]);
// 点击触发
const change = (value: any) => {
	console.log(value);
};
</script>

<style lang="scss" scoped>
.table-list {
	padding: 20px;
	.info-wrap {
		padding: 10px 0;
		.img {
			flex-shrink: 0;
			width: 36px;
			height: 36px;
			font-size: 18px;
			line-height: 36px;
			color: var(--el-color-white);
			text-align: center;
			background: #2578f2;
			border-radius: 10px;
		}
		.info-name {
			.name {
				font-size: 14px;
			}
			.num {
				font-size: 14px;
				font-weight: 400;
				color: #9b9b9b;
			}
		}
		.responsibility-list {
			display: flex;
			flex-grow: 1;
			align-items: center;
			justify-content: space-between;
			width: 300px;
			max-width: 400px;
			.res-info {
				.title {
					font-size: 14px;
					color: #9b9b9b;
				}
				.num {
					font-size: 14px;
				}
			}
		}
		.progress-bar {
			width: 400px;
		}
	}
}
</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值