mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 08:55:33 +00:00
feat(data-vi): support for using url params and current role variables (#4586)
* feat(data-vi): support for using url params and current role variable * fix: bug
This commit is contained in:
parent
837f4f4158
commit
34108f1fcb
@ -11,3 +11,5 @@ export * from './useBaseVariable';
|
||||
export * from './useDateVariable';
|
||||
export * from './useUserVariable';
|
||||
export * from './useVariableOptions';
|
||||
export * from './useURLSearchParamsVariable';
|
||||
export * from './useRoleVariable';
|
||||
|
@ -12,30 +12,17 @@ import {
|
||||
VariableInput,
|
||||
VariableScopeProvider,
|
||||
getShouldChange,
|
||||
useCurrentUserVariable,
|
||||
useDatetimeVariable,
|
||||
CollectionProvider,
|
||||
} from '@nocobase/client';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useGeneralVariableOptions } from '../hooks';
|
||||
|
||||
export const ChartFilterVariableInput: React.FC<any> = (props) => {
|
||||
const { value, onChange, fieldSchema } = props;
|
||||
const collectionField = fieldSchema?.['x-collection-field'] || '';
|
||||
const [collection] = collectionField.split('.');
|
||||
const { currentUserSettings } = useCurrentUserVariable({
|
||||
collectionField: { uiSchema: fieldSchema },
|
||||
uiSchema: fieldSchema,
|
||||
});
|
||||
const { datetimeSettings } = useDatetimeVariable({
|
||||
operator: fieldSchema['x-component-props']?.['filter-operator'],
|
||||
schema: fieldSchema,
|
||||
noDisabled: true,
|
||||
});
|
||||
const options = useMemo(
|
||||
() => [currentUserSettings, datetimeSettings].filter(Boolean),
|
||||
[datetimeSettings, currentUserSettings],
|
||||
);
|
||||
const options = useGeneralVariableOptions(fieldSchema, fieldSchema['x-component-props']?.['filter-operator']);
|
||||
const schema = {
|
||||
...fieldSchema,
|
||||
'x-component': fieldSchema['x-component'] || 'Input',
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
useActionContext,
|
||||
useCollectionManager_deprecated,
|
||||
useDataSourceManager,
|
||||
useVariables,
|
||||
} from '@nocobase/client';
|
||||
import { useCallback, useContext, useMemo } from 'react';
|
||||
import { ChartDataContext } from '../block/ChartDataProvider';
|
||||
@ -24,7 +25,7 @@ import { Schema } from '@formily/react';
|
||||
import { useChartsTranslation } from '../locale';
|
||||
import { ChartFilterContext } from '../filter/FilterProvider';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { parse } from '@nocobase/utils/client';
|
||||
import { flatten, parse, unflatten } from '@nocobase/utils/client';
|
||||
import lodash from 'lodash';
|
||||
import { getFormulaComponent, getValuesByPath } from '../utils';
|
||||
import deepmerge from 'deepmerge';
|
||||
@ -103,6 +104,7 @@ export const useChartFilter = () => {
|
||||
const { fieldSchema } = useActionContext();
|
||||
const action = fieldSchema?.['x-action'];
|
||||
const { fields: fieldProps, form } = useContext(ChartFilterContext);
|
||||
const variables = useVariables();
|
||||
|
||||
const getChartFilterFields = ({
|
||||
dataSource,
|
||||
@ -405,6 +407,38 @@ export const useChartFilter = () => {
|
||||
.join(' / ');
|
||||
});
|
||||
|
||||
const parseFilter = useCallback(
|
||||
async (filterValue: any) => {
|
||||
const flat = flatten(filterValue, {
|
||||
breakOn({ key }) {
|
||||
return key.startsWith('$') && key !== '$and' && key !== '$or';
|
||||
},
|
||||
transformValue(value) {
|
||||
if (!(typeof value === 'string' && value.startsWith('{{$') && value?.endsWith('}}'))) {
|
||||
return value;
|
||||
}
|
||||
if (['$user', '$date', '$nDate', '$nRole'].some((n) => value.includes(n))) {
|
||||
return value;
|
||||
}
|
||||
const result = variables?.parseVariable(value);
|
||||
return result;
|
||||
},
|
||||
});
|
||||
await Promise.all(
|
||||
Object.keys(flat).map(async (key) => {
|
||||
flat[key] = await flat[key];
|
||||
if (flat[key] === undefined) {
|
||||
delete flat[key];
|
||||
}
|
||||
return flat[key];
|
||||
}),
|
||||
);
|
||||
const result = unflatten(flat);
|
||||
return result;
|
||||
},
|
||||
[variables],
|
||||
);
|
||||
|
||||
return {
|
||||
filter,
|
||||
refresh,
|
||||
@ -413,6 +447,7 @@ export const useChartFilter = () => {
|
||||
hasFilter,
|
||||
appendFilter,
|
||||
getTranslatedTitle,
|
||||
parseFilter,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -7,27 +7,49 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { useField } from '@formily/react';
|
||||
import { useCurrentUserVariable, useDatetimeVariable } from '@nocobase/client';
|
||||
import { ISchema, useField } from '@formily/react';
|
||||
import {
|
||||
useCurrentRoleVariable,
|
||||
useCurrentUserVariable,
|
||||
useDatetimeVariable,
|
||||
useURLSearchParamsVariable,
|
||||
} from '@nocobase/client';
|
||||
import { useMemo } from 'react';
|
||||
import { useFilterVariable } from './filter';
|
||||
|
||||
export const useVariableOptions = () => {
|
||||
const field = useField<any>();
|
||||
const { operator, schema } = field.data || {};
|
||||
export const useGeneralVariableOptions = (
|
||||
schema: ISchema,
|
||||
operator?: {
|
||||
value: string;
|
||||
},
|
||||
) => {
|
||||
const { currentUserSettings } = useCurrentUserVariable({
|
||||
collectionField: { uiSchema: schema },
|
||||
uiSchema: schema,
|
||||
});
|
||||
const { datetimeSettings } = useDatetimeVariable({ operator, schema });
|
||||
const filterVariable = useFilterVariable();
|
||||
const { currentRoleSettings } = useCurrentRoleVariable({ uiSchema: schema });
|
||||
const { datetimeSettings } = useDatetimeVariable({ operator, schema, noDisabled: true });
|
||||
const { urlSearchParamsSettings } = useURLSearchParamsVariable();
|
||||
|
||||
const result = useMemo(
|
||||
() => [currentUserSettings, datetimeSettings, filterVariable].filter(Boolean),
|
||||
[datetimeSettings, currentUserSettings, filterVariable],
|
||||
() => [currentUserSettings, currentRoleSettings, datetimeSettings, urlSearchParamsSettings].filter(Boolean),
|
||||
[datetimeSettings, currentUserSettings, currentRoleSettings, urlSearchParamsSettings],
|
||||
);
|
||||
|
||||
if (!operator || !schema) return [];
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const useVariableOptions = () => {
|
||||
const field = useField<any>();
|
||||
const { operator, schema } = field.data || {};
|
||||
const filterVariable = useFilterVariable();
|
||||
const generalOptions = useGeneralVariableOptions(schema, operator);
|
||||
|
||||
const result = useMemo(() => [...generalOptions, filterVariable].filter(Boolean), [generalOptions, filterVariable]);
|
||||
|
||||
if (!operator || !schema) return [];
|
||||
|
||||
return result;
|
||||
};
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
MaybeCollectionProvider,
|
||||
useAPIClient,
|
||||
useDataSourceManager,
|
||||
useParsedFilter,
|
||||
useRequest,
|
||||
} from '@nocobase/client';
|
||||
import React, { createContext, useContext } from 'react';
|
||||
@ -82,23 +83,23 @@ export const ChartRendererProvider: React.FC<ChartRendererProps> = (props) => {
|
||||
const { query, config, collection, transform, dataSource = DEFAULT_DATA_SOURCE_KEY } = props;
|
||||
const { addChart } = useContext(ChartDataContext);
|
||||
const { ready, form, enabled } = useContext(ChartFilterContext);
|
||||
const { getFilter, hasFilter, appendFilter } = useChartFilter();
|
||||
const { getFilter, hasFilter, appendFilter, parseFilter } = useChartFilter();
|
||||
const schema = useFieldSchema();
|
||||
const api = useAPIClient();
|
||||
const service = useRequest(
|
||||
(dataSource, collection, query, manual) =>
|
||||
new Promise((resolve, reject) => {
|
||||
async (dataSource, collection, query, manual) => {
|
||||
if (!(collection && query?.measures?.length)) return;
|
||||
// Check if the chart is configured
|
||||
if (!(collection && query?.measures?.length)) return resolve(undefined);
|
||||
// If the filter block is enabled, the filter form is required to be rendered
|
||||
if (enabled && !form) return resolve(undefined);
|
||||
if (enabled && !form) return;
|
||||
const filterValues = getFilter();
|
||||
const parsedFilter = await parseFilter(query.filter);
|
||||
const parsedQuery = { ...query, filter: parsedFilter };
|
||||
const config = { dataSource, collection, query: parsedQuery };
|
||||
const queryWithFilter =
|
||||
!manual && hasFilter({ dataSource, collection, query }, filterValues)
|
||||
? appendFilter({ dataSource, collection, query }, filterValues)
|
||||
: query;
|
||||
api
|
||||
.request({
|
||||
!manual && hasFilter(config, filterValues) ? appendFilter(config, filterValues) : parsedQuery;
|
||||
try {
|
||||
const res = await api.request({
|
||||
url: 'charts:query',
|
||||
method: 'POST',
|
||||
data: {
|
||||
@ -124,17 +125,17 @@ export const ChartRendererProvider: React.FC<ChartRendererProps> = (props) => {
|
||||
return measure;
|
||||
}),
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
resolve(res?.data?.data);
|
||||
})
|
||||
.finally(() => {
|
||||
});
|
||||
return res?.data?.data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
} finally {
|
||||
if (!manual && schema?.['x-uid']) {
|
||||
addChart(schema?.['x-uid'], { dataSource, collection, service, query });
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
}),
|
||||
}
|
||||
},
|
||||
{
|
||||
defaultParams: [dataSource, collection, query],
|
||||
// Wait until ChartFilterProvider is rendered and check the status of the filter form
|
||||
|
@ -8,11 +8,11 @@
|
||||
*/
|
||||
|
||||
import { Context, Next } from '@nocobase/actions';
|
||||
import { Field, FilterParser, snakeCase } from '@nocobase/database';
|
||||
import { Field, FilterParser } from '@nocobase/database';
|
||||
import { formatter } from './formatter';
|
||||
import compose from 'koa-compose';
|
||||
import { parseFilter, getDateVars } from '@nocobase/utils';
|
||||
import { Cache } from '@nocobase/cache';
|
||||
import { middlewares } from '@nocobase/server';
|
||||
|
||||
type MeasureProps = {
|
||||
field: string | string[];
|
||||
@ -259,52 +259,11 @@ export const parseFieldAndAssociations = async (ctx: Context, next: Next) => {
|
||||
|
||||
export const parseVariables = async (ctx: Context, next: Next) => {
|
||||
const { filter } = ctx.action.params.values;
|
||||
if (!filter) {
|
||||
return next();
|
||||
}
|
||||
const isNumeric = (str: any) => {
|
||||
if (typeof str === 'number') return true;
|
||||
if (typeof str != 'string') return false;
|
||||
return !isNaN(str as any) && !isNaN(parseFloat(str));
|
||||
};
|
||||
const getUser = () => {
|
||||
return async ({ fields }) => {
|
||||
const userFields = fields.filter((f) => f && ctx.db.getFieldByPath('users.' + f));
|
||||
ctx.logger?.info('parse filter variables', { userFields, method: 'parseVariables' });
|
||||
if (!ctx.state.currentUser) {
|
||||
return;
|
||||
}
|
||||
if (!userFields.length) {
|
||||
return;
|
||||
}
|
||||
const user = await ctx.db.getRepository('users').findOne({
|
||||
filterByTk: ctx.state.currentUser.id,
|
||||
fields: userFields,
|
||||
});
|
||||
ctx.logger?.info('parse filter variables', {
|
||||
$user: user?.toJSON(),
|
||||
method: 'parseVariables',
|
||||
});
|
||||
return user;
|
||||
};
|
||||
};
|
||||
ctx.action.params.values.filter = await parseFilter(filter, {
|
||||
timezone: ctx.get('x-timezone'),
|
||||
now: new Date().toISOString(),
|
||||
getField: (path: string) => {
|
||||
const fieldPath = path
|
||||
.split('.')
|
||||
.filter((p) => !p.startsWith('$') && !isNumeric(p))
|
||||
.join('.');
|
||||
const { resourceName } = ctx.action;
|
||||
return ctx.db.getFieldByPath(`${resourceName}.${fieldPath}`);
|
||||
},
|
||||
vars: {
|
||||
$nDate: getDateVars(),
|
||||
$user: getUser(),
|
||||
},
|
||||
});
|
||||
ctx.action.params.filter = filter;
|
||||
await middlewares.parseVariables(ctx, async () => {
|
||||
ctx.action.params.values.filter = ctx.action.params.filter;
|
||||
await next();
|
||||
});
|
||||
};
|
||||
|
||||
export const cacheMiddleware = async (ctx: Context, next: Next) => {
|
||||
|
Loading…
Reference in New Issue
Block a user