Преглед на файлове

feat(fix): add builder-next package and fix builder bugs.
fix(docs): update playground link and fix some bugs

feat(fix): add builder-next package and fix builder bugs

docs: update playground link

cnt1992 преди 7 години
родител
ревизия
71e6af8a3a
променени са 30 файла, в които са добавени 544 реда и са изтрити 205 реда
  1. 2 1
      docs/SUMMARY.md
  2. 3 0
      packages/builder-next/.npmignore
  3. 20 0
      packages/builder-next/LICENSE.md
  4. 2 0
      packages/builder-next/README.md
  5. 29 0
      packages/builder-next/package.json
  6. 142 0
      packages/builder-next/src/index.js
  7. 2 1
      packages/builder/package.json
  8. 97 5
      packages/builder/src/App.js
  9. 20 18
      packages/builder/src/__tests__/reducers/index.spec.js
  10. 5 1
      packages/builder/src/actions/index.js
  11. 6 4
      packages/builder/src/components/fields/index.js
  12. 7 3
      packages/builder/src/components/fields/layout.js
  13. 6 19
      packages/builder/src/components/preview/fieldMiddleware.js
  14. 5 1
      packages/builder/src/components/preview/index.js
  15. 0 103
      packages/builder/src/components/props/accordList.js
  16. 53 0
      packages/builder/src/components/props/colsDetail.js
  17. 1 1
      packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/dateDefaultEditor.js
  18. 9 5
      packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/dateTimeDefaultEditor.js
  19. 21 3
      packages/builder/src/components/props/propsSetting.js
  20. 25 1
      packages/builder/src/configs/supportConfigList.js
  21. 5 2
      packages/builder/src/configs/supportGlobalCfgList.js
  22. 14 0
      packages/builder/src/configs/supportLayoutList.js
  23. 37 4
      packages/builder/src/demo/index-1-x.js
  24. 2 4
      packages/builder/src/demo/index.js
  25. 4 1
      packages/builder/src/index.js
  26. 0 5
      packages/builder/src/reducers/componentProps.js
  27. 17 18
      packages/builder/src/reducers/initSchemaData.js
  28. 5 1
      packages/builder/src/style.js
  29. 2 2
      packages/builder/src/utils/lang.js
  30. 3 2
      packages/builder/src/utils/util.js

+ 2 - 1
docs/SUMMARY.md

@@ -30,4 +30,5 @@
      - [<FormCard/>](./API/FormCard.md)
      - [<FormBlock/>](./API/FormBlock.md)
      - [<FormItemGrid/>](./API/FormItemGrid.md)
-     - [<FormSlot/>](./API/FormSlot.md)
+     - [<FormSlot/>](./API/FormSlot.md)
+- [PlayGround](../packages/builder-next/lib/index.js)

+ 3 - 0
packages/builder-next/.npmignore

@@ -0,0 +1,3 @@
+node_modules
+*.log
+build

+ 20 - 0
packages/builder-next/LICENSE.md

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2015-present, Alibaba Group Holding Limited. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 2 - 0
packages/builder-next/README.md

@@ -0,0 +1,2 @@
+# @uform/builder-next
+> UForm 可视化配置 next实现

+ 29 - 0
packages/builder-next/package.json

@@ -0,0 +1,29 @@
+{
+  "name": "@uform/builder-next",
+  "version": "0.1.0-beta.15",
+  "license": "MIT",
+  "main": "lib/index.js",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/alibaba/uform.git"
+  },
+  "bugs": {
+    "url": "https://github.com/alibaba/uform/issues"
+  },
+  "homepage": "https://github.com/alibaba/uform#readme",
+  "engines": {
+    "npm": ">=3.0.0"
+  },
+  "peerDependencies": {
+    "react": ">=16.8.0",
+    "react-dom": ">=16.8.0"
+  },
+  "dependencies": {
+    "@alifd/next": "^1.13.1",
+    "@uform/builder": "^0.1.0-beta.14"
+  },
+  "publishConfig": {
+    "access": "public"
+  },
+  "gitHead": "f513fc2dcca781b3f7aa588c4419bce20cba2d8b"
+}

+ 142 - 0
packages/builder-next/src/index.js

@@ -0,0 +1,142 @@
+import React from 'react'
+import SchemaForm, { FormButtonGroup, Submit, Reset } from '@uform/next'
+import Builder from '@uform/builder'
+
+import {
+  Button,
+  Collapse,
+  Message,
+  Upload,
+  Input,
+  Select,
+  DatePicker,
+  Icon,
+  Checkbox,
+  NumberPicker,
+  TimePicker,
+  Radio,
+  Form,
+  Tab
+} from '@alifd/next'
+
+// style
+import '@alifd/next/dist/next.css'
+
+SchemaForm.FormButtonGroup = FormButtonGroup
+SchemaForm.Submit = Submit
+SchemaForm.Reset = Reset
+
+const renderSchema = {}
+
+const props = {
+  UI: {
+    version: '1.x',
+    Button,
+    Accordion: Collapse,
+    Toast: Message,
+    Upload,
+    Input,
+    Select,
+    Icon,
+    DatePicker,
+    TimePicker,
+    Checkbox,
+    NumberPicker,
+    Radio,
+    RadioGroup: Radio.Group,
+    TabPane: Tab.Item,
+    Form,
+    Tab
+  },
+  // 主题: dark/light,默认dark
+  themeStyle: 'dark',
+  // 是否展示布局组件,默认为false
+  showLayoutField: true,
+  // 是否展示预览按钮,默认为true
+  showPreviewBtn: true,
+  // 是否展示源码按钮
+  showSourceCodeBtn: true,
+  // 控制返回按钮点击事件
+  onBackBtnClick: () => {
+    alert('点击了返回')
+  },
+  // 额外全局按钮
+  globalButtonList: [
+    // {
+    //   key: 'submit',
+    //   title: '自定义保存',
+    //   render: (props) => {
+    //     return <Badge dot>{props.children}</Badge>
+    //   },
+    //   props: {
+    //     // loading: true,
+    //   },
+    // }, {
+    //   key: 'cancel',
+    //   title: '取消',
+    //   props: {
+    //     onClick: () => {
+    //       alert('点击取消');
+    //     }
+    //   },
+    // }
+  ],
+  // 是否展示全局配置
+  showGlobalCfg: true,
+  // 全局配置额外项
+  extraGlobalCfgList: [
+    {
+      name: 'labelCol',
+      title: 'label宽度占比',
+      type: 'string'
+    },
+    {
+      name: 'wrapperCol',
+      title: 'wrapper宽度占比',
+      type: 'string'
+    }
+  ],
+  globalCfg: {},
+  supportFieldList: [],
+  includeFieldListKeyList: [
+    'input',
+    'multipleInput',
+    'number',
+    'radio',
+    'checkbox',
+    'date',
+    'month',
+    'daterange',
+    'time'
+  ],
+
+  // 渲染引擎
+  renderEngine: SchemaForm,
+
+  schema: renderSchema,
+  // onChange: (data) => {
+  //   console.info('index onChange data', data);
+  // },
+  onSubmit: data => {
+    console.info('index onSubmit data', data)
+  }
+}
+
+class Component extends React.Component {
+  constructor(props) {
+    super(props)
+    this.state = {
+      schema: renderSchema
+    }
+  }
+
+  render() {
+    return (
+      <div style={{ marginTop: -20 }}>
+        <Builder {...props} schema={this.state.schema} />
+      </div>
+    )
+  }
+}
+
+export default Component

+ 2 - 1
packages/builder/package.json

@@ -43,7 +43,8 @@
     "redux-logger": "^3.0.6",
     "redux-thunk": "^2.2.0",
     "styled-components": "^4.1.2",
-    "uuid": "^3.2.1"
+    "uuid": "^3.2.1",
+    "react-dnd": "^7.4.1"
   },
   "publishConfig": {
     "access": "public"

+ 97 - 5
packages/builder/src/App.js

@@ -1,7 +1,6 @@
 import React, { Component } from 'react'
 import cls from 'classnames'
 import PropTypes from 'prop-types'
-import { SchemaForm } from './utils/baseForm'
 import { connect } from 'react-redux'
 import {
   changePreview,
@@ -19,17 +18,85 @@ import AppStyle from './style'
 // components
 import FieldList from './components/fields/index'
 import Preview from './components/preview/index'
-import AccordionList from './components/props/accordList'
 import generateGlobalBtnList from './components/globalBtnList/index'
 
+import PropsSetting from './components/props/propsSetting'
+import { SchemaForm, Field } from './utils/baseForm'
+import defaultGlobalCfgList from './configs/supportGlobalCfgList'
+
 const noop = () => {}
 
 class App extends Component {
   constructor(props) {
     super(props)
     this.state = {
-      systemError: false
+      systemError: false,
+      accordionList: []
+    }
+  }
+
+  generateGlobalCfgList = () => {
+    // Merge custom form global property configuration
+    const globalCfgList = [
+      ...defaultGlobalCfgList,
+      ...this.props.extraGlobalCfgList
+    ]
+    const _globalCfgList = []
+    for (let i = globalCfgList.length - 1; i >= 0; i--) {
+      if (
+        !_globalCfgList.find(cfgItem => cfgItem.name === globalCfgList[i].name)
+      ) {
+        _globalCfgList.unshift(globalCfgList[i])
+      }
+    }
+    return _globalCfgList
+  }
+
+  // global config
+  renderGlobalConfig = () => {
+    const globalCfgList = this.generateGlobalCfgList()
+
+    const content = (
+      <SchemaForm
+        onChange={value => {
+          this.props.changeGbConfig(value)
+        }}
+        defaultValue={this.props.gbConfig}
+        labelAlign='left'
+        labelTextAlign='right'
+      >
+        {globalCfgList.map(props => (
+          <Field {...props} key={props.name} x-item-props={{ labelCol: 10 }} />
+        ))}
+      </SchemaForm>
+    )
+
+    return content
+  }
+
+  getAccordionList = () => {
+    const list = [
+      {
+        title: '组件配置',
+        content: (
+          <PropsSetting
+            supportConfigList={this.props.supportConfigList}
+            renderEngine={this.props.renderEngine}
+            UI={this.props.UI}
+          />
+        ),
+        expanded: true
+      }
+    ]
+
+    if (this.props.showGlobalCfg) {
+      list.unshift({
+        title: '全局配置',
+        content: this.renderGlobalConfig(),
+        expanded: true
+      })
     }
+    return list
   }
 
   componentWillMount() {
@@ -44,6 +111,12 @@ class App extends Component {
     _initSchema(schema)
   }
 
+  componentDidMount() {
+    this.setState({
+      accordionList: this.getAccordionList()
+    })
+  }
+
   componentWillUnmount() {
     // Clear the selected componentId with the selected component
     this.props.changeComponent()
@@ -138,6 +211,7 @@ class App extends Component {
 
   render() {
     const { initSchemaData, renderEngine } = this.props
+    const { Accordion, version: UIVersion } = this.props.UI
 
     const contentHeight = window.innerHeight - 64
 
@@ -169,10 +243,28 @@ class App extends Component {
             />
           </div>
           <div className='content-col content-col-main'>
-            <Preview schema={initSchemaData} renderEngine={renderEngine} />
+            <Preview
+              schema={initSchemaData}
+              renderEngine={renderEngine}
+              UI={this.props.UI}
+            />
           </div>
           <div className='content-col content-col-right'>
-            <AccordionList {...this.props} />
+            {UIVersion === '1.x' ? (
+              <Accordion
+                dataSource={this.state.accordionList}
+                defaultExpandedKeys={['0', '1']}
+              />
+            ) : (
+              <Accordion
+                dataSource={this.state.accordionList}
+                onChange={(status, list) => {
+                  this.setState({
+                    accordionList: list
+                  })
+                }}
+              />
+            )}
           </div>
         </div>
         {this.getEditorTpl()}

+ 20 - 18
packages/builder/src/__tests__/reducers/index.spec.js

@@ -66,9 +66,8 @@ describe('reducers', () => {
           name: '__id__',
           title: '唯一标识',
           type: 'string',
-          description: '发起请求时带过去的参数字段',
-          required: true,
-          value: '11111'
+          description: '唯一标识:发起请求时带上的参数id,必填,全局保证唯一。',
+          required: true
         },
         { name: 'description', title: '提示文案', type: 'string' },
         {
@@ -215,11 +214,14 @@ describe('reducers', () => {
   test('gbConfig reducers return initial state', () => {
     expect(gbConfigReducer(undefined, {})).toEqual({
       labelAlign: 'left',
-      labelTextAlign: 'left',
+      labelTextAlign: 'right',
       autoAddColon: true,
       needFormButtonGroup: false,
       inline: false,
-      size: 'medium'
+      size: 'medium',
+      labelCol: 8,
+      wrapperCol: 16,
+      editable: true
     })
   })
   test('gbConfig reducers return custom state', () => {
@@ -229,7 +231,9 @@ describe('reducers', () => {
       autoAddColon: true,
       needFormButtonGroup: false,
       inline: false,
-      size: 'small'
+      size: 'small',
+      labelCol: 8,
+      wrapperCol: 16
     }
 
     const action = {
@@ -248,7 +252,10 @@ describe('reducers', () => {
       needFormButtonGroup: false,
       inline: true,
       size: 'large',
-      extra: 'extra'
+      extra: 'extra',
+      labelCol: 8,
+      wrapperCol: 16,
+      editable: true
     }
 
     expect(gbConfigReducer(beforeState, action)).toEqual(afterState)
@@ -480,8 +487,7 @@ describe('reducers', () => {
           title: '单行文本框',
           placeholder: '请输入',
           'x-index': 0,
-          id: '111',
-          __id__: '111'
+          id: '111'
         }
       }
     }
@@ -491,7 +497,7 @@ describe('reducers', () => {
         '111': {
           type: 'object',
           id: '111',
-          __id__: '111',
+          'x-index': 0,
           'x-component': 'layout',
           properties: {},
           'x-props': {
@@ -533,8 +539,7 @@ describe('reducers', () => {
               title: '单行文本框',
               placeholder: '请输入',
               'x-index': 0,
-              id: '222',
-              __id__: '222'
+              id: '222'
             }
           }
         }
@@ -547,8 +552,7 @@ describe('reducers', () => {
           type: 'string',
           title: '111',
           'x-index': 0,
-          id: '111',
-          __id__: '111'
+          id: '111'
         },
         '222': {
           key: 'input',
@@ -560,15 +564,13 @@ describe('reducers', () => {
           title: '单行文本框',
           placeholder: '请输入',
           'x-index': 1,
-          id: '222',
-          __id__: '222'
+          id: '222'
         },
         '333': {
           type: 'string',
           title: '333',
           'x-index': 2,
-          id: '333',
-          __id__: '333'
+          id: '333'
         }
       }
     }

+ 5 - 1
packages/builder/src/actions/index.js

@@ -28,7 +28,11 @@ export const addComponentAndEdit = (
   dispatch(addComponent(component, existId, id, type, containerId))
   dispatch(changeComponent(id))
   dispatch(showComponentProps(id, component, containerId))
-  dispatch(changeLayoutId(containerId))
+
+  if (component.__key__ === 'layout') {
+    dispatch(changeLayoutId(id))
+  }
+
   dispatch(
     editComponent(
       id,

+ 6 - 4
packages/builder/src/components/fields/index.js

@@ -63,7 +63,8 @@ class FieldList extends Component {
       : fieldItem
 
   renderFieldList() {
-    const _addComponent = this.props.addComponentAndEdit
+    const _addComponentAndEdit = this.props.addComponentAndEdit
+
     return (
       <ul className='field-list'>
         {this.fieldList.map((fieldItem, i) => {
@@ -80,7 +81,7 @@ class FieldList extends Component {
               draggable
               onDragStart={ev => this.onDragStart(ev, newFieldItem)}
               onClick={() => {
-                _addComponent && _addComponent(newFieldItem)
+                _addComponentAndEdit && _addComponentAndEdit(newFieldItem)
               }}
             >
               <i
@@ -114,7 +115,7 @@ class FieldList extends Component {
   }
 }
 
-const mapStateToProps = () => ({})
+const mapStateToProps = state => state
 
 const mapDispatchToProps = dispatch => ({
   addComponent: component => dispatch(addComponent(component)),
@@ -122,7 +123,8 @@ const mapDispatchToProps = dispatch => ({
     dispatch(editComponent(id, propsData, containerId)),
   showComponentProps: (id, comp) => dispatch(showComponentProps(id, comp)),
   changeComponent: componentId => dispatch(changeComponent(componentId)),
-  addComponentAndEdit: component => dispatch(addComponentAndEdit(component))
+  addComponentAndEdit: (component, existId, type, containerId) =>
+    dispatch(addComponentAndEdit(component, existId, type, containerId))
 })
 
 class StyledFieldListComp extends React.Component {

+ 7 - 3
packages/builder/src/components/fields/layout.js

@@ -33,7 +33,10 @@ class Component extends React.Component {
               // eslint-disable-next-line
               key={i}
               onClick={() => {
-                _addComponentAndEdit && _addComponentAndEdit(item)
+                _addComponentAndEdit &&
+                  _addComponentAndEdit(
+                    item
+                  )
               }}
             >
               {title}
@@ -57,10 +60,11 @@ class Component extends React.Component {
   }
 }
 
-const mapStateToProps = () => ({})
+const mapStateToProps = state => state
 
 const mapDispatchToProps = dispatch => ({
-  addComponentAndEdit: component => dispatch(addComponentAndEdit(component))
+  addComponentAndEdit: (component, existId, type, containerId) =>
+    dispatch(addComponentAndEdit(component, existId, type, containerId))
 })
 
 class StyledLayoutListComp extends React.Component {

+ 6 - 19
packages/builder/src/components/preview/fieldMiddleware.js

@@ -7,11 +7,14 @@ export default (FormConsumer, that) => {
   if (hasRegisted) {
     return false
   }
+  const { UI } = that.props
   window.__hasRegisted__ = true
   registerFieldMiddleware(Field => props =>
     React.createElement(FormConsumer, {}, (obj = {}) => {
       const { type } = obj
-      if (props.path.length === 0 || type !== 'preview') { return React.createElement(Field, props) }
+      if (props.path.length === 0 || type !== 'preview') {
+        return React.createElement(Field, props)
+      }
       const { title = '', active = false } = props.schema
       const id = props.path[0]
       const comp = {
@@ -49,15 +52,7 @@ export default (FormConsumer, that) => {
               className='preview-line-layer-layout'
               title='编辑'
             >
-              {
-                React.createElement(Field, {
-                  'x-component': 'Icon',
-                  'x-props': {
-                    type: 'edit',
-                    size: 'small'
-                  }
-                })
-              }
+              <UI.Icon type='edit' size='small' />
             </a>
             <a
               className='preview-line-del'
@@ -70,15 +65,7 @@ export default (FormConsumer, that) => {
               }}
               title='删除'
             >
-              {
-                React.createElement(Field, {
-                  'x-component': 'Icon',
-                  'x-props': {
-                    type: 'ashbin',
-                    size: 'small'
-                  }
-                })
-              }
+              <UI.Icon type='ashbin' size='small' />
             </a>
           </div>
         </div>

+ 5 - 1
packages/builder/src/components/preview/index.js

@@ -143,11 +143,15 @@ class Preview extends Component {
       )
 
     const globalCfg = pick(gbConfig, [
+      'labelCol',
+      'wrapperCol',
+      'action',
       'labelAlign',
       'labelTextAlign',
       'autoAddColon',
       'inline',
-      'size'
+      'size',
+      'editable'
     ])
 
     return (

+ 0 - 103
packages/builder/src/components/props/accordList.js

@@ -1,103 +0,0 @@
-import React from 'react'
-import PropsSetting from './propsSetting'
-import { SchemaForm, Field } from '../../utils/baseForm'
-import defaultGlobalCfgList from '../../configs/supportGlobalCfgList'
-
-export default class extends React.Component {
-  constructor(props) {
-    super(props)
-    this.state = {
-      accordionList: this.getAccordionList()
-    }
-  }
-
-  componentDidMount() {
-    // this.setState({
-    //   accordionList: this.getAccordionList()
-    // })
-  }
-
-  generateGlobalCfgList = () => {
-    // Merge custom form global property configuration
-    const globalCfgList = [
-      ...defaultGlobalCfgList,
-      ...this.props.extraGlobalCfgList
-    ]
-    const _globalCfgList = []
-    for (let i = globalCfgList.length - 1; i >= 0; i--) {
-      if (
-        !_globalCfgList.find(cfgItem => cfgItem.name === globalCfgList[i].name)
-      ) {
-        _globalCfgList.unshift(globalCfgList[i])
-      }
-    }
-    return _globalCfgList
-  }
-
-  // global config
-  renderGlobalConfig = () => {
-    const globalCfgList = this.generateGlobalCfgList()
-
-    const content = (
-      <SchemaForm
-        onChange={value => {
-          this.props.changeGbConfig(value)
-        }}
-        defaultValue={this.props.gbConfig}
-        labelAlign='left'
-        labelTextAlign='left'
-      >
-        {globalCfgList.map(props => (
-          <Field {...props} key={props.name} x-item-props={{ labelCol: 10 }} />
-        ))}
-      </SchemaForm>
-    )
-
-    return content
-  }
-
-  getAccordionList() {
-    const list = [
-      {
-        title: '组件配置',
-        content: (
-          <PropsSetting
-            supportConfigList={this.props.supportConfigList}
-            renderEngine={this.props.renderEngine}
-            UI={this.props.UI}
-          />
-        ),
-        expanded: true
-      }
-    ]
-
-    if (this.props.showGlobalCfg) {
-      list.unshift({
-        title: '全局配置',
-        content: this.renderGlobalConfig(),
-        expanded: true
-      })
-    }
-    return list
-  }
-
-  render() {
-    const { Accordion, version: UIVersion } = this.props.UI
-
-    return UIVersion === '1.x' ? (
-      <Accordion
-        dataSource={this.state.accordionList}
-        defaultExpandedKeys={['1']}
-      />
-    ) : (
-      <Accordion
-        dataSource={this.state.accordionList}
-        onChange={(status, list) => {
-          this.setState({
-            accordionList: list
-          })
-        }}
-      />
-    )
-  }
-}

+ 53 - 0
packages/builder/src/components/props/colsDetail.js

@@ -0,0 +1,53 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+class ColsDetail extends React.Component {
+  static propTypes = {
+    value: PropTypes.arrayOf(PropTypes.any),
+    onChange: PropTypes.func
+  }
+
+  handleChange = (idx, val) => {
+    const { UI } = this.props
+    if (!val) {
+      UI.Toast.error('请确保列宽是有效整数')
+      return false
+    }
+    const { onChange, value } = this.props
+    let newValue = [...value]
+    const diff = val - newValue[idx]
+
+    if (diff >= newValue[newValue.length - 1]) {
+      UI.Toast.error('请确保4列宽度加起来等于24')
+      return false
+    }
+
+    newValue = newValue.map((_val, i) => {
+      if (i === idx) {
+        return val
+      }
+      if (i === newValue.length - 1) {
+        return _val - diff
+      }
+      if (i < idx) {
+        return _val
+      }
+      return _val
+    })
+
+    onChange(newValue)
+  }
+
+  render() {
+    const { value = [], UI } = this.props
+    return value.map((item, idx) => (
+      <UI.NumberPicker
+        key={idx}
+        value={item}
+        onChange={val => this.handleChange(idx, val)}
+      />
+    ))
+  }
+}
+
+export default ColsDetail

+ 1 - 1
packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/dateDefaultEditor.js

@@ -69,7 +69,7 @@ class Editor extends Component {
               }}
             />
           ),
-          specify: <DatePickerDefault />
+          specify: <DatePickerDefault UI={UI} />
         }}
       />
     )

+ 9 - 5
packages/builder/src/components/props/editors/fieldAttrEditors/defaultValueEditor/dateTimeDefaultEditor.js

@@ -17,20 +17,24 @@ const ds = [
 ]
 
 const DatePickerDefault = props => (
-  <props.UI.DatePicker
-    showTime
+  <props.UI.TimePicker
     onChange={(v, vStr) => {
-      props.onChange(vStr)
+      if (vStr) {
+        props.onChange(vStr)
+      } else {
+        props.onChange(v.format('HH:mm:ss'))
+      }
     }}
     style={{
       verticalAlign: 'top',
-      marginLeft: 20
+      marginLeft: 5
     }}
   />
 )
 
 class Editor extends Component {
   render() {
+    const { UI } = this.props
     return (
       <DefaultValueGenerator
         flag='time'
@@ -38,7 +42,7 @@ class Editor extends Component {
         {...this.props}
         customEditor={{
           now: <div />,
-          specify: <DatePickerDefault />
+          specify: <DatePickerDefault UI={UI} />
         }}
       />
     )

+ 21 - 3
packages/builder/src/components/props/propsSetting.js

@@ -1,6 +1,10 @@
 import React, { Component } from 'react'
 import PropTypes from 'prop-types'
-import { SchemaForm } from '../../utils/baseForm'
+import {
+  SchemaForm,
+  registerFormFields,
+  connect as formConnect
+} from '../../utils/baseForm'
 import { connect } from 'react-redux'
 import {
   showComponentProps,
@@ -16,6 +20,7 @@ import FileSetting from './fileSetting'
 
 import './defaultValueCascader/index'
 import './dataSourceEditor/index'
+import ColsDetail from './colsDetail'
 
 import pickBy from 'lodash.pickby'
 
@@ -28,6 +33,17 @@ class PropsSetting extends Component {
     componentProps: PropTypes.object
   }
 
+  constructor(props) {
+    super(props)
+    registerFormFields({
+      colsDetail: formConnect({
+        defaultProps: {
+          UI: this.props.UI
+        }
+      })(ColsDetail)
+    })
+  }
+
   onChangeHandler = formdata => {
     const { componentId = '' } = this.props
     if (!componentId) return false
@@ -156,7 +172,9 @@ class PropsSetting extends Component {
   renderConfigList() {
     const { componentId = '' } = this.props
 
-    if (!componentId) { return <p className='props-tips'>请选择待编辑的表单字段</p> }
+    if (!componentId) {
+      return <p className='props-tips'>请选择待编辑的表单字段</p>
+    }
 
     return (
       <State initial={{ visible: false }}>
@@ -190,7 +208,7 @@ class PropsSetting extends Component {
             onChange={this.onChangeHandler}
             schema={this.generatePropsSchema()}
             labelAlign='left'
-            labelTextAlign='left'
+            labelTextAlign='right'
             labelCol={8}
           >
             {' '}

+ 25 - 1
packages/builder/src/configs/supportConfigList.js

@@ -4,7 +4,7 @@ const FIELDLIST = {
     name: '__id__',
     title: '唯一标识',
     type: 'string',
-    description: '发起请求时带过去的参数字段',
+    description: '唯一标识:发起请求时带上的参数id,必填,全局保证唯一。',
     required: true
   },
   PLACEHOLDER: {
@@ -133,6 +133,9 @@ export const getPropsByKey = key => {
         'PLACEHOLDER'
       ])
     case 'date':
+    case 'month':
+    case 'daterange':
+    case 'time':
       return generateProps(
         [
           'ID',
@@ -215,6 +218,27 @@ export const getPropsByKey = key => {
           }
         ]
       )
+    case 'wrapper_card':
+      return generateProps(
+        ['ID'],
+        [
+          {
+            name: 'x-props.title',
+            title: '标题',
+            type: 'string'
+          },
+          {
+            name: 'x-props.subTitle',
+            title: '副标题',
+            type: 'string'
+          },
+          {
+            name: 'x-props.showHeadDivider',
+            title: '是否展示标题底部横线',
+            type: 'boolean'
+          }
+        ]
+      )
     default:
       return defaultProps
   }

+ 5 - 2
packages/builder/src/configs/supportGlobalCfgList.js

@@ -23,11 +23,14 @@ const sizeEnum = [
 // 默认全局配置值
 export const defaultGlobalCfgValue = {
   labelAlign: 'left',
-  labelTextAlign: 'left',
+  labelTextAlign: 'right',
   autoAddColon: true,
   needFormButtonGroup: false,
   inline: false,
-  size: 'medium'
+  size: 'medium',
+  labelCol: 8,
+  wrapperCol: 16,
+  editable: true
 }
 
 // 默认全局配置属性列表

+ 14 - 0
packages/builder/src/configs/supportLayoutList.js

@@ -25,5 +25,19 @@ export default [
         gutter: 20
       }
     }
+  },
+  {
+    key: 'wrapper_card',
+    icon: 'clock-circle-o',
+    type: 'object',
+    title: 'FormCard卡片式布局',
+    __key__: 'layout',
+    __key__data__: {
+      'x-component': 'card',
+      'x-props': {
+        title: '卡片式布局',
+        showHeadDivider: true
+      }
+    }
   }
 ]

+ 37 - 4
packages/builder/src/demo/index-1-x.js

@@ -1,5 +1,5 @@
 import React from 'react'
-import SchemaForm from '@uform/next'
+import SchemaForm, { FormButtonGroup, Submit, Reset } from '@uform/next'
 import Index from '../index'
 import {
   Button,
@@ -12,6 +12,7 @@ import {
   Icon,
   Checkbox,
   NumberPicker,
+  TimePicker,
   Radio,
   Form,
   Tab
@@ -20,6 +21,10 @@ import {
 // style
 import '@alifd/next/dist/next.css'
 
+SchemaForm.FormButtonGroup = FormButtonGroup
+SchemaForm.Submit = Submit
+SchemaForm.Reset = Reset
+
 const renderSchema = {}
 
 const props = {
@@ -33,6 +38,7 @@ const props = {
     Select,
     Icon,
     DatePicker,
+    TimePicker,
     Checkbox,
     NumberPicker,
     Radio,
@@ -44,7 +50,7 @@ const props = {
   // 主题: dark/light,默认dark
   // themeStyle: 'light',
   // 是否展示布局组件,默认为false
-  showLayoutField: false,
+  showLayoutField: true,
   showPreviewBtn: true,
   showSourceCodeBtn: true,
   // 控制返回按钮点击事件
@@ -75,10 +81,37 @@ const props = {
   // 是否展示全局配置
   showGlobalCfg: true,
   // 全局配置额外项
-  extraGlobalCfgList: [],
+  extraGlobalCfgList: [
+    {
+      name: 'labelCol',
+      title: 'label宽度占比',
+      type: 'string'
+    },
+    {
+      name: 'wrapperCol',
+      title: 'wrapper宽度占比',
+      type: 'string'
+    },
+    {
+      name: 'editable',
+      title: '表单是否可编辑',
+      description: '若设置为false,则可快速搭建出表单详情页,只需设置每个组件的默认值',
+      type: 'boolean'
+    }
+  ],
   globalCfg: {},
   supportFieldList: [],
-  includeFieldListKeyList: ['input', 'multipleInput', 'number', 'radio', 'checkbox', 'date', 'month', 'daterange', 'time'],
+  includeFieldListKeyList: [
+    'input',
+    'multipleInput',
+    'number',
+    'radio',
+    'checkbox',
+    'date',
+    'month',
+    'daterange',
+    'time'
+  ],
 
   // 渲染引擎
   renderEngine: SchemaForm,

+ 2 - 4
packages/builder/src/demo/index.js

@@ -4,7 +4,7 @@ import Index from '../index'
 import {
   Button,
   Accordion,
-  Message,
+  Feedback,
   Upload,
   Input,
   Select,
@@ -17,8 +17,6 @@ import {
   Tab
 } from '@alife/next'
 
-// style
-// import '@alifd/next/dist/next.css'
 import '@alife/next/dist/next.min.css'
 
 const renderSchema = {}
@@ -28,7 +26,7 @@ const props = {
     version: '0.x',
     Button,
     Accordion,
-    Toast: Message,
+    Toast: Feedback.toast,
     Upload,
     Input,
     Select,

+ 4 - 1
packages/builder/src/index.js

@@ -26,8 +26,11 @@ const initialState = {
   codemode: false,
   componentProps: {},
   gbConfig: {
+    action: '',
+    labelCol: 8,
+    wrapperCol: 8,
     labelAlign: 'left',
-    labelTextAlign: 'left',
+    labelTextAlign: 'right',
     autoAddColon: true,
     needFormButtonGroup: false,
     inline: false,

+ 0 - 5
packages/builder/src/reducers/componentProps.js

@@ -13,11 +13,6 @@ export default (state = {}, action) => {
           return Object.assign(
             {},
             item,
-            name === '__id__'
-              ? {
-                value: id
-              }
-              : {},
             comp[name]
               ? {
                 value: comp[name]

+ 17 - 18
packages/builder/src/reducers/initSchemaData.js

@@ -75,21 +75,21 @@ export default (state = {}, action) => {
       }
       return newState
     case 'ADD_COMPONENT':
-      if (component.__key__ === 'layout') {
-        // 判断是否布局组件特殊处理
-        newState.properties[id] = {
-          type: 'object',
-          id,
-          __id__: id,
-          ...component.__key__data__,
-          properties: {},
-          'x-props': {
-            ...component.__key__data__['x-props'],
-            _extra: component
+      const _component_ =
+        component.__key__ === 'layout'
+          ? {
+            type: 'object',
+            id,
+            ...component.__key__data__,
+            properties: {},
+            'x-props': {
+              ...component.__key__data__['x-props'],
+              _extra: component
+            }
+          }
+          : {
+            ...component
           }
-        }
-        return newState
-      }
 
       const propertiesList1 =
         addType === 'layout'
@@ -108,14 +108,14 @@ export default (state = {}, action) => {
           }
         }
         propertiesList1[idx] = {
-          ...component,
+          ..._component_,
           id,
           'x-index': idx
         }
       } else {
         // 在最后插入新的组件
         propertiesList1[propertiesList1.length] = {
-          ...component,
+          ..._component_,
           id,
           'x-index': propertiesList1.length
         }
@@ -124,8 +124,7 @@ export default (state = {}, action) => {
       const _properties1 = {}
       propertiesList1.forEach(item => {
         _properties1[item.id] = {
-          ...item,
-          __id__: item.id
+          ...item
         }
       })
 

+ 5 - 1
packages/builder/src/style.js

@@ -10,6 +10,9 @@ export default styled.div`
   .next-checkbox-label {
     color: ${props => props.theme.whiteColor};
   }
+  .preview-main .next-checkbox-label {
+    color: #333;
+  }
   .schemaform-header {
     position: relative;
     height: 64px;
@@ -38,7 +41,8 @@ export default styled.div`
       top: 24px;
       width: 9px;
       height: 17px;
-      background: url('${props => props.theme.backIconUrl}') no-repeat center center;
+      background: url('${props =>
+    props.theme.backIconUrl}') no-repeat center center;
       background-size: 9px 17px;
     }
     &::after {

+ 2 - 2
packages/builder/src/utils/lang.js

@@ -18,7 +18,7 @@ export const isNum = isType('Number')
 export const isIter = obj => isArr(obj) || isObj(obj)
 
 const replaceSingleDefault = v => {
-  if (!isFlagValue(v)) return v
+  if (!isFlagValue(v)) return ''
 
   const { type, flag, value } = v
 
@@ -41,7 +41,7 @@ const replaceSingleDefault = v => {
     if (type === 'specify') {
       return value
     } else if (type === 'now') {
-      return now.format('YYYY-MM-DD HH:MM:SS')
+      return now.format('HH:MM:SS')
     } else if (type === 'url') {
       return params[value]
     }

+ 3 - 2
packages/builder/src/utils/util.js

@@ -142,7 +142,7 @@ export const wrapSubmitSchema = (schema, keepAll = false) => {
 /**
  * 根据schema获取有顺序的properties
  * @param {Object} schema
- * @param {Boolean} needFormat 是否需要转换返回数组,默认是
+ * @param {String} containerId 相对容器id
  */
 export const getOrderProperties = (schema = {}) => {
   const { properties = {} } = schema
@@ -166,7 +166,8 @@ export const getOrderProperties = (schema = {}) => {
     const index = item['x-index']
     if (typeof index === 'number') {
       if (!newProperties[index]) {
-        const _key = index > newProperties.length + 1 ? newProperties.length : index
+        const _key =
+          index > newProperties.length + 1 ? newProperties.length : index
         newProperties[_key] = {
           ...item,
           id: key