refactor: bulk edit action set changeTo as default value (#3455)

This commit is contained in:
katherinehhh 2024-01-29 16:56:05 +08:00 committed by GitHub
parent 535d14b0bb
commit dad6072abb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 297 additions and 281 deletions

View File

@ -15,7 +15,6 @@ import { useFilterBlock } from '../../filter-provider/FilterProvider';
import { transformToFilter } from '../../filter-provider/utils'; import { transformToFilter } from '../../filter-provider/utils';
import { useRecord } from '../../record-provider'; import { useRecord } from '../../record-provider';
import { removeNullCondition, useActionContext, useCompile } from '../../schema-component'; import { removeNullCondition, useActionContext, useCompile } from '../../schema-component';
import { BulkEditFormItemValueType } from '../../schema-initializer/components';
import { useCurrentUserContext } from '../../user'; import { useCurrentUserContext } from '../../user';
import { useLocalVariables, useVariables } from '../../variables'; import { useLocalVariables, useVariables } from '../../variables';
import { isVariable } from '../../variables/utils/isVariable'; import { isVariable } from '../../variables/utils/isVariable';
@ -680,94 +679,6 @@ export const useCustomizeBulkUpdateActionProps = () => {
}; };
}; };
export const useCustomizeBulkEditActionProps = () => {
const form = useForm();
const { t } = useTranslation();
const { field, resource, __parent } = useBlockRequestContext();
const expressionScope = useContext(SchemaExpressionScopeContext);
const actionContext = useActionContext();
const navigate = useNavigate();
const compile = useCompile();
const actionField = useField();
const tableBlockContext = useTableBlockContext();
const { modal } = App.useApp();
const { rowKey } = tableBlockContext;
const selectedRecordKeys =
tableBlockContext.field?.data?.selectedRowKeys ?? expressionScope?.selectedRecordKeys ?? {};
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();
}
const 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 (!selectedRecordKeys?.length) {
message.error(t('Please select the records to be updated'));
return;
}
updateData.filter = { $and: [{ [rowKey || 'id']: { $in: selectedRecordKeys } }] };
}
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 {
navigate(onSuccess.redirectTo);
}
}
},
});
} else {
message.success(compile(onSuccess?.successMessage));
}
} finally {
actionField.data.loading = false;
}
},
};
};
export const useCustomizeRequestActionProps = () => { export const useCustomizeRequestActionProps = () => {
const apiClient = useAPIClient(); const apiClient = useAPIClient();
const navigate = useNavigate(); const navigate = useNavigate();

View File

@ -282,119 +282,3 @@ export const updateFormActionInitializers = new SchemaInitializer({
}, },
], ],
}); });
export const bulkEditFormActionInitializers = new SchemaInitializer({
name: 'BulkEditFormActionInitializers',
title: '{{t("Configure actions")}}',
icon: 'SettingOutlined',
items: [
{
type: 'itemGroup',
title: '{{t("Enable actions")}}',
name: 'enableActions',
children: [
{
name: 'submit',
title: '{{t("Submit")}}',
Component: 'BulkEditSubmitActionInitializer',
schema: {
'x-action-settings': {},
},
},
],
},
{
name: 'divider',
type: 'divider',
},
{
type: 'subMenu',
title: '{{t("Customize")}}',
name: 'customize',
children: [
{
name: 'popup',
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: {},
},
},
},
},
},
},
},
},
},
},
{
name: 'saveRecord',
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("Submitted successfully")}}',
},
},
'x-component-props': {
useProps: '{{ useUpdateActionProps }}',
},
},
},
{
name: 'customRequest',
title: '{{t("Custom request")}}',
Component: 'CustomRequestInitializer',
},
],
},
],
});

View File

@ -1,3 +1,2 @@
export * from './assigned-field'; export * from './assigned-field';
export * from './BulkEditField';
export * from './CreateRecordAction'; export * from './CreateRecordAction';

View File

@ -1,7 +1,6 @@
import { Plugin } from '../application/Plugin'; import { Plugin } from '../application/Plugin';
import { import {
blockInitializers, blockInitializers,
bulkEditFormActionInitializers,
createFormActionInitializers, createFormActionInitializers,
createFormBlockInitializers, createFormBlockInitializers,
cusomeizeCreateFormBlockInitializers, cusomeizeCreateFormBlockInitializers,
@ -74,7 +73,6 @@ export class SchemaInitializerPlugin extends Plugin {
this.app.schemaInitializerManager.add(filterFormActionInitializers); this.app.schemaInitializerManager.add(filterFormActionInitializers);
this.app.schemaInitializerManager.add(createFormActionInitializers); this.app.schemaInitializerManager.add(createFormActionInitializers);
this.app.schemaInitializerManager.add(updateFormActionInitializers); this.app.schemaInitializerManager.add(updateFormActionInitializers);
this.app.schemaInitializerManager.add(bulkEditFormActionInitializers);
this.app.schemaInitializerManager.add(filterFormItemInitializers); this.app.schemaInitializerManager.add(filterFormItemInitializers);
this.app.schemaInitializerManager.add(gridCardActionInitializers); this.app.schemaInitializerManager.add(gridCardActionInitializers);
this.app.schemaInitializerManager.add(gridCardItemActionInitializers); this.app.schemaInitializerManager.add(gridCardItemActionInitializers);

View File

@ -6,7 +6,6 @@ export * from '../../schema-component/antd/association-filter/AssociationFilterD
export * from './ActionInitializer'; export * from './ActionInitializer';
export * from './BlockInitializer'; export * from './BlockInitializer';
export * from './BulkDestroyActionInitializer'; export * from './BulkDestroyActionInitializer';
export * from './BulkEditSubmitActionInitializer';
export * from './CollectionFieldInitializer'; export * from './CollectionFieldInitializer';
export * from './CreateActionInitializer'; export * from './CreateActionInitializer';
export * from './CreateChildInitializer'; export * from './CreateChildInitializer';

View File

@ -0,0 +1,116 @@
import { SchemaInitializer } from '@nocobase/client';
export const BulkEditFormActionInitializers = new SchemaInitializer({
name: 'BulkEditFormActionInitializers',
title: '{{t("Configure actions")}}',
icon: 'SettingOutlined',
items: [
{
type: 'itemGroup',
title: '{{t("Enable actions")}}',
name: 'enableActions',
children: [
{
name: 'submit',
title: '{{t("Submit")}}',
Component: 'BulkEditSubmitActionInitializer',
schema: {
'x-action-settings': {},
},
},
],
},
{
name: 'divider',
type: 'divider',
},
{
type: 'subMenu',
title: '{{t("Customize")}}',
name: 'customize',
children: [
{
name: 'popup',
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: {},
},
},
},
},
},
},
},
},
},
},
{
name: 'saveRecord',
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("Submitted successfully")}}',
},
},
'x-component-props': {
useProps: '{{ useUpdateActionProps }}',
},
},
},
{
name: 'customRequest',
title: '{{t("Custom request")}}',
Component: 'CustomRequestInitializer',
},
],
},
],
});

View File

@ -2,14 +2,19 @@ import { SchemaComponentOptions } from '@nocobase/client';
import React from 'react'; import React from 'react';
import { CustomizeBulkEditActionInitializer } from './CustomizeBulkEditActionInitializer'; import { CustomizeBulkEditActionInitializer } from './CustomizeBulkEditActionInitializer';
import { CreateFormBulkEditBlockInitializer } from './CreateFormBulkEditBlockInitializer'; import { CreateFormBulkEditBlockInitializer } from './CreateFormBulkEditBlockInitializer';
import { BulkEditSubmitActionInitializer } from './BulkEditSubmitActionInitializer';
import { useCustomizeBulkEditActionProps } from './utils';
import { BulkEditField } from './component/BulkEditField';
export const BulkEditPluginProvider = (props: any) => { export const BulkEditPluginProvider = (props: any) => {
return ( return (
<SchemaComponentOptions <SchemaComponentOptions
components={{ components={{
CustomizeBulkEditActionInitializer, CustomizeBulkEditActionInitializer,
CreateFormBulkEditBlockInitializer, CreateFormBulkEditBlockInitializer,
BulkEditSubmitActionInitializer,
BulkEditField,
}} }}
scope={{ useCustomizeBulkEditActionProps }}
> >
{props.children} {props.children}
</SchemaComponentOptions> </SchemaComponentOptions>

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { ActionInitializer } from './ActionInitializer'; import { ActionInitializer } from '@nocobase/client';
export const BulkEditSubmitActionInitializer = (props) => { export const BulkEditSubmitActionInitializer = (props) => {
const schema = { const schema = {

View File

@ -5,11 +5,19 @@ import { merge, uid } from '@formily/shared';
import { Checkbox, Select, Space } from 'antd'; import { Checkbox, Select, Space } from 'antd';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useFormBlockContext } from '../../block-provider'; import {
import { CollectionFieldProvider, useCollection, useCollectionField } from '../../collection-manager'; useFormBlockContext,
import { useCompile, useComponent } from '../../schema-component'; CollectionFieldProvider,
import { DeletedField } from './DeletedField'; useCollection,
useCollectionField,
useCompile,
useComponent,
} from '@nocobase/client';
export const DeletedField = () => {
const { t } = useTranslation();
return <div style={{ color: '#ccc' }}>{t('The field has bee deleted')}</div>;
};
const InternalField: React.FC = (props) => { const InternalField: React.FC = (props) => {
const field = useField<Field>(); const field = useField<Field>();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
@ -83,7 +91,7 @@ export const BulkEditField = (props: any) => {
const { t } = useTranslation(); const { t } = useTranslation();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const field = useField<Field>(); const field = useField<Field>();
const [type, setType] = useState<number>(BulkEditFormItemValueType.RemainsTheSame); const [type, setType] = useState<number>(BulkEditFormItemValueType.ChangedTo);
const [value, setValue] = useState(null); const [value, setValue] = useState(null);
const { getField } = useCollection(); const { getField } = useCollection();
const collectionField = getField(fieldSchema.name) || {}; const collectionField = getField(fieldSchema.name) || {};
@ -135,15 +143,9 @@ export const BulkEditField = (props: any) => {
<Select.Option value={BulkEditFormItemValueType.AddAttach}>{t('Add attach')}</Select.Option> <Select.Option value={BulkEditFormItemValueType.AddAttach}>{t('Add attach')}</Select.Option>
)} )}
</Select> </Select>
{/* XXX: Not a best practice */}
{[BulkEditFormItemValueType.ChangedTo, BulkEditFormItemValueType.AddAttach].includes(type) && {[BulkEditFormItemValueType.ChangedTo, BulkEditFormItemValueType.AddAttach].includes(type) &&
collectionField?.interface !== 'checkbox' && ( collectionField?.interface !== 'checkbox' && (
<CollectionField {...props} value={value} onChange={valueChangeHandler} style={{ minWidth: 150 }} /> <CollectionField {...props} value={value} onChange={valueChangeHandler} style={{ minWidth: 150 }} />
// <SchemaComponent
// schema={collectionSchema}
// components={{ BulkEditCollectionField: CollectionField }}
// onlyRenderProperties
// />
)} )}
{[BulkEditFormItemValueType.ChangedTo, BulkEditFormItemValueType.AddAttach].includes(type) && {[BulkEditFormItemValueType.ChangedTo, BulkEditFormItemValueType.AddAttach].includes(type) &&
collectionField?.interface === 'checkbox' && <Checkbox checked={value} onChange={valueChangeHandler} />} collectionField?.interface === 'checkbox' && <Checkbox checked={value} onChange={valueChangeHandler} />}

View File

@ -2,6 +2,7 @@ import { Plugin, useCollection } from '@nocobase/client';
import { BulkEditPluginProvider } from './BulkEditPluginProvider'; import { BulkEditPluginProvider } from './BulkEditPluginProvider';
import { BulkEditFormItemInitializers } from './BulkEditFormItemInitializers'; import { BulkEditFormItemInitializers } from './BulkEditFormItemInitializers';
import { CreateFormBulkEditBlockInitializers } from './CreateFormBulkEditBlockInitializers'; import { CreateFormBulkEditBlockInitializers } from './CreateFormBulkEditBlockInitializers';
import { BulkEditFormActionInitializers } from './BulkEditFormActionInitializers';
import { bulkEditactionSettings } from './BulkEditAction.Settings'; import { bulkEditactionSettings } from './BulkEditAction.Settings';
export class BulkEditPlugin extends Plugin { export class BulkEditPlugin extends Plugin {
async load() { async load() {
@ -9,6 +10,7 @@ export class BulkEditPlugin extends Plugin {
this.app.schemaSettingsManager.add(bulkEditactionSettings); this.app.schemaSettingsManager.add(bulkEditactionSettings);
this.app.schemaInitializerManager.add(BulkEditFormItemInitializers); this.app.schemaInitializerManager.add(BulkEditFormItemInitializers);
this.app.schemaInitializerManager.add(CreateFormBulkEditBlockInitializers); this.app.schemaInitializerManager.add(CreateFormBulkEditBlockInitializers);
this.app.schemaInitializerManager.add(BulkEditFormActionInitializers);
const initializerData = { const initializerData = {
type: 'item', type: 'item',

View File

@ -1,59 +0,0 @@
import {
useCollection,
useCollectionManager,
useRemoveGridFormItem,
SchemaInitializerItemType,
} from '@nocobase/client';
import { useForm } from '@formily/react';
import { useMemo } from 'react';
export const useCustomBulkEditFormItemInitializerFields = (options?: any) => {
const { name, fields } = useCollection();
const { getInterface, getCollection } = useCollectionManager();
const form = useForm();
const { readPretty = form.readPretty, block = 'Form' } = options || {};
const remove = useRemoveGridFormItem();
const filterFields = useMemo(
() =>
fields
?.filter((field) => {
return (
field?.interface &&
!field?.uiSchema?.['x-read-pretty'] &&
field.interface !== 'snapshot' &&
field.type !== 'sequence'
);
})
.map((field) => {
const interfaceConfig = getInterface(field.interface);
const schema = {
type: 'string',
name: field.name,
title: field?.uiSchema?.title || field.name,
'x-designer': 'FormItem.Designer',
'x-component': 'BulkEditField',
'x-decorator': 'FormItem',
'x-collection-field': `${name}.${field.name}`,
};
return {
name: field?.uiSchema?.title || field.name,
type: 'item',
title: field?.uiSchema?.title || field.name,
Component: 'CollectionFieldInitializer',
remove: remove,
schemaInitialize: (s) => {
interfaceConfig?.schemaInitialize?.(s, {
field,
block,
readPretty,
targetCollection: getCollection(field.target),
});
},
schema,
} as SchemaInitializerItemType;
}),
[fields],
);
return filterFields;
};

View File

@ -0,0 +1,159 @@
import {
useCollection,
useCollectionManager,
useRemoveGridFormItem,
SchemaInitializerItemType,
useBlockRequestContext,
useActionContext,
useCompile,
useTableBlockContext,
TableFieldResource,
} from '@nocobase/client';
import React, { useContext } from 'react';
import { App, message } from 'antd';
import { useNavigate } from 'react-router-dom';
import { SchemaExpressionScopeContext, useField, useForm } from '@formily/react';
import { cloneDeep } from 'lodash';
import { isURL } from '@nocobase/utils/client';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { BulkEditFormItemValueType } from './component/BulkEditField';
export const useCustomBulkEditFormItemInitializerFields = (options?: any) => {
const { name, fields } = useCollection();
const { getInterface, getCollection } = useCollectionManager();
const form = useForm();
const { readPretty = form.readPretty, block = 'Form' } = options || {};
const remove = useRemoveGridFormItem();
const filterFields = useMemo(
() =>
fields
?.filter((field) => {
return (
field?.interface &&
!field?.uiSchema?.['x-read-pretty'] &&
field.interface !== 'snapshot' &&
field.type !== 'sequence'
);
})
.map((field) => {
const interfaceConfig = getInterface(field.interface);
const schema = {
type: 'string',
name: field.name,
title: field?.uiSchema?.title || field.name,
'x-designer': 'FormItem.Designer',
'x-component': 'BulkEditField',
'x-decorator': 'FormItem',
'x-collection-field': `${name}.${field.name}`,
};
return {
name: field?.uiSchema?.title || field.name,
type: 'item',
title: field?.uiSchema?.title || field.name,
Component: 'CollectionFieldInitializer',
remove: remove,
schemaInitialize: (s) => {
interfaceConfig?.schemaInitialize?.(s, {
field,
block,
readPretty,
targetCollection: getCollection(field.target),
});
},
schema,
} as SchemaInitializerItemType;
}),
[fields],
);
return filterFields;
};
export const useCustomizeBulkEditActionProps = () => {
const form = useForm();
const { t } = useTranslation();
const { field, resource, __parent } = useBlockRequestContext();
const expressionScope = useContext(SchemaExpressionScopeContext);
const actionContext = useActionContext();
const navigate = useNavigate();
const compile = useCompile();
const actionField = useField();
const tableBlockContext = useTableBlockContext();
const { modal } = App.useApp();
const { rowKey } = tableBlockContext;
const selectedRecordKeys =
tableBlockContext.field?.data?.selectedRowKeys ?? expressionScope?.selectedRecordKeys ?? {};
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();
}
const 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 (!selectedRecordKeys?.length) {
message.error(t('Please select the records to be updated'));
return;
}
updateData.filter = { $and: [{ [rowKey || 'id']: { $in: selectedRecordKeys } }] };
}
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 {
navigate(onSuccess.redirectTo);
}
}
},
});
} else {
message.success(compile(onSuccess?.successMessage));
}
} finally {
actionField.data.loading = false;
}
},
};
};