---
URL: "/zh/components/cascader.html"
LLMS_URL: "/zh/components/cascader.md"
---

# Cascader 级联控件

文件夹、组织架构、生物分类、国家地区等等，世间万物的大多数结构都是树形结构。使用级联控件可以清晰展现其中的层级关系，并具有展开收起选择等交互功能。

## 组件注册

```js
import { FCascader } from '@fesjs/fes-design';

app.use(FCascader);
```

## 代码演示

### 基础用法

基础的树形结构展示。

```vue
<template>
    <FForm :labelWidth="160">
        <FFormItem label="选中可取消：">
            <FRadioGroup v-model="cancelable">
                <FRadio :value="true">是(默认)</FRadio>
                <FRadio :value="false">否</FRadio>
            </FRadioGroup>
        </FFormItem>
        <FFormItem label="展开次级菜单：">
            <FRadioGroup v-model="expandTrigger">
                <FRadio value="click">click(默认)</FRadio>
                <FRadio value="hover">hover</FRadio>
            </FRadioGroup>
        </FFormItem>
        <FFormItem label="父节点可选中：">
            <FRadioGroup v-model="checkStrictly">
                <FRadio value="all">是</FRadio>
                <FRadio value="">否(默认)</FRadio>
            </FRadioGroup>
        </FFormItem>
    </FForm>

    <FDivider />

    <FCascader
        :data="data"
        :cancelable="cancelable"
        :expandTrigger="expandTrigger"
        :checkStrictly="checkStrictly"
    />
</template>

<script>
import { reactive, ref } from 'vue';

function createData(level = 1, baseKey = '') {
    if (!level) {
        return undefined;
    }
    return Array.apply(null, { length: 2 }).map((_, index) => {
        const key = `${baseKey}${level}${index}`;
        return {
            label: createLabel(level),
            value: key,
            children: createData(level - 1, key),
        };
    });
}

function createLabel(level) {
    if (level === 4) {
        return '道生一';
    }
    if (level === 3) {
        return '一生二';
    }
    if (level === 2) {
        return '二生三';
    }
    if (level === 1) {
        return '三生万物';
    }
}

export default {
    setup() {
        const data = reactive(createData(4));
        const cancelable = ref(true);
        const expandTrigger = ref('hover');
        const checkStrictly = ref('');
        return {
            data,
            cancelable,
            expandTrigger,
            checkStrictly,
        };
    },
};
</script>
```

#### 可选择多个节点

可以选择多个节点。

```vue
<template>
    <FForm :labelWidth="160">
        <FFormItem label="可选中多个节点：">
            <FRadioGroup v-model="multiple">
                <FRadio :value="false">否(默认)</FRadio>
                <FRadio :value="true">是</FRadio>
            </FRadioGroup>
        </FFormItem>
    </FForm>

    <FDivider />

    <FCascader :data="data" :multiple="multiple" />
</template>

<script>
import { reactive, ref } from 'vue';

function createData(level = 1, baseKey = '') {
    if (!level) {
        return undefined;
    }
    return Array.apply(null, { length: 2 }).map((_, index) => {
        const key = `${baseKey}${level}${index}`;
        return {
            label: createLabel(level),
            value: key,
            children: createData(level - 1, key),
        };
    });
}

function createLabel(level) {
    if (level === 4) {
        return '道生一';
    }
    if (level === 3) {
        return '一生二';
    }
    if (level === 2) {
        return '二生三';
    }
    if (level === 1) {
        return '三生万物';
    }
}

export default {
    setup() {
        const data = reactive(createData(4));
        const multiple = ref(true);
        return {
            data,
            multiple,
        };
    },
};
</script>
```

### 可勾选

适用于需要选择层级时使用。

```vue
<template>
    <FForm :labelWidth="160">
        <FFormItem label="父子关联：">
            <FRadioGroup v-model="cascade">
                <FRadio :value="true">是(默认)</FRadio>
                <FRadio :value="false">否</FRadio>
            </FRadioGroup>
        </FFormItem>
    </FForm>

    <FDivider />

    <FCascader
        :data="data"
        checkable
        :cascade="cascade"
        :selectable="false"
    />
</template>

<script>
import { reactive, ref } from 'vue';

function createData(level = 1, baseKey = '') {
    if (!level) {
        return undefined;
    }
    return Array.apply(null, { length: 2 }).map((_, index) => {
        const key = `${baseKey}${level}${index}`;
        return {
            label: createLabel(level),
            value: key,
            children: createData(level - 1, key),
        };
    });
}

function createLabel(level) {
    if (level === 4) {
        return '道生一';
    }
    if (level === 3) {
        return '一生二';
    }
    if (level === 2) {
        return '二生三';
    }
    if (level === 1) {
        return '三生万物';
    }
}

export default {
    setup() {
        const data = reactive(createData(4));
        const cascade = ref(true);
        return {
            data,
            cascade,
        };
    },
};
</script>
```

#### 默认展开 + 默认选中 + 默认勾选

> 默认配置并不会做校验处理，比如`cascade = true`的时候，默认仅勾选父节点，子节点并不会自动勾选。

-   通过`expandedKeys`配置默认展开节点；
-   通过`selectedKeys`配置默认选择节点；
-   通过`checkedKeys`配置默认勾选节点；

```vue
<template>
    <FCascader
        v-model:selectedKeys="selectedKeys"
        v-model:expandedKeys="expandedKeys"
        v-model:checkedKeys="checkedKeys"
        :data="data"
        checkable
    />
</template>

<script>
import { reactive, ref } from 'vue';

function createData(level = 1, baseKey = '') {
    if (!level) {
        return undefined;
    }
    return Array.apply(null, { length: 2 }).map((_, index) => {
        const key = `${baseKey}${level}${index}`;
        return {
            label: createLabel(level),
            value: key,
            children: createData(level - 1, key),
        };
    });
}

function createLabel(level) {
    if (level === 4) {
        return '道生一';
    }
    if (level === 3) {
        return '一生二';
    }
    if (level === 2) {
        return '二生三';
    }
    if (level === 1) {
        return '三生万物';
    }
}

export default {
    setup() {
        const data = reactive(createData(4));
        const selectedKeys = ref(['4031']);
        const expandedKeys = ref(['40']);
        const checkedKeys = ref(['40']);

        return {
            data,
            selectedKeys,
            expandedKeys,
            checkedKeys,
        };
    },
};
</script>
```

#### 禁用节点

无法被选中和点击。

```vue
<template>
    <FForm :labelWidth="160">
        <FFormItem label="禁用选项：">
            <FRadioGroup v-model="disabledOption">
                <FRadio value="parent">仅父节点</FRadio>
                <FRadio value="leaf">仅叶子节点</FRadio>
                <FRadio value="specific">指定项</FRadio>
            </FRadioGroup>
        </FFormItem>
    </FForm>

    <FDivider />

    <FCascader :data="data" checkable />
</template>

<script>
import { computed, ref } from 'vue';
import { cloneDeep } from 'lodash-es';

function createData(level = 1, baseKey = '') {
    if (!level) {
        return undefined;
    }
    return Array.apply(null, { length: 2 }).map((_, index) => {
        const key = `${baseKey}${level}${index}`;
        return {
            label: createLabel(level),
            value: key,
            children: createData(level - 1, key),
        };
    });
}
function createLabel(level) {
    if (level === 4) {
        return '道生一';
    }
    if (level === 3) {
        return '一生二';
    }
    if (level === 2) {
        return '二生三';
    }
    if (level === 1) {
        return '三生万物';
    }
}

function flatNodes(nodes = []) {
    return nodes.reduce((res, node) => {
        res.push(node);
        if (node.children && node.children.length) {
            res = res.concat(flatNodes(node.children));
        }
        return res;
    }, []);
}

export default {
    setup() {
        const originData = createData(4);
        const disabledOption = ref('parent');
        const data = computed(() => {
            const data = cloneDeep(originData);
            const allNodes = flatNodes(data);
            if (disabledOption.value === 'specific') {
                data.forEach((item) => {
                    item.disabled = true;
                });
            } else if (disabledOption.value === 'parent') {
                allNodes.forEach((item) => {
                    item.children
                    && item.children.length
                    && (item.disabled = true);
                });
            } else if (disabledOption.value === 'leaf') {
                allNodes.forEach((item) => {
                    !(item.children && item.children.length)
                    && (item.disabled = true);
                });
            }

            return data;
        });
        return {
            data,
            disabledOption,
        };
    },
};
</script>
```

### 异步加载

点击展开节点时加载子选项。

```vue
<template>
    <FCascader :data="data" :loadData="loadData" checkable remote />
</template>

<script>
import { reactive } from 'vue';

function createData(level = 1, baseKey = '') {
    if (!level) {
        return undefined;
    }
    return Array.apply(null, { length: 2 }).map((_, index) => {
        const key = `${baseKey}${level}${index}`;
        return {
            label: createLabel(level),
            value: key,
            children: createData(level - 1, key),
        };
    });
}

function createLabel(level) {
    if (level === 4) {
        return '道生一';
    }
    if (level === 3) {
        return '一生二';
    }
    if (level === 2) {
        return '二生三';
    }
    if (level === 1) {
        return '三生万物';
    }
}

export default {
    setup() {
        const data = reactive([]);
        const loadData = (node) => {
            return new Promise((resolve) => {
                setTimeout(() => {
                    let children = [];
                    if (node) {
                        children = [
                            {
                                label: `${node.label}1`,
                                value: `${node.value}-1`,
                                isLeaf:
                                    node.value.split('-').length > 1,
                            },
                            {
                                label: `${node.label}2`,
                                value: `${node.value}-2`,
                                isLeaf:
                                    node.value.split('-').length > 1,
                            },
                        ];
                    } else {
                        children = createData(2);
                    }
                    resolve(children);
                }, 2000);
            });
        };

        return {
            loadData,
            data,
        };
    },
};
</script>
```

### 前缀与后缀

放一些附加展示或操作。

```vue
<template>
    <FCascader :data="data" checkable />
</template>

<script>
import { h, reactive } from 'vue';
import { PictureOutlined } from '@fesjs/fes-design/icon';

function createData(level = 1, baseKey = '', prefix, suffix) {
    if (!level) {
        return undefined;
    }
    return Array.apply(null, { length: 2 }).map((_, index) => {
        const key = `${baseKey}${level}${index}`;
        const children = createData(level - 1, key, prefix, suffix);
        return {
            label: createLabel(level),
            value: key,
            children,
            prefix: prefix ? () => h(PictureOutlined) : null,
            suffix:
                suffix && children && children.length
                    ? () => h('span', null, children.length)
                    : null,
        };
    });
}

function createLabel(level) {
    if (level === 4) {
        return '道生一';
    }
    if (level === 3) {
        return '一生二';
    }
    if (level === 2) {
        return '二生三';
    }
    if (level === 1) {
        return '三生万物';
    }
}

export default {
    setup() {
        const data = reactive(createData(4, '', true, true));

        return {
            data,
        };
    },
};
</script>
```

### 文字超长溢出

```vue
<template>
    <FCascader :data="data" />
</template>

<script>
import { reactive, ref } from 'vue';

function repeatString(str, n) {
    return new Array(n + 1).join(str);
}

function createData(level = 1, baseKey = '') {
    if (!level) {
        return undefined;
    }
    return Array.apply(null, { length: 2 }).map((_, index) => {
        const key = `${baseKey}${level}${index}`;
        return {
            label: repeatString(createLabel(level), 10),
            value: key,
            children: createData(level - 1, key),
        };
    });
}

function createLabel(level) {
    if (level === 4) {
        return '道生一';
    }
    if (level === 3) {
        return '一生二';
    }
    if (level === 2) {
        return '二生三';
    }
    if (level === 1) {
        return '三生万物';
    }
}

export default {
    setup() {
        const data = reactive(createData(4));
        const cancelable = ref(true);
        return {
            data,
            cancelable,
        };
    },
};
</script>
```


<script setup>
import Async from '../../.vitepress/.temp/components/cascader/async.vue'
import Checkable from '../../.vitepress/.temp/components/cascader/checkable.vue'
import Common from '../../.vitepress/.temp/components/cascader/common.vue'
import Default from '../../.vitepress/.temp/components/cascader/default.vue'
import Fix from '../../.vitepress/.temp/components/cascader/fix.vue'
import ItemDisabled from '../../.vitepress/.temp/components/cascader/itemDisabled.vue'
import LongLabel from '../../.vitepress/.temp/components/cascader/longLabel.vue'
import Multiple from '../../.vitepress/.temp/components/cascader/multiple.vue'
</script>


## Cascader Props

| 属性                  | 说明                                                                                                                                                                                                                      | 类型                                                          | 默认值     |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ---------- |
| data                  | 展示数据                                                                                                                                                                                                                  | Array\<CascaderOption\>                                       | `[]`       |
| expandedKeys(v-model) | 展开的节点的 key 的数组                                                                                                                                                                                                   | Array<string \| number>                                       | `[]`       |
| selectable            | 是否可选中节点                                                                                                                                                                                                            | boolean                                                       | `true`     |
| selectedKeys(v-model) | 设置选中的节点                                                                                                                                                                                                            | Array<string \| number>                                       | `[]`       |
| multiple              | 是否能选中多个节点                                                                                                                                                                                                        | boolean                                                       | `false`    |
| cancelable            | 选中后是否可以再次点击取消选中                                                                                                                                                                                            | boolean                                                       | `true`     |
| checkable             | 是否显示 `Checkbox` 选择框                                                                                                                                                                                                | boolean                                                       | `false`    |
| cascade               | 当勾选选择框时，父子节点的选择框勾选状态是否关联，相互影响                                                                                                                                                                | boolean                                                       | `true`     |
| checkStrictly         | 设置勾选策略来指定勾选回调返回的值。多选时，`all` 表示回调函数值为全部选中节点；`parent` 表示回调函数值为父节点（当父节点下所有子节点都选中时）；`child` 表示回调函数值为子节点。单选时，`all` 表示回调函数值可为父节点。 | string                                                        | `child`    |
| checkedKeys(v-model)  | 勾选节点 key 的数组                                                                                                                                                                                                       | Array<string \| number>                                       | `[]`       |
| childrenField         | 替代 `CascaderOption` 中的 `children` 字段名                                                                                                                                                                              | string                                                        | `children` |
| valueField            | 替代 `CascaderOption` 中的 `value` 字段名                                                                                                                                                                                 | string                                                        | `value`    |
| labelField            | 替代 `CascaderOption` 中的 `label` 字段名                                                                                                                                                                                 | string                                                        | `label`    |
| remote                | 是否异步获取选项，和 `loadData` 配合                                                                                                                                                                                      | boolean                                                       | `false`    |
| loadData              | 异步加载数据的回调函数                                                                                                                                                                                                    | (node: null \| CascaderOption) => Promise\<CascaderOption[]\> | -          |
| expandTrigger         | 次级菜单的展开方式，可选值为 `click`,`hover`                                                                                                                                                                              | string                                                        | `click`    |

## Cascader Events

| 事件名称 | 说明                     | 回调参数                                          |
| -------- | ------------------------ | ------------------------------------------------- |
| check    | 点击节点中的选择框时触发 | ({ checkedKeys, node, event, checked }) => void   |
| expand   | 展开、收起节点时触发     | ({ expandedKeys, node, event, expanded }) => void |
| select   | 点击节点内容时触发       | ({ selectedKeys, node, event, selected }) => void |

## Cascader Methods

| 方法名称   | 说明       | 参数    |
| ---------- | ---------- | ------- |
| selectNode | 选中节点   | (value) |
| expandNode | 展开树节点 | (value) |
| checkNode  | check 节点 | (value) |

## CascaderOption props

| 属性        | 说明                                                   | 类型                        | 默认值  |
| ----------- | ------------------------------------------------------ | --------------------------- | ------- |
| value       | 节点的 `key`，需要唯一，可使用 `valueField` 修改字段名 | string / number             | `-`     |
| label       | 节点的内容，可使用 `labelField` 修改字段名             | string                      | `-`     |
| children?   | 节点的子节点                                           | CascaderOption[]            | `[]`    |
| disabled?   | 是否禁用节点， 默认为`Cascader`组件的`disabled`        | boolean                     | `-`     |
| selectable? | 是否禁用选中节点，默认为`Cascader`组件的`selectable`   | boolean                     | `-`     |
| checkable?  | 是否禁用勾选节点，默认为`Cascader`组件的`checkable`    | boolean                     | `-`     |
| isLeaf?     | 节点是否是叶节点，在 remote 模式下是必须的             | boolean                     | `false` |
| prefix?     | 节点的前缀                                             | string / (() => VNodeChild) | `null`  |
| suffix?     | 节点的后缀                                             | string / (() => VNodeChild) | `null`  |
