diff --git a/package-lock.json b/package-lock.json index 65efc3e..b575a69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6886,11 +6886,6 @@ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, - "node_modules/devalue": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/devalue/-/devalue-4.3.3.tgz", - "integrity": "sha512-UH8EL6H2ifcY8TbD2QsxwCC/pr5xSwPvv85LrLXVihmHVC3T3YqTCIwnR5ak0yO1KYqlxrPVOA/JVZJYPy2ATg==" - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz", @@ -16096,12 +16091,12 @@ }, "packages/editor": { "name": "@simpleform/editor", - "version": "4.1.13", + "version": "4.2.0", "license": "ISC", "dependencies": { "@codemirror/lang-javascript": "^6.2.1", "@codemirror/lang-json": "^6.0.1", - "@simpleform/render": "^4.1.26", + "@simpleform/render": "^4.1.27", "@szhsin/react-menu": "^4.1.0", "@types/file-saver": "^2.0.7", "@types/js-beautify": "^1.14.3", @@ -16112,7 +16107,6 @@ "codemirror": "^6.0.1", "copy-anything": "^3.0.5", "dayjs": "^1.11.10", - "devalue": "^4.3.2", "file-saver": "^2.0.5", "js-beautify": "^1.14.11", "react-collapsed": "^4.1.2", @@ -16120,6 +16114,7 @@ "react-fast-compare": "^3.2.2", "react-responsive-modal": "^6.4.2", "react-sortablejs": "^6.1.4", + "serialize-javascript": "^6.0.2", "sortablejs": "^1.15.2" }, "devDependencies": { @@ -16164,7 +16159,7 @@ }, "packages/form": { "name": "@simpleform/form", - "version": "2.1.14", + "version": "2.1.15", "license": "ISC", "dependencies": { "classnames": "^2.3.2", @@ -16212,10 +16207,10 @@ }, "packages/render": { "name": "@simpleform/render", - "version": "4.1.26", + "version": "4.1.27", "license": "ISC", "dependencies": { - "@simpleform/form": "^2.1.14", + "@simpleform/form": "^2.1.15", "classnames": "^2.3.2", "copy-anything": "^3.0.5", "serialize-javascript": "^6.0.2" diff --git a/packages/editor/README.md b/packages/editor/README.md index 095f7ac..233840c 100644 --- a/packages/editor/README.md +++ b/packages/editor/README.md @@ -2,7 +2,7 @@ English | [中文说明](./README_CN.md) -[![](https://img.shields.io/badge/version-4.1.13-green)](https://www.npmjs.com/package/@simpleform/editor) +[![](https://img.shields.io/badge/version-4.1.14-green)](https://www.npmjs.com/package/@simpleform/editor) > Based on `react` implementation of the form designer , support for custom components , template import and export , visual design and other form design features , the secondary development is very simple . @@ -10,6 +10,9 @@ English | [中文说明](./README_CN.md) * [Guide](https://mezhanglei.github.io/simpleform/docs/#/) * [UI widgets](https://ant.design/index-cn/) `antd@5.x`, the designer registers the base form control by default. +# Matters + - It is recommended to use version `>=4.2`; + ## Introduction - Designer Composition: The designer consists of six modules * Configuration context `EditorProvider`: provides the context for the editor diff --git a/packages/editor/README_CN.md b/packages/editor/README_CN.md index 6d71ac9..2dec247 100644 --- a/packages/editor/README_CN.md +++ b/packages/editor/README_CN.md @@ -2,7 +2,7 @@ [English](./README.md) | 中文说明 -[![](https://img.shields.io/badge/version-4.1.13-green)](https://www.npmjs.com/package/@simpleform/editor) +[![](https://img.shields.io/badge/version-4.1.14-green)](https://www.npmjs.com/package/@simpleform/editor) > 基于`react`实现的表单设计器,支持自定义组件,模板导入导出,可视化设计等表单设计功能,二次开发非常简单。 @@ -10,6 +10,9 @@ * [开发指南](https://mezhanglei.github.io/simpleform/docs/#/) * [默认组件库](https://ant.design/index-cn/) `antd@5.x`,设计器默认注册了基础的表单控件. +# 注意 + - 建议使用稳定版本`>=4.2`; + ## 简介 - 设计器组成:设计器包含六大模块 * 配置上下文`EditorProvider`:提供编辑器的上下文环境 diff --git a/packages/editor/src/defineConfig.ts b/packages/editor/example/demo/FormEditor/FormRender/defineConfig.ts similarity index 55% rename from packages/editor/src/defineConfig.ts rename to packages/editor/example/demo/FormEditor/FormRender/defineConfig.ts index caf7d54..991c9ba 100644 --- a/packages/editor/src/defineConfig.ts +++ b/packages/editor/example/demo/FormEditor/FormRender/defineConfig.ts @@ -1,11 +1,18 @@ -import widgets from './components'; +import widgets from '../components'; import dayjs from 'dayjs'; +import { createRequest } from '@simpleform/editor'; + +// TODO axios请求配置 +const axiosConfig = { + +}; // 渲染引擎配置项 export default { // 组件内的变量 variables: { - dayjs + dayjs, + request: createRequest(axiosConfig) }, // 注册组件 components: widgets, diff --git a/packages/editor/example/demo/FormRender/index.tsx b/packages/editor/example/demo/FormEditor/FormRender/index.tsx similarity index 53% rename from packages/editor/example/demo/FormRender/index.tsx rename to packages/editor/example/demo/FormEditor/FormRender/index.tsx index 06d9005..4a91cda 100644 --- a/packages/editor/example/demo/FormRender/index.tsx +++ b/packages/editor/example/demo/FormEditor/FormRender/index.tsx @@ -1,21 +1,18 @@ import React from 'react'; -import { FormRender as DefaultFormRender, FormChildren as DefaultFormChildren, createRequest, CustomFormChildrenProps, CustomFormRenderProps } from '../../../src'; +import DefaultFormRender, { FormChildren as DefaultFormChildren } from '@simpleform/render'; +import { CustomFormChildrenProps, CustomFormRenderProps } from '@simpleform/editor'; +import '@simpleform/editor/lib/css/main.css'; import defineConfig from './defineConfig'; -export * from '../../../src'; - -// TODO axios请求配置 -const axiosConfig = { - -}; +export * from '@simpleform/render'; export function FormChildren(props: CustomFormChildrenProps) { const { components, variables, ...rest } = props; return ( ); @@ -25,9 +22,9 @@ export default function FormRender(props: CustomFormRenderProps) { const { components, variables, ...rest } = props; return ( ); diff --git a/packages/editor/example/demo/FormEditor/ImportTemplate/index.tsx b/packages/editor/example/demo/FormEditor/ImportTemplate/index.tsx index 13cbec5..311d5a8 100644 --- a/packages/editor/example/demo/FormEditor/ImportTemplate/index.tsx +++ b/packages/editor/example/demo/FormEditor/ImportTemplate/index.tsx @@ -2,35 +2,33 @@ import classnames from 'classnames'; import React, { useEffect, useState } from 'react'; import { Button, Flex, Tag } from 'antd'; import './index.less'; -import { CustomOptions, FormDesignData } from '../../FormRender'; -import ModalWrapper, { ModalWrapperProps } from '../../../../src/components/common/GlobalModal/modalWrapper'; +import { ModalWrapper, ModalWrapperProps, CustomOptions, FormDesignData } from '../'; import demo from './demo'; export interface TemplateItem { - img: string; + img?: string; name: string; data: FormDesignData; }; export interface ImportModalProps extends Partial { data?: Array onSelect?: (item?: TemplateItem) => void; - context?: CustomOptions['context']; + editorContext?: CustomOptions['editorContext']; } // 模板列表 const templateList = [{ name: 'demo', data: demo, -}]; +}] as TemplateItem[]; const ImportModal: React.FC = (props) => { const { - children, + onClose, className, open, - context, - onClose, + editorContext, data = templateList, onSelect, ...rest @@ -44,14 +42,15 @@ const ImportModal: React.FC = (props) => { const closeModal = () => { setModalOpen(false); + onClose?.(); }; const prefixCls = 'import-modal'; const cls = classnames(prefixCls, className); const load = (item?: TemplateItem) => { - context.dispatch((old) => ({ ...old, widgetList: item?.data })); - onSelect && onSelect(item); + editorContext?.dispatch((old) => ({ ...old, widgetList: item?.data })); + onSelect?.(item); setModalOpen(false); }; diff --git a/packages/editor/src/config/pc/divider/index.ts b/packages/editor/example/demo/FormEditor/components/Divider/config.ts similarity index 100% rename from packages/editor/src/config/pc/divider/index.ts rename to packages/editor/example/demo/FormEditor/components/Divider/config.ts diff --git a/packages/editor/src/components/common/Divider/index.tsx b/packages/editor/example/demo/FormEditor/components/Divider/index.tsx similarity index 87% rename from packages/editor/src/components/common/Divider/index.tsx rename to packages/editor/example/demo/FormEditor/components/Divider/index.tsx index 8f62c7d..7443424 100644 --- a/packages/editor/src/components/common/Divider/index.tsx +++ b/packages/editor/example/demo/FormEditor/components/Divider/index.tsx @@ -1,6 +1,6 @@ import { Divider, DividerProps } from 'antd'; import React from 'react'; -import { CommonFormProps } from '../../../formrender'; +import { CommonFormProps } from '@simpleform/editor'; // 分割线(在拖拽过程中会出现渲染报错,所以需要移除children属性) export const CustomDivider: React.FC & DividerProps & { label?: string }> = (props) => { diff --git a/packages/editor/src/config/pc/divider/setting.ts b/packages/editor/example/demo/FormEditor/components/Divider/setting.ts similarity index 97% rename from packages/editor/src/config/pc/divider/setting.ts rename to packages/editor/example/demo/FormEditor/components/Divider/setting.ts index 9011c78..fa0695d 100644 --- a/packages/editor/src/config/pc/divider/setting.ts +++ b/packages/editor/example/demo/FormEditor/components/Divider/setting.ts @@ -34,7 +34,7 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' diff --git a/packages/editor/example/demo/FormEditor/config/example/index.ts b/packages/editor/example/demo/FormEditor/components/Example/config.ts similarity index 100% rename from packages/editor/example/demo/FormEditor/config/example/index.ts rename to packages/editor/example/demo/FormEditor/components/Example/config.ts diff --git a/packages/editor/example/demo/FormRender/example/data.ts b/packages/editor/example/demo/FormEditor/components/Example/data.ts similarity index 100% rename from packages/editor/example/demo/FormRender/example/data.ts rename to packages/editor/example/demo/FormEditor/components/Example/data.ts diff --git a/packages/editor/example/demo/FormRender/example/index.tsx b/packages/editor/example/demo/FormEditor/components/Example/index.tsx similarity index 77% rename from packages/editor/example/demo/FormRender/example/index.tsx rename to packages/editor/example/demo/FormEditor/components/Example/index.tsx index 20ce66e..b42a24c 100644 --- a/packages/editor/example/demo/FormRender/example/index.tsx +++ b/packages/editor/example/demo/FormEditor/components/Example/index.tsx @@ -1,5 +1,6 @@ import React, { useEffect } from 'react'; -import FormRender, { useSimpleForm, FormRenderProps, CommonFormProps } from '..'; +import FormRender, { useSimpleForm, FormRenderProps } from '../../FormRender'; +import { CommonFormProps } from '@simpleform/editor'; import data from './data'; const ExampleGroup = (props: CommonFormProps) => { @@ -13,7 +14,7 @@ const ExampleGroup = (props: CommonFormProps) => { }, [value]); const handleChange: FormRenderProps['onFieldsChange'] = (_, values) => { - onChange && onChange(values); + onChange?.(values); }; return >(({ columns = [], @@ -32,10 +39,10 @@ const EditorTable = React.forwardRef>(({ placeholder: `${prefix}-placeholder`, }; - const { path, formrender, context } = _options || {}; + const { path, formrender, editorContext } = _options || {}; const commonOptions = getCommonOptions(_options); const columnsPath = joinFormPath(path, 'props.columns'); - const { settingForm, editorConfig } = context?.state || {}; + const { settingForm, editorConfig } = editorContext?.state || {}; // 监听列控件设置值 const columnInputChange: CustomFormRenderProps['onValuesChange'] = ({ value }) => { @@ -51,7 +58,7 @@ const EditorTable = React.forwardRef>(({ '操作属性': configSetting?.['操作属性'], '校验规则': configSetting?.['校验规则'] }); - context?.dispatch && context?.dispatch((old) => ({ + editorContext?.dispatch && editorContext?.dispatch((old) => ({ ...old, selected: Object.assign({ setting: mergeSetting }, nextSelected) })); @@ -102,7 +109,7 @@ const EditorTable = React.forwardRef>(({ _options={_childOptions} configLabel="表格列" onSelectHandler={onSelectHandler} - tools={[ copyItem(col, colIndex)} />]} + tools={[ copyItem(col, colIndex)} />]} >
diff --git a/packages/editor/src/components/common/FormTable/index.tsx b/packages/editor/example/demo/FormEditor/components/FormTable/index.tsx similarity index 93% rename from packages/editor/src/components/common/FormTable/index.tsx rename to packages/editor/example/demo/FormEditor/components/FormTable/index.tsx index 2882a8d..13d3fe4 100644 --- a/packages/editor/src/components/common/FormTable/index.tsx +++ b/packages/editor/example/demo/FormEditor/components/FormTable/index.tsx @@ -1,6 +1,6 @@ import { ColumnType, TableProps } from 'antd/lib/table'; import React, { CSSProperties } from 'react'; -import { CommonFormProps, CustomGenerateWidgetItem } from '../../../formrender'; +import { CommonFormProps, CustomGenerateWidgetItem } from '@simpleform/editor'; import EditorTable from './editor'; import FormTable from './render'; diff --git a/packages/editor/src/components/common/FormTable/render/columnGroup.tsx b/packages/editor/example/demo/FormEditor/components/FormTable/render/columnGroup.tsx similarity index 100% rename from packages/editor/src/components/common/FormTable/render/columnGroup.tsx rename to packages/editor/example/demo/FormEditor/components/FormTable/render/columnGroup.tsx diff --git a/packages/editor/src/components/common/FormTable/render/components.less b/packages/editor/example/demo/FormEditor/components/FormTable/render/components.less similarity index 100% rename from packages/editor/src/components/common/FormTable/render/components.less rename to packages/editor/example/demo/FormEditor/components/FormTable/render/components.less diff --git a/packages/editor/src/components/common/FormTable/render/components.tsx b/packages/editor/example/demo/FormEditor/components/FormTable/render/components.tsx similarity index 97% rename from packages/editor/src/components/common/FormTable/render/components.tsx rename to packages/editor/example/demo/FormEditor/components/FormTable/render/components.tsx index 043a7fc..b10d30f 100644 --- a/packages/editor/src/components/common/FormTable/render/components.tsx +++ b/packages/editor/example/demo/FormEditor/components/FormTable/render/components.tsx @@ -1,7 +1,7 @@ import React from 'react'; import classNames from 'classnames'; import "./components.less"; -import pickAttrs from '../../../../utils/pickAttrs'; +import { pickAttrs } from '@simpleform/editor'; const prefix = "form-table"; export const Classes = { diff --git a/packages/editor/src/components/common/FormTable/render/index.less b/packages/editor/example/demo/FormEditor/components/FormTable/render/index.less similarity index 100% rename from packages/editor/src/components/common/FormTable/render/index.less rename to packages/editor/example/demo/FormEditor/components/FormTable/render/index.less diff --git a/packages/editor/src/components/common/FormTable/render/index.tsx b/packages/editor/example/demo/FormEditor/components/FormTable/render/index.tsx similarity index 92% rename from packages/editor/src/components/common/FormTable/render/index.tsx rename to packages/editor/example/demo/FormEditor/components/FormTable/render/index.tsx index 69ded17..e6905f2 100644 --- a/packages/editor/src/components/common/FormTable/render/index.tsx +++ b/packages/editor/example/demo/FormEditor/components/FormTable/render/index.tsx @@ -1,11 +1,14 @@ import React, { useEffect, useMemo, useState } from "react"; import { Button, message, Table, TableProps } from "antd"; -import { renderWidgetItem, joinFormPath } from "../../../../formrender"; +import { + renderWidgetItem, + joinFormPath, + defaultGetId, + SvgIcon, + pickAttrs +} from "@simpleform/editor"; import './index.less'; -import { defaultGetId } from "../../../../utils/utils"; -import SvgIcon from '../../SvgIcon'; import { FormTableProps } from ".."; -import pickAttrs from '../../../../utils/pickAttrs'; const FormTable = React.forwardRef((props, ref) => { const { diff --git a/packages/editor/src/config/pc/formTable/setting.ts b/packages/editor/example/demo/FormEditor/components/FormTable/setting.ts similarity index 89% rename from packages/editor/src/config/pc/formTable/setting.ts rename to packages/editor/example/demo/FormEditor/components/FormTable/setting.ts index c89e820..dd4a21a 100644 --- a/packages/editor/src/config/pc/formTable/setting.ts +++ b/packages/editor/example/demo/FormEditor/components/FormTable/setting.ts @@ -21,14 +21,14 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' }, { name: 'props.disabled', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '禁用' @@ -38,7 +38,7 @@ const operationSetting = [ const rulesSetting = [ { name: 'rules', - type: 'RulesGroup', + type: 'ValidatorSetting', compact: true, props: { includes: ['required'] diff --git a/packages/editor/src/config/pc/grid/col-setting.ts b/packages/editor/example/demo/FormEditor/components/Grid/col-setting.ts similarity index 100% rename from packages/editor/src/config/pc/grid/col-setting.ts rename to packages/editor/example/demo/FormEditor/components/Grid/col-setting.ts diff --git a/packages/editor/example/demo/FormEditor/components/Grid/col.less b/packages/editor/example/demo/FormEditor/components/Grid/col.less new file mode 100644 index 0000000..ff4f29a --- /dev/null +++ b/packages/editor/example/demo/FormEditor/components/Grid/col.less @@ -0,0 +1,21 @@ +@form-primary-color: #FF4040; // 全局主色 + +.col-selection { + min-height: 40px; + height: 100%; + display: inline-flex; + flex-wrap: wrap; + padding: 10px; + word-wrap: break-word; + word-break: break-all; + box-sizing: border-box; + outline: 1px dashed #b8b8b8; +} + +.custom-col { + padding: 10px; +} + +.custom-col.edit-col { + padding: 1px; +} \ No newline at end of file diff --git a/packages/editor/src/components/common/grid/col.tsx b/packages/editor/example/demo/FormEditor/components/Grid/col.tsx similarity index 66% rename from packages/editor/src/components/common/grid/col.tsx rename to packages/editor/example/demo/FormEditor/components/Grid/col.tsx index 48bf332..e2dae21 100644 --- a/packages/editor/src/components/common/grid/col.tsx +++ b/packages/editor/example/demo/FormEditor/components/Grid/col.tsx @@ -2,14 +2,16 @@ import { ColProps } from 'antd'; import classnames from 'classnames'; import React from 'react'; import './col.less'; -import { joinFormPath, CustomCol, renderWidgetItem, CustomGenerateWidgetItem } from '../../../formrender'; -import BaseDnd from '../../../view/BaseDnd'; -import { BaseSelectionProps } from '../../../view/BaseSelection'; -import SvgIcon from '../SvgIcon'; -import { getCommonOptions, insertWidgetItem } from '../../../utils/utils'; -import BaseSelection from '../../../view/BaseSelection'; -import { getParent } from '../../../formrender'; -import GridColConfig from '../../../config/pc/grid/col'; +import { + BaseDnd, + BaseSelection, + BaseSelectionProps, + joinFormPath, + CustomCol, + renderWidgetItem, + CustomGenerateWidgetItem, + getCommonOptions +} from '@simpleform/editor'; export type CustomColProps = Omit & BaseSelectionProps & { column: ColProps & { children?: CustomGenerateWidgetItem[] }; @@ -33,15 +35,6 @@ const GridCol = React.forwardRef((props, ref) => 'edit-col': isEditor }); - const addCol = () => { - const nextIndex = (colIndex || 0) + 1; - const newItem = { - ...GridColConfig, - children: [] - }; - insertWidgetItem(formrender, newItem, nextIndex, getParent(path)); - }; - const childs = widgetList?.map((child, childIndex) => { const _childOptions = { ...commonOptions, @@ -59,7 +52,6 @@ const GridCol = React.forwardRef((props, ref) => _options={_options} className="col-selection" configLabel="栅格列" - tools={[]} > }>; diff --git a/packages/editor/src/config/pc/grid/setting.ts b/packages/editor/example/demo/FormEditor/components/Grid/setting.ts similarity index 100% rename from packages/editor/src/config/pc/grid/setting.ts rename to packages/editor/example/demo/FormEditor/components/Grid/setting.ts diff --git a/packages/editor/src/config/pc/imageUpload/index.ts b/packages/editor/example/demo/FormEditor/components/ImageUpload/config.ts similarity index 83% rename from packages/editor/src/config/pc/imageUpload/index.ts rename to packages/editor/example/demo/FormEditor/components/ImageUpload/config.ts index 908e6f2..44f4abd 100644 --- a/packages/editor/src/config/pc/imageUpload/index.ts +++ b/packages/editor/example/demo/FormEditor/components/ImageUpload/config.ts @@ -1,5 +1,5 @@ import Setting from './setting'; -import FieldSetting from '../../fieldSetting'; +import FieldSetting from '../../editorConfig/fieldSetting'; export default { panel: { diff --git a/packages/editor/src/components/pc/ImageUpload/index.tsx b/packages/editor/example/demo/FormEditor/components/ImageUpload/index.tsx similarity index 96% rename from packages/editor/src/components/pc/ImageUpload/index.tsx rename to packages/editor/example/demo/FormEditor/components/ImageUpload/index.tsx index c207f9e..06c0bb6 100644 --- a/packages/editor/src/components/pc/ImageUpload/index.tsx +++ b/packages/editor/example/demo/FormEditor/components/ImageUpload/index.tsx @@ -2,10 +2,8 @@ import { message, Modal, Upload, UploadProps } from 'antd'; import { RcFile } from 'antd/lib/upload'; import React, { useEffect, useState } from 'react'; import { UploadFile } from 'antd/lib/upload/interface'; -import { objectToFormData } from '../../../utils/object'; import { getBase64 } from './util'; -import { IMAGE_MIME_KEYS, isImageFile } from '../../../utils/mime'; -import { CommonFormProps } from '../../../formrender'; +import { CommonFormProps, objectToFormData, IMAGE_MIME_KEYS, isImageFile } from '@simpleform/editor'; import { AxiosInstance } from 'axios'; // 扩展后的文件类型 diff --git a/packages/editor/src/config/pc/imageUpload/setting.ts b/packages/editor/example/demo/FormEditor/components/ImageUpload/setting.ts similarity index 92% rename from packages/editor/src/config/pc/imageUpload/setting.ts rename to packages/editor/example/demo/FormEditor/components/ImageUpload/setting.ts index e2a7c5f..38a1996 100644 --- a/packages/editor/src/config/pc/imageUpload/setting.ts +++ b/packages/editor/example/demo/FormEditor/components/ImageUpload/setting.ts @@ -37,14 +37,14 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' }, { name: 'props.disabled', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '禁用' @@ -54,7 +54,7 @@ const operationSetting = [ const rulesSetting = [ { name: 'rules', - type: 'RulesGroup', + type: 'ValidatorSetting', compact: true, props: { includes: ['required'] diff --git a/packages/editor/src/components/pc/ImageUpload/util.ts b/packages/editor/example/demo/FormEditor/components/ImageUpload/util.ts similarity index 100% rename from packages/editor/src/components/pc/ImageUpload/util.ts rename to packages/editor/example/demo/FormEditor/components/ImageUpload/util.ts diff --git a/packages/editor/src/components/common/LayoutTable/TableCell.tsx b/packages/editor/example/demo/FormEditor/components/LayoutTable/TableCell.tsx similarity index 91% rename from packages/editor/src/components/common/LayoutTable/TableCell.tsx rename to packages/editor/example/demo/FormEditor/components/LayoutTable/TableCell.tsx index 60e7439..9436de9 100644 --- a/packages/editor/src/components/common/LayoutTable/TableCell.tsx +++ b/packages/editor/example/demo/FormEditor/components/LayoutTable/TableCell.tsx @@ -1,22 +1,26 @@ import React, { useState } from 'react'; -import BaseSelection, { BaseSelectionProps } from '../../../view/BaseSelection'; -import { joinFormPath, renderWidgetItem, } from "../../../formrender"; +import { + BaseSelection, + BaseSelectionProps, + getCommonOptions, + SvgIcon, + BaseDnd, + joinFormPath, + renderWidgetItem, + TableUtils +} from "@simpleform/editor"; import classnames from 'classnames'; -import BaseDnd from '../../../view/BaseDnd'; -import { getCommonOptions } from '../../../utils/utils'; -import SvgIcon from '../SvgIcon'; import './index.less'; import { Menu, MenuDivider, MenuItem } from '@szhsin/react-menu'; import '@szhsin/react-menu/dist/index.css'; import { LayoutTableRows } from '.'; -import TableMergeUtils from "../../../utils/tableUtils"; export type CustomTableCellProps = React.HtmlHTMLAttributes & BaseSelectionProps & { rows: NonNullable; rowIndex: number; cols: NonNullable; colIndex: number; - tableUtils: TableMergeUtils; + tableUtils: TableUtils; }; const CustomTableCell = React.forwardRef((props, ref) => { @@ -31,7 +35,7 @@ const CustomTableCell = React.forwardRef({ @@ -134,7 +138,7 @@ const CustomTableCell = React.forwardRef { - context?.dispatch((old) => ({ ...old, selected: nextSelected })); + editorContext?.dispatch((old) => ({ ...old, selected: nextSelected })); handleSelectCell(rowIndex, colIndex); }} > diff --git a/packages/editor/src/config/pc/layoutTable/cell-setting.ts b/packages/editor/example/demo/FormEditor/components/LayoutTable/cell-setting.ts similarity index 100% rename from packages/editor/src/config/pc/layoutTable/cell-setting.ts rename to packages/editor/example/demo/FormEditor/components/LayoutTable/cell-setting.ts diff --git a/packages/editor/example/demo/FormEditor/components/LayoutTable/config.ts b/packages/editor/example/demo/FormEditor/components/LayoutTable/config.ts new file mode 100644 index 0000000..5ebf5fe --- /dev/null +++ b/packages/editor/example/demo/FormEditor/components/LayoutTable/config.ts @@ -0,0 +1,29 @@ +import Setting from './setting'; +import cellSetting from './cell-setting'; + +export const TableCellConfig = { + panel: { + label: '单元格', + nonform: true, + nonselection: true, + }, + type: 'TableCell', + colspan: 1, + rowspan: 1, + children: [], + setting: cellSetting, +}; + +export default { + panel: { + icon: 'table', + label: '表格布局', + nonform: true, + nonselection: true, + }, + type: 'LayoutTable', + rows: [ + [TableCellConfig] + ], + setting: Setting, +}; diff --git a/packages/editor/src/components/common/LayoutTable/index.less b/packages/editor/example/demo/FormEditor/components/LayoutTable/index.less similarity index 100% rename from packages/editor/src/components/common/LayoutTable/index.less rename to packages/editor/example/demo/FormEditor/components/LayoutTable/index.less diff --git a/packages/editor/src/components/common/LayoutTable/index.tsx b/packages/editor/example/demo/FormEditor/components/LayoutTable/index.tsx similarity index 83% rename from packages/editor/src/components/common/LayoutTable/index.tsx rename to packages/editor/example/demo/FormEditor/components/LayoutTable/index.tsx index d5868c6..60a674f 100644 --- a/packages/editor/src/components/common/LayoutTable/index.tsx +++ b/packages/editor/example/demo/FormEditor/components/LayoutTable/index.tsx @@ -1,15 +1,20 @@ import React, { CSSProperties, useMemo } from "react"; import classnames from "classnames"; import './index.less'; -import pickAttrs from '../../../utils/pickAttrs'; -import BaseSelection from '../../../view/BaseSelection'; -import SvgIcon from '../SvgIcon'; -import { joinFormPath, CommonFormProps, CustomGenerateWidgetItem } from "../../../formrender"; +import { + pickAttrs, + joinFormPath, + CommonFormProps, + CustomGenerateWidgetItem, + BaseSelection, + SvgIcon, + getCommonOptions, + setWidgetItem, + TableUtils, + TableCellType +} from "@simpleform/editor"; import TableCell from './TableCell'; -import { getCommonOptions, setWidgetItem } from '../../../utils/utils'; -import TableMergeUtils from "../../../utils/tableUtils"; -import { TableCellType } from "../../../utils/tableUtils/util"; -import cellConfig from '../../../config/pc/layoutTable/cell'; +import { TableCellConfig } from "./config"; const prefix = "r-"; export const Classes = { @@ -35,7 +40,7 @@ const Table = React.forwardRef((props, ref) const { formrender, isEditor, rows = [], path: tablePath } = _options || {}; const commonOptions = getCommonOptions(_options); const rowsPath = joinFormPath(tablePath, 'rows'); - const tableUtils = useMemo(() => new TableMergeUtils<{ + const tableUtils = useMemo(() => new TableUtils<{ children: LayoutTableRows }>(rows, { minRetainRow: 1, @@ -43,7 +48,7 @@ const Table = React.forwardRef((props, ref) minSplitVrowspan: 1, fixRowType: 2, CELL_DEFAULT_CONFIG: { - ...cellConfig + ...TableCellConfig }, onUpdate(newData) { setWidgetItem(formrender, newData?.rows, rowsPath); diff --git a/packages/editor/src/config/pc/layoutTable/setting.ts b/packages/editor/example/demo/FormEditor/components/LayoutTable/setting.ts similarity index 100% rename from packages/editor/src/config/pc/layoutTable/setting.ts rename to packages/editor/example/demo/FormEditor/components/LayoutTable/setting.ts diff --git a/packages/editor/src/config/pc/richEditor/index.ts b/packages/editor/example/demo/FormEditor/components/RichEditor/config.ts similarity index 82% rename from packages/editor/src/config/pc/richEditor/index.ts rename to packages/editor/example/demo/FormEditor/components/RichEditor/config.ts index c93838b..dba41f2 100644 --- a/packages/editor/src/config/pc/richEditor/index.ts +++ b/packages/editor/example/demo/FormEditor/components/RichEditor/config.ts @@ -1,5 +1,5 @@ import Setting from './setting'; -import FieldSetting from '../../fieldSetting'; +import FieldSetting from '../../editorConfig/fieldSetting'; export default { panel: { diff --git a/packages/editor/src/components/common/RichEditor/index.less b/packages/editor/example/demo/FormEditor/components/RichEditor/index.less similarity index 100% rename from packages/editor/src/components/common/RichEditor/index.less rename to packages/editor/example/demo/FormEditor/components/RichEditor/index.less diff --git a/packages/editor/src/components/common/RichEditor/index.tsx b/packages/editor/example/demo/FormEditor/components/RichEditor/index.tsx similarity index 90% rename from packages/editor/src/components/common/RichEditor/index.tsx rename to packages/editor/example/demo/FormEditor/components/RichEditor/index.tsx index 5942e70..67e715f 100644 --- a/packages/editor/src/components/common/RichEditor/index.tsx +++ b/packages/editor/example/demo/FormEditor/components/RichEditor/index.tsx @@ -2,16 +2,11 @@ import React, { useEffect, useRef, useState } from 'react'; import classnames from 'classnames'; import { Input, Button, ButtonProps } from 'antd'; import './index.less'; -import CustomModal from '../AntdModal'; import Quill from 'quill'; import 'quill/dist/quill.snow.css'; -import { CommonFormProps } from '../../../formrender'; +import { CustomModal, CommonFormProps } from '@simpleform/editor'; - -export interface RichEditorProps extends CommonFormProps { -} - -const RichEditor: React.FC = (props) => { +const RichEditor: React.FC> = (props) => { const { value, diff --git a/packages/editor/src/config/pc/richEditor/setting.ts b/packages/editor/example/demo/FormEditor/components/RichEditor/setting.ts similarity index 86% rename from packages/editor/src/config/pc/richEditor/setting.ts rename to packages/editor/example/demo/FormEditor/components/RichEditor/setting.ts index b13c131..06daa34 100644 --- a/packages/editor/src/config/pc/richEditor/setting.ts +++ b/packages/editor/example/demo/FormEditor/components/RichEditor/setting.ts @@ -9,14 +9,14 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' }, { name: 'props.disabled', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '禁用' @@ -26,7 +26,7 @@ const operationSetting = [ const rulesSetting = [ { name: 'rules', - type: 'RulesGroup', + type: 'ValidatorSetting', compact: true, props: { includes: ['required', 'pattern', 'max', 'min'], diff --git a/packages/editor/src/config/pc/richText/index.ts b/packages/editor/example/demo/FormEditor/components/RichText/config.ts similarity index 100% rename from packages/editor/src/config/pc/richText/index.ts rename to packages/editor/example/demo/FormEditor/components/RichText/config.ts diff --git a/packages/editor/src/components/common/RichText/index.tsx b/packages/editor/example/demo/FormEditor/components/RichText/index.tsx similarity index 86% rename from packages/editor/src/components/common/RichText/index.tsx rename to packages/editor/example/demo/FormEditor/components/RichText/index.tsx index 1cd5d45..0ff32c6 100644 --- a/packages/editor/src/components/common/RichText/index.tsx +++ b/packages/editor/example/demo/FormEditor/components/RichText/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { CommonFormProps } from '../../../formrender'; +import { CommonFormProps } from '@simpleform/editor'; // 按钮弹窗富文本 export const RichText = React.forwardRef>((props, ref) => { diff --git a/packages/editor/src/config/pc/richText/setting.ts b/packages/editor/example/demo/FormEditor/components/RichText/setting.ts similarity index 91% rename from packages/editor/src/config/pc/richText/setting.ts rename to packages/editor/example/demo/FormEditor/components/RichText/setting.ts index ff5be8f..1eb2cf6 100644 --- a/packages/editor/src/config/pc/richText/setting.ts +++ b/packages/editor/example/demo/FormEditor/components/RichText/setting.ts @@ -9,7 +9,7 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' diff --git a/packages/editor/example/demo/FormEditor/components/config.ts b/packages/editor/example/demo/FormEditor/components/config.ts new file mode 100644 index 0000000..215beee --- /dev/null +++ b/packages/editor/example/demo/FormEditor/components/config.ts @@ -0,0 +1,22 @@ +import RichEditor from "../components/RichEditor/config"; +import RichText from "../components/RichText/config"; +import FileUpload from '../components/FileUpload/config'; +import ImageUpload from '../components/ImageUpload/config'; +import Grid from '../components/Grid/config'; +import LayoutTable from '../components/LayoutTable/config'; +import FormTable from "../components/FormTable/config"; +import Example from './Example/config'; +import Divider from './Divider/config'; + +// 编辑器组件配置项 +export default { + Grid, + LayoutTable, + FormTable, + FileUpload, + ImageUpload, + RichText, + RichEditor, + Divider, + Example, +}; diff --git a/packages/editor/src/components/pc/index.ts b/packages/editor/example/demo/FormEditor/components/index.ts similarity index 73% rename from packages/editor/src/components/pc/index.ts rename to packages/editor/example/demo/FormEditor/components/index.ts index f94f810..bf1afb0 100644 --- a/packages/editor/src/components/pc/index.ts +++ b/packages/editor/example/demo/FormEditor/components/index.ts @@ -13,13 +13,24 @@ import { Cascader, Alert, } from 'antd'; -import { CustomFormRenderProps } from '../../formrender'; -import bindRequest from '../bind-request'; -import { CustomDivider } from '../common/Divider'; import FileUpload from './FileUpload'; +import RichEditor, { RichEditorModalBtn } from './RichEditor'; +import { RichText } from './RichText'; +import FormTable from './FormTable'; +import Grid from './Grid'; +import LayoutTable from './LayoutTable'; +import { CustomDivider } from './Divider'; import ImageUpload from './ImageUpload'; +import { + ValidatorSetting, + CodeTextArea, + DataSetting, + CheckboxWithRules, + bindRequest, + CustomFormRenderProps +} from '@simpleform/editor'; -// ant-design UI组件 +// 注册组件 const widgets: CustomFormRenderProps['components'] = { // ui库组件 "Input": Input, // 输入控件 @@ -49,5 +60,17 @@ const widgets: CustomFormRenderProps['components'] = { // 自定义组件 "FileUpload": FileUpload, // 文件上传 "ImageUpload": ImageUpload, // 图片上传 + RichEditor, + RichText, + RichEditorModalBtn, + FormTable, + LayoutTable, + Grid, + // 默认导出组件 + CodeTextArea, + ValidatorSetting, + DataSetting, + CheckboxWithRules }; + export default widgets; diff --git a/packages/editor/example/demo/FormEditor/config/index.ts b/packages/editor/example/demo/FormEditor/config/index.ts deleted file mode 100644 index e9ecc59..0000000 --- a/packages/editor/example/demo/FormEditor/config/index.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import GroupExample from './example'; - -export default { - "example": GroupExample, -}; diff --git a/packages/editor/src/config/pc/alert/index.ts b/packages/editor/example/demo/FormEditor/editorConfig/Alert/index.ts similarity index 100% rename from packages/editor/src/config/pc/alert/index.ts rename to packages/editor/example/demo/FormEditor/editorConfig/Alert/index.ts diff --git a/packages/editor/src/config/pc/alert/setting.ts b/packages/editor/example/demo/FormEditor/editorConfig/Alert/setting.ts similarity index 97% rename from packages/editor/src/config/pc/alert/setting.ts rename to packages/editor/example/demo/FormEditor/editorConfig/Alert/setting.ts index 5345236..23e55ed 100644 --- a/packages/editor/src/config/pc/alert/setting.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/Alert/setting.ts @@ -28,7 +28,7 @@ const baseSetting = [ const operationSetting = [ { - type: 'OperateCheckbox', + type: 'CheckboxWithRules', name: 'hidden', inline: true, compact: true, diff --git a/packages/editor/src/config/pc/cascader/index.ts b/packages/editor/example/demo/FormEditor/editorConfig/Cascader/index.ts similarity index 95% rename from packages/editor/src/config/pc/cascader/index.ts rename to packages/editor/example/demo/FormEditor/editorConfig/Cascader/index.ts index c820e27..c376d60 100644 --- a/packages/editor/src/config/pc/cascader/index.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/Cascader/index.ts @@ -1,5 +1,5 @@ import Setting from './setting'; -import FieldSetting from '../../fieldSetting'; +import FieldSetting from '../fieldSetting'; export default { panel: { diff --git a/packages/editor/src/config/pc/cascader/setting.ts b/packages/editor/example/demo/FormEditor/editorConfig/Cascader/setting.ts similarity index 83% rename from packages/editor/src/config/pc/cascader/setting.ts rename to packages/editor/example/demo/FormEditor/editorConfig/Cascader/setting.ts index 22a7b6d..78af75b 100644 --- a/packages/editor/src/config/pc/cascader/setting.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/Cascader/setting.ts @@ -11,7 +11,7 @@ const baseSetting = [ }, { name: 'props.options', - type: 'SetOptions', + type: 'DataSetting', label: '选项数据', props: { includes: ['json', 'request', 'dynamic'] @@ -22,35 +22,35 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' }, { name: 'props.disabled', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '禁用' }, { name: 'props.allowClear', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '可清除' }, { name: 'props.multiple', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '多选' }, { name: 'props.showSearch', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '可搜索' @@ -60,7 +60,7 @@ const operationSetting = [ const rulesSetting = [ { name: 'rules', - type: 'RulesGroup', + type: 'ValidatorSetting', compact: true, props: { includes: ['required'] diff --git a/packages/editor/src/config/pc/checkboxGroup/index.ts b/packages/editor/example/demo/FormEditor/editorConfig/CheckboxGroup/index.ts similarity index 89% rename from packages/editor/src/config/pc/checkboxGroup/index.ts rename to packages/editor/example/demo/FormEditor/editorConfig/CheckboxGroup/index.ts index bbe60b6..6964f3c 100644 --- a/packages/editor/src/config/pc/checkboxGroup/index.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/CheckboxGroup/index.ts @@ -1,5 +1,5 @@ import Setting from './setting'; -import FieldSetting from '../../fieldSetting'; +import FieldSetting from '../fieldSetting'; export default { panel: { diff --git a/packages/editor/src/config/pc/checkboxGroup/setting.ts b/packages/editor/example/demo/FormEditor/editorConfig/CheckboxGroup/setting.ts similarity index 86% rename from packages/editor/src/config/pc/checkboxGroup/setting.ts rename to packages/editor/example/demo/FormEditor/editorConfig/CheckboxGroup/setting.ts index 05858fe..abbc844 100644 --- a/packages/editor/src/config/pc/checkboxGroup/setting.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/CheckboxGroup/setting.ts @@ -11,7 +11,7 @@ const baseSetting = [ }, { name: 'props.options', - type: 'SetOptions', + type: 'DataSetting', label: '选项数据', props: { } @@ -21,14 +21,14 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' }, { name: 'props.disabled', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '禁用' @@ -38,7 +38,7 @@ const operationSetting = [ const rulesSetting = [ { name: 'rules', - type: 'RulesGroup', + type: 'ValidatorSetting', compact: true, props: { includes: ['required'] diff --git a/packages/editor/src/config/pc/datePicker/index.ts b/packages/editor/example/demo/FormEditor/editorConfig/DatePicker/index.ts similarity index 91% rename from packages/editor/src/config/pc/datePicker/index.ts rename to packages/editor/example/demo/FormEditor/editorConfig/DatePicker/index.ts index 88ed11e..8ec2d53 100644 --- a/packages/editor/src/config/pc/datePicker/index.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/DatePicker/index.ts @@ -1,5 +1,5 @@ import Setting from './setting'; -import FieldSetting from '../../fieldSetting'; +import FieldSetting from '../fieldSetting'; export default { panel: { diff --git a/packages/editor/src/config/pc/datePicker/setting.ts b/packages/editor/example/demo/FormEditor/editorConfig/DatePicker/setting.ts similarity index 95% rename from packages/editor/src/config/pc/datePicker/setting.ts rename to packages/editor/example/demo/FormEditor/editorConfig/DatePicker/setting.ts index 6f1fa12..6e1a053 100644 --- a/packages/editor/src/config/pc/datePicker/setting.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/DatePicker/setting.ts @@ -73,21 +73,21 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' }, { name: 'props.disabled', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '禁用' }, { name: 'props.allowClear', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '可清除' @@ -97,7 +97,7 @@ const operationSetting = [ const rulesSetting = [ { name: 'rules', - type: 'RulesGroup', + type: 'ValidatorSetting', compact: true, props: { includes: ['required'] diff --git a/packages/editor/src/config/pc/datePickerRangePicker/index.ts b/packages/editor/example/demo/FormEditor/editorConfig/DatePickerRangePicker/index.ts similarity index 93% rename from packages/editor/src/config/pc/datePickerRangePicker/index.ts rename to packages/editor/example/demo/FormEditor/editorConfig/DatePickerRangePicker/index.ts index 7ba84a6..30e1b2d 100644 --- a/packages/editor/src/config/pc/datePickerRangePicker/index.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/DatePickerRangePicker/index.ts @@ -1,5 +1,5 @@ import Setting from './setting'; -import FieldSetting from '../../fieldSetting'; +import FieldSetting from '../fieldSetting'; export default { panel: { diff --git a/packages/editor/src/config/pc/datePickerRangePicker/setting.ts b/packages/editor/example/demo/FormEditor/editorConfig/DatePickerRangePicker/setting.ts similarity index 95% rename from packages/editor/src/config/pc/datePickerRangePicker/setting.ts rename to packages/editor/example/demo/FormEditor/editorConfig/DatePickerRangePicker/setting.ts index e97d56c..befa072 100644 --- a/packages/editor/src/config/pc/datePickerRangePicker/setting.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/DatePickerRangePicker/setting.ts @@ -73,21 +73,21 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' }, { name: 'props.disabled', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '禁用' }, { name: 'props.allowClear', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '可清除' @@ -97,7 +97,7 @@ const operationSetting = [ const rulesSetting = [ { name: 'rules', - type: 'RulesGroup', + type: 'ValidatorSetting', compact: true, props: { includes: ['required'] diff --git a/packages/editor/src/config/pc/input/index.ts b/packages/editor/example/demo/FormEditor/editorConfig/Input/index.ts similarity index 81% rename from packages/editor/src/config/pc/input/index.ts rename to packages/editor/example/demo/FormEditor/editorConfig/Input/index.ts index 16c0c48..85a86f7 100644 --- a/packages/editor/src/config/pc/input/index.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/Input/index.ts @@ -1,5 +1,5 @@ import Setting from './setting'; -import FieldSetting from '../../fieldSetting'; +import FieldSetting from '../fieldSetting'; export default { panel: { diff --git a/packages/editor/src/config/pc/input/setting.ts b/packages/editor/example/demo/FormEditor/editorConfig/Input/setting.ts similarity index 93% rename from packages/editor/src/config/pc/input/setting.ts rename to packages/editor/example/demo/FormEditor/editorConfig/Input/setting.ts index 687be4d..921e436 100644 --- a/packages/editor/src/config/pc/input/setting.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/Input/setting.ts @@ -55,21 +55,21 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' }, { name: 'props.disabled', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '禁用' }, { name: 'props.allowClear', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '可清除' @@ -79,7 +79,7 @@ const operationSetting = [ const rulesSetting = [ { name: 'rules', - type: 'RulesGroup', + type: 'ValidatorSetting', compact: true, props: { includes: ['required', 'pattern', 'max', 'min'], diff --git a/packages/editor/src/config/pc/radioGroup/index.ts b/packages/editor/example/demo/FormEditor/editorConfig/RadioGroup/index.ts similarity index 86% rename from packages/editor/src/config/pc/radioGroup/index.ts rename to packages/editor/example/demo/FormEditor/editorConfig/RadioGroup/index.ts index da5c081..45e9da7 100644 --- a/packages/editor/src/config/pc/radioGroup/index.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/RadioGroup/index.ts @@ -1,5 +1,5 @@ import Setting from './setting'; -import FieldSetting from '../../fieldSetting'; +import FieldSetting from '../fieldSetting'; export default { panel: { diff --git a/packages/editor/src/config/pc/radioGroup/setting.ts b/packages/editor/example/demo/FormEditor/editorConfig/RadioGroup/setting.ts similarity index 91% rename from packages/editor/src/config/pc/radioGroup/setting.ts rename to packages/editor/example/demo/FormEditor/editorConfig/RadioGroup/setting.ts index dc8f43c..dd074d9 100644 --- a/packages/editor/src/config/pc/radioGroup/setting.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/RadioGroup/setting.ts @@ -12,7 +12,7 @@ const baseSetting = [ { label: '选项数据', name: 'props.options', - type: 'SetOptions', + type: 'DataSetting', props: { } }, @@ -48,14 +48,14 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' }, { name: 'props.disabled', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '禁用' @@ -65,7 +65,7 @@ const operationSetting = [ const rulesSetting = [ { name: 'rules', - type: 'RulesGroup', + type: 'ValidatorSetting', compact: true, props: { includes: ['required'], diff --git a/packages/editor/src/config/pc/rate/index.ts b/packages/editor/example/demo/FormEditor/editorConfig/Rate/index.ts similarity index 81% rename from packages/editor/src/config/pc/rate/index.ts rename to packages/editor/example/demo/FormEditor/editorConfig/Rate/index.ts index 17c32af..35c9f28 100644 --- a/packages/editor/src/config/pc/rate/index.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/Rate/index.ts @@ -1,5 +1,5 @@ import Setting from './setting'; -import FieldSetting from '../../fieldSetting'; +import FieldSetting from '../fieldSetting'; export default { panel: { diff --git a/packages/editor/src/config/pc/rate/setting.ts b/packages/editor/example/demo/FormEditor/editorConfig/Rate/setting.ts similarity index 86% rename from packages/editor/src/config/pc/rate/setting.ts rename to packages/editor/example/demo/FormEditor/editorConfig/Rate/setting.ts index a6d7386..7777cd6 100644 --- a/packages/editor/src/config/pc/rate/setting.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/Rate/setting.ts @@ -20,28 +20,28 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' }, { name: 'props.disabled', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '禁用' }, { name: 'props.allowClear', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '可清除' }, { name: 'props.allowHalf', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '可半选' @@ -51,7 +51,7 @@ const operationSetting = [ const rulesSetting = [ { name: 'rules', - type: 'RulesGroup', + type: 'ValidatorSetting', compact: true, props: { includes: ['required', 'max', 'min'], diff --git a/packages/editor/src/config/pc/select/index.ts b/packages/editor/example/demo/FormEditor/editorConfig/Select/index.ts similarity index 87% rename from packages/editor/src/config/pc/select/index.ts rename to packages/editor/example/demo/FormEditor/editorConfig/Select/index.ts index 140117a..247323e 100644 --- a/packages/editor/src/config/pc/select/index.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/Select/index.ts @@ -1,5 +1,5 @@ import Setting from './setting'; -import FieldSetting from '../../fieldSetting'; +import FieldSetting from '../fieldSetting'; export default { panel: { diff --git a/packages/editor/src/config/pc/select/setting.ts b/packages/editor/example/demo/FormEditor/editorConfig/Select/setting.ts similarity index 91% rename from packages/editor/src/config/pc/select/setting.ts rename to packages/editor/example/demo/FormEditor/editorConfig/Select/setting.ts index b3130de..afcf887 100644 --- a/packages/editor/src/config/pc/select/setting.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/Select/setting.ts @@ -17,7 +17,7 @@ const baseSetting = [ }, { name: 'props.options', - type: 'SetOptions', + type: 'DataSetting', label: '选项数据', props: { } @@ -61,28 +61,28 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' }, { name: 'props.disabled', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '禁用' }, { name: 'props.allowClear', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '可清除' }, { name: 'props.showSearch', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '可搜索' @@ -92,7 +92,7 @@ const operationSetting = [ const rulesSetting = [ { name: 'rules', - type: 'RulesGroup', + type: 'ValidatorSetting', compact: true, props: { includes: ['required'], diff --git a/packages/editor/src/config/pc/slider/index.ts b/packages/editor/example/demo/FormEditor/editorConfig/Slider/index.ts similarity index 84% rename from packages/editor/src/config/pc/slider/index.ts rename to packages/editor/example/demo/FormEditor/editorConfig/Slider/index.ts index 1f61872..2497038 100644 --- a/packages/editor/src/config/pc/slider/index.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/Slider/index.ts @@ -1,5 +1,5 @@ import Setting from './setting'; -import FieldSetting from '../../fieldSetting'; +import FieldSetting from '../fieldSetting'; export default { panel: { diff --git a/packages/editor/src/config/pc/slider/setting.ts b/packages/editor/example/demo/FormEditor/editorConfig/Slider/setting.ts similarity index 88% rename from packages/editor/src/config/pc/slider/setting.ts rename to packages/editor/example/demo/FormEditor/editorConfig/Slider/setting.ts index e74e2fb..5df4290 100644 --- a/packages/editor/src/config/pc/slider/setting.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/Slider/setting.ts @@ -32,28 +32,28 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' }, { name: 'props.disabled', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '禁用' }, { name: 'props.reverse', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '反向' }, { name: 'props.vertical', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '垂直' @@ -63,7 +63,7 @@ const operationSetting = [ const rulesSetting = [ { name: 'rules', - type: 'RulesGroup', + type: 'ValidatorSetting', compact: true, props: { includes: ['required', 'max', 'min'], diff --git a/packages/editor/src/config/pc/switch/index.ts b/packages/editor/example/demo/FormEditor/editorConfig/Switch/index.ts similarity index 83% rename from packages/editor/src/config/pc/switch/index.ts rename to packages/editor/example/demo/FormEditor/editorConfig/Switch/index.ts index d9e709e..8363d97 100644 --- a/packages/editor/src/config/pc/switch/index.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/Switch/index.ts @@ -1,5 +1,5 @@ import Setting from './setting'; -import FieldSetting from '../../fieldSetting'; +import FieldSetting from '../fieldSetting'; export default { panel: { diff --git a/packages/editor/src/config/pc/switch/setting.ts b/packages/editor/example/demo/FormEditor/editorConfig/Switch/setting.ts similarity index 91% rename from packages/editor/src/config/pc/switch/setting.ts rename to packages/editor/example/demo/FormEditor/editorConfig/Switch/setting.ts index 4311408..3e36d66 100644 --- a/packages/editor/src/config/pc/switch/setting.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/Switch/setting.ts @@ -29,14 +29,14 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' }, { name: 'props.disabled', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '禁用' @@ -46,7 +46,7 @@ const operationSetting = [ const rulesSetting = [ { name: 'rules', - type: 'RulesGroup', + type: 'ValidatorSetting', compact: true, props: { includes: ['required'], diff --git a/packages/editor/src/config/pc/timePicker/index.ts b/packages/editor/example/demo/FormEditor/editorConfig/TimePicker/index.ts similarity index 91% rename from packages/editor/src/config/pc/timePicker/index.ts rename to packages/editor/example/demo/FormEditor/editorConfig/TimePicker/index.ts index e039da1..5c17dba 100644 --- a/packages/editor/src/config/pc/timePicker/index.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/TimePicker/index.ts @@ -1,5 +1,5 @@ import Setting from './setting'; -import FieldSetting from '../../fieldSetting'; +import FieldSetting from '../fieldSetting'; export default { panel: { diff --git a/packages/editor/src/config/pc/timePicker/setting.ts b/packages/editor/example/demo/FormEditor/editorConfig/TimePicker/setting.ts similarity index 93% rename from packages/editor/src/config/pc/timePicker/setting.ts rename to packages/editor/example/demo/FormEditor/editorConfig/TimePicker/setting.ts index 2cf8f22..22453d9 100644 --- a/packages/editor/src/config/pc/timePicker/setting.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/TimePicker/setting.ts @@ -57,21 +57,21 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' }, { name: 'props.disabled', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '禁用' }, { name: 'props.allowClear', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, initialValue: true, @@ -79,7 +79,7 @@ const operationSetting = [ }, { name: 'props.use12Hours', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '12小时制' @@ -89,7 +89,7 @@ const operationSetting = [ const rulesSetting = [ { name: 'rules', - type: 'RulesGroup', + type: 'ValidatorSetting', compact: true, props: { includes: ['required'], diff --git a/packages/editor/src/config/pc/timePickerRangePicker/index.ts b/packages/editor/example/demo/FormEditor/editorConfig/TimePickerRangePicker/index.ts similarity index 93% rename from packages/editor/src/config/pc/timePickerRangePicker/index.ts rename to packages/editor/example/demo/FormEditor/editorConfig/TimePickerRangePicker/index.ts index c3a03c1..7ca13bf 100644 --- a/packages/editor/src/config/pc/timePickerRangePicker/index.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/TimePickerRangePicker/index.ts @@ -1,5 +1,5 @@ import Setting from './setting'; -import FieldSetting from '../../fieldSetting'; +import FieldSetting from '../fieldSetting'; export default { panel: { diff --git a/packages/editor/src/config/pc/timePickerRangePicker/setting.ts b/packages/editor/example/demo/FormEditor/editorConfig/TimePickerRangePicker/setting.ts similarity index 93% rename from packages/editor/src/config/pc/timePickerRangePicker/setting.ts rename to packages/editor/example/demo/FormEditor/editorConfig/TimePickerRangePicker/setting.ts index 17f87cb..0ad6c91 100644 --- a/packages/editor/src/config/pc/timePickerRangePicker/setting.ts +++ b/packages/editor/example/demo/FormEditor/editorConfig/TimePickerRangePicker/setting.ts @@ -57,21 +57,21 @@ const baseSetting = [ const operationSetting = [ { name: 'hidden', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '隐藏' }, { name: 'props.disabled', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '禁用' }, { name: 'props.allowClear', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, initialValue: true, @@ -79,7 +79,7 @@ const operationSetting = [ }, { name: 'props.use12Hours', - type: 'OperateCheckbox', + type: 'CheckboxWithRules', inline: true, compact: true, children: '12小时制' @@ -89,7 +89,7 @@ const operationSetting = [ const rulesSetting = [ { name: 'rules', - type: 'RulesGroup', + type: 'ValidatorSetting', compact: true, props: { includes: ['required'], diff --git a/packages/editor/src/config/fieldSetting.ts b/packages/editor/example/demo/FormEditor/editorConfig/fieldSetting.ts similarity index 100% rename from packages/editor/src/config/fieldSetting.ts rename to packages/editor/example/demo/FormEditor/editorConfig/fieldSetting.ts diff --git a/packages/editor/example/demo/FormEditor/editorConfig/index.ts b/packages/editor/example/demo/FormEditor/editorConfig/index.ts new file mode 100644 index 0000000..b7291d8 --- /dev/null +++ b/packages/editor/example/demo/FormEditor/editorConfig/index.ts @@ -0,0 +1,35 @@ +import Input from './Input'; +import RadioGroup from './RadioGroup'; +import CheckboxGroup from './CheckboxGroup'; +import Select from './Select'; +import Switch from './Switch'; +import TimePicker from './TimePicker'; +import TimePickerRangePicker from './TimePickerRangePicker'; +import DatePicker from './DatePicker'; +import DatePickerRangePicker from './DatePickerRangePicker'; +import Slider from './Slider'; +import Rate from './Rate'; +import Cascader from './Cascader'; +import Alert from './Alert'; +import FieldSetting from './fieldSetting'; +import ComponentsConfig from '../components/config'; + +// 编辑器组件的配置项 +export default { + Alert, + Input, + "Radio.Group": RadioGroup, + "Checkbox.Group": CheckboxGroup, + Select, + Switch, + TimePicker, + "TimePicker.RangePicker": TimePickerRangePicker, + DatePicker, + "DatePicker.RangePicker": DatePickerRangePicker, + Slider, + Rate, + Cascader, + ...ComponentsConfig +}; + +export { FieldSetting }; diff --git a/packages/editor/example/demo/FormEditor/index.tsx b/packages/editor/example/demo/FormEditor/index.tsx index 2945999..b2c835c 100644 --- a/packages/editor/example/demo/FormEditor/index.tsx +++ b/packages/editor/example/demo/FormEditor/index.tsx @@ -1,13 +1,17 @@ import { Col, Row } from 'antd'; import React, { CSSProperties } from 'react'; -import FormRender, { EditorPanel, EditorProvider, EditorProviderProps, EditorSetting, EditorTools, EditorView } from '../FormRender'; -import EditorConfig from './config'; +import { EditorPanel, EditorProvider, EditorProviderProps, EditorSetting, EditorTools, EditorView } from '@simpleform/editor'; +import '@simpleform/editor/lib/css/main.css'; import panelData from './panelData'; import ImportModal from './ImportTemplate'; +import editorConfig from './editorConfig'; +import renderConfig from './FormRender/defineConfig'; import './index.less'; -const renderTools = (context) => { - return ; +export * from '@simpleform/editor'; + +const renderTools = (editorContext) => { + return ; }; export type EasyFormEditorProps = EditorProviderProps & { @@ -20,8 +24,8 @@ const FormEditor = () => { return ( diff --git a/packages/editor/example/demo/FormRender/defineConfig.ts b/packages/editor/example/demo/FormRender/defineConfig.ts deleted file mode 100644 index 4198f54..0000000 --- a/packages/editor/example/demo/FormRender/defineConfig.ts +++ /dev/null @@ -1,11 +0,0 @@ - -import Exmaple from './example'; - -// 渲染引擎配置项 -export default { - // 注册组件 - components: { - // 自定义控件组demo - "example": Exmaple, - } -}; diff --git a/packages/editor/package.json b/packages/editor/package.json index 47a6a22..111115e 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,6 +1,6 @@ { "name": "@simpleform/editor", - "version": "4.1.13", + "version": "4.2.0", "description": "editor for simple form", "author": "mezhanglei <496623925@qq.com>", "homepage": "https://github.com/mezhanglei/simpleform#readme", @@ -47,7 +47,7 @@ "dependencies": { "@codemirror/lang-javascript": "^6.2.1", "@codemirror/lang-json": "^6.0.1", - "@simpleform/render": "^4.1.26", + "@simpleform/render": "^4.1.27", "@szhsin/react-menu": "^4.1.0", "@types/file-saver": "^2.0.7", "@types/js-beautify": "^1.14.3", @@ -58,7 +58,6 @@ "codemirror": "^6.0.1", "copy-anything": "^3.0.5", "dayjs": "^1.11.10", - "devalue": "^4.3.2", "file-saver": "^2.0.5", "js-beautify": "^1.14.11", "react-collapsed": "^4.1.2", @@ -66,6 +65,7 @@ "react-fast-compare": "^3.2.2", "react-responsive-modal": "^6.4.2", "react-sortablejs": "^6.1.4", + "serialize-javascript": "^6.0.2", "sortablejs": "^1.15.2" }, "peerDependencies": { diff --git a/packages/editor/src/components/common/AntdModal/index.tsx b/packages/editor/src/common/AntdModal/index.tsx similarity index 100% rename from packages/editor/src/components/common/AntdModal/index.tsx rename to packages/editor/src/common/AntdModal/index.tsx diff --git a/packages/editor/src/components/common/AntdModal/style.less b/packages/editor/src/common/AntdModal/style.less similarity index 100% rename from packages/editor/src/components/common/AntdModal/style.less rename to packages/editor/src/common/AntdModal/style.less diff --git a/packages/editor/src/components/bind-request.tsx b/packages/editor/src/common/BindRequest.tsx similarity index 95% rename from packages/editor/src/components/bind-request.tsx rename to packages/editor/src/common/BindRequest.tsx index c68e869..0dec386 100644 --- a/packages/editor/src/components/bind-request.tsx +++ b/packages/editor/src/common/BindRequest.tsx @@ -1,11 +1,11 @@ import { AxiosInstance } from 'axios'; import React, { useEffect, useState } from 'react'; import { CommonFormProps, ReactComponent } from '../formrender'; -import { objectToFormData } from '../utils/object'; +import { objectToFormData } from '../utils'; /** * 自动给目标组件某个数据来源字段绑定请求,默认该数据的字段为options - * @param component 目标控件 + * @param Component 目标控件 * @param codeStr 请求数据源的字段名 * @returns */ diff --git a/packages/editor/src/components/common/CodeMirror/index.less b/packages/editor/src/common/CodeMirror/index.less similarity index 100% rename from packages/editor/src/components/common/CodeMirror/index.less rename to packages/editor/src/common/CodeMirror/index.less diff --git a/packages/editor/src/components/common/CodeMirror/index.tsx b/packages/editor/src/common/CodeMirror/index.tsx similarity index 72% rename from packages/editor/src/components/common/CodeMirror/index.tsx rename to packages/editor/src/common/CodeMirror/index.tsx index 406a71e..c7e57ea 100644 --- a/packages/editor/src/components/common/CodeMirror/index.tsx +++ b/packages/editor/src/common/CodeMirror/index.tsx @@ -1,14 +1,14 @@ -import React, { CSSProperties, FocusEventHandler, useEffect, useImperativeHandle, useRef, useState } from "react"; +import React, { CSSProperties, FocusEventHandler, useImperativeHandle, useRef } from "react"; import classNames from 'classnames'; import './index.less'; import { Button } from "antd"; import { js_beautify } from 'js-beautify'; import CustomModal from "../AntdModal"; -import { convertToString, evalString } from '../../../utils/string'; import { javascript } from '@codemirror/lang-javascript'; import CodeMirror, { ViewUpdate } from '@uiw/react-codemirror'; import { json } from "@codemirror/lang-json"; -import { CommonFormProps } from "../../../formrender"; +import { CommonFormProps } from "../../formrender"; +import { convertToString, evalString } from "../../utils"; const prefixCls = 'custom-editor'; const classes = { @@ -23,8 +23,7 @@ export interface EditorCodeMirrorProps extends CommonFormProps { style?: CSSProperties; } export interface EditorCodeMirrorRef { - getStr?: () => string; - getCode?: () => unknown; + getValue?: () => unknown; } // 代码编辑器(不可以编辑函数) export const EditorCodeMirror = React.forwardRef((props, ref) => { @@ -34,32 +33,24 @@ export const EditorCodeMirror = React.forwardRef(null); useImperativeHandle(ref, () => ({ - // 获取字符串 - getStr: () => { + getValue: () => { const cm = editorRef.current?.view; const codeStr = cm?.state.doc.toString() || ''; - return codeStr; - }, - // 获取值 - getCode: () => { - const cm = editorRef.current?.view; - const codeStr = cm?.state.doc.toString() || ''; - const code = evalString(codeStr); - return code; - }, + const val = evalString(codeStr); + return val; + } })); const onBlur: FocusEventHandler = () => { const cm = editorRef.current?.view; const codeStr = cm?.state.doc.toString() || ''; - const code = evalString(codeStr); - onChange && onChange(code); + const val = evalString(codeStr); + onChange?.(val); }; const codeStr = convertToString(value); @@ -89,24 +80,18 @@ export const EditorCodeMirrorModal = (props: EditorCodeMirrorProps) => { disabled, } = props; - const [codeStr, setCodeStr] = useState(''); const codemirrorRef = useRef(null); - useEffect(() => { - const code = convertToString(value); - setCodeStr(code ?? ''); - }, [value]); - const handleOk = (closeModal: () => void) => { const codemirror = codemirrorRef.current; if (!codemirror) return; - const code = codemirror.getCode?.() || ''; - const codeStr = codemirror.getStr?.() || ''; + const val = codemirror.getValue?.() || ''; closeModal(); - setCodeStr(codeStr); - onChange && onChange(code); + onChange && onChange(val); }; + const codeStr = convertToString(value); + return ( ( diff --git a/packages/editor/src/components/common/CodeTextarea/index.tsx b/packages/editor/src/common/CodeTextArea/index.tsx similarity index 86% rename from packages/editor/src/components/common/CodeTextarea/index.tsx rename to packages/editor/src/common/CodeTextArea/index.tsx index 7c6550e..e169b9d 100644 --- a/packages/editor/src/components/common/CodeTextarea/index.tsx +++ b/packages/editor/src/common/CodeTextArea/index.tsx @@ -2,11 +2,11 @@ import { Input } from 'antd'; import { TextAreaProps } from 'antd/lib/input'; import { TextAreaRef } from 'antd/lib/input/TextArea'; import React, { useEffect, useState } from 'react'; -import { CommonFormProps } from '../../../formrender'; -import { convertToString, evalString } from '../../../utils/string'; +import { convertToString, evalString } from '../../utils'; +import { CommonFormProps } from '../../formrender'; // 函数代码编辑器 -export interface CodeTextAreaProps extends Omit,CommonFormProps { +export interface CodeTextAreaProps extends Omit, CommonFormProps { } const CodeTextArea = React.forwardRef((props, ref) => { diff --git a/packages/editor/src/components/common/Collapse/index.less b/packages/editor/src/common/Collapse/index.less similarity index 100% rename from packages/editor/src/components/common/Collapse/index.less rename to packages/editor/src/common/Collapse/index.less diff --git a/packages/editor/src/components/common/Collapse/index.tsx b/packages/editor/src/common/Collapse/index.tsx similarity index 100% rename from packages/editor/src/components/common/Collapse/index.tsx rename to packages/editor/src/common/Collapse/index.tsx diff --git a/packages/editor/src/common/DataSetting/Linkage.tsx b/packages/editor/src/common/DataSetting/Linkage.tsx new file mode 100644 index 0000000..a63b4a4 --- /dev/null +++ b/packages/editor/src/common/DataSetting/Linkage.tsx @@ -0,0 +1,11 @@ +import React from "react"; +import CodeTextArea from "../CodeTextArea"; +import ButtonWithRules, { ButtonWithRulesProps } from "../RulesSetting/button"; + +// 联动设置 +const LinkageSetting: React.FC = (props) => { + + return }} />; +}; + +export default LinkageSetting; diff --git a/packages/editor/src/components/common/SetOptions/index.less b/packages/editor/src/common/DataSetting/index.less similarity index 100% rename from packages/editor/src/components/common/SetOptions/index.less rename to packages/editor/src/common/DataSetting/index.less diff --git a/packages/editor/src/common/DataSetting/index.tsx b/packages/editor/src/common/DataSetting/index.tsx new file mode 100644 index 0000000..64bd5dc --- /dev/null +++ b/packages/editor/src/common/DataSetting/index.tsx @@ -0,0 +1,81 @@ +import { Select } from "antd"; +import React, { useMemo } from "react"; +import './index.less'; +import LinkageSetting from "./Linkage"; +import RequestSetting from './request'; +import OptionList from './list'; +import { + joinFormPath, + CommonFormProps +} from "../../formrender"; +import { EditorCodeMirror } from '../CodeMirror'; +import { + getWidgetItem, + getArrMap, + setWidgetItem, +} from '../../utils/index'; + +/** + * 数据源的配置组件。 + */ + +export interface DataSettingProps extends CommonFormProps { + includes?: string[]; // 当前可用模块 +} + +const prefixCls = 'option-source'; +const classes = { + type: `${prefixCls}-type`, + component: `${prefixCls}-component` +}; + +const Options = [ + { label: '选项数据', value: 'list', component: OptionList }, + { label: '静态数据', value: 'json', component: EditorCodeMirror }, + { label: '接口请求', value: 'request', component: RequestSetting }, + { label: '联动设置', value: 'dynamic', component: LinkageSetting }, +]; +const OptionsMap = getArrMap(Options, 'value'); +const OptionsKeys = Options.map((item) => item.value); + +const DataSetting: React.FC = (props) => { + + const { + includes = OptionsKeys, + value, + onChange, + _options + } = props; + + const editorContext = _options?.editorContext; + const { selected, editor } = editorContext?.state || {}; + const buttons = useMemo(() => (Options?.filter((item) => includes?.includes(item?.value))), [includes]); + const currentValue = getWidgetItem(editor, joinFormPath(selected?.path, 'props.optionSelect')) || buttons[0]?.value; + + const selectTypeChange = (key?: string) => { + if (key) { + onChange?.(); + setWidgetItem(editor, key, joinFormPath(selected?.path, 'props.optionSelect')); + } + }; + + const handleChange = (value) => { + if (!currentValue) return; + onChange?.(value); + }; + + const Com = currentValue && OptionsMap[currentValue]?.component; + + return ( + <> +
+ , }, { name: `[${index}]value`, compact: true, outside: { type: 'col', props: { span: 10 } }, rules: [{ required: true }], - type: 'Input', - props: { - placeholder: 'value', - style: { width: '100%' } - } + typeRender: () => , }, { outside: { type: 'col', props: { span: 4 } }, diff --git a/packages/editor/src/components/common/SetOptions/request.tsx b/packages/editor/src/common/DataSetting/request.tsx similarity index 78% rename from packages/editor/src/components/common/SetOptions/request.tsx rename to packages/editor/src/common/DataSetting/request.tsx index 5779785..669c40c 100644 --- a/packages/editor/src/components/common/SetOptions/request.tsx +++ b/packages/editor/src/common/DataSetting/request.tsx @@ -1,8 +1,9 @@ +import { Input, Select } from "antd"; import classNames from "classnames"; import React, { CSSProperties, useEffect } from "react"; -import CodeTextArea from "../CodeTextarea"; import { EditorCodeMirrorModal } from "../CodeMirror"; -import DefaultFormRender, { useSimpleForm, CommonFormProps } from "../../../formrender"; +import CodeTextArea from "../CodeTextArea"; +import FormRender, { useSimpleForm, CommonFormProps } from "../../formrender"; export interface RequestResponseConfig { url?: string; // 请求的路径 @@ -42,8 +43,6 @@ const RequestSetting = React.forwardRef((props ...rest } = props; - const context = _options?.context; - const FormRender = context?.state?.FormRender || DefaultFormRender; const form = useSimpleForm(); useEffect(() => { @@ -59,21 +58,14 @@ const RequestSetting = React.forwardRef((props layout: 'horizontal', labelWidth: 80, required: true, - type: 'Input', - props: { - style: { width: '100%' }, - } + typeRender: () => , }, { label: '请求方式', name: 'method', layout: 'horizontal', labelWidth: 80, - type: 'Select', - props: { - style: { width: '100%' }, - options: methodOptions - } + typeRender: () => , }, { label: '请求参数', name: 'params', layout: 'horizontal', labelWidth: 80, - typeRender: , + typeRender: () => , }, { label: 'header信息', name: 'headers', layout: 'horizontal', labelWidth: 80, - typeRender: , + typeRender: () => , }, { label: '解析函数', @@ -107,7 +95,7 @@ const RequestSetting = React.forwardRef((props layout: 'horizontal', labelWidth: 80, initialValue: 'function (res){\n return res.data;\n}', - typeRender: , + typeRender: () => , }, ]; diff --git a/packages/editor/src/components/common/GlobalModal/baseModal.less b/packages/editor/src/common/GlobalModal/baseModal.less similarity index 100% rename from packages/editor/src/components/common/GlobalModal/baseModal.less rename to packages/editor/src/common/GlobalModal/baseModal.less diff --git a/packages/editor/src/components/common/GlobalModal/baseModal.tsx b/packages/editor/src/common/GlobalModal/baseModal.tsx similarity index 100% rename from packages/editor/src/components/common/GlobalModal/baseModal.tsx rename to packages/editor/src/common/GlobalModal/baseModal.tsx diff --git a/packages/editor/src/components/common/GlobalModal/createPromise.tsx b/packages/editor/src/common/GlobalModal/createPromise.tsx similarity index 92% rename from packages/editor/src/components/common/GlobalModal/createPromise.tsx rename to packages/editor/src/common/GlobalModal/createPromise.tsx index 0737acb..121389e 100644 --- a/packages/editor/src/components/common/GlobalModal/createPromise.tsx +++ b/packages/editor/src/common/GlobalModal/createPromise.tsx @@ -26,7 +26,7 @@ const renderModal = (Template, props) => { return dom; }; -export const create = async (Template, data = {}, options: { unmountDelay?: number } = {}) => { +const create = async (Template, data = {}, options: { unmountDelay?: number } = {}) => { let instance: Node | null = null; // unmount @@ -61,3 +61,5 @@ export const create = async (Template, data = {}, options: { unmountDelay?: numb return p.then(callbackResolve, callbackReject).catch((err) => err); }; + +export default create; diff --git a/packages/editor/src/components/common/GlobalModal/modalWrapper.tsx b/packages/editor/src/common/GlobalModal/modalWrapper.tsx similarity index 100% rename from packages/editor/src/components/common/GlobalModal/modalWrapper.tsx rename to packages/editor/src/common/GlobalModal/modalWrapper.tsx diff --git a/packages/editor/src/common/RulesSetting/RulesEditor.less b/packages/editor/src/common/RulesSetting/RulesEditor.less new file mode 100644 index 0000000..e69de29 diff --git a/packages/editor/src/common/RulesSetting/RulesEditor.tsx b/packages/editor/src/common/RulesSetting/RulesEditor.tsx new file mode 100644 index 0000000..4b549d0 --- /dev/null +++ b/packages/editor/src/common/RulesSetting/RulesEditor.tsx @@ -0,0 +1,45 @@ +import React, { useEffect, useImperativeHandle, useState } from "react"; +import './RulesEditor.less'; +import { + CommonFormProps, + CustomGenerateWidgetItem +} from "../../formrender"; + +export interface RulesEditorProps extends CommonFormProps { + widgetConfig?: CustomGenerateWidgetItem; +} + +export interface RulesEditorRef { + getValue?: () => unknown; +} + +/** + * 规则编辑器 + */ +const RulesEditor = React.forwardRef>((props, ref) => { + + const { + value, + onChange, + widgetConfig, + _options + } = props; + + useImperativeHandle(ref, () => ({ + getValue: () => { + // return val; + } + })); + + const handleOk = (closeModal: () => void) => { + closeModal(); + // onChange && onChange(codeStr); + }; + + + return ( +
2024-10-01号之前更新完成
+ ); +}); + +export default RulesEditor; diff --git a/packages/editor/src/common/RulesSetting/button.tsx b/packages/editor/src/common/RulesSetting/button.tsx new file mode 100644 index 0000000..88eedcd --- /dev/null +++ b/packages/editor/src/common/RulesSetting/button.tsx @@ -0,0 +1,53 @@ +import { Button } from "antd"; +import React, { useEffect, useRef, useState } from "react"; +import CustomModal, { CustomModalProps } from "../AntdModal"; +import RulesEditor, { RulesEditorProps, RulesEditorRef } from "./RulesEditor"; + +export type ButtonWithRulesProps = CustomModalProps & RulesEditorProps; +// 按钮触发添加规则编辑器 +const ButtonWithRules = (props: ButtonWithRulesProps) => { + + const { + value, + onChange, + widgetConfig, + ...rest + } = props; + + const [codeStr, setCodeStr] = useState(); + const rulesEditorRef = useRef(null); + + useEffect(() => { + setCodeStr(value); + }, [value]); + + const handOk = (closeModal: () => void) => { + const val = rulesEditorRef.current?.getValue?.() as string; + if (codeStr) { + closeModal(); + } + setCodeStr(val); + onChange && onChange(val); + }; + + return ( + ( +
+ {typeof value === 'string' ? value : null} + +
+ ) + }> + +
+ ); +}; + +export default ButtonWithRules; diff --git a/packages/editor/src/components/common/LinkageSetting/checkbox.less b/packages/editor/src/common/RulesSetting/checkbox.less similarity index 100% rename from packages/editor/src/components/common/LinkageSetting/checkbox.less rename to packages/editor/src/common/RulesSetting/checkbox.less diff --git a/packages/editor/src/components/common/LinkageSetting/checkbox.tsx b/packages/editor/src/common/RulesSetting/checkbox.tsx similarity index 59% rename from packages/editor/src/components/common/LinkageSetting/checkbox.tsx rename to packages/editor/src/common/RulesSetting/checkbox.tsx index d3aa1f1..d370d93 100644 --- a/packages/editor/src/components/common/LinkageSetting/checkbox.tsx +++ b/packages/editor/src/common/RulesSetting/checkbox.tsx @@ -1,24 +1,26 @@ -import { Checkbox, CheckboxProps } from "antd"; -import React, { useEffect, useState } from "react"; -import SvgIcon from "../SvgIcon"; +import { Checkbox, CheckboxProps, Switch } from "antd"; +import React, { useEffect, useRef, useState } from "react"; import { CheckboxChangeEvent } from "antd/es/checkbox"; -import LinkageSettingModal, { SettingModalProps } from "./modal"; import './checkbox.less'; -import { CommonFormProps } from "../../../formrender"; +import SvgIcon from "../SvgIcon"; +import CustomModal, { CustomModalProps } from "../AntdModal"; +import RulesEditor, { RulesEditorProps, RulesEditorRef } from "./RulesEditor"; -// checkbox点击联动弹窗 -const OperateCheckbox = (props: SettingModalProps & CheckboxProps & CommonFormProps) => { +export type CheckboxWithRulesProps = CustomModalProps & CheckboxProps & RulesEditorProps +// checkbox框触发规则编辑器 +const CheckboxWithRules = (props: CheckboxWithRulesProps) => { const { value, onChange, children, - widgetConfig = { type: 'Switch', valueProp: 'checked' }, + widgetConfig = { typeRender: () => , valueProp: 'checked' }, ...rest } = props; const [checked, setChecked] = useState(false); const [codeStr, setCodeStr] = useState(); + const rulesEditorRef = useRef(null); useEffect(() => { if (typeof value === 'string') { @@ -46,13 +48,17 @@ const OperateCheckbox = (props: SettingModalProps & CheckboxProps & CommonFormPr } }; - // 联动值的变化 - const handOk = (data?: string) => { - setCodeStr(data); + // 确认 + const handOk = (closeModal: () => void) => { + const val = rulesEditorRef.current?.getValue?.() as string; + setCodeStr(val); + if (codeStr) { + closeModal(); + } // 选中状态则直接更改值 if (checked) { setChecked(true); - onChange && onChange(data); + onChange && onChange(val); } }; @@ -65,12 +71,12 @@ const OperateCheckbox = (props: SettingModalProps & CheckboxProps & CommonFormPr }; return ( - (
@@ -81,8 +87,10 @@ const OperateCheckbox = (props: SettingModalProps & CheckboxProps & CommonFormPr {codeStr && }
) - } /> + }> + + ); }; -export default OperateCheckbox; +export default CheckboxWithRules; diff --git a/packages/editor/src/components/common/SvgIcon/index.less b/packages/editor/src/common/SvgIcon/index.less similarity index 100% rename from packages/editor/src/components/common/SvgIcon/index.less rename to packages/editor/src/common/SvgIcon/index.less diff --git a/packages/editor/src/components/common/SvgIcon/index.tsx b/packages/editor/src/common/SvgIcon/index.tsx similarity index 77% rename from packages/editor/src/components/common/SvgIcon/index.tsx rename to packages/editor/src/common/SvgIcon/index.tsx index 22fab84..75c63c6 100644 --- a/packages/editor/src/components/common/SvgIcon/index.tsx +++ b/packages/editor/src/common/SvgIcon/index.tsx @@ -10,12 +10,8 @@ context.keys().forEach((filename: string) => { // @ts-ignore svgs[componentName] = Com; }); -interface SvgIconProps extends React.HtmlHTMLAttributes { - name: string; - className?: string; -} -const SvgIcon = React.forwardRef((props, ref) => { +const SvgIcon = React.forwardRef>((props, ref) => { const { name, className, ...rest } = props; const svgClass = className ? 'svg-icon ' + className : 'svg-icon'; // @ts-ignore diff --git a/packages/editor/src/components/common/SvgIcon/svg/add.svg b/packages/editor/src/common/SvgIcon/svg/add.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/add.svg rename to packages/editor/src/common/SvgIcon/svg/add.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/alert.svg b/packages/editor/src/common/SvgIcon/svg/alert.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/alert.svg rename to packages/editor/src/common/SvgIcon/svg/alert.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/button.svg b/packages/editor/src/common/SvgIcon/svg/button.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/button.svg rename to packages/editor/src/common/SvgIcon/svg/button.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/card.svg b/packages/editor/src/common/SvgIcon/svg/card.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/card.svg rename to packages/editor/src/common/SvgIcon/svg/card.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/cascader-field.svg b/packages/editor/src/common/SvgIcon/svg/cascader-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/cascader-field.svg rename to packages/editor/src/common/SvgIcon/svg/cascader-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/checkbox-field.svg b/packages/editor/src/common/SvgIcon/svg/checkbox-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/checkbox-field.svg rename to packages/editor/src/common/SvgIcon/svg/checkbox-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/close.svg b/packages/editor/src/common/SvgIcon/svg/close.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/close.svg rename to packages/editor/src/common/SvgIcon/svg/close.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/color-field.svg b/packages/editor/src/common/SvgIcon/svg/color-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/color-field.svg rename to packages/editor/src/common/SvgIcon/svg/color-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/custom-component.svg b/packages/editor/src/common/SvgIcon/svg/custom-component.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/custom-component.svg rename to packages/editor/src/common/SvgIcon/svg/custom-component.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/data-table.svg b/packages/editor/src/common/SvgIcon/svg/data-table.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/data-table.svg rename to packages/editor/src/common/SvgIcon/svg/data-table.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/date-field.svg b/packages/editor/src/common/SvgIcon/svg/date-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/date-field.svg rename to packages/editor/src/common/SvgIcon/svg/date-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/date-range-field.svg b/packages/editor/src/common/SvgIcon/svg/date-range-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/date-range-field.svg rename to packages/editor/src/common/SvgIcon/svg/date-range-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/delete.svg b/packages/editor/src/common/SvgIcon/svg/delete.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/delete.svg rename to packages/editor/src/common/SvgIcon/svg/delete.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/divider.svg b/packages/editor/src/common/SvgIcon/svg/divider.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/divider.svg rename to packages/editor/src/common/SvgIcon/svg/divider.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/document.svg b/packages/editor/src/common/SvgIcon/svg/document.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/document.svg rename to packages/editor/src/common/SvgIcon/svg/document.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/drag.svg b/packages/editor/src/common/SvgIcon/svg/drag.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/drag.svg rename to packages/editor/src/common/SvgIcon/svg/drag.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/edit.svg b/packages/editor/src/common/SvgIcon/svg/edit.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/edit.svg rename to packages/editor/src/common/SvgIcon/svg/edit.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/file-upload-field.svg b/packages/editor/src/common/SvgIcon/svg/file-upload-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/file-upload-field.svg rename to packages/editor/src/common/SvgIcon/svg/file-upload-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/fuzhi.svg b/packages/editor/src/common/SvgIcon/svg/fuzhi.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/fuzhi.svg rename to packages/editor/src/common/SvgIcon/svg/fuzhi.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/github.svg b/packages/editor/src/common/SvgIcon/svg/github.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/github.svg rename to packages/editor/src/common/SvgIcon/svg/github.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/grid.svg b/packages/editor/src/common/SvgIcon/svg/grid.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/grid.svg rename to packages/editor/src/common/SvgIcon/svg/grid.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/html-text.svg b/packages/editor/src/common/SvgIcon/svg/html-text.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/html-text.svg rename to packages/editor/src/common/SvgIcon/svg/html-text.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/insert-col.svg b/packages/editor/src/common/SvgIcon/svg/insert-col.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/insert-col.svg rename to packages/editor/src/common/SvgIcon/svg/insert-col.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/insert-row.svg b/packages/editor/src/common/SvgIcon/svg/insert-row.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/insert-row.svg rename to packages/editor/src/common/SvgIcon/svg/insert-row.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/menu.svg b/packages/editor/src/common/SvgIcon/svg/menu.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/menu.svg rename to packages/editor/src/common/SvgIcon/svg/menu.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/node-tree.svg b/packages/editor/src/common/SvgIcon/svg/node-tree.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/node-tree.svg rename to packages/editor/src/common/SvgIcon/svg/node-tree.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/number-field.svg b/packages/editor/src/common/SvgIcon/svg/number-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/number-field.svg rename to packages/editor/src/common/SvgIcon/svg/number-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/picture-upload-field.svg b/packages/editor/src/common/SvgIcon/svg/picture-upload-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/picture-upload-field.svg rename to packages/editor/src/common/SvgIcon/svg/picture-upload-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/qingchu.svg b/packages/editor/src/common/SvgIcon/svg/qingchu.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/qingchu.svg rename to packages/editor/src/common/SvgIcon/svg/qingchu.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/radio-field.svg b/packages/editor/src/common/SvgIcon/svg/radio-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/radio-field.svg rename to packages/editor/src/common/SvgIcon/svg/radio-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/rate-field.svg b/packages/editor/src/common/SvgIcon/svg/rate-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/rate-field.svg rename to packages/editor/src/common/SvgIcon/svg/rate-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/redo.svg b/packages/editor/src/common/SvgIcon/svg/redo.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/redo.svg rename to packages/editor/src/common/SvgIcon/svg/redo.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/rich-editor-field.svg b/packages/editor/src/common/SvgIcon/svg/rich-editor-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/rich-editor-field.svg rename to packages/editor/src/common/SvgIcon/svg/rich-editor-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/section.svg b/packages/editor/src/common/SvgIcon/svg/section.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/section.svg rename to packages/editor/src/common/SvgIcon/svg/section.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/select-field.svg b/packages/editor/src/common/SvgIcon/svg/select-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/select-field.svg rename to packages/editor/src/common/SvgIcon/svg/select-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/shanchu.svg b/packages/editor/src/common/SvgIcon/svg/shanchu.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/shanchu.svg rename to packages/editor/src/common/SvgIcon/svg/shanchu.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/slider-field.svg b/packages/editor/src/common/SvgIcon/svg/slider-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/slider-field.svg rename to packages/editor/src/common/SvgIcon/svg/slider-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/static-text.svg b/packages/editor/src/common/SvgIcon/svg/static-text.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/static-text.svg rename to packages/editor/src/common/SvgIcon/svg/static-text.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/sub-form.svg b/packages/editor/src/common/SvgIcon/svg/sub-form.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/sub-form.svg rename to packages/editor/src/common/SvgIcon/svg/sub-form.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/switch-field.svg b/packages/editor/src/common/SvgIcon/svg/switch-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/switch-field.svg rename to packages/editor/src/common/SvgIcon/svg/switch-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/tab.svg b/packages/editor/src/common/SvgIcon/svg/tab.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/tab.svg rename to packages/editor/src/common/SvgIcon/svg/tab.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/table.svg b/packages/editor/src/common/SvgIcon/svg/table.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/table.svg rename to packages/editor/src/common/SvgIcon/svg/table.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/text-field.svg b/packages/editor/src/common/SvgIcon/svg/text-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/text-field.svg rename to packages/editor/src/common/SvgIcon/svg/text-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/time-field.svg b/packages/editor/src/common/SvgIcon/svg/time-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/time-field.svg rename to packages/editor/src/common/SvgIcon/svg/time-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/time-range-field.svg b/packages/editor/src/common/SvgIcon/svg/time-range-field.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/time-range-field.svg rename to packages/editor/src/common/SvgIcon/svg/time-range-field.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/undo.svg b/packages/editor/src/common/SvgIcon/svg/undo.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/undo.svg rename to packages/editor/src/common/SvgIcon/svg/undo.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/wenhao.svg b/packages/editor/src/common/SvgIcon/svg/wenhao.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/wenhao.svg rename to packages/editor/src/common/SvgIcon/svg/wenhao.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/youjiantou.svg b/packages/editor/src/common/SvgIcon/svg/youjiantou.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/youjiantou.svg rename to packages/editor/src/common/SvgIcon/svg/youjiantou.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/zhedie-down.svg b/packages/editor/src/common/SvgIcon/svg/zhedie-down.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/zhedie-down.svg rename to packages/editor/src/common/SvgIcon/svg/zhedie-down.svg diff --git a/packages/editor/src/components/common/SvgIcon/svg/zhedie-right.svg b/packages/editor/src/common/SvgIcon/svg/zhedie-right.svg similarity index 100% rename from packages/editor/src/components/common/SvgIcon/svg/zhedie-right.svg rename to packages/editor/src/common/SvgIcon/svg/zhedie-right.svg diff --git a/packages/editor/src/components/common/rules/core/index.less b/packages/editor/src/common/ValidatorSetting/core/index.less similarity index 100% rename from packages/editor/src/components/common/rules/core/index.less rename to packages/editor/src/common/ValidatorSetting/core/index.less diff --git a/packages/editor/src/components/common/rules/core/index.tsx b/packages/editor/src/common/ValidatorSetting/core/index.tsx similarity index 86% rename from packages/editor/src/components/common/rules/core/index.tsx rename to packages/editor/src/common/ValidatorSetting/core/index.tsx index cf7d86f..5a9b5cd 100644 --- a/packages/editor/src/components/common/rules/core/index.tsx +++ b/packages/editor/src/common/ValidatorSetting/core/index.tsx @@ -1,12 +1,17 @@ import React, { useEffect, useImperativeHandle, useRef, useState } from "react"; -import SvgIcon from "../../SvgIcon"; import './index.less'; -import CustomModal from "../../AntdModal"; import classNames from "classnames"; -import DefaultFormRender, { Form, useSimpleForm, matchExpression, CommonFormProps, CustomGenerateWidgetItem } from "../../../../formrender"; -import { Radio } from "antd"; -import ShowSettingModal from "../../LinkageSetting/ShowSettingModal"; -import { InputFormRuleKey } from ".."; +import FormRender, { + Form, + useSimpleForm, + matchExpression, + CommonFormProps, + CustomGenerateWidgetItem +} from "../../../formrender"; +import { Input, Radio } from "antd"; +import { InputFormRuleKey } from "../index"; +import ButtonWithRules from "../../RulesSetting/button"; +import { CustomModal, SvgIcon } from "../../"; export interface RuleCoreRefs { showRuleModal: () => void @@ -44,15 +49,12 @@ const RuleCore = React.forwardRef((props, ref) => { widgetConfig, onChange, className, - _options, } = props; const [ruleValue, setRuleValue] = useState(); const [selectType, setSelectType] = useState('handle'); const editRef = useRef(null); const currentForm = useSimpleForm(); - const context = _options?.context; - const FormRender = context?.state?.FormRender || DefaultFormRender; useImperativeHandle(ref, () => ({ showRuleModal: () => editRef.current?.click() })); @@ -63,7 +65,7 @@ const RuleCore = React.forwardRef((props, ref) => { layout: 'horizontal', rules: [{ required: true, message: '请输入' }], labelWidth: 80, - typeRender: + typeRender: () => } : { ...widgetConfig, name: name, @@ -77,9 +79,7 @@ const RuleCore = React.forwardRef((props, ref) => { layout: 'horizontal', rules: [{ required: true, message: '请输入' }], labelWidth: 80, - type: 'Input', - props: { - } + typeRender: () => , }, ] : undefined; diff --git a/packages/editor/src/components/common/rules/index.less b/packages/editor/src/common/ValidatorSetting/index.less similarity index 100% rename from packages/editor/src/components/common/rules/index.less rename to packages/editor/src/common/ValidatorSetting/index.less diff --git a/packages/editor/src/components/common/rules/index.tsx b/packages/editor/src/common/ValidatorSetting/index.tsx similarity index 94% rename from packages/editor/src/components/common/rules/index.tsx rename to packages/editor/src/common/ValidatorSetting/index.tsx index da0aeb7..e4f3240 100644 --- a/packages/editor/src/components/common/rules/index.tsx +++ b/packages/editor/src/common/ValidatorSetting/index.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from "react"; import { Checkbox } from "antd"; import { CheckboxChangeEvent } from "antd/lib/checkbox"; import './index.less'; -import { CommonFormProps } from "../../../formrender"; +import { CommonFormProps } from "../../formrender"; import RequiredComponent from "./required"; import MinOrMaxComponent from "./minOrMax"; import PatternComponent from "./pattern"; @@ -18,11 +18,10 @@ const RuleWidget = { }; export type InputFormRuleKey = keyof typeof RuleWidget & string; export type RulesKeyList = Array; -export interface RulesGroupProps extends CommonFormProps> { +export interface ValidatorSettingProps extends CommonFormProps> { includes?: Array; } - const prefixCls = 'rules-add'; const classes = { rules: prefixCls, @@ -35,7 +34,7 @@ const RuleKeys = Object.keys(RuleWidget) as RulesKeyList; /** * 校验规则的配置组件。 */ -const RulesGroup: React.FC = (props) => { +const ValidatorSetting: React.FC = (props) => { const { includes = RuleKeys, @@ -138,4 +137,4 @@ const RulesGroup: React.FC = (props) => { ); }; -export default RulesGroup; +export default ValidatorSetting; diff --git a/packages/editor/src/components/common/rules/minOrMax.tsx b/packages/editor/src/common/ValidatorSetting/minOrMax.tsx similarity index 75% rename from packages/editor/src/components/common/rules/minOrMax.tsx rename to packages/editor/src/common/ValidatorSetting/minOrMax.tsx index 6aca3aa..8e9dfe3 100644 --- a/packages/editor/src/components/common/rules/minOrMax.tsx +++ b/packages/editor/src/common/ValidatorSetting/minOrMax.tsx @@ -1,3 +1,4 @@ +import { InputNumber } from "antd"; import React from "react"; import RuleItem, { RuleCoreProps, RuleCoreRefs } from "./core"; @@ -5,10 +6,7 @@ const MinOrMaxComponent = React.forwardRef((props, return , }} />; }); diff --git a/packages/editor/src/components/common/rules/pattern.tsx b/packages/editor/src/common/ValidatorSetting/pattern.tsx similarity index 71% rename from packages/editor/src/components/common/rules/pattern.tsx rename to packages/editor/src/common/ValidatorSetting/pattern.tsx index 8233fc1..1ebc0ef 100644 --- a/packages/editor/src/components/common/rules/pattern.tsx +++ b/packages/editor/src/common/ValidatorSetting/pattern.tsx @@ -1,14 +1,12 @@ import React from "react"; +import CodeTextArea from "../CodeTextArea"; import RuleItem, { RuleCoreProps, RuleCoreRefs } from "./core"; const PatternComponent = React.forwardRef((props, ref) => { return , }} />; }); diff --git a/packages/editor/src/components/common/rules/required.tsx b/packages/editor/src/common/ValidatorSetting/required.tsx similarity index 74% rename from packages/editor/src/components/common/rules/required.tsx rename to packages/editor/src/common/ValidatorSetting/required.tsx index 4701f57..819e322 100644 --- a/packages/editor/src/components/common/rules/required.tsx +++ b/packages/editor/src/common/ValidatorSetting/required.tsx @@ -1,9 +1,10 @@ +import { Switch } from "antd"; import React from "react"; import RuleItem, { RuleCoreProps, RuleCoreRefs } from "./core"; const RequiredComponent = React.forwardRef((props, ref) => { - return ; + return }} />; }); export default RequiredComponent; diff --git a/packages/editor/src/common/index.ts b/packages/editor/src/common/index.ts new file mode 100644 index 0000000..d82042b --- /dev/null +++ b/packages/editor/src/common/index.ts @@ -0,0 +1,34 @@ +import Collapse from './Collapse'; +import ModalWrapper from './GlobalModal/modalWrapper'; +import createModalPromise from './GlobalModal/createPromise'; +import bindRequest from './BindRequest'; +import SvgIcon from './SvgIcon/index'; +import CustomModal from './AntdModal'; +import RulesEditor from './RulesSetting/RulesEditor'; +import ButtonWithRules from './RulesSetting/button'; +import CheckboxWithRules from './RulesSetting/checkbox'; +import CodeTextArea from './CodeTextArea'; +import DataSetting from './DataSetting'; +import ValidatorSetting from './ValidatorSetting'; + +export { + Collapse, + ModalWrapper, + createModalPromise, + bindRequest, + SvgIcon, + CustomModal, + RulesEditor, + ButtonWithRules, + CheckboxWithRules, + CodeTextArea, + DataSetting, + ValidatorSetting, +}; + +export * from './GlobalModal/modalWrapper'; +export * from './CodeMirror'; +export * from './AntdModal'; +export * from './RulesSetting/RulesEditor'; +export * from './DataSetting'; +export * from './ValidatorSetting'; diff --git a/packages/editor/src/components/common/CodeInput/index.tsx b/packages/editor/src/components/common/CodeInput/index.tsx deleted file mode 100644 index 6f9d38d..0000000 --- a/packages/editor/src/components/common/CodeInput/index.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { Input, InputProps, InputRef } from 'antd'; -import React, { CSSProperties, useEffect, useState } from 'react'; -import { CommonFormProps } from '../../../formrender'; -import { convertToString, evalString } from '../../../utils/string'; - -// 值的输入框 -export interface CodeInputProps extends Omit, CommonFormProps { - style?: CSSProperties; -} -const CodeInput = React.forwardRef((props, ref) => { - - const { - value, - onChange, - className, - ...rest - } = props; - - const [curValue, setCurValue] = useState(); - - useEffect(() => { - setCurValue(convertToString(value)); - }, [value]); - - const onBlur: InputProps['onBlur'] = (e) => { - const codeStr = e?.target?.value; - const code = evalString(codeStr); - onChange && onChange(code); - }; - - const handleChange: InputProps['onChange'] = (e) => { - const codeStr = e?.target?.value; - setCurValue(codeStr); - }; - - return ( - - ); -}); - -export default CodeInput; diff --git a/packages/editor/src/components/common/LinkageSetting/ShowSettingModal.tsx b/packages/editor/src/components/common/LinkageSetting/ShowSettingModal.tsx deleted file mode 100644 index 4f09bdb..0000000 --- a/packages/editor/src/components/common/LinkageSetting/ShowSettingModal.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { Button } from "antd"; -import React, { useEffect, useState } from "react"; -import LinkageSettingModal, { SettingModalProps } from "./modal"; - -// 展示联动弹窗 -const ShowSettingModal = (props: SettingModalProps) => { - - const { - value, - onChange, - widgetConfig, - ...rest - } = props; - - const [codeStr, setCodeStr] = useState(); - - useEffect(() => { - setCodeStr(value); - }, [value]); - - const handOk = (val?: string) => { - setCodeStr(val); - onChange && onChange(val); - }; - - return ( - ( -
- {typeof value === 'string' ? value : null} - -
- ) - } /> - ); -}; - -export default ShowSettingModal; diff --git a/packages/editor/src/components/common/LinkageSetting/modal.less b/packages/editor/src/components/common/LinkageSetting/modal.less deleted file mode 100644 index f247d2e..0000000 --- a/packages/editor/src/components/common/LinkageSetting/modal.less +++ /dev/null @@ -1,62 +0,0 @@ -.dynamic-rules { - &-modal { - width: 600px; - } - - &-item { - - width: 100%; - margin-top: 5px; - border-bottom: 1px solid #f5f5f5; - - &-assemble { - width: 100px; - margin: 10px 10px 10px 0; - } - - &-row { - background-color: #f5f5f5; - padding: 8px; - } - - &-prefix { - font-weight: bold; - } - - &-component { - font-weight: bold; - text-align: center; - } - - &-suffix { - font-weight: bold; - margin-right: 10px; - } - - &-youjiantou { - margin-right: 4px; - } - - &-icon { - margin-left: 10px; - cursor: pointer; - } - - &-checkbox { - margin-right: 10px; - - .ant-checkbox+span { - padding-right: 2px; - } - } - - &-add { - margin-right: 2px; - cursor: pointer; - } - - &-clear { - cursor: pointer; - } - } -} \ No newline at end of file diff --git a/packages/editor/src/components/common/LinkageSetting/modal.tsx b/packages/editor/src/components/common/LinkageSetting/modal.tsx deleted file mode 100644 index b7f9674..0000000 --- a/packages/editor/src/components/common/LinkageSetting/modal.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import React, { useEffect, useState } from "react"; -import './modal.less'; -import SvgIcon from "../SvgIcon"; -import DefaultFormRender, { useSimpleForm, CommonFormProps, CustomGenerateWidgetItem } from "../../../formrender"; -import { codeToRule, ruleToCodeStr } from "./utils"; -import CustomModal, { CustomModalProps } from "../AntdModal"; - -// 规则条件的渲染数据类型 -export type RuleSettingItem = unknown[] | '||' | '&&' | ''; -export interface SettingModalProps extends CustomModalProps, CommonFormProps { - title?: NonNullable['title'] - widgetConfig?: CustomGenerateWidgetItem; -} - -const prefixCls = 'dynamic-rules'; -const classes = { - cls: `${prefixCls}`, - item: `${prefixCls}-item`, - row: `${prefixCls}-item-row`, - assemble: `${prefixCls}-item-assemble`, - itemPrefix: `${prefixCls}-item-prefix`, - itemSuffix: `${prefixCls}-item-suffix`, - icon: `${prefixCls}-item-icon`, -}; - -const assembleOptions = [{ - label: '或', - value: '||' -}]; - -/** - * 联动规则设置弹窗 - */ -const LinkageSettingModal: React.FC = (props) => { - - const { - value, - onChange, - widgetConfig, - title, - displayElement, - _options - } = props; - - const context = _options?.context; - const FormRender = context?.state?.FormRender || DefaultFormRender; - const form = useSimpleForm(); - const [dataSource, setDataSource] = useState>([]); - - useEffect(() => { - const currentValue = typeof value === 'string' ? value : undefined; - const ruleData = codeToRule(currentValue); - const options = ruleData.length ? ruleData : [[]]; - setDataSource(options); - form.setFieldsValue(options); - }, [value]); - - const widgetList = dataSource.map((item, index) => { - if (item instanceof Array) { - return { - type: 'row', - props: { - gutter: 8, - className: classes.row, - align: "middle" - }, - children: [ - { - outside: { type: 'col', props: { span: 1 } }, - typeRender: - }, - { - outside: { type: 'col', props: { span: 8 } }, - compact: true, - name: `[${index}][0]`, - type: "Input.TextArea", - props: { - placeholder: "formvalues['表单字段'] == 值", - } - }, - { - outside: { type: 'col', props: { span: 5 } }, - typeRender: 时,设置为 - }, - { - outside: { type: 'col', props: { span: 5 } }, - name: `[${index}][1]`, - compact: true, - ...widgetConfig - }, - { - outside: { type: 'col', props: { span: 5 } }, - typeRender: index === 0 ? - addNewItem()} /> - : - deleteItem(index)} /> - } - ] - }; - } else { - return { - name: `[${index}]`, - compact: true, - hidden: index === 0, - type: 'Select', - props: { - className: classes.assemble, - options: assembleOptions - } - }; - } - }); - - const addNewItem = () => { - const newDataSource = dataSource.concat(['||', []]); - form.setFieldsValue(newDataSource); - setDataSource(newDataSource); - }; - - const deleteItem = (index: number) => { - const oldData = [...dataSource]; - if (!oldData) return; - const newData = [...oldData]; - newData.splice(index - 1, 2); - form.setFieldsValue(newData); - setDataSource(newData); - }; - - const onFieldsChange = (_, values) => { - setDataSource(values); - }; - - const handleOk = (closeModal: () => void) => { - closeModal(); - const codeStr = ruleToCodeStr(dataSource); - onChange && onChange(codeStr); - }; - - - return ( - - - - ); -}; - -export default LinkageSettingModal; diff --git a/packages/editor/src/components/common/LinkageSetting/utils.ts b/packages/editor/src/components/common/LinkageSetting/utils.ts deleted file mode 100644 index ba0342a..0000000 --- a/packages/editor/src/components/common/LinkageSetting/utils.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { convertToString, evalString } from "../../../utils/string"; -import { RuleSettingItem } from "./modal"; - -export const codeToRule = (codeStr?: string) => { - if (typeof codeStr !== 'string' || !codeStr) return []; - const codeArr = codeStr?.replace(/\{\{|\}\}/g, '').split(/\s*\(|\)\s*/).filter(Boolean); - return codeArr.map((item) => { - const code = item.trim(); - if (['||', '&&'].includes(code)) return code; - return code.split(/\s*\?|\:\s*/).map((codeItem, index) => index === 0 ? codeItem : evalString(codeItem)); - }) as Array; -}; - -const codeJoinLetter = (codeArr?: RuleSettingItem) => { - if (!(codeArr instanceof Array)) return codeArr; - const list = [codeArr[0], codeArr[1], codeArr[2]]; - const convertList = list.map((item) => { - return typeof item === 'string' ? item : convertToString(item) as string; - }); - return convertList.reduce((prev, curr, index) => { - if (index === 0) { - return prev.replace('{0}', curr); - } else if (index === 1) { - return prev.replace('{1}', curr); - } else { - return prev.replace('{2}', curr); - } - }, '({0}?{1}:{2})'); -}; - -export const ruleToCodeStr = (rules?: Array) => { - if (!(rules instanceof Array)) return; - const codeStr = rules.reduce((preStr, current) => { - const curStr = current instanceof Array ? codeJoinLetter(current) : current; - return preStr + (curStr || ''); - }, ""); - return codeStr ? `{{${codeStr}}}` : ""; -}; diff --git a/packages/editor/src/components/common/NameInput/index.tsx b/packages/editor/src/components/common/NameInput/index.tsx deleted file mode 100644 index 2c6232e..0000000 --- a/packages/editor/src/components/common/NameInput/index.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Input, InputProps, InputRef } from 'antd'; -import React, { useEffect, useState } from 'react'; - -export interface NameInputProps extends InputProps { -} -const NameInput = React.forwardRef((props, ref) => { - - const { - value, - onChange, - className, - ...rest - } = props; - - const [curValue, setCurValue] = useState(); - - useEffect(() => { - setCurValue(value); - }, [value]); - - const onBlur: InputProps['onBlur'] = (e) => { - onChange && onChange(e); - }; - - const handleChange: InputProps['onChange'] = (e) => { - const codeStr = e?.target?.value; - setCurValue(codeStr); - }; - - return ( - - ); -}); - -export default NameInput; diff --git a/packages/editor/src/components/common/SetOptions/Linkage.tsx b/packages/editor/src/components/common/SetOptions/Linkage.tsx deleted file mode 100644 index ebff7ab..0000000 --- a/packages/editor/src/components/common/SetOptions/Linkage.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from "react"; -import { SettingModalProps } from "../LinkageSetting/modal"; -import ShowSettingModal from "../LinkageSetting/ShowSettingModal"; - -// 联动设置 -const LinkageSetting: React.FC = (props) => { - - return ; -}; - -export default LinkageSetting; diff --git a/packages/editor/src/components/common/SetOptions/index.tsx b/packages/editor/src/components/common/SetOptions/index.tsx deleted file mode 100644 index 56c4aad..0000000 --- a/packages/editor/src/components/common/SetOptions/index.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { Select } from "antd"; -import React, { useMemo } from "react"; -import './index.less'; -import LinkageSetting from "./Linkage"; -import RequestSetting from './request'; -import OptionList from './list'; -import { EditorCodeMirror } from "../CodeMirror"; -import { getWidgetItem, setWidgetItem } from "../../../utils/utils"; -import { joinFormPath, CommonFormProps } from "../../../formrender"; - -/** - * 数据源的配置组件。 - */ - -export interface SetOptionsProps extends CommonFormProps { - includes?: string[]; // 当前可用模块 -} - -const prefixCls = 'option-source'; -const classes = { - type: `${prefixCls}-type`, - component: `${prefixCls}-component` -}; - -const OptionsWidget = { - list: { label: '选项数据', component: OptionList }, - json: { label: '静态数据', component: EditorCodeMirror }, - request: { label: '接口请求', component: RequestSetting }, - dynamic: { label: '联动设置', component: LinkageSetting }, -}; -type OptionsKey = keyof typeof OptionsWidget; -type OptionsKeyList = Array; -const OptionsKeys = Object.keys(OptionsWidget) as OptionsKeyList; - -const SetOptions: React.FC = (props) => { - - const { - includes = OptionsKeys, - value, - onChange, - _options - } = props; - - const context = _options?.context; - const { selected, editor } = context?.state || {}; - const buttons = useMemo(() => (OptionsKeys?.filter((key) => includes?.includes(key))), [includes]); - const optionSelect = getWidgetItem(editor, joinFormPath(selected?.path, 'props.optionSelect')) || buttons[0]; - - const selectTypeChange = (key?: OptionsKey) => { - if (key) { - onChange && onChange(undefined); - setWidgetItem(editor, key, joinFormPath(selected?.path, 'props.optionSelect')); - } - }; - - const handleChange = (value) => { - if (!optionSelect) return; - onChange && onChange(value); - }; - - const Child = optionSelect && OptionsWidget[optionSelect]?.component; - - return ( - <> -
- -
-
- {Child ? : null} -
- - ); -}; - -export default SetOptions; diff --git a/packages/editor/src/components/common/index.ts b/packages/editor/src/components/common/index.ts deleted file mode 100644 index 030f4dc..0000000 --- a/packages/editor/src/components/common/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import CodeInput from './CodeInput'; -import CodeTextArea from './CodeTextarea'; -import SetOptions from './SetOptions'; -import RulesGroup from './rules'; -import { EditorCodeMirror } from './CodeMirror'; -import RichEditor, { RichEditorModalBtn } from './RichEditor'; -import { RichText } from './RichText'; -import FormTable from './FormTable'; -import Collapse from './Collapse'; -import Grid from './grid'; -import LayoutTable from './LayoutTable'; -import OperateCheckbox from './LinkageSetting/checkbox'; - -// 编辑器公共组件 -export default { - SetOptions, - RulesGroup, - CodeInput, - CodeTextArea, - EditorCodeMirror, - OperateCheckbox, - RichEditor, - RichText, - RichEditorModalBtn, - FormTable, - LayoutTable, - Collapse, - Grid, -}; diff --git a/packages/editor/src/components/index.ts b/packages/editor/src/components/index.ts deleted file mode 100644 index 0b7211f..0000000 --- a/packages/editor/src/components/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import pcWidgets from './pc'; -import commonWidgets from './common'; -import { CustomFormRenderProps } from '../formrender'; - -// 注册组件 -const widgets: CustomFormRenderProps['components'] = { - ...pcWidgets, - ...commonWidgets -}; - -export default widgets; diff --git a/packages/editor/src/config/index.ts b/packages/editor/src/config/index.ts deleted file mode 100644 index 501725d..0000000 --- a/packages/editor/src/config/index.ts +++ /dev/null @@ -1,10 +0,0 @@ - -import pcConfigs from './pc'; -import FieldSetting from './fieldSetting'; - -// 注册编辑器的组件及组件属性 -export default { - ...pcConfigs -}; - -export { FieldSetting }; diff --git a/packages/editor/src/config/pc/grid/col.ts b/packages/editor/src/config/pc/grid/col.ts deleted file mode 100644 index ee5424c..0000000 --- a/packages/editor/src/config/pc/grid/col.ts +++ /dev/null @@ -1,12 +0,0 @@ -import GridColSetting from './col-setting'; - -export default { - panel: { - label: '栅格列', - nonform: true, - nonselection: true, - }, - type: 'GridCol', - props: { span: 12 }, - setting: { ...GridColSetting }, -}; diff --git a/packages/editor/src/config/pc/grid/index.ts b/packages/editor/src/config/pc/grid/index.ts deleted file mode 100644 index d22d0af..0000000 --- a/packages/editor/src/config/pc/grid/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import GridSetting from './setting'; -import GridCol from './col'; - -export default { - panel: { - icon: 'grid', - label: '栅格布局', - nonform: true, - nonselection: true, - }, - type: 'Grid', - setting: { ...GridSetting }, - cols: [ - { ...GridCol, children: [] }, - { ...GridCol, children: [] } - ] -}; diff --git a/packages/editor/src/config/pc/index.ts b/packages/editor/src/config/pc/index.ts deleted file mode 100644 index a5a234f..0000000 --- a/packages/editor/src/config/pc/index.ts +++ /dev/null @@ -1,50 +0,0 @@ -import Input from './input'; -import RadioGroup from './radioGroup'; -import CheckboxGroup from './checkboxGroup'; -import Select from './select'; -import Switch from './switch'; -import TimePicker from './timePicker'; -import TimePickerRangePicker from './timePickerRangePicker'; -import DatePicker from './datePicker'; -import DatePickerRangePicker from './datePickerRangePicker'; -import Slider from './slider'; -import Rate from './rate'; -import FileUpload from './fileUpload'; -import ImageUpload from './imageUpload'; -import Cascader from './cascader'; -import RichEditor from "./richEditor"; -import RichText from "./richText"; -// 布局组件 -import Grid from './grid'; -import Divider from './divider'; -import Alert from './alert'; -import LayoutTable from './layoutTable'; -// 组合组件 -import FormTable from "./formTable"; - -export default { - // 布局组件 - "Grid": Grid, - "Divider": Divider, - "Alert": Alert, - "LayoutTable": LayoutTable, - // 控件组合 - "FormTable": FormTable, - // 基础控件 - "Input": Input, - "Radio.Group": RadioGroup, - "Checkbox.Group": CheckboxGroup, - "Select": Select, - "Switch": Switch, - "TimePicker": TimePicker, - "TimePicker.RangePicker": TimePickerRangePicker, - "DatePicker": DatePicker, - "DatePicker.RangePicker": DatePickerRangePicker, - "Slider": Slider, - "Rate": Rate, - "Cascader": Cascader, - "FileUpload": FileUpload, - "ImageUpload": ImageUpload, - "RichText": RichText, - "RichEditor": RichEditor, -}; diff --git a/packages/editor/src/config/pc/layoutTable/cell.ts b/packages/editor/src/config/pc/layoutTable/cell.ts deleted file mode 100644 index e94f36f..0000000 --- a/packages/editor/src/config/pc/layoutTable/cell.ts +++ /dev/null @@ -1,14 +0,0 @@ -import cellSetting from './cell-setting'; - -export default { - panel: { - label: '单元格', - nonform: true, - nonselection: true, - }, - type: 'TableCell', - colspan: 1, - rowspan: 1, - children: [], - setting: cellSetting, -}; diff --git a/packages/editor/src/config/pc/layoutTable/index.ts b/packages/editor/src/config/pc/layoutTable/index.ts deleted file mode 100644 index fff5503..0000000 --- a/packages/editor/src/config/pc/layoutTable/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import TableCellConfig from './cell'; -import Setting from './setting'; -export default { - panel: { - icon: 'table', - label: '表格布局', - nonform: true, - nonselection: true, - }, - type: 'LayoutTable', - rows: [ - [TableCellConfig] - ], - setting: Setting, -}; diff --git a/packages/editor/src/context.ts b/packages/editor/src/context.ts index 86d9166..510ca9d 100644 --- a/packages/editor/src/context.ts +++ b/packages/editor/src/context.ts @@ -1,26 +1,41 @@ import React, { useContext, useRef, useState } from 'react'; -import { CustomWidgetItem, FormDesignData, SimpleFormRender, SimpleForm, ReactComponent } from './formrender'; +import { CustomWidgetItem, FormDesignData, SimpleFormRender, SimpleForm, ReactComponent, CustomFormChildrenProps } from './formrender'; import { PlatType } from './tools/platContainer'; -import { useMethod } from './utils/hooks'; -import SimpleUndo from './utils/simple-undo'; +import { SimpleUndo } from './utils/index'; // 配置属性类型 export interface ConfigWidgetSetting { [title: string]: FormDesignData } -// 表单编辑器的context +// 编辑器中的数据 export interface FormEditorState { editorForm?: SimpleForm; editor?: SimpleFormRender; settingForm?: SimpleForm | null; - FormRender?: ReactComponent; selected?: { path?: string; setting?: ConfigWidgetSetting }; widgetList?: FormDesignData; - editorConfig?: Record; + editorConfig: Record; // 编辑器配置 + renderConfig?: CustomFormChildrenProps; // 渲染器配置 platType?: PlatType; historyRecord?: SimpleUndo; - onEvent?: (type: string, context?: FormEditorContextProps) => void; + onEvent?: (type: string, editorContext?: FormEditorContextProps) => void; +} + +export function useMethod unknown>(method: T) { + const { current } = useRef<{ method: T, func: T | undefined }>({ + method, + func: undefined, + }); + current.method = method; + + // 只初始化一次 + if (!current.func) { + // 返回给使用方的变量 + current.func = ((...args: unknown[]) => current.method.call(current.method, ...args)) as T; + } + + return current.func; } export interface FormEditorContextProps { @@ -50,7 +65,7 @@ export function useEditorState(initialState: FormEditorState) { }; // 编辑器的context -export const FormEditorContext = React.createContext({ state: {}, dispatch: () => { } }); +export const FormEditorContext = React.createContext({ state: { editorConfig: {} }, dispatch: () => { } }); // 消费context的值 export function useEditorContext() { diff --git a/packages/editor/src/formrender.tsx b/packages/editor/src/formrender.tsx index 2b64794..c8e5aa4 100644 --- a/packages/editor/src/formrender.tsx +++ b/packages/editor/src/formrender.tsx @@ -9,17 +9,13 @@ import DefaultFormRender, { import React from 'react'; import '@simpleform/render/lib/css/main.css'; import { ConfigWidgetSetting, FormEditorContextProps } from './context'; -import createRequest from './utils/request'; -import bindRequest from './components/bind-request'; -import defineConfig from './defineConfig'; export * from '@simpleform/render'; -export { createRequest, bindRequest }; // 自定义传参 export interface CustomOptions { isEditor?: boolean; // 是否为编辑态 - context?: FormEditorContextProps; // 编辑器上下文环境 + editorContext?: FormEditorContextProps; // 编辑器上下文环境 // 配置信息 panel?: { label?: string; // 配置组件的名 @@ -30,6 +26,7 @@ export interface CustomOptions { }; // 属性表单 setting?: ConfigWidgetSetting; + props?: unknown; } // 自定义普通节点信息 export type CustomWidgetItem = WidgetItem & { type?: string }; @@ -43,28 +40,15 @@ export type CommonFormProps = WidgetContextProps; - export type CustomFormChildrenProps = FormChildrenProps; export function FormChildren(props: CustomFormChildrenProps) { - const { components, variables, ...rest } = props; return ( - + ); } export type CustomFormRenderProps = FormRenderProps; export default function FormRender(props: CustomFormRenderProps) { - const { components, variables, ...rest } = props; return ( - + ); } diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts index 8942c36..a46d66c 100644 --- a/packages/editor/src/index.ts +++ b/packages/editor/src/index.ts @@ -6,14 +6,26 @@ import EditorSetting from './setting'; import EditorTools from './tools'; import EditorProvider from './provider'; import FormRender from './formrender'; -import EditorConfig from './config'; -export { EditorConfig, EditorPanel, EditorView, EditorSetting, EditorTools, EditorProvider, FormRender, BaseSelection, BaseDnd }; +export { + EditorPanel, + EditorView, + EditorSetting, + EditorTools, + EditorProvider, + FormRender, + BaseSelection, + BaseDnd +}; + +export * from './view/BaseSelection'; +export * from './view/BaseDnd'; + +export * from './common'; export * from './panel'; export * from './view'; export * from './setting'; export * from './tools'; export * from './provider'; -export * from './utils/utils'; +export * from './utils'; export * from './formrender'; -export * from './config'; diff --git a/packages/editor/src/panel/index.tsx b/packages/editor/src/panel/index.tsx index e14eb58..8d586a0 100644 --- a/packages/editor/src/panel/index.tsx +++ b/packages/editor/src/panel/index.tsx @@ -1,7 +1,7 @@ import React, { CSSProperties } from 'react'; import classnames from 'classnames'; import './index.less'; -import { defaultGetId, getConfigItem, getListIndex, getWidgetItem, insertWidgetItem } from '../utils/utils'; +import { getConfigItem, getListIndex, getWidgetItem, insertWidgetItem } from '../utils'; import { ReactSortable } from "react-sortablejs"; import { FormEditorContextProps, useEditorContext } from '../context'; import { getParent } from '../formrender'; @@ -34,7 +34,7 @@ export interface EditorPanelProps { className?: string style?: CSSProperties panelData?: Record; // 组件面板配置 - children?: (context: FormEditorContextProps) => React.ReactElement; + children?: (editorContext: FormEditorContextProps) => React.ReactElement; } const prefixCls = `simple-form-panel`; @@ -46,11 +46,11 @@ function EditorPanel(props: EditorPanelProps) { children } = props; - const context = useEditorContext(); - const { selected, editor, editorConfig, historyRecord } = context?.state; + const editorContext = useEditorContext(); + const { selected, editor, editorConfig, historyRecord } = editorContext?.state; const cls = classnames(prefixCls, className); - const onChange = (key: string) => { + const onSelect = (key: string) => { const newIndex = getListIndex(editor, selected?.path) + 1; // 插入位置序号 const item = getWidgetItem(editor, selected?.path); // 如果选中的是容器不允许点击插入 @@ -59,14 +59,13 @@ function EditorPanel(props: EditorPanelProps) { return; }; const configItem = getConfigItem(key, editorConfig); // 插入新组件 - const newItem = configItem?.panel?.nonform ? configItem : Object.assign({ name: defaultGetId(configItem?.type) }, configItem); - insertWidgetItem(editor, newItem, newIndex, getParent(selected?.path)); + insertWidgetItem(editor, configItem, newIndex, getParent(selected?.path)); historyRecord?.save(); }; return ( typeof children == 'function' ? - children(context) + children(editorContext) :
{ @@ -91,7 +90,7 @@ function EditorPanel(props: EditorPanelProps) { list.map((key) => { const data = editorConfig?.[key] || {}; const panel = typeof data?.panel === 'object' ? data?.panel : {}; - return onChange?.(key)}>{panel.label}; + return onSelect?.(key)}>{panel.label}; }) } diff --git a/packages/editor/src/provider.tsx b/packages/editor/src/provider.tsx index 54683dd..233cc0f 100644 --- a/packages/editor/src/provider.tsx +++ b/packages/editor/src/provider.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { useSimpleFormRender, useSimpleForm } from './formrender'; import { FormEditorContext, FormEditorState, useEditorState } from './context'; -import EditorConfig from './config'; export interface EditorProviderProps extends FormEditorState { children?: React.ReactNode; @@ -12,7 +11,6 @@ function EditorProvider(props: EditorProviderProps) { const { children, - editorConfig, editor = curEditor, editorForm = curEditorForm, ...rest @@ -21,7 +19,6 @@ function EditorProvider(props: EditorProviderProps) { const [state, dispatch] = useEditorState({ editor: editor, editorForm: editorForm, - editorConfig: { ...EditorConfig, ...editorConfig }, selected: {}, widgetList: [], ...rest diff --git a/packages/editor/src/setting/component.tsx b/packages/editor/src/setting/component.tsx index 52a5c87..debbea6 100644 --- a/packages/editor/src/setting/component.tsx +++ b/packages/editor/src/setting/component.tsx @@ -1,9 +1,9 @@ import React, { CSSProperties, useEffect, useMemo } from 'react'; import classnames from 'classnames'; import { CustomFormRenderProps, Form, FormChildren, joinFormPath, useSimpleForm } from '../formrender'; -import { getWidgetItem, setWidgetItem } from '../utils/utils'; +import { getWidgetItem, setWidgetItem } from '../utils'; import './component.less'; -import CustomCollapse from '../components/common/Collapse'; +import { Collapse } from '../common'; import { useEditorContext } from '../context'; export interface SelectedSettingProps { @@ -19,8 +19,9 @@ const SelectedSetting = React.forwardRef(( className, } = props; - const context = useEditorContext(); - const { selected, editor, editorForm, editorConfig } = context?.state || {}; + const editorContext = useEditorContext(); + const renderConfig = editorContext?.state?.renderConfig; + const { selected, editor, editorForm, editorConfig } = editorContext?.state || {}; const selectedPath = selected?.path; const form = useSimpleForm(); const cls = classnames(prefixCls, className); @@ -33,7 +34,7 @@ const SelectedSetting = React.forwardRef(( }, [editor, selectedPath, editorConfig]); useEffect(() => { - context?.dispatch((old) => ({ ...old, settingForm: form })); + editorContext?.dispatch((old) => ({ ...old, settingForm: form })); }, []); useEffect(() => { @@ -61,9 +62,9 @@ const SelectedSetting = React.forwardRef(( return ( Object.entries(configSetting)?.map(([name, data]) => { return ( - - - + + + ); }) ); diff --git a/packages/editor/src/setting/index.tsx b/packages/editor/src/setting/index.tsx index c6a35ac..899e429 100644 --- a/packages/editor/src/setting/index.tsx +++ b/packages/editor/src/setting/index.tsx @@ -8,7 +8,7 @@ import { FormEditorContextProps, useEditorContext } from '../context'; export interface EditorSettingProps { className?: string style?: CSSProperties - children?: (context: FormEditorContextProps) => React.ReactElement; + children?: (editorContext: FormEditorContextProps) => React.ReactElement; } const prefixCls = `simple-form-setting`; function EditorSetting(props: EditorSettingProps) { @@ -24,13 +24,13 @@ function EditorSetting(props: EditorSettingProps) { component: ComponentSetting }]; - const context = useEditorContext(); + const editorContext = useEditorContext(); const cls = classnames(prefixCls, className); return ( typeof children === 'function' ? - children(context) + children(editorContext) :
diff --git a/packages/editor/src/tools/exportJson.tsx b/packages/editor/src/tools/exportJson.tsx index 1e4cf31..0c437fd 100644 --- a/packages/editor/src/tools/exportJson.tsx +++ b/packages/editor/src/tools/exportJson.tsx @@ -1,12 +1,9 @@ import classnames from 'classnames'; import React, { useEffect, useState } from 'react'; import { Button } from 'antd'; -import { convertToString, copyToClipboard } from '../utils/string'; -import { saveAsFile } from '../utils/file'; +import { saveAsFile, convertToString, copyToClipboard } from '../utils'; import js_beautify from 'js-beautify'; -import ModalWrapper, { ModalWrapperProps } from '../components/common/GlobalModal/modalWrapper'; -import { create } from '../components/common/GlobalModal/createPromise'; -import { EditorCodeMirror } from '../components/common/CodeMirror'; +import { createModalPromise, EditorCodeMirror, ModalWrapper, ModalWrapperProps } from '../common/index'; import './exportJson.less'; export interface ExportJsonModalProps extends ModalWrapperProps { @@ -96,5 +93,5 @@ export const showExportJsonModal = (props: Partial) => { open: true, ...props, }; - return create(ExportJsonModal, { ...Props }); + return createModalPromise(ExportJsonModal, { ...Props }); }; diff --git a/packages/editor/src/tools/index.tsx b/packages/editor/src/tools/index.tsx index 07bbc72..156020a 100644 --- a/packages/editor/src/tools/index.tsx +++ b/packages/editor/src/tools/index.tsx @@ -3,23 +3,23 @@ import classnames from 'classnames'; import './index.less'; import { Button, Divider, Flex, Radio, Tooltip } from 'antd'; import { FormEditorContextProps, useEditorContext } from '../context'; -import SvgIcon from '../components/common/SvgIcon'; +import { SvgIcon } from '../common'; import { PlatOptions } from './platContainer'; import { showPreviewModal } from './preview'; import { showExportJsonModal } from './exportJson'; -import { setWidgetItem } from '../utils/utils'; +import { setWidgetItem } from '../utils'; export interface EditorToolsProps { className?: string; style?: CSSProperties; - children?: (context: FormEditorContextProps) => React.ReactElement; - renderTools?: (context: FormEditorContextProps) => React.ReactElement; + children?: (editorContext: FormEditorContextProps) => React.ReactElement; + renderTools?: (editorContext: FormEditorContextProps) => React.ReactElement; } function EditorTools(props: EditorToolsProps) { - const context = useEditorContext(); - const { platType = 'pc', widgetList, editor, historyRecord } = context?.state || {}; + const editorContext = useEditorContext(); + const { platType = 'pc', widgetList, editor, historyRecord } = editorContext?.state || {}; const { style, @@ -30,7 +30,7 @@ function EditorTools(props: EditorToolsProps) { } = props; const showPreview = () => { - showPreviewModal({ data: widgetList, plat: platType, context }); + showPreviewModal({ data: widgetList, plat: platType, editorContext }); }; const clearEditor = () => { @@ -65,7 +65,7 @@ function EditorTools(props: EditorToolsProps) { return ( typeof children === 'function' ? - children(context) + children(editorContext) :
context.dispatch((old) => ({ ...old, platType: e?.target?.value }))} + onChange={(e) => editorContext.dispatch((old) => ({ ...old, platType: e?.target?.value }))} value={platType} optionType="button" buttonStyle="solid" />
- {renderTools ? renderTools(context) : null} + {renderTools ? renderTools(editorContext) : null} diff --git a/packages/editor/src/tools/preview.tsx b/packages/editor/src/tools/preview.tsx index 15b07fa..1ee2651 100644 --- a/packages/editor/src/tools/preview.tsx +++ b/packages/editor/src/tools/preview.tsx @@ -5,12 +5,11 @@ import PlatContainer, { PlatContainerProps, PlatOptions } from './platContainer' import { Button, Radio } from 'antd'; import { showExportJsonModal } from './exportJson'; import { CloseOutlined } from '@ant-design/icons'; -import ModalWrapper, { ModalWrapperProps } from '../components/common/GlobalModal/modalWrapper'; -import { create } from '../components/common/GlobalModal/createPromise'; -import DefaultFormRender, { CustomOptions, FormDesignData, useSimpleForm } from '../formrender'; +import { createModalPromise, ModalWrapper, ModalWrapperProps } from '../common'; +import FormRender, { CustomOptions, FormDesignData, useSimpleForm } from '../formrender'; export interface PreviewModalProps extends ModalWrapperProps { data?: FormDesignData; - context?: CustomOptions['context']; + editorContext?: CustomOptions['editorContext']; plat?: PlatContainerProps['plat']; } @@ -23,15 +22,15 @@ export const PreviewModal = React.forwardRef( onClose, data, plat, - context, + editorContext, ...rest } = props; + const renderConfig = editorContext?.state?.renderConfig; const [modalOpen, setModalOpen] = useState(false); const [disabled, setDisabled] = useState(); const [platType, setPlatType] = useState('pc'); const form = useSimpleForm(); - const FormRender = context?.state?.FormRender || DefaultFormRender; useEffect(() => { setModalOpen(open); @@ -87,7 +86,8 @@ export const PreviewModal = React.forwardRef(
@@ -109,5 +109,5 @@ export const showPreviewModal = (props: Partial) => { open: true, ...props, }; - return create(PreviewModal, { ...Props }); + return createModalPromise(PreviewModal, { ...Props }); }; diff --git a/packages/editor/src/utils/array.ts b/packages/editor/src/utils/array.ts deleted file mode 100644 index 0140006..0000000 --- a/packages/editor/src/utils/array.ts +++ /dev/null @@ -1,10 +0,0 @@ -// 转化对象数组为map数据 -export interface GetArrMap { - (arr?: T[], valueKey?: string): Record; - (arr?: T[], valueKey?: string, labelKey?: string): Record -} -export const getArrMap = (arr: T[] = [], valueKey?: string, labelKey?: string) => { - const data = {}; - arr.forEach((item) => item && item[valueKey || ''] !== undefined && (data[item?.[valueKey || '']] = labelKey ? item[labelKey] : item)); - return data as Record; -}; diff --git a/packages/editor/src/utils/file.ts b/packages/editor/src/utils/file.ts deleted file mode 100644 index 57fbae0..0000000 --- a/packages/editor/src/utils/file.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { saveAs } from 'file-saver'; - -// 保存文件 -export const saveAsFile = (fileContent: string | File, fileName: string) => { - const fileBlob = new Blob([fileContent], { type: 'text/plain;charset=utf-8' }); - saveAs(fileBlob, fileName); -}; diff --git a/packages/editor/src/utils/hooks.ts b/packages/editor/src/utils/hooks.ts deleted file mode 100644 index 0d37f2a..0000000 --- a/packages/editor/src/utils/hooks.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { useRef } from "react"; - -export function useMethod unknown>(method: T) { - const { current } = useRef<{ method: T, func: T | undefined }>({ - method, - func: undefined, - }); - current.method = method; - - // 只初始化一次 - if (!current.func) { - // 返回给使用方的变量 - current.func = ((...args: unknown[]) => current.method.call(current.method, ...args)) as T; - } - - return current.func; -} diff --git a/packages/editor/src/utils/index.ts b/packages/editor/src/utils/index.ts new file mode 100644 index 0000000..18fc111 --- /dev/null +++ b/packages/editor/src/utils/index.ts @@ -0,0 +1,135 @@ +import { saveAs } from 'file-saver'; +import { copy } from "copy-anything"; +import { isEmpty, isObject } from "./type"; +import Clipboard from 'clipboard'; +import pickAttrs from './pickAttrs'; +import createRequest from './request'; +import TableUtils from './tableUtils'; +import SimpleUndo from './simpleUndo'; +import serialize from 'serialize-javascript'; + +export { + pickAttrs, + createRequest, + TableUtils, + SimpleUndo, +}; + +export * from './mime'; +export * from './type'; +export * from './utils'; +export * from './tableUtils'; +export * from './request'; + +// 转化对象数组为map数据 +export interface GetArrMap { + (arr?: T[], valueKey?: string): Record; + (arr?: T[], valueKey?: string, labelKey?: string): Record +} +export const getArrMap = (arr: T[] = [], valueKey?: string, labelKey?: string) => { + const data = {}; + arr.forEach((item) => item && item[valueKey || ''] !== undefined && (data[item?.[valueKey || '']] = labelKey ? item[labelKey] : item)); + return data as Record; +}; + +// 保存文件 +export const saveAsFile = (fileContent: string | File, fileName: string) => { + const fileBlob = new Blob([fileContent], { type: 'text/plain;charset=utf-8' }); + saveAs(fileBlob, fileName); +}; + +export function deepClone(value: T) { + return copy(value); +} + +/** + * 递归去除参数的前后空格 + * @param {*} data 参数 + */ +export const trimParams = (data?: V) => { + if (typeof data === 'string') return data.trim(); + if (data && isObject(data)) { + for (let key of Object.keys(data)) { + data[key] = trimParams(data[key]); + } + } + return data; +}; + + +/** + * 递归将对象/嵌套对象的数据转化为formdata格式数据 + * @param {Object} obj 传入的对象数据 + * @param {FormData} formData 是否传入已有的formData数据 + */ +export function objectToFormData(obj?: object, formData?: FormData) { + const fd = (formData instanceof FormData) ? formData : new FormData(); + if (typeof obj !== 'object') return fd; + let formKey; + for (let property of Object.keys(obj)) { + if (obj.hasOwnProperty(property)) { + formKey = property; + // 如果传入数据的值为对象且不是二进制文件 + if (typeof obj[property] === 'object' && !(obj[property] instanceof File)) { + objectToFormData(obj[property], fd); + } else { + fd.append(formKey, obj[property]); + } + } + } + return fd; +} + +// 深度合并两个对象 +export const deepMergeObject = (obj1: V, obj2?: unknown): V => { + const obj1Type = Object.prototype.toString.call(obj1); + const obj2Type = Object.prototype.toString.call(obj2); + if (obj1Type !== obj2Type || typeof obj2 !== 'object') return obj1; + const cloneObj = deepClone(obj1); + for (let key of Object.keys(obj2 || {})) { + if (isObject(cloneObj[key])) { + cloneObj[key] = deepMergeObject(cloneObj[key], obj2?.[key]); + } else { + cloneObj[key] = obj2?.[key]; + } + } + return cloneObj; +}; + +// 复制到剪贴板 +export function copyToClipboard(content?: unknown, clickEvent?: Event, successFn?: () => void, errorFn?: () => void) { + if (typeof content !== 'string') return; + const el = clickEvent?.target as HTMLElement; + const clipboard = new Clipboard(el, { + text: () => content + }); + + clipboard.on('success', () => { + successFn && successFn(); + clipboard.destroy(); + }); + + clipboard.on('error', () => { + errorFn && errorFn(); + clipboard.destroy(); + }); + + (clipboard as any)?.onClick(clickEvent); +} + +// 将对象转化为普通字符串(非json格式) +export function convertToString(val?: unknown) { + if (isEmpty(val)) return; + if (typeof val === 'string') return val; + return serialize(val); +} + +// 将普通字符串转化为js(非json格式) +export function evalString(val: string): V | undefined { + if (isEmpty(val) || typeof val !== 'string') return; + try { + return eval(`(function(){return ${val} })()`); + } catch (e) { + console.error(e); + } +} diff --git a/packages/editor/src/utils/object.ts b/packages/editor/src/utils/object.ts deleted file mode 100644 index 209acf5..0000000 --- a/packages/editor/src/utils/object.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { copy } from "copy-anything"; -import { isObject } from "./type"; - -export function deepClone(value: T) { - return copy(value); -} - -/** - * 递归去除参数的前后空格 - * @param {*} data 参数 - */ -export const trimParams = (data?: V) => { - if (typeof data === 'string') return data.trim(); - if (data && isObject(data)) { - for (let key of Object.keys(data)) { - data[key] = trimParams(data[key]); - } - } - return data; -}; - - -/** - * 递归将对象/嵌套对象的数据转化为formdata格式数据 - * @param {Object} obj 传入的对象数据 - * @param {FormData} formData 是否传入已有的formData数据 - */ -export function objectToFormData(obj?: object, formData?: FormData) { - const fd = (formData instanceof FormData) ? formData : new FormData(); - if (typeof obj !== 'object') return fd; - let formKey; - for (let property of Object.keys(obj)) { - if (obj.hasOwnProperty(property)) { - formKey = property; - // 如果传入数据的值为对象且不是二进制文件 - if (typeof obj[property] === 'object' && !(obj[property] instanceof File)) { - objectToFormData(obj[property], fd); - } else { - fd.append(formKey, obj[property]); - } - } - } - return fd; -} - -// 深度合并两个对象 -export const deepMergeObject = (obj1: V, obj2?: unknown): V => { - const obj1Type = Object.prototype.toString.call(obj1); - const obj2Type = Object.prototype.toString.call(obj2); - if (obj1Type !== obj2Type || typeof obj2 !== 'object') return obj1; - const cloneObj = deepClone(obj1); - for (let key of Object.keys(obj2 || {})) { - if (isObject(cloneObj[key])) { - cloneObj[key] = deepMergeObject(cloneObj[key], obj2?.[key]); - } else { - cloneObj[key] = obj2?.[key]; - } - } - return cloneObj; -}; diff --git a/packages/editor/src/utils/request/createRequest.ts b/packages/editor/src/utils/request/createRequest.ts index f158317..1baf4c8 100644 --- a/packages/editor/src/utils/request/createRequest.ts +++ b/packages/editor/src/utils/request/createRequest.ts @@ -1,5 +1,5 @@ import axios, { AxiosResponse, CreateAxiosDefaults } from "axios"; -import { trimParams } from "../object"; +import { trimParams } from "../"; import { IE11OrLess } from "./brower"; import { HTTP_STATUS, CancelPending, CustomConfig } from "./config"; diff --git a/packages/editor/src/utils/request/index.ts b/packages/editor/src/utils/request/index.ts index a960034..312bfaa 100644 --- a/packages/editor/src/utils/request/index.ts +++ b/packages/editor/src/utils/request/index.ts @@ -1,5 +1,5 @@ import CreateRequest, { CreateRequestParams } from './createRequest'; -import { HTTP_CODE, HTTP_CODE_MAP, HTTP_STATUS, HTTP_STATUS_MAP } from './config'; +import { HTTP_CODE, HTTP_CODE_MAP, HTTP_STATUS_MAP } from './config'; import { message } from 'antd'; // 请求体结构 @@ -26,9 +26,6 @@ const createRequest = (props?: CreateRequestParams) => { }, // 处理状态码 handleStatus: (status, msg) => { - if (status == HTTP_STATUS.AUTH) { - // loginOut(); - } const msgRes = msg || HTTP_STATUS_MAP[status]; msgRes && message.info(msgRes); }, diff --git a/packages/editor/src/utils/simple-undo.ts b/packages/editor/src/utils/simpleUndo.ts similarity index 100% rename from packages/editor/src/utils/simple-undo.ts rename to packages/editor/src/utils/simpleUndo.ts diff --git a/packages/editor/src/utils/string.ts b/packages/editor/src/utils/string.ts deleted file mode 100644 index 2efa44e..0000000 --- a/packages/editor/src/utils/string.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { isEmpty } from './type'; -import Clipboard from 'clipboard'; -import * as devalue from 'devalue'; - -// 复制到剪贴板 -export function copyToClipboard(content?: unknown, clickEvent?: Event, successFn?: () => void, errorFn?: () => void) { - if (typeof content !== 'string') return; - const el = clickEvent?.target as HTMLElement; - const clipboard = new Clipboard(el, { - text: () => content - }); - - clipboard.on('success', () => { - successFn && successFn(); - clipboard.destroy(); - }); - - clipboard.on('error', () => { - errorFn && errorFn(); - clipboard.destroy(); - }); - - (clipboard as any)?.onClick(clickEvent); -} - -// 将对象转化为普通字符串(非json格式) -export function convertToString(val?: unknown, allowFunction: boolean = true): string | undefined { - if (isEmpty(val)) return; - if (typeof val === 'string') return val; - if (typeof val === 'function') { - if (allowFunction) { - return val.toString(); - } else { - return; - } - } - try { - return devalue.uneval(val); - } catch (e) { - console.error(e); - } -} - -// 将普通字符串转化为js(非json格式) -export function evalString(val: string): V | undefined { - if (isEmpty(val) || typeof val !== 'string') return; - try { - return eval(`(function(){return ${val} })()`); - } catch (e) { - console.error(e); - } -} diff --git a/packages/editor/src/utils/tableUtils/index.ts b/packages/editor/src/utils/tableUtils/index.ts index d5e56a9..5b2d5ce 100644 --- a/packages/editor/src/utils/tableUtils/index.ts +++ b/packages/editor/src/utils/tableUtils/index.ts @@ -797,3 +797,4 @@ class TableUtils { } export default TableUtils; +export * from './util'; diff --git a/packages/editor/src/utils/type.ts b/packages/editor/src/utils/type.ts index 99a1643..8902b1f 100644 --- a/packages/editor/src/utils/type.ts +++ b/packages/editor/src/utils/type.ts @@ -5,51 +5,51 @@ export function getType(obj: T) { return Object.prototype.toString.call(obj); } -export function isBoolean(data: T) { +export function isBoolean(data: T | boolean): data is boolean { return getType(data) == '[object Boolean]'; } -export function isNumber(data: T) { +export function isNumber(data: T | number): data is number { return getType(data) == '[object Number]'; } -export function isString(data: T) { +export function isString(data: T | string): data is string { return getType(data) == '[object String]'; } -export function isFunction(data: T) { +export function isFunction(data: T | Function): data is Function { return getType(data) == '[object Function]'; } -export function isArray(data: T) { +export function isArray(data: T | unknown[]): data is unknown[] { return getType(data) == '[object Array]'; } -export function isDate(data: T) { +export function isDate(data: T | Date): data is Date { return data instanceof Date; } -export function isRegExp(data: T) { +export function isRegExp(data: T | RegExp): data is RegExp { return getType(data) == '[object RegExp]'; } -export function isUndefined(data: T) { +export function isUndefined(data: T | undefined): data is undefined { return getType(data) == '[object Undefined]'; } -export function isNull(data: T) { +export function isNull(data: T | null): data is null { return getType(data) == '[object Null]'; } -export function isObject(data: T) { +export function isObject(data: T | object): data is object { return getType(data) == '[object Object]'; } -export function isElement(data: T) { +export function isElement(data: T | Element): data is Element { return data instanceof Element; } -export function isDom(data: T & { nodeType?: number; nodeName?: string }) { +export function isDom(data: T & { nodeType?: number; nodeName?: string } | HTMLElement): data is HTMLElement { if (typeof HTMLElement === 'object') { return data instanceof HTMLElement; } else { @@ -57,12 +57,13 @@ export function isDom(data: T & { nodeType?: number; nodeName?: string }) { } } -export function isNodeList(data: T) { +export function isNodeList(data: T | NodeList): data is NodeList { return getType(data) == '[object NodeList]'; } // 判断值是否为空 -export function isEmpty(value: T) { +type EmptyType = undefined | null | '' | [] | {}; +export function isEmpty(value: T | EmptyType): value is EmptyType { if (value === undefined || value === null) return true; if (Array.isArray(value) || typeof value === 'string' @@ -82,13 +83,14 @@ export function isEmpty(value: T) { if (typeof value === 'number') { return isNaN(value); } + return false; } -export function isArrayBuffer(data: T) { +export function isArrayBuffer(data: T | ArrayBuffer): data is ArrayBuffer { return getType(data) === '[object ArrayBuffer]'; } -export function isFormData(data: T) { +export function isFormData(data: T | FormData): data is FormData { return (typeof FormData !== 'undefined') && (data instanceof FormData); } @@ -102,11 +104,11 @@ export function isArrayBufferView(data: T & { buffer?: ArrayBuffer }) { return result; } -export function isFile(data: T) { +export function isFile(data: T | File): data is File { return getType(data) === '[object File]'; } -export function isBlob(data: T) { +export function isBlob(data: T | Blob): data is Blob { return getType(data) === '[object Blob]'; } @@ -115,24 +117,27 @@ export function isStream(data: T & { pipe?: Function }) { } // 是否为简单类型 -export function isBase(data: T) { +type BaseTypes = string | number | symbol | boolean; +export function isBase(data: T | BaseTypes): data is BaseTypes { const type = typeof data; return ['string', 'number', 'symbol', 'boolean']?.includes(type); } // 是否为数字字符串或者数字 -export const isNumberStr = (str?: T) => { +type NumberOrStr = string | number; +export const isNumberStr = (str?: T | NumberOrStr): str is NumberOrStr => { if (typeof str === 'number' && !isNaN(str)) return true; if (typeof str === 'string') { const target = Number(str); if (!isNaN(target) && str) return true; } + return false; }; // 是否为带data:开头的base64的字符串 -export const isBase64 = (str?: T) => { - if (typeof str !== 'string') return; - if (str.trim() === '') return; +export const isBase64 = (str?: T | string): str is string => { + if (typeof str !== 'string') return false; + if (str.trim() === '') return false; const regx = /^(data:\S+\/\S+;base64,){1}/; return regx.test(str); }; diff --git a/packages/editor/src/utils/utils.ts b/packages/editor/src/utils/utils.ts index 6c6b32c..b37a5d4 100644 --- a/packages/editor/src/utils/utils.ts +++ b/packages/editor/src/utils/utils.ts @@ -1,8 +1,7 @@ -import { deepMergeObject } from './object'; +import { deepMergeObject } from '.'; import { nanoid } from 'nanoid'; import { SimpleFormRender, getInitialValues, getPathEnd, CustomGenerateWidgetItem, CommonFormProps } from '../formrender'; import { ConfigWidgetSetting, FormEditorState } from '../context'; -import React from 'react'; export const defaultGetId = (key?: string) => { return typeof key == 'string' ? `${key.replace(/\./g, '')}_${nanoid(6)}` : ''; @@ -58,9 +57,9 @@ export const moveWidgetItem = (formrender?: SimpleFormRender | null, from?: { in }; // 插入新节点 -export const insertWidgetItem = (formrender?: SimpleFormRender | null, data?: unknown, index?: number, parent?: string) => { +export const insertWidgetItem = (formrender?: SimpleFormRender | null, data?: CustomGenerateWidgetItem, index?: number, parent?: string) => { if (!formrender || !data) return; - const newData = data; + const newData = data?.panel?.nonform ? data : Object.assign({ name: defaultGetId(data?.type) }, data); formrender?.insertItemByIndex(newData, index, parent); }; @@ -78,6 +77,6 @@ export const delWidgetItem = (formrender?: SimpleFormRender | null, path?: strin // 提取对象中公共的选项 export const getCommonOptions = (_options?: CommonFormProps['_options']) => { - const { isEditor, formrender, form, context, props } = _options || {}; - return { isEditor, formrender, form, context, props }; + const { isEditor, formrender, form, editorContext, props } = _options || {}; + return { isEditor, formrender, form, editorContext, props }; }; diff --git a/packages/editor/src/view/BaseDnd.tsx b/packages/editor/src/view/BaseDnd.tsx index 0f035f5..d25bd98 100644 --- a/packages/editor/src/view/BaseDnd.tsx +++ b/packages/editor/src/view/BaseDnd.tsx @@ -1,6 +1,6 @@ import { ReactSortable, ReactSortableProps } from "react-sortablejs"; import React, { CSSProperties } from 'react'; -import { defaultGetId, getConfigItem, insertWidgetItem, moveWidgetItem } from '../utils/utils'; +import { getConfigItem, insertWidgetItem, moveWidgetItem } from '../utils'; import { getParent, joinFormPath, CommonFormProps } from '../formrender'; import './BaseDnd.less'; @@ -14,9 +14,9 @@ export interface ControlDndProps extends CommonFormProps, Omit, ControlDndProps>((props, ref) => { const { children, _options, dndPath, dndList = [], setList = () => { }, ...rest } = props; - const context = _options?.context; + const editorContext = _options?.editorContext; const formrender = _options?.formrender; - const { editorConfig, historyRecord } = context?.state || {}; + const { editorConfig, historyRecord } = editorContext?.state || {}; const onUpdate: ControlDndProps['onUpdate'] = (params) => { const newIndex = params?.oldIndex; @@ -24,7 +24,7 @@ const BaseDnd = React.forwardRef, ControlDndProps>((props, re const dropIndex = params?.newIndex; const dropParent = dndPath; moveWidgetItem(formrender, { index: newIndex, parent: dropParent }, { index: dropIndex, parent: dropParent }); - context?.dispatch((old) => ({ ...old, selected: Object.assign(old?.selected, { path: joinFormPath(dropParent, dropIndex) }) })); + editorContext?.dispatch((old) => ({ ...old, selected: Object.assign(old?.selected, { path: joinFormPath(dropParent, dropIndex) }) })); historyRecord?.save(); console.log(params, '同域拖放'); }; @@ -35,13 +35,12 @@ const BaseDnd = React.forwardRef, ControlDndProps>((props, re const isPanel = from?.dataset?.group === 'panel'; // 从侧边栏插入进来 if (isPanel) { - const fromId = from?.dataset?.type; + const fromType = from?.dataset?.type; const dropIndex = params?.newIndex; const dropParent = dndPath; - const configItem = getConfigItem(fromId, editorConfig); - const newItem = configItem?.panel?.nonform ? configItem : Object.assign({ name: defaultGetId(fromId) }, configItem); - insertWidgetItem(formrender, newItem, dropIndex, dropParent); - context?.dispatch((old) => ({ ...old, selected: Object.assign(old?.selected, { path: joinFormPath(dropParent, dropIndex) }) })); + const configItem = getConfigItem(fromType, editorConfig); + insertWidgetItem(formrender, configItem, dropIndex, dropParent); + editorContext?.dispatch((old) => ({ ...old, selected: Object.assign(old?.selected, { path: joinFormPath(dropParent, dropIndex) }) })); } else { const fromPath = from?.dataset?.path; if(!fromPath) { @@ -54,7 +53,7 @@ const BaseDnd = React.forwardRef, ControlDndProps>((props, re const dropIndex = params?.newIndex; const dropParent = dndPath; moveWidgetItem(formrender, { index: fromIndex, parent: fromParent }, { index: dropIndex, parent: dropParent }); - context?.dispatch((old) => ({ ...old, selected: Object.assign(old?.selected, { path: joinFormPath(dropParent, dropIndex) }) })); + editorContext?.dispatch((old) => ({ ...old, selected: Object.assign(old?.selected, { path: joinFormPath(dropParent, dropIndex) }) })); } historyRecord?.save(); }; diff --git a/packages/editor/src/view/BaseSelection.tsx b/packages/editor/src/view/BaseSelection.tsx index 744b252..312687b 100644 --- a/packages/editor/src/view/BaseSelection.tsx +++ b/packages/editor/src/view/BaseSelection.tsx @@ -1,10 +1,9 @@ import classnames from 'classnames'; import React, { ReactNode, useState } from 'react'; import './BaseSelection.less'; -import pickAttrs from '../utils/pickAttrs'; import { FormEditorState } from '../context'; -import SvgIcon from '../components/common/SvgIcon'; -import { delWidgetItem } from '../utils/utils'; +import { SvgIcon } from '../common'; +import { delWidgetItem, pickAttrs } from '../utils'; import { CommonFormProps } from '../formrender'; export interface BaseSelectionProps extends CommonFormProps, Omit, 'draggable' | 'onChange' | 'onSelect'> { @@ -37,11 +36,11 @@ const BaseSelection = React.forwardRef((prop const prefixCls = "editor-selection"; const overCls = `${prefixCls}-over`; - const context = _options?.context; + const editorContext = _options?.editorContext; const path = _options?.path; const editor = _options?.formrender; const [isOver, setIsOver] = useState(false); - const { selected, historyRecord, onEvent } = context?.state || {}; + const { selected, historyRecord, onEvent } = editorContext?.state || {}; const isSelected = path ? path === selected?.path : false; const nextSelected = { @@ -49,12 +48,12 @@ const BaseSelection = React.forwardRef((prop }; const chooseItem = () => { - onEvent && onEvent('select', context); + onEvent && onEvent('select', editorContext); if (onSelectHandler) { onSelectHandler(nextSelected); return; } - context?.dispatch && context?.dispatch((old) => ({ + editorContext?.dispatch && editorContext?.dispatch((old) => ({ ...old, selected: nextSelected })); @@ -62,7 +61,7 @@ const BaseSelection = React.forwardRef((prop const deleteColumn = (e) => { e.stopPropagation(); - context?.dispatch && context?.dispatch((old) => ({ ...old, selected: {} })); + editorContext?.dispatch && editorContext?.dispatch((old) => ({ ...old, selected: {} })); delWidgetItem(editor, path); historyRecord?.save(); }; diff --git a/packages/editor/src/view/RootDnd.tsx b/packages/editor/src/view/RootDnd.tsx index 470e074..64f63e1 100644 --- a/packages/editor/src/view/RootDnd.tsx +++ b/packages/editor/src/view/RootDnd.tsx @@ -5,11 +5,11 @@ import { useEditorContext } from '../context'; // 根节点的拖放区域 const RootDnd: React.FC = (props) => { - const context = useEditorContext(); + const editorContext = useEditorContext(); return ; }; diff --git a/packages/editor/src/view/index.less b/packages/editor/src/view/index.less index ebf3604..ce642f7 100644 --- a/packages/editor/src/view/index.less +++ b/packages/editor/src/view/index.less @@ -10,10 +10,4 @@ height: 100%; height: 100%; } - - .field-item, - .field-list, - .custom-item { - margin: 2px 0px; - } } \ No newline at end of file diff --git a/packages/editor/src/view/index.tsx b/packages/editor/src/view/index.tsx index 156620e..420afcf 100644 --- a/packages/editor/src/view/index.tsx +++ b/packages/editor/src/view/index.tsx @@ -3,22 +3,22 @@ import classnames from 'classnames'; import './index.less'; import RootDnd from './RootDnd'; import CommonSelection from './selection'; -import DefaultFormRender, { CustomFormRenderProps, joinFormPath } from '../formrender'; -import { setWidgetItem } from '../utils/utils'; +import FormRender, { joinFormPath } from '../formrender'; +import { setWidgetItem } from '../utils'; import { FormEditorContextProps, useEditorContext } from '../context'; import PlatContainer from '../tools/platContainer'; export interface EditorViewProps { className?: string; style?: CSSProperties; - children?: (context: FormEditorContextProps) => React.ReactElement; + children?: (editorContext: FormEditorContextProps) => React.ReactElement; } function EditorView(props: EditorViewProps) { - const context = useEditorContext(); - const { platType = 'pc', selected, editor, editorForm, settingForm, widgetList } = context?.state || {}; - const FormRender = context?.state?.FormRender || DefaultFormRender; + const editorContext = useEditorContext(); + const renderConfig = editorContext?.state?.renderConfig; + const { platType = 'pc', selected, editor, editorForm, settingForm, widgetList } = editorContext?.state || {}; const { style, @@ -29,34 +29,34 @@ function EditorView(props: EditorViewProps) { const onRenderChange = (newData) => { console.log(newData, '表单'); - context?.dispatch((old) => ({ + editorContext?.dispatch((old) => ({ ...old, widgetList: newData || [] })); }; // 监听编辑区域的初始表单值 - const onFieldsChange: CustomFormRenderProps['onFieldsChange'] = ({ value }) => { - setWidgetItem(editor, value, joinFormPath(selected?.path, 'initialValue')); - settingForm && settingForm.setFieldValue('initialValue', value); + const onFieldsChange = (_) => { + setWidgetItem(editor, _?.value, joinFormPath(selected?.path, 'initialValue')); + settingForm && settingForm.setFieldValue('initialValue', _?.value); }; const cls = classnames("editor-view", className); return ( typeof children === 'function' ? - children(context) + children(editorContext) :
{ - }}> + > ((props, ref) => { @@ -13,8 +13,8 @@ const CommonSelection = React.forwardRef((pr const path = _options?.path; const index = _options?.index; const editor = _options?.formrender; - const context = _options?.context; - const { historyRecord } = context?.state || {}; + const editorContext = _options?.editorContext; + const { historyRecord } = editorContext?.state || {}; const copyItem = () => { const currentIndex = index || -1; diff --git a/packages/form/README.md b/packages/form/README.md index 65f2e26..31639d4 100644 --- a/packages/form/README.md +++ b/packages/form/README.md @@ -1,7 +1,7 @@ # `@simpleform/form` English | [中文说明](./README_CN.md) -[![](https://img.shields.io/badge/version-2.1.14-green)](https://www.npmjs.com/package/@simpleform/form) +[![](https://img.shields.io/badge/version-2.1.15-green)](https://www.npmjs.com/package/@simpleform/form) > The underlying form component, Binding of form values to display and update events is accomplished through callback functions. diff --git a/packages/form/README_CN.md b/packages/form/README_CN.md index cbfcba1..e375484 100644 --- a/packages/form/README_CN.md +++ b/packages/form/README_CN.md @@ -2,7 +2,7 @@ [English](./README.md) | 中文说明 -[![](https://img.shields.io/badge/version-2.1.14-green)](https://www.npmjs.com/package/@simpleform/form) +[![](https://img.shields.io/badge/version-2.1.15-green)](https://www.npmjs.com/package/@simpleform/form) > 表单底层组件,通过回调函数方式实现表单值的显示和更新事件的绑定. diff --git a/packages/form/package.json b/packages/form/package.json index 8ebfbb7..bac5cb6 100644 --- a/packages/form/package.json +++ b/packages/form/package.json @@ -1,6 +1,6 @@ { "name": "@simpleform/form", - "version": "2.1.14", + "version": "2.1.15", "description": "simple form component", "author": "mezhanglei <496623925@qq.com>", "homepage": "https://github.com/mezhanglei/simpleform#readme", diff --git a/packages/form/src/components/Item/index.tsx b/packages/form/src/components/Item/index.tsx index e6f6959..113b956 100644 --- a/packages/form/src/components/Item/index.tsx +++ b/packages/form/src/components/Item/index.tsx @@ -4,7 +4,7 @@ import './index.less'; import pickAttrs from '../../utils/pickAttrs'; import { isEmpty } from '../../utils/type'; import SvgIcon from '../SvgIcon'; -import Tooltip from '../Tooltip'; +import { Tooltip } from 'react-tooltip'; export type Layout = 'horizontal' | 'vertical'; export interface ItemProps { diff --git a/packages/form/src/components/Tooltip/index.less b/packages/form/src/components/Tooltip/index.less deleted file mode 100644 index 57d3592..0000000 --- a/packages/form/src/components/Tooltip/index.less +++ /dev/null @@ -1,12 +0,0 @@ -.custom-tooltip.react-tooltip { - background: #fff; - color: rgba(0, 0, 0, .85); - background-clip: padding-box; - border-radius: 2px; - box-shadow: 0 2px 8px rgb(0 0 0 / 15%); - z-index: 999; - - .react-tooltip-arrow { - background: #fff; - } -} \ No newline at end of file diff --git a/packages/form/src/components/Tooltip/index.tsx b/packages/form/src/components/Tooltip/index.tsx deleted file mode 100644 index 4271d71..0000000 --- a/packages/form/src/components/Tooltip/index.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import './index.less'; -import { Tooltip } from 'react-tooltip'; - -interface TooltipCustomProps extends React.HtmlHTMLAttributes { - content: string; -} - -export default React.forwardRef((props, ref) => { - const { - children, - content, - className, - ...rest - } = props; - - return ( - <> - {children} - - - ); -}); diff --git a/packages/form/src/item-core.tsx b/packages/form/src/core.tsx similarity index 95% rename from packages/form/src/item-core.tsx rename to packages/form/src/core.tsx index 35a0f0c..ec937bc 100644 --- a/packages/form/src/item-core.tsx +++ b/packages/form/src/core.tsx @@ -76,7 +76,7 @@ export const ItemCore = (props: ItemCoreProps) => { const currentPath = (isEmpty(name) || nonform === true) ? undefined : name; const [value, setValue] = useState(); // 初始化fieldProps - currentPath && form?.setFieldProps(currentPath, fieldProps); + form?.setFieldProps(currentPath, fieldProps); // 订阅更新值的函数 useEffect(() => { @@ -100,10 +100,11 @@ export const ItemCore = (props: ItemCoreProps) => { form.setInitialValue(currentPath, initValue); onFieldsMounted && onFieldsMounted({ name: currentPath, value: initValue }, form?.getFieldValue()); return () => { + if (!currentPath) return; // 清除该表单域的props(在设置值的前面) - currentPath && form?.setFieldProps(currentPath, undefined); + form?.setFieldProps(currentPath, undefined); // 清除初始值 - currentPath && form.setInitialValue(currentPath, undefined); + form?.setInitialValue(currentPath, undefined); }; }, [JSON.stringify(currentPath)]); diff --git a/packages/form/src/form-item.tsx b/packages/form/src/form-item.tsx index a676d8c..957d6d8 100644 --- a/packages/form/src/form-item.tsx +++ b/packages/form/src/form-item.tsx @@ -2,7 +2,7 @@ import React, { useContext, CSSProperties, useMemo } from 'react'; import { SimpleFormContext } from './context'; import { useFormError } from './hooks'; import { Item, ItemProps } from './components/Item'; -import { ItemCore, ItemCoreProps } from './item-core'; +import { ItemCore, ItemCoreProps } from './core'; export type FormItemOptions

= Omit & ItemCoreProps & { component?: React.ComponentType | React.ForwardRefExoticComponent | null; diff --git a/packages/form/src/form.tsx b/packages/form/src/form.tsx index 4df0281..781634b 100644 --- a/packages/form/src/form.tsx +++ b/packages/form/src/form.tsx @@ -31,20 +31,24 @@ export function Form(props: FormProps) { useEffect(() => { if (!form || !watch) return; - Object.entries(watch)?.forEach(([key, watcher]) => { - // 函数形式 - if (typeof watcher === 'function') { - form?.subscribeFormValue(key, watcher); - // 对象形式 - } else if (isObject(watcher)) { - if (typeof watcher.handler === 'function') { - form?.subscribeFormValue(key, watcher.handler); + if (typeof watch === 'function') { + form?.subscribeFormValue(watch); + } else { + Object.entries(watch)?.forEach(([key, watcher]) => { + // 函数形式 + if (typeof watcher === 'function') { + form?.subscribeFormValue(key, watcher); + // 对象形式 + } else if (isObject(watcher)) { + if (typeof watcher.handler === 'function') { + form?.subscribeFormValue(key, watcher.handler); + } + if (watcher.immediate) { + watcher.handler(form?.getFieldValue(key), form?.getLastValue(key)); + } } - if (watcher.immediate) { - watcher.handler(form?.getFieldValue(key), form?.getLastValue(key)); - } - } - }); + }); + } return () => { form?.unsubscribeFormValue(); }; diff --git a/packages/form/src/index.ts b/packages/form/src/index.ts index 5b68827..a793f2e 100644 --- a/packages/form/src/index.ts +++ b/packages/form/src/index.ts @@ -1,10 +1,9 @@ -export * from './item-core'; +export * from './core'; export * from './form'; export * from './form-item'; export * from './store'; export * from './context'; export * from './hooks'; -export * from './components/Tooltip'; export * from './components/Item'; export * from './utils/utils'; export * from './validator'; diff --git a/packages/form/src/store.ts b/packages/form/src/store.ts index 379e4ea..d5875e7 100644 --- a/packages/form/src/store.ts +++ b/packages/form/src/store.ts @@ -1,7 +1,7 @@ import { deepGet, deepSet, getValuePropName, comparePrefix } from './utils/utils'; import { deepClone } from './utils/object'; import { handleRules, isCanTrigger } from './validator'; -import { getRulesTriggers, getTriggers } from './item-core'; +import { getRulesTriggers, getTriggers } from './core'; import { isEmpty, isObject } from './utils/type'; import { FormItemProps } from './form-item'; import { PathValue } from './typings'; @@ -104,16 +104,12 @@ export class SimpleForm { getBindProps(path?: string, newValue?: V) { if (!path) return; const props = this.getFieldProps(path); - const currentValue = this.getFieldValue(path); + const currentValue = newValue !== undefined ? newValue : this.getFieldValue(path); const { valueProp, valueSetter, trigger, validateTrigger, rules, nonform } = props || {}; const valuePropName = getValuePropName(valueProp) || ""; const triggers = getTriggers(trigger, validateTrigger, getRulesTriggers(rules)); const childValue = typeof valueSetter === 'function' ? valueSetter(currentValue) : (valueSetter ? undefined : currentValue); const bindProps = { [valuePropName]: childValue } as Record; - if (newValue !== undefined) { - const newChildValue = typeof valueSetter === 'function' ? valueSetter(newValue) : (valueSetter ? undefined : newValue); - bindProps[valuePropName] = newChildValue; - } triggers.forEach((eventName) => { bindProps[eventName] = (...args) => { this.bindChange(path, eventName, ...args); @@ -123,7 +119,7 @@ export class SimpleForm { } // 设置表单域 - public setFieldProps(path: string, field?: V) { + public setFieldProps(path?: string, field?: V) { if (!path) return; const lastField = this.fieldPropsMap[path]; if (field === undefined) { diff --git a/packages/form/src/utils/type.ts b/packages/form/src/utils/type.ts index 99a1643..8902b1f 100644 --- a/packages/form/src/utils/type.ts +++ b/packages/form/src/utils/type.ts @@ -5,51 +5,51 @@ export function getType(obj: T) { return Object.prototype.toString.call(obj); } -export function isBoolean(data: T) { +export function isBoolean(data: T | boolean): data is boolean { return getType(data) == '[object Boolean]'; } -export function isNumber(data: T) { +export function isNumber(data: T | number): data is number { return getType(data) == '[object Number]'; } -export function isString(data: T) { +export function isString(data: T | string): data is string { return getType(data) == '[object String]'; } -export function isFunction(data: T) { +export function isFunction(data: T | Function): data is Function { return getType(data) == '[object Function]'; } -export function isArray(data: T) { +export function isArray(data: T | unknown[]): data is unknown[] { return getType(data) == '[object Array]'; } -export function isDate(data: T) { +export function isDate(data: T | Date): data is Date { return data instanceof Date; } -export function isRegExp(data: T) { +export function isRegExp(data: T | RegExp): data is RegExp { return getType(data) == '[object RegExp]'; } -export function isUndefined(data: T) { +export function isUndefined(data: T | undefined): data is undefined { return getType(data) == '[object Undefined]'; } -export function isNull(data: T) { +export function isNull(data: T | null): data is null { return getType(data) == '[object Null]'; } -export function isObject(data: T) { +export function isObject(data: T | object): data is object { return getType(data) == '[object Object]'; } -export function isElement(data: T) { +export function isElement(data: T | Element): data is Element { return data instanceof Element; } -export function isDom(data: T & { nodeType?: number; nodeName?: string }) { +export function isDom(data: T & { nodeType?: number; nodeName?: string } | HTMLElement): data is HTMLElement { if (typeof HTMLElement === 'object') { return data instanceof HTMLElement; } else { @@ -57,12 +57,13 @@ export function isDom(data: T & { nodeType?: number; nodeName?: string }) { } } -export function isNodeList(data: T) { +export function isNodeList(data: T | NodeList): data is NodeList { return getType(data) == '[object NodeList]'; } // 判断值是否为空 -export function isEmpty(value: T) { +type EmptyType = undefined | null | '' | [] | {}; +export function isEmpty(value: T | EmptyType): value is EmptyType { if (value === undefined || value === null) return true; if (Array.isArray(value) || typeof value === 'string' @@ -82,13 +83,14 @@ export function isEmpty(value: T) { if (typeof value === 'number') { return isNaN(value); } + return false; } -export function isArrayBuffer(data: T) { +export function isArrayBuffer(data: T | ArrayBuffer): data is ArrayBuffer { return getType(data) === '[object ArrayBuffer]'; } -export function isFormData(data: T) { +export function isFormData(data: T | FormData): data is FormData { return (typeof FormData !== 'undefined') && (data instanceof FormData); } @@ -102,11 +104,11 @@ export function isArrayBufferView(data: T & { buffer?: ArrayBuffer }) { return result; } -export function isFile(data: T) { +export function isFile(data: T | File): data is File { return getType(data) === '[object File]'; } -export function isBlob(data: T) { +export function isBlob(data: T | Blob): data is Blob { return getType(data) === '[object Blob]'; } @@ -115,24 +117,27 @@ export function isStream(data: T & { pipe?: Function }) { } // 是否为简单类型 -export function isBase(data: T) { +type BaseTypes = string | number | symbol | boolean; +export function isBase(data: T | BaseTypes): data is BaseTypes { const type = typeof data; return ['string', 'number', 'symbol', 'boolean']?.includes(type); } // 是否为数字字符串或者数字 -export const isNumberStr = (str?: T) => { +type NumberOrStr = string | number; +export const isNumberStr = (str?: T | NumberOrStr): str is NumberOrStr => { if (typeof str === 'number' && !isNaN(str)) return true; if (typeof str === 'string') { const target = Number(str); if (!isNaN(target) && str) return true; } + return false; }; // 是否为带data:开头的base64的字符串 -export const isBase64 = (str?: T) => { - if (typeof str !== 'string') return; - if (str.trim() === '') return; +export const isBase64 = (str?: T | string): str is string => { + if (typeof str !== 'string') return false; + if (str.trim() === '') return false; const regx = /^(data:\S+\/\S+;base64,){1}/; return regx.test(str); }; diff --git a/packages/form/src/utils/utils.ts b/packages/form/src/utils/utils.ts index 5fdd741..c001dd0 100644 --- a/packages/form/src/utils/utils.ts +++ b/packages/form/src/utils/utils.ts @@ -1,5 +1,5 @@ import { PathValue } from "../typings"; -import { ItemCoreProps } from "../item-core"; +import { ItemCoreProps } from "../core"; import { isArray, isEmpty, isObject } from "./type"; import { deepClone } from "./object"; @@ -9,7 +9,7 @@ export function pathToArr(path?: FormPathType) { if (path instanceof Array) return path; if (typeof path === 'number') return [path]; const regex = /([^\.\[\]]+)|(\[\d+\])/g; - const parts = typeof path === 'string' ? path.match(regex) : []; + const parts = typeof path === 'string' ? path.match(regex) as string[] : []; return parts || []; } @@ -96,7 +96,7 @@ export function getValueFromEvent(...args) { } // 是否携带中括号 -export const isWithBracket = (code?: unknown) => { +export const isWithBracket = (code?: T) => { return typeof code === 'string' && /^\[\d+\]$/.test(code); }; diff --git a/packages/form/src/validator/index.ts b/packages/form/src/validator/index.ts index 604d405..2d40520 100644 --- a/packages/form/src/validator/index.ts +++ b/packages/form/src/validator/index.ts @@ -1,4 +1,4 @@ -import { ItemCoreProps } from "../item-core"; +import { ItemCoreProps } from "../core"; import { configValidator, ConfigValidatorKeys } from "./rules"; export type FormRule = { required?: boolean; diff --git a/packages/form/src/validator/rules.ts b/packages/form/src/validator/rules.ts index 2da6513..554e792 100644 --- a/packages/form/src/validator/rules.ts +++ b/packages/form/src/validator/rules.ts @@ -52,11 +52,11 @@ const min = (ruleValue: T, value?: unknown) => { // 导出校验方法 export const configValidator = { - 'required': required, - 'pattern': pattern, - 'whitespace': whitespace, - 'max': max, - 'min': min + required, + pattern, + whitespace, + max, + min }; export type ConfigValidator = typeof configValidator; diff --git a/packages/render/README.md b/packages/render/README.md index 8a00eb2..fa289fb 100644 --- a/packages/render/README.md +++ b/packages/render/README.md @@ -2,7 +2,7 @@ English | [中文说明](./README_CN.md) -[![](https://img.shields.io/badge/version-4.1.26-green)](https://www.npmjs.com/package/@simpleform/render) +[![](https://img.shields.io/badge/version-4.1.27-green)](https://www.npmjs.com/package/@simpleform/render) > A lightweight dynamic forms engine that makes it easy to dynamically render forms. diff --git a/packages/render/README_CN.md b/packages/render/README_CN.md index 3e079c9..7a1365d 100644 --- a/packages/render/README_CN.md +++ b/packages/render/README_CN.md @@ -2,7 +2,7 @@ [English](./README.md) | 中文说明 -[![](https://img.shields.io/badge/version-4.1.26-green)](https://www.npmjs.com/package/@simpleform/render) +[![](https://img.shields.io/badge/version-4.1.27-green)](https://www.npmjs.com/package/@simpleform/render) > 轻量级动态表单引擎,实现动态渲染表单很简单. diff --git a/packages/render/package.json b/packages/render/package.json index f4b1b99..342d0ca 100644 --- a/packages/render/package.json +++ b/packages/render/package.json @@ -1,6 +1,6 @@ { "name": "@simpleform/render", - "version": "4.1.26", + "version": "4.1.27", "description": "dynamic rendering core of simple form", "author": "mezhanglei <496623925@qq.com>", "homepage": "https://github.com/mezhanglei/simpleform#readme", @@ -45,7 +45,7 @@ "react-router-dom": "^6.19.0" }, "dependencies": { - "@simpleform/form": "^2.1.14", + "@simpleform/form": "^2.1.15", "classnames": "^2.3.2", "copy-anything": "^3.0.5", "serialize-javascript": "^6.0.2" diff --git a/packages/render/src/children.tsx b/packages/render/src/children.tsx index c860e9f..d76cef6 100644 --- a/packages/render/src/children.tsx +++ b/packages/render/src/children.tsx @@ -18,6 +18,7 @@ export default function FormChildren(props: FormChildrenProps) { variables, onRenderChange, renderList, + components = {}, widgetList: propWidgetList, parser = parseExpression, form = formContext?.form, @@ -29,6 +30,7 @@ export default function FormChildren(props: FormChildrenProps) { parser, formrender, form, + components, variables: curVariables, }); diff --git a/packages/render/src/utils/ReactIs.ts b/packages/render/src/utils/ReactIs.ts deleted file mode 100644 index 94bd929..0000000 --- a/packages/render/src/utils/ReactIs.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { isValidElementType } from "react-is"; -import { isEmpty } from "./type"; - -// 是否为组件(不包括html标签字符串) -export function isValidComponent(component?: unknown) { - if (isEmpty(component) || typeof component === 'string') return false; - return isValidElementType(component); -}; diff --git a/packages/render/src/utils/framework.ts b/packages/render/src/utils/framework.ts new file mode 100644 index 0000000..5cfd285 --- /dev/null +++ b/packages/render/src/utils/framework.ts @@ -0,0 +1,21 @@ +import React from "react"; +import { isValidElementType } from "react-is"; +import { isEmpty } from "./type"; + +// 是否为组件(不包括html标签字符串) +export function isValidComponent(component?: unknown) { + if (isEmpty(component) || typeof component === 'string') return false; + return isValidElementType(component); +}; + +export const isValidElement = (val?: T | React.ReactElement): val is React.ReactElement => { + return React.isValidElement(val); +}; + +export const createElement = (...args: Parameters): ReturnType => { + return React.createElement(...args); +}; + +export const cloneElement = (...args: Parameters): ReturnType => { + return React.cloneElement(...args); +}; diff --git a/packages/render/src/utils/index.ts b/packages/render/src/utils/index.ts index f591494..c67fd9a 100644 --- a/packages/render/src/utils/index.ts +++ b/packages/render/src/utils/index.ts @@ -1,4 +1,4 @@ -export * from './ReactIs'; +export * from './framework'; export * from './array'; export * from './transform'; export * from './object'; diff --git a/packages/render/src/utils/transform.ts b/packages/render/src/utils/transform.ts index 57b8376..dda2f48 100644 --- a/packages/render/src/utils/transform.ts +++ b/packages/render/src/utils/transform.ts @@ -1,7 +1,7 @@ import { deepSet, Form, joinFormPath } from '@simpleform/form'; import React from 'react'; import { CustomRenderType, CustomUnionType, FormRenderProps, GenerateWidgetItem, ReactComponent, RegisteredComponents, WidgetContextProps, WidgetItem, WidgetList } from "../typings"; -import { isValidComponent } from "./ReactIs"; +import { cloneElement, createElement, isValidComponent, isValidElement } from "./framework"; import { isEmpty, isObject } from './type'; import serialize from 'serialize-javascript'; @@ -15,7 +15,7 @@ export const mergeFormOptions = ( Object.keys(newConfig || {}).forEach((key) => { const oldItem = oldConfig?.[key]; const newItem = newConfig?.[key]; - if (isObject(oldItem) && isObject(newItem) && !React.isValidElement(oldItem)) { + if (isObject(oldItem) && isObject(newItem) && !isValidElement(oldItem)) { cloneConfig[key] = { ...oldItem, ...newItem }; } else if (typeof oldItem === 'function' && mergeFunNames.includes(key)) { cloneConfig[key] = (...args: unknown[]) => { @@ -71,7 +71,7 @@ export const parseExpression = (value: V, variables?: object) => { // 递归遍历转换嵌套对象的子属性 export const traverseMapObject = (val, callback) => { - const isElement = React.isValidElement(val); + const isElement = isValidElement(val); if (Array.isArray(val)) { return val.map((item) => traverseMapObject(item, callback)); } else if (isObject(val) && !isElement) { @@ -101,7 +101,7 @@ export const traverseWidgetList = ( // 递归解析对象或数组中的每个属性 export const evalAttr = (val: V, variables?: object, parser?: FormRenderProps['parser']) => { - const isElement = React.isValidElement(val); + const isElement = isValidElement(val); if (isElement || typeof parser !== 'function') return val; return traverseMapObject(val, (target) => { const generateItem = parser(target, variables); @@ -126,7 +126,7 @@ export const getFormComponent = (target?: any, widgets?: RegisteredComponents) = if (target === undefined || target === null) { return target; } - if (typeof target === 'string' || typeof target === 'number' || React.isValidElement(target as React.ReactNode)) { + if (typeof target === 'string' || typeof target === 'number' || isValidElement(target as React.ReactNode)) { return; } if (isValidComponent(target)) { @@ -149,23 +149,23 @@ export const createFormElement = (target?: CustomUnionType, props?: unknown, wid return target; } // 判断是否为ReactElment - if (React.isValidElement(target)) { - return React.cloneElement(target, props as React.Attributes); + if (isValidElement(target)) { + return cloneElement(target, props as React.Attributes); } // 判断是否为声明组件 const widgetItem = target as GenerateWidgetItem; const Com = getFormComponent(widgetItem, widgets); if (Com) { const mergeProps = Object.assign({}, props, widgetItem?.props) as React.Attributes; - return React.createElement(Com, mergeProps); + return createElement(Com, mergeProps); } }; // 目标嵌套其他组件 export const withSide = (target?: React.ReactNode, customRender?: CustomRenderType, side?: React.ReactNode, context?: WidgetContextProps) => { const childs = typeof customRender === 'function' ? customRender(target, context) : target; - const childsWithSide = React.isValidElement(side) ? React.cloneElement(side, {}, childs) : childs; - const cloneChilds = React.isValidElement(childsWithSide) ? React.cloneElement(childsWithSide, { key: context?._options?.path }) : childsWithSide; + const childsWithSide = isValidElement(side) ? cloneElement(side, {}, childs) : childs; + const cloneChilds = isValidElement(childsWithSide) ? cloneElement(childsWithSide, { key: context?._options?.path }) : childsWithSide; return cloneChilds; }; @@ -179,12 +179,12 @@ export const renderWidgetItem = ( return target; } // 判断是否为ReactElment - if (React.isValidElement(target)) { - return React.cloneElement(target, { _options: baseOptions } as React.Attributes); + if (isValidElement(target)) { + return cloneElement(target, { _options: baseOptions } as React.Attributes); } // 判断是否为ReactComponent if (isValidComponent(target)) { - return React.createElement(target as ReactComponent, { _options: baseOptions } as React.Attributes); + return createElement(target as ReactComponent, { _options: baseOptions } as React.Attributes); } const defineConfig = formrender?.config; const generateWidgetItem = evalAttr( @@ -223,22 +223,22 @@ export const renderWidgetItem = ( const typeChildren = children instanceof Array ? renderWidgetList(formrender, children, { ...baseOptions, path: joinFormPath(mergeItem?.path, 'children') }) : formrender.createFormElement(children, childContext); - const curNode = React.isValidElement(typeWidget) && !isEmpty(typeChildren) - ? React.cloneElement( + const curNode = isValidElement(typeWidget) && !isEmpty(typeChildren) + ? cloneElement( typeWidget, {}, withSide(typeChildren, defineConfig?.renderList, insideEle, childContext) ) : (isEmpty(typeWidget) ? typeChildren : typeWidget); const result = isFormWidget ? - React.createElement(Form.Item, { + createElement(Form.Item, { ...restItem, footer: formrender.createFormElement(restItem?.footer, childContext), suffix: formrender.createFormElement(restItem?.suffix, childContext), component: formrender.getFormComponent(restItem?.component), - }, - (({ bindProps }) => React.isValidElement(curNode) ? - React.cloneElement(curNode, { + } as React.Attributes, + (({ bindProps }) => isValidElement(curNode) ? + cloneElement(curNode, { ...bindProps, [triggerName]: (...args) => { (curNode?.props as GenerateWidgetItem)?.[triggerName]?.(...args); diff --git a/packages/render/src/utils/type.ts b/packages/render/src/utils/type.ts index 99a1643..8902b1f 100644 --- a/packages/render/src/utils/type.ts +++ b/packages/render/src/utils/type.ts @@ -5,51 +5,51 @@ export function getType(obj: T) { return Object.prototype.toString.call(obj); } -export function isBoolean(data: T) { +export function isBoolean(data: T | boolean): data is boolean { return getType(data) == '[object Boolean]'; } -export function isNumber(data: T) { +export function isNumber(data: T | number): data is number { return getType(data) == '[object Number]'; } -export function isString(data: T) { +export function isString(data: T | string): data is string { return getType(data) == '[object String]'; } -export function isFunction(data: T) { +export function isFunction(data: T | Function): data is Function { return getType(data) == '[object Function]'; } -export function isArray(data: T) { +export function isArray(data: T | unknown[]): data is unknown[] { return getType(data) == '[object Array]'; } -export function isDate(data: T) { +export function isDate(data: T | Date): data is Date { return data instanceof Date; } -export function isRegExp(data: T) { +export function isRegExp(data: T | RegExp): data is RegExp { return getType(data) == '[object RegExp]'; } -export function isUndefined(data: T) { +export function isUndefined(data: T | undefined): data is undefined { return getType(data) == '[object Undefined]'; } -export function isNull(data: T) { +export function isNull(data: T | null): data is null { return getType(data) == '[object Null]'; } -export function isObject(data: T) { +export function isObject(data: T | object): data is object { return getType(data) == '[object Object]'; } -export function isElement(data: T) { +export function isElement(data: T | Element): data is Element { return data instanceof Element; } -export function isDom(data: T & { nodeType?: number; nodeName?: string }) { +export function isDom(data: T & { nodeType?: number; nodeName?: string } | HTMLElement): data is HTMLElement { if (typeof HTMLElement === 'object') { return data instanceof HTMLElement; } else { @@ -57,12 +57,13 @@ export function isDom(data: T & { nodeType?: number; nodeName?: string }) { } } -export function isNodeList(data: T) { +export function isNodeList(data: T | NodeList): data is NodeList { return getType(data) == '[object NodeList]'; } // 判断值是否为空 -export function isEmpty(value: T) { +type EmptyType = undefined | null | '' | [] | {}; +export function isEmpty(value: T | EmptyType): value is EmptyType { if (value === undefined || value === null) return true; if (Array.isArray(value) || typeof value === 'string' @@ -82,13 +83,14 @@ export function isEmpty(value: T) { if (typeof value === 'number') { return isNaN(value); } + return false; } -export function isArrayBuffer(data: T) { +export function isArrayBuffer(data: T | ArrayBuffer): data is ArrayBuffer { return getType(data) === '[object ArrayBuffer]'; } -export function isFormData(data: T) { +export function isFormData(data: T | FormData): data is FormData { return (typeof FormData !== 'undefined') && (data instanceof FormData); } @@ -102,11 +104,11 @@ export function isArrayBufferView(data: T & { buffer?: ArrayBuffer }) { return result; } -export function isFile(data: T) { +export function isFile(data: T | File): data is File { return getType(data) === '[object File]'; } -export function isBlob(data: T) { +export function isBlob(data: T | Blob): data is Blob { return getType(data) === '[object Blob]'; } @@ -115,24 +117,27 @@ export function isStream(data: T & { pipe?: Function }) { } // 是否为简单类型 -export function isBase(data: T) { +type BaseTypes = string | number | symbol | boolean; +export function isBase(data: T | BaseTypes): data is BaseTypes { const type = typeof data; return ['string', 'number', 'symbol', 'boolean']?.includes(type); } // 是否为数字字符串或者数字 -export const isNumberStr = (str?: T) => { +type NumberOrStr = string | number; +export const isNumberStr = (str?: T | NumberOrStr): str is NumberOrStr => { if (typeof str === 'number' && !isNaN(str)) return true; if (typeof str === 'string') { const target = Number(str); if (!isNaN(target) && str) return true; } + return false; }; // 是否为带data:开头的base64的字符串 -export const isBase64 = (str?: T) => { - if (typeof str !== 'string') return; - if (str.trim() === '') return; +export const isBase64 = (str?: T | string): str is string => { + if (typeof str !== 'string') return false; + if (str.trim() === '') return false; const regx = /^(data:\S+\/\S+;base64,){1}/; return regx.test(str); }; diff --git a/website/docs/editor/README.md b/website/docs/editor/README.md index 8610a2e..c6c7fbc 100644 --- a/website/docs/editor/README.md +++ b/website/docs/editor/README.md @@ -7,7 +7,7 @@ nav: --- # @simpleform/editor -[![](https://img.shields.io/badge/version-4.1.13-green)](https://www.npmjs.com/package/@simpleform/editor) +[![](https://img.shields.io/badge/version-4.2.0-green)](https://www.npmjs.com/package/@simpleform/editor) > 基于`@simpleform/render`实现的表单设计器,支持自定义组件,模板导入导出,可视化设计等表单设计功能,二次开发非常简单。 @@ -26,6 +26,10 @@ nav: * 注册组件:通过自定义一个组件,在设计器和渲染器中注册,然后就可以在组件面板中配置该组件使用 * 导入模板:可渲染成界面的`JSON`列表,列表中的每一项均为注册组件,此功能未内置,可在`EditorTools`组件中扩展一个导入入口 +:::warning +推荐使用版本`>=4.2`版本 +::: + ## 安装 ### npm安装 @@ -44,27 +48,78 @@ yarn add @simpleform/editor import '@simpleform/editor/lib/css/main.css'; ``` ### 基本使用 -概述:1.配置渲染器`FormRender`的`components`用于注册组件 → 2.然后在编辑器`FormEditor`的`editorConfig`配置各个组件的信息 → 3.最后在编辑器的`EditPanel`展示列表中显示即可使用, 具体代码请在项目内的`packages/editor/example/demo`路径下查看 +概述:1.配置渲染器`FormRender`的配置项文件 → 2.配置编辑器`FormEditor`的`editorConfig`属性,并且引入`FormRender`的配置项作为`renderConfig` → 3.最后在编辑器的`EditPanel`展示列表中显示即可使用, 具体代码请在项目内的`packages/editor/example/demo`路径下查看 ## FormRender配置项 -### 请求配置 -编辑器内置了`axios`,使用编辑器前需要根据你当前的项目进行个性化配置,参考示例: +### 配置文件 +`FormRender`组件的配置项可以单独写在一个文件里面导出. ```javascript -import { createRequest } from '@simpleform/editor'; -... +// defineConfig.js +import widgets from '../components'; +import dayjs from 'dayjs'; +import { createRequest } from '../FormRender'; +// TODO axios请求配置,编辑器内置了`axios`,使用编辑器前需要根据你当前的项目进行个性化配置 const axiosConfig = { - // baseURL: '', - // headers: {}, - // // 处理响应结果 - // handleResult: (data) => { +// baseURL: '', +// headers: {}, +// // 处理响应结果 +// handleResult: (data) => { +// } +}; - // } +// 配置项 +export default { + // 组件内的变量 + variables: { + dayjs, + request: createRequest(axiosConfig) + }, + // 注册组件 + components: widgets, + // 节点属性默认配置 + options: { + props: { autoComplete: 'off' } + } +}; +``` + +### 引入配置文件 +编辑器内置了`axios`,使用编辑器前需要根据你当前的项目进行个性化配置,参考示例: +```javascript +import React from 'react'; +import DefaultFormRender, { FormChildren as DefaultFormChildren } from '@simpleform/render'; +import { CustomFormChildrenProps, CustomFormRenderProps } from '@simpleform/editor'; +import '@simpleform/editor/lib/css/main.css'; +import defineConfig from './defineConfig'; + +export * from '@simpleform/editor'; + +export function FormChildren(props: CustomFormChildrenProps) { + const { components, variables, ...rest } = props; + return ( + + ); } - +export default function FormRender(props: CustomFormRenderProps) { + const { components, variables, ...rest } = props; + return ( + + ); +} ``` ### 数据源配置 @@ -84,6 +139,10 @@ const components = { ## FormEditor配置项 +### 引入FormRender的defineConfig +```javascript +import renderConfig from './FormRender/defineConfig'; +``` ### 上传组件 默认内置了上传组件,但是需要处理响应数据.有两种方式解决: - 界面上的上传组件有`uploadCallback`(接口响应数据)配置选项,修改即可. @@ -109,8 +168,8 @@ const editorConfig = { } ``` -### editorConfig配置说明 -示例配置`editorConfig`中的`input`控件信息: +### 引入editorConfig配置 +- 示例配置`editorConfig`中的`input`控件信息: ```javascript import { FieldSetting } from '@simpleform/editor'; @@ -136,6 +195,29 @@ const EditorConfig = { ``` +- 默认内置了上传组件,但是需要处理响应数据.有两种方式解决: +1. 界面上的上传组件有`uploadCallback`(接口响应数据)配置选项,修改即可. +2. 在`editorConfig`中时重新配置上传组件的`props`属性 +```javascript +// 在配置editorConfig时重新配置上传图片和上传文件的uploadCallback + +import { EditorConfig as oldEditorConfig } from '@simpleform/editor' + +const editorConfig = { + "FileUpload": { + ...oldEditorConfig['FileUpload'], + props: { + uploadCallback: "{{(data) => ({ fileId: data.fileId })}}" + }, + }, + "ImageUpload": { + ...oldEditorConfig['ImageUpload'], + props: { + uploadCallback: "{{(data) => ({ fileId: data.fileId })}}" + }, + }, +} +``` ### EditPanel组件配置 默认的`EditPanel`组件通过`panelData`来配置显示列表. 参考示例: diff --git a/website/docs/editor/usage.md b/website/docs/editor/usage.md index 7614f05..35825b1 100644 --- a/website/docs/editor/usage.md +++ b/website/docs/editor/usage.md @@ -7,4 +7,4 @@ order: 1 ## 自定义编辑器的UI样式 编辑器内置了全面的上下文信息和方法,可以自定义任意的UI排列布局或者样式, 以下拿`EditorPanel`完全自定义样式举例.其他组件也是类比一样的. - + diff --git a/website/docs/form/README.md b/website/docs/form/README.md index 50f1772..88f4267 100644 --- a/website/docs/form/README.md +++ b/website/docs/form/README.md @@ -7,7 +7,7 @@ nav: --- # @simpleform/form -[![](https://img.shields.io/badge/version-2.1.14-green)](https://www.npmjs.com/package/@simpleform/form) +[![](https://img.shields.io/badge/version-2.1.15-green)](https://www.npmjs.com/package/@simpleform/form) > 表单底层组件,通过回调函数方式实现表单值的显示和更新事件的绑定. diff --git a/website/docs/render/README.md b/website/docs/render/README.md index 04757eb..f257b24 100644 --- a/website/docs/render/README.md +++ b/website/docs/render/README.md @@ -7,7 +7,7 @@ nav: --- # @simpleform/render -[![](https://img.shields.io/badge/version-4.1.26-green)](https://www.npmjs.com/package/@simpleform/render) +[![](https://img.shields.io/badge/version-4.1.27-green)](https://www.npmjs.com/package/@simpleform/render) > 基于`@simpleform/form`实现的轻量级动态表单引擎,实现动态渲染表单很简单. @@ -169,7 +169,7 @@ const widgetList = [{ - `onRenderChange`: `(newValue: WidgetList) => void;` `widgetList`更改时回调函数 - `formrender`: `FormRender`通过`useSimpleFormRender()`创建的实例,负责表单界面渲染,选填. - `form`: `Form`。通过`useSimpleForm()`创建,负责表单值的管理,选填. -- `parser`: `(value?: unknown, variables?: object) => V` 字符串表达式解析函数,默认方法为`parseExpression`, 传`null`则表示不解析表达式. +- `parser`: `(node?: unknown, variables?: object) => V` 字符串表达式解析函数,默认方法为`parseExpression`, 传`null`则表示不解析表达式. :::warning `>=4.1.25`新增`parser`和`wrapper`, 并且移除`uneval`. ::: diff --git a/website/src/editor/CustomFormEditor/index.less b/website/src/editor/CustomEditor/index.less similarity index 100% rename from website/src/editor/CustomFormEditor/index.less rename to website/src/editor/CustomEditor/index.less diff --git a/website/src/editor/CustomFormEditor/index.tsx b/website/src/editor/CustomEditor/index.tsx similarity index 85% rename from website/src/editor/CustomFormEditor/index.tsx rename to website/src/editor/CustomEditor/index.tsx index 19ee3a4..28706f2 100644 --- a/website/src/editor/CustomFormEditor/index.tsx +++ b/website/src/editor/CustomEditor/index.tsx @@ -12,9 +12,10 @@ import { insertWidgetItem, defaultGetId, } from '@simpleform/editor'; -import FormRender from '../FormRender'; import './index.less'; import { Tag } from 'antd'; +import editorConfig from '../FormEditor/editorConfig'; +import renderConfig from '../FormEditor/FormRender/defineConfig'; const panelData = { '基础控件': [ @@ -38,8 +39,8 @@ const panelData = { }; // 自定义EditorPanel -const CustomEditorPanel = (context) => { - const { selected, editor, editorConfig, historyRecord } = context?.state; +const CustomEditorPanel = (editorContext?: any) => { + const { selected, editor, editorConfig, historyRecord } = editorContext?.state; const onChange = (key: string) => { const newIndex = getListIndex(editor, selected?.path) + 1; // 插入位置序号 @@ -80,11 +81,12 @@ const CustomEditorPanel = (context) => { ) } -const CustomFormEditor = () => { +const CustomEditor = () => { return ( {CustomEditorPanel} @@ -98,4 +100,4 @@ const CustomFormEditor = () => { ); }; -export default CustomFormEditor; +export default CustomEditor; diff --git a/website/src/editor/FormEditor/FormRender/defineConfig.ts b/website/src/editor/FormEditor/FormRender/defineConfig.ts new file mode 100644 index 0000000..991c9ba --- /dev/null +++ b/website/src/editor/FormEditor/FormRender/defineConfig.ts @@ -0,0 +1,23 @@ +import widgets from '../components'; +import dayjs from 'dayjs'; +import { createRequest } from '@simpleform/editor'; + +// TODO axios请求配置 +const axiosConfig = { + +}; + +// 渲染引擎配置项 +export default { + // 组件内的变量 + variables: { + dayjs, + request: createRequest(axiosConfig) + }, + // 注册组件 + components: widgets, + // 节点属性默认配置 + options: { + props: { autoComplete: 'off' } + } +}; diff --git a/website/src/editor/FormEditor/FormRender/index.tsx b/website/src/editor/FormEditor/FormRender/index.tsx new file mode 100644 index 0000000..4a91cda --- /dev/null +++ b/website/src/editor/FormEditor/FormRender/index.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import DefaultFormRender, { FormChildren as DefaultFormChildren } from '@simpleform/render'; +import { CustomFormChildrenProps, CustomFormRenderProps } from '@simpleform/editor'; +import '@simpleform/editor/lib/css/main.css'; +import defineConfig from './defineConfig'; + +export * from '@simpleform/render'; + +export function FormChildren(props: CustomFormChildrenProps) { + const { components, variables, ...rest } = props; + return ( + + ); +} + +export default function FormRender(props: CustomFormRenderProps) { + const { components, variables, ...rest } = props; + return ( + + ); +} diff --git a/website/src/editor/FormEditor/template/data/demo.ts b/website/src/editor/FormEditor/ImportTemplate/demo.ts similarity index 100% rename from website/src/editor/FormEditor/template/data/demo.ts rename to website/src/editor/FormEditor/ImportTemplate/demo.ts diff --git a/website/src/editor/FormEditor/template/index.less b/website/src/editor/FormEditor/ImportTemplate/index.less similarity index 100% rename from website/src/editor/FormEditor/template/index.less rename to website/src/editor/FormEditor/ImportTemplate/index.less diff --git a/website/src/editor/FormEditor/template/index.tsx b/website/src/editor/FormEditor/ImportTemplate/index.tsx similarity index 57% rename from website/src/editor/FormEditor/template/index.tsx rename to website/src/editor/FormEditor/ImportTemplate/index.tsx index 1c2dc31..c8a0cd8 100644 --- a/website/src/editor/FormEditor/template/index.tsx +++ b/website/src/editor/FormEditor/ImportTemplate/index.tsx @@ -1,31 +1,40 @@ +import classnames from 'classnames'; import React, { useEffect, useState } from 'react'; -import { Button, Flex, Tag, Modal, ModalProps } from 'antd'; +import { Button, Flex, Tag } from 'antd'; import './index.less'; -import { CustomOptions, FormDesignData } from '../../FormRender'; -import templates from './data'; +import { ModalWrapper, ModalWrapperProps, CustomOptions, FormDesignData } from '../'; +import demo from './demo'; export interface TemplateItem { - img: string; + img?: string; name: string; data: FormDesignData; }; -export interface ImportModalProps extends Partial { +export interface ImportModalProps extends Partial { data?: Array onSelect?: (item?: TemplateItem) => void; - context?: CustomOptions['context']; + editorContext?: CustomOptions['editorContext']; } +// 模板列表 +const templateList = [{ + name: 'demo', + data: demo, +}] as TemplateItem[]; + const ImportModal: React.FC = (props) => { const { + onClose, + className, open, - context, - data = templates, + editorContext, + data = templateList, onSelect, ...rest } = props; - const [modalOpen, setModalOpen] = useState(false); + const [modalOpen, setModalOpen] = useState(); useEffect(() => { setModalOpen(open); @@ -33,13 +42,15 @@ const ImportModal: React.FC = (props) => { const closeModal = () => { setModalOpen(false); + onClose?.(); }; const prefixCls = 'import-modal'; + const cls = classnames(prefixCls, className); const load = (item?: TemplateItem) => { - context?.dispatch((old) => ({ ...old, widgetList: item?.data })); - onSelect && onSelect(item); + editorContext?.dispatch((old?: TemplateItem) => ({ ...old, widgetList: item?.data })); + onSelect?.(item); setModalOpen(false); }; @@ -50,10 +61,10 @@ const ImportModal: React.FC = (props) => { return ( <> -

导入模板
@@ -63,9 +74,8 @@ const ImportModal: React.FC = (props) => { )) } - + - ); }; diff --git a/website/src/editor/FormEditor/components/Divider/config.ts b/website/src/editor/FormEditor/components/Divider/config.ts new file mode 100644 index 0000000..4f03393 --- /dev/null +++ b/website/src/editor/FormEditor/components/Divider/config.ts @@ -0,0 +1,18 @@ +import Setting from './setting'; + +export default { + panel: { + icon: 'divider', + label: '分割线', + nonform: true, + }, + type: 'Divider', + labelWidth: 'auto', + props: { + style: { width: '100%' }, + label: '分割线', + orientation: 'left', + type: 'horizontal', + }, + setting: { ...Setting }, +}; diff --git a/website/src/editor/FormEditor/components/Divider/index.tsx b/website/src/editor/FormEditor/components/Divider/index.tsx new file mode 100644 index 0000000..7443424 --- /dev/null +++ b/website/src/editor/FormEditor/components/Divider/index.tsx @@ -0,0 +1,16 @@ +import { Divider, DividerProps } from 'antd'; +import React from 'react'; +import { CommonFormProps } from '@simpleform/editor'; + +// 分割线(在拖拽过程中会出现渲染报错,所以需要移除children属性) +export const CustomDivider: React.FC & DividerProps & { label?: string }> = (props) => { + + const { + label, + children, + ...rest + } = props; + return ( + + ); +}; diff --git a/website/src/editor/FormEditor/components/Divider/setting.ts b/website/src/editor/FormEditor/components/Divider/setting.ts new file mode 100644 index 0000000..fa0695d --- /dev/null +++ b/website/src/editor/FormEditor/components/Divider/setting.ts @@ -0,0 +1,63 @@ +const baseSetting = [ + { + label: '标题', + name: 'props.label', + type: 'Input', + }, + { + label: "对齐方式", + name: 'props.orientation', + type: "Radio.Group", + props: { + style: { width: '100%' }, + options: [ + { label: '左边对齐', value: 'left' }, + { label: '居中', value: 'center' }, + { label: '右边对齐', value: 'right' }, + ] + } + }, + { + label: "位置", + name: 'props.type', + type: "Radio.Group", + props: { + style: { width: '100%' }, + options: [ + { label: '水平', value: 'horizontal' }, + { label: '垂直', value: 'vertical' }, + ] + } + } +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.plain', + type: 'Checkbox', + inline: true, + compact: true, + children: '正文样式' + }, + { + name: 'props.dashed', + type: 'Checkbox', + inline: true, + compact: true, + children: '是否虚线' + } +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/config/example/index.ts b/website/src/editor/FormEditor/components/Example/config.ts similarity index 100% rename from website/src/editor/FormEditor/config/example/index.ts rename to website/src/editor/FormEditor/components/Example/config.ts diff --git a/website/src/editor/FormRender/components/example/data.ts b/website/src/editor/FormEditor/components/Example/data.ts similarity index 100% rename from website/src/editor/FormRender/components/example/data.ts rename to website/src/editor/FormEditor/components/Example/data.ts diff --git a/website/src/editor/FormRender/components/example/index.tsx b/website/src/editor/FormEditor/components/Example/index.tsx similarity index 77% rename from website/src/editor/FormRender/components/example/index.tsx rename to website/src/editor/FormEditor/components/Example/index.tsx index df95c30..b42a24c 100644 --- a/website/src/editor/FormRender/components/example/index.tsx +++ b/website/src/editor/FormEditor/components/Example/index.tsx @@ -1,9 +1,9 @@ import React, { useEffect } from 'react'; -import { CommonWidgetProps } from '@simpleform/editor'; -import FormRender, { useSimpleForm, FormRenderProps } from '../../../FormRender'; +import FormRender, { useSimpleForm, FormRenderProps } from '../../FormRender'; +import { CommonFormProps } from '@simpleform/editor'; import data from './data'; -const ExampleGroup = (props: CommonWidgetProps) => { +const ExampleGroup = (props: CommonFormProps) => { const form = useSimpleForm(); const { value, onChange, disabled } = props; @@ -14,7 +14,7 @@ const ExampleGroup = (props: CommonWidgetProps) => { }, [value]); const handleChange: FormRenderProps['onFieldsChange'] = (_, values) => { - onChange && onChange(values); + onChange?.(values); }; return ({ fileId: data.fileId })}}" + }, + setting: { ...Setting, ...FieldSetting }, +}; diff --git a/website/src/editor/FormEditor/components/FileUpload/index.less b/website/src/editor/FormEditor/components/FileUpload/index.less new file mode 100644 index 0000000..e69de29 diff --git a/website/src/editor/FormEditor/components/FileUpload/index.tsx b/website/src/editor/FormEditor/components/FileUpload/index.tsx new file mode 100644 index 0000000..99118b4 --- /dev/null +++ b/website/src/editor/FormEditor/components/FileUpload/index.tsx @@ -0,0 +1,127 @@ +import { Button, message, Upload, UploadProps } from 'antd'; +import { RcFile } from 'antd/lib/upload'; +import React, { useEffect, useState } from 'react'; +import { UploadFile } from 'antd/lib/upload/interface'; +import './index.less'; +import { objectToFormData, DOC_MIME_KEYS, DOC_MIME_VALUES, isDocFile, CommonFormProps } from '@simpleform/editor'; +import { AxiosInstance } from 'axios'; + +// 扩展后的文件类型 +export type FileItem = UploadFile & RcFile; +export interface FileUploadProps extends Omit, CommonFormProps> { + formdataKey: string; // FormData的key + maxSize?: number; // 每个文件的限制上传大小 + uploadCallback?: (data: unknown) => Record; // 上传请求函数回调 +} +const FileUpload = React.forwardRef((props, ref) => { + + const { + maxSize = 5, + // 组件原生props + value, + onChange, + action, + headers, + data, // 额外参数 + showUploadList, + accept = DOC_MIME_VALUES.join(','), + multiple = true, + children, + formdataKey = 'file', + _options, + uploadCallback, + ...rest + } = props; + + const [fileList, setFileList] = useState>([]); + const [loading, setLoading] = useState(); + const formrender = _options?.formrender; + const defineConfig = formrender?.config; + const request = defineConfig?.variables?.request as AxiosInstance; + + useEffect(() => { + setFileList(JSON.parse(JSON.stringify(value || []))); + }, [value]); + + const checkFile = (file: RcFile) => { + const fileSize = file.size / 1024 / 1024; + if (fileSize > maxSize) { + message.error(`附件大小应小于${maxSize}M`); + return Upload.LIST_IGNORE; + } + if (fileSize === 0) { + message.error(`文件不能为空`); + return Upload.LIST_IGNORE; + } + const isDoc = isDocFile(file); + if (!isDoc) { + message.error(`请上传正确的文件格式: ${DOC_MIME_KEYS?.join(',')}`); + return Upload.LIST_IGNORE; + } + }; + + // 自定义上传 + const UploadProps: UploadProps = { + // 默认关闭已上传列表 + showUploadList: showUploadList !== undefined ? showUploadList : false, + onRemove: (file) => { + const index = fileList.indexOf(file as RcFile); + const newFileList = fileList.slice(); + newFileList.splice(index, 1); + setFileList(newFileList); + return rest?.onRemove && rest?.onRemove(file); + }, + beforeUpload: (data) => { + const file = data as FileItem; + if (checkFile(file) == Upload.LIST_IGNORE) { + return Upload.LIST_IGNORE; + } + }, + customRequest: async (option) => { + const file = option?.file as RcFile; + if (!file || typeof action !== 'string') return; + const cloneData = [...fileList]; + const insertIndex = cloneData?.length; + const formdata = objectToFormData(data); + formdata.append(formdataKey, file); + setLoading(true); + request?.(action, { + method: 'post', + data: formdata, + headers: headers, + onUploadProgress: (event) => { + const complete = event.total && (event.loaded / event.total * 100 | 0); + cloneData[insertIndex] = { ...file, percent: complete }; + setFileList(cloneData); + } + }).then((res) => { + const data = res.data; + const params = uploadCallback ? uploadCallback(data) : {}; + cloneData[insertIndex] = { ...file, status: 'success' as FileItem['status'], ...params }; + onChange && onChange(cloneData); + setFileList(cloneData); + }).catch(() => { + cloneData[insertIndex] = { ...file, status: 'error' }; + setFileList(cloneData); + message.error(`${file.name}上传失败`); + }).finally(() => { + setLoading(false); + }); + } + }; + + return ( + + {children || } + + ); +}); + +export default FileUpload; diff --git a/website/src/editor/FormEditor/components/FileUpload/setting.ts b/website/src/editor/FormEditor/components/FileUpload/setting.ts new file mode 100644 index 0000000..91f35a1 --- /dev/null +++ b/website/src/editor/FormEditor/components/FileUpload/setting.ts @@ -0,0 +1,71 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: '上传接口路径', + name: 'props.action', + type: 'Input', + }, + { + label: '接口响应数据', + name: 'props.uploadCallback', + type: 'Input.TextArea', + }, + { + label: '参数名', + name: 'props.formdataKey', + type: 'Input', + initialValue: 'file' + }, + { + label: '最大允许上传个数', + name: 'props.maxCount', + type: 'InputNumber', + initialValue: 5 + }, + { + label: '文件大小限制(MB)', + name: 'props.maxSize', + type: 'InputNumber', + initialValue: 5 + } +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + } +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required'] + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/components/FormTable/config.ts b/website/src/editor/FormEditor/components/FormTable/config.ts new file mode 100644 index 0000000..46a99b5 --- /dev/null +++ b/website/src/editor/FormEditor/components/FormTable/config.ts @@ -0,0 +1,14 @@ +import Setting from './setting'; + +export default { + panel: { + icon: 'table', + label: '可编辑表格', + }, + label: '可编辑表格', + type: 'FormTable', + props: { + columns: [], + }, + setting: { ...Setting }, +}; diff --git a/website/src/editor/FormEditor/components/FormTable/editor/column-setting.ts b/website/src/editor/FormEditor/components/FormTable/editor/column-setting.ts new file mode 100644 index 0000000..0d5a3ef --- /dev/null +++ b/website/src/editor/FormEditor/components/FormTable/editor/column-setting.ts @@ -0,0 +1,41 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: '标题', + name: 'label', + type: 'Input' + }, + { + label: '列宽', + name: 'width', + type: 'InputNumber', + props: { + min: 0, + max: 300 + } + }, + { + label: '对齐方式', + name: 'align', + type: "Select", + props: { + style: { width: '100%' }, + allowClear: true, + options: [ + { label: '左边对齐', value: 'left' }, + { label: '居中', value: 'center' }, + { label: '右边对齐', value: 'right' }, + ] + } + }, +]; + +const setting = { + '列属性': baseSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/components/FormTable/editor/index.less b/website/src/editor/FormEditor/components/FormTable/editor/index.less new file mode 100644 index 0000000..3f36335 --- /dev/null +++ b/website/src/editor/FormEditor/components/FormTable/editor/index.less @@ -0,0 +1,52 @@ +.design-table { + border: 1px inset rgba(0, 0, 0, .1); + margin: 0; + position: relative; + white-space: nowrap; + + .table-dnd { + overflow: auto; + min-height: 100px; + } + + &-selection { + margin: 0; + max-width: 200px; + } + + &-col { + margin: 0; + max-width: 200px; + border: 1px solid #ebeef5; + // width: 100%; + display: inline-block; + padding: 1px; + + &__head { + padding: 12px 10px; + border-bottom: 1px solid #ebeef5; + overflow: hidden; + text-overflow: ellipsis; + white-space: normal; + word-break: break-all; + line-height: 23px; + color: #909399; + font-weight: 500; + } + + &__body { + padding: 12px; + min-height: 56px; + } + } + + &-placeholder { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + color: #ccc; + margin: auto; + text-align: center; + } +} \ No newline at end of file diff --git a/website/src/editor/FormEditor/components/FormTable/editor/index.tsx b/website/src/editor/FormEditor/components/FormTable/editor/index.tsx new file mode 100644 index 0000000..36e16eb --- /dev/null +++ b/website/src/editor/FormEditor/components/FormTable/editor/index.tsx @@ -0,0 +1,132 @@ +import React from "react"; +import classnames from "classnames"; +import './index.less'; +import { FormTableProps } from ".."; +import { + BaseSelection, + BaseSelectionProps, + SvgIcon, + BaseDnd, + pickAttrs, + CustomFormRenderProps, + joinFormPath, + renderWidgetItem, + getCommonOptions, + getWidgetItem, + setWidgetItem +} from "@simpleform/editor"; +import FormTableColSetting from './column-setting'; + +const EditorTable = React.forwardRef>(({ + columns = [], + className, + style, + value, + onChange, + _options, + ...restTable +}, ref) => { + + const prefix = "design-table"; + const Classes = { + Table: prefix, + TableBody: `${prefix}-body`, + TableCol: `${prefix}-col`, + TableSelection: `${prefix}-selection`, + TableColHead: `${prefix}-col__head`, + TableColBody: `${prefix}-col__body`, + TableDnd: `${prefix}-dnd`, + placeholder: `${prefix}-placeholder`, + }; + + const { path, formrender, editorContext } = _options || {}; + const commonOptions = getCommonOptions(_options); + const columnsPath = joinFormPath(path, 'props.columns'); + const { settingForm, editorConfig } = editorContext?.state || {}; + + // 监听列控件设置值 + const columnInputChange: CustomFormRenderProps['onValuesChange'] = ({ value }) => { + settingForm && settingForm.setFieldValue('initialValue', value); + }; + + const onSelectHandler: BaseSelectionProps['onSelectHandler'] = (nextSelected) => { + const selectedItem = getWidgetItem(formrender, nextSelected?.path); + const configSetting = editorConfig?.[selectedItem?.type || ''].setting; + const baseSetting = configSetting?.['基础属性']?.filter((item) => item.name !== 'name'); + const mergeSetting = Object.assign(FormTableColSetting, { + '基础属性': baseSetting, + '操作属性': configSetting?.['操作属性'], + '校验规则': configSetting?.['校验规则'] + }); + editorContext?.dispatch && editorContext?.dispatch((old) => ({ + ...old, + selected: Object.assign({ setting: mergeSetting }, nextSelected) + })); + }; + + const copyItem = (column, colIndex) => { + const nextColIndex = colIndex + 1; + const cloneColumns = [...columns]; + const newColumn = { + ...column + }; + cloneColumns.splice(nextColIndex, 0, newColumn); + setWidgetItem(formrender, cloneColumns, path); + }; + + return ( +
+ + { + columns?.map((col, colIndex) => { + const { label } = col; + const _childOptions = { + ...commonOptions, + index: colIndex, + path: joinFormPath(columnsPath, colIndex), + onValuesChange: columnInputChange + }; + const widget = { ...col, label: '' }; + const instance = renderWidgetItem(formrender, widget, _childOptions); + return ( + copyItem(col, colIndex)} />]} + > +
+
+ {label} +
+
+ {instance} +
+
+
+ ); + }) + } +
+ {!columns?.length && 将控件拖拽到此处} +
+ ); +}); + +export default EditorTable; diff --git a/website/src/editor/FormEditor/components/FormTable/index.tsx b/website/src/editor/FormEditor/components/FormTable/index.tsx new file mode 100644 index 0000000..13d3fe4 --- /dev/null +++ b/website/src/editor/FormEditor/components/FormTable/index.tsx @@ -0,0 +1,36 @@ +import { ColumnType, TableProps } from 'antd/lib/table'; +import React, { CSSProperties } from 'react'; +import { CommonFormProps, CustomGenerateWidgetItem } from '@simpleform/editor'; +import EditorTable from './editor'; +import FormTable from './render'; + +export interface FormTableProps extends Omit, 'title' | 'onChange'>, CommonFormProps { + minRows?: number; // 表格默认最少行数 + maxRows?: number; // 表格默认最大行数 + disabled?: boolean; // 禁用 + buttons?: Array<'add' | 'delete'>; // 展示或隐藏增减按钮 + columns: CustomColumnType[]; + className?: string; + style?: CSSProperties; +} + +export interface CustomColumnType extends ColumnType, CustomGenerateWidgetItem { + key?: string; + title: string; + render?: (val: unknown, record?: V, rowIndex?: number, colIndex?: number) => React.ReactNode; +} + +const Table = React.forwardRef((props, ref) => { + const { + _options, + } = props; + + const isEditor = _options?.isEditor; + const columns = _options?.props?.columns as FormTableProps['columns']; + + return ( + isEditor ? : + ); +}); + +export default Table; diff --git a/website/src/editor/FormEditor/components/FormTable/render/columnGroup.tsx b/website/src/editor/FormEditor/components/FormTable/render/columnGroup.tsx new file mode 100644 index 0000000..c3cc746 --- /dev/null +++ b/website/src/editor/FormEditor/components/FormTable/render/columnGroup.tsx @@ -0,0 +1,26 @@ +import React, { useMemo } from 'react'; +import { CustomColumnType } from '..'; + +// 列的设置 +export const ColumnGroup: React.FC<{ columns: CustomColumnType[] }> = ({ columns }) => { + const columnWidths = columns.map((ele) => ele.width).join("-"); + const cols = useMemo(() => { + let cols: React.ReactElement[] = []; + let mustInsert = false; + for (let i = columns.length; i >= 0; i--) { + const width = columns[i] && columns[i].width; + if (width || mustInsert) { + cols.unshift( + + ); + mustInsert = true; + } + } + return cols; + // eslint-disable-next-line + }, [columnWidths]); + return {cols}; +}; diff --git a/website/src/editor/FormEditor/components/FormTable/render/components.less b/website/src/editor/FormEditor/components/FormTable/render/components.less new file mode 100644 index 0000000..e69de29 diff --git a/website/src/editor/FormEditor/components/FormTable/render/components.tsx b/website/src/editor/FormEditor/components/FormTable/render/components.tsx new file mode 100644 index 0000000..b10d30f --- /dev/null +++ b/website/src/editor/FormEditor/components/FormTable/render/components.tsx @@ -0,0 +1,74 @@ +import React from 'react'; +import classNames from 'classnames'; +import "./components.less"; +import { pickAttrs } from '@simpleform/editor'; + +const prefix = "form-table"; +export const Classes = { + Table: `${prefix}`, + TableBody: `${prefix}-body`, + TableRow: `${prefix}-row`, + TableHead: `${prefix}-head`, + TableCell: `${prefix}-cell`, +}; +export interface TableCellProps extends React.HtmlHTMLAttributes { + componentType?: "th" | "td" +} +export const TableCell = React.forwardRef((props, ref) => { + const { + children, + className, + componentType = "td", + ...rest + } = props; + + return React.createElement( + componentType, + { className: classNames(Classes.TableCell, className), ref, ...pickAttrs(rest) }, + children + ); +}); + +export type TableRowProps = React.HtmlHTMLAttributes; +export const TableRow = React.forwardRef((props, ref) => { + const { + children, + className, + ...rest + } = props; + + return ( + + {children} + + ); +}); + +export const TableBody = React.forwardRef>((props, ref) => { + const { + children, + className, + ...rest + } = props; + + return ( + + {children} + + ); +}); + +export type TableHeadProps = React.HtmlHTMLAttributes; +export const TableHead = React.forwardRef((props, ref) => { + const { + children, + className, + ...rest + } = props; + + return ( + + {children} + + ); +}); diff --git a/website/src/editor/FormEditor/components/FormTable/render/index.less b/website/src/editor/FormEditor/components/FormTable/render/index.less new file mode 100644 index 0000000..a824be3 --- /dev/null +++ b/website/src/editor/FormEditor/components/FormTable/render/index.less @@ -0,0 +1,12 @@ +.form-table { + width: 100%; + + .delete-icon { + cursor: pointer; + } + + .add-btn { + padding-left: 0; + padding-right: 0; + } +} \ No newline at end of file diff --git a/website/src/editor/FormEditor/components/FormTable/render/index.tsx b/website/src/editor/FormEditor/components/FormTable/render/index.tsx new file mode 100644 index 0000000..e6905f2 --- /dev/null +++ b/website/src/editor/FormEditor/components/FormTable/render/index.tsx @@ -0,0 +1,104 @@ +import React, { useEffect, useMemo, useState } from "react"; +import { Button, message, Table, TableProps } from "antd"; +import { + renderWidgetItem, + joinFormPath, + defaultGetId, + SvgIcon, + pickAttrs +} from "@simpleform/editor"; +import './index.less'; +import { FormTableProps } from ".."; + +const FormTable = React.forwardRef((props, ref) => { + const { + className, + columns = [], + minRows = 0, + maxRows = 50, + disabled, + buttons = ['add', 'delete'], + pagination = false, + _options, + value, + onChange, + ...rest + } = props; + const form = _options?.form; + const curName = _options?.name || ''; + const formrender = _options?.formrender; + const items = Array.from({ length: Math.max(minRows || 0) }); + const defaultValue = useMemo(() => items.map(() => ({ key: defaultGetId('row') })), [items]); + const [dataSource, setDataSource] = useState([]); + + useEffect(() => { + setDataSource(defaultValue); + }, [minRows]); + + const deleteBtn = (rowIndex: number) => { + if (disabled) return; + dataSource.splice(rowIndex, 1); + const newData = [...dataSource]; + setDataSource(newData); + }; + + const addBtn = () => { + if (dataSource?.length >= maxRows) { + message.info(`最大行数不能超过${maxRows}`); + return; + } + const newData = dataSource.concat([{ key: defaultGetId('row') }]); + setDataSource(newData); + }; + + const newColumns = columns?.map((col) => { + const { name, label, render, ...restCol } = col; + return { + ...restCol, + dataIndex: name, + title: label, + render: (_, record, rowIndex) => { + const originRender = render?.(_, record, rowIndex); + const isEditable = !!(restCol?.type || restCol?.typeRender); + if (!isEditable) { + return originRender; + } + const childName = joinFormPath(curName, rowIndex, name); + const widget = { ...restCol, name: childName, label: '' }; + const _options = { form, formrender, index: rowIndex, props: { disabled } }; + return renderWidgetItem(formrender, widget, _options); + } + }; + }) as TableProps['columns'] || []; + + if (buttons?.includes('delete')) { + // 添加删除按键 + newColumns.unshift({ + title: '#', + width: 50, + render: (_, _record, index) => { + if (dataSource?.length > minRows) { + return deleteBtn(index)} />; + } + } + }); + } + + return ( + <> + + {buttons?.includes('add') && } + + ); +}); + +export default FormTable; diff --git a/website/src/editor/FormEditor/components/FormTable/setting.ts b/website/src/editor/FormEditor/components/FormTable/setting.ts new file mode 100644 index 0000000..dd4a21a --- /dev/null +++ b/website/src/editor/FormEditor/components/FormTable/setting.ts @@ -0,0 +1,55 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: '最小行数', + name: 'props.minRows', + type: 'InputNumber', + initialValue: 0 + }, + { + label: '最大行数', + name: 'props.maxRows', + type: 'InputNumber', + initialValue: 50 + } +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + }, +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required'] + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/components/Grid/col-setting.ts b/website/src/editor/FormEditor/components/Grid/col-setting.ts new file mode 100644 index 0000000..1202da6 --- /dev/null +++ b/website/src/editor/FormEditor/components/Grid/col-setting.ts @@ -0,0 +1,45 @@ +const baseSetting = [ + { + label: '栅格占位格数', + name: 'props.span', + type: 'Slider', + initialValue: 12, + props: { + style: { width: '100%' }, + max: 24 + } + }, + { + label: '栅格左侧间隔格数', + name: 'props.offset', + type: 'Slider', + props: { + style: { width: '100%' }, + max: 24 + } + }, + { + label: '栅格向左移动格数', + name: 'props.pull', + type: 'Slider', + props: { + style: { width: '100%' }, + max: 24 + } + }, + { + label: '栅格向右移动格数', + name: 'props.push', + type: 'Slider', + props: { + style: { width: '100%' }, + max: 24 + } + } +]; + +const setting = { + '基础属性': baseSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/components/Grid/col.less b/website/src/editor/FormEditor/components/Grid/col.less new file mode 100644 index 0000000..ff4f29a --- /dev/null +++ b/website/src/editor/FormEditor/components/Grid/col.less @@ -0,0 +1,21 @@ +@form-primary-color: #FF4040; // 全局主色 + +.col-selection { + min-height: 40px; + height: 100%; + display: inline-flex; + flex-wrap: wrap; + padding: 10px; + word-wrap: break-word; + word-break: break-all; + box-sizing: border-box; + outline: 1px dashed #b8b8b8; +} + +.custom-col { + padding: 10px; +} + +.custom-col.edit-col { + padding: 1px; +} \ No newline at end of file diff --git a/website/src/editor/FormEditor/components/Grid/col.tsx b/website/src/editor/FormEditor/components/Grid/col.tsx new file mode 100644 index 0000000..e2dae21 --- /dev/null +++ b/website/src/editor/FormEditor/components/Grid/col.tsx @@ -0,0 +1,70 @@ +import { ColProps } from 'antd'; +import classnames from 'classnames'; +import React from 'react'; +import './col.less'; +import { + BaseDnd, + BaseSelection, + BaseSelectionProps, + joinFormPath, + CustomCol, + renderWidgetItem, + CustomGenerateWidgetItem, + getCommonOptions +} from '@simpleform/editor'; + +export type CustomColProps = Omit & BaseSelectionProps & { + column: ColProps & { children?: CustomGenerateWidgetItem[] }; + colIndex: number; +}; +// col组件 +const GridCol = React.forwardRef((props, ref) => { + const { + className, + style, + column, + colIndex, + ...rest + } = props; + + const { _options } = rest || {}; + const { isEditor, path, formrender } = _options || {}; + const widgetList = column?.children; + const commonOptions = getCommonOptions(_options); + const cls = classnames(className, { + 'edit-col': isEditor + }); + + const childs = widgetList?.map((child, childIndex) => { + const _childOptions = { + ...commonOptions, + index: childIndex, + path: joinFormPath(path, 'children', childIndex), + }; + const instance = renderWidgetItem(formrender, child, _childOptions); + return instance; + }); + + return ( + + {isEditor ? + + + {childs} + + + : + childs + } + + ); +}); + +export default GridCol; diff --git a/website/src/editor/FormEditor/components/Grid/config.ts b/website/src/editor/FormEditor/components/Grid/config.ts new file mode 100644 index 0000000..7fa30ce --- /dev/null +++ b/website/src/editor/FormEditor/components/Grid/config.ts @@ -0,0 +1,28 @@ +import GridSetting from './setting'; +import GridColSetting from './col-setting'; + +export const GridColConfig = { + panel: { + label: '栅格列', + nonform: true, + nonselection: true, + }, + type: 'GridCol', + props: { span: 12 }, + setting: { ...GridColSetting }, +}; + +export default { + panel: { + icon: 'grid', + label: '栅格布局', + nonform: true, + nonselection: true, + }, + type: 'Grid', + setting: { ...GridSetting }, + cols: [ + { ...GridColConfig, children: [] }, + { ...GridColConfig, children: [] } + ] +}; diff --git a/packages/editor/src/components/common/grid/col.less b/website/src/editor/FormEditor/components/Grid/index.less similarity index 83% rename from packages/editor/src/components/common/grid/col.less rename to website/src/editor/FormEditor/components/Grid/index.less index e354188..89cd757 100644 --- a/packages/editor/src/components/common/grid/col.less +++ b/website/src/editor/FormEditor/components/Grid/index.less @@ -1,11 +1,11 @@ @form-primary-color: #FF4040; // 全局主色 -.edit-col { +.edit-row { padding: 1px; } -.col-selection { - min-height: 40px; +.row-selection { + min-height: 60px; height: 100%; display: inline-flex; flex-wrap: wrap; diff --git a/website/src/editor/FormEditor/components/Grid/index.tsx b/website/src/editor/FormEditor/components/Grid/index.tsx new file mode 100644 index 0000000..12fdc77 --- /dev/null +++ b/website/src/editor/FormEditor/components/Grid/index.tsx @@ -0,0 +1,75 @@ +import React from 'react'; +import { RowProps } from 'antd'; +import classnames from 'classnames'; +import './index.less'; +import { + BaseSelection, + SvgIcon, + joinFormPath, + getCommonOptions, + insertWidgetItem, + CommonFormProps, + CustomGenerateWidgetItem, + CustomRow +} from '@simpleform/editor'; +import GridCol from './col'; +import { GridColConfig } from './config'; + +export type CustomRowProps = RowProps & CommonFormProps }>; + +const Grid = React.forwardRef((props, ref) => { + const { + className, + style, + ...rest + } = props; + + const { _options } = rest || {}; + const { isEditor, cols, path, formrender } = _options || {}; + const commonOptions = getCommonOptions(_options); + const colsPath = joinFormPath(path, 'cols'); + const cls = classnames(className, { + 'edit-row': isEditor + }); + + const addCol = () => { + const childs = _options?.children instanceof Array ? _options?.children : []; + const nextIndex = childs?.length || 0; + const newItem = { + ...GridColConfig, + children: [] + }; + insertWidgetItem(formrender, newItem, nextIndex, colsPath); + }; + + const childs = cols?.map((col, colIndex) => { + const colProps = { ..._options?.props, ...col?.props }; + const _colOptions = { + ...commonOptions, + ...col, + props: colProps, + index: colIndex, + path: joinFormPath(colsPath, colIndex), + }; + return ; + }); + + return ( + + {isEditor ? + ]} + > + {childs} + + : + childs + } + + ); +}); + +export default Grid; diff --git a/website/src/editor/FormEditor/components/Grid/setting.ts b/website/src/editor/FormEditor/components/Grid/setting.ts new file mode 100644 index 0000000..93c98b8 --- /dev/null +++ b/website/src/editor/FormEditor/components/Grid/setting.ts @@ -0,0 +1,45 @@ +const baseSetting = [ + { + label: '栅格间隔', + name: 'props.gutter', + type: 'InputNumber', + valueGetter: "{{((val) => val || 0)}}" + }, + { + label: "水平排列", + name: 'props.justify', + type: "Select", + initialValue: 'start', + props: { + style: { width: '100%' }, + options: [ + { label: '从头开始', value: 'start' }, + { label: '从尾部开始', value: 'end' }, + { label: '居中排列', value: 'center' }, + { label: '均匀分布(中间间隔相等)', value: 'space-around' }, + { label: '居中均匀分布', value: 'space-between' }, + { label: '均匀分布(每个间隔相等)', value: 'space-evenly' }, + ] + } + }, + { + label: "垂直对齐", + name: 'props.align', + type: "Select", + initialValue: 'top', + props: { + style: { width: '100%' }, + options: [ + { label: '顶部对齐', value: 'top' }, + { label: '居中', value: 'middle' }, + { label: '底部对齐', value: 'bottom' }, + ] + } + } +]; + +const setting = { + '基础属性': baseSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/components/ImageUpload/config.ts b/website/src/editor/FormEditor/components/ImageUpload/config.ts new file mode 100644 index 0000000..44f4abd --- /dev/null +++ b/website/src/editor/FormEditor/components/ImageUpload/config.ts @@ -0,0 +1,15 @@ +import Setting from './setting'; +import FieldSetting from '../../editorConfig/fieldSetting'; + +export default { + panel: { + icon: 'picture-upload-field', + label: '图片上传', + }, + label: '图片上传', + type: 'ImageUpload', + props: { + uploadCallback: "{{(data) => ({ fileId: data.fileId })}}" + }, + setting: { ...Setting, ...FieldSetting }, +}; diff --git a/website/src/editor/FormEditor/components/ImageUpload/index.tsx b/website/src/editor/FormEditor/components/ImageUpload/index.tsx new file mode 100644 index 0000000..06c0bb6 --- /dev/null +++ b/website/src/editor/FormEditor/components/ImageUpload/index.tsx @@ -0,0 +1,179 @@ +import { message, Modal, Upload, UploadProps } from 'antd'; +import { RcFile } from 'antd/lib/upload'; +import React, { useEffect, useState } from 'react'; +import { UploadFile } from 'antd/lib/upload/interface'; +import { getBase64 } from './util'; +import { CommonFormProps, objectToFormData, IMAGE_MIME_KEYS, isImageFile } from '@simpleform/editor'; +import { AxiosInstance } from 'axios'; + +// 扩展后的文件类型 +export type FileItem = UploadFile & RcFile; +export interface ImageUploadProps extends Omit, CommonFormProps> { + formdataKey: string; // FormData的key + maxSize?: number; // 每张图片的限制上传大小 + uploadCallback?: (data: unknown) => Record; // 上传请求函数回调 +} +const ImageUpload = React.forwardRef((props, ref) => { + + const { + maxSize = 5, + // 组件原生props + value, + onChange, + action, + headers, + data, // 额外参数 + accept = 'image/gif,image/jpeg,image/jpg,image/png,image/webp,image/bmp', + listType = 'picture-card', + maxCount = 5, + multiple = true, + children, + formdataKey = 'file', + _options, + uploadCallback, + ...rest + } = props; + + const [fileList, setFileList] = useState>([]); + const [imageVisible, setImageVisible] = useState(); + const [imageTitle, setImageTitle] = useState(); + const [imageSrc, setImageSrc] = useState(); + const [loading, setLoading] = useState(); + const formrender = _options?.formrender; + const defineConfig = formrender?.config; + const request = defineConfig?.variables?.request as AxiosInstance; + + useEffect(() => { + setFileList(JSON.parse(JSON.stringify(value || []))); + }, [value]); + + const checkFile = (file: RcFile) => { + const fileSize = file.size / 1024 / 1024; + if (fileSize > maxSize) { + message.error(`附件大小应小于${maxSize}M`); + return Upload.LIST_IGNORE; + } + if (fileSize === 0) { + message.error(`文件不能为空`); + return Upload.LIST_IGNORE; + } + const isDoc = isImageFile(file); + if (!isDoc) { + message.error(`请上传正确的图片格式: ${IMAGE_MIME_KEYS?.join(',')}`); + return Upload.LIST_IGNORE; + } + }; + + // 远程上传 + const UploadProps: UploadProps = { + onRemove: (file) => { + const index = fileList.indexOf(file as RcFile); + const newFileList = fileList.slice(); + newFileList.splice(index, 1); + setFileList(newFileList); + return rest?.onRemove && rest?.onRemove(file); + }, + beforeUpload: async (data) => { + const file = data as RcFile; + if (checkFile(file) == Upload.LIST_IGNORE) { + return Upload.LIST_IGNORE; + } + }, + customRequest: async (option) => { + const file = option?.file as RcFile; + if (!file || typeof action !== 'string') return; + const cloneData = [...fileList]; + const insertIndex = cloneData?.length; + const formdata = objectToFormData(data); + formdata.append(formdataKey, file); + setLoading(true); + request?.(action, { + method: 'post', + data: formdata, + headers: headers, + onUploadProgress: (event) => { + const complete = event?.total && (event.loaded / event?.total * 100 | 0); + cloneData[insertIndex] = { ...file, percent: complete }; + setFileList(cloneData); + } + }).then((res) => { + const data = res.data; + const params = typeof uploadCallback === 'function' ? uploadCallback(data) : {}; + cloneData[insertIndex] = { ...file, status: 'success' as FileItem['status'], ...params }; + if (!cloneData[insertIndex].url) { + getBase64(file).then((url) => { + cloneData[insertIndex].thumbUrl = url; + }).finally(() => { + onChange && onChange(cloneData); + setFileList(cloneData); + }); + } else { + onChange && onChange(cloneData); + setFileList(cloneData); + } + }).catch(() => { + cloneData[insertIndex] = { ...file, status: 'error' }; + setFileList(cloneData); + message.error(`${file.name}上传失败`); + }).finally(() => { + setLoading(false); + }); + } + }; + + const handlePreview = async (data: UploadFile) => { + const file = data as FileItem; + if (!file.url && !file.preview) { + const fileChoose = file.originFileObj || file; // 源文件或当前的文件 + file.preview = await getBase64(fileChoose); + } + setImageSrc(file.url || file.preview); + setImageVisible(true); + const filename = getFileName(file); + setImageTitle(filename); + }; + + const handleCancel = () => { + setImageVisible(false); + }; + + // 获取文件名 + const getFileName = (file?: FileItem) => { + const defaultFileName = file?.url && (file.url.substring(file.url.lastIndexOf('/') + 1)); + return file?.name || defaultFileName; + }; + + const uploadButton = children || ( +
+
上传图片
+
+ ); + + return ( + <> + + {fileList?.length >= maxCount ? null : uploadButton} + + + example + + + ); +}); + +export default ImageUpload; diff --git a/website/src/editor/FormEditor/components/ImageUpload/setting.ts b/website/src/editor/FormEditor/components/ImageUpload/setting.ts new file mode 100644 index 0000000..38a1996 --- /dev/null +++ b/website/src/editor/FormEditor/components/ImageUpload/setting.ts @@ -0,0 +1,71 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: '上传接口路径', + name: 'props.action', + type: 'Input', + }, + { + label: '接口响应数据', + name: 'props.uploadCallback', + type: 'Input.TextArea', + }, + { + label: '参数名', + name: 'props.formdataKey', + type: 'Input', + initialValue: 'file' + }, + { + label: '最大允许上传个数', + name: 'props.maxCount', + type: 'InputNumber', + initialValue: 5 + }, + { + label: '文件大小限制(MB)', + name: 'props.maxSize', + type: 'InputNumber', + initialValue: 5 + }, +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + }, +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required'] + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/components/ImageUpload/util.ts b/website/src/editor/FormEditor/components/ImageUpload/util.ts new file mode 100644 index 0000000..5999609 --- /dev/null +++ b/website/src/editor/FormEditor/components/ImageUpload/util.ts @@ -0,0 +1,9 @@ +export function getBase64(file?: File): Promise { + if (!file) return Promise.resolve(undefined); + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = () => resolve(reader.result as string); + reader.onerror = error => reject(error); + }); +} diff --git a/website/src/editor/FormEditor/components/LayoutTable/TableCell.tsx b/website/src/editor/FormEditor/components/LayoutTable/TableCell.tsx new file mode 100644 index 0000000..9436de9 --- /dev/null +++ b/website/src/editor/FormEditor/components/LayoutTable/TableCell.tsx @@ -0,0 +1,160 @@ +import React, { useState } from 'react'; +import { + BaseSelection, + BaseSelectionProps, + getCommonOptions, + SvgIcon, + BaseDnd, + joinFormPath, + renderWidgetItem, + TableUtils +} from "@simpleform/editor"; +import classnames from 'classnames'; +import './index.less'; +import { Menu, MenuDivider, MenuItem } from '@szhsin/react-menu'; +import '@szhsin/react-menu/dist/index.css'; +import { LayoutTableRows } from '.'; + +export type CustomTableCellProps = React.HtmlHTMLAttributes & BaseSelectionProps & { + rows: NonNullable; + rowIndex: number; + cols: NonNullable; + colIndex: number; + tableUtils: TableUtils; +}; + +const CustomTableCell = React.forwardRef((props, ref) => { + const { + className, + rows, + rowIndex, + cols, + colIndex, + tableUtils, + ...rest + } = props; + + const { _options } = rest || {}; + const { isEditor, path, formrender, editorContext } = _options || {}; + const widgetList = cols?.[colIndex]?.children || []; + const commonOptions = getCommonOptions(_options); + const [selectCell, setSelectCell] = useState<{ rowIndex: number; colIndex: number }>({ + rowIndex: -1, + colIndex: -1, + }); + const [disabledHandles, setDisabledHandles] = useState(tableUtils.getDisabledHandles()); + + const handleSelectCell = (rowIndex: number, colIndex: number) => { + setSelectCell({ + rowIndex, + colIndex + }); + const handles = tableUtils.getDisabledHandles( + rowIndex, + colIndex + ); + setDisabledHandles(handles); + }; + + const handleFn = (operateName: string) => { + const { rowIndex, colIndex } = selectCell; + if (rowIndex > -1 && colIndex > -1) { + switch (operateName) { + case "insertTopRow": { + const res = tableUtils.insertRow(rowIndex, colIndex, 0); + res && handleSelectCell(res?.rowIndex, res?.colIndex); + break; + } + case "insertBottomRow": { + const res = tableUtils.insertRow(rowIndex, colIndex, 1); + res && handleSelectCell(res?.rowIndex, res?.colIndex); + break; + } + + case "insertLeftCol": { + const res = tableUtils.insertCol(rowIndex, colIndex, 0); + res && handleSelectCell(res?.rowIndex, res?.colIndex); + break; + } + case "insertRightCol": { + const res = tableUtils.insertCol(rowIndex, colIndex, 1); + res && handleSelectCell(res?.rowIndex, res?.colIndex); + break; + } + default: { + const fn = (tableUtils[operateName] as Function).bind(tableUtils); + if (fn) { + const res = fn(rowIndex, colIndex); + res && handleSelectCell(res?.rowIndex, res?.colIndex); + } + } + } + } + }; + + const cls = classnames(className, { + 'edit-cell': isEditor + }); + + const childs = widgetList?.map((child, childIndex) => { + const _childOptions = { + ...commonOptions, + index: childIndex, + path: joinFormPath(path, 'children', childIndex), + }; + const instance = renderWidgetItem(formrender, child, _childOptions); + return instance; + }); + + const operateBtn = ( + }> + handleFn('insertLeftCol')}>插入左侧列 + handleFn('insertRightCol')}>插入右侧列 + handleFn('insertTopRow')}>插入上方行 + handleFn('insertBottomRow')}>插入下方行 + + handleFn('leftMerge')}>合并左侧单元格 + handleFn('rightMerge')}>合并右侧单元格 + {/* 合并整行 */} + + handleFn('topMerge')}>合并上方单元格 + handleFn('bottomMerge')}>合并下方单元格 + {/* 合并整列 */} + + handleFn('splitH')}>水平拆分 + handleFn('splitV')}>垂直拆分 + handleFn('delCol')}>删除整列 + handleFn('delRow')}>删除整行 + + ); + + return ( +
+ ); +}); + +export default CustomTableCell; diff --git a/website/src/editor/FormEditor/components/LayoutTable/cell-setting.ts b/website/src/editor/FormEditor/components/LayoutTable/cell-setting.ts new file mode 100644 index 0000000..2e75977 --- /dev/null +++ b/website/src/editor/FormEditor/components/LayoutTable/cell-setting.ts @@ -0,0 +1,18 @@ +const baseSetting = [ + { + label: '列宽度', + name: 'width', + type: 'InputNumber', + }, + { + label: '行高度', + name: 'height', + type: 'InputNumber', + }, +]; + +const setting = { + '基础属性': baseSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/components/LayoutTable/config.ts b/website/src/editor/FormEditor/components/LayoutTable/config.ts new file mode 100644 index 0000000..5ebf5fe --- /dev/null +++ b/website/src/editor/FormEditor/components/LayoutTable/config.ts @@ -0,0 +1,29 @@ +import Setting from './setting'; +import cellSetting from './cell-setting'; + +export const TableCellConfig = { + panel: { + label: '单元格', + nonform: true, + nonselection: true, + }, + type: 'TableCell', + colspan: 1, + rowspan: 1, + children: [], + setting: cellSetting, +}; + +export default { + panel: { + icon: 'table', + label: '表格布局', + nonform: true, + nonselection: true, + }, + type: 'LayoutTable', + rows: [ + [TableCellConfig] + ], + setting: Setting, +}; diff --git a/website/src/editor/FormEditor/components/LayoutTable/index.less b/website/src/editor/FormEditor/components/LayoutTable/index.less new file mode 100644 index 0000000..cdc0cd9 --- /dev/null +++ b/website/src/editor/FormEditor/components/LayoutTable/index.less @@ -0,0 +1,40 @@ +@table-cell-color: #e5e5e5; // 全局主色 + +.r { + &-table { + table-layout: fixed; + width: 100%; + border-spacing: 0; + border-collapse: collapse; + font-size: 13px; + border-top: 1px solid @table-cell-color; + border-left: 1px solid @table-cell-color; + /*去掉单元格间隙*/ + border-spacing: 0; + + &-body { + background-color: #fff; + } + + &-cell { + padding: 6px; + height: 36px; + border-bottom: 1px solid @table-cell-color; + border-right: 1px solid @table-cell-color; + + &.edit-cell { + padding: 0; + } + } + } +} + +.table-selection { + padding: 4px; +} + +.cell-selection { + height: 100%; + margin: 0; + background-color: transparent; +} \ No newline at end of file diff --git a/website/src/editor/FormEditor/components/LayoutTable/index.tsx b/website/src/editor/FormEditor/components/LayoutTable/index.tsx new file mode 100644 index 0000000..60a674f --- /dev/null +++ b/website/src/editor/FormEditor/components/LayoutTable/index.tsx @@ -0,0 +1,111 @@ +import React, { CSSProperties, useMemo } from "react"; +import classnames from "classnames"; +import './index.less'; +import { + pickAttrs, + joinFormPath, + CommonFormProps, + CustomGenerateWidgetItem, + BaseSelection, + SvgIcon, + getCommonOptions, + setWidgetItem, + TableUtils, + TableCellType +} from "@simpleform/editor"; +import TableCell from './TableCell'; +import { TableCellConfig } from "./config"; + +const prefix = "r-"; +export const Classes = { + Table: `${prefix}table`, + TableBody: `${prefix}table-body`, + TableRow: `${prefix}table-row`, + TableHead: `${prefix}table-head`, + TableCell: `${prefix}table-cell`, +}; + +export type LayoutTableRows = Array>; + +export interface LayoutTableProps extends CommonFormProps { + className?: string; + style?: CSSProperties; + tableLayout?: React.CSSProperties["tableLayout"]; +} + +const Table = React.forwardRef((props, ref) => { + + const { tableLayout, className, style, ...rest } = props; + const { _options } = rest || {}; + const { formrender, isEditor, rows = [], path: tablePath } = _options || {}; + const commonOptions = getCommonOptions(_options); + const rowsPath = joinFormPath(tablePath, 'rows'); + const tableUtils = useMemo(() => new TableUtils<{ + children: LayoutTableRows + }>(rows, { + minRetainRow: 1, + minSplitHcolspan: 1, + minSplitVrowspan: 1, + fixRowType: 2, + CELL_DEFAULT_CONFIG: { + ...TableCellConfig + }, + onUpdate(newData) { + setWidgetItem(formrender, newData?.rows, rowsPath); + } + }), [rows]); + + const appendTableRow = () => { + tableUtils.insertRow(0, 0, 1); + }; + + const appendTableCol = () => { + tableUtils.insertCol(0, 0, 1); + }; + + const childs = ( +
+ {isEditor ? + { + editorContext?.dispatch((old) => ({ ...old, selected: nextSelected })); + handleSelectCell(rowIndex, colIndex); + }} + > + + {childs} + + + : + childs + } +
+ + { + rows?.map((cols, rowIndex) => { + return ( + + { + cols?.map((col, colIndex) => { + const _childOptions = { + ...commonOptions, + index: colIndex, + path: joinFormPath(tablePath, 'rows', rowIndex, colIndex), + }; + return ; + }) + } + + ); + }) + } + +
+ ); + + return ( + isEditor ? + , ]} + > + {childs} + + : childs + ); +}); + +export default Table; diff --git a/website/src/editor/FormEditor/components/LayoutTable/setting.ts b/website/src/editor/FormEditor/components/LayoutTable/setting.ts new file mode 100644 index 0000000..ba2a204 --- /dev/null +++ b/website/src/editor/FormEditor/components/LayoutTable/setting.ts @@ -0,0 +1,20 @@ +const baseSetting = [ + { + label: "表格布局", + name: 'props.tableLayout', + type: "Select", + props: { + style: { width: '100%' }, + options: [ + { label: '固定宽度', value: 'fixed' }, + { label: '自动撑开', value: 'auto' }, + ] + } + } +]; + +const setting = { + '基础属性': baseSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/components/RichEditor/config.ts b/website/src/editor/FormEditor/components/RichEditor/config.ts new file mode 100644 index 0000000..dba41f2 --- /dev/null +++ b/website/src/editor/FormEditor/components/RichEditor/config.ts @@ -0,0 +1,15 @@ +import Setting from './setting'; +import FieldSetting from '../../editorConfig/fieldSetting'; + +export default { + panel: { + icon: 'rich-editor-field', + label: '富文本编辑器', + }, + label: '富文本编辑器', + type: 'RichEditor', + props: { + style: { width: '100%' }, + }, + setting: { ...Setting, ...FieldSetting } +}; diff --git a/website/src/editor/FormEditor/components/RichEditor/index.less b/website/src/editor/FormEditor/components/RichEditor/index.less new file mode 100644 index 0000000..ca8a2d2 --- /dev/null +++ b/website/src/editor/FormEditor/components/RichEditor/index.less @@ -0,0 +1,9 @@ +.custom-editor { + width: 100%; + height: 200px; + + .ql-container.ql-snow { + height: auto; + border: none; + } +} \ No newline at end of file diff --git a/website/src/editor/FormEditor/components/RichEditor/index.tsx b/website/src/editor/FormEditor/components/RichEditor/index.tsx new file mode 100644 index 0000000..67e715f --- /dev/null +++ b/website/src/editor/FormEditor/components/RichEditor/index.tsx @@ -0,0 +1,92 @@ +import React, { useEffect, useRef, useState } from 'react'; +import classnames from 'classnames'; +import { Input, Button, ButtonProps } from 'antd'; +import './index.less'; +import Quill from 'quill'; +import 'quill/dist/quill.snow.css'; +import { CustomModal, CommonFormProps } from '@simpleform/editor'; + +const RichEditor: React.FC> = (props) => { + + const { + value, + onChange, + } = props; + + const editorRef = useRef(); + const parentRef = useRef(null); + + useEffect(() => { + if (!parentRef.current) return; + const quill = new Quill(parentRef.current, { + theme: 'snow' + }); + quill.on('text-change', function (_delta, _oldDelta, source) { + if (source == 'user') { + const htmlStr = quill.root.innerHTML; + onChange && onChange(htmlStr); + } + }); + quill.clipboard.dangerouslyPasteHTML(value || ''); + const index = quill.getLength(); + quill.setSelection(index, Quill.sources.USER); // 需要设置USER否则移动端光标会异常 + editorRef.current = quill; + }, []); + + return ( +
+
+
+ ); +}; + +export default RichEditor; + +// 按钮弹窗富文本 +export const RichEditorModalBtn = (props: RichEditorProps & ButtonProps) => { + + const { + value, + onChange, + className + } = props; + + const [content, setContent] = useState(); + + useEffect(() => { + setContent(value); + }, [value]); + + const handleOk = (closeModal: () => void) => { + closeModal(); + onChange && onChange(content); + }; + + const richOnChange = (val?: string) => { + setContent(val); + }; + + const cls = classnames(className, 'rich-editor-modal'); + + return ( + ( +
+ + +
+ ) + }> + +
+ ); +}; diff --git a/website/src/editor/FormEditor/components/RichEditor/setting.ts b/website/src/editor/FormEditor/components/RichEditor/setting.ts new file mode 100644 index 0000000..06daa34 --- /dev/null +++ b/website/src/editor/FormEditor/components/RichEditor/setting.ts @@ -0,0 +1,43 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + } +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + } +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required', 'pattern', 'max', 'min'], + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/components/RichText/config.ts b/website/src/editor/FormEditor/components/RichText/config.ts new file mode 100644 index 0000000..a2fe93e --- /dev/null +++ b/website/src/editor/FormEditor/components/RichText/config.ts @@ -0,0 +1,12 @@ +import Setting from './setting'; + +export default { + panel: { + icon: 'html-text', + label: '富文本', + nonform: true, + }, + type: 'RichText', + initialValue: '

自定义富文本

', + setting: { ...Setting } +}; diff --git a/website/src/editor/FormEditor/components/RichText/index.tsx b/website/src/editor/FormEditor/components/RichText/index.tsx new file mode 100644 index 0000000..0ff32c6 --- /dev/null +++ b/website/src/editor/FormEditor/components/RichText/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { CommonFormProps } from '@simpleform/editor'; +// 按钮弹窗富文本 +export const RichText = React.forwardRef>((props, ref) => { + + const { + value, + onChange, + _options, + ...rest + } = props; + return ( +
+ ); +}); diff --git a/website/src/editor/FormEditor/components/RichText/setting.ts b/website/src/editor/FormEditor/components/RichText/setting.ts new file mode 100644 index 0000000..1eb2cf6 --- /dev/null +++ b/website/src/editor/FormEditor/components/RichText/setting.ts @@ -0,0 +1,24 @@ +const baseSetting = [ + { + label: '默认值', + name: 'initialValue', + type: 'RichEditorModalBtn', + }, +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/components/config.ts b/website/src/editor/FormEditor/components/config.ts new file mode 100644 index 0000000..215beee --- /dev/null +++ b/website/src/editor/FormEditor/components/config.ts @@ -0,0 +1,22 @@ +import RichEditor from "../components/RichEditor/config"; +import RichText from "../components/RichText/config"; +import FileUpload from '../components/FileUpload/config'; +import ImageUpload from '../components/ImageUpload/config'; +import Grid from '../components/Grid/config'; +import LayoutTable from '../components/LayoutTable/config'; +import FormTable from "../components/FormTable/config"; +import Example from './Example/config'; +import Divider from './Divider/config'; + +// 编辑器组件配置项 +export default { + Grid, + LayoutTable, + FormTable, + FileUpload, + ImageUpload, + RichText, + RichEditor, + Divider, + Example, +}; diff --git a/website/src/editor/FormEditor/components/index.ts b/website/src/editor/FormEditor/components/index.ts new file mode 100644 index 0000000..9fe8945 --- /dev/null +++ b/website/src/editor/FormEditor/components/index.ts @@ -0,0 +1,76 @@ +import { + Input, + InputNumber, + Checkbox, + DatePicker, + Mentions, + Radio, + Rate, + Select, + Slider, + Switch, + TimePicker, + Cascader, + Alert, +} from 'antd'; +import FileUpload from './FileUpload'; +import RichEditor, { RichEditorModalBtn } from './RichEditor'; +import { RichText } from './RichText'; +import FormTable from './FormTable'; +import Grid from './Grid'; +import LayoutTable from './LayoutTable'; +import { CustomDivider } from './Divider'; +import ImageUpload from './ImageUpload'; +import { + ValidatorSetting, + CodeTextArea, + DataSetting, + CheckboxWithRules, + bindRequest, + CustomFormRenderProps +} from '@simpleform/editor'; + +// 注册组件 +const widgets = { + // ui库组件 + "Input": Input, // 输入控件 + "Input.TextArea": Input.TextArea, // 输入文本域 + "Input.Password": Input.Password, // 输入密码组件 + "Input.Search": Input.Search, // 输入搜索组件 + "InputNumber": InputNumber, // 数字输入控件 + "Mentions": Mentions, // 携带@提示的输入控件 + "Mentions.Option": Mentions.Option, // 提示控件的option + "Checkbox": Checkbox, // 多选组件 + 'Checkbox.Group': bindRequest(Checkbox.Group), // 多选列表组件 + "Radio": Radio, // 单选组件 + "Radio.Group": bindRequest(Radio.Group), // 单选列表组件 + "Radio.Button": Radio.Button, // 单选按钮组件 + "DatePicker": DatePicker, // 日期控件 + "DatePicker.RangePicker": DatePicker.RangePicker, // 日期范围控件 + "Rate": Rate, // 星星评分控件 + "Select": bindRequest(Select), // 选择控件 + "Select.Option": Select.Option, // 选择的选项 + "Slider": Slider, // 滑动输入项 + "Switch": Switch, // 切换组件 + "TimePicker": TimePicker, // 时分秒控件 + "TimePicker.RangePicker": TimePicker.RangePicker, // 时分秒范围控件 + "Cascader": bindRequest(Cascader), + "Alert": Alert, // 提示组件 + "Divider": bindRequest(CustomDivider), // 分割线组件 + // 自定义组件 + "FileUpload": FileUpload, // 文件上传 + "ImageUpload": ImageUpload, // 图片上传 + RichEditor, + RichText, + RichEditorModalBtn, + FormTable, + LayoutTable, + Grid, + // 默认导出组件 + CodeTextArea, + ValidatorSetting, + DataSetting, + CheckboxWithRules +} as CustomFormRenderProps['components']; + +export default widgets; diff --git a/website/src/editor/FormEditor/config/index.ts b/website/src/editor/FormEditor/config/index.ts deleted file mode 100644 index e9ecc59..0000000 --- a/website/src/editor/FormEditor/config/index.ts +++ /dev/null @@ -1,6 +0,0 @@ - -import GroupExample from './example'; - -export default { - "example": GroupExample, -}; diff --git a/website/src/editor/FormEditor/editorConfig/Alert/index.ts b/website/src/editor/FormEditor/editorConfig/Alert/index.ts new file mode 100644 index 0000000..fe19e12 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/Alert/index.ts @@ -0,0 +1,17 @@ +import Setting from './setting'; + +export default { + panel: { + icon: 'alert', + label: '提示', + nonform: true, + }, + type: 'Alert', + labelWidth: 'auto', + props: { + style: { width: '100%' }, + message: '警告提示', + description: '警告提示描述' + }, + setting: { ...Setting } +}; diff --git a/website/src/editor/FormEditor/editorConfig/Alert/setting.ts b/website/src/editor/FormEditor/editorConfig/Alert/setting.ts new file mode 100644 index 0000000..23e55ed --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/Alert/setting.ts @@ -0,0 +1,58 @@ +const baseSetting = [ + { + label: '提示', + name: 'props.message', + type: 'Input', + }, + { + label: '提示描述', + name: 'props.description', + type: 'Input', + }, + { + label: "样式", + name: 'props.type', + type: "Radio.Group", + initialValue: 'success', + props: { + style: { width: '100%' }, + options: [ + { label: 'success', value: 'success' }, + { label: 'info', value: 'info' }, + { label: 'warning', value: 'warning' }, + { label: 'error', value: 'error' }, + ] + } + } +]; + +const operationSetting = [ + { + type: 'CheckboxWithRules', + name: 'hidden', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.showIcon', + type: 'Checkbox', + inline: true, + compact: true, + children: '显示图标' + }, + { + name: 'props.closable', + type: 'Checkbox', + inline: true, + compact: true, + children: '可关闭' + } +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/editorConfig/Cascader/index.ts b/website/src/editor/FormEditor/editorConfig/Cascader/index.ts new file mode 100644 index 0000000..c376d60 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/Cascader/index.ts @@ -0,0 +1,49 @@ +import Setting from './setting'; +import FieldSetting from '../fieldSetting'; + +export default { + panel: { + icon: 'cascader-field', + label: '级联选择器', + }, + label: '级联选择器', + type: 'Cascader', + props: { + style: { width: '100%' }, + 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', + }, + ], + }, + ], + }, + ] + }, + setting: { ...Setting, ...FieldSetting }, +}; diff --git a/website/src/editor/FormEditor/editorConfig/Cascader/setting.ts b/website/src/editor/FormEditor/editorConfig/Cascader/setting.ts new file mode 100644 index 0000000..78af75b --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/Cascader/setting.ts @@ -0,0 +1,77 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: '默认值', + name: 'initialValue', + type: 'CodeTextArea', + }, + { + name: 'props.options', + type: 'DataSetting', + label: '选项数据', + props: { + includes: ['json', 'request', 'dynamic'] + } + }, +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + }, + { + name: 'props.allowClear', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '可清除' + }, + { + name: 'props.multiple', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '多选' + }, + { + name: 'props.showSearch', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '可搜索' + } +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required'] + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/editorConfig/CheckboxGroup/index.ts b/website/src/editor/FormEditor/editorConfig/CheckboxGroup/index.ts new file mode 100644 index 0000000..6964f3c --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/CheckboxGroup/index.ts @@ -0,0 +1,16 @@ +import Setting from './setting'; +import FieldSetting from '../fieldSetting'; + +export default { + panel: { + icon: 'checkbox-field', + label: '多选框', + }, + label: '多选框', + type: 'Checkbox.Group', + valueSetter: "{{(value)=> (value instanceof Array ? value : [])}}", + props: { + options: [{ label: '选项1', value: '1' }, { label: '选项2', value: '2' }] + }, + setting: { ...Setting, ...FieldSetting }, +}; diff --git a/website/src/editor/FormEditor/editorConfig/CheckboxGroup/setting.ts b/website/src/editor/FormEditor/editorConfig/CheckboxGroup/setting.ts new file mode 100644 index 0000000..abbc844 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/CheckboxGroup/setting.ts @@ -0,0 +1,55 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: '默认值', + name: 'initialValue', + type: 'CodeTextArea', + }, + { + name: 'props.options', + type: 'DataSetting', + label: '选项数据', + props: { + } + } +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + }, +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required'] + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/editorConfig/DatePicker/index.ts b/website/src/editor/FormEditor/editorConfig/DatePicker/index.ts new file mode 100644 index 0000000..8ec2d53 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/DatePicker/index.ts @@ -0,0 +1,17 @@ +import Setting from './setting'; +import FieldSetting from '../fieldSetting'; + +export default { + panel: { + icon: 'date-field', + label: '日期选择器', + }, + label: '日期选择器', + valueSetter: "{{(value)=> (typeof value === 'string' ? dayjs(value) : undefined)}}", + valueGetter: "{{(value) => (dayjs.isDayjs(value) ? value.format(formvalues.props && formvalues.props.format || 'YYYY-MM-DD') : undefined)}}", + type: 'DatePicker', + props: { + style: { maxWidth: '300px', width: '100%' }, + }, + setting: { ...Setting, ...FieldSetting }, +}; diff --git a/website/src/editor/FormEditor/editorConfig/DatePicker/setting.ts b/website/src/editor/FormEditor/editorConfig/DatePicker/setting.ts new file mode 100644 index 0000000..6e1a053 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/DatePicker/setting.ts @@ -0,0 +1,114 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: '默认值', + name: 'initialValue', + type: 'DatePicker', + valueSetter: "{{(value)=> (typeof value === 'string' ? dayjs(value) : undefined)}}", + valueGetter: "{{(value) => (dayjs.isDayjs(value) ? value.format(formvalues.props && formvalues.props.format || 'YYYY-MM-DD') : undefined)}}", + props: { + picker: "{{formvalues.props && formvalues.props.picker}}", + format: "{{formvalues.props && formvalues.props.format}}", + } + }, + { + label: '占位字符', + name: 'props.placeholder', + type: 'Input', + initialValue: '请输入' + }, + { + label: "选择器类型", + name: 'props.picker', + type: "Select", + initialValue: 'date', + props: { + style: { width: '100%' }, + options: [ + { label: '日', value: 'date' }, + { label: '周', value: 'week' }, + { label: '月', value: 'month' }, + { label: '季度', value: 'quarter' }, + { label: '年', value: 'year' }, + ] + } + }, + { + label: "显示格式", + name: 'props.format', + type: "Select", + initialValue: 'YYYY-MM-DD', + props: { + style: { width: '100%' }, + options: [ + { label: '年月日', value: 'YYYY-MM-DD' }, + { label: '年月', value: 'YYYY-MM' }, + { label: '月日', value: 'MM-DD' }, + { label: '年', value: 'YYYY' }, + { label: '月', value: 'MM' }, + { label: '日', value: 'DD' }, + ] + } + }, + { + label: "尺寸", + name: 'props.size', + type: "Radio.Group", + initialValue: 'middle', + props: { + style: { width: '100%' }, + options: [ + { label: '大', value: 'large' }, + { label: '中', value: 'middle' }, + { label: '小', value: 'small' } + ] + } + } +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + }, + { + name: 'props.allowClear', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '可清除' + } +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required'] + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/editorConfig/DatePickerRangePicker/index.ts b/website/src/editor/FormEditor/editorConfig/DatePickerRangePicker/index.ts new file mode 100644 index 0000000..30e1b2d --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/DatePickerRangePicker/index.ts @@ -0,0 +1,17 @@ +import Setting from './setting'; +import FieldSetting from '../fieldSetting'; + +export default { + panel: { + icon: 'date-field', + label: '日期范围', + }, + label: '日期范围', + valueSetter: "{{(value)=> value instanceof Array ? value.map((item) => typeof item === 'string' ? dayjs(item) : undefined) : undefined}}", + valueGetter: "{{(value) => value instanceof Array ? value.map((item) => dayjs.isDayjs(item) ? item.format(formvalues.props && formvalues.props.format || 'YYYY-MM-DD') : undefined) : undefined}}", + type: 'DatePicker.RangePicker', + props: { + style: { maxWidth: '300px', width: '100%' }, + }, + setting: { ...Setting, ...FieldSetting }, +}; diff --git a/website/src/editor/FormEditor/editorConfig/DatePickerRangePicker/setting.ts b/website/src/editor/FormEditor/editorConfig/DatePickerRangePicker/setting.ts new file mode 100644 index 0000000..befa072 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/DatePickerRangePicker/setting.ts @@ -0,0 +1,114 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: '默认值', + name: 'initialValue', + type: 'DatePicker.RangePicker', + valueSetter: "{{(value)=> value instanceof Array ? value.map((item) => typeof item === 'string' ? dayjs(item) : undefined) : undefined}}", + valueGetter: "{{(value) => value instanceof Array ? value.map((item) => dayjs.isDayjs(item) ? item.format(formvalues.props && formvalues.props.format || 'YYYY-MM-DD') : undefined) : undefined}}", + props: { + picker: "{{formvalues.props && formvalues.props.picker}}", + format: "{{formvalues.props && formvalues.props.format}}", + } + }, + { + label: '占位字符', + name: 'props.placeholder', + type: 'Input', + initialValue: '请输入' + }, + { + label: "选择器类型", + name: 'props.picker', + type: "Select", + initialValue: 'date', + props: { + style: { width: '100%' }, + options: [ + { label: '日', value: 'date' }, + { label: '周', value: 'week' }, + { label: '月', value: 'month' }, + { label: '季度', value: 'quarter' }, + { label: '年', value: 'year' }, + ] + } + }, + { + label: "显示格式", + name: 'props.format', + type: "Select", + initialValue: 'YYYY-MM-DD', + props: { + style: { width: '100%' }, + options: [ + { label: '年月日', value: 'YYYY-MM-DD' }, + { label: '年月', value: 'YYYY-MM' }, + { label: '月日', value: 'MM-DD' }, + { label: '年', value: 'YYYY' }, + { label: '月', value: 'MM' }, + { label: '日', value: 'DD' }, + ] + } + }, + { + label: "尺寸", + name: 'props.size', + type: "Radio.Group", + initialValue: 'middle', + props: { + style: { width: '100%' }, + options: [ + { label: '大', value: 'large' }, + { label: '中', value: 'middle' }, + { label: '小', value: 'small' } + ] + } + } +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + }, + { + name: 'props.allowClear', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '可清除' + } +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required'] + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/editorConfig/Input/index.ts b/website/src/editor/FormEditor/editorConfig/Input/index.ts new file mode 100644 index 0000000..85a86f7 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/Input/index.ts @@ -0,0 +1,12 @@ +import Setting from './setting'; +import FieldSetting from '../fieldSetting'; + +export default { + panel: { + icon: 'text-field', + label: '输入框', + }, + label: '输入框', + type: 'Input', + setting: { ...Setting, ...FieldSetting }, +}; diff --git a/website/src/editor/FormEditor/editorConfig/Input/setting.ts b/website/src/editor/FormEditor/editorConfig/Input/setting.ts new file mode 100644 index 0000000..921e436 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/Input/setting.ts @@ -0,0 +1,96 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: "输入框类型", + name: 'type', + type: "Select", + initialValue: "Input", + props: { + style: { width: '100%' }, + options: [ + { label: '单行输入', value: 'Input' }, + { label: '多行输入', value: 'Input.TextArea' }, + { label: '数字输入', value: 'InputNumber' }, + { label: '密码输入', value: 'Input.Password' }, + ] + } + }, + { + label: '默认值', + name: 'initialValue', + type: "{{formvalues && formvalues.type ? formvalues.type : 'Input'}}", + }, + { + label: '占位字符', + name: 'props.placeholder', + type: 'Input', + initialValue: '请输入' + }, + { + label: '最大输入字符数', + name: 'props.maxLength', + type: 'InputNumber', + initialValue: 30 + }, + { + label: "尺寸", + name: 'props.size', + type: "Radio.Group", + initialValue: 'middle', + props: { + style: { width: '100%' }, + options: [ + { label: '大', value: 'large' }, + { label: '中', value: 'middle' }, + { label: '小', value: 'small' } + ] + } + } +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + }, + { + name: 'props.allowClear', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '可清除' + } +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required', 'pattern', 'max', 'min'], + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/editorConfig/RadioGroup/index.ts b/website/src/editor/FormEditor/editorConfig/RadioGroup/index.ts new file mode 100644 index 0000000..45e9da7 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/RadioGroup/index.ts @@ -0,0 +1,15 @@ +import Setting from './setting'; +import FieldSetting from '../fieldSetting'; + +export default { + panel: { + icon: 'radio-field', + label: '单选框', + }, + label: '单选框', + type: 'Radio.Group', + props: { + options: [{ label: '选项1', value: '1' }, { label: '选项2', value: '2' }] + }, + setting: { ...Setting, ...FieldSetting }, +}; diff --git a/website/src/editor/FormEditor/editorConfig/RadioGroup/setting.ts b/website/src/editor/FormEditor/editorConfig/RadioGroup/setting.ts new file mode 100644 index 0000000..dd074d9 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/RadioGroup/setting.ts @@ -0,0 +1,82 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: '默认值', + name: 'initialValue', + type: 'Input', + }, + { + label: '选项数据', + name: 'props.options', + type: 'DataSetting', + props: { + } + }, + { + label: "样式", + name: 'props.optionType', + type: "Radio.Group", + initialValue: 'default', + props: { + style: { width: '100%' }, + options: [ + { label: '默认', value: 'default' }, + { label: '按钮', value: 'button' } + ] + } + }, + { + label: "尺寸", + name: 'props.size', + type: "Radio.Group", + initialValue: 'middle', + props: { + style: { width: '100%' }, + options: [ + { label: '大', value: 'large' }, + { label: '中', value: 'middle' }, + { label: '小', value: 'small' } + ] + } + } +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + } +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required'], + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/editorConfig/Rate/index.ts b/website/src/editor/FormEditor/editorConfig/Rate/index.ts new file mode 100644 index 0000000..35c9f28 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/Rate/index.ts @@ -0,0 +1,14 @@ +import Setting from './setting'; +import FieldSetting from '../fieldSetting'; + +export default { + panel: { + icon: 'rate-field', + label: '评分', + }, + label: '评分', + type: 'Rate', + props: { + }, + setting: { ...Setting, ...FieldSetting }, +}; diff --git a/website/src/editor/FormEditor/editorConfig/Rate/setting.ts b/website/src/editor/FormEditor/editorConfig/Rate/setting.ts new file mode 100644 index 0000000..7777cd6 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/Rate/setting.ts @@ -0,0 +1,68 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: '默认值', + name: 'initialValue', + type: 'InputNumber', + }, + { + label: 'star总数', + name: 'props.count', + type: 'InputNumber', + initialValue: 5 + } +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + }, + { + name: 'props.allowClear', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '可清除' + }, + { + name: 'props.allowHalf', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '可半选' + } +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required', 'max', 'min'], + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/editorConfig/Select/index.ts b/website/src/editor/FormEditor/editorConfig/Select/index.ts new file mode 100644 index 0000000..247323e --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/Select/index.ts @@ -0,0 +1,16 @@ +import Setting from './setting'; +import FieldSetting from '../fieldSetting'; + +export default { + panel: { + icon: 'select-field', + label: '下拉框', + }, + label: '下拉框', + type: 'Select', + props: { + style: { width: "100%" }, + options: [{ label: '选项1', value: '1' }, { label: '选项2', value: '2' }] + }, + setting: { ...Setting, ...FieldSetting }, +}; diff --git a/website/src/editor/FormEditor/editorConfig/Select/setting.ts b/website/src/editor/FormEditor/editorConfig/Select/setting.ts new file mode 100644 index 0000000..afcf887 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/Select/setting.ts @@ -0,0 +1,109 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: '默认值', + name: 'initialValue', + type: 'CodeTextArea', + }, + { + label: '占位字符', + name: 'props.placeholder', + type: 'Input', + initialValue: '请输入' + }, + { + name: 'props.options', + type: 'DataSetting', + label: '选项数据', + props: { + } + }, + { + label: '选择模式', + name: 'props.mode', + type: "Select", + props: { + style: { width: '100%' }, + allowClear: true, + options: [ + { label: '多选', value: 'multiple' }, + { label: '标签', value: 'tags' } + ] + } + }, + { + label: '标签最大数量', + name: 'props.maxTagCount', + type: 'InputNumber', + hidden: "{{formvalues && formvalues.props && formvalues.props.mode !== 'tags'}}", + initialValue: 10 + }, + { + label: "尺寸", + name: 'props.size', + type: "Radio.Group", + initialValue: 'middle', + props: { + style: { width: '100%' }, + options: [ + { label: '大', value: 'large' }, + { label: '中', value: 'middle' }, + { label: '小', value: 'small' } + ] + } + }, +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + }, + { + name: 'props.allowClear', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '可清除' + }, + { + name: 'props.showSearch', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '可搜索' + }, +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required'], + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/editorConfig/Slider/index.ts b/website/src/editor/FormEditor/editorConfig/Slider/index.ts new file mode 100644 index 0000000..2497038 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/Slider/index.ts @@ -0,0 +1,15 @@ +import Setting from './setting'; +import FieldSetting from '../fieldSetting'; + +export default { + panel: { + icon: 'slider-field', + label: '滑动输入', + }, + label: '滑动输入', + type: 'Slider', + props: { + style: { width: '100%' } + }, + setting: { ...Setting, ...FieldSetting }, +}; diff --git a/website/src/editor/FormEditor/editorConfig/Slider/setting.ts b/website/src/editor/FormEditor/editorConfig/Slider/setting.ts new file mode 100644 index 0000000..5df4290 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/Slider/setting.ts @@ -0,0 +1,80 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: '默认值', + name: 'initialValue', + type: 'InputNumber', + }, + { + label: '最小值', + name: 'props.min', + type: 'InputNumber', + initialValue: 0 + }, + { + label: '最大值', + name: 'props.max', + type: 'InputNumber', + initialValue: 100 + }, + { + label: '步长', + name: 'props.step', + type: 'InputNumber', + initialValue: 1 + } +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + }, + { + name: 'props.reverse', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '反向' + }, + { + name: 'props.vertical', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '垂直' + } +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required', 'max', 'min'], + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/editorConfig/Switch/index.ts b/website/src/editor/FormEditor/editorConfig/Switch/index.ts new file mode 100644 index 0000000..8363d97 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/Switch/index.ts @@ -0,0 +1,15 @@ +import Setting from './setting'; +import FieldSetting from '../fieldSetting'; + +export default { + panel: { + icon: 'switch-field', + label: '开关', + }, + label: '开关', + type: 'Switch', + valueProp: 'checked', + props: { + }, + setting: { ...Setting, ...FieldSetting }, +}; diff --git a/website/src/editor/FormEditor/editorConfig/Switch/setting.ts b/website/src/editor/FormEditor/editorConfig/Switch/setting.ts new file mode 100644 index 0000000..3e36d66 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/Switch/setting.ts @@ -0,0 +1,63 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: '默认值', + name: 'initialValue', + type: 'Switch', + valueProp: 'checked', + }, + { + label: "尺寸", + name: 'props.size', + type: "Radio.Group", + initialValue: 'middle', + props: { + style: { width: '100%' }, + options: [ + { label: '大', value: 'large' }, + { label: '中', value: 'middle' }, + { label: '小', value: 'small' } + ] + } + } +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + } +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required'], + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/editorConfig/TimePicker/index.ts b/website/src/editor/FormEditor/editorConfig/TimePicker/index.ts new file mode 100644 index 0000000..5c17dba --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/TimePicker/index.ts @@ -0,0 +1,17 @@ +import Setting from './setting'; +import FieldSetting from '../fieldSetting'; + +export default { + panel: { + icon: 'time-field', + label: '时间选择器', + }, + label: '时间选择器', + type: 'TimePicker', + valueSetter: "{{(value) => typeof value === 'string' ? dayjs(value, 'HH:mm:ss') : undefined}}", + valueGetter: "{{(value) => dayjs.isDayjs(value) ? value.format(formvalues.props && formvalues.props.format || 'HH:mm:ss') : undefined}}", + props: { + style: { maxWidth: '300px', width: '100%' }, + }, + setting: { ...Setting, ...FieldSetting }, +}; diff --git a/website/src/editor/FormEditor/editorConfig/TimePicker/setting.ts b/website/src/editor/FormEditor/editorConfig/TimePicker/setting.ts new file mode 100644 index 0000000..22453d9 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/TimePicker/setting.ts @@ -0,0 +1,106 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: '默认值', + name: 'initialValue', + type: 'TimePicker', + valueSetter: "{{(value) => typeof value === 'string' ? dayjs(value, 'HH:mm:ss') : undefined}}", + valueGetter: "{{(value) => dayjs.isDayjs(value) ? value.format(formvalues.props && formvalues.props.format || 'HH:mm:ss') : undefined}}", + props: { + format: "{{formvalues.props && formvalues.props.format}}", + use12Hours: "{{formvalues.props && formvalues.props.use12Hours}}", + } + }, + { + label: '占位字符', + name: 'props.placeholder', + type: 'Input', + initialValue: '请输入' + }, + { + label: "显示格式", + name: 'props.format', + type: "Select", + initialValue: 'HH:mm:ss', + props: { + style: { width: '100%' }, + options: [ + { label: '时分秒', value: 'HH:mm:ss' }, + { label: '时分', value: 'HH:mm' }, + { label: '分秒', value: 'mm:ss' }, + { label: '小时', value: 'HH' }, + { label: '分钟', value: 'mm' }, + { label: '秒', value: 'ss' }, + ] + } + }, + { + label: "尺寸", + name: 'props.size', + type: "Radio.Group", + initialValue: 'middle', + props: { + style: { width: '100%' }, + options: [ + { label: '大', value: 'large' }, + { label: '中', value: 'middle' }, + { label: '小', value: 'small' } + ] + } + }, +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + }, + { + name: 'props.allowClear', + type: 'CheckboxWithRules', + inline: true, + compact: true, + initialValue: true, + children: '可清除' + }, + { + name: 'props.use12Hours', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '12小时制' + } +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required'], + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/editorConfig/TimePickerRangePicker/index.ts b/website/src/editor/FormEditor/editorConfig/TimePickerRangePicker/index.ts new file mode 100644 index 0000000..7ca13bf --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/TimePickerRangePicker/index.ts @@ -0,0 +1,17 @@ +import Setting from './setting'; +import FieldSetting from '../fieldSetting'; + +export default { + panel: { + icon: 'time-field', + label: '时间范围', + }, + label: '时间范围', + type: 'TimePicker.RangePicker', + valueSetter: "{{(value)=> value instanceof Array ? value.map((item) => typeof item === 'string' ? dayjs(item, 'HH:mm:ss') : undefined) : undefined}}", + valueGetter: "{{(value) => value instanceof Array ? value.map((item) => dayjs.isDayjs(item) ? item.format(formvalues.props && formvalues.props.format || 'HH:mm:ss') : undefined) : undefined}}", + props: { + style: { maxWidth: '300px', width: '100%' }, + }, + setting: { ...Setting, ...FieldSetting }, +}; diff --git a/website/src/editor/FormEditor/editorConfig/TimePickerRangePicker/setting.ts b/website/src/editor/FormEditor/editorConfig/TimePickerRangePicker/setting.ts new file mode 100644 index 0000000..0ad6c91 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/TimePickerRangePicker/setting.ts @@ -0,0 +1,106 @@ +const baseSetting = [ + { + label: '字段名', + name: 'name', + type: 'Input' + }, + { + label: '默认值', + name: 'initialValue', + type: 'TimePicker.RangePicker', + valueSetter: "{{(value)=> value instanceof Array ? value.map((item) => typeof item === 'string' ? dayjs(item, 'HH:mm:ss') : undefined) : undefined}}", + valueGetter: "{{(value) => value instanceof Array ? value.map((item) => dayjs.isDayjs(item) ? item.format(formvalues.props && formvalues.props.format || 'HH:mm:ss') : undefined) : undefined}}", + props: { + format: "{{formvalues.props && formvalues.props.format}}", + use12Hours: "{{formvalues.props && formvalues.props.use12Hours}}", + } + }, + { + label: '占位字符', + name: 'props.placeholder', + type: 'Input', + initialValue: '请输入' + }, + { + label: "显示格式", + name: 'props.format', + type: "Select", + initialValue: 'HH:mm:ss', + props: { + style: { width: '100%' }, + options: [ + { label: '时分秒', value: 'HH:mm:ss' }, + { label: '时分', value: 'HH:mm' }, + { label: '分秒', value: 'mm:ss' }, + { label: '小时', value: 'HH' }, + { label: '分钟', value: 'mm' }, + { label: '秒', value: 'ss' }, + ] + } + }, + { + label: "尺寸", + name: 'props.size', + type: "Radio.Group", + initialValue: 'middle', + props: { + style: { width: '100%' }, + options: [ + { label: '大', value: 'large' }, + { label: '中', value: 'middle' }, + { label: '小', value: 'small' } + ] + } + } +]; + +const operationSetting = [ + { + name: 'hidden', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '隐藏' + }, + { + name: 'props.disabled', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '禁用' + }, + { + name: 'props.allowClear', + type: 'CheckboxWithRules', + inline: true, + compact: true, + initialValue: true, + children: '可清除' + }, + { + name: 'props.use12Hours', + type: 'CheckboxWithRules', + inline: true, + compact: true, + children: '12小时制' + } +]; + +const rulesSetting = [ + { + name: 'rules', + type: 'ValidatorSetting', + compact: true, + props: { + includes: ['required'], + } + }, +]; + +const setting = { + '基础属性': baseSetting, + '操作属性': operationSetting, + '校验规则': rulesSetting, +}; + +export default setting; diff --git a/website/src/editor/FormEditor/editorConfig/fieldSetting.ts b/website/src/editor/FormEditor/editorConfig/fieldSetting.ts new file mode 100644 index 0000000..4d77301 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/fieldSetting.ts @@ -0,0 +1,75 @@ +// 默认的表单域显示组件的属性 +const baseSetting = [ + { + label: '标签名称', + name: 'label', + type: 'Input' + }, + { + label: "标签布局", + name: 'layout', + type: "Radio.Group", + initialValue: "horizontal", + props: { + style: { width: '100%' }, + options: [ + { key: 'horizontal', value: "horizontal", label: "水平" }, + { key: 'vertical', value: "vertical", label: "垂直" }, + ] + } + }, + { + label: '标签水平排列', + name: 'labelAlign', + type: "Select", + props: { + style: { width: '100%' }, + allowClear: true, + options: [ + { label: '左边对齐', value: 'left' }, + { label: '居中', value: 'center' }, + { label: '右边对齐', value: 'right' }, + ] + } + }, + { + label: '标签间距', + name: 'gutter', + type: 'InputNumber', + props: { + min: 0, + max: 300 + } + }, + { + label: '标签宽度', + name: 'labelWidth', + type: 'InputNumber', + props: { + min: 0, + max: 300 + } + }, + { + label: '后缀', + name: 'suffix', + type: 'Input' + }, + { + label: '描述', + name: 'footer', + type: 'Input' + }, + { + label: '携带冒号', + name: 'colon', + type: 'Switch', + valueProp: 'checked', + } +]; + +const setting = { + '公共属性': baseSetting +}; + +export default setting; diff --git a/website/src/editor/FormEditor/editorConfig/index.ts b/website/src/editor/FormEditor/editorConfig/index.ts new file mode 100644 index 0000000..b7291d8 --- /dev/null +++ b/website/src/editor/FormEditor/editorConfig/index.ts @@ -0,0 +1,35 @@ +import Input from './Input'; +import RadioGroup from './RadioGroup'; +import CheckboxGroup from './CheckboxGroup'; +import Select from './Select'; +import Switch from './Switch'; +import TimePicker from './TimePicker'; +import TimePickerRangePicker from './TimePickerRangePicker'; +import DatePicker from './DatePicker'; +import DatePickerRangePicker from './DatePickerRangePicker'; +import Slider from './Slider'; +import Rate from './Rate'; +import Cascader from './Cascader'; +import Alert from './Alert'; +import FieldSetting from './fieldSetting'; +import ComponentsConfig from '../components/config'; + +// 编辑器组件的配置项 +export default { + Alert, + Input, + "Radio.Group": RadioGroup, + "Checkbox.Group": CheckboxGroup, + Select, + Switch, + TimePicker, + "TimePicker.RangePicker": TimePickerRangePicker, + DatePicker, + "DatePicker.RangePicker": DatePickerRangePicker, + Slider, + Rate, + Cascader, + ...ComponentsConfig +}; + +export { FieldSetting }; diff --git a/website/src/editor/FormEditor/index.tsx b/website/src/editor/FormEditor/index.tsx index f7a4fd6..b2c835c 100644 --- a/website/src/editor/FormEditor/index.tsx +++ b/website/src/editor/FormEditor/index.tsx @@ -1,15 +1,22 @@ import { Col, Row } from 'antd'; -import React from 'react'; -import { EditorPanel, EditorProvider, EditorSetting, EditorTools, EditorView } from '@simpleform/editor'; +import React, { CSSProperties } from 'react'; +import { EditorPanel, EditorProvider, EditorProviderProps, EditorSetting, EditorTools, EditorView } from '@simpleform/editor'; import '@simpleform/editor/lib/css/main.css'; -import FormRender from '../FormRender'; -import EditorConfig from './config'; -import panelData from './config/panelData'; -import ImportModal from './template'; +import panelData from './panelData'; +import ImportModal from './ImportTemplate'; +import editorConfig from './editorConfig'; +import renderConfig from './FormRender/defineConfig'; import './index.less'; -const renderTools = (context) => { - return ; +export * from '@simpleform/editor'; + +const renderTools = (editorContext) => { + return ; +}; + +export type EasyFormEditorProps = EditorProviderProps & { + className?: string; + style?: CSSProperties; }; const FormEditor = () => { @@ -17,8 +24,8 @@ const FormEditor = () => { return ( diff --git a/website/src/editor/FormEditor/config/panelData.ts b/website/src/editor/FormEditor/panelData.ts similarity index 87% rename from website/src/editor/FormEditor/config/panelData.ts rename to website/src/editor/FormEditor/panelData.ts index 8e0019b..a9586fe 100644 --- a/website/src/editor/FormEditor/config/panelData.ts +++ b/website/src/editor/FormEditor/panelData.ts @@ -1,5 +1,5 @@ const panelData = { - '布局组件': ['Grid', 'Divider', 'Alert'], + '布局组件': ['LayoutTable', 'Grid', 'Divider', 'Alert'], '控件组合': ['FormTable'], '基础控件': [ "Input", diff --git a/website/src/editor/FormEditor/template/data/index.ts b/website/src/editor/FormEditor/template/data/index.ts deleted file mode 100644 index b722772..0000000 --- a/website/src/editor/FormEditor/template/data/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import demo from './demo'; - -// 模板列表 -const data = [{ - name: 'demo', - data: demo, -}]; - -export default data; diff --git a/website/src/editor/FormRender/components/index.ts b/website/src/editor/FormRender/components/index.ts deleted file mode 100644 index 7c5ddeb..0000000 --- a/website/src/editor/FormRender/components/index.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import Exmaple from './example/index'; - -// 注册组件 -export default { - // 自定义控件组demo - "example": Exmaple, -}; diff --git a/website/src/editor/FormRender/index.tsx b/website/src/editor/FormRender/index.tsx deleted file mode 100644 index 8fdbf53..0000000 --- a/website/src/editor/FormRender/index.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import { FormRender as DefaultFormRender, FormChildren as DefaultFormChildren, createRequest, CustomFormChildrenProps, CustomFormRenderProps } from '@simpleform/editor'; -import widgets from './components'; -import '@simpleform/editor/lib/css/main.css'; - -export * from '@simpleform/editor'; - -// TODO axios请求配置 -const axiosConfig = { - -}; - -export function FormChildren(props: CustomFormChildrenProps) { - const { components, variables, ...rest } = props; - return ( - - ); -} - -export default function FormRender(props: CustomFormRenderProps) { - const { components, variables, ...rest } = props; - return ( - - ); -} diff --git a/website/src/form/base.tsx b/website/src/form/base.tsx index 33c27c7..f5a45cf 100644 --- a/website/src/form/base.tsx +++ b/website/src/form/base.tsx @@ -19,7 +19,7 @@ export default function Demo() { return Promise.reject(new Error('length is < 2')); } } - + return (

报错:{formRes.error}

diff --git a/website/src/render/FormRender/defineConfig.ts b/website/src/render/FormRender/defineConfig.ts index 45bd9b2..6567e10 100644 --- a/website/src/render/FormRender/defineConfig.ts +++ b/website/src/render/FormRender/defineConfig.ts @@ -13,9 +13,10 @@ import { TreeSelect, } from 'antd'; import dayjs from 'dayjs'; +import { CustomFormRenderProps } from '@simpleform/editor'; // 渲染引擎配置项 -export default { +const defineConfig = { // 组件内的变量 variables: { dayjs @@ -49,4 +50,6 @@ export default { options: { props: { autoComplete: 'off' } } -}; +} as CustomFormRenderProps; + +export default defineConfig;