feat: filter with variable (#574)
This commit is contained in:
		
							parent
							
								
									ec7bc2bc8b
								
							
						
					
					
						commit
						e3b6c0513a
					
				@ -1,12 +1,12 @@
 | 
				
			|||||||
import { Action } from '@nocobase/resourcer';
 | 
					import { Action } from '@nocobase/resourcer';
 | 
				
			||||||
import EventEmitter from 'events';
 | 
					import EventEmitter from 'events';
 | 
				
			||||||
 | 
					import parse from 'json-templates';
 | 
				
			||||||
import compose from 'koa-compose';
 | 
					import compose from 'koa-compose';
 | 
				
			||||||
import lodash from 'lodash';
 | 
					import lodash from 'lodash';
 | 
				
			||||||
import { AclAvailableAction, AvailableActionOptions } from './acl-available-action';
 | 
					import { AclAvailableAction, AvailableActionOptions } from './acl-available-action';
 | 
				
			||||||
import { ACLAvailableStrategy, AvailableStrategyOptions, predicate } from './acl-available-strategy';
 | 
					import { ACLAvailableStrategy, AvailableStrategyOptions, predicate } from './acl-available-strategy';
 | 
				
			||||||
import { ACLRole, RoleActionParams } from './acl-role';
 | 
					import { ACLRole, RoleActionParams } from './acl-role';
 | 
				
			||||||
import { AllowManager } from './allow-manager';
 | 
					import { AllowManager } from './allow-manager';
 | 
				
			||||||
const parse = require('json-templates');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface CanResult {
 | 
					interface CanResult {
 | 
				
			||||||
  role: string;
 | 
					  role: string;
 | 
				
			||||||
 | 
				
			|||||||
@ -70,6 +70,14 @@ export const number = [
 | 
				
			|||||||
export const id = [
 | 
					export const id = [
 | 
				
			||||||
  { label: '{{t("is")}}', value: '$eq', selected: true },
 | 
					  { label: '{{t("is")}}', value: '$eq', selected: true },
 | 
				
			||||||
  { label: '{{t("is not")}}', value: '$ne' },
 | 
					  { label: '{{t("is not")}}', value: '$ne' },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    label: '{{t("is variable")}}',
 | 
				
			||||||
 | 
					    value: '$isVar',
 | 
				
			||||||
 | 
					    schema: {
 | 
				
			||||||
 | 
					      'x-component': 'VariableCascader',
 | 
				
			||||||
 | 
					      'x-component-props': {},
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    label: '{{t("is current logged-in user")}}',
 | 
					    label: '{{t("is current logged-in user")}}',
 | 
				
			||||||
    value: '$isCurrentUser',
 | 
					    value: '$isCurrentUser',
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,52 @@
 | 
				
			|||||||
import { createForm, onFieldValueChange } from '@formily/core';
 | 
					import { createForm, onFieldValueChange } from '@formily/core';
 | 
				
			||||||
import { FieldContext, FormContext } from '@formily/react';
 | 
					import { connect, FieldContext, FormContext } from '@formily/react';
 | 
				
			||||||
import { merge } from '@formily/shared';
 | 
					import { merge } from '@formily/shared';
 | 
				
			||||||
 | 
					import { Cascader } from 'antd';
 | 
				
			||||||
import React, { useContext, useMemo } from 'react';
 | 
					import React, { useContext, useMemo } from 'react';
 | 
				
			||||||
import { SchemaComponent } from '../../core';
 | 
					import { SchemaComponent } from '../../core';
 | 
				
			||||||
import { useComponent } from '../../hooks';
 | 
					import { useCompile, useComponent } from '../../hooks';
 | 
				
			||||||
import { FilterContext } from './context';
 | 
					import { FilterContext } from './context';
 | 
				
			||||||
 | 
					import { useFilterOptions } from './useFilterActionProps';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const VariableCascader = connect((props) => {
 | 
				
			||||||
 | 
					  const fields = useFilterOptions('users');
 | 
				
			||||||
 | 
					  const compile = useCompile();
 | 
				
			||||||
 | 
					  const { value, onChange } = props;
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Cascader
 | 
				
			||||||
 | 
					      value={value ? value.split('.') : []}
 | 
				
			||||||
 | 
					      fieldNames={{
 | 
				
			||||||
 | 
					        label: 'title',
 | 
				
			||||||
 | 
					        value: 'name',
 | 
				
			||||||
 | 
					        children: 'children',
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      onChange={(value) => {
 | 
				
			||||||
 | 
					        onChange(value ? value.join('.') : null);
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      options={compile([
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          title: '{{t("Current user")}}',
 | 
				
			||||||
 | 
					          name: 'currentUser',
 | 
				
			||||||
 | 
					          children: fields
 | 
				
			||||||
 | 
					            .filter((field) => {
 | 
				
			||||||
 | 
					              if (!field.target) {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              return field.type === 'belongsTo';
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .map((field) => {
 | 
				
			||||||
 | 
					              if (field.children) {
 | 
				
			||||||
 | 
					                field.children = field.children.filter((child) => {
 | 
				
			||||||
 | 
					                  return !child.target;
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              return field;
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ])}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const DynamicComponent = (props) => {
 | 
					export const DynamicComponent = (props) => {
 | 
				
			||||||
  const { dynamicComponent } = useContext(FilterContext);
 | 
					  const { dynamicComponent } = useContext(FilterContext);
 | 
				
			||||||
@ -38,6 +80,9 @@ export const DynamicComponent = (props) => {
 | 
				
			|||||||
            'x-validator': undefined,
 | 
					            'x-validator': undefined,
 | 
				
			||||||
            'x-decorator': undefined,
 | 
					            'x-decorator': undefined,
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
 | 
					          components={{
 | 
				
			||||||
 | 
					            VariableCascader,
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
      </FieldContext.Provider>
 | 
					      </FieldContext.Provider>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,8 @@ export const useFilterOptions = (collectionName: string) => {
 | 
				
			|||||||
    const { nested, children, operators } = fieldInterface.filterable;
 | 
					    const { nested, children, operators } = fieldInterface.filterable;
 | 
				
			||||||
    const option = {
 | 
					    const option = {
 | 
				
			||||||
      name: field.name,
 | 
					      name: field.name,
 | 
				
			||||||
 | 
					      type: field.type,
 | 
				
			||||||
 | 
					      target: field.target,
 | 
				
			||||||
      title: field?.uiSchema?.title || field.name,
 | 
					      title: field?.uiSchema?.title || field.name,
 | 
				
			||||||
      schema: field?.uiSchema,
 | 
					      schema: field?.uiSchema,
 | 
				
			||||||
      operators:
 | 
					      operators:
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,8 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@nocobase/test": "0.7.1-alpha.7",
 | 
					    "@nocobase/test": "0.7.1-alpha.7",
 | 
				
			||||||
    "@types/jsonwebtoken": "^8.5.8"
 | 
					    "@types/jsonwebtoken": "^8.5.8",
 | 
				
			||||||
 | 
					    "json-templates": "^4.2.0"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "gitHead": "7e9556e489007577fc0ed89063b3a9ce2f9aae53"
 | 
					  "gitHead": "7e9556e489007577fc0ed89063b3a9ce2f9aae53"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ export function parseToken(options?: { plugin: UsersPlugin }) {
 | 
				
			|||||||
  return async function parseToken(ctx: Context, next: Next) {
 | 
					  return async function parseToken(ctx: Context, next: Next) {
 | 
				
			||||||
    const user = await findUserByToken(ctx, options.plugin);
 | 
					    const user = await findUserByToken(ctx, options.plugin);
 | 
				
			||||||
    if (user) {
 | 
					    if (user) {
 | 
				
			||||||
 | 
					      console.log('appends', user.toJSON());
 | 
				
			||||||
      ctx.state.currentUser = user;
 | 
					      ctx.state.currentUser = user;
 | 
				
			||||||
      setCurrentRole(ctx);
 | 
					      setCurrentRole(ctx);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -45,12 +46,18 @@ async function findUserByToken(ctx: Context, plugin: UsersPlugin) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    const { userId } = await plugin.jwtService.decode(token);
 | 
					    const { userId } = await plugin.jwtService.decode(token);
 | 
				
			||||||
 | 
					    const collection = ctx.db.getCollection('users');
 | 
				
			||||||
 | 
					    const appends = ['roles'];
 | 
				
			||||||
 | 
					    for (const [, field] of collection.fields) {
 | 
				
			||||||
 | 
					      if (field.type === 'belongsTo') {
 | 
				
			||||||
 | 
					        appends.push(field.name);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    return await ctx.db.getRepository('users').findOne({
 | 
					    return await ctx.db.getRepository('users').findOne({
 | 
				
			||||||
 | 
					      appends,
 | 
				
			||||||
      filter: {
 | 
					      filter: {
 | 
				
			||||||
        id: userId,
 | 
					        id: userId,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      appends: ['roles'],
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  } catch (error) {
 | 
					  } catch (error) {
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import { Collection, Op } from '@nocobase/database';
 | 
					import { Collection, Op } from '@nocobase/database';
 | 
				
			||||||
import { Plugin } from '@nocobase/server';
 | 
					import { Plugin } from '@nocobase/server';
 | 
				
			||||||
 | 
					import parse from 'json-templates';
 | 
				
			||||||
import { resolve } from 'path';
 | 
					import { resolve } from 'path';
 | 
				
			||||||
import { namespace } from './';
 | 
					import { namespace } from './';
 | 
				
			||||||
import * as actions from './actions/users';
 | 
					import * as actions from './actions/users';
 | 
				
			||||||
@ -35,6 +36,12 @@ export default class UsersPlugin extends Plugin<UserPluginConfig> {
 | 
				
			|||||||
          [Op.eq]: ctx?.app?.ctx?.state?.currentUser?.id || -1,
 | 
					          [Op.eq]: ctx?.app?.ctx?.state?.currentUser?.id || -1,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
 | 
					      $isVar(val, ctx) {
 | 
				
			||||||
 | 
					        const obj = parse({ val: `{{${val}}}` })(JSON.parse(JSON.stringify(ctx?.app?.ctx?.state)));
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					          [Op.eq]: obj.val,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    this.db.registerModels({ UserModel });
 | 
					    this.db.registerModels({ UserModel });
 | 
				
			||||||
    this.db.on('users.afterCreateWithAssociations', async (model, options) => {
 | 
					    this.db.on('users.afterCreateWithAssociations', async (model, options) => {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user