From 95fef868804541ba5a764d0a9d29aab946788d65 Mon Sep 17 00:00:00 2001 From: jack zhang <1098626505@qq.com> Date: Tue, 30 Apr 2024 21:21:17 +0800 Subject: [PATCH] client components (#4216) * docs: update docs components * docs: add more component docs * docs: add more docs * fix: add more docs * fix: build bug * feat: docs * fix: build error * fix: docs * fix: change x-read-pretty to x-patten * fix: upgrade docs and types * fix: build bug * fix: add more docs * fix: build bug * fix: cascader component * fix: bug * fix: add more docs * fix: add backend ci time --------- Co-authored-by: katherinehhh --- .github/workflows/nocobase-test-backend.yml | 8 +- packages/core/client/.dumirc.ts | 212 ++++++++++++-- .../core/data-block/data-block-provider.md | 2 +- .../data-block-resource-provider.md | 2 +- .../docs/en-US/core/request/demos/demo1.tsx | 24 ++ .../docs/en-US/core/request/demos/demo2.tsx | 23 ++ .../docs/en-US/core/request/demos/demo3.tsx | 69 +++++ .../client/docs/en-US/core/request/index.md | 167 +++++++++++ .../core/data-block/data-block-provider.md | 2 +- .../data-block-resource-provider.md | 2 +- .../docs/zh-CN/core/request/demos/demo1.tsx | 24 ++ .../docs/zh-CN/core/request/demos/demo2.tsx | 23 ++ .../docs/zh-CN/core/request/demos/demo3.tsx | 69 +++++ .../client/docs/zh-CN/core/request/index.md | 167 +++++++++++ packages/core/client/src/api-client/index.md | 5 - .../__tests__/Application.test.tsx | 2 +- .../components/defaultComponents.tsx | 2 +- .../hoc/withDynamicSchemaProps.tsx | 5 +- packages/core/client/src/index.ts | 2 +- .../data-blocks/details-multi/hooks/index.ts | 3 + .../blocks/data-blocks/details-multi/index.ts | 2 + .../data-blocks/details-single/hooks/index.ts | 2 + .../data-blocks/details-single/index.ts | 1 + .../modules/blocks/data-blocks/table/index.ts | 1 + .../core/client/src/modules/blocks/index.ts | 2 + .../antd/action/Action.Drawer.tsx | 2 +- .../antd/action/Action.Modal.tsx | 4 +- .../antd/action/Action.Popover.tsx | 9 - .../schema-component/antd/action/Action.tsx | 9 +- .../antd/action/ActionBar.tsx | 11 +- .../schema-component/antd/action/context.tsx | 21 +- .../demos/new-demos/action-container.tsx | 49 ++++ .../action/demos/new-demos/action-context.tsx | 59 ++++ .../action/demos/new-demos/action-link.tsx | 17 ++ .../action/demos/new-demos/action-modal.tsx | 103 +++++++ .../action/demos/new-demos/action-popover.tsx | 33 +++ .../demos/new-demos/actionbar-one-column.tsx | 77 +++++ .../demos/new-demos/actionbar-two-columns.tsx | 81 ++++++ .../antd/action/demos/new-demos/basic.tsx | 23 ++ .../antd/action/demos/new-demos/confirm.tsx | 35 +++ .../demos/new-demos/custom-component.tsx | 38 +++ .../action/demos/new-demos/drawer-basic.tsx | 26 ++ .../action/demos/new-demos/drawer-footer.tsx | 79 +++++ .../demos/new-demos/drawer-openSize.tsx | 36 +++ .../demos/new-demos/drawer-with-form.tsx | 101 +++++++ .../action/demos/new-demos/dynamic-props.tsx | 68 +++++ .../action/demos/new-demos/schema-toolbar.tsx | 64 +++++ .../src/schema-component/antd/action/index.md | 220 ++++++++++++-- .../schema-component/antd/action/index.tsx | 1 + .../src/schema-component/antd/action/types.ts | 75 ++++- .../demos/new-demos/basic.tsx | 31 ++ .../demos/new-demos/multiple.tsx | 32 +++ .../demos/new-demos/read-pretty.tsx | 33 +++ .../antd/association-select/index.md | 39 ++- .../antd/auto-complete/AutoComplete.tsx | 15 +- .../antd/auto-complete/demos/basic.tsx | 38 +++ .../antd/auto-complete/demos/read-pretty.tsx | 22 ++ .../antd/auto-complete/index.md | 20 ++ .../antd/block-item/BlockItem.tsx | 8 +- .../antd/block-item/demos/new-demos/basic.tsx | 68 +++++ .../schema-component/antd/block-item/index.md | 27 +- .../antd/card-item/CardItem.tsx | 26 +- .../antd/card-item/demos/new-demos/basic.tsx | 58 ++++ .../card-item/demos/new-demos/lazy-render.tsx | 84 ++++++ .../schema-component/antd/card-item/index.md | 32 ++- .../antd/cascader/Cascader.tsx | 201 +++++++------ .../antd/cascader/ReadPretty.tsx | 13 +- .../antd/cascader/demos/new-demos/basic.tsx | 56 ++++ .../demos/new-demos/changeOnSelectLast.tsx | 59 ++++ .../cascader/demos/new-demos/labelInValue.tsx | 59 ++++ .../cascader/demos/new-demos/loadData.tsx | 76 +++++ .../cascader/demos/new-demos/read-pretty.tsx | 58 ++++ .../schema-component/antd/cascader/index.md | 77 +++-- .../antd/checkbox/Checkbox.tsx | 27 +- .../antd/checkbox/demos/new-demos/basic.tsx | 20 ++ .../demos/new-demos/group-read-pretty.tsx | 57 ++++ .../antd/checkbox/demos/new-demos/group.tsx | 34 +++ .../checkbox/demos/new-demos/read-pretty.tsx | 39 +++ .../schema-component/antd/checkbox/index.md | 51 +++- .../antd/collection-select/demos/basic.tsx | 20 ++ .../antd/collection-select/demos/multiple.tsx | 23 ++ .../collection-select/demos/read-pretty.tsx | 22 ++ .../antd/collection-select/index.md | 28 +- .../antd/color-picker/ColorPicker.tsx | 8 +- .../color-picker/demos/new-demos/basic.tsx | 20 ++ .../demos/new-demos/read-pretty.tsx | 22 ++ .../antd/color-picker/index.md | 21 +- .../antd/color-select/ColorSelect.tsx | 8 +- .../color-select/demos/new-demos/basic.tsx | 20 ++ .../demos/new-demos/read-pretty.tsx | 22 ++ .../antd/color-select/index.md | 27 +- .../src/schema-component/antd/cron/Cron.tsx | 14 +- .../schema-component/antd/cron/CronSet.tsx | 9 +- .../antd/cron/demos/new-demos/cron-basic.tsx | 20 ++ .../cron/demos/new-demos/cron-read-pretty.tsx | 22 ++ .../cron/demos/new-demos/cronset-basic.tsx | 40 +++ .../demos/new-demos/cronset-read-pretty.tsx | 34 +++ .../src/schema-component/antd/cron/index.md | 55 +++- .../antd/date-picker/DatePicker.tsx | 29 +- .../antd/date-picker/ReadPretty.tsx | 38 ++- .../demos/new-demos/date-basic.tsx | 20 ++ .../demos/new-demos/date-format.tsx | 24 ++ .../demos/new-demos/date-gmt-utc.tsx | 37 +++ .../date-picker/demos/new-demos/date-gmt.tsx | 35 +++ .../demos/new-demos/date-read-pretty.tsx | 85 ++++++ .../demos/new-demos/date-show-time.tsx | 23 ++ .../date-picker/demos/new-demos/date-utc.tsx | 35 +++ .../demos/new-demos/range-basic.tsx | 20 ++ .../demos/new-demos/range-format.tsx | 24 ++ .../demos/new-demos/range-gmt-utc.tsx | 37 +++ .../date-picker/demos/new-demos/range-gmt.tsx | 35 +++ .../demos/new-demos/range-read-pretty.tsx | 74 +++++ .../date-picker/demos/new-demos/range-utc.tsx | 35 +++ .../antd/date-picker/index.md | 102 +++++-- .../schema-component/antd/details/Details.tsx | 20 +- .../antd/details/demos/new-demos/list.tsx | 64 +++++ .../antd/details/demos/new-demos/single.tsx | 56 ++++ .../schema-component/antd/details/index.md | 29 +- .../error-fallback/demos/new-demos/basic.tsx | 26 ++ .../antd/error-fallback/index.md | 11 +- .../schema-component/antd/filter/Filter.tsx | 14 +- .../antd/filter/FilterAction.tsx | 11 +- .../schema-component/antd/filter/context.ts | 5 +- .../antd/filter/demos/new-demos/basic.tsx | 68 +++++ .../demos/new-demos/collection-fields.tsx | 52 ++++ .../filter/demos/new-demos/default-value.tsx | 84 ++++++ .../antd/filter/demos/new-demos/options.ts | 65 +++++ .../demos/new-demos/with-data-block.tsx | 73 +++++ .../src/schema-component/antd/filter/index.md | 41 +-- .../antd/form-item/FormItem.tsx | 6 +- .../antd/form-item/demos/new-demos/basic.tsx | 44 +++ .../schema-component/antd/form-item/index.md | 15 +- .../schema-component/antd/form-v2/Form.tsx | 7 +- .../antd/form-v2/demos/new-demos/basic.tsx | 69 +++++ .../demos/new-demos/collection-data.tsx | 89 ++++++ .../form-v2/demos/new-demos/collection.tsx | 83 ++++++ .../form-v2/demos/new-demos/default-value.tsx | 56 ++++ .../demos/new-demos/extend-collection.tsx | 109 +++++++ .../form-v2/demos/new-demos/form-layout.tsx | 42 +++ .../antd/form-v2/demos/new-demos/grid.tsx | 73 +++++ .../antd/form-v2/demos/new-demos/linkage.tsx | 31 ++ .../form-v2/demos/new-demos/read-pretty.tsx | 52 ++++ .../schema-component/antd/form-v2/index.md | 88 ++++-- .../src/schema-component/antd/form/index.md | 6 - .../src/schema-component/antd/g2plot/index.md | 8 +- .../antd/grid-card/GridCard.tsx | 14 +- .../antd/grid-card/demos/basic.tsx | 45 +++ .../schema-component/antd/grid-card/index.md | 29 +- .../src/schema-component/antd/grid/Grid.tsx | 10 +- .../antd/grid/demos/new-demos/basic.tsx | 105 +++++++ .../src/schema-component/antd/grid/index.md | 25 +- .../antd/icon-picker/IconPicker.tsx | 17 +- .../icon-picker/demos/new-demos/basic.tsx | 20 ++ .../demos/new-demos/read-pretty.tsx | 22 ++ .../antd/icon-picker/index.md | 30 +- .../client/src/schema-component/antd/index.ts | 5 +- .../antd/input-number/InputNumber.tsx | 8 +- .../antd/input-number/ReadPretty.tsx | 9 +- .../new-demos/addonBefore-addonAfter.tsx | 24 ++ .../input-number/demos/new-demos/basic.tsx | 20 ++ .../demos/new-demos/format-style.tsx | 25 ++ .../demos/new-demos/read-pretty.tsx | 26 ++ .../demos/new-demos/separator.tsx | 53 ++++ .../demos/new-demos/unit-conversion.tsx | 36 +++ .../antd/input-number/index.md | 60 +++- .../src/schema-component/antd/input/Input.tsx | 12 +- .../antd/input/ReadPretty.tsx | 79 ++++- .../demos/new-demos/input-read-pretty.tsx | 37 +++ .../antd/input/demos/new-demos/input.tsx | 20 ++ .../demos/new-demos/json-read-pretty.tsx | 22 ++ .../antd/input/demos/new-demos/json.tsx | 20 ++ .../demos/new-demos/textarea-read-pretty.tsx | 51 ++++ .../antd/input/demos/new-demos/textarea.tsx | 20 ++ .../input/demos/new-demos/url-read-pretty.tsx | 22 ++ .../antd/input/demos/new-demos/url.tsx | 20 ++ .../src/schema-component/antd/input/index.md | 119 ++++++-- .../antd/list/demos/basic.tsx | 54 ++++ .../antd/list/demos/view-or-edit-record.tsx | 244 ++++++++++++++++ .../antd/list/demos/with-action.tsx | 105 +++++++ .../src/schema-component/antd/list/index.md | 29 +- .../antd/markdown/Markdown.Void.tsx | 11 +- .../antd/markdown/demos/new-demos/basic.tsx | 20 ++ .../markdown/demos/new-demos/read-pretty.tsx | 22 ++ .../schema-component/antd/markdown/index.md | 36 ++- .../src/schema-component/antd/menu/index.md | 6 - .../NanoIDInput.tsx | 2 +- .../__tests__/NanoIDInput.test.tsx | 0 .../antd/nanoid-input/demos/basic.tsx | 21 ++ .../antd/nanoid-input/demos/read-pretty.tsx | 22 ++ .../antd/nanoid-input/index.md | 19 ++ .../{nanoIDInput => nanoid-input}/index.tsx | 0 .../src/schema-component/antd/page/index.md | 6 - .../antd/pagination/demos/basic.tsx | 6 + .../antd/pagination/demos/hide.tsx | 6 + .../schema-component/antd/pagination/index.md | 18 ++ .../antd/pagination/index.tsx | 8 +- .../antd/password/Password.tsx | 8 +- .../antd/password/demos/new-demos/basic.tsx | 20 ++ .../demos/new-demos/checkStrength.tsx | 23 ++ .../password/demos/new-demos/read-pretty.tsx | 22 ++ .../schema-component/antd/password/index.md | 32 ++- .../antd/percent/demos/new-demos/basic.tsx | 20 ++ .../percent/demos/new-demos/read-pretty.tsx | 22 ++ .../schema-component/antd/percent/index.md | 23 +- .../antd/preview/demos/demo1.tsx | 2 +- .../schema-component/antd/preview/index.md | 17 +- .../antd/quick-edit/QuickEdit.tsx | 14 +- .../quick-edit/__tests__/QuickEdit.test.tsx | 2 +- .../antd/quick-edit/demos/basic.tsx | 39 +++ .../antd/quick-edit/demos/read-pretty.tsx | 36 +++ .../antd/quick-edit/demos/subtable.tsx | 76 +++++ .../schema-component/antd/quick-edit/index.md | 27 ++ .../radio/demos/new-demos/group-basic.tsx | 32 +++ .../new-demos/group-read-pretty-color.tsx | 35 +++ .../demos/new-demos/group-read-pretty.tsx | 34 +++ .../radio/demos/new-demos/radio-basic.tsx | 20 ++ .../demos/new-demos/radio-read-pretty.tsx | 29 ++ .../src/schema-component/antd/radio/index.md | 46 ++- .../antd/record-picker/index.md | 10 +- .../antd/remote-select/ReadPretty.tsx | 9 +- .../antd/remote-select/RemoteSelect.tsx | 21 +- .../antd/remote-select/demos/demo1.tsx | 1 - .../remote-select/demos/new-demos/basic.tsx | 45 +++ .../remote-select/demos/new-demos/manual.tsx | 46 +++ .../demos/new-demos/multiple.tsx | 46 +++ .../demos/new-demos/read-pretty.tsx | 47 +++ .../antd/remote-select/index.md | 64 +++-- .../antd/rich-text/demos/new-demos/basic.tsx | 20 ++ .../rich-text/demos/new-demos/read-pretty.tsx | 23 ++ .../schema-component/antd/rich-text/index.md | 28 +- .../antd/select/ReadPretty.tsx | 15 +- .../schema-component/antd/select/Select.tsx | 21 +- .../antd/select/demos/new-demos/basic.tsx | 34 +++ .../select/demos/new-demos/fieldNames.tsx | 41 +++ .../antd/select/demos/new-demos/multiple.tsx | 37 +++ .../select/demos/new-demos/object-value.tsx | 37 +++ .../select/demos/new-demos/read-pretty.tsx | 86 ++++++ .../src/schema-component/antd/select/index.md | 82 ++++-- .../src/schema-component/antd/select/utils.ts | 8 +- .../schema-component/antd/table-v2/Table.tsx | 2 +- .../demos/new-demos/action-toolbar.tsx | 150 ++++++++++ .../antd/table-v2/demos/new-demos/basic.tsx | 53 ++++ .../table-v2/demos/new-demos/collection.tsx | 73 +++++ .../demos/new-demos/extend-collection.tsx | 159 ++++++++++ .../antd/table-v2/demos/new-demos/record.tsx | 272 ++++++++++++++++++ .../schema-component/antd/table-v2/index.md | 47 ++- .../src/schema-component/antd/table/index.md | 6 - .../antd/tabs/demos/demo1.tsx | 10 +- .../src/schema-component/antd/tabs/index.md | 11 +- .../antd/time-picker/ReadPretty.tsx | 11 +- .../new-demos/time-range-read-pretty.tsx | 22 ++ .../demos/new-demos/time-range.tsx | 20 ++ .../demos/new-demos/time-read-pretty.tsx | 22 ++ .../antd/time-picker/demos/new-demos/time.tsx | 20 ++ .../antd/time-picker/index.md | 50 +++- .../tree-select/demos/new-demos/basic.tsx | 62 ++++ .../demos/new-demos/read-pretty.tsx | 64 +++++ .../antd/tree-select/index.md | 20 +- .../UnixTimestamp.tsx | 8 +- .../__tests__/UnixTimestamp.test.tsx | 0 .../antd/unix-timestamp/demos/basic.tsx | 20 ++ .../antd/unix-timestamp/demos/read-pretty.tsx | 32 +++ .../antd/unix-timestamp/demos/second.tsx | 23 ++ .../antd/unix-timestamp/index.md | 20 ++ .../index.tsx | 0 .../antd/upload/demos/new-demos/basic.tsx | 43 +++ .../antd/upload/demos/new-demos/multiple.tsx | 44 +++ .../upload/demos/new-demos/read-pretty.tsx | 68 +++++ .../src/schema-component/antd/upload/index.md | 33 ++- .../schema-component/antd/variable/Input.tsx | 18 +- .../antd/variable/demos/demo1.tsx | 11 - .../antd/variable/demos/demo2.tsx | 11 - .../antd/variable/demos/demo3.tsx | 11 - .../schema-component/antd/variable/index.md | 27 +- .../common/dnd-context/index.md | 6 - .../common/dnd-context/index.tsx | 2 + .../common/sortable-item/index.md | 6 - packages/core/test/src/client/utils/utils.ts | 4 +- .../core/test/src/web/dataSourceMainData.json | 101 +++++++ packages/core/test/src/web/index.tsx | 51 +++- packages/core/utils/src/date.ts | 23 +- .../src/client/calendar/index.md | 6 - .../plugin-gantt/src/client/index.md | 5 +- .../plugin-kanban/src/client/index.md | 6 - .../src/client/__e2e__/schemaSettings.test.ts | 1 + 285 files changed, 9613 insertions(+), 888 deletions(-) create mode 100644 packages/core/client/docs/en-US/core/request/demos/demo1.tsx create mode 100644 packages/core/client/docs/en-US/core/request/demos/demo2.tsx create mode 100644 packages/core/client/docs/en-US/core/request/demos/demo3.tsx create mode 100644 packages/core/client/docs/en-US/core/request/index.md create mode 100644 packages/core/client/docs/zh-CN/core/request/demos/demo1.tsx create mode 100644 packages/core/client/docs/zh-CN/core/request/demos/demo2.tsx create mode 100644 packages/core/client/docs/zh-CN/core/request/demos/demo3.tsx create mode 100644 packages/core/client/docs/zh-CN/core/request/index.md create mode 100644 packages/core/client/src/modules/blocks/data-blocks/details-multi/hooks/index.ts create mode 100644 packages/core/client/src/modules/blocks/data-blocks/details-multi/index.ts create mode 100644 packages/core/client/src/modules/blocks/data-blocks/details-single/hooks/index.ts create mode 100644 packages/core/client/src/modules/blocks/data-blocks/details-single/index.ts create mode 100644 packages/core/client/src/modules/blocks/index.ts delete mode 100644 packages/core/client/src/schema-component/antd/action/Action.Popover.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/action-container.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/action-context.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/action-link.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/action-modal.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/action-popover.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/actionbar-one-column.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/actionbar-two-columns.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/confirm.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/custom-component.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-footer.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-openSize.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-with-form.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/dynamic-props.tsx create mode 100644 packages/core/client/src/schema-component/antd/action/demos/new-demos/schema-toolbar.tsx create mode 100644 packages/core/client/src/schema-component/antd/association-select/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/association-select/demos/new-demos/multiple.tsx create mode 100644 packages/core/client/src/schema-component/antd/association-select/demos/new-demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/auto-complete/demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/auto-complete/demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/auto-complete/index.md create mode 100644 packages/core/client/src/schema-component/antd/block-item/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/card-item/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/card-item/demos/new-demos/lazy-render.tsx create mode 100644 packages/core/client/src/schema-component/antd/cascader/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/cascader/demos/new-demos/changeOnSelectLast.tsx create mode 100644 packages/core/client/src/schema-component/antd/cascader/demos/new-demos/labelInValue.tsx create mode 100644 packages/core/client/src/schema-component/antd/cascader/demos/new-demos/loadData.tsx create mode 100644 packages/core/client/src/schema-component/antd/cascader/demos/new-demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/group-read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/group.tsx create mode 100644 packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/collection-select/demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/collection-select/demos/multiple.tsx create mode 100644 packages/core/client/src/schema-component/antd/collection-select/demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/color-picker/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/color-picker/demos/new-demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/color-select/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/color-select/demos/new-demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/cron/demos/new-demos/cron-basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/cron/demos/new-demos/cron-read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/cron/demos/new-demos/cronset-basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/cron/demos/new-demos/cronset-read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/new-demos/date-basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/new-demos/date-format.tsx create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/new-demos/date-gmt-utc.tsx create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/new-demos/date-gmt.tsx create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/new-demos/date-read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/new-demos/date-show-time.tsx create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/new-demos/date-utc.tsx create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/new-demos/range-basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/new-demos/range-format.tsx create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/new-demos/range-gmt-utc.tsx create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/new-demos/range-gmt.tsx create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/new-demos/range-read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/date-picker/demos/new-demos/range-utc.tsx create mode 100644 packages/core/client/src/schema-component/antd/details/demos/new-demos/list.tsx create mode 100644 packages/core/client/src/schema-component/antd/details/demos/new-demos/single.tsx create mode 100644 packages/core/client/src/schema-component/antd/error-fallback/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/filter/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/filter/demos/new-demos/collection-fields.tsx create mode 100644 packages/core/client/src/schema-component/antd/filter/demos/new-demos/default-value.tsx create mode 100644 packages/core/client/src/schema-component/antd/filter/demos/new-demos/options.ts create mode 100644 packages/core/client/src/schema-component/antd/filter/demos/new-demos/with-data-block.tsx create mode 100644 packages/core/client/src/schema-component/antd/form-item/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/form-v2/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/form-v2/demos/new-demos/collection-data.tsx create mode 100644 packages/core/client/src/schema-component/antd/form-v2/demos/new-demos/collection.tsx create mode 100644 packages/core/client/src/schema-component/antd/form-v2/demos/new-demos/default-value.tsx create mode 100644 packages/core/client/src/schema-component/antd/form-v2/demos/new-demos/extend-collection.tsx create mode 100644 packages/core/client/src/schema-component/antd/form-v2/demos/new-demos/form-layout.tsx create mode 100644 packages/core/client/src/schema-component/antd/form-v2/demos/new-demos/grid.tsx create mode 100644 packages/core/client/src/schema-component/antd/form-v2/demos/new-demos/linkage.tsx create mode 100644 packages/core/client/src/schema-component/antd/form-v2/demos/new-demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/grid-card/demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/grid/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/icon-picker/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/icon-picker/demos/new-demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/input-number/demos/new-demos/addonBefore-addonAfter.tsx create mode 100644 packages/core/client/src/schema-component/antd/input-number/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/input-number/demos/new-demos/format-style.tsx create mode 100644 packages/core/client/src/schema-component/antd/input-number/demos/new-demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/input-number/demos/new-demos/separator.tsx create mode 100644 packages/core/client/src/schema-component/antd/input-number/demos/new-demos/unit-conversion.tsx create mode 100644 packages/core/client/src/schema-component/antd/input/demos/new-demos/input-read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/input/demos/new-demos/input.tsx create mode 100644 packages/core/client/src/schema-component/antd/input/demos/new-demos/json-read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/input/demos/new-demos/json.tsx create mode 100644 packages/core/client/src/schema-component/antd/input/demos/new-demos/textarea-read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/input/demos/new-demos/textarea.tsx create mode 100644 packages/core/client/src/schema-component/antd/input/demos/new-demos/url-read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/input/demos/new-demos/url.tsx create mode 100644 packages/core/client/src/schema-component/antd/list/demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/list/demos/view-or-edit-record.tsx create mode 100644 packages/core/client/src/schema-component/antd/list/demos/with-action.tsx create mode 100644 packages/core/client/src/schema-component/antd/markdown/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/markdown/demos/new-demos/read-pretty.tsx rename packages/core/client/src/schema-component/antd/{nanoIDInput => nanoid-input}/NanoIDInput.tsx (99%) rename packages/core/client/src/schema-component/antd/{nanoIDInput => nanoid-input}/__tests__/NanoIDInput.test.tsx (100%) create mode 100644 packages/core/client/src/schema-component/antd/nanoid-input/demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/nanoid-input/demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/nanoid-input/index.md rename packages/core/client/src/schema-component/antd/{nanoIDInput => nanoid-input}/index.tsx (100%) create mode 100644 packages/core/client/src/schema-component/antd/pagination/demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/pagination/demos/hide.tsx create mode 100644 packages/core/client/src/schema-component/antd/pagination/index.md create mode 100644 packages/core/client/src/schema-component/antd/password/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/password/demos/new-demos/checkStrength.tsx create mode 100644 packages/core/client/src/schema-component/antd/password/demos/new-demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/percent/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/percent/demos/new-demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/quick-edit/demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/quick-edit/demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/quick-edit/demos/subtable.tsx create mode 100644 packages/core/client/src/schema-component/antd/quick-edit/index.md create mode 100644 packages/core/client/src/schema-component/antd/radio/demos/new-demos/group-basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/radio/demos/new-demos/group-read-pretty-color.tsx create mode 100644 packages/core/client/src/schema-component/antd/radio/demos/new-demos/group-read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/radio/demos/new-demos/radio-basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/radio/demos/new-demos/radio-read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/remote-select/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/remote-select/demos/new-demos/manual.tsx create mode 100644 packages/core/client/src/schema-component/antd/remote-select/demos/new-demos/multiple.tsx create mode 100644 packages/core/client/src/schema-component/antd/remote-select/demos/new-demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/rich-text/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/rich-text/demos/new-demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/select/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/select/demos/new-demos/fieldNames.tsx create mode 100644 packages/core/client/src/schema-component/antd/select/demos/new-demos/multiple.tsx create mode 100644 packages/core/client/src/schema-component/antd/select/demos/new-demos/object-value.tsx create mode 100644 packages/core/client/src/schema-component/antd/select/demos/new-demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/table-v2/demos/new-demos/action-toolbar.tsx create mode 100644 packages/core/client/src/schema-component/antd/table-v2/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/table-v2/demos/new-demos/collection.tsx create mode 100644 packages/core/client/src/schema-component/antd/table-v2/demos/new-demos/extend-collection.tsx create mode 100644 packages/core/client/src/schema-component/antd/table-v2/demos/new-demos/record.tsx create mode 100644 packages/core/client/src/schema-component/antd/time-picker/demos/new-demos/time-range-read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/time-picker/demos/new-demos/time-range.tsx create mode 100644 packages/core/client/src/schema-component/antd/time-picker/demos/new-demos/time-read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/time-picker/demos/new-demos/time.tsx create mode 100644 packages/core/client/src/schema-component/antd/tree-select/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/tree-select/demos/new-demos/read-pretty.tsx rename packages/core/client/src/schema-component/antd/{unixTimestamp => unix-timestamp}/UnixTimestamp.tsx (90%) rename packages/core/client/src/schema-component/antd/{unixTimestamp => unix-timestamp}/__tests__/UnixTimestamp.test.tsx (100%) create mode 100644 packages/core/client/src/schema-component/antd/unix-timestamp/demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/unix-timestamp/demos/read-pretty.tsx create mode 100644 packages/core/client/src/schema-component/antd/unix-timestamp/demos/second.tsx create mode 100644 packages/core/client/src/schema-component/antd/unix-timestamp/index.md rename packages/core/client/src/schema-component/antd/{unixTimestamp => unix-timestamp}/index.tsx (100%) create mode 100644 packages/core/client/src/schema-component/antd/upload/demos/new-demos/basic.tsx create mode 100644 packages/core/client/src/schema-component/antd/upload/demos/new-demos/multiple.tsx create mode 100644 packages/core/client/src/schema-component/antd/upload/demos/new-demos/read-pretty.tsx diff --git a/.github/workflows/nocobase-test-backend.yml b/.github/workflows/nocobase-test-backend.yml index 0224ac2dd1..cb34991e22 100644 --- a/.github/workflows/nocobase-test-backend.yml +++ b/.github/workflows/nocobase-test-backend.yml @@ -68,7 +68,7 @@ jobs: DB_STORAGE: /tmp/db.sqlite DB_TEST_PREFIX: test_ DB_UNDERSCORED: ${{ matrix.underscored }} - timeout-minutes: 40 + timeout-minutes: 60 postgres-test: strategy: @@ -132,7 +132,7 @@ jobs: COLLECTION_MANAGER_SCHEMA: ${{ matrix.collection_schema }} DB_TEST_DISTRIBUTOR_PORT: 23450 DB_TEST_PREFIX: test_ - timeout-minutes: 40 + timeout-minutes: 60 mysql-test: strategy: @@ -183,7 +183,7 @@ jobs: DB_UNDERSCORED: ${{ matrix.underscored }} DB_TEST_DISTRIBUTOR_PORT: 23450 DB_TEST_PREFIX: test_ - timeout-minutes: 40 + timeout-minutes: 60 mariadb-test: strategy: matrix: @@ -233,4 +233,4 @@ jobs: DB_UNDERSCORED: ${{ matrix.underscored }} DB_TEST_DISTRIBUTOR_PORT: 23450 DB_TEST_PREFIX: test_ - timeout-minutes: 40 + timeout-minutes: 60 diff --git a/packages/core/client/.dumirc.ts b/packages/core/client/.dumirc.ts index ebb48d33a9..6c38d3e7fa 100644 --- a/packages/core/client/.dumirc.ts +++ b/packages/core/client/.dumirc.ts @@ -1,5 +1,3 @@ -import path from 'path'; -import glob from 'glob'; import _ from 'lodash' import { getUmiConfig } from '@nocobase/devtools/umiConfig'; import { defineConfig } from 'dumi'; @@ -11,19 +9,6 @@ const lang = process.env.DOC_LANG; console.log('process.env.DOC_LANG', lang); -const componentsDir = 'src/schema-component/antd'; - -function getComponentsMenu() { - const cwd = path.join(__dirname, componentsDir); - const ignores = ['table/index.md', 'form/index.md']; // 老版本,不需要展示 - const files = glob.sync('*/index.md', { cwd, ignore: ignores }); - - return files.map((file) => ({ - title: _.upperFirst(_.camelCase(file.replace('/index.md', ''))), - link: `/components/${file.replace('/index.md', '')}`, - })); -} - export default defineConfig({ hash: true, alias: { @@ -39,7 +24,7 @@ export default defineConfig({ resolve: { docDirs: [`./docs/${lang}`], atomDirs: [ - { type: 'component', dir: componentsDir }, + { type: 'component', dir: 'src/schema-component/antd' }, ], }, locales: [ @@ -92,6 +77,10 @@ export default defineConfig({ title: 'PluginSettingsManager', link: '/core/application/plugin-settings-manager', }, + { + title: 'Request', + link: '/core/request', + }, ], }, { @@ -225,7 +214,196 @@ export default defineConfig({ ] } ], - '/components': getComponentsMenu(), + '/components': [ + { + title: 'Action', + type: 'group', + children: [ + { + "title": "Action", + "link": "/components/action" + }, + { + "title": "Filter", + "link": "/components/filter" + }, + ] + }, + { + title: 'Field', + type: 'group', + children: [ + { + "title": "Checkbox", + "link": "/components/checkbox" + }, + { + "title": "Cascader", + "link": "/components/cascader" + }, + { + "title": "ColorPicker", + "link": "/components/color-picker" + }, + { + "title": "ColorSelect", + "link": "/components/color-select" + }, + { + "title": "DatePicker", + "link": "/components/date-picker" + }, + { + "title": "TimePicker", + "link": "/components/time-picker" + }, + { + "title": "IconPicker", + "link": "/components/icon-picker" + }, + { + "title": "InputNumber", + "link": "/components/input-number" + }, + { + "title": "Input", + "link": "/components/input" + }, + { + "title": "AutoComplete", + "link": "/components/auto-complete" + }, + { + "title": "NanoIDInput", + "link": "/components/nanoid-input" + }, + { + "title": "Password", + "link": "/components/password" + }, + { + "title": "Percent", + "link": "/components/percent" + }, + { + "title": "Radio", + "link": "/components/radio" + }, + { + "title": "Select", + "link": "/components/select" + }, + { + "title": "RemoteSelect", + "link": "/components/remote-select" + }, + { + "title": "TreeSelect", + "link": "/components/tree-select" + }, + { + "title": "Upload", + "link": "/components/upload" + }, + { + "title": "CollectionSelect", + "link": "/components/collection-select" + }, + { + "title": "Cron", + "link": "/components/cron" + }, + { + "title": "Markdown", + "link": "/components/markdown" + }, + { + "title": "Variable", + "link": "/components/variable" + }, + { + "title": "QuickEdit", + "link": "/components/quick-edit" + }, + { + "title": "RichText", + "link": "/components/rich-text" + } + ] + }, + { + title: 'Block', + type: 'group', + children: [ + { + "title": "BlockItem", + "link": "/components/block-item" + }, + { + "title": "CardItem", + "link": "/components/card-item" + }, + { + "title": "FormItem", + "link": "/components/form-item" + }, + { + "title": "FormV2", + "link": "/components/form-v2" + }, + { + "title": "TableV2", + "link": "/components/table-v2" + }, + { + "title": "Details", + "link": "/components/details" + }, + { + "title": "GridCard", + "link": "/components/grid-card" + }, + { + "title": "Grid", + "link": "/components/grid" + }, + { + "title": "List", + "link": "/components/list" + }, + ] + }, + { + title: 'Others', + type: 'group', + children: [ + { + "title": "Tabs", + "link": "/components/tabs" + }, + { + "title": "ErrorFallback", + "link": "/components/error-fallback" + }, + { + "title": "G2Plot", + "link": "/components/g2plot" + }, + { + "title": "Menu", + "link": "/components/menu" + }, + { + "title": "Pagination", + "link": "/components/pagination" + }, + { + "title": "Preview", + "link": "/components/preview" + }, + ] + }, + ] // '/ui-schema': [ // { // title: 'Overview', diff --git a/packages/core/client/docs/en-US/core/data-block/data-block-provider.md b/packages/core/client/docs/en-US/core/data-block/data-block-provider.md index 6aeaec0492..1ffc718afd 100644 --- a/packages/core/client/docs/en-US/core/data-block/data-block-provider.md +++ b/packages/core/client/docs/en-US/core/data-block/data-block-provider.md @@ -37,7 +37,7 @@ Table 中的字段信息及列表数据,都是存储在数据库中的。 - `DataBlockProvider`:封装了下面的所有组件,并提供了区块属性 - [CollectionProvider](/core/data-source/collection-provider) / [AssociationProvider](/core/data-source/association-provider): 根据 `DataBlockProvider` 提供的上下文信息,查询对应数据表数据及关系字段信息并传递 - - [BlockResourceProvider](/core/data-block/data-block-resource-provider): 根据 `DataBlockProvider` 提供的上下文信息,构建区块 [Resource](https://docs.nocobase.com/api/sdk#resource-action) API,用于区块数据的增删改查 + - [BlockResourceProvider](/core/data-block/data-block-resource-provider): 根据 `DataBlockProvider` 提供的上下文信息,构建区块 [Resource](/core/request) API,用于区块数据的增删改查 - [BlockRequestProvider](/core/data-block/data-block-request-provider): 根据 `DataBlockProvider` 提供的上下文信息,自动调用 `BlockResourceProvider` 提供的 `resource.get()` 或 `resource.list()` 发起请求,得到区块数据,并传递 - [CollectionRecordProvider](/core/data-source/record-provider): 对于 `resource.get()` 场景,会自动嵌套 `CollectionRecordProvider` 并将 `resource.get()` 请求结果传递下去,`resource.list()` 场景则需要自行使用 `CollectionRecordProvider` 提供数据记录 diff --git a/packages/core/client/docs/en-US/core/data-block/data-block-resource-provider.md b/packages/core/client/docs/en-US/core/data-block/data-block-resource-provider.md index e94246b6c7..50178dc86f 100644 --- a/packages/core/client/docs/en-US/core/data-block/data-block-resource-provider.md +++ b/packages/core/client/docs/en-US/core/data-block/data-block-resource-provider.md @@ -1,6 +1,6 @@ # DataBlockResourceProvider -根据 `DataBlockProvider` 中的 `collection`、`association`、`sourceId` 等属性,构建好 [resource](https://docs.nocobase.com/api/sdk#resource-action) 对象,方便子组件对区块数据的增删改查操作,其内置在 [DataBlockProvider](/core/data-block/data-block-provider) 中 +根据 `DataBlockProvider` 中的 `collection`、`association`、`sourceId` 等属性,构建好 [resource](/core/request) 对象,方便子组件对区块数据的增删改查操作,其内置在 [DataBlockProvider](/core/data-block/data-block-provider) 中 ## useDataBlockResource diff --git a/packages/core/client/docs/en-US/core/request/demos/demo1.tsx b/packages/core/client/docs/en-US/core/request/demos/demo1.tsx new file mode 100644 index 0000000000..dbc18123d2 --- /dev/null +++ b/packages/core/client/docs/en-US/core/request/demos/demo1.tsx @@ -0,0 +1,24 @@ +import { APIClient, APIClientProvider, compose, useRequest } from '@nocobase/client'; +import MockAdapter from 'axios-mock-adapter'; +import React from 'react'; + +const apiClient = new APIClient(); + +const mock = new MockAdapter(apiClient.axios); + +mock.onGet('/users:get').reply(200, { + data: { id: 1, name: 'John Smith' }, +}); + +const providers = [[APIClientProvider, { apiClient }]]; + +export default compose(...providers)(() => { + const { data } = useRequest<{ + data: any; + }>({ + resource: 'users', + action: 'get', + params: {}, + }); + return
{data?.data?.name}
; +}); diff --git a/packages/core/client/docs/en-US/core/request/demos/demo2.tsx b/packages/core/client/docs/en-US/core/request/demos/demo2.tsx new file mode 100644 index 0000000000..823a5dbb41 --- /dev/null +++ b/packages/core/client/docs/en-US/core/request/demos/demo2.tsx @@ -0,0 +1,23 @@ +import { APIClient, APIClientProvider, compose, useRequest } from '@nocobase/client'; +import MockAdapter from 'axios-mock-adapter'; +import React from 'react'; + +const apiClient = new APIClient(); + +const mock = new MockAdapter(apiClient.axios); + +mock.onGet('/users:get').reply(200, { + data: { id: 1, name: 'John Smith' }, +}); + +const providers = [[APIClientProvider, { apiClient }]]; + +export default compose(...providers)(() => { + const { data } = useRequest<{ + data: any; + }>({ + url: 'users:get', + method: 'get', + }); + return
{data?.data?.name}
; +}); diff --git a/packages/core/client/docs/en-US/core/request/demos/demo3.tsx b/packages/core/client/docs/en-US/core/request/demos/demo3.tsx new file mode 100644 index 0000000000..d27bf0470f --- /dev/null +++ b/packages/core/client/docs/en-US/core/request/demos/demo3.tsx @@ -0,0 +1,69 @@ +import { uid } from '@formily/shared'; +import { APIClient, APIClientProvider, useAPIClient, useRequest } from '@nocobase/client'; +import { Button, Input, Space, Table } from 'antd'; +import MockAdapter from 'axios-mock-adapter'; +import React from 'react'; + +const apiClient = new APIClient(); + +const mock = new MockAdapter(apiClient.axios); +const sleep = (value: number) => new Promise((resolve) => setTimeout(resolve, value)); + +mock.onGet('/users:list').reply(async () => { + await sleep(1000); + return [ + 200, + { + data: [ + { id: 1, name: uid() }, + { id: 2, name: uid() }, + ], + }, + ]; +}); + +const ComponentA = () => { + console.log('ComponentA'); + const { data, loading } = useRequest<{ + data: any; + }>( + { + url: 'users:list', + method: 'get', + }, + { + uid: 'test', // 当指定了 uid 的 useRequest 的结果,可以通过 api.service(uid) 获取 + }, + ); + return ( + + ); +}; + +const ComponentB = () => { + console.log('ComponentB'); + const apiClient = useAPIClient(); + return ( + + + + + ); +}; + +export default () => { + return ( + + +
+
+ +
+ ); +}; diff --git a/packages/core/client/docs/en-US/core/request/index.md b/packages/core/client/docs/en-US/core/request/index.md new file mode 100644 index 0000000000..d442a20194 --- /dev/null +++ b/packages/core/client/docs/en-US/core/request/index.md @@ -0,0 +1,167 @@ +# APIClient + +## APIClient + +```ts +class APIClient { + // axios 实例 + axios: AxiosInstance; + // 缓存带 uid 的 useRequest({}, {uid}) 的结果,可供其他组件调用 + services: Record>; + // 构造器 + constructor(instance?: AxiosInstance | AxiosRequestConfig); + // 客户端请求,支持 AxiosRequestConfig 和 ResourceActionOptions + request, D = any>(config: AxiosRequestConfig | ResourceActionOptions): Promise; + // 获取资源 + resource(name: string, of?: any): R; +} +``` + +示例 + +```ts +import axios from 'axios'; + +// 不传参时,内部直接创建 axios 实例 +const apiClient = new APIClient(); + +// 提供 AxiosRequestConfig 配置参数 +const apiClient = new APIClient({ + baseURL: '', +}); + +// 提供 AxiosInstance +const instance = axios.create({ + baseURL: '', +}); +const apiClient = new APIClient(instance); + +// 常规请求 +const response = await apiClient.request({ url }); + +// NocoBase 特有的资源操作 +const response = await apiClient.resource('posts').list(); + +// 请求共享 +const { data, loading, run } = apiClient.service('uid'); +``` + +`api.service(uid)` 的例子,ComponentB 里刷新 ComponentA 的请求数据 + + + +## APIClientProvider + +提供 APIClient 实例的上下文。 + +```tsx | pure +const apiClient = new APIClient(); + + +``` + +## useAPIClient + +获取当前上下文的 APIClient 实例。 + +```ts +const apiClient = useAPIClient(); +``` + +## useRequest + +```ts +function useRequest

( + service: AxiosRequestConfig

| ResourceActionOptions

| FunctionService, + options?: Options, +); +``` + +支持 `axios.request(config)`,config 详情查看 [axios](https://github.com/axios/axios#request-config) + +```ts +const { data, loading, refresh, run, params } = useRequest({ url: '/users' }); + +// useRequest 里传的是 AxiosRequestConfig,所以 run 里传的也是 AxiosRequestConfig +run({ + params: { + pageSize: 20, + } +}); +``` + +例子如下: + + + +或者是 NocoBase 的 resource & action 请求: + +```ts +const { data, run } = useRequest({ + resource: 'users', + action: 'list', + params: { + pageSize: 20, + }, +}); + +// useRequest 传的是 ResourceActionOptions,所以 run 直接传 action params 就可以了。 +run({ + pageSize: 50, +}); +``` + +例子如下: + + + +也可以是自定义的异步函数: + +```ts +const { data, loading, run, refresh, params } = useRequest((...params) => Promise.resolve({})); + +run(...params); +``` + +更多用法查看 ahooks 的 [useRequest()](https://ahooks.js.org/hooks/use-request/index) + +## useResource + +```ts +function useResource(name: string, of?: string | number): IResource; +``` + +资源是 NocoBase 的核心概念,包括: + +- 独立资源,如 `posts` +- 关系资源,如 `posts.tags` `posts.user` `posts.comments` + +资源 URI + +```bash +# 独立资源,文章 +/api/posts +# 关系资源,文章 ID=1 的评论 +/api/posts/1/comments +``` + +通过 APIClient 获取资源 + +```ts +const api = new APIClient(); + +api.resource('posts'); +api.resource('posts.comments', 1); +``` + +useResource 用法: + +```ts +const resource = useResource('posts'); +const resource = useResource('posts.comments', 1); +``` + +resource 的实际场景用例参见: + +- [useCollection()](collection-manager#usecollection) +- [useCollectionField()](collection-manager#usecollectionfield) diff --git a/packages/core/client/docs/zh-CN/core/data-block/data-block-provider.md b/packages/core/client/docs/zh-CN/core/data-block/data-block-provider.md index 6aeaec0492..1ffc718afd 100644 --- a/packages/core/client/docs/zh-CN/core/data-block/data-block-provider.md +++ b/packages/core/client/docs/zh-CN/core/data-block/data-block-provider.md @@ -37,7 +37,7 @@ Table 中的字段信息及列表数据,都是存储在数据库中的。 - `DataBlockProvider`:封装了下面的所有组件,并提供了区块属性 - [CollectionProvider](/core/data-source/collection-provider) / [AssociationProvider](/core/data-source/association-provider): 根据 `DataBlockProvider` 提供的上下文信息,查询对应数据表数据及关系字段信息并传递 - - [BlockResourceProvider](/core/data-block/data-block-resource-provider): 根据 `DataBlockProvider` 提供的上下文信息,构建区块 [Resource](https://docs.nocobase.com/api/sdk#resource-action) API,用于区块数据的增删改查 + - [BlockResourceProvider](/core/data-block/data-block-resource-provider): 根据 `DataBlockProvider` 提供的上下文信息,构建区块 [Resource](/core/request) API,用于区块数据的增删改查 - [BlockRequestProvider](/core/data-block/data-block-request-provider): 根据 `DataBlockProvider` 提供的上下文信息,自动调用 `BlockResourceProvider` 提供的 `resource.get()` 或 `resource.list()` 发起请求,得到区块数据,并传递 - [CollectionRecordProvider](/core/data-source/record-provider): 对于 `resource.get()` 场景,会自动嵌套 `CollectionRecordProvider` 并将 `resource.get()` 请求结果传递下去,`resource.list()` 场景则需要自行使用 `CollectionRecordProvider` 提供数据记录 diff --git a/packages/core/client/docs/zh-CN/core/data-block/data-block-resource-provider.md b/packages/core/client/docs/zh-CN/core/data-block/data-block-resource-provider.md index e94246b6c7..50178dc86f 100644 --- a/packages/core/client/docs/zh-CN/core/data-block/data-block-resource-provider.md +++ b/packages/core/client/docs/zh-CN/core/data-block/data-block-resource-provider.md @@ -1,6 +1,6 @@ # DataBlockResourceProvider -根据 `DataBlockProvider` 中的 `collection`、`association`、`sourceId` 等属性,构建好 [resource](https://docs.nocobase.com/api/sdk#resource-action) 对象,方便子组件对区块数据的增删改查操作,其内置在 [DataBlockProvider](/core/data-block/data-block-provider) 中 +根据 `DataBlockProvider` 中的 `collection`、`association`、`sourceId` 等属性,构建好 [resource](/core/request) 对象,方便子组件对区块数据的增删改查操作,其内置在 [DataBlockProvider](/core/data-block/data-block-provider) 中 ## useDataBlockResource diff --git a/packages/core/client/docs/zh-CN/core/request/demos/demo1.tsx b/packages/core/client/docs/zh-CN/core/request/demos/demo1.tsx new file mode 100644 index 0000000000..dbc18123d2 --- /dev/null +++ b/packages/core/client/docs/zh-CN/core/request/demos/demo1.tsx @@ -0,0 +1,24 @@ +import { APIClient, APIClientProvider, compose, useRequest } from '@nocobase/client'; +import MockAdapter from 'axios-mock-adapter'; +import React from 'react'; + +const apiClient = new APIClient(); + +const mock = new MockAdapter(apiClient.axios); + +mock.onGet('/users:get').reply(200, { + data: { id: 1, name: 'John Smith' }, +}); + +const providers = [[APIClientProvider, { apiClient }]]; + +export default compose(...providers)(() => { + const { data } = useRequest<{ + data: any; + }>({ + resource: 'users', + action: 'get', + params: {}, + }); + return

{data?.data?.name}
; +}); diff --git a/packages/core/client/docs/zh-CN/core/request/demos/demo2.tsx b/packages/core/client/docs/zh-CN/core/request/demos/demo2.tsx new file mode 100644 index 0000000000..823a5dbb41 --- /dev/null +++ b/packages/core/client/docs/zh-CN/core/request/demos/demo2.tsx @@ -0,0 +1,23 @@ +import { APIClient, APIClientProvider, compose, useRequest } from '@nocobase/client'; +import MockAdapter from 'axios-mock-adapter'; +import React from 'react'; + +const apiClient = new APIClient(); + +const mock = new MockAdapter(apiClient.axios); + +mock.onGet('/users:get').reply(200, { + data: { id: 1, name: 'John Smith' }, +}); + +const providers = [[APIClientProvider, { apiClient }]]; + +export default compose(...providers)(() => { + const { data } = useRequest<{ + data: any; + }>({ + url: 'users:get', + method: 'get', + }); + return
{data?.data?.name}
; +}); diff --git a/packages/core/client/docs/zh-CN/core/request/demos/demo3.tsx b/packages/core/client/docs/zh-CN/core/request/demos/demo3.tsx new file mode 100644 index 0000000000..d27bf0470f --- /dev/null +++ b/packages/core/client/docs/zh-CN/core/request/demos/demo3.tsx @@ -0,0 +1,69 @@ +import { uid } from '@formily/shared'; +import { APIClient, APIClientProvider, useAPIClient, useRequest } from '@nocobase/client'; +import { Button, Input, Space, Table } from 'antd'; +import MockAdapter from 'axios-mock-adapter'; +import React from 'react'; + +const apiClient = new APIClient(); + +const mock = new MockAdapter(apiClient.axios); +const sleep = (value: number) => new Promise((resolve) => setTimeout(resolve, value)); + +mock.onGet('/users:list').reply(async () => { + await sleep(1000); + return [ + 200, + { + data: [ + { id: 1, name: uid() }, + { id: 2, name: uid() }, + ], + }, + ]; +}); + +const ComponentA = () => { + console.log('ComponentA'); + const { data, loading } = useRequest<{ + data: any; + }>( + { + url: 'users:list', + method: 'get', + }, + { + uid: 'test', // 当指定了 uid 的 useRequest 的结果,可以通过 api.service(uid) 获取 + }, + ); + return ( +
+ ); +}; + +const ComponentB = () => { + console.log('ComponentB'); + const apiClient = useAPIClient(); + return ( + + + + + ); +}; + +export default () => { + return ( + + +
+
+ +
+ ); +}; diff --git a/packages/core/client/docs/zh-CN/core/request/index.md b/packages/core/client/docs/zh-CN/core/request/index.md new file mode 100644 index 0000000000..d442a20194 --- /dev/null +++ b/packages/core/client/docs/zh-CN/core/request/index.md @@ -0,0 +1,167 @@ +# APIClient + +## APIClient + +```ts +class APIClient { + // axios 实例 + axios: AxiosInstance; + // 缓存带 uid 的 useRequest({}, {uid}) 的结果,可供其他组件调用 + services: Record>; + // 构造器 + constructor(instance?: AxiosInstance | AxiosRequestConfig); + // 客户端请求,支持 AxiosRequestConfig 和 ResourceActionOptions + request, D = any>(config: AxiosRequestConfig | ResourceActionOptions): Promise; + // 获取资源 + resource(name: string, of?: any): R; +} +``` + +示例 + +```ts +import axios from 'axios'; + +// 不传参时,内部直接创建 axios 实例 +const apiClient = new APIClient(); + +// 提供 AxiosRequestConfig 配置参数 +const apiClient = new APIClient({ + baseURL: '', +}); + +// 提供 AxiosInstance +const instance = axios.create({ + baseURL: '', +}); +const apiClient = new APIClient(instance); + +// 常规请求 +const response = await apiClient.request({ url }); + +// NocoBase 特有的资源操作 +const response = await apiClient.resource('posts').list(); + +// 请求共享 +const { data, loading, run } = apiClient.service('uid'); +``` + +`api.service(uid)` 的例子,ComponentB 里刷新 ComponentA 的请求数据 + + + +## APIClientProvider + +提供 APIClient 实例的上下文。 + +```tsx | pure +const apiClient = new APIClient(); + + +``` + +## useAPIClient + +获取当前上下文的 APIClient 实例。 + +```ts +const apiClient = useAPIClient(); +``` + +## useRequest + +```ts +function useRequest

( + service: AxiosRequestConfig

| ResourceActionOptions

| FunctionService, + options?: Options, +); +``` + +支持 `axios.request(config)`,config 详情查看 [axios](https://github.com/axios/axios#request-config) + +```ts +const { data, loading, refresh, run, params } = useRequest({ url: '/users' }); + +// useRequest 里传的是 AxiosRequestConfig,所以 run 里传的也是 AxiosRequestConfig +run({ + params: { + pageSize: 20, + } +}); +``` + +例子如下: + + + +或者是 NocoBase 的 resource & action 请求: + +```ts +const { data, run } = useRequest({ + resource: 'users', + action: 'list', + params: { + pageSize: 20, + }, +}); + +// useRequest 传的是 ResourceActionOptions,所以 run 直接传 action params 就可以了。 +run({ + pageSize: 50, +}); +``` + +例子如下: + + + +也可以是自定义的异步函数: + +```ts +const { data, loading, run, refresh, params } = useRequest((...params) => Promise.resolve({})); + +run(...params); +``` + +更多用法查看 ahooks 的 [useRequest()](https://ahooks.js.org/hooks/use-request/index) + +## useResource + +```ts +function useResource(name: string, of?: string | number): IResource; +``` + +资源是 NocoBase 的核心概念,包括: + +- 独立资源,如 `posts` +- 关系资源,如 `posts.tags` `posts.user` `posts.comments` + +资源 URI + +```bash +# 独立资源,文章 +/api/posts +# 关系资源,文章 ID=1 的评论 +/api/posts/1/comments +``` + +通过 APIClient 获取资源 + +```ts +const api = new APIClient(); + +api.resource('posts'); +api.resource('posts.comments', 1); +``` + +useResource 用法: + +```ts +const resource = useResource('posts'); +const resource = useResource('posts.comments', 1); +``` + +resource 的实际场景用例参见: + +- [useCollection()](collection-manager#usecollection) +- [useCollectionField()](collection-manager#usecollectionfield) diff --git a/packages/core/client/src/api-client/index.md b/packages/core/client/src/api-client/index.md index e4c67d46d3..1804df06b2 100644 --- a/packages/core/client/src/api-client/index.md +++ b/packages/core/client/src/api-client/index.md @@ -166,8 +166,3 @@ useResource 用法: const resource = useResource('posts'); const resource = useResource('posts.comments', 1); ``` - -resource 的实际场景用例参见: - -- [useCollection()](collection-manager#usecollection) -- [useCollectionField()](collection-manager#usecollectionfield) diff --git a/packages/core/client/src/application/__tests__/Application.test.tsx b/packages/core/client/src/application/__tests__/Application.test.tsx index e44217888e..a45689fe44 100644 --- a/packages/core/client/src/application/__tests__/Application.test.tsx +++ b/packages/core/client/src/application/__tests__/Application.test.tsx @@ -386,7 +386,7 @@ describe('Application', () => { render(); await sleep(10); - expect(screen.getByText('Load Plugin Error')).toBeInTheDocument(); + expect(screen.getByText('App Error')).toBeInTheDocument(); }); it('replace Component', async () => { diff --git a/packages/core/client/src/application/components/defaultComponents.tsx b/packages/core/client/src/application/components/defaultComponents.tsx index f011954c89..aeb2c475b9 100644 --- a/packages/core/client/src/application/components/defaultComponents.tsx +++ b/packages/core/client/src/application/components/defaultComponents.tsx @@ -14,7 +14,7 @@ const Loading: FC = () =>

Loading...
; const AppError: FC<{ error: Error }> = ({ error }) => { return (
-
Load Plugin Error
+
App Error
{error?.message} {process.env.__TEST__ && error?.stack}
diff --git a/packages/core/client/src/application/hoc/withDynamicSchemaProps.tsx b/packages/core/client/src/application/hoc/withDynamicSchemaProps.tsx index 6c653e8c88..df68c317b7 100644 --- a/packages/core/client/src/application/hoc/withDynamicSchemaProps.tsx +++ b/packages/core/client/src/application/hoc/withDynamicSchemaProps.tsx @@ -18,7 +18,10 @@ interface WithSchemaHookOptions { displayName?: string; } -export function withDynamicSchemaProps(Component: any, options: WithSchemaHookOptions = {}) { +export function withDynamicSchemaProps( + Component: React.ComponentType, + options: WithSchemaHookOptions = {}, +) { const displayName = options.displayName || Component.displayName || Component.name; const ComponentWithProps: ComponentType = (props) => { const { dn, findComponent } = useDesignable(); diff --git a/packages/core/client/src/index.ts b/packages/core/client/src/index.ts index e38c4978c2..b3c6b0c668 100644 --- a/packages/core/client/src/index.ts +++ b/packages/core/client/src/index.ts @@ -63,10 +63,10 @@ export * from './variables'; export { withDynamicSchemaProps } from './application/hoc/withDynamicSchemaProps'; export * from './modules/blocks/BlockSchemaToolbar'; -export * from './modules/blocks/data-blocks/details-multi/setDataLoadingModeSettingsItem'; export * from './modules/blocks/data-blocks/form'; export * from './modules/blocks/data-blocks/table'; export * from './modules/blocks/data-blocks/table-selector'; export * from './modules/blocks/useParentRecordCommon'; +export * from './modules/blocks/index'; export { DeclareVariable } from './modules/variable/DeclareVariable'; diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-multi/hooks/index.ts b/packages/core/client/src/modules/blocks/data-blocks/details-multi/hooks/index.ts new file mode 100644 index 0000000000..8e61dbd187 --- /dev/null +++ b/packages/core/client/src/modules/blocks/data-blocks/details-multi/hooks/index.ts @@ -0,0 +1,3 @@ +export * from './useDetailsWithPaginationBlockParams'; +export * from './useDetailsWithPaginationProps'; +export * from './useDetailsWithPaginationDecoratorProps'; diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-multi/index.ts b/packages/core/client/src/modules/blocks/data-blocks/details-multi/index.ts new file mode 100644 index 0000000000..8f95a33778 --- /dev/null +++ b/packages/core/client/src/modules/blocks/data-blocks/details-multi/index.ts @@ -0,0 +1,2 @@ +export * from './hooks'; +export * from './setDataLoadingModeSettingsItem'; diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-single/hooks/index.ts b/packages/core/client/src/modules/blocks/data-blocks/details-single/hooks/index.ts new file mode 100644 index 0000000000..85a5c57245 --- /dev/null +++ b/packages/core/client/src/modules/blocks/data-blocks/details-single/hooks/index.ts @@ -0,0 +1,2 @@ +export * from './useDetailsDecoratorProps'; +export * from './useDetailsProps'; diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-single/index.ts b/packages/core/client/src/modules/blocks/data-blocks/details-single/index.ts new file mode 100644 index 0000000000..4cc90d02bd --- /dev/null +++ b/packages/core/client/src/modules/blocks/data-blocks/details-single/index.ts @@ -0,0 +1 @@ +export * from './hooks'; diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/index.ts b/packages/core/client/src/modules/blocks/data-blocks/table/index.ts index 646b14e223..f0f4eb2f72 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/index.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table/index.ts @@ -16,3 +16,4 @@ export * from './tableColumnSettings'; export * from './TableColumnInitializers'; export * from './createTableBlockUISchema'; export * from './hooks/useTableBlockDecoratorProps'; +export * from './hooks/useTableBlockProps'; diff --git a/packages/core/client/src/modules/blocks/index.ts b/packages/core/client/src/modules/blocks/index.ts new file mode 100644 index 0000000000..dece9dd13b --- /dev/null +++ b/packages/core/client/src/modules/blocks/index.ts @@ -0,0 +1,2 @@ +export * from './data-blocks/details-multi'; +export * from './data-blocks/details-single'; diff --git a/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx b/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx index 50c851a633..c420af24d2 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx @@ -11,7 +11,7 @@ import { observer, RecursionField, useField, useFieldSchema } from '@formily/rea import { Drawer } from 'antd'; import classNames from 'classnames'; import React from 'react'; -import { OpenSize } from './'; +import { OpenSize } from './types'; import { useStyles } from './Action.Drawer.style'; import { useActionContext } from './hooks'; import { useSetAriaLabelForDrawer } from './hooks/useSetAriaLabelForDrawer'; diff --git a/packages/core/client/src/schema-component/antd/action/Action.Modal.tsx b/packages/core/client/src/schema-component/antd/action/Action.Modal.tsx index ac4ceb4575..14f2738062 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.Modal.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.Modal.tsx @@ -12,10 +12,10 @@ import { observer, RecursionField, useField, useFieldSchema } from '@formily/rea import { Modal, ModalProps } from 'antd'; import classNames from 'classnames'; import React from 'react'; -import { OpenSize, useActionContext } from '.'; import { useToken } from '../../../style'; import { useSetAriaLabelForModal } from './hooks/useSetAriaLabelForModal'; -import { ComposedActionDrawer } from './types'; +import { ComposedActionDrawer, OpenSize } from './types'; +import { useActionContext } from './hooks'; const openSizeWidthMap = new Map([ ['small', '40%'], diff --git a/packages/core/client/src/schema-component/antd/action/Action.Popover.tsx b/packages/core/client/src/schema-component/antd/action/Action.Popover.tsx deleted file mode 100644 index d3436f53a5..0000000000 --- a/packages/core/client/src/schema-component/antd/action/Action.Popover.tsx +++ /dev/null @@ -1,9 +0,0 @@ -/** - * 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. - */ - diff --git a/packages/core/client/src/schema-component/antd/action/Action.tsx b/packages/core/client/src/schema-component/antd/action/Action.tsx index 52ff8ae02c..ff9768fac1 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.tsx @@ -42,11 +42,11 @@ import useStyles from './Action.style'; import { ActionContextProvider } from './context'; import { useA } from './hooks'; import { useGetAriaLabelOfAction } from './hooks/useGetAriaLabelOfAction'; -import { ComposedAction } from './types'; +import { ActionProps, ComposedAction } from './types'; import { linkageAction, setInitialActionState } from './utils'; export const Action: ComposedAction = withDynamicSchemaProps( - observer((props: any) => { + observer((props: ActionProps) => { const { popover, confirm, @@ -59,6 +59,7 @@ export const Action: ComposedAction = withDynamicSchemaProps( title, onClick, style, + loading, openSize: os, disabled: propsDisabled, actionCallback, @@ -180,14 +181,14 @@ export const Action: ComposedAction = withDynamicSchemaProps( aria-label={getAriaLabel()} {...others} onMouseEnter={handleMouseEnter} - loading={field?.data?.loading} + loading={field?.data?.loading || loading} icon={icon ? : null} disabled={disabled} style={buttonStyle} onClick={handleButtonClick} component={tarComponent || Button} className={classnames(componentCls, hashId, className, 'nb-action')} - type={props.type === 'danger' ? undefined : props.type} + type={(props as any).type === 'danger' ? undefined : props.type} > {actionTitle} diff --git a/packages/core/client/src/schema-component/antd/action/ActionBar.tsx b/packages/core/client/src/schema-component/antd/action/ActionBar.tsx index 610a7dca79..bf06ff2ba4 100644 --- a/packages/core/client/src/schema-component/antd/action/ActionBar.tsx +++ b/packages/core/client/src/schema-component/antd/action/ActionBar.tsx @@ -9,7 +9,7 @@ import { cx } from '@emotion/css'; import { RecursionField, observer, useFieldSchema } from '@formily/react'; -import { Space } from 'antd'; +import { Space, SpaceProps } from 'antd'; import React, { CSSProperties, useContext } from 'react'; import { createPortal } from 'react-dom'; import { DndContext } from '../../common'; @@ -17,10 +17,11 @@ import { useDesignable, useProps } from '../../hooks'; import { useSchemaInitializerRender } from '../../../application'; import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps'; -interface ActionBarContextForceProps { - layout?: 'one-column' | 'tow-columns'; +export interface ActionBarProps { + layout?: 'one-column' | 'two-columns'; style?: CSSProperties; className?: string; + spaceProps?: SpaceProps; } export interface ActionBarContextValue { @@ -28,7 +29,7 @@ export interface ActionBarContextValue { /** * override props */ - forceProps?: ActionBarContextForceProps; + forceProps?: ActionBarProps; parentComponents?: string[]; } @@ -61,7 +62,7 @@ export const ActionBar = withDynamicSchemaProps( const { forceProps = {} } = useActionBarContext(); // 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema - const { layout = 'tow-columns', style, spaceProps, ...others } = { ...useProps(props), ...forceProps } as any; + const { layout = 'two-columns', style, spaceProps, ...others } = { ...useProps(props), ...forceProps } as any; const fieldSchema = useFieldSchema(); const { render } = useSchemaInitializerRender(fieldSchema['x-initializer'], fieldSchema['x-initializer-props']); diff --git a/packages/core/client/src/schema-component/antd/action/context.tsx b/packages/core/client/src/schema-component/antd/action/context.tsx index be51c5877f..cc7067521d 100644 --- a/packages/core/client/src/schema-component/antd/action/context.tsx +++ b/packages/core/client/src/schema-component/antd/action/context.tsx @@ -7,11 +7,10 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { Schema } from '@formily/react'; -import { DrawerProps, ModalProps } from 'antd'; import React, { createContext, useEffect, useRef, useState } from 'react'; import { useActionContext } from './hooks'; import { useDataBlockRequest } from '../../../data-source'; +import { ActionContextProps } from './types'; export const ActionContext = createContext({}); ActionContext.displayName = 'ActionContext'; @@ -46,21 +45,3 @@ export const ActionContextProvider: React.FC ); }; - -export type OpenSize = 'small' | 'middle' | 'large'; -export interface ActionContextProps { - button?: any; - visible?: boolean; - setVisible?: (v: boolean) => void; - openMode?: 'drawer' | 'modal' | 'page'; - snapshot?: boolean; - openSize?: OpenSize; - containerRefKey?: string; - formValueChanged?: boolean; - setFormValueChanged?: (v: boolean) => void; - fieldSchema?: Schema; - drawerProps?: DrawerProps; - modalProps?: ModalProps; - submitted?: boolean; - setSubmitted?: (v: boolean) => void; -} diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/action-container.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/action-container.tsx new file mode 100644 index 0000000000..71dc9d9279 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/action-container.tsx @@ -0,0 +1,49 @@ +import { useActionContext } from '@nocobase/client'; +import { getAppComponent } from '@nocobase/test/web'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + properties: { + test: { + type: 'void', + 'x-component': 'Action', + title: 'Open', + 'x-component-props': { + openMode: 'drawer', + }, + properties: { + drawer: { + type: 'void', + 'x-component': 'Action.Container', + title: 'Title', + properties: { + footer: { + type: 'void', + 'x-component': 'Action.Container.Footer', + properties: { + close: { + title: 'Close', + 'x-component': 'Action', + 'x-use-component-props': function useActionProps() { + const { setVisible } = useActionContext(); + return { + type: 'default', + onClick() { + setVisible(false); + }, + }; + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/action-context.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/action-context.tsx new file mode 100644 index 0000000000..bec1655bbe --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/action-context.tsx @@ -0,0 +1,59 @@ +import { ISchema, observer } from '@formily/react'; +import { + Action, + ActionContextProvider, + Form, + FormItem, + Input, + SchemaComponent, + SchemaComponentProvider, + useActionContext, +} from '@nocobase/client'; +import React, { useState } from 'react'; + +const schema: ISchema = { + type: 'object', + properties: { + drawer1: { + 'x-component': 'Action.Drawer', + type: 'void', + title: 'Drawer Title', + properties: { + hello1: { + 'x-content': 'Hello', + title: 'T1', + }, + footer1: { + 'x-component': 'Action.Drawer.Footer', + type: 'void', + properties: { + close1: { + title: 'Close', + 'x-component': 'Action', + 'x-use-component-props': function useActionProps() { + const { setVisible } = useActionContext(); + return { + onClick() { + setVisible(false); + }, + }; + }, + }, + }, + }, + }, + }, + }, +}; + +export default observer(() => { + const [visible, setVisible] = useState(false); + return ( + + + setVisible(true)}>Open + + + + ); +}); diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/action-link.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/action-link.tsx new file mode 100644 index 0000000000..83ce03763c --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/action-link.tsx @@ -0,0 +1,17 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + properties: { + test: { + type: 'void', + 'x-component': 'Action.Link', + title: 'Edit', + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/action-modal.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/action-modal.tsx new file mode 100644 index 0000000000..8aede324d1 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/action-modal.tsx @@ -0,0 +1,103 @@ +import { getAppComponent } from '@nocobase/test/web'; +import { Space, App as AntdApp } from 'antd'; +import { useAPIClient, useActionContext } from '@nocobase/client'; +import { useForm } from '@formily/react'; + +const useCloseActionProps = () => { + const { setVisible } = useActionContext(); + return { + type: 'default', + onClick() { + setVisible(false); + }, + }; +}; + +const useSubmitActionProps = () => { + const { setVisible } = useActionContext(); + const api = useAPIClient(); + const { message } = AntdApp.useApp(); + const form = useForm(); + + return { + type: 'primary', + async onClick() { + // Submit the form + await form.submit(); + const values = form.values; + + console.log('values:', values); + const { data } = await api.request({ url: 'test', data: values, method: 'POST' }); + if (data.data === 'ok') { + message.success('Submit success'); + setVisible(false); + form.reset(); // 提交成功后重置表单 + } + }, + }; +}; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-component': Space, + properties: { + test: { + type: 'void', + 'x-component': 'Action', + title: 'Open Modal', + 'x-component-props': { + openSize: 'small', + }, + properties: { + drawer: { + type: 'void', + 'x-component': 'Action.Modal', + title: 'Modal Title', + 'x-decorator': 'FormV2', + properties: { + username: { + type: 'string', + title: `Username`, + required: true, + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + footer: { + type: 'void', + 'x-component': 'Action.Modal.Footer', + properties: { + close: { + title: 'Close', + 'x-component': 'Action', + 'x-component-props': { + type: 'default', + }, + 'x-use-component-props': 'useCloseActionProps', + }, + submit: { + title: 'Submit', + 'x-component': 'Action', + 'x-use-component-props': 'useSubmitActionProps', + }, + }, + }, + }, + }, + }, + }, + }, + }, + appOptions: { + scopes: { + useSubmitActionProps, + useCloseActionProps, + }, + }, + apis: { + test: { data: 'ok' }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/action-popover.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/action-popover.tsx new file mode 100644 index 0000000000..89832901d7 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/action-popover.tsx @@ -0,0 +1,33 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + properties: { + test: { + 'x-component': 'Action', + 'x-component-props': { + type: 'primary', + popover: true, + }, + type: 'void', + title: 'Open', + properties: { + popover: { + type: 'void', + 'x-component': 'Action.Popover', + properties: { + hello: { + type: 'void', + 'x-content': 'Hello', + }, + }, + }, + }, + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/actionbar-one-column.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/actionbar-one-column.tsx new file mode 100644 index 0000000000..89cb6e106d --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/actionbar-one-column.tsx @@ -0,0 +1,77 @@ +import { ActionInitializer, SchemaInitializer } from '@nocobase/client'; +import { getAppComponent } from '@nocobase/test/web'; + +const addActionButton = new SchemaInitializer({ + name: 'addActionButton', + designable: true, + title: 'Configure actions', + style: { + marginLeft: 8, + }, + items: [ + { + type: 'itemGroup', + title: 'Enable actions', + name: 'enableActions', + children: [ + { + name: 'action1', + title: '{{t("Action 1")}}', + Component: 'ActionInitializer', + schema: { + title: 'Action 1', + 'x-component': 'Action', + 'x-action': 'a1', // unique identifier + }, + }, + { + name: 'action2', + title: '{{t("Action 2")}}', + Component: 'ActionInitializer', + schema: { + title: 'Action 2', + 'x-component': 'Action', + 'x-action': 'a2', // unique identifier + }, + }, + ], + }, + ], +}); + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + properties: { + test: { + type: 'void', + 'x-component': 'ActionBar', + 'x-initializer': 'addActionButton', + 'x-component-props': { + layout: 'one-column', + }, + properties: { + a1: { + title: 'Action 1', + 'x-component': 'Action', + 'x-action': 'a1', + }, + a2: { + title: 'Action 2', + 'x-component': 'Action', + 'x-action': 'a2', + }, + }, + }, + }, + }, + appOptions: { + schemaInitializers: [addActionButton], + components: { + ActionInitializer, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/actionbar-two-columns.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/actionbar-two-columns.tsx new file mode 100644 index 0000000000..4dce2ac3a9 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/actionbar-two-columns.tsx @@ -0,0 +1,81 @@ +import { ActionInitializer, SchemaInitializer } from '@nocobase/client'; +import { getAppComponent } from '@nocobase/test/web'; + +const addActionButton = new SchemaInitializer({ + name: 'addActionButton', + designable: true, + title: 'Configure actions', + style: { + marginLeft: 8, + }, + items: [ + { + type: 'itemGroup', + title: 'Enable actions', + name: 'enableActions', + children: [ + { + name: 'action1', + title: '{{t("Action 1")}}', + Component: 'ActionInitializer', + schema: { + title: 'Action 1', + 'x-component': 'Action', + 'x-action': 'a1', + 'x-align': 'left', + }, + }, + { + name: 'action2', + title: '{{t("Action 2")}}', + Component: 'ActionInitializer', + schema: { + title: 'Action 2', + 'x-component': 'Action', + 'x-action': 'a2', + 'x-align': 'right', + }, + }, + ], + }, + ], +}); + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + properties: { + test: { + type: 'void', + 'x-component': 'ActionBar', + 'x-initializer': 'addActionButton', + 'x-component-props': { + layout: 'two-columns', + }, + properties: { + a1: { + title: 'Action 1', + 'x-component': 'Action', + 'x-action': 'a1', + 'x-align': 'left', + }, + a2: { + title: 'Action 2', + 'x-component': 'Action', + 'x-action': 'a2', + 'x-align': 'right', + }, + }, + }, + }, + }, + appOptions: { + schemaInitializers: [addActionButton], + components: { + ActionInitializer, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/basic.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/basic.tsx new file mode 100644 index 0000000000..e2025dbfb5 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/basic.tsx @@ -0,0 +1,23 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + properties: { + test: { + type: 'void', + 'x-component': 'Action', + 'x-component-props': { + ghost: true, // ButtonProps + type: 'dashed', // ButtonProps + danger: true, // ButtonProps + title: 'Open', // title + }, + // title: 'Open', // It's also possible here + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/confirm.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/confirm.tsx new file mode 100644 index 0000000000..fb357752e4 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/confirm.tsx @@ -0,0 +1,35 @@ +import { getAppComponent } from '@nocobase/test/web'; +import { Space, App as AntdApp } from 'antd'; +import { ActionProps } from '@nocobase/client'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-component': Space, + properties: { + test: { + type: 'void', + 'x-component': 'Action', + title: 'Delete', + 'x-use-component-props': function useActionProps(): ActionProps { + const { message } = AntdApp.useApp(); + + return { + confirm: { + // confirm props + title: 'Delete', + content: 'Are you sure you want to delete it?', + }, + onClick() { + // after confirm ok + message.success('Deleted!'); + }, + }; + }, + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/custom-component.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/custom-component.tsx new file mode 100644 index 0000000000..44576f6d20 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/custom-component.tsx @@ -0,0 +1,38 @@ +import { getAppComponent } from '@nocobase/test/web'; +import { Button, Space } from 'antd'; +import React from 'react'; + +const ComponentButton = (props) => { + return ; +}; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-component': Space, + properties: { + test1: { + type: 'void', + 'x-component': 'Action', + 'x-component-props': { + component: 'ComponentButton', // string type + }, + }, + test2: { + type: 'void', + 'x-component': 'Action', + 'x-component-props': { + component: ComponentButton, // ComponentType type + }, + }, + }, + }, + appOptions: { + components: { + ComponentButton, // register custom component + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-basic.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-basic.tsx new file mode 100644 index 0000000000..f56e96e94e --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-basic.tsx @@ -0,0 +1,26 @@ +import { getAppComponent } from '@nocobase/test/web'; +import { Space } from 'antd'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-component': Space, + properties: { + test: { + type: 'void', + 'x-component': 'Action', + title: 'Open Drawer', + properties: { + drawer: { + type: 'void', + 'x-component': 'Action.Drawer', + title: 'Drawer Title', + }, + }, + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-footer.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-footer.tsx new file mode 100644 index 0000000000..ec41e92455 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-footer.tsx @@ -0,0 +1,79 @@ +import { getAppComponent } from '@nocobase/test/web'; +import { Space } from 'antd'; +import { useActionContext } from '@nocobase/client'; + +const useCloseActionProps = () => { + const { setVisible } = useActionContext(); + return { + type: 'default', + onClick() { + setVisible(false); + }, + }; +}; + +const useSubmitActionProps = () => { + const { setVisible } = useActionContext(); + return { + type: 'primary', + onClick() { + console.log('submit'); + setVisible(false); + }, + }; +}; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-component': Space, + properties: { + test: { + type: 'void', + 'x-component': 'Action', + title: 'Open Drawer', + properties: { + drawer: { + type: 'void', + 'x-component': 'Action.Drawer', + title: 'Drawer Title', + properties: { + content: { + type: 'void', + 'x-content': 'Hello', + }, + footer: { + type: 'void', + 'x-component': 'Action.Drawer.Footer', // must be `Action.Drawer.Footer` + properties: { + close: { + title: 'Close', + 'x-component': 'Action', + 'x-use-component-props': 'useCloseActionProps', + }, + submit: { + title: 'Submit', + 'x-component': 'Action', + 'x-use-component-props': 'useSubmitActionProps', + }, + }, + }, + }, + }, + }, + }, + }, + }, + appOptions: { + scopes: { + useCloseActionProps, + useSubmitActionProps, + }, + }, + apis: { + test: { data: 'ok' }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-openSize.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-openSize.tsx new file mode 100644 index 0000000000..7a1be043ab --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-openSize.tsx @@ -0,0 +1,36 @@ +import { getAppComponent } from '@nocobase/test/web'; +import { Space } from 'antd'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-component': Space, + properties: { + test: { + type: 'void', + 'x-component': 'Action', + title: 'Open Drawer', + 'x-component-props': { + openSize: 'large', // open drawer size + }, + properties: { + drawer: { + type: 'void', + title: 'Drawer Title', + 'x-component': 'Action.Drawer', + properties: { + // Drawer content + hello: { + type: 'void', + 'x-content': 'Hello', + }, + }, + }, + }, + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-with-form.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-with-form.tsx new file mode 100644 index 0000000000..aec8eade74 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/drawer-with-form.tsx @@ -0,0 +1,101 @@ +import { getAppComponent } from '@nocobase/test/web'; +import { Space, App as AntdApp } from 'antd'; +import { useAPIClient, useActionContext } from '@nocobase/client'; +import { useForm } from '@formily/react'; + +const useCloseActionProps = () => { + const { setVisible } = useActionContext(); + return { + type: 'default', + onClick() { + setVisible(false); + }, + }; +}; + +const useSubmitActionProps = () => { + const { setVisible } = useActionContext(); + const api = useAPIClient(); + const { message } = AntdApp.useApp(); + const form = useForm(); + + return { + type: 'primary', + async onClick() { + // Submit the form + await form.submit(); + const values = form.values; + + console.log('values:', values); + const { data } = await api.request({ url: 'test', data: values, method: 'POST' }); + if (data.data === 'ok') { + message.success('Submit success'); + setVisible(false); + form.reset(); // 提交成功后重置表单 + } + }, + }; +}; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-component': Space, + properties: { + test: { + type: 'void', + 'x-component': 'Action', + title: 'Open Drawer', + properties: { + drawer: { + type: 'void', + 'x-component': 'Action.Drawer', + title: 'Drawer Title', + 'x-decorator': 'FormV2', // This uses the `FormV2` component. + properties: { + username: { + // This is a form field. + type: 'string', + title: `Username`, + required: true, + 'x-decorator': 'FormItem', + 'x-component': 'Input', + }, + footer: { + type: 'void', + 'x-component': 'Action.Drawer.Footer', + properties: { + close: { + title: 'Close', + 'x-component': 'Action', + 'x-component-props': { + type: 'default', + }, + 'x-use-component-props': 'useCloseActionProps', + }, + submit: { + title: 'Submit', + 'x-component': 'Action', + 'x-use-component-props': 'useSubmitActionProps', + }, + }, + }, + }, + }, + }, + }, + }, + }, + appOptions: { + scopes: { + useSubmitActionProps, + useCloseActionProps, + }, + }, + apis: { + test: { data: 'ok' }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/dynamic-props.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/dynamic-props.tsx new file mode 100644 index 0000000000..441fa8b77e --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/dynamic-props.tsx @@ -0,0 +1,68 @@ +import { getAppComponent } from '@nocobase/test/web'; +import { Space, App as AntdApp } from 'antd'; +import { useAPIClient, ActionProps } from '@nocobase/client'; + +const useCustomActionProps = (): ActionProps => { + const api = useAPIClient(); + const { message } = AntdApp.useApp(); + + return { + onClick: async () => { + const { data } = await api.request({ url: 'test' }); + if (data.data.result === 'ok') { + message.success('Success!'); + } + }, + }; +}; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-component': Space, + properties: { + test1: { + type: 'void', + 'x-component': 'Action', + title: 'test1', + 'x-use-component-props': useCustomActionProps, // function type + }, + test2: { + type: 'void', + 'x-component': 'Action', + title: 'test2', + 'x-use-component-props': function useCustomActionProps(): ActionProps { + // inline function type + const api = useAPIClient(); + const { message } = AntdApp.useApp(); + + return { + onClick: async () => { + const { data } = await api.request({ url: 'test' }); + if (data.data.result === 'ok') { + message.success('Success!'); + } + }, + }; + }, + }, + test3: { + type: 'void', + 'x-component': 'Action', + title: 'test2', + 'x-use-component-props': 'useCustomActionProps', // string type + }, + }, + }, + appOptions: { + scopes: { + useCustomActionProps, + }, + }, + apis: { + test: { data: { result: 'ok' } }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/action/demos/new-demos/schema-toolbar.tsx b/packages/core/client/src/schema-component/antd/action/demos/new-demos/schema-toolbar.tsx new file mode 100644 index 0000000000..17d65644d9 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/action/demos/new-demos/schema-toolbar.tsx @@ -0,0 +1,64 @@ +import { SchemaSettings } from '@nocobase/client'; +import { getAppComponent } from '@nocobase/test/web'; + +const myActionSettings = new SchemaSettings({ + name: 'myActionSettings', + items: [ + { + name: 'delete', + type: 'remove', + }, + ], +}); + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + properties: { + test: { + type: 'void', + 'x-component': 'ActionBar', + 'x-component-props': { + layout: 'two-columns', + }, + properties: { + a1: { + title: 'Action 1', + 'x-component': 'Action', + 'x-action': 'a1', + 'x-align': 'right', + 'x-settings': 'myActionSettings', + }, + a2: { + title: 'Action 2', + 'x-component': 'Action', + 'x-action': 'a2', + 'x-align': 'right', + 'x-settings': 'myActionSettings', + }, + a3: { + title: 'Action 3', + 'x-component': 'Action', + 'x-action': 'a1', + 'x-align': 'left', + 'x-settings': 'myActionSettings', + }, + a4: { + title: 'Action 4', + 'x-component': 'Action', + 'x-action': 'a2', + 'x-align': 'left', + 'x-settings': 'myActionSettings', + }, + }, + }, + }, + }, + appOptions: { + schemaSettings: [myActionSettings], + }, + designable: true, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/action/index.md b/packages/core/client/src/schema-component/antd/action/index.md index 490c1339b2..d1c99547ed 100644 --- a/packages/core/client/src/schema-component/antd/action/index.md +++ b/packages/core/client/src/schema-component/antd/action/index.md @@ -1,46 +1,206 @@ ---- -group: - title: Schema Components - order: 3 ---- - # Action -## Nodes +将按钮和各种操作结合在一起,提供了一种统一的方式来处理操作。 -- Action -- Action.Drawer -- Action.Drawer.Footer -- Action.Modal -- Action.Modal.Footer -- Action.Drawer -- Action.Drawer.Footer -- Action.Container -- Action.Container.Footer -- ActionBar +## Action -## Examples +操作的触发器,默认是 ant-design 的 `Button` 组件,并为后续的弹窗或者抽屉提供上下文。 -### Action + Action.Drawer +```ts +export interface ActionProps extends ButtonProps { + /** + * button title + */ + title?: string; - + /** + * custom component, replace the default Button component + */ + component?: string | ComponentType; -### ActionContext + Action.Drawer + /** + * Dynamic rendering of the opened content in conjunction with `Action.Container`. + */ + openMode?: 'drawer' | 'modal' | 'page'; + /** + * The size of the pop-up window, only valid when `openMode: 'modal'` + */ + openSize?: 'small' | 'middle' | 'large'; + /** + * Customize the position of the pop-up window + */ + containerRefKey?: string; -只配置 Action.Drawer,而不需要 Action,结合 ActionContext 自定义按钮。 + /** + * Whether to display the popover, only valid when `openMode: 'popover'` + */ + popover?: boolean; - + /** + * When the button is clicked, whether a pop-up confirmation is required + */ + confirm?: false | { + title: string; + content: string; + }; +} +``` -### 不同的打开方式 +### Basic Usage - +- `ButtonProp` +- title -### Action + Action.Popover + - +### Custom Component - +- component -### ActionBar + - +### Dynamic Props + +这里使用了 `x-use-component-props` 的能力,具体可以查看 [x-use-component-props](https://docs.nocobase.com/development/client/ui-schema/what-is-ui-schema#x-component-props-%E5%92%8C-x-use-component-props)。 + + + +### Confirm + +- confirm + + + +## Action.Link + +将 `Button` 组件替换为 `a` 标签。 + + + +## Action.Drawer + +主要用于在右侧弹出一个抽屉。 + +```ts +interface ActionDrawer extends DrawerProps {} +``` + +### Basic Usage + +- `DrawerProps` + + + +### openSize + + + +### Footer + +Footer 可以放一些按钮,比如取消或者提交等。 + +其 Schema `x-component` 必须为 `Action.Drawer.Footer` 组件。 + + + +### With Form + + + +## Action.Modal + +```ts +interface ActionModal extends ModalProps {} +``` + +其用法和 `Action.Drawer` 类似,这里只举一个例子。 + + + +## Action.Popover + +注意,此时 Action 的 `popover` 属性必须为 `true`。 + + + +## Action.Container + +当根据需要动态渲染内容时,可以使用 `Action.Container` + Action `openMode` 属性动态决定。 + + + +## ActionBar + +一般用于区块的顶部的操作按钮,其内部会自动处理布局和渲染 [schema-initializer](/core/ui-schema/schema-initializer)。 + +```ts +import { SpaceProps } from 'antd' + +interface ActionBarProps { + layout?: 'one-column' | 'two-columns'; + style?: CSSProperties; + className?: string; + spaceProps?: SpaceProps; +} +``` + +### one-column + +一列布局,全部靠左。 + +Schema 中的 `x-action` 是按钮的唯一标识,不能和已有的重复,用于 `ActionInitializer` 的查找和删除。 + + + +### two-columns + +左右布局,通过 `x-align` 控制。 + + + +## ActionContext + +封装在 `Action` 组件内部,用于传递上下文。 + +```ts +export type OpenSize = 'small' | 'middle' | 'large'; +export interface ActionContextProps { + button?: React.JSX.Element; + visible?: boolean; + setVisible?: (v: boolean) => void; + openMode?: 'drawer' | 'modal' | 'page'; + snapshot?: boolean; + openSize?: OpenSize; + /** + * Customize the position of the pop-up window + */ + containerRefKey?: string; + formValueChanged?: boolean; + setFormValueChanged?: (v: boolean) => void; + fieldSchema?: Schema; + drawerProps?: DrawerProps; + modalProps?: ModalProps; + submitted?: boolean; + setSubmitted?: (v: boolean) => void; +} +``` + +假设 Action 组件无法满足需求,我们可以直接使用 ActionContext 组件进行自定义。 + + + +## ActionSchemaToolbar + +用于单个按钮渲染 [SchemaToolbar](/core/ui-schema/schema-toolbar) 和 [SchemaSettings](/core/ui-schema/schema-settings)。 + + + +## Hooks + +### useActionContext() + +获取 `ActionContext` 上下文。 + +```ts +const { visible, setVisible, fieldSchema } = useActionContext(); +``` diff --git a/packages/core/client/src/schema-component/antd/action/index.tsx b/packages/core/client/src/schema-component/antd/action/index.tsx index b9f6ad3745..22db0e3b38 100644 --- a/packages/core/client/src/schema-component/antd/action/index.tsx +++ b/packages/core/client/src/schema-component/antd/action/index.tsx @@ -16,3 +16,4 @@ export * from './hooks/useGetAriaLabelOfDrawer'; export * from './hooks/useGetAriaLabelOfModal'; export * from './hooks/useGetAriaLabelOfPopover'; export * from './Action.Designer'; +export * from './types'; diff --git a/packages/core/client/src/schema-component/antd/action/types.ts b/packages/core/client/src/schema-component/antd/action/types.ts index 733026bf97..a03db6377a 100644 --- a/packages/core/client/src/schema-component/antd/action/types.ts +++ b/packages/core/client/src/schema-component/antd/action/types.ts @@ -8,14 +8,79 @@ */ import { ButtonProps, DrawerProps, ModalProps } from 'antd'; +import { ComponentType } from 'react'; +import { Schema } from '@formily/react'; -export type ActionProps = ButtonProps & { - component?: any; - useAction?: () => { - run(): Promise; - }; +export type OpenSize = 'small' | 'middle' | 'large'; +export interface ActionContextProps { + button?: React.JSX.Element; + visible?: boolean; + setVisible?: (v: boolean) => void; + openMode?: 'drawer' | 'modal' | 'page'; + snapshot?: boolean; + openSize?: OpenSize; + /** + * Customize the position of the pop-up window + */ + containerRefKey?: string; + formValueChanged?: boolean; + setFormValueChanged?: (v: boolean) => void; + fieldSchema?: Schema; + drawerProps?: DrawerProps; + modalProps?: ModalProps; + submitted?: boolean; + setSubmitted?: (v: boolean) => void; +} + +export type UseActionType = (callback?: () => void) => { + run: () => void | Promise; }; +export interface ActionProps extends ButtonProps { + /** + * button title + */ + title?: string; + + /** + * custom component, replace the default Button component + */ + component?: string | ComponentType; + + openMode?: ActionContextProps['openMode']; + openSize?: ActionContextProps['openSize']; + containerRefKey?: ActionContextProps['containerRefKey']; + + /** + * Whether to display the popover, only valid when `openMode: 'popover'` + */ + popover?: boolean; + + /** + * When the button is clicked, whether a pop-up confirmation is required + */ + confirm?: + | false + | { + title: string; + content: string; + }; + + /** + * @deprecated + */ + useAction?: string | UseActionType; + /** + * @deprecated + */ + actionCallback?: () => void; + + /** + * @internal + */ + addChild?: boolean; +} + export type ComposedAction = React.FC & { Drawer?: ComposedActionDrawer; [key: string]: any; diff --git a/packages/core/client/src/schema-component/antd/association-select/demos/new-demos/basic.tsx b/packages/core/client/src/schema-component/antd/association-select/demos/new-demos/basic.tsx new file mode 100644 index 0000000000..bae26987f2 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/association-select/demos/new-demos/basic.tsx @@ -0,0 +1,31 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + properties: { + test: { + type: 'string', + title: 'Test', + 'x-decorator': 'FormItem', + 'x-component': 'AssociationSelect', + 'x-component-props': { + service: { + resource: 'roles', // roles 表 + action: 'list', // 列表接口 + }, + fieldNames: { + label: 'title', // 显示的字段 + value: 'name', // 值字段 + }, + }, + }, + }, + }, + delayResponse: 500, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/association-select/demos/new-demos/multiple.tsx b/packages/core/client/src/schema-component/antd/association-select/demos/new-demos/multiple.tsx new file mode 100644 index 0000000000..da678a00b7 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/association-select/demos/new-demos/multiple.tsx @@ -0,0 +1,32 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + properties: { + test: { + type: 'array', // 数组类型 + title: 'Test', + 'x-decorator': 'FormItem', + 'x-component': 'AssociationSelect', + 'x-component-props': { + multiple: true, // 多选 + service: { + resource: 'roles', + action: 'list', + }, + fieldNames: { + label: 'title', + value: 'name', + }, + }, + }, + }, + }, + delayResponse: 500, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/association-select/demos/new-demos/read-pretty.tsx b/packages/core/client/src/schema-component/antd/association-select/demos/new-demos/read-pretty.tsx new file mode 100644 index 0000000000..39fedfdb6e --- /dev/null +++ b/packages/core/client/src/schema-component/antd/association-select/demos/new-demos/read-pretty.tsx @@ -0,0 +1,33 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + properties: { + test: { + type: 'string', + title: 'Test', + default: 'admin', + 'x-decorator': 'FormItem', + 'x-component': 'AssociationSelect', + 'x-pattern': 'readPretty', + 'x-component-props': { + service: { + resource: 'roles', + action: 'list', + }, + fieldNames: { + label: 'title', + value: 'name', + }, + }, + }, + }, + }, + delayResponse: 500, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/association-select/index.md b/packages/core/client/src/schema-component/antd/association-select/index.md index 3b63d043c3..6b425a6a9c 100644 --- a/packages/core/client/src/schema-component/antd/association-select/index.md +++ b/packages/core/client/src/schema-component/antd/association-select/index.md @@ -1,27 +1,24 @@ ---- -group: - title: Schema Components - order: 3 ---- - # AssociationSelect -## Examples - - - -## API - -基于 Ant Design 的 [Select](https://ant.design/components/select/#API),相关扩展属性有: - -- `objectValue` 值为 object 类型 -- `fieldNames` 默认值有区别 +通过指定数据表和字段,获取数据表的数据。 ```ts -export const defaultFieldNames = { - label: 'label', - value: 'value', - color: 'color', - options: 'children', +type AssociationSelectProps

= RemoteSelectProps

& { + action?: string; + multiple?: boolean; }; ``` + +## Basic Usage + + + +## Multiple Selection + +`type` 需要改为 `array`,并且属性需要增加 `multiple: true`。 + + + +## Read Pretty + + diff --git a/packages/core/client/src/schema-component/antd/auto-complete/AutoComplete.tsx b/packages/core/client/src/schema-component/antd/auto-complete/AutoComplete.tsx index 282febaf58..f3737ae320 100644 --- a/packages/core/client/src/schema-component/antd/auto-complete/AutoComplete.tsx +++ b/packages/core/client/src/schema-component/antd/auto-complete/AutoComplete.tsx @@ -10,11 +10,14 @@ import { connect, mapProps, mapReadPretty } from '@formily/react'; import { AutoComplete as AntdAutoComplete } from 'antd'; import { ReadPretty } from '../input'; +import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps'; -export const AutoComplete = connect( - AntdAutoComplete, - mapProps({ - dataSource: 'options', - }), - mapReadPretty(ReadPretty.Input), +export const AutoComplete = withDynamicSchemaProps( + connect( + AntdAutoComplete, + mapProps({ + dataSource: 'options', + }), + mapReadPretty(ReadPretty.Input), + ), ); diff --git a/packages/core/client/src/schema-component/antd/auto-complete/demos/basic.tsx b/packages/core/client/src/schema-component/antd/auto-complete/demos/basic.tsx new file mode 100644 index 0000000000..db3c0c89b0 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/auto-complete/demos/basic.tsx @@ -0,0 +1,38 @@ +import { useField } from '@formily/react'; +import { getAppComponent } from '@nocobase/test/web'; + +const mockVal = (str: string, repeat = 1) => ({ + value: str.repeat(repeat), +}); +const getPanelValue = (searchText: string) => + !searchText ? [] : [mockVal(searchText), mockVal(searchText, 2), mockVal(searchText, 3)]; + +function useAutoCompleteProps() { + const field = useField(); + + return { + onSearch(text: string) { + field.dataSource = getPanelValue(text); + }, + }; +} + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + properties: { + test: { + type: 'boolean', + title: 'Test', + 'x-decorator': 'FormItem', + 'x-component': 'AutoComplete', + 'x-use-component-props': useAutoCompleteProps, + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/auto-complete/demos/read-pretty.tsx b/packages/core/client/src/schema-component/antd/auto-complete/demos/read-pretty.tsx new file mode 100644 index 0000000000..758676a558 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/auto-complete/demos/read-pretty.tsx @@ -0,0 +1,22 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + 'x-pattern': 'readPretty', + properties: { + test: { + type: 'boolean', + title: 'Test', + default: 'aaa', + 'x-decorator': 'FormItem', + 'x-component': 'AutoComplete', + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/auto-complete/index.md b/packages/core/client/src/schema-component/antd/auto-complete/index.md new file mode 100644 index 0000000000..ecf72084b0 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/auto-complete/index.md @@ -0,0 +1,20 @@ + +# AutoComplete + +自动完成输出框。其基于 ant-design [AutoComplete](https://ant.design/components/auto-complete) 组件封装。 + +## Basic Usage + +```ts +type AutoCompleteProps = AntdAutoCompleteProps; +``` + + + +## Read Pretty + +```ts +type AutoCompleteReadPrettyProps = InputReadPrettyProps; +``` + + diff --git a/packages/core/client/src/schema-component/antd/block-item/BlockItem.tsx b/packages/core/client/src/schema-component/antd/block-item/BlockItem.tsx index 9c7bb0b18d..5c4c67b6e9 100644 --- a/packages/core/client/src/schema-component/antd/block-item/BlockItem.tsx +++ b/packages/core/client/src/schema-component/antd/block-item/BlockItem.tsx @@ -67,7 +67,13 @@ const useStyles = createStyles(({ css, token }: CustomCreateStylesUtils) => { `; }); -export const BlockItem: React.FC = withDynamicSchemaProps( +export interface BlockItemProps { + name?: string; + className?: string; + children?: React.ReactNode; +} + +export const BlockItem: React.FC = withDynamicSchemaProps( (props) => { // 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema const { className, children } = useProps(props); diff --git a/packages/core/client/src/schema-component/antd/block-item/demos/new-demos/basic.tsx b/packages/core/client/src/schema-component/antd/block-item/demos/new-demos/basic.tsx new file mode 100644 index 0000000000..55a8937cf7 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/block-item/demos/new-demos/basic.tsx @@ -0,0 +1,68 @@ +import { getAppComponent } from '@nocobase/test/web'; +import { DragHandler, SchemaSettings } from '@nocobase/client'; +import { useFieldSchema } from '@formily/react'; +import { observer } from '@formily/reactive-react'; +import React from 'react'; + +const simpleSettings = new SchemaSettings({ + name: 'simpleSettings', + items: [ + { + name: 'delete', + type: 'remove', + }, + ], +}); + +const MyBlock = observer( + () => { + const fieldSchema = useFieldSchema(); + return ( +

+ {fieldSchema.name} + +
+ ); + }, + { displayName: 'MyBlock' }, +); + +const App = getAppComponent({ + designable: true, + schema: { + type: 'void', + name: 'root', + 'x-component': 'DndContext', + properties: { + block1: { + type: 'void', + 'x-decorator': 'BlockItem', + 'x-component': 'MyBlock', + 'x-settings': 'simpleSettings', + }, + block2: { + type: 'void', + 'x-decorator': 'BlockItem', + 'x-component': 'MyBlock', + 'x-settings': 'simpleSettings', + }, + block3: { + type: 'void', + 'x-decorator': 'BlockItem', + 'x-component': 'MyBlock', + 'x-settings': 'simpleSettings', + }, + }, + }, + appOptions: { + schemaSettings: [simpleSettings], + components: { + MyBlock, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/block-item/index.md b/packages/core/client/src/schema-component/antd/block-item/index.md index c2f97b4ffe..842def429d 100644 --- a/packages/core/client/src/schema-component/antd/block-item/index.md +++ b/packages/core/client/src/schema-component/antd/block-item/index.md @@ -1,11 +1,22 @@ ---- -group: - title: Schema Components - order: 3 ---- - # BlockItem -普通的装饰器(Decorator)组件,无特殊 UI 效果,一般用在 x-decorator 中。用于提供区块的管理,如拖拽功能、当前节点的 SettingsForm。CardItem 和 FormItem 组件都是基于 BlockItem 实现,也具备以上相同功能。 +普通的装饰器(Decorator)组件,无 UI 效果,一般用在 `x-decorator` 中。 - +主要提供了以下 2 个能力: + +- 拖拽功能 +- [SchemaToolbar](/core/ui-schema/schema-toolbar) 和 [SchemaSettings](/core/ui-schema/schema-settings) 的渲染 + +[CardItem](/components/card-item) 和 [FormItem](/components/form-item) 组件都是基于 BlockItem 实现,也具备以上相同功能。 + +```ts +interface BlockItemProps { + name?: string; + className?: string; + children?: React.ReactNode; +} +``` + +注意拖拽功能需要配置 `DndContext` 组件。 + + diff --git a/packages/core/client/src/schema-component/antd/card-item/CardItem.tsx b/packages/core/client/src/schema-component/antd/card-item/CardItem.tsx index 980050f365..1537576a18 100644 --- a/packages/core/client/src/schema-component/antd/card-item/CardItem.tsx +++ b/packages/core/client/src/schema-component/antd/card-item/CardItem.tsx @@ -8,32 +8,38 @@ */ import { useFieldSchema } from '@formily/react'; -import { Skeleton } from 'antd'; -import React from 'react'; -import { useInView } from 'react-intersection-observer'; +import { Skeleton, CardProps } from 'antd'; +import React, { FC } from 'react'; +import { IntersectionOptions, useInView } from 'react-intersection-observer'; import { useSchemaTemplate } from '../../../schema-templates'; import { BlockItem } from '../block-item'; import { BlockItemCard } from '../block-item/BlockItemCard'; import { BlockItemError } from '../block-item/BlockItemError'; import useStyles from './style'; -interface Props { - children?: React.ReactNode; - /** 区块标识 */ +interface CardItemProps extends CardProps { name?: string; - [key: string]: any; + children?: React.ReactNode; + /** + * lazy render options + * @default { threshold: 0, initialInView: true, triggerOnce: true } + * @see https://github.com/thebuilder/react-intersection-observer + */ + lazyRender?: IntersectionOptions & { element?: React.JSX.Element }; } -export const CardItem = (props: Props) => { - const { children, name, ...restProps } = props; +export const CardItem: FC = (props) => { + const { children, name, lazyRender = {}, ...restProps } = props; const template = useSchemaTemplate(); const fieldSchema = useFieldSchema(); const templateKey = fieldSchema?.['x-template-key']; + const { element: lazyRenderElement, ...resetLazyRenderOptions } = lazyRender; const { ref, inView } = useInView({ threshold: 0, initialInView: true, triggerOnce: true, skip: !!process.env.__E2E__, + ...resetLazyRenderOptions, }); const { wrapSSR, componentCls, hashId } = useStyles(); @@ -42,7 +48,7 @@ export const CardItem = (props: Props) => { - {inView ? props.children : } + {inView ? props.children : lazyRenderElement ?? } , diff --git a/packages/core/client/src/schema-component/antd/card-item/demos/new-demos/basic.tsx b/packages/core/client/src/schema-component/antd/card-item/demos/new-demos/basic.tsx new file mode 100644 index 0000000000..0fcaaccba3 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/card-item/demos/new-demos/basic.tsx @@ -0,0 +1,58 @@ +import { getAppComponent } from '@nocobase/test/web'; +import { SchemaSettings } from '@nocobase/client'; + +const simpleSettings = new SchemaSettings({ + name: 'simpleSettings', + items: [ + { + name: 'delete', + type: 'remove', + }, + ], +}); + +const App = getAppComponent({ + designable: true, + schema: { + type: 'void', + name: 'root', + 'x-component': 'DndContext', + properties: { + block1: { + type: 'void', + 'x-component': 'CardItem', + 'x-component-props': { + title: 'Block 1', + }, + 'x-settings': 'simpleSettings', + properties: { + hello: { + type: 'void', + 'x-component': 'div', + 'x-content': 'Hello Card!', + }, + }, + }, + block2: { + type: 'void', + 'x-component': 'CardItem', + 'x-settings': 'simpleSettings', + 'x-component-props': { + title: 'Block 2', + }, + properties: { + hello: { + type: 'void', + 'x-component': 'div', + 'x-content': 'Hello Card!', + }, + }, + }, + }, + }, + appOptions: { + schemaSettings: [simpleSettings], + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/card-item/demos/new-demos/lazy-render.tsx b/packages/core/client/src/schema-component/antd/card-item/demos/new-demos/lazy-render.tsx new file mode 100644 index 0000000000..e050ed3074 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/card-item/demos/new-demos/lazy-render.tsx @@ -0,0 +1,84 @@ +import { getAppComponent } from '@nocobase/test/web'; +import { SchemaSettings } from '@nocobase/client'; + +const simpleSettings = new SchemaSettings({ + name: 'simpleSettings', + items: [ + { + name: 'delete', + type: 'remove', + }, + ], +}); + +const App = getAppComponent({ + designable: true, + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'DndContext', + 'x-component': 'div', + 'x-component-props': { + style: { + height: 300, + overflow: 'auto', + border: '1px solid #f0f0f0', + }, + }, + properties: { + block1: { + type: 'void', + 'x-component': 'CardItem', + 'x-component-props': { + title: 'Block 1', + }, + 'x-settings': 'simpleSettings', + properties: { + hello: { + type: 'void', + 'x-component': 'div', + 'x-content': 'Hello Card!', + }, + }, + }, + block2: { + type: 'void', + 'x-component': 'CardItem', + 'x-settings': 'simpleSettings', + 'x-component-props': { + title: 'Block 2', + }, + properties: { + hello: { + type: 'void', + 'x-component': 'div', + 'x-content': 'Hello Card!', + }, + }, + }, + block3: { + type: 'void', + 'x-component': 'CardItem', + 'x-settings': 'simpleSettings', + 'x-component-props': { + title: 'Block 3', + lazyRender: { + threshold: 1, + }, + }, + properties: { + hello: { + type: 'void', + 'x-component': 'div', + 'x-content': 'Hello Card!', + }, + }, + }, + }, + }, + appOptions: { + schemaSettings: [simpleSettings], + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/card-item/index.md b/packages/core/client/src/schema-component/antd/card-item/index.md index 29a0ec13e8..78b426912d 100644 --- a/packages/core/client/src/schema-component/antd/card-item/index.md +++ b/packages/core/client/src/schema-component/antd/card-item/index.md @@ -1,13 +1,27 @@ ---- -group: - title: Schema Components - order: 3 ---- - # CardItem -卡片装饰器。除此之外,也继承了 BlockItem 的功能。 +卡片装饰器。主要功能为: -## Example +- 拖拽和 [SchemaToolbar](/core/ui-schema/schema-toolbar) 和 [SchemaSettings](/core/ui-schema/schema-settings) 的渲染,继承自[BlockItem](/components/block-item) +- 懒渲染 - +其基于 ant-design [Card](https://ant.design/components/card-cn/) 组件进行封装,懒加载基于 [react-intersection-observer](https://github.com/thebuilder/react-intersection-observer) 实现。 + +```ts +interface CardItemProps extends CardProps { + name?: string; + /** + * lazy render options + * @see https://github.com/thebuilder/react-intersection-observer + */ + lazyRender?: IntersectionOptions; +} +``` + +## Basic + + + +## Custom lazy render + + diff --git a/packages/core/client/src/schema-component/antd/cascader/Cascader.tsx b/packages/core/client/src/schema-component/antd/cascader/Cascader.tsx index 8ed483eaf9..1af4621609 100644 --- a/packages/core/client/src/schema-component/antd/cascader/Cascader.tsx +++ b/packages/core/client/src/schema-component/antd/cascader/Cascader.tsx @@ -11,107 +11,140 @@ import { LoadingOutlined } from '@ant-design/icons'; import { ArrayField } from '@formily/core'; import { connect, mapProps, mapReadPretty, useField } from '@formily/react'; import { toArr } from '@formily/shared'; -import { Cascader as AntdCascader, Space } from 'antd'; +import { Cascader as AntdCascader, Space, CascaderProps as AntdCascaderProps } from 'antd'; import { isBoolean, omit } from 'lodash'; import React from 'react'; -import { useRequest } from '../../../api-client'; +import { UseRequestResult, useRequest } from '../../../api-client'; import { ReadPretty } from './ReadPretty'; import { defaultFieldNames } from './defaultFieldNames'; +import { BaseOptionType } from 'antd/es/select'; +import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps'; -const useDefDataSource = (options) => { +const useDefDataSource = (options, props: any) => { const field = useField(); - return useRequest(() => Promise.resolve({ data: field.dataSource || [] }), options); + return useRequest(() => Promise.resolve({ data: field.dataSource || props.options || [] }), { + ...options, + refreshDeps: [field.dataSource, props.options], + }); }; const useDefLoadData = (props: any) => { return props?.loadData; }; -export const Cascader = connect( - (props: any) => { - const field = useField(); - const { - value, - onChange, - labelInValue, - // fieldNames = defaultFieldNames, - useDataSource = useDefDataSource, - useLoadData = useDefLoadData, - changeOnSelectLast, - changeOnSelect, - maxLevel, - ...others - } = props; - const fieldNames = { ...defaultFieldNames, ...props.fieldNames }; - const loadData = useLoadData(props); - const { loading, run } = useDataSource({ - onSuccess(data) { - field.dataSource = data?.data || []; - }, - }); - // 兼容值为 object[] 的情况 - const toValue = () => { - return toArr(value).map((item) => { - if (typeof item === 'object') { - return item[fieldNames.value]; - } - return item; - }); - }; - const displayRender = (labels: string[], selectedOptions: any[]) => { - return ( - - {labels.map((label, index) => { - if (selectedOptions[index]) { - return {label}; - } - const item = toArr(value) - .filter(Boolean) - .find((item) => item[fieldNames.value] === label); - return {item?.[fieldNames.label] || label}; - })} - - ); - }; - const handelDropDownVisible = (value) => { - if (value && !field.dataSource?.length) { - run(); - } - }; +export type CascaderProps = AntdCascaderProps & { + /** + * @deprecated use `x-use-component-props` instead + */ + useLoadData: (props: CascaderProps) => AntdCascaderProps['loadData']; + /** + * @deprecated use `x-use-component-props` instead + */ + useDataSource?: (options: any) => UseRequestResult; + /** + * Whether to wrap the label of option into the value + */ + labelInValue?: boolean; + /** + * must select the last level + */ + changeOnSelectLast?: boolean; + onChange?: (value: any) => void; + maxLevel?: number; +}; - return ( - { - if (value && labelInValue) { - onChange(selectedOptions.map((option) => omit(option, [fieldNames.children]))); - } else { - onChange(value); +export const Cascader = withDynamicSchemaProps( + connect( + (props: CascaderProps) => { + const field = useField(); + const { + value, + onChange, + labelInValue, + options, + // fieldNames = defaultFieldNames, + useDataSource = useDefDataSource, + useLoadData = useDefLoadData, + changeOnSelectLast, + changeOnSelect, + maxLevel, + ...others + } = props; + const fieldNames = { ...defaultFieldNames, ...props.fieldNames }; + const loadData = useLoadData(props); + const { loading, run } = useDataSource( + { + onSuccess(data) { + field.dataSource = data?.data || []; + }, + }, + props, + ); + // 兼容值为 object[] 的情况 + const toValue = () => { + return toArr(value).map((item) => { + if (typeof item === 'object') { + return item[fieldNames.value]; } - }} - /> - ); - }, - mapProps( - { - dataSource: 'options', - }, - (props, field) => { - return { - ...props, - suffixIcon: field?.['loading'] || field?.['validating'] ? : props.suffixIcon, + return item; + }); }; + const displayRender = (labels: string[], selectedOptions: any[]) => { + return ( + + {labels.map((label, index) => { + if (selectedOptions[index]) { + return {label}; + } + const item = toArr(value) + .filter(Boolean) + .find((item) => item[fieldNames.value] === label); + return {item?.[fieldNames.label] || label}; + })} + + ); + }; + const handelDropDownVisible = (value) => { + if (value && !field.dataSource?.length) { + run(); + } + }; + + return ( + { + if (value && labelInValue) { + onChange(selectedOptions.map((option) => omit(option, [fieldNames.children]))); + } else { + onChange(value); + } + }} + /> + ); }, + mapProps( + { + dataSource: 'options', + }, + (props, field) => { + return { + ...props, + suffixIcon: field?.['loading'] || field?.['validating'] ? : props.suffixIcon, + }; + }, + ), + mapReadPretty(ReadPretty), ), - mapReadPretty(ReadPretty), + { displayName: 'Cascader' }, ); export default Cascader; diff --git a/packages/core/client/src/schema-component/antd/cascader/ReadPretty.tsx b/packages/core/client/src/schema-component/antd/cascader/ReadPretty.tsx index 4274ecafbf..8ef232548e 100644 --- a/packages/core/client/src/schema-component/antd/cascader/ReadPretty.tsx +++ b/packages/core/client/src/schema-component/antd/cascader/ReadPretty.tsx @@ -13,7 +13,18 @@ import { toArr } from '@formily/shared'; import React from 'react'; import { defaultFieldNames } from './defaultFieldNames'; -export const ReadPretty: React.FC = (props: any) => { +interface FieldNames { + label: string; + value: string; + children: string; +} + +export interface CascaderReadPrettyProps { + fieldNames?: FieldNames; + value?: any; +} + +export const ReadPretty: React.FC = (props) => { const { fieldNames = defaultFieldNames } = props; const values = toArr(props.value); const len = values.length; diff --git a/packages/core/client/src/schema-component/antd/cascader/demos/new-demos/basic.tsx b/packages/core/client/src/schema-component/antd/cascader/demos/new-demos/basic.tsx new file mode 100644 index 0000000000..5b81588382 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/cascader/demos/new-demos/basic.tsx @@ -0,0 +1,56 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const options = [ + { + value: 'zhejiang', + label: 'Zhejiang', + children: [ + { + value: 'hangzhou', + label: 'Hangzhou', + children: [ + { + value: 'xihu', + label: 'West Lake', + }, + ], + }, + ], + }, + { + value: 'jiangsu', + label: 'Jiangsu', + children: [ + { + value: 'nanjing', + label: 'Nanjing', + children: [ + { + value: 'zhonghuamen', + label: 'Zhong Hua Men', + }, + ], + }, + ], + }, +]; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + properties: { + test: { + type: 'string', + title: 'Test', + 'x-decorator': 'FormItem', + enum: options, + 'x-component': 'Cascader', + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/cascader/demos/new-demos/changeOnSelectLast.tsx b/packages/core/client/src/schema-component/antd/cascader/demos/new-demos/changeOnSelectLast.tsx new file mode 100644 index 0000000000..158fbc6654 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/cascader/demos/new-demos/changeOnSelectLast.tsx @@ -0,0 +1,59 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const options = [ + { + value: 'zhejiang', + label: 'Zhejiang', + children: [ + { + value: 'hangzhou', + label: 'Hangzhou', + children: [ + { + value: 'xihu', + label: 'West Lake', + }, + ], + }, + ], + }, + { + value: 'jiangsu', + label: 'Jiangsu', + children: [ + { + value: 'nanjing', + label: 'Nanjing', + children: [ + { + value: 'zhonghuamen', + label: 'Zhong Hua Men', + }, + ], + }, + ], + }, +]; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + properties: { + test: { + type: 'string', + title: 'Test', + 'x-decorator': 'FormItem', + enum: options, + 'x-component': 'Cascader', + 'x-component-props': { + changeOnSelectLast: false, + }, + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/cascader/demos/new-demos/labelInValue.tsx b/packages/core/client/src/schema-component/antd/cascader/demos/new-demos/labelInValue.tsx new file mode 100644 index 0000000000..1d080b1745 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/cascader/demos/new-demos/labelInValue.tsx @@ -0,0 +1,59 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const options = [ + { + value: 'zhejiang', + label: 'Zhejiang', + children: [ + { + value: 'hangzhou', + label: 'Hangzhou', + children: [ + { + value: 'xihu', + label: 'West Lake', + }, + ], + }, + ], + }, + { + value: 'jiangsu', + label: 'Jiangsu', + children: [ + { + value: 'nanjing', + label: 'Nanjing', + children: [ + { + value: 'zhonghuamen', + label: 'Zhong Hua Men', + }, + ], + }, + ], + }, +]; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + properties: { + test: { + type: 'string', + title: 'Test', + 'x-decorator': 'FormItem', + enum: options, + 'x-component': 'Cascader', + 'x-component-props': { + labelInValue: true, + }, + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/cascader/demos/new-demos/loadData.tsx b/packages/core/client/src/schema-component/antd/cascader/demos/new-demos/loadData.tsx new file mode 100644 index 0000000000..195172f74c --- /dev/null +++ b/packages/core/client/src/schema-component/antd/cascader/demos/new-demos/loadData.tsx @@ -0,0 +1,76 @@ +import { useField } from '@formily/react'; +import { getAppComponent } from '@nocobase/test/web'; +import React, { useState } from 'react'; + +interface Option { + value?: string | number | null; + label: React.ReactNode; + children?: Option[]; + isLeaf?: boolean; +} + +const optionLists: Option[] = [ + { + value: 'zhejiang', + label: 'Zhejiang', + isLeaf: false, + }, + { + value: 'jiangsu', + label: 'Jiangsu', + isLeaf: false, + }, +]; + +const useCustomCascaderProps = () => { + const field = useField(); + field.dataSource = optionLists; + const loadData = (selectedOptions: Option[]) => { + const targetOption = selectedOptions[selectedOptions.length - 1]; + + // load options lazily + setTimeout(() => { + targetOption.children = [ + { + label: `${targetOption.label} Dynamic 1`, + value: 'dynamic1', + }, + { + label: `${targetOption.label} Dynamic 2`, + value: 'dynamic2', + }, + ]; + field.dataSource = [...field.dataSource]; + }, 1000); + }; + + return { + changeOnSelect: true, + loadData, + }; +}; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + properties: { + test: { + type: 'string', + title: 'Test', + 'x-decorator': 'FormItem', + 'x-component': 'Cascader', + 'x-use-component-props': 'useCustomCascaderProps', + }, + }, + }, + appOptions: { + scopes: { + useCustomCascaderProps, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/cascader/demos/new-demos/read-pretty.tsx b/packages/core/client/src/schema-component/antd/cascader/demos/new-demos/read-pretty.tsx new file mode 100644 index 0000000000..b58e74d342 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/cascader/demos/new-demos/read-pretty.tsx @@ -0,0 +1,58 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const options = [ + { + value: 'zhejiang', + label: 'Zhejiang', + children: [ + { + value: 'hangzhou', + label: 'Hangzhou', + children: [ + { + value: 'xihu', + label: 'West Lake', + }, + ], + }, + ], + }, + { + value: 'jiangsu', + label: 'Jiangsu', + children: [ + { + value: 'nanjing', + label: 'Nanjing', + children: [ + { + value: 'zhonghuamen', + label: 'Zhong Hua Men', + }, + ], + }, + ], + }, +]; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + 'x-pattern': 'readPretty', + properties: { + test: { + type: 'string', + title: 'Test', + default: ['zhejiang', 'hangzhou', 'xihu'], + 'x-decorator': 'FormItem', + enum: options, + 'x-component': 'Cascader', + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/cascader/index.md b/packages/core/client/src/schema-component/antd/cascader/index.md index abaeae2274..78d63bd9c1 100644 --- a/packages/core/client/src/schema-component/antd/cascader/index.md +++ b/packages/core/client/src/schema-component/antd/cascader/index.md @@ -1,36 +1,53 @@ ---- -group: - title: Schema Components - order: 3 ---- - # Cascader -## Examples - -### Cascader - - - -### Asynchronous Data Source - - - -## API - -基于 antd 的 [Cascader](https://ant.design/components/cascader/#API) 附加的一些属性: - -- `labelInValue` 是否把每个选项的 label 包装到 value 中 -- `changeOnSelectLast` 必须选到最后一级 -- `useLoadData` 可调用 hook 的 loadData +级联选择器,其基于 ant-design [Cascader](https://ant.design/components/cascader-cn/) 组件封装。 ```ts -{ - useLoadData: (props) => { - // 这里可以写 hook - return function loadData(selectedOptions) { - // Cascader 的 loadData - } - } +type CascaderProps = AntdCascaderProps & { + /** + * Whether to wrap the label of option into the value + */ + labelInValue?: boolean; + /** + * must select the last level + */ + changeOnSelectLast?: boolean; } ``` + +## Basic Usage + + + +## Asynchronous Data Source + + + +## labelInValue + +如果设置 `labelInValue` 为 `true`,则选中的数据为 `{ label: string, value: string }` 格式,否则为 `string` 格式。 + + + +## changeOnSelectLast + +如果设置 `changeOnSelectLast` 为 `true`,则必须选择最后一级,如果为 `false`,则可以选择任意级。 + + + +## Read Pretty + +```ts +interface FieldNames { + label: string; + value: string; + children: string; +} + +export interface CascaderReadPrettyProps { + fieldNames?: FieldNames; + value?: any; +} +``` + + diff --git a/packages/core/client/src/schema-component/antd/checkbox/Checkbox.tsx b/packages/core/client/src/schema-component/antd/checkbox/Checkbox.tsx index f683d43b46..46dd9e77dd 100644 --- a/packages/core/client/src/schema-component/antd/checkbox/Checkbox.tsx +++ b/packages/core/client/src/schema-component/antd/checkbox/Checkbox.tsx @@ -11,21 +11,29 @@ import { CheckOutlined, CloseOutlined } from '@ant-design/icons'; import { connect, mapProps, mapReadPretty, useField } from '@formily/react'; import { isValid } from '@formily/shared'; import { Checkbox as AntdCheckbox, Tag } from 'antd'; -import type { CheckboxGroupProps, CheckboxProps } from 'antd/es/checkbox'; +import type { + CheckboxGroupProps as AntdCheckboxGroupProps, + CheckboxProps as AntdCheckboxProps, +} from 'antd/es/checkbox'; import uniq from 'lodash/uniq'; -import React, { useMemo } from 'react'; +import React, { FC, useMemo } from 'react'; import { useCollectionField } from '../../../data-source/collection-field/CollectionFieldProvider'; import { EllipsisWithTooltip } from '../input/EllipsisWithTooltip'; type ComposedCheckbox = React.ForwardRefExoticComponent< Pick, string | number | symbol> & React.RefAttributes > & { - Group?: React.FC; + Group?: React.FC; __ANT_CHECKBOX?: boolean; - ReadPretty?: React.FC; + ReadPretty?: React.FC; }; -const ReadPretty = (props) => { +export interface CheckboxReadPrettyProps { + showUnchecked?: boolean; + value?: boolean; +} + +const ReadPretty: FC = (props) => { if (props.value) { return ; } @@ -33,7 +41,7 @@ const ReadPretty = (props) => { }; export const Checkbox: ComposedCheckbox = connect( - (props: any) => { + (props: AntdCheckboxProps) => { const changeHandler = (val) => { props?.onChange(val); }; @@ -52,12 +60,17 @@ Checkbox.ReadPretty.displayName = 'Checkbox.ReadPretty'; Checkbox.__ANT_CHECKBOX = true; +export interface CheckboxGroupReadPrettyProps { + value?: any[]; + ellipsis?: boolean; +} + Checkbox.Group = connect( AntdCheckbox.Group, mapProps({ dataSource: 'options', }), - mapReadPretty((props) => { + mapReadPretty((props: CheckboxGroupReadPrettyProps) => { if (!isValid(props.value)) { return null; } diff --git a/packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/basic.tsx b/packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/basic.tsx new file mode 100644 index 0000000000..780323b173 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/basic.tsx @@ -0,0 +1,20 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + properties: { + test: { + type: 'boolean', + title: 'Test', + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox', + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/group-read-pretty.tsx b/packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/group-read-pretty.tsx new file mode 100644 index 0000000000..1ed3d01edf --- /dev/null +++ b/packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/group-read-pretty.tsx @@ -0,0 +1,57 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const options = [ + { + label: '选项1', + value: 1, + color: 'red', + }, + { + label: '选项2', + value: 2, + color: 'blue', + }, + { + label: '选项3', + value: 3, + color: 'yellow', + }, +]; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + 'x-pattern': 'readPretty', + properties: { + test1: { + type: 'array', + default: [1, 2], + title: 'Test1', + enum: options, + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox.Group', + }, + test2: { + type: 'array', + default: [1, 2, 3], + title: 'Test2', + enum: options, + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox.Group', + 'x-decorator-props': { + style: { + width: 100, + }, + }, + 'x-component-props': { + ellipsis: true, + }, + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/group.tsx b/packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/group.tsx new file mode 100644 index 0000000000..c1cfa50e6c --- /dev/null +++ b/packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/group.tsx @@ -0,0 +1,34 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const options = [ + { + label: '选项1', + value: 1, + color: 'red', + }, + { + label: '选项2', + value: 2, + color: 'blue', + }, +]; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + properties: { + test: { + type: 'array', + title: 'Test', + enum: options, + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox.Group', + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/read-pretty.tsx b/packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/read-pretty.tsx new file mode 100644 index 0000000000..4b1348079c --- /dev/null +++ b/packages/core/client/src/schema-component/antd/checkbox/demos/new-demos/read-pretty.tsx @@ -0,0 +1,39 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + 'x-pattern': 'readPretty', + properties: { + test: { + type: 'boolean', + default: true, + title: 'Test1', + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox', + }, + test2: { + type: 'boolean', + default: false, + title: 'Test2', + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox', + }, + test3: { + type: 'boolean', + default: false, + title: 'Test3', + 'x-decorator': 'FormItem', + 'x-component': 'Checkbox', + 'x-component-props': { + showUnchecked: true, + }, + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/checkbox/index.md b/packages/core/client/src/schema-component/antd/checkbox/index.md index a24296c51c..57a25e1cff 100644 --- a/packages/core/client/src/schema-component/antd/checkbox/index.md +++ b/packages/core/client/src/schema-component/antd/checkbox/index.md @@ -1,17 +1,46 @@ ---- -group: - title: Schema Components - order: 3 ---- - # Checkbox -## Examples +复选框,其基于 ant-design [Checkbox](https://ant.design/components/checkbox/) 组件封装。 -### 勾选 - +## Basic Usage -### 组 +```ts +type CheckboxProps = AntdCheckboxProps; +``` - + + +## Read Pretty + +```ts +interface CheckboxReadPrettyProps { + showUnchecked?: boolean; + value?: boolean; +} +``` + +如果值为 `false`,默认情况下不显示内容,可以通过 `showUnchecked` 属性来显示未选中的复选框。 + + + +## Checkbox Group + +```ts +type CheckboxGroupProps = CheckboxGroupProps; +``` + +注意 schema 的 type 属性为 `array`。 + + + +## Checkbox Group Read Pretty + +```ts +export interface CheckboxGroupReadPrettyProps { + value?: any[]; + ellipsis?: boolean; +} +``` + + diff --git a/packages/core/client/src/schema-component/antd/collection-select/demos/basic.tsx b/packages/core/client/src/schema-component/antd/collection-select/demos/basic.tsx new file mode 100644 index 0000000000..1d5c91f958 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/collection-select/demos/basic.tsx @@ -0,0 +1,20 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + properties: { + test: { + type: 'string', + title: 'Test', + 'x-decorator': 'FormItem', + 'x-component': 'CollectionSelect', + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/collection-select/demos/multiple.tsx b/packages/core/client/src/schema-component/antd/collection-select/demos/multiple.tsx new file mode 100644 index 0000000000..7049ebb9ae --- /dev/null +++ b/packages/core/client/src/schema-component/antd/collection-select/demos/multiple.tsx @@ -0,0 +1,23 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + properties: { + test: { + type: 'array', + title: 'Test', + 'x-decorator': 'FormItem', + 'x-component': 'CollectionSelect', + 'x-component-props': { + mode: 'multiple', + }, + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/collection-select/demos/read-pretty.tsx b/packages/core/client/src/schema-component/antd/collection-select/demos/read-pretty.tsx new file mode 100644 index 0000000000..851d59d3c2 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/collection-select/demos/read-pretty.tsx @@ -0,0 +1,22 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-component': 'ShowFormData', + 'x-decorator': 'FormV2', + 'x-read-pretty': true, + properties: { + test: { + type: 'string', + default: 'users', + title: 'Test', + 'x-decorator': 'FormItem', + 'x-component': 'CollectionSelect', + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/collection-select/index.md b/packages/core/client/src/schema-component/antd/collection-select/index.md index a1d9a0c005..28fd451655 100644 --- a/packages/core/client/src/schema-component/antd/collection-select/index.md +++ b/packages/core/client/src/schema-component/antd/collection-select/index.md @@ -1,8 +1,24 @@ ---- -group: - title: Schema Components ---- - # CollectionSelect -## Example +用于选择当前数据源的数据表。 + +```ts +type CollectionSelectProps = SelectProps & { + filter?: (item: any, index: number, array: any[]) => boolean; + isTableOid?: boolean; +}; +``` + +## Basic Usage + + + +## Multiple Selection + +`type` 需要改为 `array`,并且属性需要增加 `mode: 'multiple'`。 + + + +## Read Pretty + + diff --git a/packages/core/client/src/schema-component/antd/color-picker/ColorPicker.tsx b/packages/core/client/src/schema-component/antd/color-picker/ColorPicker.tsx index 33b99abf74..45a88c92f3 100644 --- a/packages/core/client/src/schema-component/antd/color-picker/ColorPicker.tsx +++ b/packages/core/client/src/schema-component/antd/color-picker/ColorPicker.tsx @@ -10,12 +10,16 @@ import { css } from '@emotion/css'; import { usePrefixCls } from '@formily/antd-v5/esm/__builtins__'; import { connect, mapProps, mapReadPretty } from '@formily/react'; -import { ColorPicker as AntdColorPicker } from 'antd'; +import { ColorPicker as AntdColorPicker, ColorPickerProps as AntdColorPickerProps } from 'antd'; import cls from 'classnames'; import React from 'react'; +export interface ColorPickerProps extends Omit { + onChange?: (color: string) => void; +} + export const ColorPicker = connect( - (props) => { + (props: ColorPickerProps) => { const { value, onChange, ...others } = props; return (
diff --git a/packages/core/client/src/schema-component/antd/color-picker/demos/new-demos/basic.tsx b/packages/core/client/src/schema-component/antd/color-picker/demos/new-demos/basic.tsx new file mode 100644 index 0000000000..f45203b915 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/color-picker/demos/new-demos/basic.tsx @@ -0,0 +1,20 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + properties: { + test: { + type: 'string', + title: 'Test', + 'x-decorator': 'FormItem', + 'x-component': 'ColorPicker', + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/color-picker/demos/new-demos/read-pretty.tsx b/packages/core/client/src/schema-component/antd/color-picker/demos/new-demos/read-pretty.tsx new file mode 100644 index 0000000000..d666c4ac11 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/color-picker/demos/new-demos/read-pretty.tsx @@ -0,0 +1,22 @@ +import { getAppComponent } from '@nocobase/test/web'; + +const App = getAppComponent({ + schema: { + type: 'void', + name: 'root', + 'x-decorator': 'FormV2', + 'x-component': 'ShowFormData', + 'x-pattern': 'readPretty', + properties: { + test: { + type: 'string', + default: '#8BBB11', + title: 'Test', + 'x-decorator': 'FormItem', + 'x-component': 'ColorPicker', + }, + }, + }, +}); + +export default App; diff --git a/packages/core/client/src/schema-component/antd/color-picker/index.md b/packages/core/client/src/schema-component/antd/color-picker/index.md index f30325a471..9240492f54 100644 --- a/packages/core/client/src/schema-component/antd/color-picker/index.md +++ b/packages/core/client/src/schema-component/antd/color-picker/index.md @@ -1,18 +1,17 @@ ---- -group: - title: Schema Components - order: 3 ---- - # ColorPicker -## Examples - -### Basic - - +颜色选择器,其基于 ant-design [ColorPicker](https://ant.design/components/color-picker/) 组件进行封装。 +```ts +interface ColorPickerProps extends Omit { + onChange?: (color: string) => void; +} +``` +## Basic Usage + +## Read Pretty + diff --git a/packages/core/client/src/schema-component/antd/color-select/ColorSelect.tsx b/packages/core/client/src/schema-component/antd/color-select/ColorSelect.tsx index 3daf9d95b8..7324b4dae7 100644 --- a/packages/core/client/src/schema-component/antd/color-select/ColorSelect.tsx +++ b/packages/core/client/src/schema-component/antd/color-select/ColorSelect.tsx @@ -9,7 +9,7 @@ import { LoadingOutlined } from '@ant-design/icons'; import { connect, mapProps, mapReadPretty } from '@formily/react'; -import { Select, Tag } from 'antd'; +import { Select, SelectProps, Tag } from 'antd'; import React from 'react'; import { useCompile } from '../../hooks/useCompile'; @@ -28,8 +28,12 @@ const colors = { default: '{{t("Default")}}', }; +export interface ColorSelectProps extends SelectProps { + suffix?: React.ReactNode; +} + export const ColorSelect = connect( - (props) => { + (props: ColorSelectProps) => { const compile = useCompile(); return (