DrawerSelect, Filter ...

This commit is contained in:
chenos 2021-07-11 10:46:34 +08:00
parent 9cc5363f49
commit d39a7d822d
12 changed files with 284 additions and 328 deletions

View File

@ -30,7 +30,6 @@ import { Checkbox } from '../checkbox';
import { ColorSelect } from '../color-select';
import { DatabaseField } from '../database-field';
import { DatePicker } from '../date-picker';
import { DrawerSelect } from '../drawer-select';
import { Filter } from '../filter';
import { Form } from '../form';
import { Grid } from '../grid';
@ -97,7 +96,6 @@ export const components = {
ColorSelect,
DatabaseField,
DatePicker,
DrawerSelect,
Filter,
Form,
Grid,

View File

@ -116,13 +116,13 @@ const BaseAction = observer((props: any) => {
const { run } = useAction();
const { DesignableBar } = useDesignableBar();
const { setVisible } = useVisibleContext();
// const schema = useFieldSchema();
const fieldSchema = useFieldSchema();
const { schema } = useDesignable();
useEffect(() => {
field.componentProps.setVisible = setVisible;
}, []);
console.log('BaseAction', { field, schema }, field.title);
console.log('BaseAction', { field, schema, fieldSchema }, field.title);
const renderButton = () => (
<ButtonComponent

View File

@ -1,12 +0,0 @@
---
title: DrawerSelect - 抽屉选择器
nav:
title: 组件
path: /client
group:
order: 1
title: Schemas
path: /client/schemas
---
# DrawerSelect - 抽屉选择器

View File

@ -1,135 +0,0 @@
import React from 'react';
import { connect, mapProps, mapReadPretty } from '@formily/react';
import { Select, Tag } from 'antd';
import { LoadingOutlined } from '@ant-design/icons';
import Drawer from '../../components/Drawer';
import { useRequest } from 'ahooks';
import { isArr, isValid } from '@formily/shared';
function getFieldName(field: any) {
if (typeof field === 'string') {
return field;
}
if (typeof field === 'object') {
return field.name;
}
}
export const DrawerSelect = connect(
(props) => {
const {
value,
dataRequest,
labelField: lf,
valueField: vf,
renderTag,
renderOptions,
onChange,
...others
} = props;
const labelField = getFieldName(lf);
const valueField = getFieldName(vf);
const getValues = () => {
if (!isValid(value)) {
return [];
}
return isArr(value) ? value : [value];
}
const getOptionValue = () => {
const values = getValues().map((item) => {
return {
value: item[valueField],
label: item[labelField],
};
});
if (props.mode === 'multiple' || props.mode === 'tags') {
return values;
}
return values.length ? values[0] : null;
};
return (
<Select
{...others}
labelInValue
open={false}
value={getOptionValue()}
options={[]}
onChange={(selectValue) => {
const getSelectValue = () => {
const selected = isArr(selectValue) ? selectValue : [selectValue];
return selected.map((item: any) => item.value);
}
const selected = getSelectValue();
const values = getValues().filter(item => {
return selected.includes(item[valueField]);
});
onChange(values);
}}
onClick={(e) => {
Drawer.open({
title: '选择数据',
content: (contentProps) => {
return renderOptions ? renderOptions({ ...contentProps, onChange }) : null;
},
});
}}
/>
);
},
mapProps((props, field) => {
return {
...props,
suffix: (
<span>
{field?.['loading'] || field?.['validating'] ? (
<LoadingOutlined />
) : (
props.suffix
)}
</span>
),
};
}),
mapReadPretty((props) => {
const {
value,
dataRequest,
labelField: lf,
valueField: vf,
renderItem,
renderTag,
...others
} = props;
const labelField = getFieldName(lf);
const valueField = getFieldName(vf);
const getValues = () => {
if (!isValid(value)) {
return [];
}
return isArr(value) ? value : [value];
}
const values = getValues();
return (
<div>
{values.map((item) => (
<a
onClick={() => {
Drawer.open({
title: item[labelField],
content: (contentProps) => {
return renderItem
? renderItem({ ...contentProps, item })
: null;
},
});
}}
>
{renderTag ? renderTag(item) : item[labelField]}
</a>
))}
</div>
);
}),
);
export default DrawerSelect;

View File

@ -1,7 +1,7 @@
import React, { useMemo, useState } from 'react';
import React, { useContext, useMemo, useState } from 'react';
import { SchemaField } from '..';
import { createForm, onFieldChange, onFieldReact } from '@formily/core'
import { FormProvider, FormConsumer } from '@formily/react'
import { createForm, onFieldChange, onFieldReact } from '@formily/core';
import { FormProvider, FormConsumer, useFieldSchema, Schema, SchemaOptionsContext, ISchema } from '@formily/react';
import { Field } from '@formily/core/esm/models/Field';
import { Form } from '@formily/core/esm/models/Form';
import {
@ -11,105 +11,127 @@ import {
FormButtonGroup,
Submit,
Space,
} from '@formily/antd'
import { CloseCircleOutlined } from '@ant-design/icons'
} from '@formily/antd';
import { CloseCircleOutlined } from '@ant-design/icons';
import { get } from 'lodash';
function useFilterColumns(): Schema[] {
const schema = useFieldSchema();
const columns = schema.reduceProperties((columns, current) => {
if (current['x-component'] === 'Filter.Column') {
return [...columns, current];
}
return [...columns];
}, []);
return columns;
}
export const FilterItem = (props) => {
const { initialValues = {}, fields, onRemove } = props;
const { initialValues = {}, onRemove } = props;
const options = useContext(SchemaOptionsContext);
const { key } = initialValues;
const columns = useFilterColumns();
console.log('useFilterColumns', columns);
const Remove = (props) => {
return (
onRemove && (
<a onClick={() => onRemove()}>
<CloseCircleOutlined />
</a>
)
);
};
const getComponent = (column: ISchema) => {
const field = Object.values(column.properties).shift();
return field ? get(options.components, field['x-component']) : null;
}
const form = useMemo(
() =>
createForm({
initialValues,
effects: (form) => {
onFieldChange('key', (field: Field, form: Form) => {
form.setFieldState('value', state => {
state.value = null;
onFieldChange('column', (field: Field, form: Form) => {
const column = (field.value || {}) as ISchema;
const operations = column?.['x-component-props']?.['operations']||[];
field.query('operation').take((f: Field) => {
f.setDataSource(operations);
f.initialValue = get(operations, [0]);
});
field.query('value').take((f: Field) => {
f.value = null;
const component = getComponent(column);
console.log({ component });
f.setComponent(component, {});
});
});
onFieldReact('op', (field: Field) => {
const key = field.query('key').get('value');
const schema = fields.find(field => field.name === key) || {};
if (schema['x-operations']) {
field.value = schema['x-operations'][0].value;
field.dataSource = schema['x-operations'].map(item => ({
label: item.label,
value: item.value,
}));
console.log({key, schema}, schema['x-operations'][0].value);
}
onFieldReact('operation', (field: Field) => {
console.log('operation', field.value);
const operation = field.value || {};
field.query('value').take((f: Field) => {
f.visible = !operation.noValue;
});
onFieldReact('value', (field: Field, form: Form) => {
const key = field.query('key').get('value');
const op = field.query('op').get('value');
const schema = fields.find(field => field.name === key) || {};
if (schema['x-operations']) {
const operation = schema['x-operations'].find(operation => operation.value === op)
field.visible = operation.noValue ? false : true;
} else {
field.visible = true;
}
// field.setComponent(getFieldComponent(schema['x-component']));
// field.visible = opValue ? !opValue.noValue : true;
});
},
}),
[],
);
return (
<FormProvider form={form}>
<FormLayout layout={'inline'}>
<SchemaField>
<SchemaField.Void
<SchemaField components={{ Remove }}>
<SchemaField.Void x-component="Space">
<SchemaField.String
name="column"
x-decorator="FormItem"
x-decorator-props={{
asterisk: true,
feedbackLayout: 'none',
addonAfter: onRemove && (
<a onClick={() => onRemove()}><CloseCircleOutlined/></a>
),
}}
x-component="Space"
>
<SchemaField.String
name="key"
x-decorator="FormItem"
x-component="Select"
x-reactions={[
]}
x-component="Select.Object"
x-reactions={[]}
x-component-props={{
style: {
width: 100,
},
fieldNames: {
label: 'title',
value: 'name',
}
}}
enum={fields.map(field => ({
label: field.title,
value: field.name,
}))}
required
enum={columns.map((column) => column.toJSON())}
/>
<SchemaField.String
name="op"
name="operation"
x-decorator="FormItem"
x-component="Select"
x-decorator-props={{
asterisk: true,
feedbackLayout: 'none',
}}
x-component="Select.Object"
x-component-props={{
style: {
width: 100,
},
}}
enum={[]}
required
/>
<SchemaField.String
name="value"
x-decorator="FormItem"
x-decorator-props={{
asterisk: true,
feedbackLayout: 'none',
}}
x-component="Input"
required
/>
<SchemaField.Void x-component="Remove" />
</SchemaField.Void>
</SchemaField>
</FormLayout>
@ -122,6 +144,6 @@ export const FilterItem = (props) => {
</FormConsumer> */}
</FormProvider>
);
}
};
export default FilterItem;

View File

@ -1,83 +0,0 @@
import React, { createContext, useContext, useEffect } from 'react';
import { connect, mapProps, mapReadPretty } from '@formily/react';
import { CloseCircleOutlined } from '@ant-design/icons';
import { useDynamicList } from 'ahooks';
import { Select } from 'antd';
import { FilterItem } from './FilterItem';
import './style.less';
export const FilterSourceFieldsContext = createContext([]);
export const FilterTargetFieldsContext = createContext([]);
export function Group(props) {
const { onRemove } = props;
return (
<div className={'filter-group'}>
{onRemove && (
<a onClick={() => onRemove()}>
<CloseCircleOutlined />
</a>
)}
<div style={{ marginBottom: 14 }}>
{' '}
<Select
style={{ width: 80 }}
onChange={value => {
}}
defaultValue={'and'}
>
<Select.Option value={'and'}></Select.Option>
<Select.Option value={'or'}></Select.Option>
</Select>{' '}
</div>
<List
initialValue={[{}]}
onChange={(list) => {
console.log({ list });
}}
/>
</div>
);
}
export function List(props) {
const { initialValue } = props;
const { list, push, remove } = useDynamicList<any>(initialValue || []);
useEffect(() => {
props.onChange && props.onChange(list);
}, [list]);
const fields = useContext(FilterSourceFieldsContext);
return (
<div>
<div>
{list.map((item, index) => {
if (item.type === 'group') {
return <Group key={index} onRemove={() => remove(index)} />;
}
return <FilterItem key={index} fields={fields} onRemove={() => remove(index)} />;
})}
</div>
<a
onClick={() => {
push({
type: 'item',
key: new Date().toTimeString(),
});
}}
>
</a>{' '}
<a
onClick={() => {
push({
type: 'group',
key: new Date().toTimeString(),
});
}}
>
</a>
</div>
);
}

View File

@ -8,3 +8,66 @@ group:
title: Schemas
path: /client/schemas
---
<!-- <Filter>
<Filter.Column title={'字段1'} operations={[]}>
<Input/>
</Filter.Column>
<Filter.Column title={'字段2'}>
<Input/>
</Filter.Column>
</Filter> -->
```tsx
import React from 'react';
import { SchemaRenderer } from '../';
const schema = {
name: 'filter',
type: 'object',
'x-component': 'Filter',
properties: {
column1: {
type: 'void',
title: '字段1',
'x-component': 'Filter.Column',
'x-component-props': {
operations: [
{ label: '等于', value: 'eq' },
{ label: '不等于', value: 'ne' },
],
},
properties: {
field1: {
type: 'string',
'x-component': 'Input',
},
},
},
column2: {
type: 'void',
title: '字段2',
'x-component': 'Filter.Column',
'x-component-props': {
operations: [
{ label: '大于', value: 'gt' },
{ label: '小于', value: 'lt' },
{ label: '非空', value: 'notNull', noValue: true },
],
},
properties: {
field1: {
type: 'number',
'x-component': 'InputNumber',
},
},
},
}
};
export default () => {
return (
<SchemaRenderer schema={schema} />
);
};
```

View File

@ -1,16 +1,93 @@
import React, { useEffect } from 'react'
import { connect, mapProps, mapReadPretty, useFieldSchema } from '@formily/react'
import { LoadingOutlined } from '@ant-design/icons'
import React, { useEffect } from 'react';
import {
connect,
mapProps,
mapReadPretty,
} from '@formily/react';
import { LoadingOutlined } from '@ant-design/icons';
import { useDynamicList } from 'ahooks';
import { Group, FilterSourceFieldsContext } from './Group'
import { Select } from 'antd';
import { CloseCircleOutlined } from '@ant-design/icons';
import { FilterItem } from './FilterItem';
import './style.less';
export function FilterGroup(props) {
const { onRemove } = props;
return (
<div className={'nb-filter-group'}>
{onRemove && (
<a className={'nb-filter-group-close'} onClick={() => onRemove()}>
<CloseCircleOutlined />
</a>
)}
<div style={{ marginBottom: 14 }}>
{' '}
<Select
style={{ width: 80 }}
onChange={(value) => {}}
defaultValue={'and'}
>
<Select.Option value={'and'}></Select.Option>
<Select.Option value={'or'}></Select.Option>
</Select>{' '}
</div>
<FilterList
initialValue={[{}]}
onChange={(list) => {
console.log({ list });
}}
/>
</div>
);
}
export function FilterList(props) {
const { initialValue } = props;
const { list, push, remove } = useDynamicList<any>(initialValue || []);
useEffect(() => {
props.onChange && props.onChange(list);
}, [list]);
return (
<div className={'nb-filter-list'}>
<div>
{list.map((item, index) => {
if (item.type === 'group') {
return <FilterGroup key={index} onRemove={() => remove(index)} />;
}
return <FilterItem key={index} onRemove={() => remove(index)} />;
})}
</div>
<a
onClick={() => {
push({
type: 'item',
key: new Date().toTimeString(),
});
}}
>
</a>{' '}
<a
onClick={() => {
push({
type: 'group',
key: new Date().toTimeString(),
});
}}
>
</a>
</div>
);
}
export const Filter = connect(
(props) => {
const schema = useFieldSchema();
return (
<FilterSourceFieldsContext.Provider value={schema['x-source-fields']}>
<Group/>
</FilterSourceFieldsContext.Provider>
<div>
<FilterGroup />
</div>
);
},
mapProps((props, field) => {
@ -25,12 +102,11 @@ export const Filter = connect(
)}
</span>
),
}
};
}),
mapReadPretty((props) => {
return null;
})
)
}),
);
export default Filter;

View File

@ -1,6 +1,10 @@
.filter-group {
.nb-filter-group {
position: relative;
margin-bottom: 14px;
padding: 14px;
border: 1px dashed rgb(222, 222, 222);
&-close {
position: absolute;
right: 10px;
}
}

View File

@ -362,7 +362,7 @@ export default () => {
```tsx
import React from 'react';
import { SchemaRenderer } from '../';
import { useSelect } from './';
import { useSelect, useOptionTagValues } from './';
console.log({ useSelect })
@ -519,9 +519,15 @@ const schema = {
form: {
type: 'void',
'x-component': 'Form',
'x-component-props': {
useValues: '{{ useOptionTagValues }}',
},
properties: {
input: {
title: {
type: 'string',
title: '标题',
'x-read-pretty': true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
@ -535,7 +541,7 @@ const schema = {
export default () => {
return (
<SchemaRenderer scope={{ useSelect, useValues }} schema={schema} />
<SchemaRenderer scope={{ useSelect, useValues, useOptionTagValues }} schema={schema} />
);
};
```

View File

@ -17,7 +17,7 @@ import { useState } from 'react';
import { useDesignable } from '../DesignableSchemaField';
import { createContext } from 'react';
import { useContext } from 'react';
import { useTableContext } from '../table';
import { SelectedRowKeysContext, useTableContext } from '../table';
export const Select: any = connect(
(props) => {
@ -128,8 +128,9 @@ Select.Object = connect(
{...others}
value={optionValue}
onChange={(selectValue: any) => {
if (!selectValue) {
if (!isValid(selectValue)) {
onChange(null);
return;
}
if (isArr(selectValue)) {
const selectValues = selectValue.map((s) => s.value);
@ -273,6 +274,10 @@ Select.Drawer = connect(
}
};
const selectedKeys = toArr(optionValue).map(item => item.value);
console.log({ selectedKeys })
return (
<>
<AntdSelect
@ -300,7 +305,9 @@ Select.Drawer = connect(
width={'50%'}
visible={visible}
onClose={() => setVisible(false)}
destroyOnClose
>
<SelectedRowKeysContext.Provider value={selectedKeys}>
<SelectContext.Provider
value={{
onChange(selectValue) {
@ -317,6 +324,7 @@ Select.Drawer = connect(
}}
/>
</SelectContext.Provider>
</SelectedRowKeysContext.Provider>
</Drawer>
</>
);
@ -386,6 +394,11 @@ export function useSelect() {
};
}
export function useOptionTagValues() {
const { data } = useContext(OptionTagContext);
return data;
}
Select.OptionTag = observer((props) => {
const [visible, setVisible] = useState(false);
const { data, fieldNames } = useContext(OptionTagContext);

View File

@ -240,7 +240,9 @@ export function useTableUpdateAction() {
const [TableContextProvider, useTableContext] = constate(() => {
const field = useField<Formily.Core.Models.ArrayField>();
const schema = useFieldSchema();
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const defaultSelectedRowKeys = useContext(SelectedRowKeysContext);
const [selectedRowKeys, setSelectedRowKeys] = useState(defaultSelectedRowKeys || []);
console.log({ defaultSelectedRowKeys })
const pagination = usePaginationProps();
const response = useRequest<{
list: any[];
@ -284,6 +286,8 @@ const [TableContextProvider, useTableContext] = constate(() => {
export { TableContextProvider, useTableContext };
export const SelectedRowKeysContext = createContext([]);
const TableContainer = observer((props) => {
const field = useField<Formily.Core.Models.ArrayField>();
const schema = useFieldSchema();