feat: filter with variable (#574)

This commit is contained in:
chenos 2022-07-04 17:50:18 +08:00 committed by GitHub
parent ec7bc2bc8b
commit e3b6c0513a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 76 additions and 6 deletions

View File

@ -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;

View File

@ -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',

View File

@ -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>
); );

View File

@ -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:

View File

@ -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"
} }

View File

@ -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;

View File

@ -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) => {