Skip to content

Cascader 级联选择

介绍

级联选择器,用于多层级数据的选择,典型场景为省市区选择。

基础用法

传入options列表。

html
<template>
    <nut-cell title="选择地址" :desc="demo1.value.toString() || '请选择地址'" @click="demo1.visible = true"> </nut-cell>
    <nut-cascader
      title="地址选择"
      v-model:visible="demo1.visible"
      v-model="demo1.value"
      @change="events.change"
      @pathChange="events.pathChange"
      :options="demo1.options"
    ></nut-cascader>
</template>
<script lang="ts">
import { reactive } from 'vue';
export default {
  setup() {
    // 基础用法
    const demo1 = reactive({
      visible: false,
      value: ['湖南'],
      options: [
        {
          value: '浙江',
          text: '浙江',
          children: [
            {
              value: '杭州',
              text: '杭州',
              disabled: true,
              children: [
                { value: '西湖区', text: '西湖区' },
                { value: '余杭区', text: '余杭区' }
              ]
            },
            {
              value: '温州',
              text: '温州',
              children: [
                { value: '鹿城区', text: '鹿城区' },
                { value: '瓯海区', text: '瓯海区' }
              ]
            }
          ]
        },
        {
          value: '湖南',
          text: '湖南',
          disabled: true
        },
        {
          value: '福建',
          text: '福建',
          children: [
            {
              value: '福州',
              text: '福州',
              leaf: false,
              children: []
            },
            {
              value: '厦门',
              text: '厦门',
            },
          ]
        }
      ]
    });
    const events = {
      change(...args: any) {
        console.log('change', ...args);
      },
      pathChange(...args: any) {
        console.log('pathChange', ...args);
      }
    };

    return {
      demo1,
      events
    }
  }
}
</script>

自定义属性名称

可通过textKeyvalueKeychildrenKey指定属性名。

html
<template>
  <nut-cell title="选择地址" :desc="demo2.value.toString() || '请选择地址'" @click="demo2.visible = true"> </nut-cell>
    <nut-cascader
      title="地址选择"
      v-model:visible="demo2.visible"
      v-model="demo2.value"
      labelKey="text"
      @change="events.change"
      @pathChange="events.pathChange"
      valueKey="text"
      childrenKey="items"
      :options="demo2.options"
    ></nut-cascader>
</template>
<script lang="ts">
import { reactive } from 'vue';
export default {
  setup() {
    // 自定义属性名称
    const demo2 = reactive({
      visible: false,
      value: ['福建', '福州', '台江区'],
      options: [
        {
          text: '浙江',
          items: [
            {
              text: '杭州',
              disabled: true,
              items: [{ text: '西湖区' }, { text: '余杭区' }]
            },
            {
              text: '温州',
              items: [{ text: '鹿城区' }, { text: '瓯海区' }]
            }
          ]
        },
        {
          text: '福建',
          items: [
            {
              text: '福州',
              items: [{ text: '鼓楼区' }, { text: '台江区' }]
            }
          ]
        }
      ]
    });
    const events = {
      change(...args: any) {
        console.log('change', ...args);
      },
      pathChange(...args: any) {
        console.log('pathChange', ...args);
      },
    };

    return { demo2, events };
  }
}
</script>

动态加载

使用lazy标识是否需要动态获取数据,此时不传options代表所有数据都需要通过lazyLoad加载,首次加载通过root属性区分,当遇到非叶子节点时会调用lazyLoad方法,参数为当前节点和resolve方法,注意resolve方法必须调用,不传子节点时会被当做叶子节点处理。

html
<template>
    <nut-cell title="选择地址" :desc="demo3.value.toString() || '请选择地址'" @click="demo3.visible = true"> </nut-cell>
    <nut-cascader
      title="地址选择"
      v-model:visible="demo3.visible"
      v-model="demo3.value"
      @change="events.change"
      @pathChange="events.pathChange"
      lazy
      :lazyLoad="demo3.lazyLoad"
    ></nut-cascader>
</template>
<script lang="ts">
import { reactive } from 'vue';
export default {
  setup() {
    const demo3 = reactive({
      visible: false,
      value: ['A0', 'A12', 'A23', 'A32'],
      lazyLoad(node: any, resolve: (children: any) => void) {
        setTimeout(() => {
          // root表示第一层数据
          if (node.root) {
            resolve([
              { value: 'A0', text: 'A0' },
              { value: 'B0', text: 'B0' },
              { value: 'C0', text: 'C0' }
            ]);
          } else {
            const { value, level } = node;
            const text = value.substring(0, 1);
            const value1 = `${text}${level + 1}1`;
            const value2 = `${text}${level + 1}2`;
            const value3 = `${text}${level + 1}3`;
            resolve([
              { value: value1, text: value1, leaf: level >= 6 },
              { value: value2, text: value2, leaf: level >= 6 },
              { value: value3, text: value3, leaf: level >= 6 }
            ]);
          }
        }, 2000);
      }
    });
    const events = {
      change(...args: any) {
        console.log('change', ...args);
      },
      pathChange(...args: any) {
        console.log('pathChange', ...args);
      },
    };

    return { demo3, events };
  }
}
</script>

部分数据动态加载

html
<template>
  <nut-cell title="选择地址" :desc="demo4.value.toString() || '请选择地址'" @click="demo4.visible = true"> </nut-cell>
  <nut-cascader
    title="地址选择"
    v-model:visible="demo4.visible"
    v-model="demo4.value"
    @change="events.change"
    @pathChange="events.pathChange"
    :options="demo4.options"
    lazy
    :lazyLoad="demo4.lazyLoad"
  ></nut-cascader>
</template>
<script lang="ts">
import { reactive } from 'vue';
export default {
  setup() {
    const demo4 = reactive({
      visible: false,
      value: [],
      options: [
        { value: 'A0', text: 'A0' },
        {
          value: 'B0',
          text: 'B0',
          children: [
            { value: 'B11', text: 'B11', leaf: true },
            { value: 'B12', text: 'B12' }
          ]
        },
        { value: 'C0', text: 'C0' }
      ],
      lazyLoad(node: any, resolve: (children: any) => void) {
        setTimeout(() => {
          const { value, level } = node;
          const text = value.substring(0, 1);
          const value1 = `${text}${level + 1}1`;
          const value2 = `${text}${level + 1}2`;
          resolve([
            { value: value1, text: value1, leaf: level >= 2 },
            { value: value2, text: value2, leaf: level >= 1 }
          ]);
        }, 500);
      }
    });
    const events = {
      change(...args: any) {
        console.log('change', ...args);
      },
      pathChange(...args: any) {
        console.log('pathChange', ...args);
      },
    };

    return { demo4, events };
  }
}
</script>

自动转换

如果你的数据为可转换为树形结构的扁平结构时,可以通过convertConfig告诉组件需要进行自动转换,convertConfig接受4个参数,topId为顶层节点的父级id,idKey为节点唯一id,pidKey为指向父节点id的属性名,存在sortKey将根据指定字段调用Array.prototype.sort()进行同层排序。

html
<template>
    <nut-cell title="选择地址" :desc="demo5.value.toString() || '请选择地址'" @click="demo5.visible = true"> </nut-cell>
    <nut-cascader
      title="地址选择"
      v-model:visible="demo5.visible"
      v-model="demo5.value"
      @change="events.change"
      @pathChange="events.pathChange"
      :options="demo5.options"
      :convertConfig="demo5.convertConfig"
    ></nut-cascader>
</template>
<script lang="ts">
import { reactive } from 'vue';
export default {
  setup() {
    const demo5 = reactive({
      visible: false,
      value: ['广东省', '广州市'],
      convertConfig: {
        topId: null,
        idKey: 'id',
        pidKey: 'pid',
        sortKey: ''
      },
      options: [
        { value: '北京', text: '北京', id: 1, pid: null },
        { value: '朝阳区', text: '朝阳区', id: 11, pid: 1 },
        { value: '亦庄', text: '亦庄', id: 111, pid: 11 },
        { value: '广东省', text: '广东省', id: 2, pid: null },
        { value: '广州市', text: '广州市', id: 21, pid: 2 }
      ]
    });
    const events = {
      change(...args: any) {
        console.log('change', ...args);
      },
      pathChange(...args: any) {
        console.log('pathChange', ...args);
      },
    };

    return { demo5, events };
  }
}
</script>

API

Props

参数说明类型默认值
v-model选中值,双向绑定Array-
v-model:visible显示选择层booleanfalse
options级联数据Array-
lazy是否开启动态加载boolean-
lazy-load动态加载回调,开启动态加载时生效Function-
value-key自定义 options 结构中 value 的字段string-
text-key自定义 options 结构中 text 的字段string-
children-key自定义 options 结构中 children 的字段string-
convert-configoptions 为可转换为树形结构的扁平结构时,配置转换规则object-
title标题string''
close-icon-position取消按钮位置,继承 Popup 组件string"top-right"
closeable是否显示关闭按钮,继承 Popup 组件booleanfalse
poppable是否需要弹层展示(设置为 false 后,title 失效)booleantrue
title-gutter v1.1.7标签间隙number | string0
title-size v1.1.7标签栏字体尺寸大小 可选值 large normal smallstringnormal
title-ellipsis v1.1.7是否省略过长的标题文字booleantrue
title-type v1.1.7选中底部展示样式 可选值 line、smilestringline

Events

事件名说明回调参数
change选中值改变时触发(value, pathNodes)
path-change选中项改变时触发(pathNodes)

Slots

名称说明
title v1.5.4自定义顶部标题部分

options数据结构

键名说明类型默认值
text显示名称,可以通过text-key更改键名string-
value唯一标识,可以通过value-key更改键名string number-
children子级选项,可以通过children-key更改键名array-
disabled是否禁用booleanfalse
leaf是否叶子节点booleanchildren为空时leaf等于true,不为空时leaf等于false
loading加载状态booleanfalse

主题定制

样式变量

组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 ConfigProvider 组件

名称默认值
--nut-cascader-font-sizevar(--nut-font-size-2)
--nut-cascader-line-height22px
--nut-cascader-tabs-item-padding0 10px
--nut-cascader-bar-padding24px 20px 17px
--nut-cascader-bar-font-sizevar(--nut-font-size-4)
--nut-cascader-bar-line-height20px
--nut-cascader-bar-colorvar(--nut-title-color)
--nut-cascader-item-padding10px 20px
--nut-cascader-item-colorvar(--nut-title-color)
--nut-cascader-item-font-sizevar(--nut-font-size-2)
--nut-cascader-item-active-colorvar(--nut-primary-color)

MIT Licensed