Unverified Commit 89af2175 authored by chenos's avatar chenos Committed by GitHub
Browse files

Some features (#979)


* feat: add import client

* feat: add import server

* refactor: change export use library of file-saver

* refactor: upload excel file done

* refactor: upload xls transform

* feat: upload ui done

* feat: exclude unable import fields

* feat: excel file validator done

* feat: import done

* feat: import transform done

* fix: add import plugin in presets

* fix: explain will not output in template what is empty

* fix: config permission

* fix: permission skip

* fix: import password must be string

* fix: done close Modal

* fix: loop through, inserting data item by item

* fix: number calc with using mathjs

* fix: import plugin add locale

* fix: fix some bugs

* feat: bulk update done

* fix: transaction cannot be rolled back because it has been finished with state: rollback

* fix(plugin-system-settings): convert array to json

* fix(collection-manager): o2m is array type

* fix: missing RefreshActionInitializer

* fix(collection-manger): incorrect scope key parameter

* fix: can't access pages without permission via url (#826)

* feat(database): add sequence field type (#779)

* feat(database): add serialString field type

* feat(database): add serial string type field ui (skip ci)

* test(feat/database): test field options

* docs: demo

* fix(database): fix array table field behavior

* fix(database): fix serial type interface ui

* fix(database): add match logic for patterns changes

* fix(database): fix serial type query last bug in mysql

* refactor(database): refactor last record logic

* chore: revert modification on unnecessary file

* refactor(database): rename serialString type to sequence
Co-authored-by: default avatarchenos <chenlinxh@gmail.com>

* added Russian translation (#840)

* Russian translation

* Add files via upload

Add RU locale into index.ts
Bugs fixed in the ru_RU.ts

* Update index.ts

Correct lines 4 and 8

* feat: update option must have filter or filterByTk (#847)

* feat: update option must have filter or filterByTk

* fix: typo

* fix: typo

* feat(core/cache): support cache (#876)

* feat(core/cache): support cache

* build(create-nocobase-app): remove --cache-store-package cli option

* perf(core/cache): modify default cache config and remove unnecessary logic code

* fix: slow join query issued by appends field in find method of repository  (#845)

* fix: slow join query issue by appends field in repository.find

* feat: handle appending query in multiple relation repository

* feat: handle appending query in single relation repository
Co-authored-by: default avatarchenos <chenlinxh@gmail.com>

* fix: sort parameter is missing (#849)

* fix: 审计日志翻页sort丢失

* fix: 审计日志翻页sort丢失

* fix: 审计日志翻页sort丢失
Co-authored-by: default avatar唐小爱 <tangxiaoai@192.168.0.103>

* fix(formula): support integer and fix NaN error (#879)

* fix(formula): support integer and fix NaN error

* style(formula-input): remove debugger

* fix(database): fix the index name too long error

* feat(collection-manager): inverse fields can be configured (#883)

* feat: inverse field

* feat: improve code

* feat: translations

* fix: required

* fix: run test by jest (#891)

* fix: unable to submit form during file upload (#892)

* fix(client/block-select-collection): fix select collection menu view error (#889)

* fix(client/block-select-collection): fix too many collection menu view error

* fix(client/relate-collection-field-menu): fix relate collection field menu view too long error

* fix(client/record-picker): support record-picker show format DataPicker (#888)

* fix(client/record-picker): support record-picker show format DataPicker

* fix(client/record-picker): undefined judgment and when change field's label refresh format in time

* feat: improve signin and signup page components

* feat(plugin-workflow): add concat calculator (#894)

* fix: single relation repository appends query issue (#901)

* fix: appends merge includes (#905)

* fix: build error

* fix(client): tab pane initializers for create form block

* fix: version judgment is not accurate

* fix: sync collection field default value (#907)

* feat: limit database identifier (#908)

* fix: cannot read properties of undefined (reading 'target')

* fix: appends merge now using primary key (#911)

* fix: appends merge now using primary key

* chore: console.log

* fix: unbind on error throwing (#914)

* feat: create with array of values (#912)

* feat: create with array of values

* chore: console.log

* chore: debug

* fix(client/route-switch): skip sub routes

* Feat: plugin workflow collection field (#919)

* feat(plugin-workflow): use Collectionfield component to render form

* fix(plugin-workflow): fix association types value assigning in nodes

* fix: missing menuItemGroupCss

* fix: multiple = false

* chore(versions): :blush:

 publish v0.7.5-alpha.1 (#920)

* fix(plugin-workflow): temp disable validation of collection field in node (#928)

* fix(plugin-workflow): fix schedule infinitely trigger when repeat not set (#926)

* Feat/plugin workflow collection field (#934)

* feat(plugin-workflow): support association constant simple input

* fix(plugin-workflow): remove useless code

* fix(plugin-workflow): add req context to processor (#936)

* feat: bulk update done

* feat: bulk edit done

* fix: fix import bug

* Update database.ts

* fix: workflow

* fix: error

* fix: plugin-import

* fix: handle locale

* fix: handle locale

* fix: allow email is undefined

* fix: action add loading

* fix: fix import bug

* fix: not allow sequence import

* fix: remove field not allow download template

* fix: remove field not allow download template

* fix: checkbox batch edit error

* fix: fix build edit
Co-authored-by: default avatarSemmy <semmywong@126.com>
Co-authored-by: default avatarJunyi <mytharcher@users.noreply.github.com>
Co-authored-by: default avatararzanov <59161748+arzanov@users.noreply.github.com>
Co-authored-by: default avatarChengLei Shao <chareice@live.com>
Co-authored-by: default avatarlyf-coder <58352715+lyf-coder@users.noreply.github.com>
Co-authored-by: default avatarkatherinehhh <shunai.tang@hand-china.com>
Co-authored-by: default avatar唐小爱 <tangxiaoai@192.168.0.103>
parent 31815c54
Showing with 489 additions and 16 deletions
+489 -16
......@@ -4,7 +4,7 @@ networks:
driver: bridge
services:
app:
image: nocobase/nocobase:0.7.4-alpha.7
image: nocobase/nocobase:0.7.5-alpha.1
networks:
- nocobase
depends_on:
......@@ -15,7 +15,7 @@ services:
- DB_DATABASE=nocobase
- DB_USER=nocobase
- DB_PASSWORD=nocobase
- LOCAL_STORAGE_BASE_URL=http://localhost:13000/storage/uploads
- LOCAL_STORAGE_BASE_URL=/storage/uploads
volumes:
- ./storage:/app/nocobase/storage
ports:
......
......@@ -4,7 +4,7 @@ networks:
driver: bridge
services:
app:
image: nocobase/nocobase:0.7.4-alpha.7
image: nocobase/nocobase:0.7.5-alpha.1
networks:
- nocobase
environment:
......@@ -13,7 +13,7 @@ services:
- DB_DATABASE=nocobase
- DB_USER=nocobase
- DB_PASSWORD=nocobase
- LOCAL_STORAGE_BASE_URL=http://localhost:13000/storage/uploads
- LOCAL_STORAGE_BASE_URL=/storage/uploads
volumes:
- ./storage:/app/nocobase/storage
ports:
......
......@@ -4,11 +4,11 @@ networks:
driver: bridge
services:
app:
image: nocobase/nocobase:0.7.4-alpha.7
image: nocobase/nocobase:0.7.5-alpha.1
networks:
- nocobase
environment:
- LOCAL_STORAGE_BASE_URL=http://localhost:13000/storage/uploads
- LOCAL_STORAGE_BASE_URL=/storage/uploads
volumes:
- ./storage:/app/nocobase/storage
ports:
......
export { default } from '@nocobase/plugin-import/client';
\ No newline at end of file
import { useField, useFieldSchema, useForm } from '@formily/react';
import { message, Modal } from 'antd';
import parse from 'json-templates';
import { cloneDeep } from 'lodash';
import get from 'lodash/get';
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
import { useReactToPrint } from 'react-to-print';
import { useFormBlockContext } from '../..';
import { useFormBlockContext, useTableBlockContext } from '../..';
import { useAPIClient } from '../../api-client';
import { useCollection } from '../../collection-manager';
import { useRecord } from '../../record-provider';
import { useActionContext, useCompile } from '../../schema-component';
import { BulkEditFormItemValueType } from '../../schema-initializer/components';
import { useCurrentUserContext } from '../../user';
import { useBlockRequestContext, useFilterByTk } from '../BlockProvider';
import { useDetailsBlockContext } from '../DetailsBlockProvider';
......@@ -231,6 +233,173 @@ export const useCustomizeUpdateActionProps = () => {
};
};
export const useCustomizeBulkUpdateActionProps = () => {
const { field, resource, __parent, service } = useBlockRequestContext();
const actionSchema = useFieldSchema();
const currentRecord = useRecord();
const tableBlockContext = useTableBlockContext();
const { rowKey } = tableBlockContext;
const { selectedRowKeys } = tableBlockContext.field?.data ?? {};
const currentUserContext = useCurrentUserContext();
const currentUser = currentUserContext?.data?.data;
const history = useHistory();
const compile = useCompile();
const { t } = useTranslation();
const actionField = useField();
return {
async onClick() {
const {
assignedValues: originalAssignedValues = {},
onSuccess,
updateMode,
} = actionSchema?.['x-action-settings'] ?? {};
actionField.data = field.data || {};
actionField.data.loading = true;
const assignedValues = parse(originalAssignedValues)({ currentTime: new Date(), currentUser });
Modal.confirm({
title: t('Bulk update'),
content: updateMode === 'selected' ? t('Update selected data?') : t('Update all data?'),
async onOk() {
const { filter } = service.params?.[0] ?? {};
const updateData: { filter?: any; values: any; forceUpdate: boolean } = {
values: { ...assignedValues },
filter,
forceUpdate: false,
};
if (updateMode === 'selected') {
if (!selectedRowKeys?.length) {
message.error(t('Please select the records to be updated'));
actionField.data.loading = false;
return;
}
updateData.filter = { $and: [{ [rowKey || 'id']: { $in: selectedRowKeys } }] };
}
if (!updateData.filter) {
updateData.forceUpdate = true;
}
try {
await resource.update(updateData);
} catch (error) {
} finally {
actionField.data.loading = false;
}
service?.refresh?.();
if (!(resource instanceof TableFieldResource)) {
__parent?.service?.refresh?.();
}
if (!onSuccess?.successMessage) {
return;
}
if (onSuccess?.manualClose) {
Modal.success({
title: compile(onSuccess?.successMessage),
onOk: async () => {
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
if (isURL(onSuccess.redirectTo)) {
window.location.href = onSuccess.redirectTo;
} else {
history.push(onSuccess.redirectTo);
}
}
},
});
} else {
message.success(compile(onSuccess?.successMessage));
}
},
async onCancel() {
actionField.data.loading = false;
},
});
},
};
};
export const useCustomizeBulkEditActionProps = () => {
const form = useForm();
const { t } = useTranslation();
const { field, resource, __parent } = useBlockRequestContext();
const actionContext = useActionContext();
const history = useHistory();
const compile = useCompile();
const actionField = useField();
const tableBlockContext = useTableBlockContext();
const { rowKey } = tableBlockContext;
const { selectedRowKeys } = tableBlockContext.field?.data ?? {};
const { setVisible, fieldSchema: actionSchema } = actionContext;
return {
async onClick() {
const { onSuccess, skipValidator, updateMode } = actionSchema?.['x-action-settings'] ?? {};
const { filter } = __parent.service.params?.[0] ?? {};
if (!skipValidator) {
await form.submit();
}
let values = cloneDeep(form.values);
actionField.data = field.data || {};
actionField.data.loading = true;
for (const key in values) {
if (Object.prototype.hasOwnProperty.call(values, key)) {
const value = values[key];
if (BulkEditFormItemValueType.Clear in value) {
values[key] = null;
} else if (BulkEditFormItemValueType.ChangedTo in value) {
values[key] = value[BulkEditFormItemValueType.ChangedTo];
} else if (BulkEditFormItemValueType.RemainsTheSame in value) {
delete values[key];
}
}
}
try {
const updateData: { filter?: any; values: any; forceUpdate: boolean } = {
values,
filter,
forceUpdate: false,
};
if (updateMode === 'selected') {
if (!selectedRowKeys?.length) {
message.error(t('Please select the records to be updated'));
return;
}
updateData.filter = { $and: [{ [rowKey || 'id']: { $in: selectedRowKeys } }] };
}
if (!updateData.filter) {
updateData.forceUpdate = true;
}
await resource.update(updateData);
actionField.data.loading = false;
if (!(resource instanceof TableFieldResource)) {
__parent?.__parent?.service?.refresh?.();
}
__parent?.service?.refresh?.();
setVisible?.(false);
if (!onSuccess?.successMessage) {
return;
}
if (onSuccess?.manualClose) {
Modal.success({
title: compile(onSuccess?.successMessage),
onOk: async () => {
await form.reset();
if (onSuccess?.redirecting && onSuccess?.redirectTo) {
if (isURL(onSuccess.redirectTo)) {
window.location.href = onSuccess.redirectTo;
} else {
history.push(onSuccess.redirectTo);
}
}
},
});
} else {
message.success(compile(onSuccess?.successMessage));
}
} finally {
actionField.data.loading = false;
}
},
};
};
export const useCustomizeRequestActionProps = () => {
const apiClient = useAPIClient();
const history = useHistory();
......
......@@ -86,8 +86,8 @@ export const o2m: IField = {
},
};
} else {
schema['x-component'] = 'CollectionField';
schema.type = 'array';
// schema['x-component'] = 'CollectionField';
// schema.type = 'array';
if (block === 'Form') {
schema['properties'] = {
......@@ -102,7 +102,7 @@ export const o2m: IField = {
} else {
schema['properties'] = {
selector: cloneDeep(recordPickerSelector),
}
};
}
}
}
......
......@@ -557,4 +557,16 @@ export default {
"Print": "Print",
'Single select and radio fields can be used as the grouping field': 'Single select and radio fields can be used as the grouping field',
'Sign up successfully, and automatically jump to the sign in page': 'Sign up successfully, and automatically jump to the sign in page',
"After successful bulk update": "After successful bulk update",
"All": "All",
"Update selected data?": "Update selected data?",
"Update all data?": "Update all data?",
"Bulk edit": "Bulk edit",
"Data will be updated": "Data will be updated",
"Selected": "Selected",
"Remains the same": "Remains the same",
"Changed to": "Changed to",
"Clear":"Clear",
"Add attach":"Add attach",
"Please select the records to be updated": "Please select the records to be updated"
}
......@@ -234,6 +234,7 @@ export default {
"Edit button": "编辑按钮",
"Hide": "隐藏",
"Enable actions": "启用操作",
"Import": "导入",
"Export": "导出",
"Customize": "自定义",
"Function": "Function",
......@@ -326,6 +327,7 @@ export default {
"Title": "标题",
"Select view": "切换视图",
"Reset": "重置",
"Importable fields": "可导入字段",
"Exportable fields": "可导出字段",
"Saved successfully": "保存成功",
"Nickname": "昵称",
......@@ -691,6 +693,7 @@ export default {
"Enabled languages": "启用的语言",
"View all plugins": "查看所有插件",
"Print": "打印",
"Done": "完成",
'Sign up successfully, and automatically jump to the sign in page': '注册成功,即将跳转到登录页面',
'File manager': '文件管理器',
'ACL': '访问控制',
......@@ -706,4 +709,17 @@ export default {
'Create inverse field in the target collection': '在目标数据表里创建反向关系字段',
'Inverse field name': '反向关系字段标识',
'Inverse field display name': '反向关系字段名称',
"Bulk update": "批量更新",
"After successful bulk update": "批量成功更新后",
"Bulk edit": "批量编辑",
"Data will be updated": "更新的数据",
"Selected": "选中",
"All": "所有",
"Update selected data?": "更新选中的数据吗?",
"Update all data?": "更新全部数据吗?",
"Remains the same": "不更新",
"Changed to": "修改为",
"Clear":"清空",
"Add attach":"增加关联",
"Please select the records to be updated": "请选择要更新的记录"
}
......@@ -40,6 +40,7 @@ export const ActionDesigner = (props) => {
const { t } = useTranslation();
const compile = useCompile();
const isPopupAction = ['create', 'update', 'view', 'customize:popup'].includes(fieldSchema['x-action'] || '');
const isUpdateModePopupAction = ['customize:bulkUpdate', 'customize:bulkEdit'].includes(fieldSchema['x-action']);
const context = useActionContext();
const [initialSchema, setInitialSchema] = useState<ISchema>();
const actionType = fieldSchema['x-action'] || '';
......@@ -152,6 +153,26 @@ export const ActionDesigner = (props) => {
}}
/>
)}
{isUpdateModePopupAction && (
<SchemaSettings.SelectItem
title={t('Data will be updated')}
options={[
{ label: t('Selected'), value: 'selected' },
{ label: t('All'), value: 'all' },
]}
value={fieldSchema?.['x-action-settings']?.['updateMode']}
onChange={(value) => {
fieldSchema['x-action-settings']['updateMode'] = value;
dn.emit('patch', {
schema: {
'x-uid': fieldSchema['x-uid'],
'x-action-settings': fieldSchema['x-action-settings'],
},
});
dn.refresh();
}}
/>
)}
{isValid(fieldSchema?.['x-action-settings']?.assignedValues) && (
<SchemaSettings.ActionModalItem
......@@ -247,6 +268,7 @@ export const ActionDesigner = (props) => {
'customize:update': t('After successful update'),
'customize:table:request': t('After successful request'),
'customize:form:request': t('After successful request'),
'customize:bulkUpdate': t('After successful bulk update'),
}[actionType]
}
initialValues={fieldSchema?.['x-action-settings']?.['onSuccess']}
......@@ -258,6 +280,7 @@ export const ActionDesigner = (props) => {
'customize:update': t('After successful update'),
'customize:table:request': t('After successful request'),
'customize:form:request': t('After successful request'),
'customize:bulkUpdate': t('After successful bulk update'),
}[actionType],
properties: {
successMessage: {
......
......@@ -74,7 +74,6 @@ export const Action: ComposedAction = observer((props: any) => {
component,
useAction = useA,
className,
disabled,
icon,
title,
...others
......@@ -130,6 +129,7 @@ export const Action: ComposedAction = observer((props: any) => {
setFormValueChanged,
openMode,
containerRefKey,
fieldSchema,
}}
>
{popover && <RecursionField basePath={field.address} onlyRenderProperties schema={fieldSchema} />}
......
import { Schema } from '@formily/react';
import { createContext } from 'react';
export const ActionContext = createContext<ActionContextProps>({});
......@@ -10,4 +11,5 @@ export interface ActionContextProps {
containerRefKey?: string;
formValueChanged?: boolean;
setFormValueChanged?: (v: boolean) => void;
fieldSchema?: Schema;
}
......@@ -12,7 +12,12 @@ type ComposedCheckbox = React.FC<CheckboxProps> & {
};
export const Checkbox: ComposedCheckbox = connect(
AntdCheckbox,
(props: any) => {
const changeHandler = (val) => {
props?.onChange(val);
};
return <AntdCheckbox {...props} onChange={changeHandler} />;
},
mapProps(
{
value: 'checked',
......
......@@ -37,6 +37,7 @@ export const FilterActionDesigner = (props) => {
const checked = !nonfilterable.includes(field.name);
return (
<SchemaSettings.SwitchItem
key={field.name}
checked={checked}
title={compile(field?.uiSchema?.title)}
onChange={(value) => {
......
import { observer, useField, useFieldSchema } from '@formily/react';
import { Button, Input as AntdInput, Space } from 'antd';
import cls from 'classnames';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useDesignable } from '../../hooks/useDesignable';
......@@ -41,7 +42,7 @@ const MarkdownEditor = (props: any) => {
};
export const MarkdownVoid: any = observer((props: any) => {
const { content } = props;
const { content, className } = props;
const field = useField();
const schema = useFieldSchema();
const { dn } = useDesignable();
......@@ -49,6 +50,7 @@ export const MarkdownVoid: any = observer((props: any) => {
return field?.editable ? (
<MarkdownEditor
{...props}
className
defaultValue={content}
onCancel={() => {
field.editable = false;
......@@ -71,7 +73,7 @@ export const MarkdownVoid: any = observer((props: any) => {
}}
/>
) : (
<div className={'nb-markdown'} dangerouslySetInnerHTML={{ __html: markdown(content) }} />
<div className={cls(['nb-markdown', className])} dangerouslySetInnerHTML={{ __html: markdown(content) }} />
);
});
......
......@@ -195,9 +195,13 @@ Upload.Attachment = connect((props: UploadProps) => {
Upload.Dragger = connect(
(props: DraggerProps) => {
const { tipContent } = props;
return (
<div className={usePrefixCls('upload-dragger')}>
<AntdUpload.Dragger {...useUploadProps(props)} />
<AntdUpload.Dragger {...useUploadProps(props)}>
{tipContent}
{props.children}
</AntdUpload.Dragger>
</div>
);
},
......
......@@ -12,6 +12,8 @@ export type UploadProps = Omit<AntdUploadProps, 'onChange'> & {
export type DraggerProps = Omit<AntdDraggerProps, 'onChange'> & {
onChange?: (fileList: UploadFile[]) => void;
serviceErrorMessage?: string;
tipContent?: string | React.ReactNode;
children?: React.ReactNode;
};
export type ComposedUpload = React.FC<UploadProps> & {
......
......@@ -27,7 +27,6 @@ export const Sortable = (props: any) => {
if (isOver) {
droppableStyle['color'] = 'rgba(241, 139, 98, .1)';
}
return React.createElement(
component || 'div',
{
......
import { union } from 'lodash';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaInitializer } from '../SchemaInitializer';
import {
gridRowColWrap,
useAssociatedFormItemInitializerFields,
useCustomBulkEditFormItemInitializerFields,
} from '../utils';
export const BulkEditFormItemInitializers = (props: any) => {
const { t } = useTranslation();
const { insertPosition, component } = props;
const associationFields = useAssociatedFormItemInitializerFields({ readPretty: true, block: 'Form' });
return (
<SchemaInitializer.Button
wrap={gridRowColWrap}
icon={'SettingOutlined'}
items={union<any>(
[
{
type: 'itemGroup',
title: t('Display fields'),
children: useCustomBulkEditFormItemInitializerFields(),
},
],
associationFields.length > 0
? [
{
type: 'divider',
},
{
type: 'itemGroup',
title: t('Display association fields'),
children: associationFields,
},
]
: [],
[
{
type: 'divider',
},
{
type: 'item',
title: t('Add text'),
component: 'BlockInitializer',
schema: {
type: 'void',
'x-editable': false,
'x-decorator': 'FormItem',
'x-designer': 'Markdown.Void.Designer',
'x-component': 'Markdown.Void',
'x-component-props': {
content: t('This is a demo text, **supports Markdown syntax**.'),
},
},
},
],
)}
insertPosition={insertPosition}
component={component}
title={component ? null : t('Configure fields')}
/>
);
};
import React from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaInitializer } from '../..';
import { gridRowColWrap } from '../utils';
export const CreateFormBulkEditBlockInitializers = (props: any) => {
const { t } = useTranslation();
const { insertPosition, component } = props;
return (
<SchemaInitializer.Button
wrap={gridRowColWrap}
title={component ? null : t('Add block')}
icon={'PlusOutlined'}
insertPosition={insertPosition}
component={component}
items={[
{
type: 'itemGroup',
title: '{{t("Data blocks")}}',
children: [
{
type: 'item',
title: '{{t("Form")}}',
component: 'CreateFormBulkEditBlockInitializer',
},
],
},
{
type: 'itemGroup',
title: '{{t("Other blocks")}}',
children: [
{
type: 'item',
title: '{{t("Markdown")}}',
component: 'MarkdownBlockInitializer',
},
],
},
]}
/>
);
};
......@@ -388,3 +388,133 @@ export const UpdateFormActionInitializers = {
},
],
};
export const BulkEditFormActionInitializers = {
title: '{{t("Configure actions")}}',
icon: 'SettingOutlined',
items: [
{
type: 'itemGroup',
title: '{{t("Enable actions")}}',
children: [
{
type: 'item',
title: '{{t("Submit")}}',
component: 'BulkEditSubmitActionInitializer',
schema: {
'x-action-settings': {},
},
},
],
},
{
type: 'divider',
},
{
type: 'subMenu',
title: '{{t("Customize")}}',
children: [
{
type: 'item',
title: '{{t("Popup")}}',
component: 'CustomizeActionInitializer',
schema: {
type: 'void',
title: '{{ t("Popup") }}',
'x-action': 'customize:popup',
'x-designer': 'Action.Designer',
'x-component': 'Action',
'x-component-props': {
openMode: 'drawer',
},
properties: {
drawer: {
type: 'void',
title: '{{ t("Popup") }}',
'x-component': 'Action.Container',
'x-component-props': {
className: 'nb-action-popup',
},
properties: {
tabs: {
type: 'void',
'x-component': 'Tabs',
'x-component-props': {},
'x-initializer': 'TabPaneInitializers',
properties: {
tab1: {
type: 'void',
title: '{{t("Details")}}',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
properties: {
grid: {
type: 'void',
'x-component': 'Grid',
'x-initializer': 'RecordBlockInitializers',
properties: {},
},
},
},
},
},
},
},
},
},
},
{
type: 'item',
title: '{{t("Save record")}}',
component: 'CustomizeActionInitializer',
schema: {
title: '{{ t("Save") }}',
'x-component': 'Action',
'x-action': 'customize:save',
'x-designer': 'Action.Designer',
'x-designer-props': {
modalTip:
'{{ t("When the button is clicked, the following fields will be assigned and saved together with the fields in the form. If there are overlapping fields, the value here will overwrite the value in the form.") }}',
},
'x-action-settings': {
assignedValues: {},
skipValidator: false,
onSuccess: {
manualClose: true,
redirecting: false,
successMessage: '{{t("Saved successfully")}}',
},
},
'x-component-props': {
useProps: '{{ useUpdateActionProps }}',
},
},
},
{
type: 'item',
title: '{{t("Custom request")}}',
component: 'CustomizeActionInitializer',
schema: {
title: '{{ t("Custom request") }}',
'x-component': 'Action',
'x-action': 'customize:form:request',
'x-designer': 'Action.Designer',
'x-action-settings': {
requestSettings: {},
skipValidator: false,
onSuccess: {
manualClose: false,
redirecting: false,
successMessage: '{{t("Request success")}}',
},
},
'x-component-props': {
useProps: '{{ useCustomizeRequestActionProps }}',
},
},
},
],
},
],
};
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment