Cascader
级联选择器
Markup Schema 案例
vue
<script lang="ts" setup>
import type { DataField } from '@silver-formily/core'
import { createForm, onFieldReact } from '@silver-formily/core'
import { Cascader, Form, FormItem, Submit } from '@silver-formily/element-plus'
import { action } from '@silver-formily/reactive'
import { createSchemaField } from '@silver-formily/vue'
type IAddress = { name: string, code: string, cities: number, districts: number } | string
function transformAddress(data = {}) {
return Object.entries(data).reduce(
(
buf,
[key, value]: [string, IAddress],
) => {
if (typeof value === 'string')
return buf.concat({ label: value, value: key })
const { name, code, cities, districts } = value as any
const _cities = transformAddress(cities)
const _districts = transformAddress(districts)
return buf.concat({
label: name,
value: code,
children: _cities.length > 0
? _cities
: (_districts.length > 0
? _districts
: undefined),
})
},
[],
)
}
function useAddress(pattern) {
onFieldReact(pattern, (field: DataField) => {
if (globalThis.window === undefined)
return
field.loading = true
fetch('/location.json')
.then(res => res.json())
.then(
action.bound((data) => {
field.dataSource = transformAddress(data)
field.loading = false
}),
)
})
}
const form = createForm({
effects: () => {
useAddress('address')
},
})
const { SchemaField, SchemaStringField } = createSchemaField({
components: {
FormItem,
Cascader,
},
})
async function onSubmit(value: Record<string, any>) {
console.log(value)
}
</script>
<template>
<Form :form="form">
<SchemaField>
<SchemaStringField
name="address"
title="地址选择"
required
x-decorator="FormItem"
x-component="Cascader"
:x-component-props="{
style: {
width: '240px',
},
}"
/>
</SchemaField>
<Submit @submit="onSubmit">
提交
</Submit>
</Form>
</template>查看源码
JSON Schema 案例
vue
<script lang="ts" setup>
import { createForm } from '@silver-formily/core'
import { Cascader, Form, FormItem, Submit } from '@silver-formily/element-plus'
import { action } from '@silver-formily/reactive'
import { createSchemaField } from '@silver-formily/vue'
type IAddress = { name: string, code: string, cities: number, districts: number } | string
function transformAddress(data = {}) {
return Object.entries(data).reduce(
(
buf,
[key, value]: [string, IAddress],
) => {
if (typeof value === 'string')
return buf.concat({ label: value, value: key })
const { name, code, cities, districts } = value as any
const _cities = transformAddress(cities)
const _districts = transformAddress(districts)
return buf.concat({
label: name,
value: code,
children: _cities.length > 0
? _cities
: (_districts.length > 0
? _districts
: undefined),
})
},
[],
)
}
function useAsyncDataSource(url, transform) {
return (field) => {
if (globalThis.window === undefined)
return
field.loading = true
fetch(url)
.then(res => res.json())
.then(
action.bound((data) => {
field.dataSource = transform(data)
field.loading = false
}),
)
}
}
const schema = {
type: 'object',
properties: {
cascader: {
'type': 'string',
'title': '地址选择',
'x-decorator': 'FormItem',
'x-component': 'Cascader',
'x-component-props': {
style: {
width: '240px',
},
},
'x-reactions': [
'{{useAsyncDataSource("/location.json",transformAddress)}}',
],
},
},
}
const form = createForm()
const { SchemaField } = createSchemaField({
components: {
FormItem,
Cascader,
},
})
async function onSubmit(value: Record<string, any>) {
console.log(value)
}
</script>
<template>
<Form :form="form">
<SchemaField :schema="schema" :scope="{ useAsyncDataSource, transformAddress }" />
<Submit @submit="onSubmit">
提交
</Submit>
</Form>
</template>查看源码
Template 案例
vue
<script lang="ts" setup>
import type { DataField } from '@silver-formily/core'
import { createForm, onFieldReact } from '@silver-formily/core'
import { Cascader, Form, FormItem, Submit } from '@silver-formily/element-plus'
import { action } from '@silver-formily/reactive'
import { Field } from '@silver-formily/vue'
type IAddress = { name: string, code: string, cities: number, districts: number } | string
function transformAddress(data = {}) {
return Object.entries(data).reduce(
(
buf,
[key, value]: [string, IAddress],
) => {
if (typeof value === 'string')
return buf.concat({ label: value, value: key })
const { name, code, cities, districts } = value as any
const _cities = transformAddress(cities)
const _districts = transformAddress(districts)
return buf.concat({
label: name,
value: code,
children: _cities.length > 0
? _cities
: (_districts.length > 0
? _districts
: undefined),
})
},
[],
)
}
function useAddress(pattern) {
onFieldReact(pattern, (field: DataField) => {
if (globalThis.window === undefined)
return
field.loading = true
fetch('/location.json')
.then(res => res.json())
.then(
action.bound((data) => {
field.dataSource = transformAddress(data)
field.loading = false
}),
)
})
}
const form = createForm({
effects: () => {
useAddress('address')
},
})
async function onSubmit(value: Record<string, any>) {
console.log(value)
}
</script>
<template>
<Form :form="form">
<Field
name="address"
title="地址选择"
required
:decorator="[FormItem]"
:component="[
Cascader,
{
style: {
width: '240px',
},
},
]"
/>
<Submit @submit="onSubmit">
提交
</Submit>
</Form>
</template>查看源码
API
参考 https://cn.element-plus.org/zh-CN/component/cascader.html
对象值与属性筛选
设置 optionAsValue: true 可将选项对象作为表单值;optionValueKeys?: string[] 仅保留指定的顶层属性。未配置时返回完整对象,空数组返回空对象,缺失属性忽略。默认 optionAsValue 为 false。
筛选只作用于表单输出,不修改 dataSource。请在白名单中保留选项标识(如 value、nodeKey 或 rowKey 对应属性),回显按标识从完整数据源读取标签。仅提供标识而不保存 label 也可以正常回显。
ts
const componentProps = {
optionAsValue: true,
optionValueKeys: ['value'],
}
// dataSource: [{ value: 1, label: '选项一', extra: '业务属性' }]
// 表单值: { value: 1 }emitPath=true 时返回对象路径数组(多选为二维数组);emitPath=false 时返回节点对象(多选为对象数组)。
vue
<script setup lang="ts">
import { createForm } from '@silver-formily/core'
import { Cascader, FormItem } from '@silver-formily/element-plus'
import { Field, FormConsumer, FormProvider } from '@silver-formily/vue'
const form = createForm()
const options = [
{ value: 1, label: 'Alpha', extra: 'Not stored in the form' },
{ value: 2, label: 'Beta', extra: 'Not stored in the form' },
]
</script>
<template>
<FormProvider :form="form">
<Field
name="choice"
title="精简对象值"
:decorator="[FormItem]"
:component="[Cascader, { optionAsValue: true, optionValueKeys: ['value'], clearable: true }]"
:data-source="options"
/>
<FormConsumer v-slot="{ form: currentForm }">
<pre>{{ JSON.stringify(currentForm.values, null, 2) }}</pre>
</FormConsumer>
</FormProvider>
</template>精简对象值:
{}查看源码