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: {
|
key: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
title: t('Key'),
|
title: t('Name'),
|
||||||
'x-component': 'Input',
|
'x-component': 'Input',
|
||||||
'x-validator': 'uid',
|
'x-validator': 'uid',
|
||||||
required: true,
|
required: true,
|
||||||
@ -298,6 +298,10 @@ function getTemplateSchemaFromPage(schema: ISchema) {
|
|||||||
}
|
}
|
||||||
_.set(t, `properties.['${key}']`, {});
|
_.set(t, `properties.['${key}']`, {});
|
||||||
traverseSchema(s.properties[key], 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 isPage = location.pathname.startsWith('/admin/') || location.pathname.startsWith('/page/');
|
||||||
const notInPopup = !location.pathname.includes('/popups/');
|
const notInPopup = !location.pathname.includes('/popups/');
|
||||||
const notInSetting = !location.pathname.startsWith('/admin/settings/');
|
const notInSetting = !location.pathname.startsWith('/admin/settings/');
|
||||||
|
const notInWorkflow = !location.pathname.startsWith('/admin/workflow/workflows/');
|
||||||
const notInBlockTemplate = !location.pathname.startsWith('/block-templates/');
|
const notInBlockTemplate = !location.pathname.startsWith('/block-templates/');
|
||||||
return isPage && notInPopup && notInSetting && notInBlockTemplate;
|
return isPage && notInPopup && notInSetting && notInWorkflow && notInBlockTemplate;
|
||||||
}, [location.pathname, fieldSchema]);
|
}, [location.pathname, fieldSchema]);
|
||||||
|
|
||||||
return isPageBlock;
|
return isPageBlock;
|
||||||
|
@ -28,6 +28,8 @@ import {
|
|||||||
import { BlockTemplateMenusProvider } from './components/BlockTemplateMenusProvider';
|
import { BlockTemplateMenusProvider } from './components/BlockTemplateMenusProvider';
|
||||||
import { disabledDeleteSettingItem } from './settings/disabledDeleteSetting';
|
import { disabledDeleteSettingItem } from './settings/disabledDeleteSetting';
|
||||||
import { saveAsTemplateSetting } from './settings/saveAsTemplateSetting';
|
import { saveAsTemplateSetting } from './settings/saveAsTemplateSetting';
|
||||||
|
import { convertToNormalBlockSettingItem } from './settings/convertToNormalBlockSetting';
|
||||||
|
|
||||||
export class PluginBlockTemplateClient extends Plugin {
|
export class PluginBlockTemplateClient extends Plugin {
|
||||||
templateInfos = new Map();
|
templateInfos = new Map();
|
||||||
templateschemacache = {};
|
templateschemacache = {};
|
||||||
@ -158,9 +160,10 @@ export class PluginBlockTemplateClient extends Plugin {
|
|||||||
deleteItemIndex !== -1 &&
|
deleteItemIndex !== -1 &&
|
||||||
!schemaSetting.items.find((item) => item.name === 'template-revertSettingItem')
|
!schemaSetting.items.find((item) => item.name === 'template-revertSettingItem')
|
||||||
) {
|
) {
|
||||||
schemaSetting.items.splice(deleteItemIndex, 0, revertSettingItem);
|
schemaSetting.items.splice(deleteItemIndex, 0, revertSettingItem, convertToNormalBlockSettingItem);
|
||||||
} else {
|
} else {
|
||||||
schemaSetting.add('template-revertSettingItem', revertSettingItem);
|
schemaSetting.add('template-revertSettingItem', revertSettingItem);
|
||||||
|
schemaSetting.add('template-convertToNormalBlockSettingItem', convertToNormalBlockSettingItem);
|
||||||
}
|
}
|
||||||
schemaSetting.add('template-disabledDeleteItem', disabledDeleteSettingItem);
|
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",
|
"Mobile": "Mobile",
|
||||||
"Current": "Current record",
|
"Current": "Current record",
|
||||||
"Revert to template": "Revert to template",
|
"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 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",
|
"Templates": "Templates",
|
||||||
"Block templates": "Block templates",
|
"Block templates": "Block templates",
|
||||||
"Submit": "Submit",
|
"Submit": "Submit",
|
||||||
@ -22,6 +24,7 @@
|
|||||||
"Saved successfully": "Saved successfully",
|
"Saved successfully": "Saved successfully",
|
||||||
"Block template": "Block template",
|
"Block template": "Block template",
|
||||||
"Reset successfully": "Reset successfully",
|
"Reset successfully": "Reset successfully",
|
||||||
|
"Converted successfully": "Converted successfully",
|
||||||
"Delete successfully": "Delete successfully",
|
"Delete successfully": "Delete successfully",
|
||||||
"Template block settings": "Template block settings",
|
"Template block settings": "Template block settings",
|
||||||
"Filter": "Filter",
|
"Filter": "Filter",
|
||||||
|
@ -12,7 +12,9 @@
|
|||||||
"Mobile": "移动端",
|
"Mobile": "移动端",
|
||||||
"Current": "当前记录",
|
"Current": "当前记录",
|
||||||
"Revert to template": "恢复到模板",
|
"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 revert all changes from the template?": "您确定要恢复所有对模板的更改吗?",
|
||||||
|
"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": "转换成功",
|
||||||
"Delete successfully": "删除成功",
|
"Delete successfully": "删除成功",
|
||||||
"Template block settings": "模板区块设置",
|
"Template block settings": "模板区块设置",
|
||||||
"Filter": "筛选",
|
"Filter": "筛选",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user