diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md
index c49bced5456..21c7908aa48 100644
--- a/docs/SUMMARY.md
+++ b/docs/SUMMARY.md
@@ -53,3 +53,4 @@
- [国际化](./Examples/antd/International.md)
- [知乎专栏](https://zhuanlan.zhihu.com/uform)
- [GitHub](https://github.com/alibaba/uform)
+- [PlayGround DEMO](../packages/builder/src/demo/index-1-x.js)
diff --git a/packages/builder/package.json b/packages/builder/package.json
index bd6ffb84dfc..b0a78c0eada 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -7,9 +7,6 @@
"type": "git",
"url": "git+https://github.com/alibaba/uform.git"
},
- "scripts": {
- "start": "node scripts/start.js"
- },
"bugs": {
"url": "https://github.com/alibaba/uform/issues"
},
@@ -28,6 +25,8 @@
"@uform/utils": "^0.1.15",
"@uform/validator": "^0.1.15",
"classnames": "^2.2.5",
+ "immutability-helper": "^3.0.0",
+ "lodash.flow": "^3.5.0",
"lodash.isequal": "^4.5.0",
"lodash.merge": "^4.6.1",
"lodash.pick": "^4.4.0",
@@ -37,6 +36,7 @@
"moment": "^2.24.0",
"prop-types": "^15.6.1",
"react-dnd": "^7.4.1",
+ "react-dnd-html5-backend": "^7.4.0",
"react-powerplug": "^1.0.0",
"react-redux": "^5.0.7",
"redux": "^4.0.0",
@@ -46,7 +46,8 @@
"uuid": "^3.2.1"
},
"publishConfig": {
- "access": "public"
+ "access": "public",
+ "registry": "https://registry.npm.alibaba-inc.com"
},
"gitHead": "4d068dad6183e8da294a4c899a158326c0b0b050",
"devDependencies": {
diff --git a/packages/builder/src/App.js b/packages/builder/src/App.js
index e9ec1332d28..3d638edc981 100644
--- a/packages/builder/src/App.js
+++ b/packages/builder/src/App.js
@@ -1,4 +1,4 @@
-import React, { Component } from 'react'
+import React, { Component, createRef } from 'react'
import cls from 'classnames'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
@@ -16,11 +16,13 @@ import isEqual from 'lodash.isequal'
import AppStyle from './style'
// components
-import FieldList from './components/fields/index'
-import Preview from './components/preview/index'
-import generateGlobalBtnList from './components/globalBtnList/index'
+import {
+ FieldList,
+ Preview,
+ GlobalBtnList,
+ PropsSetting
+} from './components/index'
-import PropsSetting from './components/props/propsSetting'
import { SchemaForm, Field } from './utils/baseForm'
import defaultGlobalCfgList from './configs/supportGlobalCfgList'
@@ -33,6 +35,8 @@ class App extends Component {
systemError: false,
accordionList: []
}
+ this.appRef = createRef(null)
+ this.appHeaderRef = createRef(null)
}
generateGlobalCfgList = () => {
@@ -63,10 +67,11 @@ class App extends Component {
}}
defaultValue={this.props.gbConfig}
labelAlign='left'
+ labelCol={10}
labelTextAlign='right'
>
{globalCfgList.map(props => (
-
系统发生异常
) : ( -可将选项拖动到主面板进行编辑
+可将选项拖动到主面板进行编辑
-+ 请从左边字段拖拽组件进来这里 +
+ ) : ( + React.createElement(Field, props) + )} +- 请从左边字段拖拽组件进来这里 -
- ) : ( - React.createElement(Field, { ...props, layoutId: id }) - )} - -请从左边字段添加组件进来吧
- } - - const children = - gbConfig.needFormButtonGroup === true ? ( -组件过多时可下拉查看更多
请从左边字段添加组件进来吧
+ } + + let children = ' ' + try { + children = + needFormButtonGroup === true || needFormButtonGroup === 'true' ? ( +请选择待编辑的表单字段
} @@ -219,9 +209,9 @@ class PropsSetting extends Component { } renderOptions() { - const { componentId = '', initSchemaData = {}, layoutId } = this.props + const { componentId, initSchemaData = {} } = this.props - if (!componentId) return null + if (!componentId.length) return null const curComponentAttr = getCompDetailById(componentId, initSchemaData) @@ -232,13 +222,9 @@ class PropsSetting extends Component { UI={this.props.UI} xprops={curComponentAttr['x-props'] || {}} onChange={xprops => { - this.props.editComponent( - componentId, - { - 'x-props': xprops - }, - layoutId - ) + this.props.editComponent(componentId, { + 'x-props': xprops + }) }} /> ) @@ -261,10 +247,8 @@ const mapStateToProps = state => state const mapDispatchToProps = dispatch => ({ showComponentProps: (id, comp) => dispatch(showComponentProps(id, comp)), - editComponentProps: (id, propsData) => - dispatch(editComponentProps(id, propsData)), - editComponent: (id, propsData, containerId) => - dispatch(editComponent(id, propsData, containerId)) + editComponentProps: (...args) => dispatch(editComponentProps(...args)), + editComponent: (...args) => dispatch(editComponent(...args)) }) class StyledPropsSettingComp extends React.Component { diff --git a/packages/builder/src/configs/supportConfigList.js b/packages/builder/src/configs/supportConfigList.js index dd1644b7140..452b16dbefa 100644 --- a/packages/builder/src/configs/supportConfigList.js +++ b/packages/builder/src/configs/supportConfigList.js @@ -2,9 +2,9 @@ const FIELDLIST = { ID: { name: '__id__', - title: '唯一标识', + title: '字段名称', type: 'string', - description: '唯一标识:发起请求时带上的参数id,必填,全局保证唯一。', + description: '字段名称:发起请求时带上的参数id,必填,全局保证唯一。', required: true }, PLACEHOLDER: { @@ -98,14 +98,14 @@ export const getPropsByKey = key => { return generateProps( [ 'ID', - 'DESCRIPTION', 'TITLE', 'DEFAULT', + 'DESCRIPTION', + 'PLACEHOLDER', 'REQUIRED', 'READONLY', 'DISABLED', - 'HIDDEN', - 'PLACEHOLDER' + 'HIDDEN' ], [ { @@ -123,14 +123,14 @@ export const getPropsByKey = key => { case 'number': return generateProps([ 'ID', - 'DESCRIPTION', 'TITLE', 'DEFAULT', + 'DESCRIPTION', + 'PLACEHOLDER', 'REQUIRED', 'READONLY', 'DISABLED', - 'HIDDEN', - 'PLACEHOLDER' + 'HIDDEN' ]) case 'date': case 'month': @@ -139,9 +139,9 @@ export const getPropsByKey = key => { return generateProps( [ 'ID', - 'DESCRIPTION', 'TITLE', 'DEFAULT', + 'DESCRIPTION', 'REQUIRED', 'READONLY', 'DISABLED', @@ -161,8 +161,8 @@ export const getPropsByKey = key => { return generateProps( [ 'ID', - 'DESCRIPTION', 'TITLE', + 'DESCRIPTION', 'REQUIRED', 'READONLY', 'DISABLED', @@ -240,7 +240,17 @@ export const getPropsByKey = key => { ] ) default: - return defaultProps + return generateProps([ + 'ID', + 'TITLE', + 'DEFAULT', + 'DESCRIPTION', + 'PLACEHOLDER', + 'REQUIRED', + 'READONLY', + 'DISABLED', + 'HIDDEN' + ]) } } diff --git a/packages/builder/src/constants/context.js b/packages/builder/src/constants/context.js new file mode 100644 index 00000000000..d4b4340f7e4 --- /dev/null +++ b/packages/builder/src/constants/context.js @@ -0,0 +1,2 @@ +import React from 'react' +export const { Consumer: FormConsumer, Provider: FormProvider } = React.createContext() diff --git a/packages/builder/src/constants/itemType.js b/packages/builder/src/constants/itemType.js new file mode 100644 index 00000000000..72ceaa9d424 --- /dev/null +++ b/packages/builder/src/constants/itemType.js @@ -0,0 +1,5 @@ +export default { + CARD: 'card', + FIELD: 'field', + LAYOUT: 'layout' +} diff --git a/packages/builder/src/demo/index-1-x.js b/packages/builder/src/demo/index-1-x.js index 56b53af8b1d..30d64dbb145 100644 --- a/packages/builder/src/demo/index-1-x.js +++ b/packages/builder/src/demo/index-1-x.js @@ -50,7 +50,7 @@ const props = { // 主题: dark/light,默认dark // themeStyle: 'light', // 是否展示布局组件,默认为false - showLayoutField: true, + showLayoutField: false, showPreviewBtn: true, showSourceCodeBtn: true, // 控制返回按钮点击事件 @@ -95,7 +95,8 @@ const props = { { name: 'editable', title: '表单是否可编辑', - description: '若设置为false,则可快速搭建出表单详情页,只需设置每个组件的默认值', + description: + '若设置为false,则可快速搭建出表单详情页,只需设置每个组件的默认值', type: 'boolean' } ], @@ -121,6 +122,7 @@ const props = { // console.info('index onChange data', data); // }, onSubmit: data => { + alert(`保存数据:${JSON.stringify(data)}`) console.info('index onSubmit data', data) } } diff --git a/packages/builder/src/index.js b/packages/builder/src/index.js index c6f589dee19..7cbad14a487 100644 --- a/packages/builder/src/index.js +++ b/packages/builder/src/index.js @@ -8,6 +8,8 @@ import rootReducer from './reducers' import App from './App' import ThemeList, { THEME_ENUM, DEFAULT_THEME } from './configs/theme' import { ThemeProvider } from 'styled-components' +import { DragDropContext } from 'react-dnd' +import HTML5Backend from 'react-dnd-html5-backend' const logger = createLogger({ collapsed: true @@ -20,8 +22,7 @@ const middleware = [ ].filter(Boolean) const initialState = { - componentId: '', - layoutId: '', + componentId: [], preview: false, codemode: false, componentProps: {}, @@ -48,7 +49,7 @@ const store = createStore( applyMiddleware(...middleware) ) -export default class Component extends React.Component { +class Component extends React.Component { static propTypes = { themeStyle: PropTypes.string } @@ -82,3 +83,5 @@ export default class Component extends React.Component { ) } } + +export default DragDropContext(HTML5Backend)(Component) diff --git a/packages/builder/src/reducers/componentId.js b/packages/builder/src/reducers/componentId.js index af0b17b0ef0..5e38e2550af 100644 --- a/packages/builder/src/reducers/componentId.js +++ b/packages/builder/src/reducers/componentId.js @@ -1,11 +1,10 @@ -export default (state = '', action) => { - let newState = state - const { data = {} } = action - const _componentId = data.componentId || '' +export default (state = [], action) => { + let newState = [...state] + const { data: { componentId = [] } = {}, type } = action - switch (action.type) { + switch (type) { case 'CHANGE_COMPONENT': - newState = _componentId + newState = Array.isArray(componentId) ? componentId : [componentId] return newState default: return state diff --git a/packages/builder/src/reducers/index.js b/packages/builder/src/reducers/index.js index d2e7dff5335..073d0d18560 100644 --- a/packages/builder/src/reducers/index.js +++ b/packages/builder/src/reducers/index.js @@ -3,7 +3,6 @@ import { combineReducers } from 'redux' import preview from './preview' import codemode from './codemode' import componentId from './componentId' -import layoutId from './layoutId' import componentProps from './componentProps' import gbConfig from './gbConfig' import initSchemaData from './initSchemaData' @@ -14,6 +13,5 @@ export default combineReducers({ codemode, componentProps, gbConfig, - initSchemaData, - layoutId + initSchemaData }) diff --git a/packages/builder/src/reducers/initSchemaData.js b/packages/builder/src/reducers/initSchemaData.js index 17b75c8f10a..f80f78c9dac 100644 --- a/packages/builder/src/reducers/initSchemaData.js +++ b/packages/builder/src/reducers/initSchemaData.js @@ -6,16 +6,49 @@ export default (state = {}, action) => { ...state } const { data = {} } = action - // eslint-disable-next-line const { component, id, + targetId, propsData = {}, existId = null, - addType, - containerId + containerId = [] } = data + const loop = (obj, idArr = []) => { + const _idArr = [...idArr] + const _id = _idArr.shift() + if (!_idArr.length) return obj.properties[_id] + return loop(obj.properties[_id], _idArr) + } + + const getProperties = (obj, idArr = []) => { + const _idArr = [...idArr] + const _id = _idArr.shift() + if (!_idArr.length) return obj.properties + return getProperties(obj.properties[_id], _idArr) + } + + const setProperties = (obj, idArr = [], prop) => { + const _idArr = [...idArr] + if (!_idArr.length) { + obj.properties = prop + } else { + const _id = _idArr.shift() + setProperties(obj.properties[_id], _idArr, prop) + } + } + + const deleteItem = (obj, idArr = []) => { + const _idArr = [...idArr] + const _id = _idArr.shift() + if (!_idArr.length) { + delete obj.properties[_id] + } else { + deleteItem(obj.properties[_id], _idArr) + } + } + switch (action.type) { case 'INIT_SCHEMA': // 自动生成z-index顺序 @@ -24,77 +57,49 @@ export default (state = {}, action) => { ...newState, ...newSchema } + return newState + case 'MOVE_COMOPNENT': + const sourceItem = loop(newState, [...id]) + const targetItem = loop(newState, [...targetId]) + + deleteItem(newState, [...id]) + + targetItem.properties[sourceItem.id] = sourceItem + return newState case 'CHANGE_COMPONENT_ORDER': - const { targetId } = data - let _tmpNewState = { - ...newState - } - if (containerId) { - _tmpNewState = newState.properties[containerId] + const _propertiesList = getOrderProperties(newState, [...containerId]) + const _sourceItem = loop(newState, [...id]) + const _targetItem = loop(newState, [...targetId]) + const targetIdx = _targetItem['x-index'] + const sourceIdx = _sourceItem['x-index'] + + if (id.length !== targetId.length) { + alert('目前只支持同级别组件的顺序替换') + return newState } - const propertiesList = getOrderProperties(_tmpNewState) - const targetItem = propertiesList.find(_item => _item.id === targetId) - let targetIdx = targetItem - ? targetItem['x-index'] - : propertiesList[propertiesList.length - 1]['x-index'] - const sourceItem = propertiesList.find(_item => _item.id === id) - const sourceIdx = sourceItem - ? sourceItem['x-index'] - : propertiesList[0]['x-index'] - const len = propertiesList.length - if (targetIdx < 0) { - targetIdx = 0 + + _propertiesList[targetIdx] = { + ..._sourceItem, + 'x-index': targetIdx } - propertiesList.splice(sourceIdx, 1) - for (let i = 0; i < targetIdx; i++) { - propertiesList[i] = { - ...propertiesList[i], - 'x-index': i - } + _propertiesList[sourceIdx] = { + ..._targetItem, + 'x-index': sourceIdx } - for (let i = len - 1; i > targetIdx; i--) { - propertiesList[i] = { - ...propertiesList[i - 1], - 'x-index': i + + const _properties11 = {} + _propertiesList.forEach(item => { + _properties11[item.id] = { + ...item } - } - propertiesList[targetIdx] = { - ...sourceItem, - 'x-index': targetIdx - } - const _properties = {} - propertiesList.forEach(item => { - _properties[item.id] = item }) - if (containerId) { - newState.properties[containerId].properties = _properties - } else { - newState.properties = _properties - } + setProperties(newState, containerId, _properties11) + return newState case 'ADD_COMPONENT': - const _component_ = - component.__key__ === 'layout' - ? { - type: 'object', - id, - ...component.__key__data__, - properties: {}, - 'x-props': { - ...component.__key__data__['x-props'], - _extra: component - } - } - : { - ...component - } - - const propertiesList1 = - addType === 'layout' - ? getOrderProperties(newState.properties[containerId]) - : getOrderProperties(newState) + const propertiesList1 = getOrderProperties(newState, [...containerId]) if (existId) { // 在特定的existId之前插入新的组件 @@ -108,14 +113,14 @@ export default (state = {}, action) => { } } propertiesList1[idx] = { - ..._component_, + ...component, id, 'x-index': idx } } else { // 在最后插入新的组件 propertiesList1[propertiesList1.length] = { - ..._component_, + ...component, id, 'x-index': propertiesList1.length } @@ -128,11 +133,7 @@ export default (state = {}, action) => { } }) - if (addType === 'layout') { - newState.properties[containerId].properties = _properties1 - } else { - newState.properties = _properties1 - } + setProperties(newState, containerId, _properties1) if (!newState.type) { newState.type = 'object' @@ -140,17 +141,17 @@ export default (state = {}, action) => { return newState case 'EDIT_COMPONENT': - const _data_ = - containerId && containerId !== id - ? state.properties[containerId].properties - : state.properties + const _data_ = getProperties(newState, id) + const lastId = [...id].pop() + Object.keys(_data_).forEach(compId => { if (compId) { _data_[compId] = merge( {}, _data_[compId], - id === null || compId === id + id === null || compId === lastId ? { + active: true, ...propsData } : { @@ -163,7 +164,7 @@ export default (state = {}, action) => { propsData['x-props'] && propsData['x-props'].enum && Array.isArray(propsData['x-props'].enum) && - (id === null || compId === id) + (id === null || compId === lastId) ) { _data_[compId]['x-props'] = _data_[compId]['x-props'] || {} _data_[compId]['x-props'].enum = propsData['x-props'].enum @@ -173,7 +174,7 @@ export default (state = {}, action) => { propsData['x-props'] && propsData['x-props'].requestOptions && propsData['x-props'].requestOptions.data && - (id === null || compId === id) + (id === null || compId === lastId) ) { _data_[compId]['x-props'].requestOptions = _data_[compId]['x-props'].requestOptions || {} @@ -184,39 +185,7 @@ export default (state = {}, action) => { }) return newState case 'DELETE_COMPONENT': - const newProp = { - ...newState.properties - } - - Object.keys(newState.properties).forEach(_key => { - if (_key === id) { - delete newProp[_key] - } - if ( - newState.properties[_key].type === 'object' && - newState.properties[_key].properties - ) { - Object.keys(newState.properties[_key].properties).forEach(__key => { - if (__key === id) { - delete newState.properties[_key].properties[__key] - } - }) - } - }) - - const _propertiesList_ = getOrderProperties({ - properties: newProp - }).filter(item => !!item) - const _properties_ = {} - _propertiesList_.forEach((item, idx) => { - if (item && item.id) { - _properties_[item.id] = { - ...item, - 'x-index': idx - } - } - }) - newState.properties = _properties_ + deleteItem(newState, [...id]) return newState default: return state diff --git a/packages/builder/src/reducers/layoutId.js b/packages/builder/src/reducers/layoutId.js deleted file mode 100644 index f5da407d8c3..00000000000 --- a/packages/builder/src/reducers/layoutId.js +++ /dev/null @@ -1,13 +0,0 @@ -export default (state = '', action) => { - const { data = {} } = action - const { id = '' } = data - let newState = state - - switch (action.type) { - case 'CHANGE_LAYOUTID': - newState = id - return newState - default: - return state - } -} diff --git a/packages/builder/src/style.js b/packages/builder/src/style.js index 2a9c4ebdb3f..d54e05c1817 100644 --- a/packages/builder/src/style.js +++ b/packages/builder/src/style.js @@ -87,7 +87,6 @@ export default styled.div` position: relative; overflow: hidden; padding: 0 340px 0 240px; - min-height: 700px; &::after { content: ""; diff --git a/packages/builder/src/utils/lang.js b/packages/builder/src/utils/lang.js index 1265165a7b4..ab2361af761 100644 --- a/packages/builder/src/utils/lang.js +++ b/packages/builder/src/utils/lang.js @@ -18,14 +18,22 @@ export const isNum = isType('Number') export const isIter = obj => isArr(obj) || isObj(obj) const replaceSingleDefault = v => { - if (!isFlagValue(v)) return '' + if (!isFlagValue(v)) return v const { type, flag, value } = v const now = moment(Date.now()) const params = Arg.all() - if (flag === 'date') { + if (flag === 'weekRange') { + if (type === 'pastStart') { + return now.subtract(value, 'weeks').format('YYYY-MM-DD') + } else if (type === 'future') { + return now.add(value, 'weeks').format('YYYY-MM-DD') + } else if (type === 'specify') { + return value + } + } else if (flag === 'date') { if (type === 'past') { return now.subtract(value, 'days').format('YYYY-MM-DD') } else if (type === 'future') { diff --git a/packages/builder/src/utils/util.js b/packages/builder/src/utils/util.js index 042ac79659d..9257959b268 100644 --- a/packages/builder/src/utils/util.js +++ b/packages/builder/src/utils/util.js @@ -1,3 +1,4 @@ +import React from 'react' import merge from 'lodash.merge' export * from './comp' @@ -38,37 +39,24 @@ export const wrapEnums = enums => /** * 根据组件id获取组件信息 - * @param {String} componentId 组件的id + * @param {Array} componentIdList 组件的id list * @param {Array} schema 组件schema */ -export const getCompDetailById = (componentId, schema = {}) => { +export const getCompDetailById = (componentIdList = [], schema = {}) => { + const _componentIdList = [...componentIdList] + const _componentId = _componentIdList.shift() const { properties = {} } = schema - if (properties[componentId]) { - return { - ...properties[componentId], - id: componentId - } - } - - if (!Object.keys(properties).length) { - return {} - } else { - for (const key in properties) { - if (Object.hasOwnProperty.call(properties, key)) { - const childProps = properties[key].properties - if (childProps && typeof childProps === 'object') { - if (childProps[componentId]) { - return { - ...childProps[componentId], - id: componentId - } - } - } + if (!_componentIdList.length) { + return properties[_componentId] + ? { + id: _componentId, + ...properties[_componentId] } - } - return {} + : {} } + + return getCompDetailById(_componentIdList, properties[_componentId]) } /** @@ -144,7 +132,12 @@ export const wrapSubmitSchema = (schema, keepAll = false) => { * @param {Object} schema * @param {String} containerId 相对容器id */ -export const getOrderProperties = (schema = {}) => { +export const getOrderProperties = (schema = {}, containerId = []) => { + if (containerId.length) { + const id = containerId.shift() + return getOrderProperties(schema.properties[id], containerId) + } + const { properties = {} } = schema if (isEmptyObj(properties)) return [] @@ -260,3 +253,15 @@ export const checkRepeatId = (schema = {}) => { loop(schema) return !!Object.keys(result).length } + +export const wrapComp2Class = Comp => + class extends React.Component { + render() { + return