Cascader 계단식 선택기
계단식 선택기는 여러 계층의 데이터를 다루는 상황에 사용합니다. 예를 들어 성/시/구역 선택이 있습니다.
기본 사용법
독립적으로 사용
vue
<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>vue
<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>다중 선택 모드
다중 선택 모드로 설정하면 여러 결과를 선택할 수 있습니다.
다중 선택 모드
vue
<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>vue
<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 속성으로 노드를 비활성화합니다.
옵션 비활성화
vue
<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>vue
<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로 초기화 버튼을 끕니다.
크기, 비활성화, 읽기 전용
vue
<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>vue
<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=" > "일 때는 저장성 > 항저우시 > 시후구로 표시합니다.
경로 표시와 검색
vue
<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>vue
<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 타입 정의:
ts
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
ts
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에서 가져옴)
ts
/**
* Cascader props의 item 속성 매핑
*/
export interface LhCascaderItemConfig {
value?: string
label?: string
children?: string
disabled?: string
leaf?: string
}LhCascaderNode (@lh-ui/share/core/cascader에서 가져옴)
ts
/**
* Cascader 노드
*/
export interface LhCascaderNode {
value: string | number
label: string
disabled?: boolean
leaf?: boolean
children?: LhCascaderNode[]
}LhComponentSize (@lh-ui/share/core/shared에서 가져옴)
ts
export type LhComponentSize = (typeof LH_SIZE_CONST)[keyof typeof LH_SIZE_CONST]Slots
| 슬롯 | 설명 | 기본 내용 |
|---|---|---|
| no-match | 필터링 결과가 없을 때 검색 패널의 커스텀 내용 | 언어 팩 cascader.noMatch(중국어 '无匹配数据') |