mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 05:29:26 +08:00
feat: add convert template block to page block action (#6662)
* feat: add convert template block to page block action * feat: convert template block to normal block * fix: remove remaining template properties * fix: list template not loading data * fix: user menu render error * fix: save as template and convert to block should hide in workflow config page * fix: incorrect translation
This commit is contained in:
parent
5eb337bd7a
commit
d31aa4a91c
@ -0,0 +1,182 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { SchemaSettingsItem, useAPIClient, useDesignable, useFormBlockProps } from '@nocobase/client';
|
||||
import { useFieldSchema, useForm, useField } from '@formily/react';
|
||||
import { App } from 'antd';
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import { Schema } from '@formily/json-schema';
|
||||
import { useT } from '../locale';
|
||||
import { uid } from '@nocobase/utils/client';
|
||||
|
||||
const findInsertPosition = (parentSchema, uid) => {
|
||||
const postion = {
|
||||
insertPosition: 'beforeBegin',
|
||||
insertTarget: null,
|
||||
};
|
||||
const properties = Object.values(parentSchema.properties || {}).sort((a, b) => {
|
||||
return (a as any)['x-index'] - (b as any)['x-index'];
|
||||
});
|
||||
for (let i = 0; i < properties.length; i++) {
|
||||
const property = properties[i];
|
||||
if ((property as any)['x-uid'] === uid) {
|
||||
postion.insertPosition = 'beforeBegin';
|
||||
if (i === properties.length - 1) {
|
||||
postion.insertPosition = 'beforeEnd';
|
||||
postion.insertTarget = parentSchema['x-uid'];
|
||||
} else {
|
||||
postion.insertPosition = 'beforeBegin';
|
||||
postion.insertTarget = (properties[i + 1] as any)['x-uid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return postion;
|
||||
};
|
||||
|
||||
const findParentRootTemplateSchema = (fieldSchema) => {
|
||||
if (!fieldSchema) {
|
||||
return null;
|
||||
}
|
||||
if (fieldSchema['x-template-root-uid']) {
|
||||
return fieldSchema;
|
||||
} else {
|
||||
return findParentRootTemplateSchema(fieldSchema.parent);
|
||||
}
|
||||
};
|
||||
|
||||
// Create a copy of the schema with all template associations removed
|
||||
const convertToNormalBlockSchema = (schema) => {
|
||||
const newSchema = _.cloneDeep(schema);
|
||||
|
||||
// Remove template associations from the schema
|
||||
const removeTemplateAssociations = (s) => {
|
||||
// Remove template-specific properties
|
||||
delete s['x-template-uid'];
|
||||
delete s['x-template-root-uid'];
|
||||
delete s['x-template-version'];
|
||||
delete s['x-block-template-key'];
|
||||
delete s['x-template-root-ref'];
|
||||
delete s['x-template-title'];
|
||||
delete s['x-virtual'];
|
||||
|
||||
if (s['x-toolbar-props']?.toolbarClassName?.includes('nb-in-template')) {
|
||||
s['x-toolbar-props'].toolbarClassName = s['x-toolbar-props'].toolbarClassName.replace('nb-in-template', '');
|
||||
}
|
||||
|
||||
if (s['x-uid']) {
|
||||
s['x-uid'] = uid();
|
||||
}
|
||||
// Process nested properties
|
||||
if (s.properties) {
|
||||
for (const key in s.properties) {
|
||||
if (!s.properties[key]['x-template-root-uid']) {
|
||||
removeTemplateAssociations(s.properties[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
removeTemplateAssociations(newSchema);
|
||||
return newSchema;
|
||||
};
|
||||
|
||||
export const ConvertToNormalBlockSetting = () => {
|
||||
const { refresh } = useDesignable();
|
||||
const t = useT();
|
||||
const api = useAPIClient();
|
||||
const form = useForm();
|
||||
const field = useField();
|
||||
const { form: blockForm } = useFormBlockProps();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { modal, message } = App.useApp();
|
||||
const blockTemplatesResource = api.resource('blockTemplates');
|
||||
|
||||
const confirm = {
|
||||
okText: t('Yes'),
|
||||
cancelText: t('No'),
|
||||
};
|
||||
|
||||
return (
|
||||
<SchemaSettingsItem
|
||||
title={t('Convert to normal block')}
|
||||
onClick={() => {
|
||||
modal.confirm({
|
||||
title: t('Convert to normal block'),
|
||||
content: t('Are you sure you want to convert this template block to a normal block?'),
|
||||
...confirm,
|
||||
async onOk() {
|
||||
const newSchema = convertToNormalBlockSchema(fieldSchema.toJSON());
|
||||
const position = findInsertPosition(fieldSchema.parent, fieldSchema['x-uid']);
|
||||
// TODO: Remove old schema, and links
|
||||
|
||||
// Remove old schema
|
||||
await api.request({
|
||||
url: `/uiSchemas:remove/${fieldSchema['x-uid']}`,
|
||||
});
|
||||
|
||||
// Insert new schema
|
||||
const schema = new Schema(newSchema);
|
||||
await api.request({
|
||||
url: `/uiSchemas:insertAdjacent/${position.insertTarget}?position=${position.insertPosition}`,
|
||||
method: 'post',
|
||||
data: {
|
||||
schema,
|
||||
},
|
||||
});
|
||||
|
||||
// Update the UI to show the new schema
|
||||
fieldSchema.toJSON = () => {
|
||||
const ret = schema.toJSON();
|
||||
return ret;
|
||||
};
|
||||
|
||||
refresh({ refreshParentSchema: true });
|
||||
|
||||
// Update component properties
|
||||
field['componentProps'] = {
|
||||
...field['componentProps'],
|
||||
key: uid(),
|
||||
};
|
||||
|
||||
if (field.parent?.['componentProps']) {
|
||||
field.parent['componentProps'] = {
|
||||
...field.parent['componentProps'],
|
||||
key: uid(),
|
||||
};
|
||||
}
|
||||
|
||||
// Update decorator properties
|
||||
field['decoratorProps'] = {
|
||||
...field['decoratorProps'],
|
||||
key: uid(),
|
||||
};
|
||||
|
||||
if (field.parent?.['decoratorProps']) {
|
||||
field.parent['decoratorProps'] = {
|
||||
...field.parent['decoratorProps'],
|
||||
key: uid(),
|
||||
};
|
||||
}
|
||||
|
||||
// Reset forms
|
||||
form.reset();
|
||||
blockForm?.reset();
|
||||
form.clearFormGraph('*', false);
|
||||
blockForm?.clearFormGraph('*', false);
|
||||
|
||||
message.success(t('Converted successfully'), 0.2);
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t('Convert to normal block')}
|
||||
</SchemaSettingsItem>
|
||||
);
|
||||
};
|
@ -74,7 +74,7 @@ export const SaveAsTemplateSetting = () => {
|
||||
key: {
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
title: t('Key'),
|
||||
title: t('Name'),
|
||||
'x-component': 'Input',
|
||||
'x-validator': 'uid',
|
||||
required: true,
|
||||
@ -298,6 +298,10 @@ function getTemplateSchemaFromPage(schema: ISchema) {
|
||||
}
|
||||
_.set(t, `properties.['${key}']`, {});
|
||||
traverseSchema(s.properties[key], t.properties[key]);
|
||||
// array's key will be set to number when render, so we need to set the name to the key
|
||||
if (s.type === 'array' && t['properties']?.[key]?.name) {
|
||||
_.set(t, `properties.['${key}'].name`, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -22,8 +22,9 @@ export const useIsPageBlock = () => {
|
||||
const isPage = location.pathname.startsWith('/admin/') || location.pathname.startsWith('/page/');
|
||||
const notInPopup = !location.pathname.includes('/popups/');
|
||||
const notInSetting = !location.pathname.startsWith('/admin/settings/');
|
||||
const notInWorkflow = !location.pathname.startsWith('/admin/workflow/workflows/');
|
||||
const notInBlockTemplate = !location.pathname.startsWith('/block-templates/');
|
||||
return isPage && notInPopup && notInSetting && notInBlockTemplate;
|
||||
return isPage && notInPopup && notInSetting && notInWorkflow && notInBlockTemplate;
|
||||
}, [location.pathname, fieldSchema]);
|
||||
|
||||
return isPageBlock;
|
||||
|
@ -28,6 +28,8 @@ import {
|
||||
import { BlockTemplateMenusProvider } from './components/BlockTemplateMenusProvider';
|
||||
import { disabledDeleteSettingItem } from './settings/disabledDeleteSetting';
|
||||
import { saveAsTemplateSetting } from './settings/saveAsTemplateSetting';
|
||||
import { convertToNormalBlockSettingItem } from './settings/convertToNormalBlockSetting';
|
||||
|
||||
export class PluginBlockTemplateClient extends Plugin {
|
||||
templateInfos = new Map();
|
||||
templateschemacache = {};
|
||||
@ -158,9 +160,10 @@ export class PluginBlockTemplateClient extends Plugin {
|
||||
deleteItemIndex !== -1 &&
|
||||
!schemaSetting.items.find((item) => item.name === 'template-revertSettingItem')
|
||||
) {
|
||||
schemaSetting.items.splice(deleteItemIndex, 0, revertSettingItem);
|
||||
schemaSetting.items.splice(deleteItemIndex, 0, revertSettingItem, convertToNormalBlockSettingItem);
|
||||
} else {
|
||||
schemaSetting.add('template-revertSettingItem', revertSettingItem);
|
||||
schemaSetting.add('template-convertToNormalBlockSettingItem', convertToNormalBlockSettingItem);
|
||||
}
|
||||
schemaSetting.add('template-disabledDeleteItem', disabledDeleteSettingItem);
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { useFieldSchema } from '@formily/react';
|
||||
import { ConvertToNormalBlockSetting } from '../components/ConvertToNormalBlockSetting';
|
||||
import { tStr } from '../locale';
|
||||
import { useIsInTemplate } from '../hooks/useIsInTemplate';
|
||||
|
||||
export const convertToNormalBlockSettingItem = {
|
||||
name: 'template-convertToNormalBlockSettingItem',
|
||||
title: tStr('Convert to normal block'),
|
||||
Component: ConvertToNormalBlockSetting,
|
||||
useVisible: () => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
return fieldSchema?.['x-template-root-uid'];
|
||||
},
|
||||
};
|
@ -12,7 +12,9 @@
|
||||
"Mobile": "Mobile",
|
||||
"Current": "Current record",
|
||||
"Revert to template": "Revert to template",
|
||||
"Convert to normal block": "Convert to normal block",
|
||||
"Are you sure you want to revert all changes from the template?": "Are you sure you want to revert all changes from the template?",
|
||||
"Are you sure you want to convert this template block to a normal block?": "Are you sure you want to convert this template block to a normal block?",
|
||||
"Templates": "Templates",
|
||||
"Block templates": "Block templates",
|
||||
"Submit": "Submit",
|
||||
@ -22,6 +24,7 @@
|
||||
"Saved successfully": "Saved successfully",
|
||||
"Block template": "Block template",
|
||||
"Reset successfully": "Reset successfully",
|
||||
"Converted successfully": "Converted successfully",
|
||||
"Delete successfully": "Delete successfully",
|
||||
"Template block settings": "Template block settings",
|
||||
"Filter": "Filter",
|
||||
|
@ -12,7 +12,9 @@
|
||||
"Mobile": "移动端",
|
||||
"Current": "当前记录",
|
||||
"Revert to template": "恢复到模板",
|
||||
"Convert to normal block": "转换成普通区块",
|
||||
"Are you sure you want to revert all changes from the template?": "您确定要恢复所有对模板的更改吗?",
|
||||
"Are you sure you want to convert this template block to a normal block?": "您确定要将此模板区块转换为普通区块吗?",
|
||||
"Templates": "模板",
|
||||
"Block templates": "区块模板",
|
||||
"Submit": "提交",
|
||||
@ -22,6 +24,7 @@
|
||||
"Saved successfully": "保存成功",
|
||||
"Block template": "区块模板",
|
||||
"Reset successfully": "重置成功",
|
||||
"Converted successfully": "转换成功",
|
||||
"Delete successfully": "删除成功",
|
||||
"Template block settings": "模板区块设置",
|
||||
"Filter": "筛选",
|
||||
|
Loading…
x
Reference in New Issue
Block a user