From a2be1a0e33a86f3e4895582854b19d6f0aac8db2 Mon Sep 17 00:00:00 2001 From: Zeke Zhang <958414905@qq.com> Date: Fri, 8 Dec 2023 19:19:53 +0800 Subject: [PATCH] feat(variable): add current role (#3167) * feat(variable): add current role * chore: translate * feat: parsing in the backend * fix: fix tests --- packages/core/client/src/locale/en_US.json | 1 + packages/core/client/src/locale/es_ES.json | 1 + packages/core/client/src/locale/fr_FR.json | 1 + packages/core/client/src/locale/ja_JP.json | 1 + packages/core/client/src/locale/ru_RU.json | 1 + packages/core/client/src/locale/tr_TR.json | 1 + packages/core/client/src/locale/uk_UA.json | 1 + packages/core/client/src/locale/zh-CN.json | 1 + .../VariableInput/VariableInput.tsx | 4 ++ .../VariableInput/hooks/useBaseVariable.tsx | 7 +++- .../VariableInput/hooks/useRoleVariable.ts | 39 +++++++++++++++++++ .../VariableInput/hooks/useVariableOptions.ts | 9 +++++ .../hooks/useParseDataScopeFilter.ts | 2 +- .../variables/__tests__/useVariables.test.tsx | 17 ++++++++ .../variables/hooks/useBuiltinVariables.ts | 7 ++++ .../server/src/middlewares/parse-variables.ts | 2 +- 16 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 packages/core/client/src/schema-settings/VariableInput/hooks/useRoleVariable.ts diff --git a/packages/core/client/src/locale/en_US.json b/packages/core/client/src/locale/en_US.json index 00bb19b33..f1c95259f 100644 --- a/packages/core/client/src/locale/en_US.json +++ b/packages/core/client/src/locale/en_US.json @@ -606,6 +606,7 @@ "Constant value": "Constant value", "Dynamic value": "Dynamic value", "Current user": "Current user", + "Current role": "Current role", "Current record": "Current record", "Parent record": "Parent record", "Current time": "Current time", diff --git a/packages/core/client/src/locale/es_ES.json b/packages/core/client/src/locale/es_ES.json index af047c3fe..769be2c02 100644 --- a/packages/core/client/src/locale/es_ES.json +++ b/packages/core/client/src/locale/es_ES.json @@ -602,6 +602,7 @@ "Constant value": "Valor constante", "Dynamic value": "Valor dinámico", "Current user": "Usuario actual", + "Current role": "Rol actual", "Current record": "Registro actual", "Parent record": "Registro padre", "Current time": "Hora actual", diff --git a/packages/core/client/src/locale/fr_FR.json b/packages/core/client/src/locale/fr_FR.json index 5074a7dcf..224d9ab2e 100644 --- a/packages/core/client/src/locale/fr_FR.json +++ b/packages/core/client/src/locale/fr_FR.json @@ -599,6 +599,7 @@ "Constant value": "Valeur constante", "Dynamic value": "Valeur dynamique", "Current user": "Utilisateur actuel", + "Current role": "Rôle actuel", "Current record": "Enregistrement actuel", "Parent record": "Enregistrement parent", "Current time": "Heure actuelle", diff --git a/packages/core/client/src/locale/ja_JP.json b/packages/core/client/src/locale/ja_JP.json index e968b660d..b4ca532c6 100644 --- a/packages/core/client/src/locale/ja_JP.json +++ b/packages/core/client/src/locale/ja_JP.json @@ -501,6 +501,7 @@ "Constant value": "定数値", "Dynamic value": "動的値", "Current user": "現在のユーザー", + "Current role": "現在の役割", "Current record": "現在のレコード", "Popup close method": "ポップアップを閉じる方法", "Automatic close": "自動で閉じる", diff --git a/packages/core/client/src/locale/ru_RU.json b/packages/core/client/src/locale/ru_RU.json index e2d615365..db4a1017d 100644 --- a/packages/core/client/src/locale/ru_RU.json +++ b/packages/core/client/src/locale/ru_RU.json @@ -438,6 +438,7 @@ "Constant value": "Постоянное значение", "Dynamic value": "Динамическое значение", "Current user": "Текущий пользователь", + "Current role": "Текущая роль", "Current record": "Текущая запись", "Parent record": "Родительская запись", "Popup close method": "Метод закрытия всплывающего окна", diff --git a/packages/core/client/src/locale/tr_TR.json b/packages/core/client/src/locale/tr_TR.json index 96174de7e..0b8b0b0a9 100644 --- a/packages/core/client/src/locale/tr_TR.json +++ b/packages/core/client/src/locale/tr_TR.json @@ -437,6 +437,7 @@ "Constant value": "Sabit değer", "Dynamic value": "Dinamik değer", "Current user": "Seçili kullanıcı", + "Current role": "Seçili rol", "Current record": "Seçili kayıt", "Parent record": "Üst kayıt", "Popup close method": "Açılır pencere kapatma metodu", diff --git a/packages/core/client/src/locale/uk_UA.json b/packages/core/client/src/locale/uk_UA.json index a33fb9034..814ceb291 100644 --- a/packages/core/client/src/locale/uk_UA.json +++ b/packages/core/client/src/locale/uk_UA.json @@ -619,6 +619,7 @@ "Constant value": "Константне значення", "Dynamic value": "Динамічне значення", "Current user": "Поточний користувач", + "Current role": "Поточна роль", "Current record": "Поточний запис", "Parent record": "Батьківський запис", "Current time": "Поточний час", diff --git a/packages/core/client/src/locale/zh-CN.json b/packages/core/client/src/locale/zh-CN.json index 5cc1ba396..28211e18e 100644 --- a/packages/core/client/src/locale/zh-CN.json +++ b/packages/core/client/src/locale/zh-CN.json @@ -622,6 +622,7 @@ "Constant value": "静态值", "Dynamic value": "动态值", "Current user": "当前用户", + "Current role": "当前角色", "Current record": "当前记录", "Parent record": "上级记录", "Current time": "当前时间", diff --git a/packages/core/client/src/schema-settings/VariableInput/VariableInput.tsx b/packages/core/client/src/schema-settings/VariableInput/VariableInput.tsx index 061a1cf9b..275a33e42 100644 --- a/packages/core/client/src/schema-settings/VariableInput/VariableInput.tsx +++ b/packages/core/client/src/schema-settings/VariableInput/VariableInput.tsx @@ -146,6 +146,10 @@ export const getShouldChange = ({ const collectionsInheritChain = collectionField ? getAllCollectionsInheritChain(collectionField.target) : []; return async (value: any, optionPath: any[]) => { + if (_.isString(value) && value.includes('$nRole')) { + return true; + } + if (!isVariable(value) || !variables || !collectionField) { return true; } diff --git a/packages/core/client/src/schema-settings/VariableInput/hooks/useBaseVariable.tsx b/packages/core/client/src/schema-settings/VariableInput/hooks/useBaseVariable.tsx index 7a5c78df8..0f73e11b7 100644 --- a/packages/core/client/src/schema-settings/VariableInput/hooks/useBaseVariable.tsx +++ b/packages/core/client/src/schema-settings/VariableInput/hooks/useBaseVariable.tsx @@ -49,6 +49,10 @@ interface BaseProps { * 不需要禁用选项,一般会在表达式中使用 */ noDisabled?: boolean; + /** + * 不需要二级菜单 + */ + noChildren?: boolean; /** * 对每一个关系字段的 fields 进行过滤 * @param fields @@ -131,6 +135,7 @@ export const useBaseVariable = ({ name, title, collectionName, + noChildren = false, // TODO: 等整理完完整测试用例后,再开启该功能 noDisabled = true, returnFields = (fields) => fields, @@ -202,7 +207,7 @@ export const useBaseVariable = ({ label: title, value: name, key: name, - isLeaf: false, + isLeaf: noChildren, field: { target: collectionName, }, diff --git a/packages/core/client/src/schema-settings/VariableInput/hooks/useRoleVariable.ts b/packages/core/client/src/schema-settings/VariableInput/hooks/useRoleVariable.ts new file mode 100644 index 000000000..f75c05477 --- /dev/null +++ b/packages/core/client/src/schema-settings/VariableInput/hooks/useRoleVariable.ts @@ -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; +}; diff --git a/packages/core/client/src/schema-settings/VariableInput/hooks/useVariableOptions.ts b/packages/core/client/src/schema-settings/VariableInput/hooks/useVariableOptions.ts index 1c407ed9a..ac757a376 100644 --- a/packages/core/client/src/schema-settings/VariableInput/hooks/useVariableOptions.ts +++ b/packages/core/client/src/schema-settings/VariableInput/hooks/useVariableOptions.ts @@ -10,6 +10,7 @@ import { useFormVariable } from './useFormVariable'; import { useIterationVariable } from './useIterationVariable'; import { useParentRecordVariable } from './useParentRecordVariable'; import { useRecordVariable } from './useRecordVariable'; +import { useRoleVariable } from './useRoleVariable'; import { useUserVariable } from './useUserVariable'; interface Props { @@ -58,6 +59,12 @@ export const useVariableOptions = ({ noDisabled, targetFieldSchema, }); + const roleVariable = useRoleVariable({ + uiSchema: uiSchema, + collectionField, + noDisabled, + targetFieldSchema, + }); const dateVariable = useDateVariable({ operator, schema: uiSchema, noDisabled }); const formVariable = useFormVariable({ schema: uiSchema, @@ -91,6 +98,7 @@ export const useVariableOptions = ({ return useMemo(() => { return [ userVariable, + roleVariable, dateVariable, form && !form.readPretty && formVariable, (isInSubForm || isInSubTable) && iterationVariable, @@ -101,6 +109,7 @@ export const useVariableOptions = ({ ].filter(Boolean); }, [ userVariable, + roleVariable, dateVariable, form, formVariable, diff --git a/packages/core/client/src/schema-settings/hooks/useParseDataScopeFilter.ts b/packages/core/client/src/schema-settings/hooks/useParseDataScopeFilter.ts index d6368c8a4..6c02297a7 100644 --- a/packages/core/client/src/schema-settings/hooks/useParseDataScopeFilter.ts +++ b/packages/core/client/src/schema-settings/hooks/useParseDataScopeFilter.ts @@ -14,7 +14,7 @@ interface Props { } // TODO: 建议变量名统一命名为 `$n` 开头,以防止与 formily 内置变量冲突 -const defaultExclude = ['$user', '$date', '$nDate']; +const defaultExclude = ['$user', '$date', '$nDate', '$nRole']; const useParseDataScopeFilter = ({ exclude = defaultExclude, currentRecord }: Props = {}) => { const localVariables = useLocalVariables({ currentRecord }); diff --git a/packages/core/client/src/variables/__tests__/useVariables.test.tsx b/packages/core/client/src/variables/__tests__/useVariables.test.tsx index 0a2d24776..86b306c54 100644 --- a/packages/core/client/src/variables/__tests__/useVariables.test.tsx +++ b/packages/core/client/src/variables/__tests__/useVariables.test.tsx @@ -56,6 +56,10 @@ vi.mock('../../collection-manager', async () => { }); const { apiClient, mockRequest } = mockAPIClient(); + +// 用于解析 `$nRole` 的值 +apiClient.auth.role = 'root'; + mockRequest.onGet('/auth:check').reply(() => { return [ 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(() => { return [ 200, @@ -194,6 +208,7 @@ describe('useVariables', () => { "tomorrow": [Function], "yesterday": [Function], }, + "$nRole": "root", "$system": { "now": [Function], }, @@ -404,6 +419,7 @@ describe('useVariables', () => { "tomorrow": [Function], "yesterday": [Function], }, + "$nRole": "root", "$system": { "now": [Function], }, @@ -487,6 +503,7 @@ describe('useVariables', () => { "tomorrow": [Function], "yesterday": [Function], }, + "$nRole": "root", "$new": { "name": "new variable", }, diff --git a/packages/core/client/src/variables/hooks/useBuiltinVariables.ts b/packages/core/client/src/variables/hooks/useBuiltinVariables.ts index 6a37fa832..e21bc01b4 100644 --- a/packages/core/client/src/variables/hooks/useBuiltinVariables.ts +++ b/packages/core/client/src/variables/hooks/useBuiltinVariables.ts @@ -1,11 +1,13 @@ import { dayjs } from '@nocobase/utils/client'; import { useMemo } from 'react'; +import { useAPIClient } from '../../api-client'; import { getDateRanges } from '../../schema-component/antd/date-picker/util'; import { useCurrentUserContext } from '../../user'; import { VariableOption } from '../types'; const useBuiltInVariables = () => { const data = useCurrentUserContext(); + const apiClient = useAPIClient(); const currentUser = data?.data?.data; const dateVars = getDateRanges(); @@ -16,6 +18,11 @@ const useBuiltInVariables = () => { ctx: currentUser, collectionName: 'users', }, + { + name: '$nRole', + ctx: apiClient.auth?.role, + collectionName: 'roles', + }, /** * @deprecated * 兼容老版本 diff --git a/packages/core/server/src/middlewares/parse-variables.ts b/packages/core/server/src/middlewares/parse-variables.ts index cdc940615..b1dc14b0e 100644 --- a/packages/core/server/src/middlewares/parse-variables.ts +++ b/packages/core/server/src/middlewares/parse-variables.ts @@ -1,5 +1,4 @@ import { getDateVars, parseFilter } from '@nocobase/utils'; -import lodash from 'lodash'; function getUser(ctx) { return async ({ fields }) => { @@ -54,6 +53,7 @@ export const parseVariables = async (ctx, next) => { // 新的命名方式,防止和 formily 内置变量冲突 $nDate: getDateVars(), $user: getUser(ctx), + $nRole: ctx.state.currentRole, }, }); await next();