feat: add delete filter functionality and improve filter context management

This commit is contained in:
Sheldon Guo 2025-03-02 12:25:06 +08:00
parent 8f8dcb9c05
commit 7cf500bf62
4 changed files with 61 additions and 9 deletions

View File

@ -240,11 +240,20 @@ export function Input(props: VariableInputProps) {
const updateFilterParams = useCallback( const updateFilterParams = useCallback(
({ filterId, params }: { filterId: number; params: any[] }) => { ({ filterId, params }: { filterId: number; params: any[] }) => {
const copyedFilters = cloneDeep(filters); const copyedFilters = cloneDeep(filters);
copyedFilters[filterId].args = params.map((val) => JSON.stringify(val)); copyedFilters[filterId].args = params;
onChange(composeTemplate({ fullVariable, filters: copyedFilters })); onChange(composeTemplate({ fullVariable, filters: copyedFilters }));
}, },
[filters, fullVariable, onChange], [filters, fullVariable, onChange],
); );
const deleteFilter = useCallback(
({ filterId }: { filterId: number }) => {
const newFilters = filters.filter((_, index) => index !== filterId);
onChange(composeTemplate({ fullVariable, filters: newFilters }));
},
[filters, fullVariable, onChange],
);
const parsed = useMemo(() => parseValue(variableSegments, parseOptions), [parseOptions, variableSegments]); const parsed = useMemo(() => parseValue(variableSegments, parseOptions), [parseOptions, variableSegments]);
const isConstant = typeof parsed === 'string'; const isConstant = typeof parsed === 'string';
const type = isConstant ? parsed : ''; const type = isConstant ? parsed : '';
@ -472,7 +481,7 @@ export function Input(props: VariableInputProps) {
</React.Fragment> </React.Fragment>
); );
})} })}
<FilterContext.Provider value={{ updateFilterParams }}> <FilterContext.Provider value={{ updateFilterParams, deleteFilter }}>
<Filters filters={filters} onFilterChange={onFilterAdd} /> <Filters filters={filters} onFilterChange={onFilterAdd} />
{variableText.length > 0 && <Addition variable={fullVariable} onFilterAdd={onFilterAdd} />} {variableText.length > 0 && <Addition variable={fullVariable} onFilterAdd={onFilterAdd} />}

View File

@ -13,7 +13,7 @@ import { createForm } from '@formily/core';
import { useForm, useParentForm, FormProvider, useField, FormContext } from '@formily/react'; import { useForm, useParentForm, FormProvider, useField, FormContext } from '@formily/react';
import { FormLayout, FormButtonGroup, Submit } from '@formily/antd-v5'; import { FormLayout, FormButtonGroup, Submit } from '@formily/antd-v5';
import { FilterOutlined } from '@ant-design/icons'; import { FilterOutlined } from '@ant-design/icons';
import { uid } from '@nocobase/utils/client'; import { uid, tval } from '@nocobase/utils/client';
import { variableFilters } from '@nocobase/json-templates'; import { variableFilters } from '@nocobase/json-templates';
import type { MenuProps } from 'antd'; import type { MenuProps } from 'antd';
import { SchemaComponent } from '../../core/SchemaComponent'; import { SchemaComponent } from '../../core/SchemaComponent';
@ -60,8 +60,19 @@ export function Filters({ filters, onFilterChange }) {
} }
export function Filter({ config, filter, filterId }) { export function Filter({ config, filter, filterId }) {
const [open, setOpen] = useState(false);
const hide = () => {
setOpen(false);
};
const handleOpenChange = (newOpen: boolean) => {
setOpen(newOpen);
};
const argsMap = Object.fromEntries(config.paramSchema.map((param, index) => [param.name, filter.args[index]])); const argsMap = Object.fromEntries(config.paramSchema.map((param, index) => [param.name, filter.args[index]]));
const form = useMemo(() => createForm({ initialValues: argsMap }), [argsMap]); const form = useMemo(() => createForm({ initialValues: argsMap }), [argsMap]);
const useSaveActionProps = () => { const useSaveActionProps = () => {
const form = useForm(); const form = useForm();
const { updateFilterParams } = useContext(FilterContext); const { updateFilterParams } = useContext(FilterContext);
@ -72,9 +83,24 @@ export function Filter({ config, filter, filterId }) {
const values = form.values; const values = form.values;
const params = config.paramSchema.map((param) => values[param.name]); const params = config.paramSchema.map((param) => values[param.name]);
updateFilterParams({ filterId, params }); updateFilterParams({ filterId, params });
setOpen(false);
}, },
}; };
}; };
const useDeleteActionProps = () => {
const form = useForm();
const { deleteFilter } = useContext(FilterContext);
return {
type: 'primary',
danger: true,
onClick: async () => {
deleteFilter({ filterId });
setOpen(false);
},
};
};
const useFormBlockProps = () => { const useFormBlockProps = () => {
return { form }; return { form };
}; };
@ -96,12 +122,18 @@ export function Filter({ config, filter, filterId }) {
), ),
actions: { actions: {
type: 'void', type: 'void',
title: 'Save', title: tval('Save'),
'x-component': 'ActionBar', 'x-component': 'ActionBar',
properties: { properties: {
delete: {
type: 'void',
title: tval('Delete'),
'x-component': 'Action',
'x-use-component-props': 'useDeleteActionProps',
},
save: { save: {
type: 'void', type: 'void',
title: 'Save', title: tval('Save'),
'x-component': 'Action', 'x-component': 'Action',
'x-use-component-props': 'useSaveActionProps', 'x-use-component-props': 'useSaveActionProps',
}, },
@ -109,16 +141,23 @@ export function Filter({ config, filter, filterId }) {
}, },
}, },
}; };
return ( return (
<> <>
<span style={{ color: '#bfbfbf', margin: '0 5px' }}>|</span> <span style={{ color: '#bfbfbf', margin: '0 5px' }}>|</span>
<Popover <Popover
open={open}
onOpenChange={handleOpenChange}
content={ content={
<FormContext.Provider value={form}> <FormContext.Provider value={form}>
<SchemaComponent schema={schema} scope={{ useSaveActionProps, useFormBlockProps }} basePath={['']} /> <SchemaComponent
schema={schema}
scope={{ useSaveActionProps, useFormBlockProps, useDeleteActionProps }}
basePath={['']}
/>
</FormContext.Provider> </FormContext.Provider>
} }
trigger={'click'} trigger={'hover'}
> >
<div style={{ color: '#52c41a', display: 'inline-block', cursor: 'pointer' }}>{config.label}</div> <div style={{ color: '#52c41a', display: 'inline-block', cursor: 'pointer' }}>{config.label}</div>
</Popover> </Popover>
@ -128,8 +167,10 @@ export function Filter({ config, filter, filterId }) {
type FilterContextType = { type FilterContextType = {
updateFilterParams: (args: { filterId: number; params: any[] }) => any; updateFilterParams: (args: { filterId: number; params: any[] }) => any;
deleteFilter: (args: { filterId: number }) => any;
}; };
export const FilterContext = React.createContext<FilterContextType>({ export const FilterContext = React.createContext<FilterContextType>({
updateFilterParams: (params: any) => {}, updateFilterParams: (params: any) => {},
deleteFilter: (params: any) => {},
}); });

View File

@ -183,7 +183,7 @@ export const getShouldChange = ({
return true; return true;
} }
const lastOption = optionPath[optionPath.length - 1]; const lastOption = Array.isArray(optionPath) ? optionPath[optionPath.length - 1] : null;
// 点击叶子节点时,必须更新 value // 点击叶子节点时,必须更新 value
if (lastOption && _.isEmpty(lastOption.children) && !lastOption.loadChildren) { if (lastOption && _.isEmpty(lastOption.children) && !lastOption.loadChildren) {

View File

@ -34,7 +34,9 @@ export function extractTemplateElements(template: string) {
} }
const composeFilterTemplate = (filter: Filter) => { const composeFilterTemplate = (filter: Filter) => {
const value = `${filter.name}${filter.args.length ? `:${filter.args.join(',')}` : ''}`; const value = `${filter.name}${
filter.args.length ? `:${filter.args.map((val) => JSON.stringify(val)).join(',')}` : ''
}`;
return value; return value;
}; };