Каскадный выбор
Каскадный выбор применяется для данных с несколькими уровнями иерархии — например, выбор провинции, города и района.
Базовое использование
<script setup>
import { ref } from 'vue'
const value1 = ref([])
const options1 = [
{
value: 'zhinan',
label: 'Руководство',
children: [
{
value: 'sheji',
label: 'Руководство по дизайну',
children: [
{ value: 'yuansze', label: 'Цвет' },
{ value: 'fuziyuanze', label: 'Шрифт' },
],
},
{
value: 'shejiguize',
label: 'Правила дизайна',
},
],
},
{
value: 'zujian',
label: 'Компоненты',
children: [
{ value: 'jiben', label: 'Базовые' },
{ value: 'fankui', label: 'Обратная связь' },
],
},
]
</script>
<template>
<!-- Базовое использование -->
<lh-cascader v-model="value1" :options="options1" />
</template><script>
export default {
data() {
return {
value1: [],
options1: [
{
value: 'zhinan',
label: 'Руководство',
children: [
{
value: 'sheji',
label: 'Руководство по дизайну',
children: [
{ value: 'yuansze', label: 'Цвет' },
{ value: 'fuziyuanze', label: 'Шрифт' },
],
},
{
value: 'shejiguize',
label: 'Правила дизайна',
},
],
},
{
value: 'zujian',
label: 'Компоненты',
children: [
{ value: 'jiben', label: 'Базовые' },
{ value: 'fankui', label: 'Обратная связь' },
],
},
],
}
},
}
</script>
<template>
<!-- Базовое использование -->
<lh-cascader v-model="value1" :options="options1" />
</template>Режим множественного выбора
Переключение в режим множественного выбора позволяет выбрать сразу несколько результатов.
<script setup>
import { ref } from 'vue'
const value2 = ref([])
const options2 = [
{
value: 'zhinan',
label: 'Руководство',
children: [
{ value: 'sheji', label: 'Руководство по дизайну' },
{ value: 'shejiguize', label: 'Правила дизайна' },
],
},
{
value: 'zujian',
label: 'Компоненты',
children: [
{ value: 'jiben', label: 'Базовые компоненты' },
{ value: 'fankui', label: 'Компоненты обратной связи' },
],
},
]
</script>
<template>
<!-- Режим множественного выбора -->
<lh-cascader v-model="value2" :options="options2" multiple />
</template><script>
export default {
data() {
return {
value2: [],
options2: [
{
value: 'zhinan',
label: 'Руководство',
children: [
{ value: 'sheji', label: 'Руководство по дизайну' },
{ value: 'shejiguize', label: 'Правила дизайна' },
],
},
{
value: 'zujian',
label: 'Компоненты',
children: [
{ value: 'jiben', label: 'Базовые компоненты' },
{ value: 'fankui', label: 'Компоненты обратной связи' },
],
},
],
}
},
}
</script>
<template>
<!-- Режим множественного выбора -->
<lh-cascader v-model="value2" :options="options2" multiple />
</template>Отключение опций
Отключение узлов через свойство disabled.
<script setup>
import { ref } from 'vue'
const value3 = ref([])
const options3 = [
{
value: 'zhinan',
label: 'Руководство',
disabled: true,
children: [
{ value: 'sheji', label: 'Руководство по дизайну' },
],
},
{
value: 'zujian',
label: 'Компоненты',
children: [
{ value: 'jiben', label: 'Базовые компоненты' },
{ value: 'fankui', label: 'Компоненты обратной связи', disabled: true },
],
},
]
</script>
<template>
<!-- Отключение опций -->
<lh-cascader v-model="value3" :options="options3" />
</template><script>
export default {
data() {
return {
value3: [],
options3: [
{
value: 'zhinan',
label: 'Руководство',
disabled: true,
children: [
{ value: 'sheji', label: 'Руководство по дизайну' },
],
},
{
value: 'zujian',
label: 'Компоненты',
children: [
{ value: 'jiben', label: 'Базовые компоненты' },
{ value: 'fankui', label: 'Компоненты обратной связи', disabled: true },
],
},
],
}
},
}
</script>
<template>
<!-- Отключение опций -->
<lh-cascader v-model="value3" :options="options3" />
</template>Размер, отключение и только чтение
size настраивает размер, disabled отключает весь селектор, readonly включает режим «только для чтения», placeholder задаёт плейсхолдер, clearable убирает кнопку очистки.
<script setup lang="ts">
import { ref } from 'vue'
const demoCascSize = ref([])
const demoCascSize2 = ref([])
const demoCascDisabled = ref([])
const demoCascReadonly = ref([])
const demoCascPlaceholder = ref([])
const demoCascNoClear = ref([])
const options = [
{
value: 'zhinan',
label: 'Руководство',
children: [
{ value: 'sheji', label: 'Руководство по дизайну' },
{ value: 'shejiguize', label: 'Правила дизайна' },
],
},
{
value: 'zujian',
label: 'Компоненты',
children: [
{ value: 'jiben', label: 'Базовые компоненты' },
{ value: 'fankui', label: 'Компоненты обратной связи' },
],
},
]
</script>
<template>
<!-- small -->
<lh-cascader v-model="demoCascSize" :options="options" size="small" placeholder="small" />
<!-- large -->
<lh-cascader v-model="demoCascSize2" :options="options" size="large" placeholder="large" />
<!-- Отключённое состояние -->
<lh-cascader v-model="demoCascDisabled" :options="options" disabled placeholder="Отключённое состояние" />
<!-- Только для чтения -->
<lh-cascader v-model="demoCascReadonly" :options="options" readonly placeholder="Только для чтения" />
<!-- Свой плейсхолдер -->
<lh-cascader v-model="demoCascPlaceholder" :options="options" placeholder="Свой плейсхолдер" />
<!-- Без очистки -->
<lh-cascader v-model="demoCascNoClear" :options="options" :clearable="false" placeholder="Без очистки" />
</template><script>
export default {
data() {
return {
demoCascSize: [],
demoCascSize2: [],
demoCascDisabled: [],
demoCascReadonly: [],
demoCascPlaceholder: [],
demoCascNoClear: [],
options: [
{
value: 'zhinan',
label: 'Руководство',
children: [
{ value: 'sheji', label: 'Руководство по дизайну' },
{ value: 'shejiguize', label: 'Правила дизайна' },
],
},
{
value: 'zujian',
label: 'Компоненты',
children: [
{ value: 'jiben', label: 'Базовые компоненты' },
{ value: 'fankui', label: 'Компоненты обратной связи' },
],
},
],
}
},
}
</script>
<template>
<!-- small -->
<lh-cascader v-model="demoCascSize" :options="options" size="small" placeholder="small" />
<!-- large -->
<lh-cascader v-model="demoCascSize2" :options="options" size="large" placeholder="large" />
<!-- Отключённое состояние -->
<lh-cascader v-model="demoCascDisabled" :options="options" disabled placeholder="Отключённое состояние" />
<!-- Только для чтения -->
<lh-cascader v-model="demoCascReadonly" :options="options" readonly placeholder="Только для чтения" />
<!-- Свой плейсхолдер -->
<lh-cascader v-model="demoCascPlaceholder" :options="options" placeholder="Свой плейсхолдер" />
<!-- Без очистки -->
<lh-cascader v-model="demoCascNoClear" :options="options" :clearable="false" placeholder="Без очистки" />
</template>Отображение пути и поиск
show-all-levelsуправляет тем, показывает ли поле ввода полный путь (по умолчаниюtrue; при отключении показывается только название последнего уровня);separatorзадаёт пользовательский разделитель пути; действует и в панели результатов поиска;- при
emit-path: falseвv-modelпривязывается только значение последнего узла, а в поле ввода всё равно отображается полный путь; check-strictlyпозволяет выбирать узел на любом уровне (клик по любому уровню сразу выбирает его, а просмотр дочерних уровней разворачивается при наведении);filterableвключает поиск: после ввода ключевого слова совпадения отображаются с полным путём;collapse-tagsсворачивает теги в режиме множественного выбора;cascadingотключает связанную отметку родительских и дочерних узлов;item-configзадаёт пользовательские псевдонимы полей узла (например, построение дерева поid/name); отрисовка пути также учитывает их.
Все примеры ниже уже содержат выбранные значения, чтобы можно было сравнить отображение без пошагового выбора: полный путь пров. Чжэцзян / г. Ханчжоу / район Сиху, при отключенном show-all-levels — только район Сиху, а при separator=" > " — пров. Чжэцзян > г. Ханчжоу > район Сиху.
<script setup lang="ts">
import { ref } from 'vue'
const demoCascFullPath = ref(['zhejiang', 'hangzhou', 'xihu'])
const demoCascAllLevels = ref(['zhejiang', 'hangzhou', 'xihu'])
const demoCascSep = ref(['zhejiang', 'hangzhou', 'xihu'])
const demoCascEmit = ref(['xihu'])
const demoCascStrict = ref(['zhejiang', 'hangzhou'])
const demoCascFilter = ref([])
const demoCascCollapse = ref(['xihu', 'haishu'])
const demoCascLoose = ref(['xihu', 'binjiang'])
const demoCascItem = ref(['zhejiang', 'hangzhou', 'xihu'])
const pathOptions = [
{
value: 'zhejiang',
label: 'пров. Чжэцзян',
children: [
{
value: 'hangzhou',
label: 'г. Ханчжоу',
children: [
{ value: 'xihu', label: 'район Сиху' },
{ value: 'binjiang', label: 'район Биньцзян' },
],
},
{
value: 'ningbo',
label: 'г. Нинбо',
children: [{ value: 'haishu', label: 'район Хайшу' }],
},
],
},
{
value: 'jiangsu',
label: 'пров. Цзянсу',
children: [
{
value: 'nanjing',
label: 'г. Нанкин',
children: [{ value: 'xuanwu', label: 'район Сюаньу' }],
},
],
},
]
const aliasedOptions = [
{
id: 'zhejiang',
name: 'пров. Чжэцзян',
sub: [
{
id: 'hangzhou',
name: 'г. Ханчжоу',
sub: [
{ id: 'xihu', name: 'район Сиху' },
{ id: 'binjiang', name: 'район Биньцзян' },
],
},
{
id: 'ningbo',
name: 'г. Нинбо',
sub: [{ id: 'haishu', name: 'район Хайшу' }],
},
],
},
]
</script>
<template>
<!-- Полный путь (по умолчанию) -->
<lh-cascader v-model="demoCascFullPath" :options="pathOptions" placeholder="Полный путь (по умолчанию)" />
<!-- Только последний уровень -->
<lh-cascader v-model="demoCascAllLevels" :options="pathOptions" :show-all-levels="false" placeholder="Только последний уровень" />
<!-- Разделитель > -->
<lh-cascader v-model="demoCascSep" :options="pathOptions" separator=" > " placeholder="Разделитель >" />
<!-- Только значение последнего уровня -->
<lh-cascader v-model="demoCascEmit" :options="pathOptions" :emit-path="false" placeholder="Только значение последнего уровня" />
<!-- Любой уровень доступен -->
<lh-cascader v-model="demoCascStrict" :options="pathOptions" check-strictly placeholder="Любой уровень доступен" />
<!-- С поиском -->
<lh-cascader v-model="demoCascFilter" :options="pathOptions" filterable placeholder="С поиском" />
<!-- Свёрнутые теги (множественный выбор) -->
<lh-cascader v-model="demoCascCollapse" :options="pathOptions" multiple collapse-tags placeholder="Свёрнутые теги (множественный выбор)" />
<!-- Родитель и потомки не связаны -->
<lh-cascader v-model="demoCascLoose" :options="pathOptions" multiple :cascading="false" placeholder="Родитель и потомки не связаны" />
<!-- Псевдонимы полей -->
<lh-cascader v-model="demoCascItem" :options="aliasedOptions" :item-config="{ value: 'id', label: 'name', children: 'sub' }" placeholder="Псевдонимы полей" />
</template><script>
export default {
data() {
return {
demoCascFullPath: ['zhejiang', 'hangzhou', 'xihu'],
demoCascAllLevels: ['zhejiang', 'hangzhou', 'xihu'],
demoCascSep: ['zhejiang', 'hangzhou', 'xihu'],
demoCascEmit: ['xihu'],
demoCascStrict: ['zhejiang', 'hangzhou'],
demoCascFilter: [],
demoCascCollapse: ['xihu', 'haishu'],
demoCascLoose: ['xihu', 'binjiang'],
demoCascItem: ['zhejiang', 'hangzhou', 'xihu'],
pathOptions: [
{
value: 'zhejiang',
label: 'пров. Чжэцзян',
children: [
{
value: 'hangzhou',
label: 'г. Ханчжоу',
children: [
{ value: 'xihu', label: 'район Сиху' },
{ value: 'binjiang', label: 'район Биньцзян' },
],
},
{
value: 'ningbo',
label: 'г. Нинбо',
children: [{ value: 'haishu', label: 'район Хайшу' }],
},
],
},
{
value: 'jiangsu',
label: 'пров. Цзянсу',
children: [
{
value: 'nanjing',
label: 'г. Нанкин',
children: [{ value: 'xuanwu', label: 'район Сюаньу' }],
},
],
},
],
aliasedOptions: [
{
id: 'zhejiang',
name: 'пров. Чжэцзян',
sub: [
{
id: 'hangzhou',
name: 'г. Ханчжоу',
sub: [
{ id: 'xihu', name: 'район Сиху' },
{ id: 'binjiang', name: 'район Биньцзян' },
],
},
{
id: 'ningbo',
name: 'г. Нинбо',
sub: [{ id: 'haishu', name: 'район Хайшу' }],
},
],
},
],
}
},
}
</script>
<template>
<!-- Полный путь (по умолчанию) -->
<lh-cascader v-model="demoCascFullPath" :options="pathOptions" placeholder="Полный путь (по умолчанию)" />
<!-- Только последний уровень -->
<lh-cascader v-model="demoCascAllLevels" :options="pathOptions" :show-all-levels="false" placeholder="Только последний уровень" />
<!-- Разделитель > -->
<lh-cascader v-model="demoCascSep" :options="pathOptions" separator=" > " placeholder="Разделитель >" />
<!-- Только значение последнего уровня -->
<lh-cascader v-model="demoCascEmit" :options="pathOptions" :emit-path="false" placeholder="Только значение последнего уровня" />
<!-- Любой уровень доступен -->
<lh-cascader v-model="demoCascStrict" :options="pathOptions" check-strictly placeholder="Любой уровень доступен" />
<!-- С поиском -->
<lh-cascader v-model="demoCascFilter" :options="pathOptions" filterable placeholder="С поиском" />
<!-- Свёрнутые теги (множественный выбор) -->
<lh-cascader v-model="demoCascCollapse" :options="pathOptions" multiple collapse-tags placeholder="Свёрнутые теги (множественный выбор)" />
<!-- Родитель и потомки не связаны -->
<lh-cascader v-model="demoCascLoose" :options="pathOptions" multiple :cascading="false" placeholder="Родитель и потомки не связаны" />
<!-- Псевдонимы полей -->
<lh-cascader v-model="demoCascItem" :options="aliasedOptions" :item-config="{ value: 'id', label: 'name', children: 'sub' }" placeholder="Псевдонимы полей" />
</template>Props
| Свойство | Описание | Тип | По умолчанию |
|---|---|---|---|
| modelValue | Привязанное значение | (string | number)[] | () => [] |
| options | Список опций | [LhCascaderNode](#type-LhCascaderNode)[] | () => [] |
| size | Размер | "small" | "medium" | "large" | "mini" | "x-large" | 'medium' |
| disabled | Отключён ли селектор | boolean | false |
| clearable | Доступна ли очистка | boolean | true |
| placeholder | Плейсхолдер (если не передано, берётся из языкового пакета cascader.placeholder; в китайском —「请选择」) | string | '' |
| readonly | Только для чтения | boolean | false |
| showAllLevels | Показывать ли в поле ввода полный путь | boolean | true |
| checkStrictly | Доступны для выбора узлы на любом уровне | boolean | false |
| emitPath | Включает ли возвращаемое значение полный путь | boolean | true |
| multiple | Множественный выбор | boolean | false |
| cascading | Каскадная связь родительских и дочерних узлов | boolean | true |
| separator | Разделитель | string | '/' |
| collapseTags | Сворачивать ли теги в режиме множественного выбора | boolean | false |
| filterable | Доступен ли поиск | boolean | false |
| itemConfig | Псевдонимы полей узла | [LhCascaderItemConfig](#type-LhCascaderItemConfig) | () => ({}) |
| sx | Сокращённые свойства компоновки (например { mt: 2, w: '50%' }), преобразуются в инлайн-стили и применяются к корневому элементу триггера (на всплывающую панель не влияют) | LhSxProps | — |
Events
| Событие | Описание | Параметр обратного вызова |
|---|---|---|
| change | Срабатывает при изменении выбранного значения | (value: (string | number)[], path: LhCascaderNode[] | null) |
| update:model-value | Срабатывает при изменении привязанного значения | value: (string | number)[] |
Структура данных
Определение типа LhCascaderNode:
interface LhCascaderNode {
value: string | number
label: string
disabled?: boolean
leaf?: boolean
children?: LhCascaderNode[]
}Типы
TypeScript-типы, экспортируемые этим компонентом (извлечены автоматически из packages/components/cascader/src/types.ts и исходников @lh-ui/share/core/*; раскройте нужный пункт, чтобы увидеть полное определение; описания свойств — в таблице Props выше):
LhCascaderProps
export interface LhCascaderProps {
modelValue?: (string | number)[]
options?: LhCascaderNode[]
size?: LhComponentSize
disabled?: boolean
clearable?: boolean
placeholder?: string
readonly?: boolean
showAllLevels?: boolean
checkStrictly?: boolean
emitPath?: boolean
multiple?: boolean
cascading?: boolean
separator?: string
collapseTags?: boolean
filterable?: boolean
itemConfig?: LhCascaderItemConfig
/** Сокращённые свойства компоновки sx */
sx?: LhSxProps
}LhCascaderItemConfig (из @lh-ui/share/core/cascader)
/**
* Отображение свойства item в props Cascader
*/
export interface LhCascaderItemConfig {
value?: string
label?: string
children?: string
disabled?: string
leaf?: string
}LhCascaderNode (из @lh-ui/share/core/cascader)
/**
* Узел Cascader
*/
export interface LhCascaderNode {
value: string | number
label: string
disabled?: boolean
leaf?: boolean
children?: LhCascaderNode[]
}LhComponentSize (из @lh-ui/share/core/shared)
export type LhComponentSize = (typeof LH_SIZE_CONST)[keyof typeof LH_SIZE_CONST]Slots
| Слот | Описание | Содержимое по умолчанию |
|---|---|---|
| no-match | Пользовательское содержимое панели поиска, если совпадений не найдено | Языковой пакет cascader.noMatch (в китайском —「无匹配数据」) |