chore: deprecate the current record variable from the form (#4063)

* chore: remove the current record variable from the form

* chore: fix failed e2e

* refactor(VariableInput): support for setting the react node for label

* feat: support to show tooltip

* chore: stash

* chore: add translation

* chore: add translation

* chore: fix expresion

* test: add e2e for deprecated variables

* refactor: migrate file

* chore: make e2e pass

* chore: make e2e pass

* chore: make e2e pass

* chore: adjust

* chore: natch

* chore: revert match
This commit is contained in:
Zeke Zhang 2024-04-23 21:18:27 +08:00 committed by GitHub
parent 785cc525c4
commit fd4c9cb288
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 647 additions and 95 deletions

View File

@ -194,7 +194,7 @@ export const RenderChildrenWithAssociationFilter: React.FC<any> = (props) => {
/**
* @internal
*/
const BlockContext = createContext<{
export const BlockContext = createContext<{
/** 用以区分区块的标识 */
name: string;
}>(null);

View File

@ -822,5 +822,6 @@
"Owners": "Owners",
"Plugin settings": "Plugin settings",
"Menu": "Menu",
"Drag and drop sorting field": "Drag and drop sorting field"
"Drag and drop sorting field": "Drag and drop sorting field",
"This variable has been deprecated and can be replaced with \"Current form\"": "This variable has been deprecated and can be replaced with \"Current form\""
}

View File

@ -755,5 +755,6 @@
"DataSource": "Fuente de datos",
"Home page": "Página de inicio",
"Handbook": "Manual de usuario",
"License": "Licencia"
"License": "Licencia",
"This variable has been deprecated and can be replaced with \"Current form\"": "La variable ha sido obsoleta; \"Formulario actual\" puede ser utilizada como sustituto"
}

View File

@ -775,5 +775,6 @@
"Allow selection of existing records":"Permet de sélectionner des données existantes",
"Home page": "Page d'accueil",
"Handbook": "Manuel de l'utilisateur",
"License": "Licence"
"License": "Licence",
"This variable has been deprecated and can be replaced with \"Current form\"": "La variable a été obsolète ; \"Formulaire actuel\" peut être utilisé comme substitut"
}

View File

@ -694,5 +694,6 @@
"The {{type}} \"{{name}}\" may have been deleted. Please remove this {{blockType}}.": "{{type}} \"{{name}}\" は削除されている可能性があります。この {{blockType}} を削除してください。",
"Home page": "ホームページ",
"Handbook": "ユーザーマニュアル",
"License": "ライセンス"
"License": "ライセンス",
"This variable has been deprecated and can be replaced with \"Current form\"": "この変数は非推奨です。代わりに「現在のフォーム」を使用してください"
}

View File

@ -866,5 +866,6 @@
"The {{type}} \"{{name}}\" may have been deleted. Please remove this {{blockType}}.": "{{type}} \"{{name}}\"이(가) 삭제되었을 수 있습니다. 이 {{blockType}}을(를) 제거하십시오.",
"Home page": "홈페이지",
"Handbook": "사용자 매뉴얼",
"License": "라이선스"
"License": "라이선스",
"This variable has been deprecated and can be replaced with \"Current form\"": "변수가 폐기되었습니다. \"현재 폼\"을 대체로 사용할 수 있습니다"
}

View File

@ -733,5 +733,6 @@
"Allow selection of existing records":"Permitir a selecção dos registos existentes",
"Home page": "Página inicial",
"Handbook": "Manual do usuário",
"License": "Licença"
"License": "Licença",
"This variable has been deprecated and can be replaced with \"Current form\"": "A variável foi descontinuada; \"Formulário atual\" pode ser usada como substituto"
}

View File

@ -569,5 +569,6 @@
"DataSource": "Источник данных",
"Home page": "Домашняя страница",
"Handbook": "Руководство пользователя",
"License": "Лицензия"
"License": "Лицензия",
"This variable has been deprecated and can be replaced with \"Current form\"": "Переменная устарела; \"Текущая форма\" может быть использована в качестве замены"
}

View File

@ -567,5 +567,6 @@
"The {{type}} \"{{name}}\" may have been deleted. Please remove this {{blockType}}.": "{{type}} \"{{name}}\" silinmiş olabilir. Lütfen bu {{blockType}}'yi kaldırın.",
"Home page": "Anasayfa",
"Handbook": "Kullanıcı kılavuzu",
"License": "Lisans"
"License": "Lisans",
"This variable has been deprecated and can be replaced with \"Current form\"": "Değişken kullanımdan kaldırıldı; \"Geçerli form\" yerine kullanılabilir"
}

View File

@ -775,5 +775,6 @@
"The {{type}} \"{{name}}\" may have been deleted. Please remove this {{blockType}}.": "{{type}} \"{{name}}\" може бути видалено. Будь ласка, видаліть цей {{blockType}}.",
"Home page": "Домашня сторінка",
"Handbook": "Посібник користувача",
"License": "Ліцензія"
"License": "Ліцензія",
"This variable has been deprecated and can be replaced with \"Current form\"": "Змінна була застарілою; \"Поточна форма\" може бути використана як заміна"
}

View File

@ -928,5 +928,6 @@
"Normal": "常规",
"Automatically generate default values": "随机生成默认值",
"Refresh data on close": "关闭后刷新数据",
"Refresh data on action": "执行后刷新数据"
"Refresh data on action": "执行后刷新数据",
"This variable has been deprecated and can be replaced with \"Current form\"": "该变量已被弃用,可以使用“当前表单”替代"
}

View File

@ -864,5 +864,6 @@
"Allow selection of existing records":"允許選擇已有資料",
"Home page": "主頁",
"Handbook": "使用手冊",
"License": "許可證"
"License": "許可證",
"This variable has been deprecated and can be replaced with \"Current form\"": "該變數已被棄用,可以使用“當前表單”作為替代"
}

View File

@ -8472,3 +8472,411 @@ export const T3979: PageConfig = {
'x-index': 1,
},
};
export const oneTableWithUsersForDeprecatedVariables = {
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
properties: {
'0kf7f0ilx5p': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'page:addBlock',
properties: {
'7nkrwaiut3c': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '0.21.0-alpha.12',
properties: {
okrwqoxd83x: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '0.21.0-alpha.12',
properties: {
h00mdakub1n: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableBlockProvider',
'x-acl-action': 'users:list',
'x-use-decorator-props': 'useTableBlockDecoratorProps',
'x-decorator-props': {
collection: 'users',
dataSource: 'main',
action: 'list',
params: {
pageSize: 20,
},
rowKey: 'id',
showIndex: true,
dragSort: false,
},
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:table',
'x-component': 'CardItem',
'x-filter-targets': [],
'x-app-version': '0.21.0-alpha.12',
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'table:configureActions',
'x-component': 'ActionBar',
'x-component-props': {
style: {
marginBottom: 'var(--nb-spacing)',
},
},
'x-app-version': '0.21.0-alpha.12',
'x-uid': 'esse5o4398b',
'x-async': false,
'x-index': 1,
},
'630zp06en3b': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'array',
'x-initializer': 'table:configureColumns',
'x-component': 'TableV2',
'x-use-component-props': 'useTableBlockProps',
'x-component-props': {
rowKey: 'id',
rowSelection: {
type: 'checkbox',
},
},
'x-app-version': '0.21.0-alpha.12',
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("Actions") }}',
'x-action-column': 'actions',
'x-decorator': 'TableV2.Column.ActionBar',
'x-component': 'TableV2.Column',
'x-designer': 'TableV2.ActionColumnDesigner',
'x-initializer': 'table:configureItemActions',
'x-app-version': '0.21.0-alpha.12',
properties: {
'1my2j5tqtvu': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'DndContext',
'x-component': 'Space',
'x-component-props': {
split: '|',
},
'x-app-version': '0.21.0-alpha.12',
properties: {
d0bwrtz9f7j: {
'x-uid': '7mzammd9sbg',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: 'Edit record',
'x-action': 'update',
'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actionSettings:edit',
'x-component': 'Action.Link',
'x-component-props': {
openMode: 'drawer',
danger: false,
},
'x-decorator': 'ACLActionProvider',
'x-designer-props': {
linkageAction: true,
},
properties: {
drawer: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("Edit record") }}',
'x-component': 'Action.Container',
'x-component-props': {
className: 'nb-action-popup',
},
properties: {
tabs: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Tabs',
'x-component-props': {},
'x-initializer': 'popup:addTab',
properties: {
tab1: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{t("Edit")}}',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'popup:common:addBlock',
properties: {
dq07f3u2u30: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '0.21.0-alpha.12',
properties: {
nxs6wg3n41c: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '0.21.0-alpha.12',
properties: {
mhk7rm0fw12: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-acl-action-props': {
skipScopeCheck: false,
},
'x-acl-action': 'users:update',
'x-decorator': 'FormBlockProvider',
'x-use-decorator-props':
'useEditFormBlockDecoratorProps',
'x-decorator-props': {
action: 'get',
dataSource: 'main',
collection: 'users',
},
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:editForm',
'x-component': 'CardItem',
'x-app-version': '0.21.0-alpha.12',
properties: {
'54b22mqfpmx': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'FormV2',
'x-use-component-props': 'useEditFormBlockProps',
'x-app-version': '0.21.0-alpha.12',
properties: {
grid: {
'x-uid': 'ufdkfceoidc',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'form:configureFields',
'x-app-version': '0.21.0-alpha.12',
'x-linkage-rules': [
{
condition: {
$and: [
{
nickname: {
$includes: '{{$nRecord.nickname}}',
},
},
],
},
actions: [
{
targetFields: ['nickname'],
operator: 'value',
value: {
mode: 'express',
value: '{{$nRecord.username}}',
result: '{{$nRecord.username}}',
},
},
],
},
],
properties: {
g5r0oap3utr: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '0.21.0-alpha.12',
properties: {
lsugzv7lefq: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '0.21.0-alpha.12',
properties: {
nickname: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-toolbar':
'FormItemSchemaToolbar',
'x-settings':
'fieldSettings:FormItem',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-collection-field':
'users.nickname',
'x-component-props': {},
'x-app-version': '0.21.0-alpha.12',
'x-uid': '96ztt5zwj94',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'bx1yby02otk',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'ov7zgdkgw75',
'x-async': false,
'x-index': 1,
},
},
'x-async': false,
'x-index': 1,
},
iia8azf8515: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'editForm:configureActions',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
style: {
marginTop: 24,
},
},
'x-app-version': '0.21.0-alpha.12',
properties: {
m2ya2utojyv: {
_isJSONSchemaObject: true,
version: '2.0',
title: '{{ t("Submit") }}',
'x-action': 'submit',
'x-component': 'Action',
'x-use-component-props':
'useUpdateActionProps',
'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actionSettings:updateSubmit',
'x-component-props': {
type: 'primary',
htmlType: 'submit',
},
'x-action-settings': {
triggerWorkflows: [],
},
type: 'void',
'x-app-version': '0.21.0-alpha.12',
'x-uid': 'bk42qy23o72',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '7l3e8eg5twd',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'hd2ivxz2yab',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'q66c3ygx762',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '6lc4ohi2dro',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'e8wspaxavfr',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'ra8agi0pvsa',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'yos0bp0yikg',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '45maugqwh7b',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'ksrayq74oxe',
'x-async': false,
'x-index': 1,
},
},
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'o0u2aidtwdw',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '14wmkjgx19j',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '5bzwy79fbvz',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'qehg70dxj8b',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '1f9ilr6t1yn',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'fcvb1l15gv9',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '2ahlv6ydwlp',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '8uggi2e1r5s',
'x-async': true,
'x-index': 1,
},
};

View File

@ -0,0 +1,59 @@
import { expect, test } from '@nocobase/test/e2e';
import { oneTableWithUsersForDeprecatedVariables } from '../form-create/templatesOfBug';
test.describe('deprecated variables', () => {
test('current record', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneTableWithUsersForDeprecatedVariables).goto();
// 1. 已设置过 Current record 的变量依然能正常显示
await page.getByLabel('action-Action.Link-Edit').click();
await page.getByLabel('block-item-CardItem-users-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:editForm-users').hover();
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
await expect(page.getByLabel('variable-tag').getByText('Current record / Nickname')).toBeVisible();
// 2. 但是变量列表中是禁用状态
await page.locator('button').filter({ hasText: /^x$/ }).click();
await page.getByRole('menuitemcheckbox', { name: 'Current record right' }).hover({ position: { x: 40, y: 12 } });
await expect(page.getByRole('tooltip', { name: 'This variable has been deprecated' })).toBeVisible();
await expect(page.getByRole('menuitemcheckbox', { name: 'Current record right' })).toHaveClass(
new RegExp('ant-cascader-menu-item-disabled'),
);
await page.mouse.move(0, 300);
// 使下拉菜单消失
await page.getByLabel('Linkage rules').getByText('Linkage rules').click();
// 表达式输入框也是一样
await page.getByText('xSelect a variable').click();
await page.getByRole('menuitemcheckbox', { name: 'Current record right' }).hover({ position: { x: 40, y: 12 } });
await expect(page.getByRole('tooltip', { name: 'This variable has been deprecated' })).toBeVisible();
await expect(page.getByRole('menuitemcheckbox', { name: 'Current record right' })).toHaveClass(
new RegExp('ant-cascader-menu-item-disabled'),
);
await page.mouse.move(0, 300);
// 使下拉菜单消失
await page.getByLabel('Linkage rules').getByText('Linkage rules').click();
// 3. 当设置为其它变量后,再次打开,变量列表中的弃用变量不再显示
await page.locator('button').filter({ hasText: /^x$/ }).click();
await page.getByRole('menuitemcheckbox', { name: 'Current form right' }).click();
await page.getByRole('menuitemcheckbox', { name: 'Nickname' }).click();
await expect(page.getByLabel('variable-tag').getByText('Current form / Nickname')).toBeVisible();
// 清空表达式
await page.getByLabel('textbox').clear();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 4. 再次打开弹窗,变量列表中的弃用变量不再显示
await page.getByLabel('block-item-CardItem-users-form').hover();
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:editForm-users').hover();
await page.getByRole('menuitem', { name: 'Linkage rules' }).click();
await page.locator('button').filter({ hasText: /^x$/ }).click();
await expect(page.getByRole('menuitemcheckbox', { name: 'Current record right' })).toBeHidden();
// 使下拉菜单消失
await page.getByLabel('Linkage rules').getByText('Linkage rules').click();
// 表达式也是一样
await page.getByText('xSelect a variable').click();
await expect(page.getByRole('menuitemcheckbox', { name: 'Current record right' })).toBeHidden();
});
});

View File

@ -37,7 +37,7 @@ test.describe('grid card block schema settings', () => {
await page.getByLabel('block-item-BlockItem-general-grid-card').hover();
await page.getByLabel('designer-schema-settings-BlockItem-GridCard.Designer-general').hover();
await page.getByRole('menuitem', { name: 'Set the count of columns displayed in a row' }).click();
await page.getByLabel('block-item-Slider-general-Desktop device').getByText('2', { exact: true }).click();
await page.getByLabel('block-item-Slider-general-grid-card-Desktop device').getByText('2', { exact: true }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 需要刷新页面才会生效
@ -64,7 +64,7 @@ test.describe('grid card block schema settings', () => {
await page.getByLabel('block-item-BlockItem-general-').hover();
await page.getByLabel('designer-schema-settings-BlockItem-blockSettings:gridCard-general').hover();
await page.getByRole('menuitem', { name: 'Set the count of columns displayed in a row' }).click();
await page.getByLabel('block-item-Slider-general-Desktop device').getByText('2', { exact: true }).click();
await page.getByLabel('block-item-Slider-general-grid-card-Desktop device').getByText('2', { exact: true }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 需要刷新页面才会生效

View File

@ -438,8 +438,8 @@ test.describe('actions schema settings', () => {
await showMenu(page);
await page.getByRole('menuitem', { name: 'Edit button' }).click();
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').click();
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').fill('1234');
await page.getByLabel('block-item-Input-general-').getByRole('textbox').click();
await page.getByLabel('block-item-Input-general-').getByRole('textbox').fill('1234');
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByRole('button', { name: '1234' })).toBeVisible();
@ -569,8 +569,8 @@ test.describe('actions schema settings', () => {
await showMenu(page);
await page.getByRole('menuitem', { name: 'Edit button' }).click();
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').click();
await page.getByLabel('block-item-Input-general-Button title').getByRole('textbox').fill('Delete record');
await page.getByLabel('block-item-Input-general-').getByRole('textbox').click();
await page.getByLabel('block-item-Input-general-').getByRole('textbox').fill('Delete record');
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByLabel('action-Action.Link-Delete record-destroy-general-table-0')).toBeVisible();

View File

@ -25,8 +25,8 @@ test.describe('tabs schema settings', () => {
await showSettings(page);
await page.getByRole('menuitem', { name: 'Edit', exact: true }).click();
await page.mouse.move(300, 0);
await page.getByLabel('block-item-Input-general-Tab name').getByRole('textbox').click();
await page.getByLabel('block-item-Input-general-Tab name').getByRole('textbox').fill('Add new with new name');
await page.getByLabel('block-item-Input-general-').getByRole('textbox').click();
await page.getByLabel('block-item-Input-general-').getByRole('textbox').fill('Add new with new name');
await page.getByRole('button', { name: 'Select icon' }).click();
await page.getByLabel('account-book').locator('svg').click();
await page.getByRole('button', { name: 'OK', exact: true }).click();

View File

@ -12,7 +12,7 @@ test.describe('z-index of dialog', () => {
await page.getByRole('button', { name: 'designer-schema-settings-' }).hover();
await page.getByRole('menuitem', { name: 'Edit block title' }).click();
await expect(page.getByLabel('block-item-Input-users-Block')).toBeVisible();
await expect(page.getByLabel('block-item-Input-users-form-')).toBeVisible();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByLabel('block-item-Input-users-Block')).not.toBeVisible();
});

View File

@ -7,7 +7,6 @@ import useAntdInputStyle from 'antd/es/input/style';
import type { DefaultOptionType } from 'antd/lib/cascader';
import classNames from 'classnames';
import dayjs from 'dayjs';
import { cloneDeep } from 'lodash';
import React, { useCallback, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useCompile } from '../../hooks';
@ -151,7 +150,7 @@ export function Input(props) {
const { t } = useTranslation();
const form = useForm();
const [options, setOptions] = React.useState<DefaultOptionType[]>([]);
const [variableText, setVariableText] = React.useState('');
const [variableText, setVariableText] = React.useState([]);
const parsed = useMemo(() => parseValue(value), [value]);
const isConstant = typeof parsed === 'string';
@ -189,8 +188,12 @@ export function Input(props) {
}, [type, useTypedConstant]);
useEffect(() => {
setOptions([compile(constantOption), ...(scope ? cloneDeep(scope) : [])]);
}, [scope]);
const options = [compile(constantOption), ...(scope ? [...scope] : [])].filter((item) => {
return !item.deprecated || variable?.[0] === item[names.value];
});
setOptions(options);
}, [scope, variable]);
const loadData = async (selectedOptions: DefaultOptionType[]) => {
const option = selectedOptions[selectedOptions.length - 1];
@ -251,7 +254,7 @@ export function Input(props) {
}
}
setOptions([...options]);
setVariableText(labels.join(' / '));
setVariableText([...labels]);
};
run();
@ -296,6 +299,7 @@ export function Input(props) {
<div
role="button"
aria-label="variable-tag"
style={{ overflow: 'hidden' }}
onInput={(e) => e.preventDefault()}
onKeyDown={(e) => {
if (e.key !== 'Backspace') {
@ -309,7 +313,14 @@ export function Input(props) {
suppressContentEditableWarning
>
<Tag contentEditable={false} color="blue">
{variableText}
{variableText.map((item, index) => {
return (
<>
{index ? ' / ' : ''}
{item}
</>
);
})}
</Tag>
</div>
{!disabled ? (

View File

@ -1,12 +1,13 @@
import { css, cx } from '@emotion/css';
import { useForm } from '@formily/react';
import { Input, Space } from 'antd';
import { cloneDeep } from 'lodash';
import { Space } from 'antd';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { renderToString } from 'react-dom/server';
import sanitizeHTML from 'sanitize-html';
import { error } from '@nocobase/utils/client';
import { isReactElement } from '@formily/shared';
import { EllipsisWithTooltip } from '../..';
import { VariableSelect } from './VariableSelect';
import { useStyles } from './style';
@ -114,7 +115,17 @@ function createOptionsValueLabelMap(options: any[]) {
}
function createVariableTagHTML(variable, keyLabelMap) {
const labels = keyLabelMap.get(variable);
let labels = keyLabelMap.get(variable);
if (labels) {
labels = labels.map((label) => {
if (isReactElement(label)) {
return renderToString(label);
}
return label;
});
}
return `<span class="ant-tag ant-tag-blue" contentEditable="false" data-variable="${variable}">${
labels ? labels.join(' / ') : '...'
}</span>`;
@ -428,8 +439,12 @@ export function TextArea(props) {
);
}
async function preloadOptions(scope, value) {
const options = cloneDeep(scope ?? []);
async function preloadOptions(scope, value: string) {
let options = [...(scope ?? [])];
options = options.filter((item) => {
return !item.deprecated || value?.includes(item.value);
});
// 重置正则的匹配位置
VARIABLE_RE.lastIndex = 0;

View File

@ -68,8 +68,10 @@ import {
useSortFields,
} from '..';
import {
BlockContext,
BlockRequestContext_deprecated,
FormBlockContext,
useBlockContext,
useFormBlockContext,
useFormBlockType,
useTableBlockContext,
@ -975,6 +977,7 @@ export const SchemaSettingsModalItem: FC<SchemaSettingsModalItemProps> = (props)
const record = useCollectionRecord();
const { association } = useDataBlockProps() || {};
const formCtx = useFormBlockContext();
const blockOptions = useBlockContext();
// 解决变量`当前对象`值在弹窗中丢失的问题
const { formValue: subFormValue, collection: subFormCollection } = useSubFormValue();
@ -996,6 +999,7 @@ export const SchemaSettingsModalItem: FC<SchemaSettingsModalItemProps> = (props)
{ title: schema.title || title, width },
() => {
return (
<BlockContext.Provider value={blockOptions}>
<DeclareVariable
name="$nPopupRecord"
title={popupRecordVariable.title}
@ -1048,6 +1052,7 @@ export const SchemaSettingsModalItem: FC<SchemaSettingsModalItemProps> = (props)
</FormBlockContext.Provider>
</CollectionRecordProvider>
</DeclareVariable>
</BlockContext.Provider>
);
},
theme,

View File

@ -248,7 +248,7 @@ export function useCompatOldVariables(props: {
return variables;
}
variables = _.cloneDeep(variables);
variables = [...variables];
const systemVariable: Option = {
value: '$system',

View File

@ -1,4 +1,5 @@
import { ISchema, Schema } from '@formily/json-schema';
import { Tooltip } from 'antd';
import React, { useContext, useMemo } from 'react';
import { CollectionFieldOptions_deprecated, useCollectionManager_deprecated } from '../../../collection-manager';
import { useCompile, useGetFilterOptions } from '../../../schema-component';
@ -29,6 +30,10 @@ interface GetOptionsParams {
compile: (value: string) => any;
isDisabled?: (params: IsDisabledParams) => boolean;
getCollectionField?: (name: string) => CollectionFieldOptions_deprecated;
/**
* true
*/
deprecated?: boolean;
}
interface BaseProps {
@ -60,6 +65,11 @@ interface BaseProps {
*/
returnFields?(fields: FieldOption[], option: Option): FieldOption[];
dataSource?: string;
/**
* true
*/
deprecated?: boolean;
tooltip?: string;
}
interface BaseVariableProviderProps {
@ -89,6 +99,7 @@ const getChildren = (
isDisabled,
targetFieldSchema,
getCollectionField,
deprecated,
}: GetOptionsParams,
): Option[] => {
const result = options
@ -98,9 +109,11 @@ const getChildren = (
key: option.name,
value: option.name,
label: compile(option.title),
disabled: noDisabled
disabled:
deprecated ||
(noDisabled
? false
: isDisabled({ option, collectionField, uiSchema, targetFieldSchema, getCollectionField }),
: isDisabled({ option, collectionField, uiSchema, targetFieldSchema, getCollectionField })),
isLeaf: true,
depth,
};
@ -117,9 +130,11 @@ const getChildren = (
isLeaf: false,
field: option,
depth,
disabled: noDisabled
disabled:
deprecated ||
(noDisabled
? false
: isDisabled({ option, collectionField, uiSchema, targetFieldSchema, getCollectionField }),
: isDisabled({ option, collectionField, uiSchema, targetFieldSchema, getCollectionField })),
loadChildren,
};
})
@ -141,6 +156,8 @@ export const useBaseVariable = ({
noDisabled = true,
dataSource,
returnFields = (fields) => fields,
deprecated,
tooltip,
}: BaseProps) => {
const compile = useCompile();
const getFilterOptions = useGetFilterOptions();
@ -167,6 +184,7 @@ export const useBaseVariable = ({
compile,
isDisabled: isDisabled || isDisabledDefault,
getCollectionField,
deprecated,
}) || []
)
// 将叶子节点排列在上面,方便用户选择
@ -206,7 +224,25 @@ export const useBaseVariable = ({
const result = useMemo(() => {
return {
label: title,
label: tooltip ? (
<Tooltip placement="left" title={tooltip} zIndex={9999}>
<span
style={{
position: 'relative',
display: 'inline-block',
marginLeft: -14,
paddingLeft: 14,
marginRight: -80,
paddingRight: 80,
zIndex: 1,
}}
>
{title}
</span>
</Tooltip>
) : (
title
),
value: name,
key: name,
isLeaf: noChildren,
@ -216,6 +252,8 @@ export const useBaseVariable = ({
depth: 0,
loadChildren,
children: [],
disabled: !!deprecated,
deprecated,
} as Option;
}, [uiSchema?.['x-component']]);

View File

@ -1,6 +1,7 @@
import { Schema } from '@formily/json-schema';
import _ from 'lodash';
import { useTranslation } from 'react-i18next';
import { useBlockContext } from '../../../block-provider/BlockProvider';
import { useFormBlockContext } from '../../../block-provider/FormBlockProvider';
import { CollectionFieldOptions_deprecated } from '../../../collection-manager';
import { useCollection, useCollectionRecordData } from '../../../data-source';
@ -46,6 +47,7 @@ export const useRecordVariable = (props: Props) => {
*/
export const useCurrentRecordVariable = (props: Props = {}) => {
const { t } = useTranslation();
const { name: blockType } = useBlockContext() || {};
const collection = useCollection();
const recordData = useCollectionRecordData();
const { formRecord, collectionName } = useFormBlockContext();
@ -58,6 +60,8 @@ export const useCurrentRecordVariable = (props: Props = {}) => {
collectionName: realCollectionName,
noDisabled: props.noDisabled,
targetFieldSchema: props.targetFieldSchema,
deprecated: blockType === 'form',
tooltip: blockType === 'form' ? t('This variable has been deprecated and can be replaced with "Current form"') : '',
});
return {

View File

@ -14,6 +14,7 @@ export interface Option extends DefaultOptionType {
loadChildren?(option: Option): Promise<void>;
field?: FieldOption;
depth?: number;
deprecated?: boolean;
}
export interface FieldOption {

View File

@ -55,7 +55,7 @@ test.describe('form item & create form', () => {
},
supportVariables: ['Constant', 'Current user', 'Date variables', 'Current form'],
inputConstantValue: async () => {
await page.getByLabel('block-item-CollectionField-general-general.richText').locator('.ql-editor').click();
await page.getByLabel('block-item-VariableInput-').locator('.ql-editor').click();
await page.keyboard.type('test rich text');
},
expectConstantValue: async () => {

View File

@ -303,8 +303,8 @@ test.describe('table column & table', () => {
// 显示出原字段名称
await expect(page.getByRole('dialog').getByText('Original field title: singleLineText')).toBeVisible();
// 输入新字段名称
await page.getByLabel('block-item-Input-general-column title').getByRole('textbox').click();
await page.getByLabel('block-item-Input-general-column title').getByRole('textbox').fill('new column title');
await page.getByLabel('block-item-Input-general-').getByRole('textbox').click();
await page.getByLabel('block-item-Input-general-').getByRole('textbox').fill('new column title');
await page.getByRole('button', { name: 'OK', exact: true }).click();
// 新名称应该显示出来
@ -415,7 +415,7 @@ test.describe('table column & sub-table in edit form', () => {
'Current role',
'Current form',
'Current object',
'Current record',
// 'Current record',
],
variableValue: ['Current user', 'Nickname'],
expectVariableValue: async () => {