فهرست منبع

feat: 开放工具栏视图切换工具挂件

lanjianrong 3 سال پیش
والد
کامیت
bcc7ca3075

+ 11 - 2
src/pages/Schema/Base/components/Designable/index.tsx

@@ -11,11 +11,13 @@ import {
   ViewPanel, // 视图布局面板
   SettingsPanel, // 右侧配置表单布局面板
   ComponentTreeWidget, // 组件树渲染器
-  CompositePanel // 左侧组合布局面板
+  CompositePanel, // 左侧组合布局面板
+  ToolbarPanel, // 工具栏布局面板
+  ViewToolsWidget // 视图切换工具挂件
 } from '@designable/react'
 import { SettingsForm } from '@designable/react-settings-form'
 import { createDesigner, GlobalRegistry, Shortcut, KeyCode } from '@designable/core'
-import { ActionsWidget } from './widgets'
+import { ActionsWidget, PreviewWidget, SchemaEditorWidget } from './widgets'
 import { saveSchema } from './service'
 import {
   Form,
@@ -113,6 +115,9 @@ const Designable: FC<DesignableProps> = ({ base, title }) => {
 
         <Workspace id="form">
           <WorkspacePanel>
+            <ToolbarPanel>
+              <ViewToolsWidget use={['DESIGNABLE', 'JSONTREE', 'PREVIEW']} />
+            </ToolbarPanel>
             <ViewportPanel>
               <ViewPanel type="DESIGNABLE">
                 {() => (
@@ -137,6 +142,10 @@ const Designable: FC<DesignableProps> = ({ base, title }) => {
                   />
                 )}
               </ViewPanel>
+              <ViewPanel type="JSONTREE" scrollable={false}>
+                {(tree, onChange) => <SchemaEditorWidget tree={tree} onChange={onChange} />}
+              </ViewPanel>
+              <ViewPanel type="PREVIEW">{tree => <PreviewWidget tree={tree} />}</ViewPanel>
             </ViewportPanel>
           </WorkspacePanel>
         </Workspace>

+ 164 - 0
src/pages/Schema/Base/components/Designable/widgets/MarkupSchemaWidget.tsx

@@ -0,0 +1,164 @@
+import React from 'react'
+import { TreeNode } from '@designable/core'
+import { MonacoInput } from '@designable/react-settings-form'
+import { isEmpty, isPlainObj } from '@formily/shared'
+
+export interface IMarkupSchemaWidgetProps {
+  tree: TreeNode
+}
+
+const transformToMarkupSchemaCode = (tree: TreeNode) => {
+  const printAttribute = (node: TreeNode) => {
+    if (!node) return ''
+    const props = { ...node.props }
+    if (node.depth !== 0) {
+      props.name = node.props.name || node.id
+    }
+    return `${Object.keys(props)
+      .map((key) => {
+        if (
+          key === 'x-designable-id' ||
+          key === 'x-designable-source-name' ||
+          key === '_isJSONSchemaObject' ||
+          key === 'version' ||
+          key === 'type'
+        )
+          return ''
+        const value = props[key]
+        if (isPlainObj(value) && isEmpty(value)) return ''
+        if (typeof value === 'string') return `${key}="${value}"`
+        return `${key}={${JSON.stringify(value)}}`
+      })
+      .join(' ')}`
+  }
+  const printChildren = (node: TreeNode) => {
+    if (!node) return ''
+    return node.children
+      .map((child) => {
+        return printNode(child)
+      })
+      .join('')
+  }
+  const printTag = (node: TreeNode) => {
+    if (node.props.type === 'string') return 'SchemaField.String'
+    if (node.props.type === 'number') return 'SchemaField.Number'
+    if (node.props.type === 'boolean') return 'SchemaField.Boolean'
+    if (node.props.type === 'date') return 'SchemaField.Date'
+    if (node.props.type === 'datetime') return 'SchemaField.DateTime'
+    if (node.props.type === 'array') return 'SchemaField.Array'
+    if (node.props.type === 'object') return 'SchemaField.Object'
+    if (node.props.type === 'void') return 'SchemaField.Void'
+    return 'SchemaField.Markup'
+  }
+  const printNode = (node: TreeNode) => {
+    if (!node) return ''
+    return `<${printTag(node)} ${printAttribute(node)} ${
+      node.children.length
+        ? `>${printChildren(node)}</${printTag(node)}>`
+        : '/>'
+    }`
+  }
+  const root = tree.find((child) => {
+    return child.componentName === 'Form' || child.componentName === 'Root'
+  })
+  return `import React, { useMemo } from 'react'
+import { createForm } from '@formily/core'
+import { createSchemaField } from '@formily/react'
+import {
+  Form,
+  FormItem,
+  DatePicker,
+  Checkbox,
+  Cascader,
+  Editable,
+  Input,
+  NumberPicker,
+  Switch,
+  Password,
+  PreviewText,
+  Radio,
+  Reset,
+  Select,
+  Space,
+  Submit,
+  TimePicker,
+  Transfer,
+  TreeSelect,
+  Upload,
+  FormGrid,
+  FormLayout,
+  FormTab,
+  FormCollapse,
+  ArrayTable,
+  ArrayCards,
+} from '@formily/antd'
+import { Card, Slider, Rate } from 'antd'
+
+const Text: React.FC<{
+  value?: string
+  content?: string
+  mode?: 'normal' | 'h1' | 'h2' | 'h3' | 'p'
+}> = ({ value, mode, content, ...props }) => {
+  const tagName = mode === 'normal' || !mode ? 'div' : mode
+  return React.createElement(tagName, props, value || content)
+}
+
+const SchemaField = createSchemaField({
+  components: {
+    Space,
+    FormGrid,
+    FormLayout,
+    FormTab,
+    FormCollapse,
+    ArrayTable,
+    ArrayCards,
+    FormItem,
+    DatePicker,
+    Checkbox,
+    Cascader,
+    Editable,
+    Input,
+    Text,
+    NumberPicker,
+    Switch,
+    Password,
+    PreviewText,
+    Radio,
+    Reset,
+    Select,
+    Submit,
+    TimePicker,
+    Transfer,
+    TreeSelect,
+    Upload,
+    Card,
+    Slider,
+    Rate,
+  },
+})
+
+export default ()=>{
+  const form = useMemo(() => createForm(), [])
+
+  return <Form form={form} ${printAttribute(root)}>
+    <SchemaField>
+      ${printChildren(root)}
+    </SchemaField>
+  </Form>
+}
+  
+`
+}
+
+export const MarkupSchemaWidget: React.FC<IMarkupSchemaWidgetProps> = (
+  props
+) => {
+  return (
+    <MonacoInput
+      {...props}
+      options={{ readOnly: true }}
+      value={transformToMarkupSchemaCode(props.tree)}
+      language="typescript"
+    />
+  )
+}

+ 91 - 0
src/pages/Schema/Base/components/Designable/widgets/PreviewWidget.tsx

@@ -0,0 +1,91 @@
+import React, { useMemo } from 'react'
+import { createForm } from '@formily/core'
+import { createSchemaField } from '@formily/react'
+import {
+  Form,
+  FormItem,
+  DatePicker,
+  Checkbox,
+  Cascader,
+  Editable,
+  Input,
+  NumberPicker,
+  Switch,
+  Password,
+  PreviewText,
+  Radio,
+  Reset,
+  Select,
+  Space,
+  Submit,
+  TimePicker,
+  Transfer,
+  TreeSelect,
+  Upload,
+  FormGrid,
+  FormLayout,
+  FormTab,
+  FormCollapse,
+  ArrayTable,
+  ArrayCards,
+} from '@formily/antd'
+import { Card, Slider, Rate } from 'antd'
+import { TreeNode } from '@designable/core'
+import { transformToSchema } from '@designable/formily-transformer'
+
+const Text: React.FC<{
+  value?: string
+  content?: string
+  mode?: 'normal' | 'h1' | 'h2' | 'h3' | 'p'
+}> = ({ value, mode, content, ...props }) => {
+  const tagName = mode === 'normal' || !mode ? 'div' : mode
+  return React.createElement(tagName, props, value || content)
+}
+
+const SchemaField = createSchemaField({
+  components: {
+    Space,
+    FormGrid,
+    FormLayout,
+    FormTab,
+    FormCollapse,
+    ArrayTable,
+    ArrayCards,
+    FormItem,
+    DatePicker,
+    Checkbox,
+    Cascader,
+    Editable,
+    Input,
+    Text,
+    NumberPicker,
+    Switch,
+    Password,
+    PreviewText,
+    Radio,
+    Reset,
+    Select,
+    Submit,
+    TimePicker,
+    Transfer,
+    TreeSelect,
+    Upload,
+    Card,
+    Slider,
+    Rate,
+  },
+})
+
+export interface IPreviewWidgetProps {
+  tree: TreeNode
+}
+
+export const PreviewWidget: React.FC<IPreviewWidgetProps> = (props) => {
+  const form = useMemo(() => createForm(), [])
+  const { form: formProps, schema } = transformToSchema(props.tree)
+  return (
+    <Form {...formProps} form={form}>
+      <SchemaField schema={schema} />
+    </Form>
+  )
+}

+ 27 - 0
src/pages/Schema/Base/components/Designable/widgets/SchemaEditorWidget.tsx

@@ -0,0 +1,27 @@
+import React from 'react'
+import {
+  transformToSchema,
+  transformToTreeNode,
+} from '@designable/formily-transformer'
+import { TreeNode, ITreeNode } from '@designable/core'
+import { MonacoInput } from '@designable/react-settings-form'
+
+export interface ISchemaEditorWidgetProps {
+  tree: TreeNode
+  onChange?: (tree: ITreeNode) => void
+}
+
+export const SchemaEditorWidget: React.FC<ISchemaEditorWidgetProps> = (
+  props
+) => {
+  return (
+    <MonacoInput
+      {...props}
+      value={JSON.stringify(transformToSchema(props.tree), null, 2)}
+      onChange={(value) => {
+        props.onChange?.(transformToTreeNode(JSON.parse(value)))
+      }}
+      language="json"
+    />
+  )
+}

+ 3 - 0
src/pages/Schema/Base/components/Designable/widgets/index.ts

@@ -1 +1,4 @@
 export * from './ActionsWidget'
+export * from './PreviewWidget'
+export * from './SchemaEditorWidget'
+export * from './MarkupSchemaWidget'