QueryFormItem
基于
QueryForm的装饰器组件,用于按条件请求并更新当前字段的dataSource。
SelectTable + 分页
<script setup lang="ts">
import type { ISchema } from '@silver-formily/json-schema'
import { createForm } from '@silver-formily/core'
import { QueryFormItem, SelectTable } from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
import { ElButton, ElMessage } from 'element-plus'
import { createUserRequest } from './mock-user-request'
const form = createForm()
const request = createUserRequest()
const querySchema: ISchema = {
type: 'object',
properties: {
keyword: {
'type': 'string',
'title': 'Keyword',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
clearable: true,
placeholder: 'Search by name',
},
},
department: {
'type': 'string',
'title': 'Department',
'enum': [
{ label: 'All', value: '' },
{ label: 'R&D', value: 'R&D' },
{ label: 'Product', value: 'Product' },
],
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
clearable: true,
},
},
},
}
async function handleSubmit() {
try {
const values = await form.submit()
ElMessage.success(`Submit: ${JSON.stringify(values)}`)
}
catch {
ElMessage.error('Please select at least one user before submit')
}
}
const schema: ISchema = {
type: 'object',
properties: {
selectedUsers: {
'type': 'array',
'x-validator': [
{
required: true,
message: 'Please select at least one user',
},
],
'x-decorator': 'QueryFormItem',
'x-decorator-props': {
label: 'Target Users',
required: true,
tooltip: 'Select at least one user from the table',
extra: 'Use the query area to filter the table before selecting.',
querySchema,
request,
paginationProps: {
pageSize: 8,
},
queryFormProps: {
submitText: 'Search',
resetText: 'Reset',
},
},
'x-component': 'SelectTable',
'x-component-props': {
mode: 'multiple',
rowKey: 'id',
columns: [
{ prop: 'name', label: 'Name' },
{ prop: 'department', label: 'Department' },
],
},
},
},
}
const { SchemaField } = createSchemaField({
components: {
QueryFormItem,
SelectTable,
},
})
</script>
<template>
<FormProvider :form="form">
<SchemaField :schema="schema" />
<ElButton type="primary" style="margin-top: 12px;" @click="handleSubmit">
Submit
</ElButton>
</FormProvider>
</template>Target Users:
暂无数据
共 0 条
- 1
Use the query area to filter the table before selecting.
查看源码
Tree + 轻量模式(无分页)
<script setup lang="ts">
import type { ISchema } from '@silver-formily/json-schema'
import { createForm } from '@silver-formily/core'
import { QueryFormItem, Tree } from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
import { ElButton, ElMessage } from 'element-plus'
interface TreeNode {
id: number
label: string
children?: TreeNode[]
}
const form = createForm()
const source: TreeNode[] = [
{
id: 1,
label: 'East Region',
children: [
{ id: 11, label: 'Shanghai' },
{ id: 12, label: 'Hangzhou' },
],
},
{
id: 2,
label: 'South Region',
children: [
{ id: 21, label: 'Shenzhen' },
{ id: 22, label: 'Guangzhou' },
],
},
]
function filterTree(nodes: TreeNode[], keyword: string): TreeNode[] {
if (!keyword)
return nodes
return nodes
.map((node) => {
const children = node.children ? filterTree(node.children, keyword) : undefined
if (node.label.includes(keyword) || (children && children.length > 0)) {
return { ...node, children }
}
return null
})
.filter(Boolean) as TreeNode[]
}
const querySchema: ISchema = {
type: 'object',
properties: {
keyword: {
'type': 'string',
'title': 'Keyword',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
clearable: true,
placeholder: 'Filter node name',
},
},
},
}
async function request(values: Record<string, any>) {
await new Promise(resolve => setTimeout(resolve, 150))
const keyword = `${values.keyword ?? ''}`.trim()
const data = filterTree(source, keyword)
return {
data,
success: true,
total: data.length,
}
}
async function handleSubmit() {
try {
const values = await form.submit()
ElMessage.success(`Submit: ${JSON.stringify(values)}`)
}
catch {
ElMessage.error('Please select at least one node before submit')
}
}
const schema: ISchema = {
type: 'object',
properties: {
selectedNodes: {
'type': 'array',
'x-validator': [
{
required: true,
message: 'Please select at least one node',
},
],
'x-decorator': 'QueryFormItem',
'x-decorator-props': {
label: 'Target Nodes',
required: true,
tooltip: 'Select at least one node from the tree',
extra: 'Light mode still supports FormItem label/required/feedback.',
querySchema,
request,
mode: 'light',
pagination: false,
queryFormProps: {
throttleWait: 200,
},
},
'x-component': 'Tree',
'x-component-props': {
nodeKey: 'id',
maxHeight: 260,
},
},
},
}
const { SchemaField } = createSchemaField({
components: {
QueryFormItem,
Tree,
},
})
</script>
<template>
<FormProvider :form="form">
<SchemaField :schema="schema" />
<ElButton type="primary" style="margin-top: 12px;" @click="handleSubmit">
Submit
</ElButton>
</FormProvider>
</template>Target Nodes:
暂无数据
Light mode still supports FormItem label/required/feedback.
查看源码
注册自定义组件(Segmented)
<script setup lang="ts">
import type { ISchema } from '@silver-formily/json-schema'
import { createForm } from '@silver-formily/core'
import { QueryFormItem, Segmented, SelectTable } from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
import { ElButton, ElMessage } from 'element-plus'
import { createUserRequest } from './mock-user-request'
const form = createForm()
const request = createUserRequest()
const querySchema: ISchema = {
type: 'object',
properties: {
department: {
'type': 'string',
'x-decorator': 'FormItem',
'x-component': 'Segmented',
'enum': [
{ label: 'All', value: '' },
{ label: 'R&D', value: 'R&D' },
{ label: 'Product', value: 'Product' },
],
'default': '',
},
},
}
async function handleSubmit() {
try {
const values = await form.submit()
ElMessage.success(`Submit: ${JSON.stringify(values)}`)
}
catch {
ElMessage.error('Please select at least one user before submit')
}
}
const schema: ISchema = {
type: 'object',
properties: {
selectedUsers: {
'type': 'array',
'x-validator': [
{
required: true,
message: 'Please select at least one user',
},
],
'x-decorator': 'QueryFormItem',
'x-decorator-props': {
label: 'Target Users',
required: true,
extra: '通过 queryFormProps.components 注册 Segmented 后,可在 querySchema 中直接使用。',
querySchema,
request,
mode: 'light',
paginationProps: {
pageSize: 8,
},
queryFormProps: {
throttleWait: 200,
components: {
Segmented,
},
},
},
'x-component': 'SelectTable',
'x-component-props': {
mode: 'multiple',
rowKey: 'id',
columns: [
{ prop: 'name', label: 'Name' },
{ prop: 'department', label: 'Department' },
],
},
},
},
}
const { SchemaField } = createSchemaField({
components: {
QueryFormItem,
SelectTable,
},
})
</script>
<template>
<FormProvider :form="form">
<SchemaField :schema="schema" />
<ElButton type="primary" style="margin-top: 12px;" @click="handleSubmit">
Submit
</ElButton>
</FormProvider>
</template>Target Users:
暂无数据
共 0 条
- 1
通过 queryFormProps.components 注册 Segmented 后,可在 querySchema 中直接使用。
查看源码
通过外部传入 Form 设置初始值
注意
在Decorator中如果需要传入form需要使用函数返回的写法,这是因为Decorator中的props会经过toJS处理,会引起组件循环渲染。具体写法见下。
<script setup lang="ts">
import type { ISchema } from '@silver-formily/json-schema'
import { createForm } from '@silver-formily/core'
import { QueryFormItem, SelectTable } from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
import { ElButton, ElMessage } from 'element-plus'
import { createUserRequest } from './mock-user-request'
const form = createForm()
const request = createUserRequest()
// External query form instance with initial query params.
const queryForm = createForm({
initialValues: {
keyword: 'User-1',
department: 'R&D',
},
})
const querySchema: ISchema = {
type: 'object',
properties: {
keyword: {
'type': 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
clearable: true,
placeholder: 'Keyword',
},
},
department: {
'type': 'string',
'enum': [
{ label: 'All', value: '' },
{ label: 'R&D', value: 'R&D' },
{ label: 'Product', value: 'Product' },
],
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
clearable: true,
placeholder: 'Department',
style: 'width: 120px;',
},
},
},
}
async function handleSubmit() {
const values = await form.submit()
ElMessage.success(`Submit: ${JSON.stringify(values)}`)
}
const schema: ISchema = {
type: 'object',
properties: {
selectedUsers: {
'type': 'array',
'x-decorator': 'QueryFormItem',
'x-decorator-props': {
mode: 'light',
label: '',
extra: 'Light mode query area uses an external form with initial values.',
request,
querySchema,
queryFormProps: {
form: () => queryForm,
throttleWait: 200,
},
},
'x-component': 'SelectTable',
'x-component-props': {
mode: 'multiple',
rowKey: 'id',
columns: [
{ prop: 'name', label: 'Name' },
{ prop: 'department', label: 'Department' },
],
},
},
},
}
const { SchemaField } = createSchemaField({
components: {
QueryFormItem,
SelectTable,
},
})
</script>
<template>
<FormProvider :form="form">
<SchemaField :schema="schema" />
<ElButton type="primary" style="margin-top: 12px;" @click="handleSubmit">
Submit
</ElButton>
</FormProvider>
</template>暂无数据
共 0 条
- 1
Light mode query area uses an external form with initial values.
查看源码
Transfer + 条件变化清空已选值
<script setup lang="ts">
import type { ISchema } from '@silver-formily/json-schema'
import { createForm } from '@silver-formily/core'
import { QueryFormItem, Transfer } from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
import { ElButton, ElMessage } from 'element-plus'
import { createPermissionRequest } from './mock-user-request'
const form = createForm()
const request = createPermissionRequest()
const querySchema: ISchema = {
type: 'object',
properties: {
keyword: {
'type': 'string',
'title': '关键词',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
clearable: true,
placeholder: '按权限名称过滤',
},
},
module: {
'type': 'string',
'title': '模块',
'enum': [
{ label: '全部', value: '' },
{ label: '用户', value: 'user' },
{ label: '订单', value: 'order' },
{ label: '财务', value: 'finance' },
],
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
clearable: true,
style: 'width: 130px;',
},
},
},
}
async function handleSubmit() {
try {
const values = await form.submit()
ElMessage.success(`Submit: ${JSON.stringify(values)}`)
}
catch {
ElMessage.error('请先选择至少一项权限')
}
}
const schema: ISchema = {
type: 'object',
properties: {
selectedPermissions: {
'type': 'array',
'x-validator': [
{
required: true,
message: '请选择至少一项权限',
},
],
'x-decorator': 'QueryFormItem',
'x-decorator-props': {
label: '',
required: true,
querySchema,
request,
pagination: false,
clearOnDataChange: true,
extra: '修改过滤条件并点击查询后,会自动清空已选择的数据。',
},
'x-component': 'Transfer',
'x-component-props': {
titles: ['可选权限', '已选权限'],
filterable: true,
},
},
},
}
const { SchemaField } = createSchemaField({
components: {
QueryFormItem,
Transfer,
},
})
</script>
<template>
<FormProvider :form="form">
<SchemaField :schema="schema" />
<ElButton type="primary" style="margin-top: 12px;" @click="handleSubmit">
Submit
</ElButton>
</FormProvider>
</template>无数据
无数据
修改过滤条件并点击查询后,会自动清空已选择的数据。
查看源码
通过 decorator-content 注入 selected-list 到 extra
<script setup lang="ts">
import type { QueryFormItemSelectedListItem } from '@silver-formily/element-plus'
import type { ISchema } from '@silver-formily/json-schema'
import { createForm } from '@silver-formily/core'
import { QueryFormItem, QueryFormItemSelectedList, SelectTable } from '@silver-formily/element-plus'
import { Field, FormProvider } from '@silver-formily/vue'
import { defineComponent, h } from 'vue'
import { createUserRequest } from './mock-user-request'
const form = createForm()
const request = createUserRequest()
function getSelectedUserText(item: QueryFormItemSelectedListItem) {
return item.record?.name ?? String(item.value)
}
const SelectedListExtra = defineComponent({
name: 'SelectedListExtra',
setup() {
return () => h(QueryFormItemSelectedList, {
itemText: getSelectedUserText,
})
},
})
const querySchema: ISchema = {
type: 'object',
properties: {
keyword: {
'type': 'string',
'title': '关键词',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
clearable: true,
placeholder: '按名称搜索',
},
},
department: {
'type': 'string',
'title': '部门',
'enum': [
{ label: '全部', value: '' },
{ label: '研发', value: 'R&D' },
{ label: '产品', value: 'Product' },
],
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
clearable: true,
},
},
},
}
</script>
<template>
<FormProvider :form="form">
<Field
name="selectedUsers"
:decorator="[
QueryFormItem,
{
label: '目标用户',
querySchema,
request,
paginationProps: {
pageSize: 8,
},
queryFormProps: {
submitText: '查询',
resetText: '重置',
},
},
]"
:decorator-content="{
extra: SelectedListExtra,
}"
:component="[
SelectTable,
{
mode: 'multiple',
rowKey: 'id',
optionAsValue: true,
showAlertToolbar: false,
columns: [
{ prop: 'name', label: '名称' },
{ prop: 'department', label: '部门' },
],
},
]"
/>
</FormProvider>
</template>目标用户:
暂无数据
共 0 条
- 1
已选择 0 项
查看源码
API
QueryFormItem Props
基本继承了所有 FormItem 的配置项。为了避免校验失败时报错的样式影响到内部的QueryForm,组件修改了FormItem的class名,因此可能会出现部分配置项不生效的情况。下面的这些配置项是 QueryFormItem 独有的。
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
mode | 查询模式 | enum | 'default' |
request | 查询函数,参见 Request 约定 | Function | - |
clearOnDataChange | 查询成功后是否清空当前字段值 | boolean | false |
querySchema | 等价于queryFormProps.schema | object | - |
queryFormProps | 查询表单配置 | object | 参考QueryForm默认值 |
pagination | 是否启用分页 | boolean | true |
paginationProps | 分页配置,透传给 ElPagination | 参考Element-plus 官方文档 | 略 |
paginationMap | 分页参数映射(用于请求入参键名) | object | 分页参数映射 |
immediate | 挂载后是否立即执行一次查询 | boolean | true |
事件
| 属性名 | 类型 | 描述 | 默认值 |
|---|---|---|---|
requestSuccess | Function | 查询成功后触发,参数见 QueryFormItemRequestSuccessPayload | - |
requestFailed | Function | 查询失败后触发 | - |
QueryFormItemRequestSuccessPayload
requestSuccess 在请求成功且 result.success 为 true 后触发,其 payload 包含以下字段:
| 属性名 | 类型 | 说明 |
|---|---|---|
values | object | QueryForm 收集的查询值,不包含分页参数 |
pagination | object | undefined | 当前页码和每页条数;禁用分页时为 undefined |
dataSource | array | result.data,同时会同步到当前字段的 dataSource |
total | number | undefined | 数据总数;当 result.total 未返回时,使用 result.data.length |
result | object | request 返回的原始结果;其中 total 不会被上面的回退值覆盖 |
Request 约定
如果启用分页,那么request的入参除了QueryForm中获取的值之外还会额外传入 current 与 pageSize。可以通过paginationMap参数配置映射。
request 必须返回以下格式(参考 ProTable):
ts
interface QueryResult {
data: any[]
success: boolean
total?: number
}success必须为true才会解析data到字段dataSource。total不传时默认使用data.length,分页场景建议显式返回。
分页参数映射
默认分页参数键名为 current 和 pageSize。如果后端需要其他键名,可通过 paginationMap 配置:
ts
const props = {
paginationMap: {
current: 'pageNum',
pageSize: 'pageSize',
},
}SelectedList API
QueryFormItemSelectedList 用于展示当前字段已经勾选的项,通常通过 decoratorContent.extra 注入到 QueryFormItem 的 extra 区域。组件会自动读取当前字段值,并内置清空全部选择、移除单项选择的交互。
SelectedList Props
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
itemText | 必填,用于决定每一项显示的文字内容 | Function | - |
width | 列表宽度 | number | 200 |
selectionText | 已选数量文案;传入字符串时可使用 {count} 作为已选数量占位符 | string | ((count: number) => string) | 根据当前语言自动生成 |
clearSelectionText | 清空全部选择的操作文案 | string | 根据当前语言自动生成 |
QueryFormItemSelectedListItem
itemText 接收的参数结构如下:
| 属性名 | 说明 | 类型 |
|---|---|---|
value | 当前项的值;当能根据 rowKey 解析到记录时,优先为记录的键值 | any |
rawValue | 字段值中原始保存的当前项 | any |
record | 从 dataSource 中匹配到的完整记录;optionAsValue 时为原始项 | object | undefined |
index | 当前项在字段值数组中的原始下标 | number |