improved designable styling

This commit is contained in:
chenos 2021-08-17 14:40:38 +08:00
parent da418720aa
commit 2ba5e6f76f
13 changed files with 967 additions and 842 deletions

View File

@ -349,7 +349,6 @@ const schema: ISchema = {
'x-component-props': {
className: 'nb-table-operation',
},
'x-designable-bar': 'Table.Operation.DesignableBar',
properties: {
[uid()]: {
type: 'void',

View File

@ -1,5 +1,5 @@
import React, { useContext, useEffect } from 'react';
import { Button, Dropdown, Menu } from 'antd';
import { Button, Dropdown, Menu, Spin } from 'antd';
import { useHistory } from 'react-router-dom';
import { request } from '../../schemas';
import { useRequest } from 'ahooks';
@ -25,7 +25,7 @@ export function SystemSettingsProvider(props) {
);
return (
<SystemSettingsContext.Provider value={{ service, resource }}>
{props.children}
{service.loading ? <Spin /> : props.children}
</SystemSettingsContext.Provider>
);
}

View File

@ -89,7 +89,7 @@ function LayoutWithMenu(props: LayoutWithMenuProps) {
};
return (
<Layout>
<Layout.Header style={{ display: 'flex' }}>
<Layout.Header className={'site-header'} style={{ display: 'flex' }}>
<SiteTitle />
<SchemaRenderer
schema={schema}

View File

@ -50,8 +50,9 @@
.nb-designable-toggle {
background: none !important;
border-radius: 0 !important;
&.active {
background: #1890ff !important;
// background: #faad14 !important;
}
}
@ -76,3 +77,23 @@
padding-right: 8px;
}
}
.site-header {
.ant-menu.ant-menu-dark .ant-menu-item-selected, .ant-menu-submenu-popup.ant-menu-dark .ant-menu-item-selected {
background-color: rgba(255, 255, 255, 0.1);
}
.ant-menu-dark.ant-menu-horizontal > .ant-menu-item:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.ant-btn {
color: rgba(255, 255, 255, 0.65) !important;
&.nb-designable-toggle:hover {
color: #fff !important;
background-color: #F18B62 !important
}
}
.ant-btn:hover {
color: #fff !important;
background-color: rgba(255, 255, 255, 0.1) !important;
}
}

View File

@ -214,7 +214,7 @@ function AddActionButton() {
</Menu>
}
>
<Button style={{ marginLeft: 8 }} type={'dashed'} icon={<PlusOutlined />}>
<Button className={'designable-btn designable-btn-dash'} style={{ marginLeft: 8 }} type={'dashed'} icon={<PlusOutlined />}>
</Button>
</Dropdown>

View File

@ -339,101 +339,107 @@ Action.DesignableBar = (props: any) => {
}}
className={cls('designable-bar-actions', { active: visible })}
>
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.Item
onClick={async (e) => {
const values = await FormDialog('修改名称和图标', () => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
schema={{
type: 'object',
properties: {
title: {
type: 'string',
title: '按钮名称',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
<Space size={2}>
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.Item
onClick={async (e) => {
const values = await FormDialog('修改名称和图标', () => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
schema={{
type: 'object',
properties: {
title: {
type: 'string',
title: '按钮名称',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
icon: {
type: 'string',
title: '按钮图标',
'x-decorator': 'FormItem',
'x-component': 'IconPicker',
},
},
icon: {
type: 'string',
title: '按钮图标',
'x-decorator': 'FormItem',
'x-component': 'IconPicker',
},
},
}}
/>
</FormLayout>
);
}).open({
initialValues: {
title: schema['title'],
icon: schema['x-component-props']?.['icon'],
},
});
schema['title'] = values.title;
schema['x-component-props']['icon'] = values.icon;
field.componentProps.icon = values.icon;
field.title = values.title;
updateSchema(schema);
refresh();
}}
>
</Menu.Item>
{isPopup && (
<Menu.Item>
{' '}
<Select
bordered={false}
size={'small'}
defaultValue={'Action.Modal'}
onChange={(value) => {
const s = Object.values(schema.properties).shift();
s['x-component'] = value;
refresh();
updateSchema(s);
}}
>
<Select.Option value={'Action.Modal'}></Select.Option>
<Select.Option value={'Action.Drawer'}></Select.Option>
<Select.Option value={'Action.Window'}>
</Select.Option>
</Select>{' '}
}}
/>
</FormLayout>
);
}).open({
initialValues: {
title: schema['title'],
icon: schema['x-component-props']?.['icon'],
},
});
schema['title'] = values.title;
schema['x-component-props']['icon'] = values.icon;
field.componentProps.icon = values.icon;
field.title = values.title;
updateSchema(schema);
refresh();
}}
>
</Menu.Item>
)}
<Menu.Divider />
<Menu.Item
onClick={async () => {
const displayName =
schema?.['x-decorator-props']?.['displayName'];
const data = remove();
await removeSchema(data);
if (displayName) {
displayed.remove(displayName);
}
setVisible(false);
}}
>
</Menu.Item>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
{isPopup && (
<Menu.Item>
{' '}
<Select
bordered={false}
size={'small'}
defaultValue={'Action.Modal'}
onChange={(value) => {
const s = Object.values(schema.properties).shift();
s['x-component'] = value;
refresh();
updateSchema(s);
}}
>
<Select.Option value={'Action.Modal'}>
</Select.Option>
<Select.Option value={'Action.Drawer'}>
</Select.Option>
<Select.Option value={'Action.Window'}>
</Select.Option>
</Select>{' '}
</Menu.Item>
)}
<Menu.Divider />
<Menu.Item
onClick={async () => {
const displayName =
schema?.['x-decorator-props']?.['displayName'];
const data = remove();
await removeSchema(data);
if (displayName) {
displayed.remove(displayName);
}
setVisible(false);
}}
>
</Menu.Item>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
</Space>
</span>
</div>
);

View File

@ -935,7 +935,7 @@ AddNew.CardItem = observer((props: any) => {
{ghost ? (
<PlusOutlined />
) : (
<Button type={'dashed'} icon={<PlusOutlined />}>
<Button type={'dashed'} className={'designable-btn designable-btn-dash'} icon={<PlusOutlined />}>
</Button>
)}
@ -1122,7 +1122,7 @@ AddNew.FormItem = observer((props: any) => {
{ghost ? (
<PlusOutlined />
) : (
<Button type={'dashed'} icon={<SettingOutlined />}>
<Button type={'dashed'} className={'designable-btn designable-btn-dash'} icon={<SettingOutlined />}>
</Button>
)}
@ -1326,7 +1326,7 @@ AddNew.PaneItem = observer((props: any) => {
{ghost ? (
<PlusOutlined />
) : (
<Button block type={'dashed'} icon={<PlusOutlined />}>
<Button block className={'designable-btn designable-btn-dash'} type={'dashed'} icon={<PlusOutlined />}>
</Button>
)}

View File

@ -27,9 +27,6 @@
color: #fff;
z-index: 10;
padding: 0 3px;
.ant-space {
gap: 1px !important;
}
.anticon {
width: 16px;
height: 16px;

View File

@ -1,8 +1,71 @@
@bgcolor: #F18B62;
.designable-info {
position: absolute;
font-size: 12px;
background: #1890ff;
background: @bgcolor;
color: #fff;
padding: 0 5px;
line-height: 16px;
height: 16px;
border-bottom-right-radius: 2px;
}
.designable-bar {
border-radius: 1px !important;
border: 2px solid @bgcolor !important;
.designable-bar-actions {
padding: 0 !important;
background: none !important;
display: flex !important;
right: 2px !important;
// .ant-space-item {
// margin: 0 !important;
// }
.ant-space {
gap: 2px !important;
}
.anticon {
width: 16px !important;
height: 16px !important;
vertical-align: top;
line-height: 16px !important;
// font-size: 10px;
// display: inline-flex;
background-color: @bgcolor !important;
border-bottom-left-radius: 1px;
border-bottom-right-radius: 1px;
// margin-left: 0 !important;
// margin-right: 2px !important;
}
}
}
.designable-btn-dash {
border-color: @bgcolor !important;
color: @bgcolor !important;
}
.nb-add-new-menu-item.menu-mode-horizontal.designable {
color: @bgcolor !important;
border: 1px dashed !important;
height: 32px !important;
// &:hover {
// color: #fff !important;
// background: @bgcolor !important;
// }
// &:active {
// color: #fff !important;
// background: @bgcolor !important;
// }
}
.ant-menu-item-group.nb-menu-add-new {
.menu-mode-horizontal {
margin-left: 8px;
}
}
.nb-designable-toggle.active {
background: @bgcolor !important;
}

View File

@ -62,7 +62,7 @@ export const FieldDesignableBar = observer((props) => {
}}
className={cls('designable-bar-actions', { active: visible })}
>
<Space size={'small'}>
<Space size={2}>
<AddNew.FormItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<Dropdown

View File

@ -29,6 +29,7 @@ import {
Dropdown,
Modal,
Button,
Space,
} from 'antd';
import { uid } from '@formily/shared';
import cls from 'classnames';
@ -97,7 +98,7 @@ const SideMenu = (props: any) => {
<Button
block
type={'dashed'}
className={`nb-add-new-menu-item menu-mode-inline`}
className={`nb-add-new-menu-item menu-mode-inline designable-btn designable-btn-dash`}
>
<PlusOutlined />
</Button>
@ -207,7 +208,7 @@ export const Menu: any = observer((props: any) => {
<Menu.AddNew key={uid()} path={path}>
{/* <PlusOutlined className={'nb-add-new-icon'} /> */}
<Button
className={`nb-add-new-menu-item menu-mode-${
className={`designable-btn designable-btn-dash nb-add-new-menu-item designable menu-mode-${
mode === 'mix' ? 'horizontal' : mode
}`}
block
@ -660,350 +661,357 @@ Menu.DesignableBar = (props) => {
}}
className={'designable-bar-actions'}
>
<DragHandle />
<Dropdown
overlayStyle={{
minWidth: 150,
}}
trigger={['click']}
overlay={
<AntdMenu
onClick={async (info) => {
if (['update', 'move', 'delete'].includes(info.key)) {
return;
}
const methodLabels = {
insertBefore: '之前',
insertAfter: '之后',
appendChild: '里',
};
const keys = info.key.split('.');
const method = keys.shift();
const type = keys.join('.');
const config = schemas[type];
if (!config) {
return;
}
const values = await FormDialog(
`在「${schema.title}${methodLabels[method]}新建${config.title}`,
() => {
return (
<FormLayout layout={'vertical'}>
<SchemaField schema={config.schema} />
</FormLayout>
);
},
).open();
const methods = {
insertAfter,
insertBefore,
appendChild,
};
const defaults = generateDefaultSchema(type);
const data = methods[method](deepmerge(defaults, values));
await createSchema(data);
}}
>
<AntdMenu.Item
key={'update'}
onClick={async () => {
const initialValues = {};
Object.keys(formConfig.schema.properties).forEach((name) => {
_.set(initialValues, name, get(schema, name));
});
<Space size={2}>
<DragHandle />
<Dropdown
overlayStyle={{
minWidth: 150,
}}
trigger={['click']}
overlay={
<AntdMenu
onClick={async (info) => {
if (['update', 'move', 'delete'].includes(info.key)) {
return;
}
const methodLabels = {
insertBefore: '之前',
insertAfter: '之后',
appendChild: '里',
};
const keys = info.key.split('.');
const method = keys.shift();
const type = keys.join('.');
const config = schemas[type];
if (!config) {
return;
}
const values = await FormDialog(
`修改${formConfig.title}`,
`在「${schema.title}${methodLabels[method]}新建${config.title}`,
() => {
return (
<FormLayout layout={'vertical'}>
<SchemaField schema={formConfig.schema} />
<SchemaField schema={config.schema} />
</FormLayout>
);
},
).open({
initialValues,
});
if (values.title) {
schema.title = values.title;
}
const icon = _.get(values, 'x-component-props.icon') || null;
schema['x-component-props'] =
schema['x-component-props'] || {};
schema['x-component-props']['icon'] = icon;
field.componentProps['icon'] = icon;
refresh();
await updateSchema(schema);
).open();
const methods = {
insertAfter,
insertBefore,
appendChild,
};
const defaults = generateDefaultSchema(type);
const data = methods[method](deepmerge(defaults, values));
await createSchema(data);
}}
>
<EditOutlined /> {formConfig.title}
</AntdMenu.Item>
<AntdMenu.Item
key={'move'}
onClick={async () => {
let menuSchema: Schema;
let parent = schema.parent;
while (parent) {
if (parent['x-component'] === 'Menu') {
menuSchema = parent;
break;
}
parent = parent.parent;
}
console.log({ menuSchema });
const toTreeData = (s: Schema) => {
const items = [];
Object.keys(s.properties || {}).forEach((name) => {
const current = s.properties[name];
if (
!(current['x-component'] as string).startsWith('Menu.')
) {
return;
}
// if (current.name === schema.name) {
// return;
// }
items.push({
key: current['key'] || current.name,
label: current.title,
title: current.title,
value: getSchemaPath(current).join('.'),
children: toTreeData(current),
});
<AntdMenu.Item
key={'update'}
onClick={async () => {
const initialValues = {};
Object.keys(formConfig.schema.properties).forEach(
(name) => {
_.set(initialValues, name, get(schema, name));
},
);
const values = await FormDialog(
`修改${formConfig.title}`,
() => {
return (
<FormLayout layout={'vertical'}>
<SchemaField schema={formConfig.schema} />
</FormLayout>
);
},
).open({
initialValues,
});
return items;
};
if (values.title) {
schema.title = values.title;
}
const icon =
_.get(values, 'x-component-props.icon') || null;
schema['x-component-props'] =
schema['x-component-props'] || {};
schema['x-component-props']['icon'] = icon;
field.componentProps['icon'] = icon;
refresh();
await updateSchema(schema);
}}
>
<EditOutlined /> {formConfig.title}
</AntdMenu.Item>
<AntdMenu.Item
key={'move'}
onClick={async () => {
let menuSchema: Schema;
let parent = schema.parent;
while (parent) {
if (parent['x-component'] === 'Menu') {
menuSchema = parent;
break;
}
parent = parent.parent;
}
const dataSource = toTreeData(menuSchema);
console.log({ menuSchema });
const values = await FormDialog(
`将「${schema.title}」移动到`,
() => {
const toTreeData = (s: Schema) => {
const items = [];
Object.keys(s.properties || {}).forEach((name) => {
const current = s.properties[name];
if (
!(current['x-component'] as string).startsWith(
'Menu.',
)
) {
return;
}
// if (current.name === schema.name) {
// return;
// }
items.push({
key: current['key'] || current.name,
label: current.title,
title: current.title,
value: getSchemaPath(current).join('.'),
children: toTreeData(current),
});
});
return items;
};
const dataSource = toTreeData(menuSchema);
const values = await FormDialog(
`将「${schema.title}」移动到`,
() => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
schema={{
type: 'object',
properties: {
path: {
type: 'string',
title: '目标位置',
enum: dataSource,
'x-decorator': 'FormItem',
'x-component': 'TreeSelect',
},
method: {
type: 'string',
default: 'insertAfter',
enum: [
{ label: '之后', value: 'insertAfter' },
{ label: '之前', value: 'insertBefore' },
],
'x-decorator': 'FormItem',
'x-component': 'Radio.Group',
},
},
}}
/>
</FormLayout>
);
},
).open({
effects(form) {
onFieldChange('path', (field) => {
const target = findPropertyByPath(
schema.root,
// @ts-ignore
field.value,
);
console.log({ field });
field.query('method').take((f) => {
// @ts-ignore
// f.value = 'insertAfter';
// @ts-ignore
f.dataSource =
target['x-component'] === 'Menu.SubMenu'
? [
{ label: '之后', value: 'insertAfter' },
{ label: '之前', value: 'insertBefore' },
{ label: '组里', value: 'appendChild' },
]
: [
{ label: '之后', value: 'insertAfter' },
{ label: '之前', value: 'insertBefore' },
];
});
});
},
});
const methods = {
insertAfter,
insertBefore,
appendChild,
};
const data = schema.toJSON();
remove();
const source = methods[values.method](data, values.path);
await updateSchema(source);
}}
>
<DragOutlined />
</AntdMenu.Item>
<AntdMenu.Divider />
<AntdMenu.SubMenu
key={'insertBefore'}
icon={<ArrowUpOutlined />}
title={'当前菜单项之前'}
>
<AntdMenu.Item
key={'insertBefore.Menu.Link'}
icon={<MenuOutlined />}
>
</AntdMenu.Item>
<AntdMenu.Item
key={'insertBefore.Menu.SubMenu'}
icon={<GroupOutlined />}
>
</AntdMenu.Item>
<AntdMenu.Item
key={'insertBefore.Menu.URL'}
icon={<LinkOutlined />}
>
</AntdMenu.Item>
</AntdMenu.SubMenu>
<AntdMenu.SubMenu
key={'insertAfter'}
icon={<ArrowDownOutlined />}
title={'当前菜单项之后'}
>
<AntdMenu.Item
key={'insertAfter.Menu.Link'}
icon={<MenuOutlined />}
>
</AntdMenu.Item>
<AntdMenu.Item
key={'insertAfter.Menu.SubMenu'}
icon={<GroupOutlined />}
>
</AntdMenu.Item>
<AntdMenu.Item
key={'insertAfter.Menu.URL'}
icon={<LinkOutlined />}
>
</AntdMenu.Item>
</AntdMenu.SubMenu>
{isSubMenu && (
<AntdMenu.SubMenu
key={'appendChild'}
icon={<ArrowRightOutlined />}
title={'当前菜单分组里'}
>
<AntdMenu.Item
key={'appendChild.Menu.Link'}
icon={<MenuOutlined />}
>
</AntdMenu.Item>
<AntdMenu.Item
key={'appendChild.Menu.SubMenu'}
icon={<GroupOutlined />}
>
</AntdMenu.Item>
<AntdMenu.Item
key={'appendChild.Menu.URL'}
icon={<LinkOutlined />}
>
</AntdMenu.Item>
</AntdMenu.SubMenu>
)}
<AntdMenu.Item
icon={<LockOutlined />}
onClick={async () => {
const loadRoles = async () => {
const resource = Resource.make('roles');
const data = await resource.list();
console.log('loadRoles', data);
return data?.data.map((item) => ({
label: item.title,
value: item.name,
}));
};
const resource = Resource.make({
associatedName: 'ui_schemas',
associatedKey: schema['key'],
resourceName: 'roles',
});
const uiSchemasRoles = await resource.list();
console.log({ uiSchemasRoles });
const values = await FormDialog(`设置权限`, () => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
scope={{ loadRoles }}
schema={{
type: 'object',
properties: {
path: {
type: 'string',
title: '目标位置',
enum: dataSource,
'x-decorator': 'FormItem',
'x-component': 'TreeSelect',
},
method: {
type: 'string',
default: 'insertAfter',
enum: [
{ label: '之后', value: 'insertAfter' },
{ label: '之前', value: 'insertBefore' },
roles: {
type: 'array',
title: '权限组',
'x-reactions': [
'{{useAsyncDataSource(loadRoles)}}',
],
'x-decorator': 'FormItem',
'x-component': 'Radio.Group',
'x-decorator': 'FormilyFormItem',
'x-component': 'Select',
'x-component-props': {
mode: 'tags',
},
},
},
}}
/>
</FormLayout>
);
},
).open({
effects(form) {
onFieldChange('path', (field) => {
const target = findPropertyByPath(
schema.root,
// @ts-ignore
field.value,
);
console.log({ field });
field.query('method').take((f) => {
// @ts-ignore
// f.value = 'insertAfter';
// @ts-ignore
f.dataSource =
target['x-component'] === 'Menu.SubMenu'
? [
{ label: '之后', value: 'insertAfter' },
{ label: '之前', value: 'insertBefore' },
{ label: '组里', value: 'appendChild' },
]
: [
{ label: '之后', value: 'insertAfter' },
{ label: '之前', value: 'insertBefore' },
];
});
});
},
});
const methods = {
insertAfter,
insertBefore,
appendChild,
};
const data = schema.toJSON();
remove();
const source = methods[values.method](data, values.path);
await updateSchema(source);
}}
>
<DragOutlined />
</AntdMenu.Item>
<AntdMenu.Divider />
<AntdMenu.SubMenu
key={'insertBefore'}
icon={<ArrowUpOutlined />}
title={'当前菜单项之前'}
>
<AntdMenu.Item
key={'insertBefore.Menu.Link'}
icon={<MenuOutlined />}
}).open({
initialValues: {
roles: uiSchemasRoles?.data?.map((role) => role.name),
},
});
await Resource.make({
resourceName: 'ui_schemas',
resourceKey: schema['key'],
}).save(values);
}}
>
</AntdMenu.Item>
<AntdMenu.Divider />
<AntdMenu.Item
key={'insertBefore.Menu.SubMenu'}
icon={<GroupOutlined />}
key={'delete'}
onClick={() => {
Modal.confirm({
title: '删除菜单',
content: '确认删除此菜单项吗?',
onOk: async () => {
const target = remove();
await removeSchema(target);
ctx.onRemove && ctx.onRemove(target);
},
});
}}
>
<DeleteOutlined />
</AntdMenu.Item>
<AntdMenu.Item
key={'insertBefore.Menu.URL'}
icon={<LinkOutlined />}
>
</AntdMenu.Item>
</AntdMenu.SubMenu>
<AntdMenu.SubMenu
key={'insertAfter'}
icon={<ArrowDownOutlined />}
title={'当前菜单项之后'}
>
<AntdMenu.Item
key={'insertAfter.Menu.Link'}
icon={<MenuOutlined />}
>
</AntdMenu.Item>
<AntdMenu.Item
key={'insertAfter.Menu.SubMenu'}
icon={<GroupOutlined />}
>
</AntdMenu.Item>
<AntdMenu.Item
key={'insertAfter.Menu.URL'}
icon={<LinkOutlined />}
>
</AntdMenu.Item>
</AntdMenu.SubMenu>
{isSubMenu && (
<AntdMenu.SubMenu
key={'appendChild'}
icon={<ArrowRightOutlined />}
title={'当前菜单分组里'}
>
<AntdMenu.Item
key={'appendChild.Menu.Link'}
icon={<MenuOutlined />}
>
</AntdMenu.Item>
<AntdMenu.Item
key={'appendChild.Menu.SubMenu'}
icon={<GroupOutlined />}
>
</AntdMenu.Item>
<AntdMenu.Item
key={'appendChild.Menu.URL'}
icon={<LinkOutlined />}
>
</AntdMenu.Item>
</AntdMenu.SubMenu>
)}
<AntdMenu.Item
icon={<LockOutlined />}
onClick={async () => {
const loadRoles = async () => {
const resource = Resource.make('roles');
const data = await resource.list();
console.log('loadRoles', data);
return data?.data.map((item) => ({
label: item.title,
value: item.name,
}));
};
const resource = Resource.make({
associatedName: 'ui_schemas',
associatedKey: schema['key'],
resourceName: 'roles',
});
const uiSchemasRoles = await resource.list();
console.log({ uiSchemasRoles });
const values = await FormDialog(`设置权限`, () => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
scope={{ loadRoles }}
schema={{
type: 'object',
properties: {
roles: {
type: 'array',
title: '权限组',
'x-reactions': [
'{{useAsyncDataSource(loadRoles)}}',
],
'x-decorator': 'FormilyFormItem',
'x-component': 'Select',
'x-component-props': {
mode: 'tags',
},
},
},
}}
/>
</FormLayout>
);
}).open({
initialValues: {
roles: uiSchemasRoles?.data?.map((role) => role.name),
},
});
await Resource.make({
resourceName: 'ui_schemas',
resourceKey: schema['key'],
}).save(values);
}}
>
</AntdMenu.Item>
<AntdMenu.Divider />
<AntdMenu.Item
key={'delete'}
onClick={() => {
Modal.confirm({
title: '删除菜单',
content: '确认删除此菜单项吗?',
onOk: async () => {
const target = remove();
await removeSchema(target);
ctx.onRemove && ctx.onRemove(target);
},
});
}}
>
<DeleteOutlined />
</AntdMenu.Item>
</AntdMenu>
}
>
<MenuOutlined />
</Dropdown>
</AntdMenu>
}
>
<MenuOutlined />
</Dropdown>
</Space>
</div>
</div>
);

View File

@ -110,4 +110,4 @@
.nb-add-new-menu-item.menu-mode-inline {
margin: 0 14px;
width: calc(100% - 28px);
}
}

View File

@ -510,7 +510,11 @@ function AddColumn() {
</Menu>
}
>
<Button type={'dashed'} icon={<SettingOutlined />}>
<Button
type={'dashed'}
className={'designable-btn designable-btn-dash'}
icon={<SettingOutlined />}
>
</Button>
</Dropdown>
@ -1089,7 +1093,12 @@ function AddActionButton() {
</Menu>
}
>
<Button style={{ marginLeft: 8 }} type={'dashed'} icon={<PlusOutlined />}>
<Button
className={'designable-btn designable-btn-dash'}
style={{ marginLeft: 8 }}
type={'dashed'}
icon={<PlusOutlined />}
>
</Button>
</Dropdown>
@ -1350,108 +1359,111 @@ Table.Filter.DesignableBar = () => {
}}
className={cls('designable-bar-actions', { active: visible })}
>
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.ItemGroup title={'筛选字段'}>
{fields
.filter((collectionField) => {
const option = interfaces.get(collectionField.interface);
return option.operations?.length;
})
.map((collectionField) => (
<SwitchMenuItem
title={collectionField?.uiSchema?.title}
checked={fieldNames.includes(collectionField.name)}
onChange={async (checked) => {
if (checked) {
fieldNames.push(collectionField.name);
} else {
const index = fieldNames.indexOf(
collectionField.name,
);
if (index > -1) {
fieldNames.splice(index, 1);
<Space>
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.ItemGroup title={'筛选字段'}>
{fields
.filter((collectionField) => {
const option = interfaces.get(collectionField.interface);
return option.operations?.length;
})
.map((collectionField) => (
<SwitchMenuItem
title={collectionField?.uiSchema?.title}
checked={fieldNames.includes(collectionField.name)}
onChange={async (checked) => {
if (checked) {
fieldNames.push(collectionField.name);
} else {
const index = fieldNames.indexOf(
collectionField.name,
);
if (index > -1) {
fieldNames.splice(index, 1);
}
}
}
console.log({ fieldNames, field });
schema['x-component-props']['fieldNames'] = fieldNames;
field.componentProps.fieldNames = fieldNames;
updateSchema(schema);
}}
/>
))}
</Menu.ItemGroup>
<Menu.Divider />
<Menu.Item
onClick={async (e) => {
const values = await FormDialog('修改名称和图标', () => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
schema={{
type: 'object',
properties: {
title: {
type: 'string',
title: '按钮名称',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
console.log({ fieldNames, field });
schema['x-component-props']['fieldNames'] =
fieldNames;
field.componentProps.fieldNames = fieldNames;
updateSchema(schema);
}}
/>
))}
</Menu.ItemGroup>
<Menu.Divider />
<Menu.Item
onClick={async (e) => {
const values = await FormDialog('修改名称和图标', () => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
schema={{
type: 'object',
properties: {
title: {
type: 'string',
title: '按钮名称',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
icon: {
type: 'string',
title: '按钮图标',
'x-decorator': 'FormItem',
'x-component': 'IconPicker',
},
},
icon: {
type: 'string',
title: '按钮图标',
'x-decorator': 'FormItem',
'x-component': 'IconPicker',
},
},
}}
/>
</FormLayout>
);
}).open({
initialValues: {
title: schema['title'],
icon: schema['x-component-props']?.['icon'],
},
});
schema['title'] = values.title;
schema['x-component-props']['icon'] = values.icon;
field.componentProps.icon = values.icon;
field.title = values.title;
updateSchema(schema);
refresh();
}}
>
</Menu.Item>
<Menu.Divider />
<Menu.Item
onClick={async () => {
const displayName =
schema?.['x-decorator-props']?.['displayName'];
const data = remove();
await removeSchema(data);
if (displayName) {
displayed.remove(displayName);
}
setVisible(false);
}}
>
</Menu.Item>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
}}
/>
</FormLayout>
);
}).open({
initialValues: {
title: schema['title'],
icon: schema['x-component-props']?.['icon'],
},
});
schema['title'] = values.title;
schema['x-component-props']['icon'] = values.icon;
field.componentProps.icon = values.icon;
field.title = values.title;
updateSchema(schema);
refresh();
}}
>
</Menu.Item>
<Menu.Divider />
<Menu.Item
onClick={async () => {
const displayName =
schema?.['x-decorator-props']?.['displayName'];
const data = remove();
await removeSchema(data);
if (displayName) {
displayed.remove(displayName);
}
setVisible(false);
}}
>
</Menu.Item>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
</Space>
</span>
</div>
);
@ -1475,101 +1487,105 @@ Table.ExportActionDesignableBar = () => {
}}
className={cls('designable-bar-actions', { active: visible })}
>
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.ItemGroup title={'导出字段'}>
{fields.map((collectionField) => (
<SwitchMenuItem
title={collectionField?.uiSchema?.title}
checked={fieldNames.includes(collectionField.name)}
onChange={async (checked) => {
if (checked) {
fieldNames.push(collectionField.name);
} else {
const index = fieldNames.indexOf(collectionField.name);
if (index > -1) {
fieldNames.splice(index, 1);
<Space>
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.ItemGroup title={'导出字段'}>
{fields.map((collectionField) => (
<SwitchMenuItem
title={collectionField?.uiSchema?.title}
checked={fieldNames.includes(collectionField.name)}
onChange={async (checked) => {
if (checked) {
fieldNames.push(collectionField.name);
} else {
const index = fieldNames.indexOf(
collectionField.name,
);
if (index > -1) {
fieldNames.splice(index, 1);
}
}
}
console.log({ fieldNames, field });
schema['x-component-props']['fieldNames'] = fieldNames;
field.componentProps.fieldNames = fieldNames;
updateSchema(schema);
}}
/>
))}
</Menu.ItemGroup>
<Menu.Divider />
<Menu.Item
onClick={async (e) => {
const values = await FormDialog('修改名称和图标', () => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
schema={{
type: 'object',
properties: {
title: {
type: 'string',
title: '按钮名称',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
console.log({ fieldNames, field });
schema['x-component-props']['fieldNames'] = fieldNames;
field.componentProps.fieldNames = fieldNames;
updateSchema(schema);
}}
/>
))}
</Menu.ItemGroup>
<Menu.Divider />
<Menu.Item
onClick={async (e) => {
const values = await FormDialog('修改名称和图标', () => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
schema={{
type: 'object',
properties: {
title: {
type: 'string',
title: '按钮名称',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
icon: {
type: 'string',
title: '按钮图标',
'x-decorator': 'FormItem',
'x-component': 'IconPicker',
},
},
icon: {
type: 'string',
title: '按钮图标',
'x-decorator': 'FormItem',
'x-component': 'IconPicker',
},
},
}}
/>
</FormLayout>
);
}).open({
initialValues: {
title: schema['title'],
icon: schema['x-component-props']?.['icon'],
},
});
schema['title'] = values.title;
schema['x-component-props']['icon'] = values.icon;
field.componentProps.icon = values.icon;
field.title = values.title;
updateSchema(schema);
refresh();
}}
>
</Menu.Item>
<Menu.Divider />
<Menu.Item
onClick={async () => {
const displayName =
schema?.['x-decorator-props']?.['displayName'];
const data = remove();
await removeSchema(data);
if (displayName) {
displayed.remove(displayName);
}
setVisible(false);
}}
>
</Menu.Item>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
}}
/>
</FormLayout>
);
}).open({
initialValues: {
title: schema['title'],
icon: schema['x-component-props']?.['icon'],
},
});
schema['title'] = values.title;
schema['x-component-props']['icon'] = values.icon;
field.componentProps.icon = values.icon;
field.title = values.title;
updateSchema(schema);
refresh();
}}
>
</Menu.Item>
<Menu.Divider />
<Menu.Item
onClick={async () => {
const displayName =
schema?.['x-decorator-props']?.['displayName'];
const data = remove();
await removeSchema(data);
if (displayName) {
displayed.remove(displayName);
}
setVisible(false);
}}
>
</Menu.Item>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
</Space>
</span>
</div>
);
@ -1580,9 +1596,7 @@ Table.Operation = observer((props: any) => {
return (
<div className={'nb-table-column'}>
{designable && schema['x-designable-bar'] && (
<Table.Operation.DesignableBar path={props.path} />
)}
<Table.Operation.DesignableBar path={props.path} />
</div>
);
});
@ -1600,9 +1614,10 @@ Table.Operation.Cell = observer((props: any) => {
Table.Operation.DesignableBar = (props) => {
const { schema, remove, refresh, appendChild } = useDesignable(props.path);
const [visible, setVisible] = useState(false);
if (!schema['x-designable-bar']) {
if (!schema?.parent?.parent['x-designable-bar']) {
return null;
}
console.log('Table.Operation.DesignableBar', schema?.parent?.parent);
const map = new Map();
schema.mapProperties((s) => {
if (!s['x-action-type']) {
@ -1618,52 +1633,54 @@ Table.Operation.DesignableBar = (props) => {
}}
className={cls('designable-bar-actions', { active: visible })}
>
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.ItemGroup title={'操作展示'}>
{[
{ title: '查看', name: 'view' },
{ title: '编辑', name: 'update' },
{ title: '删除', name: 'destroy' },
].map((item) => (
<SwitchMenuItem
key={item.name}
title={item.title}
checked={map.has(item.name)}
onChange={async (checked) => {
if (checked) {
const s = generateMenuActionSchema(item.name);
const data = appendChild(s);
await createSchema(data);
} else if (map.get(item.name)) {
const removed = remove([
...props.path,
map.get(item.name),
]);
await removeSchema(removed);
}
}}
/>
))}
</Menu.ItemGroup>
<Menu.Divider />
<Menu.SubMenu title={'自定义'}>
<Menu.Item style={{ minWidth: 120 }}></Menu.Item>
<Menu.Item></Menu.Item>
<Menu.Item></Menu.Item>
</Menu.SubMenu>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
<Space>
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.ItemGroup title={'操作展示'}>
{[
{ title: '查看', name: 'view' },
{ title: '编辑', name: 'update' },
{ title: '删除', name: 'destroy' },
].map((item) => (
<SwitchMenuItem
key={item.name}
title={item.title}
checked={map.has(item.name)}
onChange={async (checked) => {
if (checked) {
const s = generateMenuActionSchema(item.name);
const data = appendChild(s);
await createSchema(data);
} else if (map.get(item.name)) {
const removed = remove([
...props.path,
map.get(item.name),
]);
await removeSchema(removed);
}
}}
/>
))}
</Menu.ItemGroup>
<Menu.Divider />
<Menu.SubMenu title={'自定义'}>
<Menu.Item style={{ minWidth: 120 }}></Menu.Item>
<Menu.Item></Menu.Item>
<Menu.Item></Menu.Item>
</Menu.SubMenu>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
</Space>
</span>
</div>
);
@ -1686,109 +1703,115 @@ Table.Action.DesignableBar = () => {
}}
className={cls('designable-bar-actions', { active: visible })}
>
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.Item
onClick={async (e) => {
const values = await FormDialog('修改名称和图标', () => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
schema={{
type: 'object',
properties: {
title: {
type: 'string',
title: '按钮名称',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
<Space>
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.Item
onClick={async (e) => {
const values = await FormDialog('修改名称和图标', () => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
schema={{
type: 'object',
properties: {
title: {
type: 'string',
title: '按钮名称',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
icon: {
type: 'string',
title: '按钮图标',
'x-decorator': 'FormItem',
'x-component': 'IconPicker',
},
},
icon: {
type: 'string',
title: '按钮图标',
'x-decorator': 'FormItem',
'x-component': 'IconPicker',
},
},
}}
/>
</FormLayout>
);
}).open({
initialValues: {
title: schema['title'],
icon: schema['x-component-props']?.['icon'],
},
});
schema['title'] = values.title;
schema['x-component-props']['icon'] = values.icon;
field.componentProps.icon = values.icon;
field.title = values.title;
updateSchema(schema);
refresh();
}}
>
</Menu.Item>
{isPopup && (
<Menu.Item>
{' '}
<Select
bordered={false}
size={'small'}
defaultValue={'Action.Modal'}
onChange={(value) => {
const s = Object.values(schema.properties).shift();
s['x-component'] = value;
refresh();
updateSchema(s);
// const f = field.query(getSchemaPath(s)).take()
// console.log('fffffff', { schema, f });
}}
>
<Select.Option value={'Action.Modal'}></Select.Option>
<Select.Option value={'Action.Drawer'}></Select.Option>
<Select.Option value={'Action.Window'}>
</Select.Option>
</Select>{' '}
}}
/>
</FormLayout>
);
}).open({
initialValues: {
title: schema['title'],
icon: schema['x-component-props']?.['icon'],
},
});
schema['title'] = values.title;
schema['x-component-props']['icon'] = values.icon;
field.componentProps.icon = values.icon;
field.title = values.title;
updateSchema(schema);
refresh();
}}
>
</Menu.Item>
)}
{!inActionBar && (
<Menu.Item>
&nbsp;&nbsp;
<Switch size={'small'} defaultChecked />
{isPopup && (
<Menu.Item>
{' '}
<Select
bordered={false}
size={'small'}
defaultValue={'Action.Modal'}
onChange={(value) => {
const s = Object.values(schema.properties).shift();
s['x-component'] = value;
refresh();
updateSchema(s);
// const f = field.query(getSchemaPath(s)).take()
// console.log('fffffff', { schema, f });
}}
>
<Select.Option value={'Action.Modal'}>
</Select.Option>
<Select.Option value={'Action.Drawer'}>
</Select.Option>
<Select.Option value={'Action.Window'}>
</Select.Option>
</Select>{' '}
</Menu.Item>
)}
{!inActionBar && (
<Menu.Item>
&nbsp;&nbsp;
<Switch size={'small'} defaultChecked />
</Menu.Item>
)}
<Menu.Divider />
<Menu.Item
onClick={async () => {
const displayName =
schema?.['x-decorator-props']?.['displayName'];
const data = remove();
await removeSchema(data);
if (displayName) {
displayed.remove(displayName);
}
setVisible(false);
}}
>
</Menu.Item>
)}
<Menu.Divider />
<Menu.Item
onClick={async () => {
const displayName =
schema?.['x-decorator-props']?.['displayName'];
const data = remove();
await removeSchema(data);
if (displayName) {
displayed.remove(displayName);
}
setVisible(false);
}}
>
</Menu.Item>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
</Space>
</span>
</div>
);
@ -1860,77 +1883,80 @@ Table.Column.DesignableBar = () => {
}}
className={cls('designable-bar-actions', { active: visible })}
>
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.Item
onClick={async (e) => {
const values = await FormDialog('修改列标题', () => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
schema={{
type: 'object',
properties: {
fieldName: {
type: 'string',
title: '原字段名称',
'x-read-pretty': true,
'x-decorator': 'FormItem',
'x-component': 'Input',
<Space>
<DragHandle />
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.Item
onClick={async (e) => {
const values = await FormDialog('修改列标题', () => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
schema={{
type: 'object',
properties: {
fieldName: {
type: 'string',
title: '原字段名称',
'x-read-pretty': true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
title: {
type: 'string',
title: '自定义列标题',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
title: {
type: 'string',
title: '自定义列标题',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
}}
/>
</FormLayout>
);
}).open({
initialValues: {
fieldName: collectionField?.uiSchema?.title,
title: schema['title'],
},
});
const title = values.title || null;
field.title = title;
schema.title = title;
refresh();
await updateSchema({
key: schema['key'],
title: title,
});
setVisible(false);
}}
>
</Menu.Item>
<Menu.Divider />
<Menu.Item
onClick={async () => {
const s = remove();
const fieldName = schema['x-component-props']?.['fieldName'];
displayed.remove(fieldName);
await removeSchema(s);
}}
>
</Menu.Item>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
}}
/>
</FormLayout>
);
}).open({
initialValues: {
fieldName: collectionField?.uiSchema?.title,
title: schema['title'],
},
});
const title = values.title || null;
field.title = title;
schema.title = title;
refresh();
await updateSchema({
key: schema['key'],
title: title,
});
setVisible(false);
}}
>
</Menu.Item>
<Menu.Divider />
<Menu.Item
onClick={async () => {
const s = remove();
const fieldName =
schema['x-component-props']?.['fieldName'];
displayed.remove(fieldName);
await removeSchema(s);
}}
>
</Menu.Item>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
</Space>
</span>
</div>
);
@ -1957,14 +1983,16 @@ Table.DesignableBar = observer((props) => {
console.log({ collectionName });
return (
<div className={cls('designable-bar', { active: visible })}>
<div className={'designable-info'}>{collection?.title || collection?.name}</div>
<div className={'designable-info'}>
{collection?.title || collection?.name}
</div>
<span
onClick={(e) => {
e.stopPropagation();
}}
className={cls('designable-bar-actions', { active: visible })}
>
<Space size={'small'}>
<Space size={2}>
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<Dropdown
@ -2245,7 +2273,10 @@ Table.useActionLogDetailsResource = ({ onSuccess }) => {
});
const service = useRequest(
(params?: any) => {
return resource.get({ ...params, appends: ['changes', 'user', 'collection'] });
return resource.get({
...params,
appends: ['changes', 'user', 'collection'],
});
},
{
formatResult: (result) => result?.data,