diff --git a/packages/client/src/demos/api/blocks-getSchema/login.ts b/packages/client/src/demos/api/blocks-getSchema/login.ts index e313f793bc..b427552b53 100644 --- a/packages/client/src/demos/api/blocks-getSchema/login.ts +++ b/packages/client/src/demos/api/blocks-getSchema/login.ts @@ -1,7 +1,7 @@ export default { type: 'object', properties: { - input: { + email: { type: 'string', required: true, 'x-decorator': 'FormItem', @@ -13,7 +13,7 @@ export default { }, }, }, - textarea: { + password: { type: 'string', required: true, 'x-decorator': 'FormItem', @@ -27,7 +27,6 @@ export default { }, actions: { type: 'void', - // 'x-decorator': 'Div', 'x-component': 'Div', properties: { submit: { diff --git a/packages/client/src/demos/api/blocks-getSchema/menu.ts b/packages/client/src/demos/api/blocks-getSchema/menu.ts index 2ee0d9b47c..068759f8b6 100644 --- a/packages/client/src/demos/api/blocks-getSchema/menu.ts +++ b/packages/client/src/demos/api/blocks-getSchema/menu.ts @@ -2,15 +2,18 @@ import { uid } from '@formily/shared'; export default { type: 'void', - name: `m_${uid()}`, + name: `menu_${uid()}`, 'x-component': 'Menu', 'x-designable-bar': 'Menu.DesignableBar', 'x-component-props': { mode: 'mix', theme: 'dark', + defaultSelectedKeys: '{{ selectedKeys }}', + sideMenuRef: '{{ sideMenuRef }}', + onSelect: '{{ onSelect }}', }, properties: { - item2: { + [uid()]: { type: 'void', title: `菜单2`, 'x-component': 'Menu.Link', @@ -18,7 +21,7 @@ export default { icon: 'MailOutlined', }, }, - item22: { + [uid()]: { type: 'void', title: `菜单22`, 'x-component': 'Menu.Link', @@ -27,15 +30,23 @@ export default { // url: 'https://www.google.com', }, }, - item3: { + [uid()]: { type: 'void', - title: '菜单组', + title: '菜单组1', + 'x-component': 'Menu.SubMenu', + 'x-component-props': { + icon: 'SettingOutlined', + }, + }, + [uid()]: { + type: 'void', + title: '菜单组2', 'x-component': 'Menu.SubMenu', 'x-component-props': { icon: 'SettingOutlined', }, properties: { - item6: { + [uid()]: { type: 'void', title: '菜单6', 'x-component': 'Menu.SubMenu', @@ -43,55 +54,48 @@ export default { icon: 'AppstoreOutlined', }, properties: { - item9: { + [uid()]: { type: 'void', title: '菜单9', 'x-component': 'Menu.SubMenu', properties: { - item10: { + [uid()]: { type: 'void', title: `子菜单10`, 'x-component': 'Menu.Link', - // properties: { - // action1: { - // type: 'void', - // title: '页面标题2', - // 'x-component': 'Action.Page', - // }, - // }, }, - item11: { + [uid()]: { type: 'void', title: `子菜单11`, 'x-component': 'Menu.Link', }, } }, - item7: { + [uid()]: { type: 'void', title: `子菜单7`, 'x-component': 'Menu.Link', }, - item8: { + [uid()]: { type: 'void', title: `子菜单8`, 'x-component': 'Menu.Link', }, } }, - item4: { + [uid()]: { type: 'void', title: `子菜单1`, 'x-component': 'Menu.Link', }, - item5: { + [uid()]: { type: 'void', title: `子菜单2`, 'x-component': 'Menu.Link', }, } }, - item1: { + [uid()]: { type: 'void', title: `菜单1`, 'x-component': 'Menu.Link', diff --git a/packages/client/src/schemas/DesignableSchemaField/index.tsx b/packages/client/src/schemas/DesignableSchemaField/index.tsx index 9e0517f8aa..7b01164a91 100644 --- a/packages/client/src/schemas/DesignableSchemaField/index.tsx +++ b/packages/client/src/schemas/DesignableSchemaField/index.tsx @@ -15,6 +15,7 @@ import { FormProvider, useField, useFieldSchema, + useForm, } from '@formily/react'; import { observable } from '@formily/reactive'; import { uid, clone } from '@formily/shared'; @@ -304,8 +305,7 @@ export function useDesignable(path?: any) { }; } -export function useSchemaPath() { - const schema = useFieldSchema(); +export function getSchemaPath(schema: Schema) { const path = [schema.name]; let parent = schema.parent; while (parent) { @@ -315,7 +315,13 @@ export function useSchemaPath() { path.unshift(parent.name); parent = parent.parent; } - console.log('useSchemaPath', path, schema); + console.log('getSchemaPath', path, schema); + return [...path]; +} + +export function useSchemaPath() { + const schema = useFieldSchema(); + const path = getSchemaPath(schema); return [...path]; } @@ -325,6 +331,7 @@ export const createDesignableSchemaField = (options) => { const SchemaField = createSchemaField(options); const DesignableSchemaField = (props) => { + const schema = useMemo(() => new Schema(props.schema), [props.schema]); const [, refresh] = useState(0); if (props.designable === false) { @@ -340,8 +347,9 @@ export const createDesignableSchemaField = (options) => { }, }} > - + + ); }; @@ -349,11 +357,34 @@ export const createDesignableSchemaField = (options) => { return DesignableSchemaField; }; -const CodePreview = ({ schema }) => { +const FormValues = () => { + const form = useForm(); const [visible, setVisible] = useState(false); return ( <> setVisible(true)} /> + setVisible(false)} + onCancel={() => setVisible(false)} + visible={visible} + > + + {/*
{JSON.stringify(schema.toJSON(), null, 2)}
*/} +
+ + ); +}; + +const CodePreview = ({ schema }) => { + const [visible, setVisible] = useState(false); + return ( + <> + setVisible(true)} /> setVisible(false)} @@ -387,6 +418,8 @@ export interface SchemaRendererProps { designable?: boolean; onRefresh?: any; onlyRenderProperties?: boolean; + scope?: any; + components?: any; } export const SchemaRenderer = (props: SchemaRendererProps) => { @@ -415,6 +448,8 @@ export const SchemaRenderer = (props: SchemaRendererProps) => { return ( { export { VisibleProvider, useVisibleContext }; const BaseAction = observer((props: any) => { - const { useAction = useDefaultAction, ...others } = props; + const { + ButtonComponent = Button, + className, + useAction = useDefaultAction, + ...others + } = props; const field = useField(); const { run } = useAction(); const { DesignableBar } = useDesignableBar(); const { setVisible } = useVisibleContext(); - const schema = useFieldSchema(); + // const schema = useFieldSchema(); + const { schema } = useDesignable(); useEffect(() => { field.componentProps.setVisible = setVisible; }, []); @@ -119,17 +125,18 @@ const BaseAction = observer((props: any) => { console.log('BaseAction', { field, schema }, field.title); const renderButton = () => ( - + ); const popover = schema.reduceProperties((items, current) => { @@ -268,6 +275,7 @@ Action.Dropdown = observer((props) => { // const { visible, setVisible } = useVisibleContext(); const schema = useFieldSchema(); return ( + <> { @@ -281,13 +289,16 @@ Action.Dropdown = observer((props) => { > + {/* popover 的按钮初始化时并未渲染,暂时先这么处理 */} +
{props.children}
+ ); }); Action.DesignableBar = () => { const field = useField(); - const schema = useFieldSchema(); - const { insertAfter } = useDesignable(); + // const schema = useFieldSchema(); + const { schema, insertAfter } = useDesignable(); const [visible, setVisible] = useState(false); return (
diff --git a/packages/client/src/schemas/menu/index.md b/packages/client/src/schemas/menu/index.md index de92423222..0f60866266 100644 --- a/packages/client/src/schemas/menu/index.md +++ b/packages/client/src/schemas/menu/index.md @@ -186,10 +186,15 @@ import { MenuContainerContext } from './'; import { Layout } from 'antd'; export default () => { - const ref = useRef(); + const sideMenuRef = useRef(); const [activeKey, setActiveKey] = useState('item3'); + const onSelect = (info) => { + setActiveKey(info.key); + console.log({ info }) + } + const schema = { type: 'object', properties: { @@ -197,13 +202,11 @@ export default () => { type: 'void', 'x-component': 'Menu', 'x-component-props': { + sideMenuRef: '{{ sideMenuRef }}', defaultSelectedKeys: [activeKey], mode: 'mix', theme: 'dark', - onSelect(info) { - setActiveKey(info.key); - console.log({ info }) - }, + onSelect: '{{ onSelect }}', }, properties: { item1: { @@ -271,14 +274,13 @@ export default () => {
- - - + - + {activeKey} @@ -299,10 +301,15 @@ import { MenuContainerContext } from './'; import { Layout } from 'antd'; export default () => { - const ref = useRef(); + const sideMenuRef = useRef(); const [activeKey, setActiveKey] = useState('item3'); + const onSelect = (info) => { + setActiveKey(info.key); + console.log({ info }) + } + const schema = { type: 'object', properties: { @@ -314,10 +321,8 @@ export default () => { defaultSelectedKeys: [activeKey], mode: 'mix', theme: 'dark', - onSelect(info) { - setActiveKey(info.key); - console.log({ info }) - }, + sideMenuRef: '{{ sideMenuRef }}', + onSelect: '{{ onSelect }}', }, properties: { item1: { @@ -385,14 +390,17 @@ export default () => {
- - - + - + {activeKey} @@ -403,3 +411,82 @@ export default () => { ) } ``` + +### Menu.Action + + +```tsx +/** + * title: 横向菜单 + */ +import React from 'react'; +import { SchemaRenderer } from '../'; + +const schema = { + type: 'object', + properties: { + menu1: { + type: 'void', + 'x-component': 'Menu', + 'x-designable-bar': 'Menu.DesignableBar', + 'x-component-props': { + mode: 'horizontal', + theme: 'dark', + }, + properties: { + item1: { + type: 'void', + title: `菜单1`, + 'x-component': 'Menu.Item', + }, + item2: { + type: 'void', + title: `菜单1`, + 'x-component': 'Menu.Action', + properties: { + drawer1: { + type: 'void', + title: '抽屉标题', + 'x-component': 'Action.Drawer', + 'x-component-props': {}, + properties: { + input: { + type: 'string', + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + action2: { + type: 'void', + title: '打开二级抽屉', + // 'x-decorator': 'FormItem', + 'x-component': 'Action', + properties: { + drawer1: { + type: 'void', + title: '二级抽屉标题', + 'x-component': 'Action.Drawer', + 'x-component-props': {}, + properties: { + input: { + type: 'string', + 'x-component': 'Input', + }, + }, + }, + }, + } + }, + }, + }, + }, + }, + }, + }, +} + +export default () => { + return ( + + ); +}; +``` diff --git a/packages/client/src/schemas/menu/index.tsx b/packages/client/src/schemas/menu/index.tsx index c39305f6f3..937620fb41 100644 --- a/packages/client/src/schemas/menu/index.tsx +++ b/packages/client/src/schemas/menu/index.tsx @@ -51,10 +51,10 @@ import cls from 'classnames'; import ReactDOM from 'react-dom'; import { useMount } from 'ahooks'; import { useDesignable, SchemaRenderer } from '..'; -import { Router } from "react-router"; +import { Router } from 'react-router'; import { useLifecycle } from 'beautiful-react-hooks'; -import { useDefaultAction } from '../action'; +import { Action, useDefaultAction } from '../action'; export type MenuType = React.FC & { Item?: React.FC; @@ -63,6 +63,7 @@ export type MenuType = React.FC & { DesignableBar?: React.FC; AddNew?: React.FC; Link?: React.FC; + Action?: React.FC; Url?: React.FC; }; @@ -92,9 +93,9 @@ function useDesignableBar() { }; } -export const Menu: MenuType = observer((props) => { - const { onSelect, mode, defaultSelectedKeys, ...others } = props; - const { sideMenuRef } = useContext(MenuContainerContext); +export const Menu: MenuType = observer((props: any) => { + const { sideMenuRef, onSelect, mode, defaultSelectedKeys, ...others } = props; + let defaultSelectedKey = defaultSelectedKeys ? defaultSelectedKeys[0] : null; const schema = useFieldSchema(); const { schema: designableSchema, refresh } = useDesignable(); const designableBar = schema['x-designable-bar']; @@ -109,33 +110,41 @@ export const Menu: MenuType = observer((props) => { if (!sideMenuRef || !sideMenuRef.current) { return; } - const properties = schema.properties[selectedKey].properties; - console.log({ selectedKey, properties }); - sideMenuRef.current.style.display = properties ? 'block' : 'none'; + const subSchema = schema.properties[selectedKey]; + if (!subSchema) { + sideMenuRef.current.style.display = 'none'; + ReactDOM.render(null, sideMenuRef.current); + return; + } + if (subSchema['x-component'] !== 'Menu.SubMenu') { + sideMenuRef.current.style.display = 'none'; + return; + } + const properties = subSchema.properties || {}; + sideMenuRef.current.style.display = 'block'; const newProps = {}; Object.keys(properties || {}).forEach((name) => { newProps[name] = properties[name].toJSON(); }); ReactDOM.render( - properties ? ( - + { const selected = designableSchema.properties[selectedKey]; const diff = subSchema.properties[`${schema.name}.${selectedKey}`]; - Object.keys(selected.properties).forEach((name) => { + Object.keys(selected.properties || {}).forEach((name) => { selected.properties[name].parent.removeProperty(name); }); Object.keys(diff.properties).forEach((name) => { if (name.endsWith('-add-new')) { return; } - console.log('diff', name) + console.log('diff', name); const current = diff.properties[name]; selected.addProperty(current.name, current.toJSON()); }); - console.log({selected }) + console.log({ selected }); refresh(); }} schema={{ @@ -155,15 +164,13 @@ export const Menu: MenuType = observer((props) => { }, }, }} - /> - ) : null, + /> + , sideMenuRef.current, ); }; useMount(() => { - const defaultSelectedKey = defaultSelectedKeys - ? defaultSelectedKeys[0] - : null; + console.log({ defaultSelectedKey }, schema.properties); renderSideMenu(defaultSelectedKey); }); return ( @@ -197,13 +204,16 @@ const AddNewAction = () => { trigger={['click']} overlay={ - { - insertBefore({ - type: 'void', - title: uid(), - "x-component": 'Menu.Item', - }) - }} style={{ minWidth: 150 }}> + { + insertBefore({ + type: 'void', + title: uid(), + 'x-component': 'Menu.Item', + }); + }} + style={{ minWidth: 150 }} + > 新建菜单 @@ -298,6 +308,25 @@ Menu.Item = observer((props: any) => { ); }); +Menu.Action = observer((props: any) => { + const { icon, ...others } = props; + const schema = useFieldSchema(); + const { DesignableBar } = useDesignableBar(); + return ( + : undefined} + ButtonComponent={AntdMenu.Item} + {...others} + /> + // + // + // + ); +}); + Menu.SubMenu = observer((props) => { const { DesignableBar } = useDesignableBar(); const schema = useFieldSchema(); @@ -372,11 +401,13 @@ Menu.DesignableBar = (props) => { const title = uid(); field.title = title; field.componentProps['icon'] = 'DeleteOutlined'; - schema['x-component-props'] = schema['x-component-props'] || {}; + schema['x-component-props'] = + schema['x-component-props'] || {}; schema['x-component-props']['icon'] = 'DeleteOutlined'; schema.title = title; fieldSchema.title = title; - fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {}; + fieldSchema['x-component-props'] = + fieldSchema['x-component-props'] || {}; fieldSchema['x-component-props']['icon'] = 'DeleteOutlined'; refresh(); }} @@ -396,6 +427,9 @@ Menu.DesignableBar = (props) => { > 删除菜单 + + + } > @@ -406,4 +440,27 @@ Menu.DesignableBar = (props) => { ); }; +function ModalButton() { + const [visible, setVisible] = useState(false); + return ( + <> +
{ + setVisible(true); + }} + > + 按钮 +
+ { + setVisible(false); + }} + > + aaa + + + ); +} + export default Menu; diff --git a/packages/client/src/schemas/menu/style.less b/packages/client/src/schemas/menu/style.less index a30b0bbb5f..ca356c60af 100644 --- a/packages/client/src/schemas/menu/style.less +++ b/packages/client/src/schemas/menu/style.less @@ -35,10 +35,12 @@ left: 0; bottom: 0; border: 2px solid #1890ff; + pointer-events: none; &.active { display: block; } .designable-bar-actions { + pointer-events: auto; position: absolute; right: 0; line-height: 1rem; @@ -60,4 +62,16 @@ right: -20px; border: 2px solid #1890ff; } +} + +.ant-menu-title-content { + .ant-btn { + background: none; + border: 0; + border-radius: 0; + color: inherit; + padding: 0 20px; + height: 100%; + margin: 0 -20px; + } } \ No newline at end of file diff --git a/packages/client/src/schemas/table/demos/demo1.tsx b/packages/client/src/schemas/table/demos/demo1.tsx index c91d0642ca..a29b10d176 100644 --- a/packages/client/src/schemas/table/demos/demo1.tsx +++ b/packages/client/src/schemas/table/demos/demo1.tsx @@ -14,7 +14,7 @@ function useAction() { } const schema = { - name: `t_${uid()}`, + name: `table_${uid()}`, type: 'array', 'x-component': 'Table', // default: [ @@ -36,7 +36,7 @@ const schema = { // isRemoteDataSource: true, }, properties: { - [`a_${uid()}`]: { + [`action_bar_${uid()}`]: { type: 'void', 'x-component': 'Table.ActionBar', properties: { @@ -132,22 +132,22 @@ const schema = { }, }, }, - [`a_${uid()}`]: { - type: 'void', - 'x-component': 'Table.ActionBar', - 'x-component-props': { - align: 'bottom', - }, - properties: { - pagination: { - type: 'void', - 'x-component': 'Table.Pagination', - 'x-component-props': { - defaultPageSize: 5, - }, - }, - }, - }, + // [`a_${uid()}`]: { + // type: 'void', + // 'x-component': 'Table.ActionBar', + // 'x-component-props': { + // align: 'bottom', + // }, + // properties: { + // pagination: { + // type: 'void', + // 'x-component': 'Table.Pagination', + // 'x-component-props': { + // defaultPageSize: 5, + // }, + // }, + // }, + // }, [`a_${uid()}`]: { type: 'void', 'x-component': 'Table.ActionBar', @@ -163,7 +163,7 @@ const schema = { }, }, }, - [`c_${uid()}`]: { + [`column_${uid()}`]: { type: 'void', title: '排序', 'x-component': 'Table.Column', @@ -174,7 +174,7 @@ const schema = { }, }, }, - [`c_${uid()}`]: { + [`column_${uid()}`]: { type: 'void', title: '序号', 'x-component': 'Table.Column', @@ -185,7 +185,7 @@ const schema = { }, }, }, - [`c_${uid()}`]: { + [`column_${uid()}`]: { type: 'void', title: '字段1', 'x-component': 'Table.Column', @@ -197,7 +197,7 @@ const schema = { field1: { type: 'string', required: true, - // 'x-read-pretty': true, + 'x-read-pretty': true, 'x-decorator-props': { feedbackLayout: 'popover', }, @@ -206,16 +206,17 @@ const schema = { }, }, }, - [`c_${uid()}`]: { + [`column_${uid()}`]: { type: 'void', title: '字段2', 'x-component': 'Table.Column', + 'x-designable-bar': 'Table.Column.DesignableBar', properties: { field2: { type: 'string', // title: '字段2', required: true, - // 'x-read-pretty': true, + 'x-read-pretty': true, 'x-decorator-props': { feedbackLayout: 'popover', }, @@ -224,18 +225,18 @@ const schema = { }, }, }, - [`col_${uid()}`]: { + [`column_${uid()}`]: { type: 'void', title: '操作', 'x-component': 'Table.Column', properties: { - action1: { + [uid()]: { type: 'void', name: 'action1', title: '查看', + 'x-component': 'Action', - 'x-default-action': true, - 'x-designable-bar': 'Action.DesignableBar', + 'x-designable-bar': 'Table.Action.DesignableBar', properties: { drawer1: { type: 'void', @@ -285,13 +286,12 @@ const schema = { }, }, }, - action2: { + [uid()]: { type: 'void', name: 'action1', title: '修改', 'x-component': 'Action', - 'x-default-action': true, - 'x-designable-bar': 'Action.DesignableBar', + 'x-designable-bar': 'Table.Action.DesignableBar', properties: { drawer1: { type: 'void', @@ -333,7 +333,7 @@ const schema = { }, }, }, - action3: { + [uid()]: { type: 'void', title: '删除', 'x-component': 'Action', @@ -341,12 +341,12 @@ const schema = { useAction: '{{ useTableDestroyAction }}', }, }, - action4: { + [uid()]: { type: 'void', title: '...', 'x-component': 'Action.Dropdown', properties: { - action5: { + [uid()]: { type: 'void', title: '操作 1', 'x-component': 'Action', @@ -355,21 +355,22 @@ const schema = { disabled: true, }, }, - action4: { + [uid()]: { type: 'void', title: '操作 2', 'x-component': 'Action', + 'x-default-action': true, 'x-component-props': { useAction, }, }, - action1: { + [uid()]: { type: 'void', name: 'action1', title: '查看', 'x-component': 'Action', // 'x-default-action': true, - 'x-designable-bar': 'Action.DesignableBar', + 'x-designable-bar': 'Table.Action.DesignableBar', properties: { drawer1: { type: 'void', @@ -419,13 +420,12 @@ const schema = { }, }, }, - action2: { + [uid()]: { type: 'void', name: 'action1', title: '修改', 'x-component': 'Action', - 'x-default-action': true, - 'x-designable-bar': 'Action.DesignableBar', + 'x-designable-bar': 'Table.Action.DesignableBar', properties: { drawer1: { type: 'void', @@ -467,7 +467,7 @@ const schema = { }, }, }, - action3: { + [uid()]: { type: 'void', title: '删除', 'x-component': 'Action', @@ -482,17 +482,14 @@ const schema = { }, }; -const form = createForm(); +const form = createForm({ + // designable: true, +}); export default observer(() => { return (
- {/* */}
); }); diff --git a/packages/client/src/schemas/table/index.tsx b/packages/client/src/schemas/table/index.tsx index a4a9562792..37654f8f6f 100644 --- a/packages/client/src/schemas/table/index.tsx +++ b/packages/client/src/schemas/table/index.tsx @@ -8,6 +8,7 @@ import { useForm, FormProvider, createSchemaField, + SchemaOptionsContext, } from '@formily/react'; import { Button, @@ -16,24 +17,32 @@ import { Space, Spin, Table as AntdTable, + Dropdown, + Menu, } from 'antd'; -import { findIndex } from 'lodash'; +import { findIndex, get } from 'lodash'; import constate from 'constate'; import useRequest from '@ahooksjs/use-request'; import { BaseResult } from '@ahooksjs/use-request/lib/types'; import { uid, clone } from '@formily/shared'; import { MenuOutlined } from '@ant-design/icons'; import { useVisibleContext } from '../action'; -import { SortableHandle, SortableContainer, SortableElement } from 'react-sortable-hoc' +import { + SortableHandle, + SortableContainer, + SortableElement, +} from 'react-sortable-hoc'; import cls from 'classnames'; +import { getSchemaPath, useDesignable, useSchemaPath } from '../DesignableSchemaField'; +import './style.less'; interface TableRowProps { index: number; data: any; } -const SortableRow = SortableElement((props: any) => ) -const SortableBody = SortableContainer((props: any) => ) +const SortableRow = SortableElement((props: any) => ); +const SortableBody = SortableContainer((props: any) => ); const TableRowContext = createContext(null); @@ -105,7 +114,8 @@ function useTableActionBars() { } function useTableColumns(props?: any) { - const schema = useFieldSchema(); + const { schema } = useDesignable(); + // const schema = useFieldSchema(); const { dataSource } = props || {}; function findColumns(schema: Schema): Schema[] { @@ -120,7 +130,7 @@ function useTableColumns(props?: any) { return findColumns(schema).map((item) => { const columnProps = item['x-component-props'] || {}; return { - title: item.title, + title: , dataIndex: item.name, ...columnProps, render(value, record, recordIndex) { @@ -282,23 +292,22 @@ const TableContainer = observer((props) => { useTableContext(); const rowKey = field.componentProps.rowKey || 'id'; const defaultAction = useDefaultAction(); - console.log({ defaultAction }); const dataSource = Array.isArray(field.value) ? field.value.slice() : []; const columns = useTableColumns({ dataSource }); - const ref = useRef() + const ref = useRef(); const addTdStyles = (node: HTMLElement) => { - const helper = document.body.querySelector(`.nb-table-sort-helper`) + const helper = document.body.querySelector(`.nb-table-sort-helper`); if (helper) { - const tds = node.querySelectorAll('td') + const tds = node.querySelectorAll('td'); requestAnimationFrame(() => { helper.querySelectorAll('td').forEach((td, index) => { if (tds[index]) { - td.style.width = getComputedStyle(tds[index]).width + td.style.width = getComputedStyle(tds[index]).width; } - }) - }) + }); + }); } - } + }; return (
{actionBars.top.map((actionBarSchema) => { @@ -339,10 +348,10 @@ const TableContainer = observer((props) => { // disableAutoscroll helperClass={`nb-table-sort-helper`} helperContainer={() => { - return ref.current?.querySelector('tbody') + return ref.current?.querySelector('tbody'); }} onSortStart={({ node }) => { - addTdStyles(node) + addTdStyles(node); }} onSortEnd={({ oldIndex, newIndex }) => { field.move(oldIndex, newIndex); @@ -352,13 +361,11 @@ const TableContainer = observer((props) => { /> ), row: (props: any) => { - const index = findIndex(field.value, item => item[rowKey] === props['data-row-key']); - return ( - - ) + const index = findIndex( + field.value, + (item) => item[rowKey] === props['data-row-key'], + ); + return ; }, }, }} @@ -366,20 +373,17 @@ const TableContainer = observer((props) => { const index = dataSource.indexOf(data); return { onClick(e) { - console.log('onRow', (e.target as HTMLElement), (e.target as HTMLElement).classList.contains('ant-table-cell')); - if (!(e.target as HTMLElement).classList.contains('ant-table-cell')) { - return; - } if (!defaultAction) { return; } - // console.log('defaultAction'); - field - .query(`.${schema.name}.${index}.${defaultAction.name}`) - .take((f) => { - const setVisible = f.componentProps.setVisible; - setVisible && setVisible(true); - }); + const el = (e.target as HTMLElement); + if ( + !el.classList.contains('ant-table-cell') + ) { + return; + } + const btn = el.parentElement.querySelector(`.name-${defaultAction.name}`); + btn && btn.click(); }, }; }} @@ -411,7 +415,99 @@ export const Table: any = observer((props) => { ); }); -Table.Column = () => null; +function Blank() { + return null; +} + +function useDesignableBar() { + const schema = useFieldSchema(); + const options = useContext(SchemaOptionsContext); + const DesignableBar = get(options.components, schema['x-designable-bar']); + + return { + DesignableBar: DesignableBar || Blank, + }; +} + +Table.Column = observer((props) => { + const schema = useFieldSchema(); + const field = useField(); + console.log('Table.Column', schema, field.title); + const { DesignableBar } = useDesignableBar(); + return ( +
+ {field.title} + +
+ ); +}); + +Table.Column.DesignableBar = () => { + const field = useField(); + // const fieldSchema = useFieldSchema(); + const { schema, remove, refresh, insertAfter } = useDesignable(); + const [visible, setVisible] = useState(false); + console.log('Table.Column.DesignableBar', { schema }); + return ( +
+ { + e.stopPropagation(); + }} + className={cls('designable-bar-actions', { active: visible })} + > + { + setVisible(visible); + }} + overlay={ + + { + const title = uid(); + field.title = title; + schema.title = title; + setVisible(false); + }}>点击修改按钮文案 + { + remove(); + console.log('Table.Column.DesignableBar', { schema }); + }}>删除列 + { + const name = uid(); + insertAfter({ + name: `column_${name}`, + type: 'void', + title: `字段 ${name}`, + 'x-component': 'Table.Column', + 'x-component-props': { + // title: 'z1', + }, + 'x-designable-bar': 'Table.Column.DesignableBar', + properties: { + [name]: { + type: 'string', + required: true, + // 'x-read-pretty': true, + 'x-decorator-props': { + feedbackLayout: 'popover', + }, + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + }, + }) + }}>插入列 + + } + > + + + +
+ ); +}; Table.ActionBar = observer((props) => { return ( @@ -447,17 +543,20 @@ const SortHandle = SortableHandle((props: any) => { className={cls(`nb-table-sort-handle`, props.className)} style={{ ...props.style }} /> - ) -}) as any + ); +}) as any; Table.SortHandle = observer((props) => { const field = useField(); console.log('SortHandle', field.value); - return ; + return ; }); Table.Index = observer((props) => { const index = useTableIndex(); + const schema = useFieldSchema(); + const field = useField(); + const path = useSchemaPath(); return
#{index + 1}
; }); @@ -484,3 +583,41 @@ Table.Addition = observer((props: any) => { ); }); + +Table.Action = () => null; + +Table.Action.DesignableBar = () => { + const field = useField(); + const path = useSchemaPath(); + const { schema, remove, refresh, insertAfter } = useDesignable(); + const [visible, setVisible] = useState(false); + console.log('Table.Action.DesignableBar', path, field.address.entire, { schema, field }); + return ( +
+ { + e.stopPropagation(); + }} + className={cls('designable-bar-actions', { active: visible })} + > + { + setVisible(visible); + }} + overlay={ + + { + schema.title = uid(); + refresh(); + }}>点击修改按钮文案 + + } + > + + + +
+ ); +}; \ No newline at end of file diff --git a/packages/client/src/schemas/table/style.less b/packages/client/src/schemas/table/style.less new file mode 100644 index 0000000000..d7769a9ae7 --- /dev/null +++ b/packages/client/src/schemas/table/style.less @@ -0,0 +1,61 @@ +.nb-table-column { + &:hover { + > .designable-bar { + display: block; + } + } + > .designable-bar { + display: none; + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + border-radius: 0; + border: 2px solid #1890ff; + &.active { + display: block; + } + .designable-bar-actions { + position: absolute; + right: 0; + line-height: 1rem; + background-color: #1890ff; + color: #fff; + z-index: 10; + padding: 0 3px; + .anticon { + font-size: 10px; + } + } + } +} + +.ant-popover.nb-action-group { + z-index: 1000; + padding-top: 0; + .ant-popover-arrow { + display: none; + } + .ant-popover-inner-content { + padding: 4px 0; + .ant-btn { + display: block; + min-width: 100px; + width: 100%; + border: 0; + border-radius: 0; + text-align: left; + padding: 5px 12px; + color: #000; + &:hover { + background-color: #f5f5f5; + } + &[disabled] { + color: #00000040; + background-color:#fff; + cursor:not-allowed; + } + } + } +} diff --git a/packages/client/src/templates/admin-layout/index.tsx b/packages/client/src/templates/admin-layout/index.tsx index a03c8372ab..85cd507a0f 100644 --- a/packages/client/src/templates/admin-layout/index.tsx +++ b/packages/client/src/templates/admin-layout/index.tsx @@ -161,33 +161,36 @@ function Database() { } function LayoutWithMenu({ schema }) { + const match = useRouteMatch(); const location = useLocation(); - const ref = useRef(); - const [activeKey, setActiveKey] = useState('item3'); - schema['x-component-props']['defaultSelectedKeys'] = [activeKey]; - schema['x-component-props']['onSelect'] = (info) => { - console.log('LayoutWithMenu', schema) + const sideMenuRef = useRef(); + const [activeKey, setActiveKey] = useState(match.params.name); + const onSelect = (info) => { + console.log('LayoutWithMenu', schema); setActiveKey(info.key); - } + }; + console.log({ match }); return ( - - - - - - - - - - {location.pathname} - - - + + + + + + + {location.pathname} + + - ) + + ); } function Content({ activeKey }) { @@ -207,7 +210,6 @@ function Content({ activeKey }) { } export function AdminLayout({ route, children }: any) { - const match = useRouteMatch(); const { data = {}, loading } = useRequest( `/api/blocks:getSchema/${route.blockId}`, @@ -220,9 +222,7 @@ export function AdminLayout({ route, children }: any) { return ; } - return ( - - ); + return ; } export default AdminLayout;