feat: improve filter action

This commit is contained in:
chenos 2020-12-12 16:32:18 +08:00
parent c776229540
commit 2dbbe29223
6 changed files with 128 additions and 44 deletions

View File

@ -28,8 +28,9 @@ class ApiClient {
const proxy: any = new Proxy({}, { const proxy: any = new Proxy({}, {
get(target, method, receiver) { get(target, method, receiver) {
return (params: ActionParams = {}) => { return (params: ActionParams = {}) => {
const { associatedKey, resourceKey, filter, ...restParams } = params; let { associatedKey, resourceKey, filter, sorter, sort = [], ...restParams } = params;
let url = `/${name}`; let url = `/${name}`;
sort = sort || [];
let options: any = { let options: any = {
params: {}, params: {},
}; };
@ -51,6 +52,21 @@ class ApiClient {
if (filter) { if (filter) {
options.params['filter'] = JSON.stringify(filter); options.params['filter'] = JSON.stringify(filter);
} }
if (sorter) {
const arr = Array.isArray(sorter) ? sorter : [sorter];
arr.forEach(({order, field}) => {
if (order === 'descend') {
sort.push(`-${field}`);
} else if (order === 'ascend') {
sort.push(field);
}
});
}
if (sort.length === 0) {
delete options.params['sort'];
} else {
options.params['sort'] = sort.join(',');
}
console.log({url, params}); console.log({url, params});
return request(url, options); return request(url, options);
}; };

View File

@ -42,6 +42,7 @@ export function Filter(props) {
<div className={'popover-button-mask'} onClick={() => setVisible(false)}></div> <div className={'popover-button-mask'} onClick={() => setVisible(false)}></div>
<ViewFactory <ViewFactory
{...props} {...props}
setVisible={setVisible}
viewName={'filter'} viewName={'filter'}
{...params} {...params}
/> />

View File

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Button, Select, Input, Space, Form, InputNumber, DatePicker } from 'antd'; import { Button, Select, Input, Space, Form, InputNumber, DatePicker } from 'antd';
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons'; import { PlusCircleOutlined, CloseCircleOutlined } from '@ant-design/icons';
import useDynamicList from './useDynamicList'; import useDynamicList from './useDynamicList';
import { connect } from '@formily/react-schema-renderer' import { connect } from '@formily/react-schema-renderer'
import { mapStyledProps } from '../shared' import { mapStyledProps } from '../shared'
@ -13,8 +13,19 @@ export function FilterGroup(props: any) {
type: 'item', type: 'item',
}, },
]); ]);
let style: any = {
position: 'relative',
};
if (showDeleteButton) {
style = {
...style,
marginBottom: 14,
padding: 14,
border: '1px dashed #dedede',
}
}
return ( return (
<div style={{marginBottom: 14, padding: 14, border: '1px dashed #dedede'}}> <div style={style}>
<div style={{marginBottom: 14}}> <div style={{marginBottom: 14}}>
{' '} {' '}
@ -32,7 +43,7 @@ export function FilterGroup(props: any) {
// console.log(item); // console.log(item);
const Component = item.type === 'group' ? FilterGroup : FilterItem; const Component = item.type === 'group' ? FilterGroup : FilterItem;
return ( return (
<div style={{marginBottom: 14}}> <div style={{marginBottom: 8}}>
{<Component {<Component
fields={fields} fields={fields}
dataSource={item} dataSource={item}
@ -58,7 +69,7 @@ export function FilterGroup(props: any) {
</div> </div>
<div> <div>
<Space> <Space>
<Button onClick={() => { <Button style={{padding: 0}} type={'link'} onClick={() => {
const data = { const data = {
type: 'item' type: 'item'
}; };
@ -67,28 +78,33 @@ export function FilterGroup(props: any) {
newList.push(data); newList.push(data);
onChange({...dataSource, list: newList}); onChange({...dataSource, list: newList});
}}> }}>
<PlusCircleOutlined />
</Button> </Button>
<Button onClick={() => { {' '}
const data = { <Button
type: 'group', style={{padding: 0}}
list: [ type={'link'}
{ onClick={() => {
type: 'item', const data = {
}, type: 'group',
], list: [
}; {
push(data); type: 'item',
const newList = [...list]; },
newList.push(data); ],
onChange({...dataSource, list: newList}); };
}}> push(data);
const newList = [...list];
newList.push(data);
onChange({...dataSource, list: newList});
}}
>
<PlusCircleOutlined />
</Button> </Button>
{showDeleteButton && <Button onClick={(e) => { {showDeleteButton && <Button style={{padding: 0, position: 'absolute', top: 0, right: 0, width: 32}} type={'link'} onClick={(e) => {
onDelete && onDelete(e); onDelete && onDelete(e);
}}> }}>
<CloseCircleOutlined />
</Button>} </Button>}
</Space> </Space>
</div> </div>
@ -169,7 +185,9 @@ const op = {
string: OP_MAP.string, string: OP_MAP.string,
textarea: OP_MAP.string, textarea: OP_MAP.string,
number: OP_MAP.number, number: OP_MAP.number,
percent: OP_MAP.number,
datetime: OP_MAP.datetime, datetime: OP_MAP.datetime,
date: OP_MAP.datetime,
}; };
const StringInput = (props) => { const StringInput = (props) => {
@ -186,7 +204,7 @@ const controls = {
textarea: StringInput, textarea: StringInput,
number: InputNumber, number: InputNumber,
// datetime: DatePicker, // datetime: DatePicker,
datetime: (props) => { date: (props) => {
const { value, onChange, ...restProps } = props; const { value, onChange, ...restProps } = props;
const m = moment(value, 'YYYY-MM-DD HH:mm:ss'); const m = moment(value, 'YYYY-MM-DD HH:mm:ss');
return ( return (
@ -204,14 +222,15 @@ export function FilterItem(props: FilterItemProps) {
useEffect(() => { useEffect(() => {
const field = fields.find(field => field.name === dataSource.column); const field = fields.find(field => field.name === dataSource.column);
if (field) { if (field) {
setType(field.interface); setType(field.component.type);
} }
}, [ }, [
dataSource, dataSource,
]); ]);
console.log({type});
const ValueControl = controls[type]||controls.string; const ValueControl = controls[type]||controls.string;
return ( return (
<Input.Group compact> <Space>
<Select value={dataSource.column} <Select value={dataSource.column}
onChange={(value) => { onChange={(value) => {
const field = fields.find(field => field.name === value); const field = fields.find(field => field.name === value);
@ -220,7 +239,8 @@ export function FilterItem(props: FilterItemProps) {
} }
onChange({...dataSource, column: value}); onChange({...dataSource, column: value});
}} }}
style={{ width: '30%' }} placeholder={'选择字段'}> style={{ width: 120 }}
placeholder={'选择字段'}>
{fields.map(field => ( {fields.map(field => (
<Select.Option value={field.name}>{field.title}</Select.Option> <Select.Option value={field.name}>{field.title}</Select.Option>
))} ))}
@ -236,16 +256,33 @@ export function FilterItem(props: FilterItemProps) {
</Select> </Select>
<ValueControl value={dataSource.value} onChange={(value) => { <ValueControl value={dataSource.value} onChange={(value) => {
onChange({...dataSource, value: value}); onChange({...dataSource, value: value});
}} style={{ width: '30%' }}/> }}
style={{ width: 180 }}
/>
{showDeleteButton && ( {showDeleteButton && (
<Button onClick={(e) => { <Button type={'link'} style={{padding: 0}} onClick={(e) => {
onDelete && onDelete(e); onDelete && onDelete(e);
}}></Button> }}><CloseCircleOutlined /></Button>
)} )}
</Input.Group> </Space>
); );
} }
function toFilter(values: any) {
let filter: any;
const { type, andor = 'and', list = [], column, op, value } = values;
if (type === 'group') {
filter = {
[andor]: list.map(value => toFilter(value)).filter(Boolean)
}
} else if (type === 'item' && column && op) {
filter = {
[`${column}`]: {[op]: value},
}
}
return filter;
}
export const Filter = connect({ export const Filter = connect({
getProps: mapStyledProps, getProps: mapStyledProps,
})((props) => { })((props) => {
@ -257,7 +294,10 @@ export const Filter = connect({
} }
], ],
}; };
return <FilterGroup dataSource={dataSource} {...props}/> const { value, onChange, ...restProps } = props;
return <FilterGroup dataSource={dataSource} onChange={(values) => {
onChange(toFilter(values));
}} {...restProps}/>
}); });
export default Filter; export default Filter;

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Tooltip, Card } from 'antd'; import { Tooltip, Button } from 'antd';
import { import {
SchemaForm, SchemaForm,
SchemaMarkupField as Field, SchemaMarkupField as Field,
@ -15,7 +15,8 @@ import {
import { QuestionCircleOutlined } from '@ant-design/icons'; import { QuestionCircleOutlined } from '@ant-design/icons';
export function FilterForm(props: any) { export function FilterForm(props: any) {
const actions = createFormActions(); const actions = createAsyncFormActions();
const { setVisible, onTrigger } = props;
const { title, fields: properties ={} } = props.schema||{}; const { title, fields: properties ={} } = props.schema||{};
return ( return (
<SchemaForm <SchemaForm
@ -23,6 +24,15 @@ export function FilterForm(props: any) {
layout={'vertical'} layout={'vertical'}
initialValues={{}} initialValues={{}}
actions={actions} actions={actions}
onReset={() => {
setVisible && setVisible(false);
}}
onSubmit={async (values) => {
if (onTrigger) {
await onTrigger(values);
}
setVisible && setVisible(false);
}}
schema={{ schema={{
type: 'object', type: 'object',
properties, properties,
@ -44,10 +54,7 @@ export function FilterForm(props: any) {
> >
<FormButtonGroup> <FormButtonGroup>
<Reset></Reset> <Reset></Reset>
<Submit onClick={async () => { <Submit></Submit>
const { values = {} } = await actions.submit();
console.log(values);
}}></Submit>
</FormButtonGroup> </FormButtonGroup>
</SchemaForm> </SchemaForm>
); );

View File

@ -4,7 +4,6 @@ import { Actions } from '@/components/actions';
import ViewFactory from '@/components/views'; import ViewFactory from '@/components/views';
import { useRequest } from 'umi'; import { useRequest } from 'umi';
import api from '@/api-client'; import api from '@/api-client';
import get from 'lodash/get';
import { components, fields2columns } from './SortableTable'; import { components, fields2columns } from './SortableTable';
export interface SimpleTableProps { export interface SimpleTableProps {
@ -30,12 +29,13 @@ export function SimpleTable(props: SimpleTableProps) {
const { sourceKey = 'id' } = activeTab.field||{}; const { sourceKey = 'id' } = activeTab.field||{};
const drawerRef = useRef<any>(); const drawerRef = useRef<any>();
const name = associatedName ? `${associatedName}.${resourceName}` : resourceName; const name = associatedName ? `${associatedName}.${resourceName}` : resourceName;
const { data, loading, pagination, mutate, refresh } = useRequest((params = {}) => { const { data, loading, pagination, mutate, refresh, params, run } = useRequest((params = {}) => {
const { current, pageSize, ...restParams } = params; const { current, pageSize, sorter, ...restParams } = params;
return api.resource(name).list({ return api.resource(name).list({
associatedKey, associatedKey,
page: paginated ? current : 1, page: paginated ? current : 1,
perPage: paginated ? pageSize : -1, perPage: paginated ? pageSize : -1,
sorter,
}) })
.then(({data = [], meta = {}}) => { .then(({data = [], meta = {}}) => {
return { return {
@ -72,6 +72,11 @@ export function SimpleTable(props: SimpleTableProps) {
refresh(); refresh();
}} }}
onTrigger={{ onTrigger={{
async filter(values) {
console.log('filter', values);
// @ts-ignore
run({...params[0], filter: values.filter});
},
async destroy() { async destroy() {
await api.resource(name).destroy({ await api.resource(name).destroy({
associatedKey, associatedKey,
@ -81,7 +86,7 @@ export function SimpleTable(props: SimpleTableProps) {
}); });
await refresh(); await refresh();
console.log('destroy.onTrigger', selectedRowKeys); console.log('destroy.onTrigger', selectedRowKeys);
} },
}} }}
/> />
<ViewFactory <ViewFactory
@ -98,6 +103,9 @@ export function SimpleTable(props: SimpleTableProps) {
loading={loading} loading={loading}
columns={fields2columns(fields)} columns={fields2columns(fields)}
dataSource={data?.list||(data as any)} dataSource={data?.list||(data as any)}
onChange={(pagination, filters, sorter, extra) => {
run({...params[0], sorter});
}}
components={components({ components={components({
data, data,
mutate, mutate,

View File

@ -46,12 +46,16 @@ export function Table(props: TableProps) {
// associatedKey, // associatedKey,
// })); // }));
const name = associatedName ? `${associatedName}.${resourceName}` : resourceName; const name = associatedName ? `${associatedName}.${resourceName}` : resourceName;
const { data, loading, pagination, mutate, refresh } = useRequest((params = {}) => { const { data, loading, pagination, mutate, refresh, run, params } = useRequest((params = {}, ...args) => {
const { current, pageSize, ...restParams } = params; const { current, pageSize, sorter, filter, ...restParams } = params;
console.log('paramsparamsparamsparamsparams', params, args);
return api.resource(name).list({ return api.resource(name).list({
associatedKey, associatedKey,
page: paginated ? current : 1, page: paginated ? current : 1,
perPage: paginated ? pageSize : -1, perPage: paginated ? pageSize : -1,
sorter,
filter,
// ...args2,
}) })
.then(({data = [], meta = {}}) => { .then(({data = [], meta = {}}) => {
return { return {
@ -88,6 +92,11 @@ export function Table(props: TableProps) {
refresh(); refresh();
}} }}
onTrigger={{ onTrigger={{
async filter(values) {
// @ts-ignore
run({...params[0], filter: values.filter});
console.log('filter', values);
},
async destroy() { async destroy() {
await api.resource(name).destroy({ await api.resource(name).destroy({
associatedKey, associatedKey,
@ -104,6 +113,9 @@ export function Table(props: TableProps) {
rowKey={rowKey} rowKey={rowKey}
columns={fields2columns(fields)} columns={fields2columns(fields)}
dataSource={data?.list||(data as any)} dataSource={data?.list||(data as any)}
onChange={(pagination, filters, sorter, extra) => {
run({...params[0], sorter});
}}
components={components({ components={components({
data, data,
mutate, mutate,