feat(components): [cascader] add header and footer slot (#21486)

* feat(components): [cascader] add header and footer slot

* docs(components): [cascader] simplify the code

* docs(components): [cascader] merge header and footer example

* docs(components): [cascader] add clear case
This commit is contained in:
Zhong
2025-07-24 20:03:41 +08:00
committed by GitHub
parent 39f439e436
commit 283fa72bc9
5 changed files with 161 additions and 0 deletions

View File

@@ -148,6 +148,16 @@ cascader/custom-tag
:::
## Custom Header & Footer of the Dropdown ^(2.10.5)
You can customize both the header and footer of the dropdown using slots.
:::demo Use slot to customize the content.
cascader/custom-header-footer
:::
## Click to Select Node ^(2.10.5)
Allows selecting nodes by clicking, with optional control visibility.
@@ -214,6 +224,8 @@ cascader/check-on-click-node
| prefix ^(2.9.4) | content as Input prefix | — |
| suggestion-item ^(2.9.5) | custom content for suggestion item when searching | ^[object]`{ item: CascaderNode }` |
| tag ^(2.10.3) | custom tags style | ^[object]`{ data: Tag[], deleteTag: (tag: Tag) => void }` |
| header ^(2.10.5) | content at the top of the dropdown | — |
| footer ^(2.10.5) | content at the bottom of the dropdown | — |
### Cascader Exposes

View File

@@ -0,0 +1,123 @@
<template>
<div class="demo">
<div>
<p>Custom header content</p>
<el-cascader
v-model="value"
popper-class="custom-header"
:options="options"
:props="props"
clearable
>
<template #header>
<el-checkbox
v-model="checkAll"
:indeterminate="indeterminate"
@change="handleCheckAll"
>
All
</el-checkbox>
</template>
</el-cascader>
</div>
<div>
<p>Custom footer content</p>
<el-cascader v-model="value" :options="options" :props="props" clearable>
<template #footer>
<el-button link size="small" @click="handleClear"> Clear </el-button>
</template>
</el-cascader>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue'
import type { CascaderOption, CheckboxValueType } from 'element-plus'
const props = { multiple: true }
const checkAll = ref(false)
const indeterminate = ref(false)
const value = ref<string[][]>([])
const options = ref([
{
value: 'guide',
label: 'Guide',
children: [
{
value: 'disciplines',
label: 'Disciplines',
children: [
{ value: 'consistency', label: 'Consistency' },
{ value: 'feedback', label: 'Feedback' },
{ value: 'efficiency', label: 'Efficiency' },
{ value: 'controllability', label: 'Controllability' },
],
},
],
},
])
const getAllValuePaths = computed(() => {
const result: string[][] = []
const queue: { node: CascaderOption; path: string[] }[] = options.value.map(
(node) => ({ node, path: [node.value] })
)
while (queue.length > 0) {
const { node, path } = queue.shift()!
if (node.children?.length) {
node.children.forEach((child) => {
queue.push({ node: child, path: [...path, child.value as string] })
})
} else {
result.push(path)
}
}
return result
})
watch(value, (val) => {
if (val.length === 0) {
checkAll.value = false
indeterminate.value = false
} else if (val.length === getAllValuePaths.value.length) {
checkAll.value = true
indeterminate.value = false
} else {
indeterminate.value = true
}
})
const handleCheckAll = (val: CheckboxValueType) => {
indeterminate.value = false
value.value = val ? getAllValuePaths.value : []
}
const handleClear = () => {
value.value = []
}
</script>
<style>
.demo {
display: flex;
}
.demo > div {
flex: 1;
text-align: center;
}
.demo > div:not(:last-child) {
border-right: 1px solid var(--el-border-color);
}
.custom-header {
.el-checkbox {
display: flex;
height: unset;
}
}
</style>

View File

@@ -149,6 +149,9 @@
</template>
<template #content>
<div v-if="$slots.header" :class="nsCascader.e('header')" @click.stop>
<slot name="header" />
</div>
<el-cascader-panel
v-show="!filtering"
ref="cascaderPanelRef"
@@ -198,6 +201,9 @@
</li>
</slot>
</el-scrollbar>
<div v-if="$slots.footer" :class="nsCascader.e('footer')" @click.stop>
<slot name="footer" />
</div>
</template>
</el-tooltip>
</template>

View File

@@ -154,6 +154,16 @@
}
}
@include e(header) {
padding: map.get($cascader-dropdown, 'header-padding');
border-bottom: map.get($cascader-dropdown, 'border');
}
@include e(footer) {
padding: map.get($cascader-dropdown, 'footer-padding');
border-top: map.get($cascader-dropdown, 'border');
}
@include e(tags) {
position: absolute;
left: 0;

View File

@@ -705,6 +705,16 @@ $cascader-search-input-margin-left: map.merge(
$cascader-search-input-margin-left
);
$cascader-dropdown: () !default;
$cascader-dropdown: map.merge(
(
'header-padding': 10px,
'footer-padding': 10px,
'border': 1px solid getCssVar('border-color-light'),
),
$cascader-dropdown
);
//statistic
// css3 var in packages/theme-chalk/src/statistic.scss
$statistic: () !default;