Browse Source

feat: 预算业务表单20%

lanjianrong 3 years atrás
parent
commit
fd81bc8eae
100 changed files with 6013 additions and 469 deletions
  1. 1 1
      .gitignore
  2. 8 2
      config/routes.ts
  3. 2 2
      package.json
  4. 468 411
      pnpm-lock.yaml
  5. 1 0
      src/locales/zh-CN/menu.ts
  6. 18 0
      src/pages/Schema/Budget/components/Designable/antd/common/Container/index.tsx
  7. 7 0
      src/pages/Schema/Budget/components/Designable/antd/common/Container/styles.less
  8. 18 0
      src/pages/Schema/Budget/components/Designable/antd/common/FormItemSwitcher/index.tsx
  9. 25 0
      src/pages/Schema/Budget/components/Designable/antd/common/LoadTemplate/index.tsx
  10. 2 0
      src/pages/Schema/Budget/components/Designable/antd/components/Field/index.ts
  11. 161 0
      src/pages/Schema/Budget/components/Designable/antd/components/Field/preview.tsx
  12. 214 0
      src/pages/Schema/Budget/components/Designable/antd/components/Field/shared.ts
  13. 1 0
      src/pages/Schema/Budget/components/Designable/antd/components/Form/index.tsx
  14. 64 0
      src/pages/Schema/Budget/components/Designable/antd/components/Form/preview.tsx
  15. 34 0
      src/pages/Schema/Budget/components/Designable/antd/components/Form/styles.less
  16. 1 0
      src/pages/Schema/Budget/components/Designable/antd/components/FormGrid/index.ts
  17. 146 0
      src/pages/Schema/Budget/components/Designable/antd/components/FormGrid/preview.tsx
  18. 5 0
      src/pages/Schema/Budget/components/Designable/antd/components/FormGrid/styles.less
  19. 1 0
      src/pages/Schema/Budget/components/Designable/antd/components/FormLayout/index.ts
  20. 35 0
      src/pages/Schema/Budget/components/Designable/antd/components/FormLayout/preview.ts
  21. 1 0
      src/pages/Schema/Budget/components/Designable/antd/components/GsCodeInput/index.ts
  22. 46 0
      src/pages/Schema/Budget/components/Designable/antd/components/GsCodeInput/preview.ts
  23. 4 0
      src/pages/Schema/Budget/components/Designable/antd/components/index.ts
  24. 1 0
      src/pages/Schema/Budget/components/Designable/antd/hooks/index.ts
  25. 23 0
      src/pages/Schema/Budget/components/Designable/antd/hooks/useDropTemplate.ts
  26. 3 0
      src/pages/Schema/Budget/components/Designable/antd/index.ts
  27. 87 0
      src/pages/Schema/Budget/components/Designable/antd/locales/ArrayBase.ts
  28. 20 0
      src/pages/Schema/Budget/components/Designable/antd/locales/ArrayCards.ts
  29. 122 0
      src/pages/Schema/Budget/components/Designable/antd/locales/ArrayTable.ts
  30. 44 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Card.ts
  31. 60 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Cascader.ts
  32. 11 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Checkbox.ts
  33. 53 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Component.ts
  34. 88 0
      src/pages/Schema/Budget/components/Designable/antd/locales/DatePicker.ts
  35. 316 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Field.ts
  36. 119 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Form.ts
  37. 83 0
      src/pages/Schema/Budget/components/Designable/antd/locales/FormCollapse.ts
  38. 77 0
      src/pages/Schema/Budget/components/Designable/antd/locales/FormGrid.ts
  39. 122 0
      src/pages/Schema/Budget/components/Designable/antd/locales/FormLayout.ts
  40. 83 0
      src/pages/Schema/Budget/components/Designable/antd/locales/FormTab.ts
  41. 50 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Input.ts
  42. 81 0
      src/pages/Schema/Budget/components/Designable/antd/locales/NumberPicker.ts
  43. 11 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Object.ts
  44. 14 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Password.ts
  45. 29 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Radio.ts
  46. 32 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Rate.ts
  47. 102 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Select.ts
  48. 73 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Slider.ts
  49. 50 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Space.ts
  50. 11 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Switch.ts
  51. 38 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Text.ts
  52. 35 0
      src/pages/Schema/Budget/components/Designable/antd/locales/TextArea.ts
  53. 74 0
      src/pages/Schema/Budget/components/Designable/antd/locales/TimePicker.ts
  54. 38 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Transfer.ts
  55. 183 0
      src/pages/Schema/Budget/components/Designable/antd/locales/TreeSelect.ts
  56. 94 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Upload.ts
  57. 11 0
      src/pages/Schema/Budget/components/Designable/antd/locales/Void.ts
  58. 31 0
      src/pages/Schema/Budget/components/Designable/antd/locales/all.ts
  59. 3 0
      src/pages/Schema/Budget/components/Designable/antd/locales/index.ts
  60. 5 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/ArrayCards.ts
  61. 101 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/ArrayTable.ts
  62. 51 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/CSSStyle.ts
  63. 36 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/Card.ts
  64. 76 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/Cascader.ts
  65. 12 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/Checkbox.ts
  66. 118 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/DatePicker.ts
  67. 11 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/Form.ts
  68. 61 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/FormCollapse.ts
  69. 79 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/FormGrid.ts
  70. 141 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/FormItem.ts
  71. 124 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/FormLayout.ts
  72. 46 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/FormTab.ts
  73. 92 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/Input.ts
  74. 81 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/NumberPicker.ts
  75. 13 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/Password.ts
  76. 38 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/Radio.ts
  77. 40 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/Rate.ts
  78. 149 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/Select.ts
  79. 79 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/Slider.ts
  80. 41 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/Space.ts
  81. 21 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/Switch.ts
  82. 21 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/Text.ts
  83. 124 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/TimePicker.ts
  84. 46 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/Transfer.ts
  85. 139 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/TreeSelect.ts
  86. 103 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/Upload.ts
  87. 27 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/all.ts
  88. 3 0
      src/pages/Schema/Budget/components/Designable/antd/schemas/index.ts
  89. 94 0
      src/pages/Schema/Budget/components/Designable/antd/shared.ts
  90. 118 0
      src/pages/Schema/Budget/components/Designable/index.tsx
  91. 1 0
      src/pages/Schema/Budget/components/Designable/service/index.ts
  92. 17 0
      src/pages/Schema/Budget/components/Designable/service/schema.ts
  93. 49 0
      src/pages/Schema/Budget/components/Designable/widgets/ActionsWidget.tsx
  94. 164 0
      src/pages/Schema/Budget/components/Designable/widgets/MarkupSchemaWidget.tsx
  95. 91 0
      src/pages/Schema/Budget/components/Designable/widgets/PreviewWidget.tsx
  96. 27 0
      src/pages/Schema/Budget/components/Designable/widgets/SchemaEditorWidget.tsx
  97. 4 0
      src/pages/Schema/Budget/components/Designable/widgets/index.ts
  98. 0 53
      src/pages/Schema/Budget/components/LeftMenu.tsx
  99. 35 0
      src/pages/Schema/Budget/detail.tsx
  100. 0 0
      src/pages/Schema/Budget/index.tsx

+ 1 - 1
.gitignore

@@ -14,7 +14,7 @@ _roadhog-api-doc
 .DS_Store
 npm-debug.log*
 yarn-error.log
-
+pnpm-locak.yaml
 /coverage
 .idea
 yarn.lock

+ 8 - 2
config/routes.ts

@@ -138,8 +138,8 @@
       {
         path: 'base/:id',
         name: 'baseDetail',
-        hideInMenu: true,
-        component: './Schema/Base/detail.tsx'
+        component: './Schema/Base/detail.tsx',
+        hideInMenu: true
       },
       {
         path: 'option',
@@ -150,6 +150,12 @@
         path: 'budget',
         name: 'budget',
         component: './Schema/Budget'
+      },
+      {
+        path: 'budget/:id',
+        name: 'budgetDetail',
+        component: './Schema/Budget/detail',
+        hideInMenu: true
       }
     ]
   },

+ 2 - 2
package.json

@@ -43,8 +43,8 @@
     "@formily/reactive": "^2.1.4",
     "@formily/reactive-react": "^2.1.4",
     "@formily/shared": "^2.1.4",
-    "@umijs/max": "4.0.0-rc.22",
-    "@umijs/plugins": "4.0.0-rc.22",
+    "@umijs/max": "4.0.0-canary.20220628.1",
+    "@umijs/plugins": "4.0.0-canary.20220628.1",
     "ahooks": "^3.0.0",
     "antd": "4.20.7",
     "array-move": "^4.0.0",

File diff suppressed because it is too large
+ 468 - 411
pnpm-lock.yaml


+ 1 - 0
src/locales/zh-CN/menu.ts

@@ -34,6 +34,7 @@ export default {
   'menu.schema.base': '基础数据设置',
   'menu.schema.option': '数据源设置',
   'menu.schema.budget': '预算业务表单',
+  'menu.schema.budgetDetail': '编辑业务表单',
   'menu.schema.test': '测试',
   'menu.schema.baseDetail': '编辑数据模型',
   'menu.system': '系统管理',

+ 18 - 0
src/pages/Schema/Budget/components/Designable/antd/common/Container/index.tsx

@@ -0,0 +1,18 @@
+import React from 'react'
+import { observer } from '@formily/reactive-react'
+import { DroppableWidget } from '@designable/react'
+import './styles.less'
+
+export const Container: React.FC = observer(props => {
+  return <DroppableWidget>{props.children}</DroppableWidget>
+})
+
+export const withContainer = (Target: React.JSXElementConstructor<any>) => {
+  return (props: any) => {
+    return (
+      <DroppableWidget>
+        <Target {...props} />
+      </DroppableWidget>
+    )
+  }
+}

+ 7 - 0
src/pages/Schema/Budget/components/Designable/antd/common/Container/styles.less

@@ -0,0 +1,7 @@
+@import '~antd/lib/style/themes/default.less';
+
+.dn-form-container {
+  margin: 0 !important;
+  padding: 20px;
+  border: 1px solid @border-color-split;
+}

+ 18 - 0
src/pages/Schema/Budget/components/Designable/antd/common/FormItemSwitcher/index.tsx

@@ -0,0 +1,18 @@
+import React from 'react'
+import { Switch } from 'antd'
+
+export interface IFormItemSwitcherProps {
+  value?: string
+  onChange?: (value: string) => void
+}
+
+export const FormItemSwitcher: React.FC<IFormItemSwitcherProps> = props => {
+  return (
+    <Switch
+      checked={props.value === 'FormItem'}
+      onChange={value => {
+        props.onChange(value ? 'FormItem' : undefined)
+      }}
+    />
+  )
+}

+ 25 - 0
src/pages/Schema/Budget/components/Designable/antd/common/LoadTemplate/index.tsx

@@ -0,0 +1,25 @@
+import React from 'react'
+import { NodeActionsWidget } from '@designable/react'
+
+export interface ITemplateAction {
+  title: React.ReactNode
+  tooltip?: React.ReactNode
+  icon?: string | React.ReactNode
+  onClick: () => void
+}
+
+export interface ILoadTemplateProps {
+  className?: string
+  style?: React.CSSProperties
+  actions?: ITemplateAction[]
+}
+
+export const LoadTemplate: React.FC<ILoadTemplateProps> = props => {
+  return (
+    <NodeActionsWidget>
+      {props.actions?.map((action, key) => {
+        return <NodeActionsWidget.Action {...action} key={key} />
+      })}
+    </NodeActionsWidget>
+  )
+}

+ 2 - 0
src/pages/Schema/Budget/components/Designable/antd/components/Field/index.ts

@@ -0,0 +1,2 @@
+export * from './preview'
+export * from './shared'

+ 161 - 0
src/pages/Schema/Budget/components/Designable/antd/components/Field/preview.tsx

@@ -0,0 +1,161 @@
+import React from 'react'
+import { FormPath } from '@formily/core'
+import { toJS } from '@formily/reactive'
+import {
+  ArrayField,
+  Field as InternalField,
+  ObjectField,
+  VoidField,
+  observer,
+  ISchema,
+  Schema
+} from '@formily/react'
+import { FormItem } from '@formily/antd'
+import { each, reduce } from '@formily/shared'
+import { createBehavior } from '@designable/core'
+import { useDesigner, useTreeNode, useComponents, DnFC } from '@designable/react'
+import { isArr, isStr } from '@designable/shared'
+import { Container } from '../../common/Container'
+import { AllLocales } from '../../locales'
+
+Schema.silent(true)
+
+const SchemaStateMap = {
+  title: 'title',
+  description: 'description',
+  default: 'value',
+  enum: 'dataSource',
+  readOnly: 'readOnly',
+  writeOnly: 'editable',
+  required: 'required',
+  'x-content': 'content',
+  'x-value': 'value',
+  'x-editable': 'editable',
+  'x-disabled': 'disabled',
+  'x-read-pretty': 'readPretty',
+  'x-read-only': 'readOnly',
+  'x-visible': 'visible',
+  'x-hidden': 'hidden',
+  'x-display': 'display',
+  'x-pattern': 'pattern'
+}
+
+const NeedShownExpression = {
+  title: true,
+  description: true,
+  default: true,
+  'x-content': true,
+  'x-value': true
+}
+
+const isExpression = (val: any) => isStr(val) && /^\{\{.*\}\}$/.test(val)
+
+const filterExpression = (val: any) => {
+  if (typeof val === 'object') {
+    const isArray = isArr(val)
+    const results = reduce(
+      val,
+      (buf: any, value, key) => {
+        if (isExpression(value)) {
+          return buf
+        } else {
+          const results = filterExpression(value)
+          if (results === undefined || results === null) return buf
+          if (isArray) {
+            return buf.concat([results])
+          }
+          buf[key] = results
+          return buf
+        }
+      },
+      isArray ? [] : {}
+    )
+    return results
+  }
+  if (isExpression(val)) {
+    return
+  }
+  return val
+}
+
+const toDesignableFieldProps = (
+  schema: ISchema,
+  components: any,
+  nodeIdAttrName: string,
+  id: string
+) => {
+  const results: any = {}
+  each(SchemaStateMap, (fieldKey, schemaKey) => {
+    const value = schema[schemaKey]
+    if (isExpression(value)) {
+      if (!NeedShownExpression[schemaKey]) return
+      if (value) {
+        results[fieldKey] = value
+        return
+      }
+    } else if (value) {
+      results[fieldKey] = filterExpression(value)
+    }
+  })
+  if (!components.FormItem) {
+    components.FormItem = FormItem
+  }
+  const decorator = schema['x-decorator'] && FormPath.getIn(components, schema['x-decorator'])
+  const component = schema['x-component'] && FormPath.getIn(components, schema['x-component'])
+  const decoratorProps = schema['x-decorator-props'] || {}
+  const componentProps = schema['x-component-props'] || {}
+
+  if (decorator) {
+    results.decorator = [decorator, toJS(decoratorProps)]
+  }
+  if (component) {
+    results.component = [component, toJS(componentProps)]
+  }
+  if (decorator) {
+    FormPath.setIn(results.decorator[1], nodeIdAttrName, id)
+  } else if (component) {
+    FormPath.setIn(results.component[1], nodeIdAttrName, id)
+  }
+  results.title = results.title && <span data-content-editable="title">{results.title}</span>
+  results.description = results.description && (
+    <span data-content-editable="description">{results.description}</span>
+  )
+  return results
+}
+
+export const Field: DnFC<ISchema> = observer(props => {
+  const designer = useDesigner()
+  const components = useComponents()
+  const node = useTreeNode()
+  if (!node) return null
+  const fieldProps = toDesignableFieldProps(
+    props,
+    components,
+    designer.props.nodeIdAttrName,
+    node.id
+  )
+  if (props.type === 'object') {
+    return (
+      <Container>
+        <ObjectField {...fieldProps} name={node.id}>
+          {props.children}
+        </ObjectField>
+      </Container>
+    )
+  } else if (props.type === 'array') {
+    return <ArrayField {...fieldProps} name={node.id} />
+  } else if (node.props.type === 'void') {
+    return (
+      <VoidField {...fieldProps} name={node.id}>
+        {props.children}
+      </VoidField>
+    )
+  }
+  return <InternalField {...fieldProps} name={node.id} />
+})
+
+Field.Behavior = createBehavior({
+  name: 'Field',
+  selector: 'Field',
+  designerLocales: AllLocales.Field
+})

+ 214 - 0
src/pages/Schema/Budget/components/Designable/antd/components/Field/shared.ts

@@ -0,0 +1,214 @@
+import type { ISchema } from '@formily/json-schema'
+import { ReactionsSetter, DataSourceSetter, ValidatorSetter } from '@designable/formily-setters'
+import { FormItemSwitcher } from '../../common/FormItemSwitcher'
+import { AllSchemas } from '../../schemas'
+
+export const createComponentSchema = (component: ISchema, decorator: ISchema) => {
+  return {
+    'component-group': component && {
+      type: 'void',
+      'x-component': 'CollapseItem',
+      'x-reactions': {
+        fulfill: {
+          state: {
+            visible: '{{!!$form.values["x-component"]}}'
+          }
+        }
+      },
+      properties: {
+        'x-component-props': component
+      }
+    },
+    'decorator-group': decorator && {
+      type: 'void',
+      'x-component': 'CollapseItem',
+      'x-component-props': { defaultExpand: false },
+      'x-reactions': {
+        fulfill: {
+          state: {
+            visible: '{{!!$form.values["x-decorator"]}}'
+          }
+        }
+      },
+      properties: {
+        'x-decorator-props': decorator
+      }
+    },
+    'component-style-group': {
+      type: 'void',
+      'x-component': 'CollapseItem',
+      'x-component-props': { defaultExpand: false },
+      'x-reactions': {
+        fulfill: {
+          state: {
+            visible: '{{!!$form.values["x-component"]}}'
+          }
+        }
+      },
+      properties: {
+        'x-component-props.style': AllSchemas.CSSStyle
+      }
+    },
+    'decorator-style-group': {
+      type: 'void',
+      'x-component': 'CollapseItem',
+      'x-component-props': { defaultExpand: false },
+      'x-reactions': {
+        fulfill: {
+          state: {
+            visible: '{{!!$form.values["x-decorator"]}}'
+          }
+        }
+      },
+      properties: {
+        'x-decorator-props.style': AllSchemas.CSSStyle
+      }
+    }
+  }
+}
+
+export const createFieldSchema = (
+  component?: ISchema,
+  decorator: ISchema = AllSchemas.FormItem
+): ISchema => {
+  return {
+    type: 'object',
+    properties: {
+      'field-group': {
+        type: 'void',
+        'x-component': 'CollapseItem',
+        properties: {
+          name: {
+            type: 'string',
+            'x-decorator': 'FormItem',
+            'x-component': 'Input'
+          },
+          title: {
+            type: 'string',
+            'x-decorator': 'FormItem',
+            'x-component': 'Input'
+          },
+          description: {
+            type: 'string',
+            'x-decorator': 'FormItem',
+            'x-component': 'Input.TextArea'
+          },
+          'x-display': {
+            type: 'string',
+            enum: ['visible', 'hidden', 'none', ''],
+            'x-decorator': 'FormItem',
+            'x-component': 'Select',
+            'x-component-props': {
+              defaultValue: 'visible'
+            }
+          },
+          'x-pattern': {
+            type: 'string',
+            enum: ['editable', 'disabled', 'readOnly', 'readPretty', ''],
+            'x-decorator': 'FormItem',
+            'x-component': 'Select',
+            'x-component-props': {
+              defaultValue: 'editable'
+            }
+          },
+          default: {
+            'x-decorator': 'FormItem',
+            'x-component': 'ValueInput'
+          },
+          enum: {
+            'x-decorator': 'FormItem',
+            'x-component': DataSourceSetter
+          },
+          'x-reactions': {
+            'x-decorator': 'FormItem',
+            'x-component': ReactionsSetter
+          },
+          'x-validator': {
+            type: 'array',
+            'x-component': ValidatorSetter
+          },
+          required: {
+            type: 'boolean',
+            'x-decorator': 'FormItem',
+            'x-component': 'Switch'
+          }
+        }
+      },
+      ...createComponentSchema(component, decorator)
+    }
+  }
+}
+
+export const createVoidFieldSchema = (
+  component?: ISchema,
+  decorator: ISchema = AllSchemas.FormItem
+) => {
+  return {
+    type: 'object',
+    properties: {
+      'field-group': {
+        type: 'void',
+        'x-component': 'CollapseItem',
+        properties: {
+          name: {
+            type: 'string',
+            'x-decorator': 'FormItem',
+            'x-component': 'Input'
+          },
+          title: {
+            type: 'string',
+            'x-decorator': 'FormItem',
+            'x-component': 'Input',
+            'x-reactions': {
+              fulfill: {
+                state: {
+                  hidden: '{{$form.values["x-decorator"] !== "FormItem"}}'
+                }
+              }
+            }
+          },
+          description: {
+            type: 'string',
+            'x-decorator': 'FormItem',
+            'x-component': 'Input.TextArea',
+            'x-reactions': {
+              fulfill: {
+                state: {
+                  hidden: '{{$form.values["x-decorator"] !== "FormItem"}}'
+                }
+              }
+            }
+          },
+          'x-display': {
+            type: 'string',
+            enum: ['visible', 'hidden', 'none', ''],
+            'x-decorator': 'FormItem',
+            'x-component': 'Select',
+            'x-component-props': {
+              defaultValue: 'visible'
+            }
+          },
+          'x-pattern': {
+            type: 'string',
+            enum: ['editable', 'disabled', 'readOnly', 'readPretty', ''],
+            'x-decorator': 'FormItem',
+            'x-component': 'Select',
+            'x-component-props': {
+              defaultValue: 'editable'
+            }
+          },
+          'x-reactions': {
+            'x-decorator': 'FormItem',
+            'x-component': ReactionsSetter
+          },
+          'x-decorator': {
+            type: 'string',
+            'x-decorator': 'FormItem',
+            'x-component': FormItemSwitcher
+          }
+        }
+      },
+      ...createComponentSchema(component, decorator)
+    }
+  }
+}

+ 1 - 0
src/pages/Schema/Budget/components/Designable/antd/components/Form/index.tsx

@@ -0,0 +1 @@
+export * from './preview'

+ 64 - 0
src/pages/Schema/Budget/components/Designable/antd/components/Form/preview.tsx

@@ -0,0 +1,64 @@
+import React, { useMemo } from 'react'
+import { createBehavior, createResource } from '@designable/core'
+import { createForm } from '@formily/core'
+import { observer } from '@formily/react'
+import { Form as FormilyForm } from '@formily/antd'
+import { usePrefix, DnFC } from '@designable/react'
+import { AllSchemas } from '../../schemas'
+import { AllLocales } from '../../locales'
+import './styles.less'
+
+export const Form: DnFC<React.ComponentProps<typeof FormilyForm>> = observer(props => {
+  const prefix = usePrefix('designable-form')
+  const form = useMemo(
+    () =>
+      createForm({
+        designable: true
+      }),
+    []
+  )
+  return (
+    <FormilyForm {...props} style={{ ...props.style }} className={prefix} form={form}>
+      {props.children}
+    </FormilyForm>
+  )
+})
+
+Form.Behavior = createBehavior({
+  name: 'Form',
+  selector: node => node.componentName === 'Form',
+  designerProps(node) {
+    return {
+      draggable: !node.isRoot,
+      cloneable: !node.isRoot,
+      deletable: !node.isRoot,
+      droppable: true,
+      propsSchema: {
+        type: 'object',
+        properties: {
+          ...(AllSchemas.FormLayout.properties as any),
+          style: AllSchemas.CSSStyle
+        }
+      },
+      defaultProps: {
+        labelCol: 6,
+        wrapperCol: 12
+      }
+    }
+  },
+  designerLocales: AllLocales.Form
+})
+
+Form.Resource = createResource({
+  title: { 'zh-CN': '表单', 'en-US': 'Form' },
+  icon: 'FormLayoutSource',
+  elements: [
+    {
+      componentName: 'Field',
+      props: {
+        type: 'object',
+        'x-component': 'Form'
+      }
+    }
+  ]
+})

+ 34 - 0
src/pages/Schema/Budget/components/Designable/antd/components/Form/styles.less

@@ -0,0 +1,34 @@
+@import '~antd/lib/style/themes/default.less';
+
+.dn-designable-form {
+  .@{ant-prefix}-input,
+  .@{ant-prefix}-input-number,
+  .@{ant-prefix}-input-affix-wrapper,
+  .@{ant-prefix}-cascader-picker,
+  .@{ant-prefix}-picker-input,
+  .@{ant-prefix}-picker,
+  .@{ant-prefix}-cascader-picker-label,
+  .@{ant-prefix}-slider,
+  .@{ant-prefix}-checkbox,
+  .@{ant-prefix}-rate,
+  .@{ant-prefix}-switch,
+  .@{ant-prefix}-radio,
+  .@{ant-prefix}-radio-wrapper,
+  .@{ant-prefix}-checkbox-group,
+  .@{ant-prefix}-checkbox-wrapper,
+  .@{ant-prefix}-radio-group,
+  .@{ant-prefix}-upload,
+  .@{ant-prefix}-transfer,
+  .@{ant-prefix}-select,
+  .@{ant-prefix}-select-selector {
+    pointer-events: none !important;
+
+    input {
+      pointer-events: none !important;
+    }
+  }
+
+  .anticon svg {
+    pointer-events: none;
+  }
+}

+ 1 - 0
src/pages/Schema/Budget/components/Designable/antd/components/FormGrid/index.ts

@@ -0,0 +1 @@
+export * from './preview'

+ 146 - 0
src/pages/Schema/Budget/components/Designable/antd/components/FormGrid/preview.tsx

@@ -0,0 +1,146 @@
+import React from 'react'
+import { FormGrid as FormilyGird } from '@formily/antd'
+import { TreeNode, createBehavior, createResource } from '@designable/core'
+import { DnFC, useTreeNode, useNodeIdProps, DroppableWidget } from '@designable/react'
+import { observer } from '@formily/reactive-react'
+import { LoadTemplate } from '../../common/LoadTemplate'
+import { createFieldSchema } from '../Field'
+import { AllSchemas } from '../../schemas'
+import { AllLocales } from '../../locales'
+import './styles.less'
+
+type formilyGrid = typeof FormilyGird
+
+export const FormGrid: DnFC<React.ComponentProps<formilyGrid>> & {
+  GridColumn?: React.FC<React.ComponentProps<formilyGrid['GridColumn']>>
+} = observer(props => {
+  const node = useTreeNode()
+  const nodeId = useNodeIdProps()
+  if (node.children.length === 0) return <DroppableWidget {...props} />
+  const totalColumns = node.children.reduce(
+    (buf, child) => buf + (child.props?.['x-component-props']?.gridSpan ?? 1),
+    0
+  )
+  return (
+    <div {...nodeId} className="dn-grid">
+      <FormilyGird {...props} key={totalColumns}>
+        {props.children}
+      </FormilyGird>
+      <LoadTemplate
+        actions={[
+          {
+            title: node.getMessage('addGridColumn'),
+            icon: 'AddColumn',
+            onClick: () => {
+              const column = new TreeNode({
+                componentName: 'Field',
+                props: {
+                  type: 'void',
+                  'x-component': 'FormGrid.GridColumn'
+                }
+              })
+              node.append(column)
+            }
+          }
+        ]}
+      />
+    </div>
+  )
+})
+
+FormGrid.GridColumn = observer(props => {
+  return (
+    <DroppableWidget
+      {...props}
+      data-span={props.gridSpan}
+      style={{
+        ...props['style'],
+        gridColumnStart: `span ${props.gridSpan || 1}`
+      }}
+    >
+      {props.children}
+    </DroppableWidget>
+  )
+})
+
+FormGrid.Behavior = createBehavior(
+  {
+    name: 'FormGrid',
+    extends: ['Field'],
+    selector: node => node.props['x-component'] === 'FormGrid',
+    designerProps: {
+      droppable: true,
+      allowDrop: node => node.props['x-component'] !== 'FormGrid',
+      propsSchema: createFieldSchema(AllSchemas.FormGrid)
+    },
+    designerLocales: AllLocales.FormGrid
+  },
+  {
+    name: 'FormGrid.GridColumn',
+    extends: ['Field'],
+    selector: node => node.props['x-component'] === 'FormGrid.GridColumn',
+    designerProps: {
+      droppable: true,
+      resizable: {
+        width(node) {
+          const span = Number(node.props['x-component-props']?.gridSpan ?? 1)
+          return {
+            plus: () => {
+              if (span + 1 > 12) return
+              node.props['x-component-props'] = node.props['x-component-props'] || {}
+              node.props['x-component-props'].gridSpan = span + 1
+            },
+            minus: () => {
+              if (span - 1 < 1) return
+              node.props['x-component-props'] = node.props['x-component-props'] || {}
+              node.props['x-component-props'].gridSpan = span - 1
+            }
+          }
+        }
+      },
+      resizeXPath: 'x-component-props.gridSpan',
+      resizeStep: 1,
+      resizeMin: 1,
+      resizeMax: 12,
+      allowDrop: node => node.props['x-component'] === 'FormGrid',
+      propsSchema: createFieldSchema(AllSchemas.FormGrid.GridColumn)
+    },
+    designerLocales: AllLocales.FormGridColumn
+  }
+)
+
+FormGrid.Resource = createResource({
+  icon: 'GridSource',
+  elements: [
+    {
+      componentName: 'Field',
+      props: {
+        type: 'void',
+        'x-component': 'FormGrid'
+      },
+      children: [
+        {
+          componentName: 'Field',
+          props: {
+            type: 'void',
+            'x-component': 'FormGrid.GridColumn'
+          }
+        },
+        {
+          componentName: 'Field',
+          props: {
+            type: 'void',
+            'x-component': 'FormGrid.GridColumn'
+          }
+        },
+        {
+          componentName: 'Field',
+          props: {
+            type: 'void',
+            'x-component': 'FormGrid.GridColumn'
+          }
+        }
+      ]
+    }
+  ]
+})

+ 5 - 0
src/pages/Schema/Budget/components/Designable/antd/components/FormGrid/styles.less

@@ -0,0 +1,5 @@
+.dn-grid-column {
+  min-height: 60px;
+  margin: 4px;
+  border: 1px dashed #aaa;
+}

+ 1 - 0
src/pages/Schema/Budget/components/Designable/antd/components/FormLayout/index.ts

@@ -0,0 +1 @@
+export * from './preview'

+ 35 - 0
src/pages/Schema/Budget/components/Designable/antd/components/FormLayout/preview.ts

@@ -0,0 +1,35 @@
+import React from 'react'
+import { FormLayout as FormilyFormLayout } from '@formily/antd'
+import { createBehavior, createResource } from '@designable/core'
+import { DnFC } from '@designable/react'
+import { withContainer } from '../../common/Container'
+import { createVoidFieldSchema } from '../Field'
+import { AllSchemas } from '../../schemas'
+import { AllLocales } from '../../locales'
+
+export const FormLayout: DnFC<React.ComponentProps<typeof FormilyFormLayout>> =
+  withContainer(FormilyFormLayout)
+
+FormLayout.Behavior = createBehavior({
+  name: 'FormLayout',
+  extends: ['Field'],
+  selector: node => node.props['x-component'] === 'FormLayout',
+  designerProps: {
+    droppable: true,
+    propsSchema: createVoidFieldSchema(AllSchemas.FormLayout)
+  },
+  designerLocales: AllLocales.FormLayout
+})
+
+FormLayout.Resource = createResource({
+  icon: 'FormLayoutSource',
+  elements: [
+    {
+      componentName: 'Field',
+      props: {
+        type: 'void',
+        'x-component': 'FormLayout'
+      }
+    }
+  ]
+})

+ 1 - 0
src/pages/Schema/Budget/components/Designable/antd/components/GsCodeInput/index.ts

@@ -0,0 +1 @@
+export * from './preview'

+ 46 - 0
src/pages/Schema/Budget/components/Designable/antd/components/GsCodeInput/preview.ts

@@ -0,0 +1,46 @@
+import React from 'react'
+import { Input as FormilyInput } from '@formily/antd'
+import { createBehavior, createResource } from '@designable/core'
+import { DnFC } from '@designable/react'
+import { createFieldSchema } from '../Field'
+import { AllSchemas } from '../../schemas'
+import { AllLocales } from '../../locales'
+
+export const Input: DnFC<React.ComponentProps<typeof FormilyInput>> = FormilyInput
+
+Input.Behavior = createBehavior(
+  {
+    name: 'Input',
+    extends: ['Field'],
+    selector: node => node.props['x-custom'] === 'Input',
+    designerProps: {
+      propsSchema: createFieldSchema(AllSchemas.Input)
+    },
+    designerLocales: AllLocales.Input
+  },
+  {
+    name: 'Input.TextArea',
+    extends: ['Field'],
+    selector: node => node.props['x-component'] === 'Input.TextArea',
+    designerProps: {
+      propsSchema: createFieldSchema(AllSchemas.Input.TextArea)
+    },
+    designerLocales: AllLocales.TextArea
+  }
+)
+
+Input.Resource = createResource({
+  icon: 'InputSource',
+  elements: [
+    {
+      componentName: 'Field',
+      props: {
+        type: 'string',
+        title: 'Input',
+        'x-decorator': 'FormItem',
+        'x-component': 'Input',
+        'x-custom': 'GsCodeInput'
+      }
+    }
+  ]
+})

+ 4 - 0
src/pages/Schema/Budget/components/Designable/antd/components/index.ts

@@ -0,0 +1,4 @@
+export * from './Field'
+export * from './Form'
+export * from './FormGrid'
+export * from './FormLayout'

+ 1 - 0
src/pages/Schema/Budget/components/Designable/antd/hooks/index.ts

@@ -0,0 +1 @@
+export * from './useDropTemplate'

+ 23 - 0
src/pages/Schema/Budget/components/Designable/antd/hooks/useDropTemplate.ts

@@ -0,0 +1,23 @@
+import { AppendNodeEvent, TreeNode } from '@designable/core'
+import { useDesigner } from '@designable/react'
+import { matchComponent, matchChildComponent } from '../shared'
+
+export const useDropTemplate = (name: string, getChildren: (source: TreeNode[]) => TreeNode[]) => {
+  return useDesigner(designer => {
+    return designer.subscribeTo(AppendNodeEvent, event => {
+      const { source, target } = event.data
+      if (Array.isArray(target)) return
+      if (!Array.isArray(source)) return
+      if (
+        matchComponent(
+          target,
+          key => key === name && source.every(child => !matchChildComponent(child, name))
+        ) &&
+        target.children.length === 0
+      ) {
+        target.setChildren(...getChildren(source))
+        return false
+      }
+    })
+  })
+}

+ 3 - 0
src/pages/Schema/Budget/components/Designable/antd/index.ts

@@ -0,0 +1,3 @@
+export * from './components'
+export * from './schemas'
+export * from './locales'

+ 87 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/ArrayBase.ts

@@ -0,0 +1,87 @@
+import { GlobalRegistry } from '@designable/core'
+
+GlobalRegistry.registerDesignerLocales({
+  'zh-CN': {
+    Previews: {
+      droppable: '可以拖入组件',
+      addTabPane: '添加选项卡',
+      addCollapsePanel: '添加手风琴卡片',
+      addTableColumn: '添加表格列',
+      addTableSortHandle: '添加排序',
+      addIndex: '添加索引',
+      addOperation: '添加操作'
+    }
+  }
+})
+
+export const ArrayAddition = {
+  'zh-CN': {
+    title: '添加按钮',
+    settings: {
+      'x-component-props': {
+        method: '方法',
+        defaultValue: '默认值'
+      }
+    }
+  }
+}
+
+export const ArrayRemove = {
+  'zh-CN': {
+    title: '删除按钮'
+  },
+  'en-US': {
+    title: 'Remove'
+  },
+  'ko-KR': {
+    title: '삭제'
+  }
+}
+
+export const ArrayMoveUp = {
+  'zh-CN': {
+    title: '上移按钮'
+  },
+  'en-US': {
+    title: 'Move Up'
+  },
+  'ko-KR': {
+    title: '위로 옮기기'
+  }
+}
+
+export const ArrayMoveDown = {
+  'zh-CN': {
+    title: '下移按钮'
+  },
+  'en-US': {
+    title: 'Move Down'
+  },
+  'ko-KR': {
+    title: '아래로 옮기기'
+  }
+}
+
+export const ArrayIndex = {
+  'zh-CN': {
+    title: '索引标识'
+  },
+  'en-US': {
+    title: 'Index'
+  },
+  'ko-KR': {
+    title: '색인'
+  }
+}
+
+export const ArraySortHandle = {
+  'zh-CN': {
+    title: '排序标识'
+  },
+  'en-US': {
+    title: 'Sort Handle'
+  },
+  'ko-KR': {
+    title: '정렬 핸들'
+  }
+}

+ 20 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/ArrayCards.ts

@@ -0,0 +1,20 @@
+import { createLocales } from '@designable/core'
+import { Card } from './Card'
+
+export const ArrayCards = createLocales(Card, {
+  'zh-CN': {
+    title: '自增卡片',
+    addIndex: '添加索引',
+    addOperation: '添加操作'
+  },
+  'en-US': {
+    title: 'Array Cards',
+    addIndex: 'Add Index',
+    addOperation: 'Add Operations'
+  },
+  'ko-KR': {
+    title: '배열 카드',
+    addIndex: '색인 추가',
+    addOperation: '작업 추가'
+  }
+})

+ 122 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/ArrayTable.ts

@@ -0,0 +1,122 @@
+export const ArrayTable = {
+  'zh-CN': {
+    title: '自增表格',
+    addSortHandle: '添加排序',
+    addColumn: '添加列',
+    addIndex: '添加索引',
+    addOperation: '添加操作',
+    settings: {
+      'x-component-props': {
+        showHeader: '显示头部',
+        sticky: '吸顶',
+        align: {
+          title: '对齐',
+          dataSource: ['左', '右', '居中']
+        },
+        colSpan: '跨列',
+        fixed: { title: '固定列', dataSource: ['左', '右', '无'] },
+        width: '宽度',
+        defaultValue: '默认值'
+      }
+    }
+  },
+  'en-US': {
+    title: 'Array Table',
+    addSortHandle: 'Add Sort Handle',
+    addColumn: 'Add Column',
+    addIndex: 'Add Index',
+    addOperation: 'Add Operations',
+    settings: {
+      'x-component-props': {
+        showHeader: 'Show Header',
+        sticky: 'Sticky',
+        align: {
+          title: 'Align',
+          dataSource: ['Left', 'Right', 'Center']
+        },
+        colSpan: 'Col Span',
+        fixed: { title: 'Fixed', dataSource: ['Left', 'Right', 'None'] },
+        width: 'Width',
+        defaultValue: 'Default Value'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '배열 테이블',
+    addSortHandle: '정렬 핸들 추가',
+    addColumn: '열 추가',
+    addIndex: '색인 추가',
+    addOperation: '작업 추가',
+    settings: {
+      'x-component-props': {
+        showHeader: '헤더 보여주기',
+        sticky: '고정',
+        align: {
+          title: '정렬',
+          dataSource: ['왼쪽', '오른쪽', '가운데']
+        },
+        colSpan: 'colSpan',
+        fixed: { title: '고정', dataSource: ['왼쪽', '오른쪽', '없음'] },
+        width: '너비',
+        defaultValue: '기본 값'
+      }
+    }
+  }
+}
+
+export const ArrayTableColumn = {
+  'zh-CN': {
+    title: '表格列',
+    settings: {
+      'x-component-props': {
+        title: '标题',
+        align: {
+          title: '内容对齐',
+          dataSource: ['左', '右', '居中']
+        },
+        colSpan: '跨列',
+        width: '宽度',
+        fixed: {
+          title: '固定',
+          dataSource: ['左', '右', '无']
+        }
+      }
+    }
+  },
+  'en-US': {
+    title: 'Column',
+    settings: {
+      'x-component-props': {
+        title: 'Title',
+        align: {
+          title: 'Align',
+          dataSource: ['Left', 'Right', 'Center']
+        },
+        colSpan: 'Col Span',
+        width: 'Width',
+        fixed: {
+          title: 'Fixed',
+          dataSource: ['Left', 'Right', 'None']
+        }
+      }
+    }
+  },
+  'ko-KR': {
+    title: '열',
+    settings: {
+      'x-component-props': {
+        title: '제목',
+        align: {
+          title: '정렬',
+          dataSource: ['왼쪽', '오른쪽', '가운데']
+        },
+        colSpan: 'Col Span',
+        width: '너비',
+        fixed: {
+          title: '고정',
+          dataSource: ['왼쪽', '오른족', '없음']
+        }
+      }
+    }
+  }
+}

+ 44 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Card.ts

@@ -0,0 +1,44 @@
+export const Card = {
+  'zh-CN': {
+    title: '卡片',
+    settings: {
+      'x-component-props': {
+        type: '类型',
+        title: '标题',
+        extra: '右侧扩展',
+        cardTypes: [
+          { label: '内置', value: 'inner' },
+          { label: '默认', value: '' }
+        ]
+      }
+    }
+  },
+  'en-US': {
+    title: 'Card',
+    settings: {
+      'x-component-props': {
+        type: 'Type',
+        title: 'Title',
+        extra: 'Extra',
+        cardTypes: [
+          { label: 'Inner', value: 'inner' },
+          { label: 'Default', value: '' }
+        ]
+      }
+    }
+  },
+  'ko-KR': {
+    title: '카드',
+    settings: {
+      'x-component-props': {
+        type: '타입',
+        title: '제목',
+        extra: '추가 항목',
+        cardTypes: [
+          { label: '안쪽', value: 'inner' },
+          { label: '기본', value: '' }
+        ]
+      }
+    }
+  }
+}

+ 60 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Cascader.ts

@@ -0,0 +1,60 @@
+export const Cascader = {
+  'zh-CN': {
+    title: '联级选择',
+    settings: {
+      'x-component-props': {
+        changeOnSelect: {
+          title: '选择时触发',
+          tooltip: '点选每级菜单选项值都会发生变化'
+        },
+        displayRender: {
+          title: '渲染函数',
+          tooltip: '选择后展示的渲染函数,默认为label => label.join("/")	'
+        },
+        fieldNames: {
+          title: '自定义字段名',
+          tooltip: '默认值:{ label: "label", value: "value", children: "children" }'
+        }
+      }
+    }
+  },
+  'en-US': {
+    title: 'Cascader',
+    settings: {
+      'x-component-props': {
+        changeOnSelect: {
+          title: 'Change On Select',
+          tooltip: 'Click on each level of menu option value will change'
+        },
+        displayRender: {
+          title: 'Display Render',
+          tooltip:
+            'The rendering function displayed after selection, the default is label => label.join("/")	'
+        },
+        fieldNames: {
+          title: 'Field Names',
+          tooltip: 'Defaults:{ label: "label", value: "value", children: "children" }'
+        }
+      }
+    }
+  },
+  'ko-KR': {
+    title: 'Cascader',
+    settings: {
+      'x-component-props': {
+        changeOnSelect: {
+          title: '선택 시 변경',
+          tooltip: '메뉴 옵션 값의 레벨을 클릭하면 변경됩니다.'
+        },
+        displayRender: {
+          title: '디스플레이 렌더링',
+          tooltip: '선택 후 실행되는 렌더링 함수로 기본 값은 label => label.join("/")	'
+        },
+        fieldNames: {
+          title: '필드 이름',
+          tooltip: '기본 값:{ label: "label", value: "value", children: "children" }'
+        }
+      }
+    }
+  }
+}

+ 11 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Checkbox.ts

@@ -0,0 +1,11 @@
+export const CheckboxGroup = {
+  'zh-CN': {
+    title: '复选框组'
+  },
+  'en-US': {
+    title: 'Checkbox'
+  },
+  'ko-KR': {
+    title: '체크박스'
+  }
+}

+ 53 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Component.ts

@@ -0,0 +1,53 @@
+export const Component = {
+  'zh-CN': {
+    settings: {
+      style: {
+        width: '宽度',
+        height: '高度',
+        display: '展示',
+        background: '背景',
+        boxShadow: '阴影',
+        font: '字体',
+        margin: '外边距',
+        padding: '内边距',
+        borderRadius: '圆角',
+        border: '边框',
+        opacity: '透明度'
+      }
+    }
+  },
+  'en-US': {
+    settings: {
+      style: {
+        width: 'Width',
+        height: 'Height',
+        display: 'Display',
+        background: 'Background',
+        boxShadow: 'Box Shadow',
+        font: 'Font',
+        margin: 'Margin',
+        padding: 'Padding',
+        borderRadius: 'Radius',
+        border: 'Border',
+        opacity: 'Opacity'
+      }
+    }
+  },
+  'ko-KR': {
+    settings: {
+      style: {
+        width: '너비',
+        height: '높이',
+        display: '디스플레이',
+        background: '배경',
+        boxShadow: '쉐도우 박스',
+        font: '폰트',
+        margin: '마진',
+        padding: '패딩',
+        borderRadius: '테두리 굴곡',
+        border: '테두리',
+        opacity: '투명도'
+      }
+    }
+  }
+}

+ 88 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/DatePicker.ts

@@ -0,0 +1,88 @@
+import { createLocales } from '@designable/core'
+
+export const DatePicker = {
+  'zh-CN': {
+    title: '日期选择',
+    settings: {
+      'x-component-props': {
+        disabledDate: {
+          title: '不可选日期',
+          tooltip: '格式 (currentDate: moment) => boolean'
+        },
+        disabledTime: {
+          title: '不可选时间',
+          tooltip: '格式 (currentDate: moment) => boolean'
+        },
+        inputReadOnly: '输入框只读',
+        format: '格式',
+        picker: {
+          title: '选择器类型',
+          dataSource: ['时间', '日期', '月份', '年', '季度', '财年']
+        },
+        showNow: '显示此刻',
+        showTime: '时间选择',
+        showToday: '显示今天'
+      }
+    }
+  },
+  'en-US': {
+    title: 'DatePicker',
+    settings: {
+      'x-component-props': {
+        disabledDate: {
+          title: 'Disabled Date',
+          tooltip: 'Format (currentDate: moment) => boolean'
+        },
+        disabledTime: {
+          title: 'Disabled Time',
+          tooltip: 'Format (currentDate: moment) => boolean'
+        },
+        inputReadOnly: 'Input ReadOnly',
+        format: 'Format',
+        picker: {
+          title: 'Picker Type',
+          dataSource: ['Time', 'Date', 'Month', 'Year', 'Quarter', 'Decade']
+        },
+        showNow: 'Show Now',
+        showTime: 'Show Time',
+        showToday: 'Show Today'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '날짜 선택 상자',
+    settings: {
+      'x-component-props': {
+        disabledDate: {
+          title: '비활성화 된 날짜',
+          tooltip: '형식 (currentDate: moment) => boolean'
+        },
+        disabledTime: {
+          title: '비활성화 된 시간',
+          tooltip: '형식 (currentDate: moment) => boolean'
+        },
+        inputReadOnly: 'ReadOnly',
+        format: '포맷',
+        picker: {
+          title: '타입',
+          dataSource: ['시간', '날짜', '월', '년', '분기', '십년 단위']
+        },
+        showNow: '현재 시각 보기',
+        showTime: '시간 보기',
+        showToday: '오늘 보기'
+      }
+    }
+  }
+}
+
+export const DateRangePicker = createLocales(DatePicker, {
+  'zh-CN': {
+    title: '日期范围'
+  },
+  'en-US': {
+    title: 'DateRange'
+  },
+  'ko-KR': {
+    title: '날짜범위 선택 상자'
+  }
+})

+ 316 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Field.ts

@@ -0,0 +1,316 @@
+export const Field = {
+  'zh-CN': {
+    settings: {
+      name: '字段标识',
+      title: '标题',
+      required: '必填',
+      description: '描述',
+      default: '默认值',
+      enum: '可选项',
+      'x-display': {
+        title: '展示状态',
+        tooltip: '半隐藏只会隐藏UI,全隐藏会删除数据',
+        dataSource: ['显示', '半隐藏', '全隐藏', '继承']
+      },
+      'x-pattern': {
+        title: 'UI形态',
+        dataSource: ['可编辑', '禁用', '只读', '阅读', '继承']
+      },
+      'x-validator': '校验规则',
+      'x-decorator': '容器组件',
+      'x-reactions': '响应器规则',
+      'field-group': '字段属性',
+      'component-group': '组件属性',
+      'decorator-group': '容器属性',
+      'component-style-group': '组件样式',
+      'decorator-style-group': '容器样式',
+      'x-component-props': {
+        size: { title: '尺寸', dataSource: ['大', '小', '默认', '继承'] },
+        allowClear: '允许清除内容',
+        autoFocus: '自动获取焦点',
+        showSearch: '支持搜索',
+        notFoundContent: '空状态内容',
+        bordered: '是否有边框',
+        placeholder: '占位提示',
+        style: {
+          width: '宽度',
+          height: '高度',
+          display: '展示',
+          background: '背景',
+          boxShadow: '阴影',
+          font: '字体',
+          margin: '外边距',
+          padding: '内边距',
+          borderRadius: '圆角',
+          border: '边框',
+          opacity: '透明度'
+        }
+      },
+      'x-decorator-props': {
+        addonAfter: '后缀标签',
+        addonBefore: '前缀标签',
+        tooltip: '提示',
+        asterisk: '星号',
+        gridSpan: '网格跨列',
+        labelCol: '标签网格宽度',
+        wrapperCol: '组件网格宽度',
+        colon: '是否有冒号',
+        labelAlign: {
+          title: '标签对齐',
+          dataSource: ['左对齐', '右对齐', '继承']
+        },
+        wrapperAlign: {
+          title: '组件对齐',
+          dataSource: ['左对齐', '右对齐', '继承']
+        },
+        labelWrap: '标签换行',
+        wrapperWrap: '组件换行',
+        labelWidth: '标签宽度',
+        wrapperWidth: '组件宽度',
+        fullness: '组件占满',
+        inset: '内联布局',
+        shallow: '是否浅传递',
+        bordered: '是否有边框',
+        size: { title: '尺寸', dataSource: ['大', '小', '默认', '继承'] },
+        layout: { title: '布局', dataSource: ['垂直', '水平', '内联', '继承'] },
+        feedbackLayout: {
+          title: '反馈布局',
+          dataSource: ['宽松', '紧凑', '弹层', '无', '继承']
+        },
+        tooltipLayout: {
+          title: '提示布局',
+          dataSource: ['图标', '文本', '继承']
+        },
+        style: {
+          width: '宽度',
+          height: '高度',
+          display: '展示',
+          background: '背景',
+          boxShadow: '阴影',
+          font: '字体',
+          margin: '外边距',
+          padding: '内边距',
+          borderRadius: '圆角',
+          border: '边框',
+          opacity: '透明度'
+        }
+      }
+    }
+  },
+  'en-US': {
+    settings: {
+      name: 'Name',
+      title: 'Title',
+      required: 'Required',
+      description: 'Description',
+      default: 'Default',
+      enum: 'Options',
+      'x-display': {
+        title: 'Display State',
+        tooltip:
+          'When the display value is "None", the data will be "Hidden" and deleted. When the display value is hidden, only the UI will be hidden',
+        dataSource: ['Visible', 'Hidden', 'None', 'Inherit']
+      },
+      'x-pattern': {
+        title: 'UI Pattern',
+        dataSource: ['Editable', 'Disabled', 'ReadOnly', 'ReadPretty', 'Inherit']
+      },
+      'x-validator': 'Validator',
+      'x-decorator': 'Decorator',
+      'x-reactions': 'Reactions',
+      'field-group': 'Field Properties',
+      'component-group': 'Component Properties',
+      'decorator-group': 'Decorator Properties',
+      'component-style-group': 'Component Style',
+      'decorator-style-group': 'Decorator Style',
+      'x-component-props': {
+        size: {
+          title: 'Size',
+          dataSource: ['Large', 'Small', 'Default', 'Inherit']
+        },
+        allowClear: 'Allow Clear',
+        autoFocus: 'Auto Focus',
+        showSearch: 'Show Search',
+        notFoundContent: 'Not Found Content',
+        bordered: 'Bordered',
+        placeholder: 'Placeholder',
+        style: {
+          width: 'Width',
+          height: 'Height',
+          display: 'Display',
+          background: 'Background',
+          boxShadow: 'Box Shadow',
+          font: 'Font',
+          margin: 'Margin',
+          padding: 'Padding',
+          borderRadius: 'Radius',
+          border: 'Border',
+          opacity: 'Opacity'
+        }
+      },
+      'x-decorator-props': {
+        addonAfter: 'Addon After',
+        addonBefore: 'Addon Before',
+        tooltip: 'Tooltip',
+        asterisk: 'Asterisk',
+        gridSpan: 'Grid Span',
+        labelCol: 'Label Col',
+        wrapperCol: 'Wrapper Col',
+        colon: 'Colon',
+        labelAlign: {
+          title: 'Label Align',
+          dataSource: ['Left', 'Right', 'Inherit']
+        },
+        wrapperAlign: {
+          title: 'Wrapper Align',
+          dataSource: ['Left', 'Right', 'Inherit']
+        },
+        labelWrap: 'Label Wrap',
+        wrapperWrap: 'Wrapper Wrap',
+        labelWidth: 'Label Width',
+        wrapperWidth: 'Wrapper Width',
+        fullness: 'Fullness',
+        inset: 'Inset',
+        shallow: 'Shallow',
+        bordered: 'Bordered',
+        size: {
+          title: 'Size',
+          dataSource: ['Large', 'Small', 'Default', 'Inherit']
+        },
+        layout: {
+          title: 'Layout',
+          dataSource: ['Vertical', 'Horizontal', 'Inline', 'Inherit']
+        },
+        feedbackLayout: {
+          title: 'Feedback Layout',
+          dataSource: ['Loose', 'Terse', 'Popup', 'None', 'Inherit']
+        },
+        tooltipLayout: {
+          title: 'Tooltip Layout',
+          dataSource: ['Icon', 'Text', 'Inherit']
+        },
+        style: {
+          width: 'Width',
+          height: 'Height',
+          display: 'Display',
+          background: 'Background',
+          boxShadow: 'Box Shadow',
+          font: 'Font',
+          margin: 'Margin',
+          padding: 'Padding',
+          borderRadius: 'Radius',
+          border: 'Border',
+          opacity: 'Opacity'
+        }
+      }
+    }
+  },
+  'ko-KR': {
+    settings: {
+      name: '이름',
+      title: '제목',
+      required: '필수항목',
+      description: '설명',
+      default: '기본 값',
+      enum: 'Options',
+      'x-display': {
+        title: '디스플레이 상태',
+        tooltip:
+          '디스플레이 값이 "없음"이면 데이터가 "숨김"상태가 되고 삭제됩니다. 디스플레이 값이 숨김이면 UI만 숨겨집니다.',
+        dataSource: ['보임', '숨김', '없음', '상속']
+      },
+      'x-pattern': {
+        title: 'UI 패턴',
+        dataSource: ['수정 가능', '비활성화', 'ReadOnly', 'ReadPretty', '상속']
+      },
+      'x-validator': '유효성 검사 도구',
+      'x-decorator': '데코레이터',
+      'x-reactions': 'Reactions',
+      'field-group': '필드 속성',
+      'component-group': '컴포넌트 속성',
+      'decorator-group': '데코레이터 속성',
+      'component-style-group': '컴포넌트 스타일',
+      'decorator-style-group': '데코레이터 스타일',
+      'x-component-props': {
+        size: {
+          title: '크기',
+          dataSource: ['크게', '작게', '보통', '상속']
+        },
+        allowClear: '삭제 허용',
+        autoFocus: '오토 포커스',
+        showSearch: '검색 보기',
+        notFoundContent: '값을 찾을 수 없음',
+        bordered: 'Bordered',
+        placeholder: 'Placeholder',
+        style: {
+          width: '너비',
+          height: '높이',
+          display: '디스플레이',
+          background: '배경',
+          boxShadow: '그림자 박스',
+          font: '폰트',
+          margin: '마진',
+          padding: '패딩',
+          borderRadius: '테두리 굴곡',
+          border: '테두리',
+          opacity: '투명도'
+        }
+      },
+      'x-decorator-props': {
+        addonAfter: '에드온 뒤',
+        addonBefore: '에드온 전',
+        tooltip: '툴팁',
+        asterisk: '별표시',
+        gridSpan: 'Grid Span',
+        labelCol: 'Label Col',
+        wrapperCol: 'Wrapper Col',
+        colon: 'Colon',
+        labelAlign: {
+          title: 'Label 정렬',
+          dataSource: ['왼쪽', '오른쪽', '상속']
+        },
+        wrapperAlign: {
+          title: 'Wrapper 정렬',
+          dataSource: ['왼쪽', '오른쪽', '상속']
+        },
+        labelWrap: 'Label Wrap',
+        wrapperWrap: 'Wrapper Wrap',
+        labelWidth: 'Label Width',
+        wrapperWidth: 'Wrapper Width',
+        fullness: 'Fullness',
+        inset: 'Inset',
+        shallow: '얇게',
+        bordered: 'Bordered',
+        size: {
+          title: '크기',
+          dataSource: ['크게', '작게', '보통', '상속']
+        },
+        layout: {
+          title: '레이아웃',
+          dataSource: ['수직', '수평', '인라인', '상속']
+        },
+        feedbackLayout: {
+          title: '피드백 레이아웃',
+          dataSource: ['느슨하게', 'Terse', '팝업', '없음', '상속']
+        },
+        tooltipLayout: {
+          title: '툴팁 레이아웃',
+          dataSource: ['아이콘', '텍스트', '상속']
+        },
+        style: {
+          width: '너비',
+          height: '높이',
+          display: '디스플레이',
+          background: '배경',
+          boxShadow: '그림자 박수',
+          font: '폰트',
+          margin: '마진',
+          padding: '패딩',
+          borderRadius: '테두리 굴곡',
+          border: '테두리',
+          opacity: '투명도'
+        }
+      }
+    }
+  }
+}

+ 119 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Form.ts

@@ -0,0 +1,119 @@
+import { createLocales } from '@designable/core'
+import { Component } from './Component'
+
+export const Form = createLocales(Component, {
+  'zh-CN': {
+    title: '表单',
+    settings: {
+      labelCol: '标签网格宽度',
+      wrapperCol: '组件网格宽度',
+      colon: '是否有冒号',
+      labelAlign: {
+        title: '标签对齐',
+        dataSource: ['左对齐', '右对齐', '继承']
+      },
+      wrapperAlign: {
+        title: '组件对齐',
+        dataSource: ['左对齐', '右对齐', '继承']
+      },
+      labelWrap: '标签换行',
+      wrapperWrap: '组件换行',
+      labelWidth: '标签宽度',
+      wrapperWidth: '组件宽度',
+      fullness: '组件占满',
+      inset: '内联布局',
+      shallow: '是否浅传递',
+      bordered: '是否有边框',
+      size: { title: '尺寸', dataSource: ['大', '小', '默认', '继承'] },
+      layout: { title: '布局', dataSource: ['垂直', '水平', '内联', '继承'] },
+      feedbackLayout: {
+        title: '反馈布局',
+        dataSource: ['宽松', '紧凑', '弹层', '无', '继承']
+      },
+      tooltipLayout: {
+        title: '提示布局',
+        dataSource: ['图标', '文本', '继承']
+      }
+    }
+  },
+  'en-US': {
+    title: 'Form',
+    settings: {
+      labelCol: 'Label Col',
+      wrapperCol: 'Wrapper Col',
+      colon: 'Colon',
+      labelAlign: {
+        title: 'Label Align',
+        dataSource: ['Left', 'Right', 'Inherit']
+      },
+      wrapperAlign: {
+        title: 'Wrapper Align',
+        dataSource: ['Left', 'Right', 'Inherit']
+      },
+      labelWrap: 'Label Wrap',
+      wrapperWrap: 'Wrapper Wrap',
+      labelWidth: 'Label Width',
+      wrapperWidth: 'Wrapper Width',
+      fullness: 'Fullness',
+      inset: 'Inset',
+      shallow: 'Shallow',
+      bordered: 'Bordered',
+      size: {
+        title: 'Size',
+        dataSource: ['Large', 'Small', 'Default', 'Inherit']
+      },
+      layout: {
+        title: 'Layout',
+        dataSource: ['Vertical', 'Horizontal', 'Inline', 'Inherit']
+      },
+      feedbackLayout: {
+        title: 'Feedback Layout',
+        dataSource: ['Loose', 'Terse', 'Popup', 'None', 'Inherit']
+      },
+      tooltipLayout: {
+        title: 'Tooltip Layout',
+        dataSource: ['Icon', 'Text', 'Inherit']
+      }
+    }
+  },
+  'ko-KR': {
+    title: '폼',
+    settings: {
+      labelCol: 'Label Col',
+      wrapperCol: 'Wrapper Col',
+      colon: 'Colon',
+      labelAlign: {
+        title: 'Label 정렬',
+        dataSource: ['왼쪽', '오른쪽', '상속']
+      },
+      wrapperAlign: {
+        title: 'Wrapper 정렬',
+        dataSource: ['왼쪽', '오른쪽', '상속']
+      },
+      labelWrap: 'Label Wrap',
+      wrapperWrap: 'Wrapper Wrap',
+      labelWidth: 'Label Width',
+      wrapperWidth: 'Wrapper Width',
+      fullness: 'Fullness',
+      inset: 'Inset',
+      shallow: 'Shallow',
+      bordered: 'Bordered',
+      size: {
+        title: '크기',
+        dataSource: ['크게', '작게', '보통', '상속']
+      },
+      layout: {
+        title: '레이아웃',
+        dataSource: ['수직', '수평', '인라인', '상속']
+      },
+      feedbackLayout: {
+        title: '피드백 레이아웃',
+        dataSource: ['Loose', 'Terse', '팝업', '없음', '상속']
+      },
+      tooltipLayout: {
+        title: '툴팁 레이아웃',
+        dataSource: ['아이콘', '텍스트', '상속']
+      }
+    }
+  }
+})

+ 83 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/FormCollapse.ts

@@ -0,0 +1,83 @@
+export const FormCollapse = {
+  'zh-CN': {
+    title: '折叠面板',
+    addCollapsePanel: '添加面板',
+    settings: {
+      'x-component-props': {
+        accordion: '手风琴模式',
+        collapsible: { title: '可折叠区域', dataSource: ['头部', '禁用'] },
+        ghost: '幽灵模式',
+        bordered: '是否有边框'
+      }
+    }
+  },
+  'en-US': {
+    title: 'Collapse',
+    addCollapsePanel: 'Add Panel',
+    settings: {
+      'x-component-props': {
+        accordion: 'Accordion Mode',
+        collapsible: {
+          title: 'Collapsible',
+          dataSource: ['Header', 'Disable']
+        },
+        ghost: 'Ghost Mode',
+        bordered: 'Bordered'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '드롭다운',
+    addCollapsePanel: '패널 추가',
+    settings: {
+      'x-component-props': {
+        accordion: 'Accordion 모드',
+        collapsible: {
+          title: '드롭 여부',
+          dataSource: ['헤더', '비활성화']
+        },
+        ghost: '고스트 모드',
+        bordered: 'Bordered'
+      }
+    }
+  }
+}
+
+export const FormCollapsePanel = {
+  'zh-CN': {
+    title: '面板',
+    settings: {
+      'x-component-props': {
+        collapsible: { title: '是否可折叠', dataSource: ['头部', '禁用'] },
+        header: '标题',
+        extra: '扩展内容'
+      }
+    }
+  },
+  'en-US': {
+    title: 'Panel',
+    settings: {
+      'x-component-props': {
+        collapsible: {
+          title: 'Collapsible',
+          dataSource: ['Header', 'Disable']
+        },
+        header: 'Header Title',
+        extra: 'Extra Content'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '패널',
+    settings: {
+      'x-component-props': {
+        collapsible: {
+          title: '드롭 여부',
+          dataSource: ['해더', '비활성화']
+        },
+        header: '헤더 제목',
+        extra: '추가 내용'
+      }
+    }
+  }
+}

+ 77 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/FormGrid.ts

@@ -0,0 +1,77 @@
+export const FormGrid = {
+  'zh-CN': {
+    title: '网格布局',
+    addGridColumn: '添加网格列',
+    settings: {
+      'x-component-props': {
+        minWidth: '最小宽度',
+        minColumns: '最小列数',
+        maxWidth: '最大宽度',
+        maxColumns: '最大列数',
+        breakpoints: '响应式断点',
+        columnGap: '列间距',
+        rowGap: '行间距',
+        colWrap: '自动换行'
+      }
+    }
+  },
+  'en-US': {
+    title: 'Grid',
+    addGridColumn: 'Add Grid Column',
+    settings: {
+      'x-component-props': {
+        minWidth: 'Min Width',
+        minColumns: 'Min Columns',
+        maxWidth: 'Max Width',
+        maxColumns: 'Max Columns',
+        breakpoints: 'Breakpoints',
+        columnGap: 'Column Gap',
+        rowGap: 'Row Gap',
+        colWrap: 'Col Wrap'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '그리드 열',
+    addGridColumn: '그리드 열 추가',
+    settings: {
+      'x-component-props': {
+        minWidth: '최소 너비',
+        minColumns: '최소 열 개수',
+        maxWidth: '최대 너비',
+        maxColumns: '최대 열 개수',
+        breakpoints: '중단점',
+        columnGap: '열 간격',
+        rowGap: '행 간격',
+        colWrap: '자동 줄바꿈'
+      }
+    }
+  }
+}
+
+export const FormGridColumn = {
+  'zh-CN': {
+    title: '网格列',
+    settings: {
+      'x-component-props': {
+        gridSpan: '跨列栏数'
+      }
+    }
+  },
+  'en-US': {
+    title: 'Grid Column',
+    settings: {
+      'x-component-props': {
+        gridSpan: 'Grid Span'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '그리드 열',
+    settings: {
+      'x-component-props': {
+        gridSpan: '그리드 스팬'
+      }
+    }
+  }
+}

+ 122 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/FormLayout.ts

@@ -0,0 +1,122 @@
+export const FormLayout = {
+  'zh-CN': {
+    title: '表单布局',
+    settings: {
+      'x-component-props': {
+        labelCol: '标签网格宽度',
+        wrapperCol: '组件网格宽度',
+        colon: '是否有冒号',
+        labelAlign: {
+          title: '标签对齐',
+          dataSource: ['左对齐', '右对齐', '继承']
+        },
+        wrapperAlign: {
+          title: '组件对齐',
+          dataSource: ['左对齐', '右对齐', '继承']
+        },
+        labelWrap: '标签换行',
+        wrapperWrap: '组件换行',
+        labelWidth: '标签宽度',
+        wrapperWidth: '组件宽度',
+        fullness: '组件占满',
+        inset: '内联布局',
+        shallow: '是否浅传递',
+        bordered: '是否有边框',
+        size: { title: '尺寸', dataSource: ['大', '小', '默认', '继承'] },
+        layout: { title: '布局', dataSource: ['水平', '垂直', '内联', '继承'] },
+        feedbackLayout: {
+          title: '反馈布局',
+          dataSource: ['宽松', '紧凑', '弹层', '无', '继承']
+        },
+        tooltipLayout: {
+          title: '提示布局',
+          dataSource: ['图标', '文本', '继承']
+        }
+      }
+    }
+  },
+  'en-US': {
+    title: 'Form Layout',
+    settings: {
+      'x-component-props': {
+        labelCol: 'Label Col',
+        wrapperCol: 'Wrapper Col',
+        colon: 'Colon',
+        labelAlign: {
+          title: 'Label Align',
+          dataSource: ['Left', 'Right', 'Inherit']
+        },
+        wrapperAlign: {
+          title: 'Wrapper Align',
+          dataSource: ['Left', 'Right', 'Inherit']
+        },
+        labelWrap: 'Label Wrap',
+        wrapperWrap: 'Wrapper Wrap',
+        labelWidth: 'Label Width',
+        wrapperWidth: 'Wrapper Width',
+        fullness: 'Fullness',
+        inset: 'Inset',
+        shallow: 'Shallow',
+        bordered: 'Bordered',
+        size: {
+          title: 'Size',
+          dataSource: ['Large', 'Small', 'Default', 'Inherit']
+        },
+        layout: {
+          title: 'Layout',
+          dataSource: ['Horizontal', 'Vertical', 'Inline', 'Inherit']
+        },
+        feedbackLayout: {
+          title: 'Feedback Layout',
+          dataSource: ['Loose', 'Terse', 'Popup', 'None', 'Inherit']
+        },
+        tooltipLayout: {
+          title: 'Tooltip Layout',
+          dataSource: ['Icon', 'Text', 'Inherit']
+        }
+      }
+    }
+  },
+  'ko-KR': {
+    title: '양식 레이아웃',
+    settings: {
+      'x-component-props': {
+        labelCol: '레이블 열',
+        wrapperCol: '래퍼 열',
+        colon: '콜론',
+        labelAlign: {
+          title: '레이블 정렬',
+          dataSource: ['왼쪽', '오른쪽', '상속']
+        },
+        wrapperAlign: {
+          title: '래퍼 정렬',
+          dataSource: ['왼쪽', '오른쪽', '상속']
+        },
+        labelWrap: '레이블 랩',
+        wrapperWrap: '래퍼 랩',
+        labelWidth: '레이블 너비',
+        wrapperWidth: '래퍼 너비',
+        fullness: '충만',
+        inset: '삽입',
+        shallow: '얕다',
+        bordered: '경계',
+        size: {
+          title: '크기',
+          dataSource: ['큰', '작은', '기본', '상속']
+        },
+        layout: {
+          title: '레이아웃',
+          dataSource: ['가로', '세로', '인라인', '상속']
+        },
+        feedbackLayout: {
+          title: '피드백 레이아웃',
+          dataSource: ['느슨한', '간단한', '팝업', '없음', '상속']
+        },
+        tooltipLayout: {
+          title: '도구 설명 레이아웃',
+          dataSource: ['아이콘', '텍스트', '상속']
+        }
+      }
+    }
+  }
+}

+ 83 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/FormTab.ts

@@ -0,0 +1,83 @@
+export const FormTab = {
+  'zh-CN': {
+    title: '选项卡',
+    addTabPane: '添加选项卡',
+    settings: {
+      'x-component-props': {
+        animated: '启用动画过渡',
+        centered: '标签居中',
+        tab: '选项名称',
+        type: {
+          title: '类型',
+          dataSource: [
+            { label: '线框', value: 'line' },
+            { label: '卡片', value: 'card' }
+          ]
+        }
+      }
+    }
+  },
+  'en-US': {
+    title: 'Tabs',
+    addTabPane: 'Add Panel',
+    settings: {
+      'x-component-props': {
+        animated: 'Enable Animated',
+        centered: 'Label Centered',
+        tab: 'Tab Title',
+        type: {
+          title: 'Type',
+          dataSource: [
+            { label: 'Line', value: 'line' },
+            { label: 'Card', value: 'card' }
+          ]
+        }
+      }
+    }
+  },
+  'ko-KR': {
+    title: '탭',
+    addTabPane: '패널 추가',
+    settings: {
+      'x-component-props': {
+        animated: '애니메이션 활성화',
+        centered: '레이블을 가운데로',
+        tab: '텝 제목',
+        type: {
+          title: '타입',
+          dataSource: [
+            { label: '라인', value: 'line' },
+            { label: '카드', value: 'card' }
+          ]
+        }
+      }
+    }
+  }
+}
+
+export const FormTabPane = {
+  'zh-CN': {
+    title: '选项卡面板',
+    settings: {
+      'x-component-props': {
+        tab: '面板标题'
+      }
+    }
+  },
+  'en-US': {
+    title: 'Tab Panel',
+    settings: {
+      'x-component-props': {
+        tab: 'Panel Title'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '탭 패널',
+    settings: {
+      'x-component-props': {
+        tab: '패널 제목'
+      }
+    }
+  }
+}

+ 50 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Input.ts

@@ -0,0 +1,50 @@
+export const Input = {
+  'zh-CN': {
+    title: '输入框',
+    settings: {
+      'x-component-props': {
+        addonAfter: '后缀标签',
+        addonBefore: '前缀标签',
+        maxLength: '最大长度',
+        prefix: '前缀',
+        suffix: '后缀',
+        autoSize: {
+          title: '自适应高度',
+          tooltip: '可设置为 true | false 或对象:{ minRows: 2, maxRows: 6 }'
+        },
+        showCount: '是否展示字数',
+        checkStrength: '检测强度'
+      }
+    }
+  },
+  'en-US': {
+    title: 'Input',
+    settings: {
+      'x-component-props': {
+        addonAfter: 'Addon After',
+        addonBefore: 'Addon Before',
+        maxLength: 'Max Length',
+        prefix: 'Prefix',
+        suffix: 'Suffix',
+        autoSize: 'Auto Size',
+        showCount: 'Show Count',
+        checkStrength: 'Check Strength'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '입력',
+    settings: {
+      'x-component-props': {
+        addonAfter: '애드온 후',
+        addonBefore: '애드온 전',
+        maxLength: '최대 길이',
+        prefix: '접두사',
+        suffix: '접미사',
+        autoSize: '자동 크기 맞춤',
+        showCount: '개수 보여주기',
+        checkStrength: '강도 체크'
+      }
+    }
+  }
+}

+ 81 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/NumberPicker.ts

@@ -0,0 +1,81 @@
+export const NumberPicker = {
+  'zh-CN': {
+    title: '数字输入',
+    settings: {
+      'x-component-props': {
+        formatter: {
+          title: '格式转换器',
+          tooltip: '格式:function(value: number | string): string'
+        },
+        keyboard: '启用快捷键',
+        parser: {
+          title: '格式解析器',
+          tooltip:
+            '指定从 格式转换器 里转换回数字的方式,和 格式转换器 搭配使用,格式:function(string): number'
+        },
+        decimalSeparator: '小数点',
+        precision: '数字精度',
+        max: '最大值',
+        min: '最小值',
+        step: '步长',
+        stringMode: {
+          title: '字符串格式',
+          tooltip: '开启后支持高精度小数。同时 onChange 将返回 string 类型'
+        }
+      }
+    }
+  },
+  'en-US': {
+    title: 'NumberInput',
+    settings: {
+      'x-component-props': {
+        formatter: {
+          title: 'Format Converter',
+          tooltip: 'Format:function(value: number | string): string'
+        },
+        keyboard: 'Enable Shortcut Keys',
+        parser: {
+          title: 'Format Parser',
+          tooltip:
+            'Specify the method of converting back to numbers from the format converter, and use it with the format converter, the format:function(string): number'
+        },
+        decimalSeparator: 'Decimal Separator',
+        precision: 'Precision',
+        max: 'Max',
+        min: 'Min',
+        step: 'Step',
+        stringMode: {
+          title: 'String Format',
+          tooltip:
+            'Support high-precision decimals after opening. At the same time onChange will return string type'
+        }
+      }
+    }
+  },
+  'ko-KR': {
+    title: '숫자 입력',
+    settings: {
+      'x-component-props': {
+        formatter: {
+          title: '포맷 컨버터',
+          tooltip: '형식: function(value: number | string): string'
+        },
+        keyboard: '숏컷 키 활성화',
+        parser: {
+          title: '포멧 파서',
+          tooltip:
+            '문자열을 숫자로 변환하는 포맷 컨버터를 만들고 사용하세요, 형식 :function(string): number'
+        },
+        decimalSeparator: '소수 구분 기호',
+        precision: '정밀도',
+        max: '최대값',
+        min: '최소값',
+        step: '스탭',
+        stringMode: {
+          title: '문자열 형식',
+          tooltip: '높은 정밀도 소수를 지원합니다. onChange에서 문자열 타입으로 반환됩니다.'
+        }
+      }
+    }
+  }
+}

+ 11 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Object.ts

@@ -0,0 +1,11 @@
+export const ObjectLocale = {
+  'zh-CN': {
+    title: '对象容器'
+  },
+  'en-US': {
+    title: 'Object'
+  },
+  'ko-KR': {
+    title: '오브젝트'
+  }
+}

+ 14 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Password.ts

@@ -0,0 +1,14 @@
+import { createLocales } from '@designable/core'
+import { Input } from './Input'
+
+export const Password = createLocales(Input, {
+  'zh-CN': {
+    title: '密码输入'
+  },
+  'en-US': {
+    title: 'Password'
+  },
+  'ko-KR': {
+    title: '비밀번호'
+  }
+})

+ 29 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Radio.ts

@@ -0,0 +1,29 @@
+export const RadioGroup = {
+  'zh-CN': {
+    title: '单选框组',
+    settings: {
+      'x-component-props': {
+        buttonStyle: { title: '按钮风格', dataSource: ['空心', '实心'] },
+        optionType: { title: '选项类型', dataSource: ['默认', '按钮'] }
+      }
+    }
+  },
+  'en-US': {
+    title: 'Radio',
+    settings: {
+      'x-component-props': {
+        buttonStyle: { title: 'Button style', dataSource: ['Hollow', 'Solid'] },
+        optionType: { title: 'Option type', dataSource: ['Default', 'Button'] }
+      }
+    }
+  },
+  'ko-KR': {
+    title: '라디오',
+    settings: {
+      'x-component-props': {
+        buttonStyle: { title: '버튼 스타일', dataSource: ['Hollow', 'Solid'] },
+        optionType: { title: '옵션 타입', dataSource: ['기본', '버튼'] }
+      }
+    }
+  }
+}

+ 32 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Rate.ts

@@ -0,0 +1,32 @@
+export const Rate = {
+  'zh-CN': {
+    title: '评分器',
+    settings: {
+      'x-component-props': {
+        allowHalf: '允许半选',
+        tooltips: { title: '提示信息', tooltip: '格式:string[]' },
+        count: '总数'
+      }
+    }
+  },
+  'en-US': {
+    title: 'Rate',
+    settings: {
+      'x-component-props': {
+        allowHalf: 'Allow Half',
+        tooltips: { title: 'Tooltips', tooltip: 'Format:string[]' },
+        count: 'Count'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '비율',
+    settings: {
+      'x-component-props': {
+        allowHalf: '절반 허용',
+        tooltips: { title: '툴팁', tooltip: '형식:string[]' },
+        count: '개수'
+      }
+    }
+  }
+}

+ 102 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Select.ts

@@ -0,0 +1,102 @@
+export const Select = {
+  'zh-CN': {
+    title: '选择框',
+    settings: {
+      'x-component-props': {
+        mode: {
+          title: '模式',
+          dataSource: ['多选', '标签', '单选']
+        },
+        autoClearSearchValue: {
+          title: '选中自动清除',
+          tooltip: '仅在多选或者标签模式下支持'
+        },
+        defaultActiveFirstOption: '默认高亮第一个选项',
+        dropdownMatchSelectWidth: {
+          title: '下拉菜单和选择器同宽',
+          tooltip: '默认将设置 min-width,当值小于选择框宽度时会被忽略。false 时会关闭虚拟滚动'
+        },
+        defaultOpen: '默认展开',
+        filterOption: '选项筛选器',
+        filterSort: '选项排序器',
+        labelInValue: {
+          title: '标签值',
+          tooltip:
+            '是否把每个选项的 label 包装到 value 中,会把 Select 的 value 类型从 string 变为 { value: string, label: ReactNode } 的格式'
+        },
+        listHeight: '弹窗滚动高度',
+        maxTagCount: {
+          title: '最多标签数量',
+          tooltip: '最多显示多少个 tag,响应式模式会对性能产生损耗'
+        },
+        maxTagPlaceholder: {
+          title: '最多标签占位',
+          tooltip: '隐藏 tag 时显示的内容'
+        },
+        maxTagTextLength: '最多标签文本长度',
+        showArrow: '显示箭头',
+        virtual: '开启虚拟滚动'
+      }
+    }
+  },
+  'en-US': {
+    title: 'Select',
+    settings: {
+      'x-component-props': {
+        mode: {
+          title: 'Mode',
+          dataSource: ['Multiple', 'Tags', 'Single']
+        },
+        autoClearSearchValue: {
+          title: 'Auto Clear Search Value',
+          tooltip: 'Only used to multiple and tags mode'
+        },
+        defaultActiveFirstOption: 'Default Active First Option',
+        dropdownMatchSelectWidth: 'Dropdown Match Select Width',
+        defaultOpen: 'Default Open',
+        filterOption: 'Filter Option',
+        filterSort: 'Filter Sort',
+        labelInValue: 'label InValue',
+        listHeight: 'List Height',
+        maxTagCount: 'Max Tag Count',
+        maxTagPlaceholder: {
+          title: 'Max Tag Placeholder',
+          tooltip: 'Content displayed when tag is hidden'
+        },
+        maxTagTextLength: 'Max Tag Text Length',
+        showArrow: 'Show Arrow',
+        virtual: 'Use Virtual Scroll'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '선택',
+    settings: {
+      'x-component-props': {
+        mode: {
+          title: '모드',
+          dataSource: ['다중', '태그', '단일']
+        },
+        autoClearSearchValue: {
+          title: '자동 검색 값 삭제',
+          tooltip: '다중 모드와 태그 모드만 사용할 수 있습니다.'
+        },
+        defaultActiveFirstOption: '기본으로 첫번째 옵션을 선택함',
+        dropdownMatchSelectWidth: '드롭다운 너비와 일치시킴',
+        defaultOpen: '기본 오픈',
+        filterOption: '옵션 필터',
+        filterSort: '정렬 필터',
+        labelInValue: '레이블 InValue',
+        listHeight: '리스트 높이',
+        maxTagCount: '최대 태그 개수',
+        maxTagPlaceholder: {
+          title: '최대 태그 Placeholder',
+          tooltip: '태그가 숨겨질때 보입니다.'
+        },
+        maxTagTextLength: '최대 태그 텍스트 길이',
+        showArrow: '화살표 보기',
+        virtual: '수직 스크롤 사용'
+      }
+    }
+  }
+}

+ 73 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Slider.ts

@@ -0,0 +1,73 @@
+export const Slider = {
+  'zh-CN': {
+    title: '滑动条',
+    settings: {
+      'x-component-props': {
+        dots: '刻度固定',
+        range: '双滑块',
+        reverse: '反向坐标系',
+        vertical: '垂直布局',
+        tooltipPlacement: {
+          title: '提示位置',
+          tooltip: '设置 提示 展示位置。参考 Tooltip'
+        },
+        tooltipVisible: {
+          title: '提示显示',
+          tooltip: '开启时,提示 将会始终显示;否则始终不显示,哪怕在拖拽及移入时'
+        },
+        max: '最大值',
+        min: '最小值',
+        step: '步长',
+        marks: '刻度标签'
+      }
+    }
+  },
+  'en-US': {
+    title: 'Slider',
+    settings: {
+      'x-component-props': {
+        dots: 'Fixed Scale',
+        range: 'Double Slider',
+        reverse: 'Reverse Coordinate System',
+        vertical: 'Vertical',
+        tooltipPlacement: {
+          title: 'Tooltip Placement',
+          tooltip: 'Set up prompt placement. Reference Tooltip'
+        },
+        tooltipVisible: {
+          title: 'Tooltip Visible',
+          tooltip:
+            'When turned on, the prompt will always be displayed; otherwise, it will always not be displayed, even when dragging and moving in'
+        },
+        max: 'Max',
+        min: 'Min',
+        step: 'Step',
+        marks: 'Marks'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '슬라이더',
+    settings: {
+      'x-component-props': {
+        dots: '고정된 크기',
+        range: '더블 슬라이더',
+        reverse: '뒤집어진 좌표 시스템',
+        vertical: '수직',
+        tooltipPlacement: {
+          title: '툴팁 배치',
+          tooltip: '툴팁이 나오는 위치를 설정'
+        },
+        tooltipVisible: {
+          title: '툴팁 보기',
+          tooltip:
+            '이 옵션을 켜면 툴팁이 항상 표시됩니다. 그렇지 않으면 끌어서 이동할 때도 툴팁이 항상 표시되지 않습니다'
+        },
+        max: '최댓값',
+        min: '최솟값',
+        step: '스탭',
+        marks: '마크'
+      }
+    }
+  }
+}

+ 50 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Space.ts

@@ -0,0 +1,50 @@
+export const Space = {
+  'zh-CN': {
+    title: '弹性间距',
+    settings: {
+      'x-component-props': {
+        direction: { title: '方向', dataSource: ['垂直', '水平'] },
+        split: '分割内容',
+        wrap: '自动换行',
+        align: {
+          title: '对齐',
+          dataSource: ['头部', '尾部', '居中', '基准线']
+        }
+      }
+    }
+  },
+  'en-US': {
+    title: 'Space',
+    settings: {
+      'x-component-props': {
+        direction: {
+          title: 'Direction',
+          dataSource: ['Vertical', 'Horizontal']
+        },
+        split: 'Split',
+        wrap: 'Word Wrap',
+        align: {
+          title: 'Align',
+          dataSource: ['Start', 'End', 'Center', 'Baseline']
+        }
+      }
+    }
+  },
+  'ko-KR': {
+    title: '간격',
+    settings: {
+      'x-component-props': {
+        direction: {
+          title: '방향',
+          dataSource: ['수직', '수평']
+        },
+        split: '분할',
+        wrap: '자동 줄 바꿈',
+        align: {
+          title: '정렬',
+          dataSource: ['시작', '끝', '가운데', '기준선']
+        }
+      }
+    }
+  }
+}

+ 11 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Switch.ts

@@ -0,0 +1,11 @@
+export const Switch = {
+  'zh-CN': {
+    title: '开关'
+  },
+  'en-US': {
+    title: 'Switch'
+  },
+  'ko-KR': {
+    title: '스위치'
+  }
+}

+ 38 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Text.ts

@@ -0,0 +1,38 @@
+export const Text = {
+  'zh-CN': {
+    title: '文本',
+    settings: {
+      'x-component-props': {
+        content: '文本内容',
+        mode: {
+          title: '文本类型',
+          dataSource: ['H1', 'H2', 'H3', 'Paragraph', 'Normal']
+        }
+      }
+    }
+  },
+  'en-US': {
+    title: 'Text',
+    settings: {
+      'x-component-props': {
+        content: 'Text Content',
+        mode: {
+          title: 'Text Mode',
+          dataSource: ['H1', 'H2', 'H3', 'Paragraph', 'Normal']
+        }
+      }
+    }
+  },
+  'ko-KR': {
+    title: '텍스트',
+    settings: {
+      'x-component-props': {
+        content: '텍스트 내용',
+        mode: {
+          title: '텍스트 모드',
+          dataSource: ['H1', 'H2', 'H3', 'Paragraph', 'Normal']
+        }
+      }
+    }
+  }
+}

+ 35 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/TextArea.ts

@@ -0,0 +1,35 @@
+export const TextArea = {
+  'zh-CN': {
+    title: '多行输入',
+    settings: {
+      'x-component-props': {
+        maxLength: '最大长度',
+        autoSize: {
+          title: '自适应高度',
+          tooltip: '可设置为 true | false 或对象:{ minRows: 2, maxRows: 6 }'
+        },
+        showCount: '是否展示字数'
+      }
+    }
+  },
+  'en-US': {
+    title: 'TextArea',
+    settings: {
+      'x-component-props': {
+        maxLength: 'Max Length',
+        autoSize: 'Auto Size',
+        showCount: 'Show Count'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '텍스트 상자',
+    settings: {
+      'x-component-props': {
+        maxLength: '최대 길이',
+        autoSize: '자동 길이 변환',
+        showCount: '개수 보기'
+      }
+    }
+  }
+}

+ 74 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/TimePicker.ts

@@ -0,0 +1,74 @@
+import { createLocales } from '@designable/core'
+import { DatePicker } from './DatePicker'
+
+export const TimePicker = createLocales(DatePicker, {
+  'zh-CN': {
+    title: '时间选择',
+    settings: {
+      'x-component-props': {
+        clearText: '清除提示',
+        disabledHours: '禁止小时',
+        disabledMinutes: '禁止分钟',
+        disabledSeconds: '禁止秒',
+        hideDisabledOptions: '隐藏禁止选项',
+        hourStep: '小时间隔',
+        minuteStep: '分钟间隔',
+        secondStep: '秒间隔',
+        use12Hours: '12小时制',
+        inputReadOnly: '输入框只读',
+        showNow: '显示此刻',
+        format: '格式'
+      }
+    }
+  },
+  'en-US': {
+    title: 'Time Picker',
+    settings: {
+      'x-component-props': {
+        clearText: 'Clear Text',
+        disabledHours: 'Disbaled Hours',
+        disabledMinutes: 'Disabled Minutes',
+        disabledSeconds: 'Disabled Seconds',
+        hideDisabledOptions: 'Hide Disabled Options',
+        hourStep: 'Hour Step',
+        minuteStep: 'Minute Step',
+        secondStep: 'Second Step',
+        use12Hours: 'Use 12-hour',
+        inputReadOnly: 'Input ReadOnly',
+        showNow: 'Show Now',
+        format: 'Format'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '시간 선택',
+    settings: {
+      'x-component-props': {
+        clearText: '텍스트 삭제',
+        disabledHours: '시 비활성화',
+        disabledMinutes: '분 비활성화',
+        disabledSeconds: '초 비활성화',
+        hideDisabledOptions: '비황성화 옵션 숨기기',
+        hourStep: '시 스탭',
+        minuteStep: '분 스탭',
+        secondStep: '초 스탭',
+        use12Hours: '12시간 단위 사용',
+        inputReadOnly: 'ReadOnly',
+        showNow: '현재 시각 보여주기',
+        format: '포맷'
+      }
+    }
+  }
+})
+
+export const TimeRangePicker = createLocales(TimePicker, {
+  'zh-CN': {
+    title: '时间范围'
+  },
+  'en-US': {
+    title: 'Time Range'
+  },
+  'ko-KR': {
+    title: '시간 범위'
+  }
+})

+ 38 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Transfer.ts

@@ -0,0 +1,38 @@
+export const Transfer = {
+  'zh-CN': {
+    title: '穿梭框',
+    settings: {
+      'x-component-props': {
+        oneWay: '单向展示',
+        operations: { title: '操作文案集合', tooltip: '格式:string[]' },
+        titles: { title: '标题集合', tooltip: '格式:string[]' },
+        showSearchAll: '支持全选',
+        filterOption: '选项筛选器'
+      }
+    }
+  },
+  'en-US': {
+    title: 'Transfer',
+    settings: {
+      'x-component-props': {
+        oneWay: 'One Way',
+        operations: { title: 'Operations', tooltip: 'Format:string[]' },
+        titles: { title: 'Titles', tooltip: 'Format:string[]' },
+        showSearchAll: 'Show Search All',
+        filterOption: 'Filter Option'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '전송',
+    settings: {
+      'x-component-props': {
+        oneWay: '하나의 방법',
+        operations: { title: '작업', tooltip: '형식:string[]' },
+        titles: { title: '제목', tooltip: '형식:string[]' },
+        showSearchAll: '모든 검색 결과 보여주기',
+        filterOption: '필터 옵션'
+      }
+    }
+  }
+}

+ 183 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/TreeSelect.ts

@@ -0,0 +1,183 @@
+export const TreeSelect = {
+  'zh-CN': {
+    title: '树选择',
+    settings: {
+      'x-component-props': {
+        mode: {
+          title: '模式',
+          dataSource: ['多选', '标签', '单选']
+        },
+        autoClearSearchValue: {
+          title: '选中自动清除',
+          tooltip: '仅在多选或者标签模式下支持'
+        },
+        defaultActiveFirstOption: '默认高亮第一个选项',
+        defaultOpen: '默认展开',
+        filterOption: '选项筛选器',
+        filterSort: '选项排序器',
+        labelInValue: {
+          title: '标签值',
+          tooltip:
+            '是否把每个选项的 label 包装到 value 中,会把 Select 的 value 类型从 string 变为 { value: string, label: ReactNode } 的格式'
+        },
+        listHeight: '弹窗滚动高度',
+        maxTagCount: {
+          title: '最多标签数量',
+          tooltip: '最多显示多少个 tag,响应式模式会对性能产生损耗'
+        },
+        maxTagPlaceholder: {
+          title: '最多标签占位',
+          tooltip: '隐藏 tag 时显示的内容'
+        },
+        maxTagTextLength: '最多标签文本长度',
+        showArrow: '显示箭头',
+        virtual: '开启虚拟滚动',
+        dropdownMatchSelectWidth: {
+          title: '下拉选择器同宽',
+          tooltip: '默认将设置 min-width,当值小于选择框宽度时会被忽略。false 时会关闭虚拟滚动'
+        },
+        showCheckedStrategy: {
+          title: '复选回显策略',
+          tooltip:
+            '配置 treeCheckable 时,定义选中项回填的方式。TreeSelect.SHOW_ALL: 显示所有选中节点(包括父节点)。TreeSelect.SHOW_PARENT: 只显示父节点(当父节点下所有子节点都选中时)。 默认只显示子节点',
+          dataSource: ['显示所有', '显示父节点', '显示子节点']
+        },
+        treeCheckable: '开启复选',
+        treeDefaultExpandAll: '默认展开所有',
+        treeDefaultExpandedKeys: {
+          title: '默认展开选项',
+          tooltip: '格式:Array<string | number>'
+        },
+        treeNodeFilterProp: {
+          title: '节点过滤属性',
+          tooltip: '输入项过滤对应的 treeNode 属性'
+        },
+        treeDataSimpleMode: {
+          title: '使用简单数据结构',
+          tooltip: `使用简单格式的 treeData,具体设置参考可设置的类型 (此时 treeData 应变为这样的数据结构: [{id:1, pId:0, value:'1', title:"test1",...},...], pId 是父节点的 id)`
+        },
+        treeNodeLabelProp: { title: '标签显示名称', tooltip: '默认为title' },
+        filterTreeNode: '节点过滤器'
+      }
+    }
+  },
+  'en-US': {
+    title: 'TreeSelect',
+    settings: {
+      'x-component-props': {
+        mode: {
+          title: 'Mode',
+          dataSource: ['Multiple', 'Tags', 'Single']
+        },
+        autoClearSearchValue: {
+          title: 'Auto Clear Search Value',
+          tooltip: 'Only used to multiple and tags mode'
+        },
+        defaultActiveFirstOption: 'Default Active First Option',
+        defaultOpen: 'Default Open',
+        filterOption: 'Filter Option',
+        filterSort: 'Filter Sort',
+        labelInValue: 'Label In Value',
+        listHeight: 'List Height',
+        maxTagCount: 'Max Tag Count',
+        maxTagPlaceholder: {
+          title: 'Max Tag Placeholder',
+          tooltip: 'Content displayed when tag is hidden'
+        },
+        maxTagTextLength: 'Max Tag Text Length',
+        notFoundContent: 'Not Found Content',
+        showArrow: 'Show Arrow',
+        virtual: 'Use Virtual Scroll',
+        dropdownMatchSelectWidth: {
+          title: 'Dropdown Match Select Width',
+          tooltip:
+            'By default, min-width will be set, and it will be ignored when the value is less than the width of the selection box. false will turn off virtual scrolling'
+        },
+        showCheckedStrategy: {
+          title: 'Show Checked Strategy',
+          tooltip:
+            'When configuring treeCheckable, define how to backfill the selected item. TreeSelect.SHOW_ALL: Show all selected nodes (including parent nodes). TreeSelect.SHOW_PARENT: Only display the parent node (when all child nodes under the parent node are selected). Only show child nodes by default',
+          dataSource: ['Show All', 'Show Parent Node', 'Show Child Nodes']
+        },
+        treeCheckable: 'Tree Checkable',
+        treeDefaultExpandAll: 'Tree Default Expand All',
+        treeDefaultExpandedKeys: {
+          title: 'Tree Default Expanded Keys',
+          tooltip: 'Format:Array<string | number>'
+        },
+        treeNodeFilterProp: {
+          title: 'Tree Node Filter Properties',
+          tooltip: 'The treeNode attribute corresponding to the input item filter'
+        },
+        treeDataSimpleMode: {
+          title: 'Tree Data Simple Mode',
+          tooltip: `Use treeData in a simple format. For specific settings, refer to the settable type (the treeData should be a data structure like this: [{id:1, pId:0, value:'1', title:"test1",...} ,...], pId is the id of the parent node)`
+        },
+        treeNodeLabelProp: {
+          title: 'Tree Node Label Properties',
+          tooltip: 'The default is title'
+        },
+        filterTreeNode: 'Filter Tree Node'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '트리 셀렉터',
+    settings: {
+      'x-component-props': {
+        mode: {
+          title: '모드',
+          dataSource: ['다중', '태그', '단일']
+        },
+        autoClearSearchValue: {
+          title: '검색값 자동 삭제',
+          tooltip: '다중 모드와 태그 모드에서만 사용 가능'
+        },
+        defaultActiveFirstOption: '기본 활성값으로 첫번째 옵션 사용',
+        defaultOpen: '기본 오픈',
+        filterOption: '옵션 필터',
+        filterSort: '정렬 필터',
+        labelInValue: '레이블 입력 값',
+        listHeight: '리스트 높이',
+        maxTagCount: '최대 태그 개수',
+        maxTagPlaceholder: {
+          title: '최대 태그 Placeholder',
+          tooltip: '태그가 숨김일때 보여줍니다.'
+        },
+        maxTagTextLength: '최대 태그 문자 길이',
+        notFoundContent: '내용 없음',
+        showArrow: '화살표 보기',
+        virtual: '수직 스크롤 사용',
+        dropdownMatchSelectWidth: {
+          title: '드롭다운 너비 맞추기',
+          tooltip: '기본적으로 최소 너비가 설정되며 값이 선택 상자의 너비보다 작으면 무시됩니다.'
+        },
+        showCheckedStrategy: {
+          title: '선택한 전략 표시',
+          tooltip:
+            'treeCheckable을 구성할 때 선택한 항목을 다시 채우는 방법을 정의합니다. TreeSelect.SHOW_ALL: 선택한 모든 노드(상위 노드 포함)를 표시합니다. TreeSelect.SHOW_PARTE: 상위 노드 아래의 모든 하위 노드를 선택한 경우에만 상위 노드를 표시합니다. 기본적으로 하위 노드만 표시합니다',
+          dataSource: ['모두 보기', '부모 노드만 보기', '자식 노드만 보기']
+        },
+        treeCheckable: '트리 체크 가능 여부',
+        treeDefaultExpandAll: '트리 기본 모두 확장',
+        treeDefaultExpandedKeys: {
+          title: '트리 기본 확장 키',
+          tooltip: '형식:Array<string | number>'
+        },
+        treeNodeFilterProp: {
+          title: '트리 노드 필터 속성',
+          tooltip: '입력 항목 필터에 해당하는 treeNode 속성'
+        },
+        treeDataSimpleMode: {
+          title: '트리 데이터 심플 모드',
+          tooltip: `treeData를 간단한 형식으로 사용합니다. 특정 설정은 설정가능한 유형을 참조하세요 (트리데이터는 다음과 같은 데이터 구조여야 합니다: [{id:1, pId:0, value:'1', title:"test1",...} ,...], pId는 id의 상위 노드입니다).`
+        },
+        treeNodeLabelProp: {
+          title: '트리 노드 레이블 속성',
+          tooltip: '기본 값은 제목'
+        },
+        filterTreeNode: '트리 노드 필터'
+      }
+    }
+  }
+}

+ 94 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Upload.ts

@@ -0,0 +1,94 @@
+import { createLocales } from '@designable/core'
+
+export const Upload = {
+  'zh-CN': {
+    title: '上传',
+    settings: {
+      'x-component-props': {
+        accept: '可接受类型',
+        action: '上传地址',
+        data: '数据/参数',
+        directory: '支持上传目录',
+        headers: '请求头',
+        listType: { title: '列表类型', dataSource: ['文本', '图片', '卡片'] },
+        multiple: '多选模式',
+        name: '字段标识',
+        openFileDialogOnClick: {
+          title: '点击打开文件对话框',
+          tooltip: '点击打开文件对话框'
+        },
+        showUploadList: '是否展示文件列表',
+        withCredentials: '携带Cookie',
+        maxCount: '最大数量',
+        method: '方法',
+        textContent: '上传文案'
+      }
+    }
+  },
+  'en-US': {
+    title: 'Upload',
+    settings: {
+      'x-component-props': {
+        accept: 'Accept',
+        action: 'Upload Address',
+        data: 'Data',
+        directory: 'Support Upload Directory',
+        headers: 'Headers',
+        listType: { title: 'List Type', dataSource: ['Text', 'Image', 'Card'] },
+        multiple: 'Multiple',
+        name: 'Name',
+        openFileDialogOnClick: 'Open File Dialog On Click',
+        showUploadList: 'Show Upload List',
+        withCredentials: 'withCredentials',
+        maxCount: 'Max Count',
+        method: 'Method',
+        textContent: 'Text Content'
+      }
+    }
+  },
+  'ko-KR': {
+    title: '업로드',
+    settings: {
+      'x-component-props': {
+        accept: '승인',
+        action: '업로드 주소',
+        data: '데이터',
+        directory: '디렉터리 업로드 지원',
+        headers: '헤더',
+        listType: {
+          title: '리스트 타입',
+          dataSource: ['텍스트', '이미지', '카드']
+        },
+        multiple: '여러개',
+        name: '이름',
+        openFileDialogOnClick: '눌러서 파일 다이얼로그 열기',
+        showUploadList: '업로드 목록 표시',
+        withCredentials: '자격 증명 포함',
+        maxCount: '최대 개수',
+        method: '메서드',
+        textContent: '텍스트 내용'
+      }
+    }
+  }
+}
+
+export const UploadDragger = createLocales(Upload, {
+  'zh-CN': {
+    title: '拖拽上传',
+    settings: {
+      'x-component-props': {}
+    }
+  },
+  'en-US': {
+    title: 'UploadDragger',
+    settings: {
+      'x-component-props': {}
+    }
+  },
+  'ko-KR': {
+    title: '드래그로 업로드',
+    settings: {
+      'x-component-props': {}
+    }
+  }
+})

+ 11 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/Void.ts

@@ -0,0 +1,11 @@
+export const Void = {
+  'zh-CN': {
+    title: '虚拟容器'
+  },
+  'en-US': {
+    title: 'Void'
+  },
+  'ko-KR': {
+    title: '비어있음'
+  }
+}

+ 31 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/all.ts

@@ -0,0 +1,31 @@
+export * from './Component'
+export * from './Field'
+export * from './Input'
+export * from './Select'
+export * from './TextArea'
+export * from './TreeSelect'
+export * from './Cascader'
+export * from './Radio'
+export * from './Checkbox'
+export * from './Slider'
+export * from './Rate'
+export * from './DatePicker'
+export * from './TimePicker'
+export * from './NumberPicker'
+export * from './Password'
+export * from './Transfer'
+export * from './Upload'
+export * from './Switch'
+export * from './Object'
+export * from './Form'
+export * from './FormLayout'
+export * from './Text'
+export * from './Card'
+export * from './ArrayBase'
+export * from './ArrayCards'
+export * from './ArrayTable'
+export * from './Void'
+export * from './Space'
+export * from './FormTab'
+export * from './FormCollapse'
+export * from './FormGrid'

+ 3 - 0
src/pages/Schema/Budget/components/Designable/antd/locales/index.ts

@@ -0,0 +1,3 @@
+import * as AllLocales from './all'
+
+export { AllLocales }

+ 5 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/ArrayCards.ts

@@ -0,0 +1,5 @@
+import { ArrayTable } from './ArrayTable'
+import { Card } from './Card'
+
+export const ArrayCards = Card
+ArrayCards.Addition = ArrayTable.Addition

+ 101 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/ArrayTable.ts

@@ -0,0 +1,101 @@
+import { ISchema } from '@formily/react'
+
+export const ArrayTable: ISchema & { Addition?: ISchema; Column?: ISchema } = {
+  type: 'object',
+  properties: {
+    bordered: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    showHeader: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    sticky: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    size: {
+      type: 'string',
+      enum: ['large', 'small', 'middle'],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'small'
+      }
+    }
+  }
+}
+
+const Column: ISchema = {
+  type: 'object',
+  properties: {
+    title: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    align: {
+      type: 'string',
+      enum: ['left', 'right', 'center'],
+      'x-decorator': 'FormItem',
+      'x-component': 'Radio.Group',
+      'x-component-props': {
+        defaultValue: 'left',
+        optionType: 'button'
+      }
+    },
+    colSpan: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    width: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    fixed: {
+      type: 'string',
+      enum: ['left', 'right', false],
+      'x-decorator': 'FormItem',
+      'x-component': 'Radio.Group',
+      'x-component-props': {
+        optionType: 'button'
+      }
+    }
+  }
+}
+
+const Addition: ISchema = {
+  type: 'object',
+  properties: {
+    method: {
+      type: 'string',
+      enum: ['push', 'unshift'],
+      'x-decorator': 'FormItem',
+      'x-component': 'Radio.Group',
+      'x-component-props': {
+        defaultValue: 'push',
+        optionType: 'button'
+      }
+    },
+    defaultValue: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput'
+    }
+  }
+}
+
+ArrayTable.Column = Column
+ArrayTable.Addition = Addition

+ 51 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/CSSStyle.ts

@@ -0,0 +1,51 @@
+import { ISchema } from '@formily/react'
+
+export const CSSStyle: ISchema = {
+  type: 'void',
+  properties: {
+    'style.width': {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'SizeInput'
+    },
+    'style.height': {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'SizeInput'
+    },
+    'style.display': {
+      'x-component': 'DisplayStyleSetter'
+    },
+    'style.background': {
+      'x-component': 'BackgroundStyleSetter'
+    },
+    'style.boxShadow': {
+      'x-component': 'BoxShadowStyleSetter'
+    },
+    'style.font': {
+      'x-component': 'FontStyleSetter'
+    },
+    'style.margin': {
+      'x-component': 'BoxStyleSetter'
+    },
+    'style.padding': {
+      'x-component': 'BoxStyleSetter'
+    },
+    'style.borderRadius': {
+      'x-component': 'BorderRadiusStyleSetter'
+    },
+    'style.border': {
+      'x-component': 'BorderStyleSetter'
+    },
+    'style.opacity': {
+      'x-decorator': 'FormItem',
+      'x-component': 'Slider',
+      'x-component-props': {
+        defaultValue: 1,
+        min: 0,
+        max: 1,
+        step: 0.01
+      }
+    }
+  }
+}

+ 36 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/Card.ts

@@ -0,0 +1,36 @@
+import { GlobalRegistry } from '@designable/core'
+import { ISchema } from '@formily/react'
+
+export const Card: ISchema & { Addition?: ISchema } = {
+  type: 'object',
+  properties: {
+    title: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    extra: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    type: {
+      type: 'boolean',
+      enum: GlobalRegistry.getDesignerMessage('settings.cardTypes'),
+      'x-decorator': 'FormItem',
+      'x-component': 'Radio.Group',
+      'x-component-props': {
+        defaultValue: '',
+        optionType: 'button'
+      }
+    },
+    bordered: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    }
+  }
+}

+ 76 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/Cascader.ts

@@ -0,0 +1,76 @@
+import { ISchema } from '@formily/react'
+
+export const Cascader: ISchema = {
+  type: 'object',
+  properties: {
+    allowClear: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    changeOnSelect: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    autoFocus: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    bordered: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    displayRender: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['EXPRESSION']
+      }
+    },
+    fieldNames: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['EXPRESSION']
+      }
+    },
+    showSearch: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    notFoundContent: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input',
+      'x-component-props': {
+        defaultValue: 'Not Found'
+      }
+    },
+    placeholder: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    size: {
+      type: 'string',
+      enum: ['large', 'small', 'middle', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'middle'
+      }
+    }
+  }
+}

+ 12 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/Checkbox.ts

@@ -0,0 +1,12 @@
+import { ISchema } from '@formily/react'
+
+export const Checkbox: ISchema & { Group?: ISchema } = {
+  type: 'object',
+  properties: {
+    autoFocus: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    }
+  }
+}

+ 118 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/DatePicker.ts

@@ -0,0 +1,118 @@
+import { ISchema } from '@formily/react'
+
+const CommonDatePickerAPI = {
+  allowClear: {
+    type: 'boolean',
+    'x-decorator': 'FormItem',
+    'x-component': 'Switch',
+    'x-component-props': {
+      defaultChecked: true
+    }
+  },
+  autoFocus: {
+    type: 'boolean',
+    'x-decorator': 'FormItem',
+    'x-component': 'Switch'
+  },
+  bordered: {
+    type: 'boolean',
+    'x-decorator': 'FormItem',
+    'x-component': 'Switch',
+    'x-component-props': {
+      defaultChecked: true
+    }
+  },
+  disabledTime: {
+    'x-decorator': 'FormItem',
+    'x-component': 'ValueInput',
+    'x-component-props': {
+      include: ['EXPRESSION']
+    }
+  },
+  disabledDate: {
+    'x-decorator': 'FormItem',
+    'x-component': 'ValueInput',
+    'x-component-props': {
+      include: ['EXPRESSION']
+    }
+  },
+  inputReadOnly: {
+    type: 'boolean',
+    'x-decorator': 'FormItem',
+    'x-component': 'Switch'
+  },
+  placeholder: {
+    type: 'string',
+    'x-decorator': 'FormItem',
+    'x-component': 'Input'
+  },
+  size: {
+    type: 'string',
+    enum: ['large', 'small', 'middle', null],
+    'x-decorator': 'FormItem',
+    'x-component': 'Select',
+    'x-component-props': {
+      defaultValue: 'middle'
+    }
+  },
+  format: {
+    type: 'string',
+    'x-decorator': 'FormItem',
+    'x-component': 'Input',
+    'x-component-props': {
+      placeholder: 'YYYY-MM-DD'
+    }
+  }
+}
+
+export const DatePicker: ISchema & { RangePicker?: ISchema } = {
+  type: 'object',
+  properties: {
+    picker: {
+      type: 'string',
+      enum: ['time', 'date', 'month', 'year', 'quarter', 'decade'],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'date'
+      }
+    },
+    ...CommonDatePickerAPI,
+    showNow: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    showTime: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    showToday: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    }
+  }
+}
+
+DatePicker.RangePicker = {
+  type: 'object',
+  properties: {
+    picker: {
+      type: 'string',
+      enum: ['time', 'date', 'month', 'year', 'decade'],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'date'
+      }
+    },
+    ...CommonDatePickerAPI,
+    showTime: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    }
+  }
+}

+ 11 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/Form.ts

@@ -0,0 +1,11 @@
+import { ISchema } from '@formily/react'
+import { FormLayout } from './FormLayout'
+import { CSSStyle } from './CSSStyle'
+
+export const Form: ISchema = {
+  type: 'object',
+  properties: {
+    ...(FormLayout.properties as any),
+    style: CSSStyle
+  }
+}

+ 61 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/FormCollapse.ts

@@ -0,0 +1,61 @@
+import { ISchema } from '@formily/react'
+
+export const FormCollapse: ISchema & { CollapsePanel?: ISchema } = {
+  type: 'object',
+  properties: {
+    accordion: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    collapsible: {
+      type: 'string',
+      enum: ['header', 'disabled'],
+      'x-decorator': 'FormItem',
+      'x-component': 'Radio.Group',
+      'x-component-props': {
+        defaultValue: 'header',
+        optionType: 'button'
+      }
+    },
+    ghost: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    bordered: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    }
+  }
+}
+
+FormCollapse.CollapsePanel = {
+  type: 'object',
+  properties: {
+    collapsible: {
+      type: 'string',
+      enum: ['header', 'disabled'],
+      'x-decorator': 'FormItem',
+      'x-component': 'Radio.Group',
+      'x-component-props': {
+        defaultValue: 'header',
+        optionType: 'button'
+      }
+    },
+    header: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    extra: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    }
+  }
+}

+ 79 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/FormGrid.ts

@@ -0,0 +1,79 @@
+import { ISchema } from '@formily/react'
+
+export const FormGrid: ISchema & { GridColumn?: ISchema } = {
+  type: 'object',
+  properties: {
+    minWidth: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker',
+      'x-component-props': {
+        defaultValue: 100
+      }
+    },
+    maxWidth: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    minColumns: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker',
+      'x-component-props': {
+        defaultValue: 0
+      }
+    },
+    maxColumns: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    breakpoints: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['EXPRESSION']
+      }
+    },
+    columnGap: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker',
+      'x-component-props': {
+        defaultValue: 10
+      }
+    },
+    rowGap: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker',
+      'x-component-props': {
+        defaultValue: 5
+      }
+    },
+    colWrap: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    }
+  }
+}
+
+FormGrid.GridColumn = {
+  type: 'object',
+  properties: {
+    gridSpan: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker',
+      'x-component-props': {
+        defaultValue: 1
+      }
+    }
+  }
+}

+ 141 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/FormItem.ts

@@ -0,0 +1,141 @@
+import { ISchema } from '@formily/react'
+
+export const FormItem: ISchema = {
+  type: 'object',
+  properties: {
+    tooltip: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    addonBefore: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    addonAfter: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    labelCol: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    wrapperCol: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    labelWidth: {
+      'x-decorator': 'FormItem',
+      'x-component': 'SizeInput'
+    },
+    wrapperWidth: {
+      'x-decorator': 'FormItem',
+      'x-component': 'SizeInput'
+    },
+    colon: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    asterisk: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    gridSpan: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    feedbackLayout: {
+      type: 'string',
+      enum: ['loose', 'terse', 'popover', 'none', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'loose'
+      }
+    },
+    size: {
+      type: 'string',
+      enum: ['large', 'small', 'default', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'default'
+      }
+    },
+    layout: {
+      type: 'string',
+      enum: ['vertical', 'horizontal', 'inline', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'horizontal'
+      }
+    },
+
+    tooltipLayout: {
+      type: 'string',
+      enum: ['icon', 'text', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'icon'
+      }
+    },
+    labelAlign: {
+      type: 'string',
+      enum: ['left', 'right', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'right'
+      }
+    },
+    wrapperAlign: {
+      type: 'string',
+      enum: ['left', 'right', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'left'
+      }
+    },
+    labelWrap: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    wrapperWrap: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    fullness: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    inset: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    bordered: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    }
+  }
+}

+ 124 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/FormLayout.ts

@@ -0,0 +1,124 @@
+import { ISchema } from '@formily/react'
+
+export const FormLayout: ISchema = {
+  type: 'object',
+  properties: {
+    labelCol: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    wrapperCol: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    labelWidth: {
+      'x-decorator': 'FormItem',
+      'x-component': 'SizeInput'
+    },
+    wrapperWidth: {
+      'x-decorator': 'FormItem',
+      'x-component': 'SizeInput'
+    },
+    colon: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    feedbackLayout: {
+      type: 'string',
+      enum: ['loose', 'terse', 'popover', 'none', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'loose'
+      }
+    },
+    size: {
+      type: 'string',
+      enum: ['large', 'small', 'default', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'default'
+      }
+    },
+    layout: {
+      type: 'string',
+      enum: ['horizontal', 'vertical', 'inline', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'horizontal'
+      }
+    },
+    tooltipLayout: {
+      type: 'string',
+      enum: ['icon', 'text', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'icon'
+      }
+    },
+    labelAlign: {
+      type: 'string',
+      enum: ['left', 'right', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'right'
+      }
+    },
+    wrapperAlign: {
+      type: 'string',
+      enum: ['left', 'right', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'left'
+      }
+    },
+    labelWrap: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    wrapperWrap: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+
+    fullness: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    inset: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    shallow: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    bordered: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    }
+  }
+}

+ 46 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/FormTab.ts

@@ -0,0 +1,46 @@
+import { ISchema } from '@formily/react'
+
+export const FormTab: ISchema & { TabPane?: ISchema } = {
+  type: 'object',
+  properties: {
+    animated: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    centered: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    size: {
+      type: 'string',
+      enum: ['large', 'small', 'default', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'default'
+      }
+    },
+    type: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Radio.Group',
+      'x-component-props': {
+        defaultValue: 'line',
+        optionType: 'button'
+      }
+    }
+  }
+}
+
+FormTab.TabPane = {
+  type: 'object',
+  properties: {
+    tab: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    }
+  }
+}

+ 92 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/Input.ts

@@ -0,0 +1,92 @@
+import { ISchema } from '@formily/react'
+
+export const Input: ISchema & { TextArea?: ISchema } = {
+  type: 'object',
+  properties: {
+    addonBefore: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    addonAfter: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    prefix: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    suffix: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    allowClear: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    bordered: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    maxLength: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    placeholder: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    size: {
+      type: 'string',
+      enum: ['large', 'small', 'middle', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'middle'
+      }
+    }
+  }
+}
+
+Input.TextArea = {
+  type: 'object',
+  properties: {
+    bordered: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    maxLength: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    placeholder: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    autoSize: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    showCount: {
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    }
+  }
+}

+ 81 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/NumberPicker.ts

@@ -0,0 +1,81 @@
+import { ISchema } from '@formily/react'
+
+export const NumberPicker: ISchema = {
+  type: 'object',
+  properties: {
+    decimalSeparator: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    precision: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    max: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    min: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    step: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    placeholder: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    size: {
+      type: 'string',
+      enum: ['large', 'small', 'middle', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'middle'
+      }
+    },
+    formatter: {
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['EXPRESSION']
+      }
+    },
+    parser: {
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['EXPRESSION']
+      }
+    },
+    stringMode: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    bordered: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    keyboard: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    }
+  }
+}

+ 13 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/Password.ts

@@ -0,0 +1,13 @@
+import { ISchema } from '@formily/react'
+import { Input } from './Input'
+export const Password: ISchema = {
+  type: 'object',
+  properties: {
+    ...(Input.properties as any),
+    checkStrength: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    }
+  }
+}

+ 38 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/Radio.ts

@@ -0,0 +1,38 @@
+import { ISchema } from '@formily/react'
+
+export const Radio: ISchema & { Group?: ISchema } = {
+  type: 'object',
+  properties: {
+    autoFocus: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    }
+  }
+}
+
+Radio.Group = {
+  type: 'object',
+  properties: {
+    optionType: {
+      type: 'string',
+      enum: ['default', 'button'],
+      'x-decorator': 'FormItem',
+      'x-component': 'Radio.Group',
+      'x-component-props': {
+        defaultValue: 'default',
+        optionType: 'button'
+      }
+    },
+    buttonStyle: {
+      type: 'string',
+      enum: ['outline', 'solid'],
+      'x-decorator': 'FormItem',
+      'x-component': 'Radio.Group',
+      'x-component-props': {
+        defaultValue: 'outline',
+        optionType: 'button'
+      }
+    }
+  }
+}

+ 40 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/Rate.ts

@@ -0,0 +1,40 @@
+import { ISchema } from '@formily/react'
+
+export const Rate: ISchema = {
+  type: 'object',
+  properties: {
+    allowClear: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    count: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker',
+      'x-component-props': {
+        defaultValue: 5
+      }
+    },
+    allowHalf: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    tooltips: {
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['EXPRESSION']
+      }
+    },
+    autoFocus: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    }
+  }
+}

+ 149 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/Select.ts

@@ -0,0 +1,149 @@
+import { ISchema } from '@formily/react'
+
+export const Select: ISchema = {
+  type: 'object',
+  properties: {
+    mode: {
+      type: 'string',
+      enum: ['multiple', 'tags', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Radio.Group',
+      'x-component-props': {
+        defaultValue: null,
+        optionType: 'button'
+      }
+    },
+    allowClear: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    autoClearSearchValue: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    dropdownMatchSelectWidth: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    autoFocus: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    bordered: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    defaultActiveFirstOption: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    defaultOpen: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    labelInValue: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    showArrow: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    showSearch: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    virtual: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultValue: true
+      }
+    },
+    filterOption: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['BOOLEAN', 'EXPRESSION']
+      }
+    },
+    filterSort: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['EXPRESSION']
+      }
+    },
+    listHeight: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker',
+      'x-component-props': {
+        defaultValue: 256
+      }
+    },
+    maxTagCount: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    maxTagPlaceholder: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    maxTagTextLength: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    notFoundContent: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input',
+      'x-component-props': {
+        defaultValue: 'Not Found'
+      }
+    },
+    placeholder: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+
+    size: {
+      type: 'string',
+      enum: ['large', 'small', 'middle', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'middle'
+      }
+    }
+  }
+}

+ 79 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/Slider.ts

@@ -0,0 +1,79 @@
+import { GlobalRegistry } from '@designable/core'
+import { ISchema } from '@formily/react'
+
+export const Slider: ISchema = {
+  type: 'object',
+  properties: {
+    allowClear: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    dots: {
+      title: GlobalRegistry.getDesignerMessage('settings.sliderDots'),
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    range: {
+      title: GlobalRegistry.getDesignerMessage('settings.sliderRange'),
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    reverse: {
+      title: GlobalRegistry.getDesignerMessage('settings.sliderReverse'),
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    vertical: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    tooltipVisible: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    tooltipPlacement: {
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['EXPRESSION']
+      }
+    },
+    marks: {
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['EXPRESSION']
+      }
+    },
+    max: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker',
+      'x-component-props': {
+        defaultValue: 100
+      }
+    },
+    min: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker',
+      'x-component-props': {
+        defaultValue: 0
+      }
+    },
+    step: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker',
+      'x-component-props': {
+        defaultValue: 1
+      }
+    }
+  }
+}

+ 41 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/Space.ts

@@ -0,0 +1,41 @@
+import { ISchema } from '@formily/react'
+
+export const Space: ISchema = {
+  type: 'object',
+  properties: {
+    align: {
+      type: 'string',
+      enum: ['start', 'end', 'center', 'baseline'],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select'
+    },
+    direction: {
+      type: 'string',
+      enum: ['vertical', 'horizontal'],
+      'x-decorator': 'FormItem',
+      'x-component': 'Radio.Group',
+      'x-component-props': {
+        defaultValue: 'horizontal',
+        optionType: 'button'
+      }
+    },
+    size: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker',
+      'x-component-props': {
+        defaultValue: 8
+      }
+    },
+    split: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    wrap: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    }
+  }
+}

+ 21 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/Switch.ts

@@ -0,0 +1,21 @@
+import { ISchema } from '@formily/react'
+
+export const Switch: ISchema = {
+  type: 'object',
+  properties: {
+    autoFocus: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    size: {
+      type: 'string',
+      enum: ['large', 'small', 'default', ''],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'default'
+      }
+    }
+  }
+}

+ 21 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/Text.ts

@@ -0,0 +1,21 @@
+import { ISchema } from '@formily/react'
+
+export const Text: ISchema = {
+  type: 'object',
+  properties: {
+    content: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input.TextArea'
+    },
+    mode: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'normal'
+      },
+      enum: ['h1', 'h2', 'h3', 'p', 'normal']
+    }
+  }
+}

+ 124 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/TimePicker.ts

@@ -0,0 +1,124 @@
+import { ISchema } from '@formily/react'
+
+export const CommonTimePickerAPI = {
+  allowClear: {
+    type: 'boolean',
+    'x-decorator': 'FormItem',
+    'x-component': 'Switch',
+    'x-component-props': {
+      defaultChecked: true
+    }
+  },
+  autoFocus: {
+    type: 'boolean',
+    'x-decorator': 'FormItem',
+    'x-component': 'Switch'
+  },
+  bordered: {
+    type: 'boolean',
+    'x-decorator': 'FormItem',
+    'x-component': 'Switch',
+    'x-component-props': {
+      defaultChecked: true
+    }
+  },
+  clearText: {
+    type: 'string',
+    'x-decorator': 'FormItem',
+    'x-component': 'Input'
+  },
+  disabledHours: {
+    'x-decorator': 'FormItem',
+    'x-component': 'ValueInput',
+    'x-component-props': {
+      include: ['EXPRESSION']
+    }
+  },
+  disabledMinutes: {
+    'x-decorator': 'FormItem',
+    'x-component': 'ValueInput',
+    'x-component-props': {
+      include: ['EXPRESSION']
+    }
+  },
+  disabledSeconds: {
+    'x-decorator': 'FormItem',
+    'x-component': 'ValueInput',
+    'x-component-props': {
+      include: ['EXPRESSION']
+    }
+  },
+  hideDisabledOptions: {
+    type: 'boolean',
+    'x-decorator': 'FormItem',
+    'x-component': 'Switch'
+  },
+  inputReadOnly: {
+    type: 'boolean',
+    'x-decorator': 'FormItem',
+    'x-component': 'Switch'
+  },
+  showNow: {
+    type: 'boolean',
+    'x-decorator': 'FormItem',
+    'x-component': 'Switch'
+  },
+  use12Hours: {
+    type: 'boolean',
+    'x-decorator': 'FormItem',
+    'x-component': 'Switch'
+  },
+  hourStep: {
+    type: 'number',
+    'x-decorator': 'FormItem',
+    'x-component': 'NumberPicker',
+    'x-component-props': {
+      defaultValue: 1
+    }
+  },
+  minuteStep: {
+    type: 'number',
+    'x-decorator': 'FormItem',
+    'x-component': 'NumberPicker',
+    'x-component-props': {
+      defaultValue: 1
+    }
+  },
+  secondStep: {
+    type: 'number',
+    'x-decorator': 'FormItem',
+    'x-component': 'NumberPicker',
+    'x-component-props': {
+      defaultValue: 1
+    }
+  },
+  placeholder: {
+    type: 'string',
+    'x-decorator': 'FormItem',
+    'x-component': 'Input'
+  },
+  size: {
+    type: 'string',
+    enum: ['large', 'small', 'middle', null],
+    'x-decorator': 'FormItem',
+    'x-component': 'Select'
+  },
+  format: {
+    type: 'string',
+    'x-decorator': 'FormItem',
+    'x-component': 'Input',
+    'x-component-props': {
+      placeholder: 'YYYY-MM-DD'
+    }
+  }
+}
+
+export const TimePicker: ISchema & { RangePicker?: ISchema } = {
+  type: 'object',
+  properties: CommonTimePickerAPI
+}
+
+TimePicker.RangePicker = {
+  type: 'object',
+  properties: CommonTimePickerAPI
+}

+ 46 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/Transfer.ts

@@ -0,0 +1,46 @@
+import { ISchema } from '@formily/react'
+
+export const Transfer: ISchema = {
+  type: 'object',
+  properties: {
+    oneWay: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    showSearch: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    showSearchAll: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    filterOption: {
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['EXPRESSION']
+      }
+    },
+    operations: {
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['EXPRESSION']
+      }
+    },
+    titles: {
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['EXPRESSION']
+      }
+    }
+  }
+}

+ 139 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/TreeSelect.ts

@@ -0,0 +1,139 @@
+import { ISchema } from '@formily/react'
+
+export const TreeSelect: ISchema = {
+  type: 'object',
+  properties: {
+    allowClear: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    autoClearSearchValue: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    autoFocus: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    bordered: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    labelInValue: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    showArrow: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    showSearch: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    virtual: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    treeCheckable: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    treeDefaultExpandAll: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    dropdownMatchSelectWidth: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    showCheckedStrategy: {
+      type: 'string',
+      enum: ['SHOW_ALL', 'SHOW_PARENT', 'SHOW_CHILD'],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'SHOW_CHILD'
+      }
+    },
+    treeDefaultExpandedKeys: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['EXPRESSION']
+      }
+    },
+    treeNodeFilterProp: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    treeNodeLabelProp: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    filterTreeNode: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['BOOLEAN', 'EXPRESSION']
+      }
+    },
+    treeDataSimpleMode: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['BOOLEAN', 'EXPRESSION']
+      }
+    },
+    listHeight: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker',
+      'x-component-props': {
+        defaultValue: 256
+      }
+    },
+    placeholder: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    size: {
+      type: 'string',
+      enum: ['large', 'small', 'middle', null],
+      'x-decorator': 'FormItem',
+      'x-component': 'Select',
+      'x-component-props': {
+        defaultValue: 'middle'
+      }
+    }
+  }
+}

+ 103 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/Upload.ts

@@ -0,0 +1,103 @@
+import { ISchema } from '@formily/react'
+
+export const Upload: ISchema & { Dragger?: ISchema } = {
+  type: 'object',
+  properties: {
+    textContent: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    accept: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input'
+    },
+    action: {
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['TEXT', 'EXPRESSION']
+      }
+    },
+    name: {
+      type: 'string',
+      'x-decorator': 'FormItem',
+      'x-component': 'Input',
+      'x-component-props': {
+        defaultValue: 'file'
+      }
+    },
+    maxCount: {
+      type: 'number',
+      'x-decorator': 'FormItem',
+      'x-component': 'NumberPicker'
+    },
+    method: {
+      enum: ['POST', 'PUT', 'GET'],
+      'x-decorator': 'FormItem',
+      'x-component': 'Radio.Group',
+      'x-component-props': {
+        defaultValue: 'POST',
+        optionType: 'button'
+      }
+    },
+    data: {
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['EXPRESSION']
+      }
+    },
+    headers: {
+      'x-decorator': 'FormItem',
+      'x-component': 'ValueInput',
+      'x-component-props': {
+        include: ['EXPRESSION']
+      }
+    },
+
+    listType: {
+      enum: ['text', 'picture', 'picture-card'],
+      'x-decorator': 'FormItem',
+      'x-component': 'Radio.Group',
+      'x-component-props': {
+        defaultValue: 'text',
+        optionType: 'button'
+      }
+    },
+    directory: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    multiple: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    },
+    openFileDialogOnClick: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    showUploadList: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch',
+      'x-component-props': {
+        defaultChecked: true
+      }
+    },
+    withCredentials: {
+      type: 'boolean',
+      'x-decorator': 'FormItem',
+      'x-component': 'Switch'
+    }
+  }
+}
+
+Upload.Dragger = Upload

+ 27 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/all.ts

@@ -0,0 +1,27 @@
+export * from './Input'
+export * from './Text'
+export * from './FormLayout'
+export * from './CSSStyle'
+export * from './Form'
+export * from './FormItem'
+export * from './Select'
+export * from './Card'
+export * from './Cascader'
+export * from './Checkbox'
+export * from './Radio'
+export * from './DatePicker'
+export * from './NumberPicker'
+export * from './Password'
+export * from './Rate'
+export * from './Slider'
+export * from './TimePicker'
+export * from './TreeSelect'
+export * from './Transfer'
+export * from './Upload'
+export * from './Switch'
+export * from './FormGrid'
+export * from './Space'
+export * from './FormTab'
+export * from './FormCollapse'
+export * from './ArrayTable'
+export * from './ArrayCards'

+ 3 - 0
src/pages/Schema/Budget/components/Designable/antd/schemas/index.ts

@@ -0,0 +1,3 @@
+import * as AllSchemas from './all'
+
+export { AllSchemas }

+ 94 - 0
src/pages/Schema/Budget/components/Designable/antd/shared.ts

@@ -0,0 +1,94 @@
+import { TreeNode, Engine } from '@designable/core'
+
+export type ComponentNameMatcher =
+  | string
+  | string[]
+  | ((name: string, node: TreeNode, context?: any) => boolean)
+
+export const matchComponent = (node: TreeNode, name: ComponentNameMatcher, context?: any) => {
+  if (name === '*') return true
+  const componentName = node?.props?.['x-component']
+  if (typeof name === 'function') return name(componentName || '', node, context)
+  if (Array.isArray(name)) return name.includes(componentName)
+  return componentName === name
+}
+
+export const matchChildComponent = (node: TreeNode, name: ComponentNameMatcher, context?: any) => {
+  if (name === '*') return true
+  const componentName = node?.props?.['x-component']
+  if (!componentName) return false
+  if (typeof name === 'function') return name(componentName || '', node, context)
+  if (Array.isArray(name)) return name.includes(componentName)
+  return componentName.indexOf(`${name}.`) > -1
+}
+
+export const includesComponent = (
+  node: TreeNode,
+  names: ComponentNameMatcher[],
+  target?: TreeNode
+) => {
+  return names.some(name => matchComponent(node, name, target))
+}
+
+export const queryNodesByComponentPath = (
+  node: TreeNode,
+  path: ComponentNameMatcher[]
+): TreeNode[] => {
+  if (path?.length === 0) return []
+  if (path?.length === 1) {
+    if (matchComponent(node, path[0])) {
+      return [node]
+    }
+  }
+  return matchComponent(node, path[0])
+    ? node.children.reduce((buf, child) => {
+        return buf.concat(queryNodesByComponentPath(child, path.slice(1)))
+      }, [])
+    : []
+}
+
+export const findNodeByComponentPath = (node: TreeNode, path: ComponentNameMatcher[]): TreeNode => {
+  if (path?.length === 0) return
+  if (path?.length === 1) {
+    if (matchComponent(node, path[0])) {
+      return node
+    }
+  }
+  if (matchComponent(node, path[0])) {
+    for (let i = 0; i < node.children.length; i++) {
+      const next = findNodeByComponentPath(node.children[i], path.slice(1))
+      if (next) {
+        return next
+      }
+    }
+  }
+}
+
+export const hasNodeByComponentPath = (node: TreeNode, path: ComponentNameMatcher[]) =>
+  !!findNodeByComponentPath(node, path)
+
+export const matchArrayItemsNode = (node: TreeNode) => {
+  return node?.parent?.props?.type === 'array' && node?.parent?.children?.[0] === node
+}
+
+export const createNodeId = (designer: Engine, id: string) => {
+  return {
+    [designer.props.nodeIdAttrName]: id
+  }
+}
+
+export const createEnsureTypeItemsNode = (type: string) => (node: TreeNode) => {
+  const objectNode = node.children.find(child => child.props['type'] === type)
+  if (objectNode) {
+    return objectNode
+  } else {
+    const newObjectNode = new TreeNode({
+      componentName: 'Field',
+      props: {
+        type
+      }
+    })
+    node.prepend(newObjectNode)
+    return newObjectNode
+  }
+}

+ 118 - 0
src/pages/Schema/Budget/components/Designable/index.tsx

@@ -0,0 +1,118 @@
+import 'antd/dist/antd.less'
+import type { FC } from 'react'
+import { useMemo } from 'react'
+import {
+  Designer, // 设计器根组件,主要用于下发上下文
+  Workspace, // 工作区组件,核心组件,用于管理工作区内的拖拽行为,树节点数据等等...
+  ResourceWidget, // 拖拽源挂件
+  StudioPanel, // 主布局面板
+  WorkspacePanel, // 工作区布局面板
+  ViewportPanel, // 视口布局面板
+  ViewPanel, // 视图布局面板
+  SettingsPanel, // 右侧配置表单布局面板
+  ComponentTreeWidget, // 组件树渲染器
+  CompositePanel, // 左侧组合布局面板
+  ToolbarPanel, // 工具栏布局面板
+  ViewToolsWidget // 视图切换工具挂件
+} from '@designable/react'
+import { SettingsForm } from '@designable/react-settings-form'
+import { createDesigner, GlobalRegistry, Shortcut, KeyCode } from '@designable/core'
+import { ActionsWidget, PreviewWidget, SchemaEditorWidget } from './widgets'
+import { saveSchema } from './service'
+import { Form, Field, FormGrid, FormLayout } from './antd'
+import { connect, useParams } from '@umijs/max'
+
+GlobalRegistry.registerDesignerLocales({
+  'zh-CN': {
+    sources: {
+      Inputs: '输入控件',
+      Layouts: '布局组件'
+    }
+  }
+})
+
+interface DesignableProps {
+  title: string
+}
+const Designable: FC<DesignableProps> = ({ templateSchema, title }) => {
+  const { id: columnType } = useParams()
+
+  const engine = useMemo(
+    () =>
+      createDesigner({
+        shortcuts: [
+          new Shortcut({
+            codes: [
+              [KeyCode.Meta, KeyCode.S],
+              [KeyCode.Control, KeyCode.S]
+            ],
+            handler(ctx) {
+              saveSchema(ctx.engine)
+            }
+          })
+        ],
+        rootComponentName: 'Form'
+      }),
+    []
+  )
+
+  return (
+    <Designer engine={engine} position="relative">
+      <StudioPanel
+        logo={<span className="text-base font-medium">{title}</span>}
+        actions={
+          <ActionsWidget
+            schema={{
+              form: {
+                labelCol: 6,
+                wrapperCol: 12,
+                fullness: true,
+                inset: false
+              },
+              schema: templateSchema[columnType]?.schema
+            }}
+          />
+        }>
+        <CompositePanel>
+          <CompositePanel.Item title="panels.Component" icon="Component">
+            <ResourceWidget title="sources.Inputs" sources={[]} />
+            <ResourceWidget title="sources.Layouts" sources={[FormGrid, FormLayout]} />
+          </CompositePanel.Item>
+        </CompositePanel>
+
+        <Workspace id="form">
+          <WorkspacePanel>
+            <ToolbarPanel>
+              <ViewToolsWidget use={['DESIGNABLE', 'JSONTREE', 'PREVIEW']} />
+            </ToolbarPanel>
+            <ViewportPanel>
+              <ViewPanel type="DESIGNABLE">
+                {() => (
+                  <ComponentTreeWidget
+                    components={{
+                      Form,
+                      Field,
+                      FormGrid,
+                      FormLayout
+                    }}
+                  />
+                )}
+              </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>
+        <SettingsPanel title="panels.PropertySettings">
+          <SettingsForm uploadAction="https://www.mocky.io/v2/5cc8019d300000980a055e76" />
+        </SettingsPanel>
+      </StudioPanel>
+    </Designer>
+  )
+}
+
+export default connect(({ templateSchema }: { templateSchema: TemplateSchemaModelState }) => ({
+  templateSchema
+}))(Designable)

+ 1 - 0
src/pages/Schema/Budget/components/Designable/service/index.ts

@@ -0,0 +1 @@
+export * from './schema'

+ 17 - 0
src/pages/Schema/Budget/components/Designable/service/schema.ts

@@ -0,0 +1,17 @@
+import type { Engine } from '@designable/core'
+import { transformToSchema, transformToTreeNode } from '@designable/formily-transformer'
+import { message } from 'antd'
+
+export const saveSchema = (designer: Engine) => {
+  localStorage.setItem(
+    'formily-schema',
+    JSON.stringify(transformToSchema(designer.getCurrentTree()))
+  )
+  message.success('Save Success')
+}
+
+export const loadInitialSchema = (designer: Engine, schema: string) => {
+  try {
+    designer.setCurrentTree(transformToTreeNode(schema))
+  } catch {}
+}

+ 49 - 0
src/pages/Schema/Budget/components/Designable/widgets/ActionsWidget.tsx

@@ -0,0 +1,49 @@
+import React, { useEffect } from 'react'
+import { Space, Button, message } from 'antd'
+import { useDesigner, TextWidget } from '@designable/react'
+import { GlobalRegistry } from '@designable/core'
+import { observer } from '@formily/react'
+import { loadInitialSchema } from '../service'
+import { useDispatch, history, useParams, useRequest } from '@umijs/max'
+import { transformToSchema } from '@designable/formily-transformer'
+import { updateSchema } from '@/services/api/schema'
+
+export const ActionsWidget = observer(({ schema }) => {
+  const designer = useDesigner()
+  const { id: columnType } = useParams()
+  useEffect(() => {
+    schema && loadInitialSchema(designer, schema)
+    GlobalRegistry.setDesignerLanguage('zh-cn')
+  }, [schema])
+  const dispatch = useDispatch()
+  const { run: tryUpdateSchema } = useRequest(updateSchema, {
+    manual: true,
+    onSuccess: result => {
+      message.success('更新成功')
+      dispatch({
+        type: 'schemaBase/changeSchemaByColumnType',
+        payload: {
+          columnType,
+          schema: result.schema
+        }
+      })
+    }
+  })
+
+  const saveSchema = () => {
+    tryUpdateSchema({
+      columnType,
+      schema: JSON.stringify(transformToSchema(designer.getCurrentTree()).schema)
+    })
+  }
+  return (
+    <Space style={{ marginRight: 10 }}>
+      <Button onClick={() => history.replace('/schema/budget')}>
+        <TextWidget>返回</TextWidget>
+      </Button>
+      <Button type="primary" onClick={() => saveSchema(designer)}>
+        <TextWidget>Publish</TextWidget>
+      </Button>
+    </Space>
+  )
+})

+ 164 - 0
src/pages/Schema/Budget/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/Budget/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/Budget/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"
+    />
+  )
+}

+ 4 - 0
src/pages/Schema/Budget/components/Designable/widgets/index.ts

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

+ 0 - 53
src/pages/Schema/Budget/components/LeftMenu.tsx

@@ -1,53 +0,0 @@
-import React from 'react'
-import type { DirectoryTreeProps } from 'antd/lib/tree'
-import { Tree } from 'antd'
-import '@/pages/Permission/Role/components/RoleLeftMenu/index.less'
-
-const { DirectoryTree } = Tree
-type LeftMenuProps = {
-  onSelect: (key: string) => void
-  options: API.DataSourceMenuItem[]
-  initFn: () => Promise<void>
-}
-
-const LeftMenu: React.FC<LeftMenuProps> = ({ onSelect, options }) => {
-  const handleOnSelect: DirectoryTreeProps['onSelect'] = keys => {
-    // console.log('Trigger Select', node)
-    onSelect?.(keys[0])
-  }
-  const renderTreeNode = tree => {
-    return tree.map((item: API.TemplateListItem & { title: string; key: string }) => {
-      const newItem = {
-        ...item,
-        title: (
-          <div className="department-node py-1">
-            <div className="title">{item.name}</div>
-          </div>
-        )
-      }
-      return newItem
-    })
-  }
-  return (
-    <div
-      className="min-w-54 rounded-20px flex flex-col bg-white "
-      style={{ height: 'calc(100vh - 122px)', background: '#ffffff' }}>
-      <div className="p-4 border-b-1 rounded-tl-20px rounded-tr-20px border-solid border-black border-opacity-10 bg-hex-f7f9fa">
-        <div className="text-base">表单列表</div>
-      </div>
-      <div
-        id="role-list"
-        className="p-4 bg-white rounded-b-20px"
-        style={{ height: 'calc(100% - 1rem*2 - 20px)' }}>
-        <DirectoryTree
-          treeData={renderTreeNode(options.map(item => ({ title: item.name, key: item.action, ...item })))}
-          onSelect={handleOnSelect}
-          showIcon={false}
-          defaultExpandAll
-        />
-      </div>
-    </div>
-  )
-}
-
-export default LeftMenu

+ 35 - 0
src/pages/Schema/Budget/detail.tsx

@@ -0,0 +1,35 @@
+import React, { useEffect } from 'react'
+import { PageContainer } from '@ant-design/pro-layout'
+import { connect, useParams } from '@umijs/max'
+import type { ConnectProps } from '@umijs/max'
+import type { RouteComponentProps } from 'react-router'
+import { TemplateSchemaModelState } from './model'
+import Designable from './components/Designable'
+
+type DetailProps = RouteComponentProps & ConnectProps
+
+const Detail: React.FC<DetailProps> = ({ dispatch, templateSchema }) => {
+  const { id: columnType } = useParams()
+
+  useEffect(() => {
+    if (templateSchema && !templateSchema[columnType]) {
+      dispatch({
+        type: 'templateSchema/querySchema',
+        payload: {
+          action: columnType
+        }
+      })
+    }
+  }, [columnType])
+  return (
+    <PageContainer title={false} breadcrumbRender={false}>
+      <div className="h-full w-full bg-white px-4 pb-4 rounded-20px">
+        <Designable title={templateSchema[columnType]?.name} />
+      </div>
+    </PageContainer>
+  )
+}
+
+export default connect(({ templateSchema }: { templateSchema: TemplateSchemaModelState }) => ({
+  templateSchema
+}))(Detail)

+ 0 - 0
src/pages/Schema/Budget/index.tsx


Some files were not shown because too many files changed in this diff