ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Vue3折叠面板(Collapse)

Vue3折叠面板(Collapse) 可自定义设置以下属性折叠面板数据items可使用 slot 替换指定 key 的 header、content、arrow、extra、lang类型Item[]默认 []当前激活 tab 面板的 keyv-model:activeKey类型number[] | number | string[] | string | null默认 null传入 string | number 类型时即为手风琴模式带边框风格的折叠面板bordered类型boolean默认 true是否禁用展开收起disabled类型boolean默认 false使折叠面板透明且无边框ghost类型boolean默认 false设置面板标题的样式headerStyle类型CSSProperties默认 {}设置面板内容的样式contentStyle类型CSSProperties默认 {}设置面板容器的样式collapseStyle类型CSSProperties默认 {}自定义箭头切换图标arrow类型VNode | Slot默认 undefined是否展示箭头showArrow类型boolean默认 true箭头位置arrowPlacement类型left | right默认 left设置面板箭头的样式arrowStyle类型CSSProperties默认 {}面板标题右侧的额外内容extra类型string | slot默认 undefined面板右上角固定内容lang例如标识 language 标识类型string | slot默认 undefined是否可复制面板内容copyable类型boolean默认 false复制按钮属性配置copyProps参考 Button Props类型object默认 {}复制按钮文本copyText类型string默认 Copy已复制按钮文本copiedText类型string默认 Copied效果如下图在线预览①创建折叠面板组件Collapse.vue其中引入使用了以下组件和工具函数Vue3按钮Button使用requestAnimationFrame模拟实现setTimeout和setIntervalscript setup langts import { ref, watchEffect } from vue import type { CSSProperties, VNode, Slot } from vue import Button from components/button import { debounce } from components/utils export interface Item { key?: string | number // 对应 activeKey如果没有传入 key 属性则默认使用数据索引 (0,1,2...) 绑定 disabled?: boolean // 是否禁用展开收起 header?: string // 面板标题 string | slot headerStyle?: CSSProperties // 设置面板标题的样式 content?: string // 面板内容 string | slot contentStyle?: CSSProperties // 设置面板内容的样式 collapseStyle?: CSSProperties // 设置面板容器的样式 arrow?: VNode // 自定义箭头切换图标 showArrow?: boolean // 是否展示箭头 arrowPlacement?: left | right // 箭头位置 arrowStyle?: CSSProperties // 设置面板箭头的样式 extra?: string // 面板标题右侧的额外内容 string | slot lang?: string // 面板右上角固定内容例如 language 标识 string | slot copyable?: boolean // 是否可复制面板内容 copyProps?: object // 复制按钮属性配置参考 Button Props copyText?: string // 复制按钮文本 copiedText?: string // 已复制按钮文本 [propName: string]: any // 用于包含带有任意数量的其他属性 } export interface Props { items?: Item[] // 折叠面板数据可使用 slot 替换指定 key 的 header、content、arrow、extra、lang activeKey?: string[] | string | number[] | number | null // (v-model) 当前激活 tab 面板的 key传入 string | number 类型时即为手风琴模式 bordered?: boolean // 带边框风格的折叠面板 disabled?: boolean // 是否禁用展开收起较低优先级 ghost?: boolean // 使折叠面板透明且无边框 headerStyle?: CSSProperties // 设置面板标题的样式较低优先级 contentStyle?: CSSProperties // 设置面板内容的样式较低优先级 collapseStyle?: CSSProperties // 设置面板容器的样式较低优先级 arrow?: VNode | Slot // 自定义箭头切换图标较低优先级 vnode | slot showArrow?: boolean // 是否展示箭头较低优先级 arrowPlacement?: left | right // 箭头位置较低优先级 arrowStyle?: CSSProperties // 设置面板箭头的样式较低优先级 extra?: string // 面板标题右侧的额外内容较低优先级 string | slot lang?: string // 面板右上角固定内容例如 language 标识较低优先级 string | slot copyable?: boolean // 是否可复制面板内容较低优先级 copyProps?: object // 复制按钮属性配置参考 Button Props较低优先级 copyText?: string // 复制按钮文本较低优先级 copiedText?: string // 已复制按钮文本较低优先级 } const props withDefaults(definePropsProps(), { items: () [], activeKey: null, bordered: true, disabled: false, ghost: false, headerStyle: () ({}), contentStyle: () ({}), collapseStyle: () ({}), arrow: undefined, showArrow: true, arrowPlacement: left, arrowStyle: () ({}), extra: undefined, lang: undefined, copyable: false, copyProps: () ({}), copyText: Copy, copiedText: Copied }) const contentRef ref() // 面板内容的模板引用 const copyBtnTxt refstring() // 复制按钮文本 const copyBtnClickedKeys ref(string | number)[]([]) // 被点击的复制按钮的 key const emits defineEmits([update:activeKey, change]) watchEffect(() { copyBtnTxt.value props.copyText }) function getComputedValue(item: Item, key: keyof Props) { let computedValue props[key as keyof Props] if (item?.[key as keyof Item] ! undefined) { computedValue item[key as keyof Item] } return computedValue } function getComputedKey(key: number | string | undefined, index: number) { if (key ! undefined) { return key } return index } function setProperties(el: Element) { ;(el as HTMLElement).style.height (el.lastElementChild as HTMLElement).offsetHeight (props.bordered !props.ghost ? 1 : 0) px ;(el as HTMLElement).style.opacity 1 } function removeProperties(el: Element) { ;(el as HTMLElement).style.removeProperty(height) ;(el as HTMLElement).style.removeProperty(opacity) } function emitValue(value: any) { emits(update:activeKey, value) emits(change, value) } function onClick(key: string | number) { if (activeCheck(key)) { if (Array.isArray(props.activeKey)) { const res (props.activeKey as any[]).filter((actKey: string | number) actKey ! key) emitValue(res) } else { emitValue(null) } } else { if (Array.isArray(props.activeKey)) { emitValue([...props.activeKey, key]) } else { emitValue(key) } } } function activeCheck(key: string | number): boolean { if (Array.isArray(props.activeKey)) { return (props.activeKey as any[]).includes(key) } else { return props.activeKey key } } function getCopyBtnTxt(item: Item, index: number) { const copyText getComputedValue(item, copyText) const copiedText getComputedValue(item, copiedText) if (copyBtnClickedKeys.value.includes(getComputedKey(item.key, index))) { return copiedText } else { return copyText } } const debounceRestCopyBtn debounce((key: string | number) { copyBtnClickedKeys.value copyBtnClickedKeys.value.filter((clickedKey: string | number) clickedKey ! key) }, 3000) function onCopy(index: number, key: string | number) { navigator.clipboard.writeText(contentRef.value[index].innerText || ).then( () { if (!copyBtnClickedKeys.value.includes(key)) { copyBtnClickedKeys.value.push(key) } /* clipboard successfully set */ debounceRestCopyBtn(key) }, (err) { /* clipboard write failed */ console.log(copy failed, err) } ) } /script template div classcollapse-wrap :class{ collapse-borderless: !bordered, collapse-ghost: ghost } div classcollapse-item :class{ collapse-arrow-left: getComputedValue(item, arrowPlacement) left, collapse-arrow-right: getComputedValue(item, arrowPlacement) right, collapse-item-disabled: getComputedValue(item, disabled) } :stylegetComputedValue(item, collapseStyle) as CSSProperties v-for(item, index) in items :keyindex div tabindex0 classcollapse-header-wrap :class{ collapse-header-no-arrow: getComputedValue(item, showArrow) } :stylegetComputedValue(item, headerStyle) as CSSProperties clickgetComputedValue(item, disabled) ? () false : onClick(getComputedKey(item.key, index)) keydown.enteronClick(getComputedKey(item.key, index)) div v-ifgetComputedValue(item, showArrow) classcollapse-arrow :stylegetComputedValue(item, arrowStyle) as CSSProperties slot namearrow :itemitem :keygetComputedKey(item.key, index) :activeactiveCheck(getComputedKey(item.key, index)) component v-ifgetComputedValue(item, arrow) :isgetComputedValue(item, arrow) classarrow-svg :class{ arrow-rotate: activeCheck(getComputedKey(item.key, index)) } / svg v-else classarrow-svg :class{ arrow-rotate: activeCheck(getComputedKey(item.key, index)) } focusablefalse >script setup langts import Collapse from ./Collapse.vue import { ref, watchEffect, h } from vue import { ArrowRightOutlined, DoubleRightOutlined, RightCircleFilled, StarOutlined, StarFilled } from ant-design/icons-vue import type { CollapseProps, CollapseItem, RadioOption } from vue-amazing-ui const collapseItems refCollapseItem[]([ { key: 1, header: This is panel header 1, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. }, { key: 2, header: This is panel header 2, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. }, { key: 3, header: This is panel header 3, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. } ]) const disabledCollapseItems refCollapseItem[]([ { key: 1, header: This is panel header 1, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. }, { key: 2, header: This is panel header 2, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. }, { key: 3, disabled: true, header: This is panel header 3, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. } ]) const nestCollapseItems refCollapseItem[]([ { key: 1, header: This is panel header 1, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. } ]) const customStyleItems refCollapseItem[]([ { key: 1, header: This is panel header 1, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. }, { key: 2, header: This is panel header 2, headerStyle: { fontSize: 16px, color: #1677ff }, contentStyle: { padding: 16px 24px, color: #eb2f96 }, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. }, { key: 3, header: This is panel header 3, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. } ]) const customArrowItems refCollapseItem[]([ { key: 1, header: This is panel header 1, arrow: h(ArrowRightOutlined), arrowStyle: { color: #1677ff }, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. }, { key: 2, header: This is panel header 2, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. }, { key: 3, header: This is panel header 3, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. } ]) const extraCollapseItems refCollapseItem[]([ { key: 1, header: This is panel header 1, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. }, { key: 2, header: This is panel header 2, extra: Extra, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. }, { key: 3, header: This is panel header 3, content: A dog is a type of domesticated animal. Known for its loyalty and faithfulness,it can be found as a welcome guest in many households across the world. } ]) const positionOptions refRadioOption[]([ { label: left, value: left }, { label: right, value: right } ]) const activeKey refCollapseProps[activeKey]([1]) const accordionActiveKey refCollapseProps[activeKey](1) const borderlessActiveKey refCollapseProps[activeKey]([1]) const disabledActiveKey refCollapseProps[activeKey]([1]) const disabledItemActiveKey refCollapseProps[activeKey]([1]) const ghostActiveKey refCollapseProps[activeKey]([1]) const nestOuterActiveKey refCollapseProps[activeKey]([1]) const nestInnerActiveKey refCollapseProps[activeKey]([1]) const customActiveKey refCollapseProps[activeKey]([1]) const customStyleActiveKey refCollapseProps[activeKey]([1]) const customArrowActiveKey1 refCollapseProps[activeKey]([1]) const customArrowActiveKey2 refCollapseProps[activeKey]([1]) const hideArrowActiveKey refCollapseProps[activeKey]([1]) const arrowPlaceActiveKey refCollapseProps[activeKey]([1]) const arrowPlacement refCollapseProps[arrowPlacement](right) const extraActiveKey refCollapseProps[activeKey]([1]) const copyableActiveKey refCollapseProps[activeKey]([1]) const customCopyActiveKey refCollapseProps[activeKey]([1]) watchEffect(() { console.log(activeKey, activeKey.value) }) watchEffect(() { console.log(accordionActiveKey, accordionActiveKey.value) }) watchEffect(() { console.log(borderlessActiveKey, borderlessActiveKey.value) }) watchEffect(() { console.log(disabledItemActiveKey, disabledItemActiveKey.value) }) watchEffect(() { console.log(ghostActiveKey, ghostActiveKey.value) }) watchEffect(() { console.log(nestOuterActiveKey, nestOuterActiveKey.value) }) watchEffect(() { console.log(nestInnerActiveKey, nestInnerActiveKey.value) }) watchEffect(() { console.log(customActiveKey, customActiveKey.value) }) watchEffect(() { console.log(customStyleActiveKey, customStyleActiveKey.value) }) watchEffect(() { console.log(customArrowActiveKey1, customArrowActiveKey1.value) }) watchEffect(() { console.log(customArrowActiveKey2, customArrowActiveKey2.value) }) watchEffect(() { console.log(hideArrowActiveKey, hideArrowActiveKey.value) }) watchEffect(() { console.log(arrowPlaceActiveKey, arrowPlaceActiveKey.value) }) watchEffect(() { console.log(arrowPlacement, arrowPlacement.value) }) watchEffect(() { console.log(extraActiveKey, extraActiveKey.value) }) watchEffect(() { console.log(copyableActiveKey, copyableActiveKey.value) }) watchEffect(() { console.log(customCopyActiveKey, customCopyActiveKey.value) }) function onChange(key: number | string) { console.log(change, key) } function handleClick(key: string | number) { console.log(key, key) } /script template div h1{{ $route.name }} {{ $route.meta.title }}/h1 h2 classmt30 mb10基本使用/h2 h3 classmb10activeKey 传入 number[] | string[]所有面板可同时展开/h3 Collapse :itemscollapseItems v-model:active-keyactiveKey changeonChange / h2 classmt30 mb10手风琴/h2 h3 classmb10只允许单个内容区域展开只需 activeKey 传入 number | string 即可/h3 Collapse :itemscollapseItems v-model:active-keyaccordionActiveKey / h2 classmt30 mb10无边框/h2 Collapse :itemscollapseItems v-model:active-keyborderlessActiveKey :borderedfalse / h2 classmt30 mb10禁用/h2 Flex vertical Collapse disabled :itemscollapseItems v-model:active-keydisabledActiveKey / Collapse :itemsdisabledCollapseItems v-model:active-keydisabledItemActiveKey / /Flex h2 classmt30 mb10幽灵折叠面板/h2 Collapse :itemscollapseItems v-model:active-keyghostActiveKey ghost / h2 classmt30 mb10面板嵌套/h2 Collapse :itemscollapseItems v-model:active-keynestOuterActiveKey template #content{ key } Collapse v-ifkey 1 :itemsnestCollapseItems v-model:active-keynestInnerActiveKey / /template /Collapse h2 classmt30 mb10自定义面板/h2 h3 classmb10自定义各个面板的背景色、圆角、边距/h3 Collapse stylebackground-color: #fff :itemscollapseItems v-model:active-keycustomActiveKey :borderedfalse :collapse-style{ backgroundColor: #f7f7f7, borderRadius: 8px, marginBottom: 16px, border: 0 } / h2 classmt30 mb10自定义样式/h2 Collapse :itemscustomStyleItems v-model:active-keycustomStyleActiveKey :header-style{ fontSize: 16px, color: #ff6900 } :content-style{ padding: 16px 24px, color: #09c8ce } :arrow-style{ fontSize: 14px, height: 25px } / h2 classmt30 mb10自定义箭头/h2 Flex vertical Collapse :itemscustomArrowItems :arrow-style{ color: #ff6900 } v-model:active-keycustomArrowActiveKey1 / Collapse :itemscustomArrowItems :arrow-style{ color: #ff6900 } v-model:active-keycustomArrowActiveKey2 template #arrow{ key, active } DoubleRightOutlined v-ifkey 2 :rotateactive ? 90 : 0 / RightCircleFilled v-ifkey 3 :rotateactive ? 90 : 0 / /template /Collapse /Flex h2 classmt30 mb10隐藏箭头/h2 Collapse :itemscollapseItems v-model:active-keyhideArrowActiveKey :show-arrowfalse / h2 classmt30 mb10箭头位置/h2 Flex vertical Radio :optionspositionOptions v-model:valuearrowPlacement button button-stylesolid / Collapse :itemscollapseItems v-model:active-keyarrowPlaceActiveKey :arrow-placementarrowPlacement / /Flex h2 classmt30 mb10面板额外内容/h2 Collapse :itemsextraCollapseItems v-model:active-keyextraActiveKey template #extra{ key } StarFilled v-ifkey 1 click.stophandleClick(key) / StarOutlined v-ifkey 3 click.stophandleClick(key) / /template /Collapse h2 classmt30 mb10可复制/h2 Collapse :itemscollapseItems v-model:active-keycopyableActiveKey langtemplate copyable / h2 classmt30 mb10自定义复制按钮/h2 Collapse :itemscollapseItems v-model:active-keycustomCopyActiveKey langjavascript copyable copy-text复制 copied-text已复制 :copy-props{ ghost: true } template #lang{ key } span v-ifkey 2typescript/span /template /Collapse /div /template
返回列表