feat(variable): add current role (#3167)

* feat(variable): add current role

* chore: translate

* feat: parsing in the backend

* fix: fix tests
This commit is contained in:
Zeke Zhang 2023-12-08 19:19:53 +08:00 committed by GitHub
parent d782776a8d
commit a2be1a0e33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 92 additions and 3 deletions

View File

@ -606,6 +606,7 @@
"Constant value": "Constant value", "Constant value": "Constant value",
"Dynamic value": "Dynamic value", "Dynamic value": "Dynamic value",
"Current user": "Current user", "Current user": "Current user",
"Current role": "Current role",
"Current record": "Current record", "Current record": "Current record",
"Parent record": "Parent record", "Parent record": "Parent record",
"Current time": "Current time", "Current time": "Current time",

View File

@ -602,6 +602,7 @@
"Constant value": "Valor constante", "Constant value": "Valor constante",
"Dynamic value": "Valor dinámico", "Dynamic value": "Valor dinámico",
"Current user": "Usuario actual", "Current user": "Usuario actual",
"Current role": "Rol actual",
"Current record": "Registro actual", "Current record": "Registro actual",
"Parent record": "Registro padre", "Parent record": "Registro padre",
"Current time": "Hora actual", "Current time": "Hora actual",

View File

@ -599,6 +599,7 @@
"Constant value": "Valeur constante", "Constant value": "Valeur constante",
"Dynamic value": "Valeur dynamique", "Dynamic value": "Valeur dynamique",
"Current user": "Utilisateur actuel", "Current user": "Utilisateur actuel",
"Current role": "Rôle actuel",
"Current record": "Enregistrement actuel", "Current record": "Enregistrement actuel",
"Parent record": "Enregistrement parent", "Parent record": "Enregistrement parent",
"Current time": "Heure actuelle", "Current time": "Heure actuelle",

View File

@ -501,6 +501,7 @@
"Constant value": "定数値", "Constant value": "定数値",
"Dynamic value": "動的値", "Dynamic value": "動的値",
"Current user": "現在のユーザー", "Current user": "現在のユーザー",
"Current role": "現在の役割",
"Current record": "現在のレコード", "Current record": "現在のレコード",
"Popup close method": "ポップアップを閉じる方法", "Popup close method": "ポップアップを閉じる方法",
"Automatic close": "自動で閉じる", "Automatic close": "自動で閉じる",

View File

@ -438,6 +438,7 @@
"Constant value": "Постоянное значение", "Constant value": "Постоянное значение",
"Dynamic value": "Динамическое значение", "Dynamic value": "Динамическое значение",
"Current user": "Текущий пользователь", "Current user": "Текущий пользователь",
"Current role": "Текущая роль",
"Current record": "Текущая запись", "Current record": "Текущая запись",
"Parent record": "Родительская запись", "Parent record": "Родительская запись",
"Popup close method": "Метод закрытия всплывающего окна", "Popup close method": "Метод закрытия всплывающего окна",

View File

@ -437,6 +437,7 @@
"Constant value": "Sabit değer", "Constant value": "Sabit değer",
"Dynamic value": "Dinamik değer", "Dynamic value": "Dinamik değer",
"Current user": "Seçili kullanıcı", "Current user": "Seçili kullanıcı",
"Current role": "Seçili rol",
"Current record": "Seçili kayıt", "Current record": "Seçili kayıt",
"Parent record": "Üst kayıt", "Parent record": "Üst kayıt",
"Popup close method": "Açılır pencere kapatma metodu", "Popup close method": "Açılır pencere kapatma metodu",

View File

@ -619,6 +619,7 @@
"Constant value": "Константне значення", "Constant value": "Константне значення",
"Dynamic value": "Динамічне значення", "Dynamic value": "Динамічне значення",
"Current user": "Поточний користувач", "Current user": "Поточний користувач",
"Current role": "Поточна роль",
"Current record": "Поточний запис", "Current record": "Поточний запис",
"Parent record": "Батьківський запис", "Parent record": "Батьківський запис",
"Current time": "Поточний час", "Current time": "Поточний час",

View File

@ -622,6 +622,7 @@
"Constant value": "静态值", "Constant value": "静态值",
"Dynamic value": "动态值", "Dynamic value": "动态值",
"Current user": "当前用户", "Current user": "当前用户",
"Current role": "当前角色",
"Current record": "当前记录", "Current record": "当前记录",
"Parent record": "上级记录", "Parent record": "上级记录",
"Current time": "当前时间", "Current time": "当前时间",

View File

@ -146,6 +146,10 @@ export const getShouldChange = ({
const collectionsInheritChain = collectionField ? getAllCollectionsInheritChain(collectionField.target) : []; const collectionsInheritChain = collectionField ? getAllCollectionsInheritChain(collectionField.target) : [];
return async (value: any, optionPath: any[]) => { return async (value: any, optionPath: any[]) => {
if (_.isString(value) && value.includes('$nRole')) {
return true;
}
if (!isVariable(value) || !variables || !collectionField) { if (!isVariable(value) || !variables || !collectionField) {
return true; return true;
} }

View File

@ -49,6 +49,10 @@ interface BaseProps {
* 使 * 使
*/ */
noDisabled?: boolean; noDisabled?: boolean;
/**
*
*/
noChildren?: boolean;
/** /**
* fields * fields
* @param fields * @param fields
@ -131,6 +135,7 @@ export const useBaseVariable = ({
name, name,
title, title,
collectionName, collectionName,
noChildren = false,
// TODO: 等整理完完整测试用例后,再开启该功能 // TODO: 等整理完完整测试用例后,再开启该功能
noDisabled = true, noDisabled = true,
returnFields = (fields) => fields, returnFields = (fields) => fields,
@ -202,7 +207,7 @@ export const useBaseVariable = ({
label: title, label: title,
value: name, value: name,
key: name, key: name,
isLeaf: false, isLeaf: noChildren,
field: { field: {
target: collectionName, target: collectionName,
}, },

View File

@ -0,0 +1,39 @@
import { Schema } from '@formily/json-schema';
import { useTranslation } from 'react-i18next';
import { CollectionFieldOptions } from '../../../collection-manager';
import { useBaseVariable } from './useBaseVariable';
/**
* `当前角色`
* @param param0
* @returns
*/
export const useRoleVariable = ({
collectionField,
uiSchema,
noDisabled,
targetFieldSchema,
maxDepth = 0,
}: {
collectionField: CollectionFieldOptions;
uiSchema: any;
maxDepth?: number;
noDisabled?: boolean;
/** 消费变量值的字段 */
targetFieldSchema?: Schema;
}) => {
const { t } = useTranslation();
const result = useBaseVariable({
collectionField,
uiSchema,
maxDepth,
name: '$nRole',
title: t('Current role'),
collectionName: 'roles',
noDisabled,
targetFieldSchema,
noChildren: true,
});
return result;
};

View File

@ -10,6 +10,7 @@ import { useFormVariable } from './useFormVariable';
import { useIterationVariable } from './useIterationVariable'; import { useIterationVariable } from './useIterationVariable';
import { useParentRecordVariable } from './useParentRecordVariable'; import { useParentRecordVariable } from './useParentRecordVariable';
import { useRecordVariable } from './useRecordVariable'; import { useRecordVariable } from './useRecordVariable';
import { useRoleVariable } from './useRoleVariable';
import { useUserVariable } from './useUserVariable'; import { useUserVariable } from './useUserVariable';
interface Props { interface Props {
@ -58,6 +59,12 @@ export const useVariableOptions = ({
noDisabled, noDisabled,
targetFieldSchema, targetFieldSchema,
}); });
const roleVariable = useRoleVariable({
uiSchema: uiSchema,
collectionField,
noDisabled,
targetFieldSchema,
});
const dateVariable = useDateVariable({ operator, schema: uiSchema, noDisabled }); const dateVariable = useDateVariable({ operator, schema: uiSchema, noDisabled });
const formVariable = useFormVariable({ const formVariable = useFormVariable({
schema: uiSchema, schema: uiSchema,
@ -91,6 +98,7 @@ export const useVariableOptions = ({
return useMemo(() => { return useMemo(() => {
return [ return [
userVariable, userVariable,
roleVariable,
dateVariable, dateVariable,
form && !form.readPretty && formVariable, form && !form.readPretty && formVariable,
(isInSubForm || isInSubTable) && iterationVariable, (isInSubForm || isInSubTable) && iterationVariable,
@ -101,6 +109,7 @@ export const useVariableOptions = ({
].filter(Boolean); ].filter(Boolean);
}, [ }, [
userVariable, userVariable,
roleVariable,
dateVariable, dateVariable,
form, form,
formVariable, formVariable,

View File

@ -14,7 +14,7 @@ interface Props {
} }
// TODO: 建议变量名统一命名为 `$n` 开头,以防止与 formily 内置变量冲突 // TODO: 建议变量名统一命名为 `$n` 开头,以防止与 formily 内置变量冲突
const defaultExclude = ['$user', '$date', '$nDate']; const defaultExclude = ['$user', '$date', '$nDate', '$nRole'];
const useParseDataScopeFilter = ({ exclude = defaultExclude, currentRecord }: Props = {}) => { const useParseDataScopeFilter = ({ exclude = defaultExclude, currentRecord }: Props = {}) => {
const localVariables = useLocalVariables({ currentRecord }); const localVariables = useLocalVariables({ currentRecord });

View File

@ -56,6 +56,10 @@ vi.mock('../../collection-manager', async () => {
}); });
const { apiClient, mockRequest } = mockAPIClient(); const { apiClient, mockRequest } = mockAPIClient();
// 用于解析 `$nRole` 的值
apiClient.auth.role = 'root';
mockRequest.onGet('/auth:check').reply(() => { mockRequest.onGet('/auth:check').reply(() => {
return [ return [
200, 200,
@ -67,6 +71,16 @@ mockRequest.onGet('/auth:check').reply(() => {
}, },
]; ];
}); });
mockRequest.onGet('/roles:check').reply(() => {
return [
200,
{
data: {
role: 'root',
},
},
];
});
mockRequest.onGet('/users/0/belongsToField:get').reply(() => { mockRequest.onGet('/users/0/belongsToField:get').reply(() => {
return [ return [
200, 200,
@ -194,6 +208,7 @@ describe('useVariables', () => {
"tomorrow": [Function], "tomorrow": [Function],
"yesterday": [Function], "yesterday": [Function],
}, },
"$nRole": "root",
"$system": { "$system": {
"now": [Function], "now": [Function],
}, },
@ -404,6 +419,7 @@ describe('useVariables', () => {
"tomorrow": [Function], "tomorrow": [Function],
"yesterday": [Function], "yesterday": [Function],
}, },
"$nRole": "root",
"$system": { "$system": {
"now": [Function], "now": [Function],
}, },
@ -487,6 +503,7 @@ describe('useVariables', () => {
"tomorrow": [Function], "tomorrow": [Function],
"yesterday": [Function], "yesterday": [Function],
}, },
"$nRole": "root",
"$new": { "$new": {
"name": "new variable", "name": "new variable",
}, },

View File

@ -1,11 +1,13 @@
import { dayjs } from '@nocobase/utils/client'; import { dayjs } from '@nocobase/utils/client';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useAPIClient } from '../../api-client';
import { getDateRanges } from '../../schema-component/antd/date-picker/util'; import { getDateRanges } from '../../schema-component/antd/date-picker/util';
import { useCurrentUserContext } from '../../user'; import { useCurrentUserContext } from '../../user';
import { VariableOption } from '../types'; import { VariableOption } from '../types';
const useBuiltInVariables = () => { const useBuiltInVariables = () => {
const data = useCurrentUserContext(); const data = useCurrentUserContext();
const apiClient = useAPIClient();
const currentUser = data?.data?.data; const currentUser = data?.data?.data;
const dateVars = getDateRanges(); const dateVars = getDateRanges();
@ -16,6 +18,11 @@ const useBuiltInVariables = () => {
ctx: currentUser, ctx: currentUser,
collectionName: 'users', collectionName: 'users',
}, },
{
name: '$nRole',
ctx: apiClient.auth?.role,
collectionName: 'roles',
},
/** /**
* @deprecated * @deprecated
* *

View File

@ -1,5 +1,4 @@
import { getDateVars, parseFilter } from '@nocobase/utils'; import { getDateVars, parseFilter } from '@nocobase/utils';
import lodash from 'lodash';
function getUser(ctx) { function getUser(ctx) {
return async ({ fields }) => { return async ({ fields }) => {
@ -54,6 +53,7 @@ export const parseVariables = async (ctx, next) => {
// 新的命名方式,防止和 formily 内置变量冲突 // 新的命名方式,防止和 formily 内置变量冲突
$nDate: getDateVars(), $nDate: getDateVars(),
$user: getUser(ctx), $user: getUser(ctx),
$nRole: ctx.state.currentRole,
}, },
}); });
await next(); await next();