Merge pull request #33 from alibaba/dev

修复了日期组件formData修改后ui展示上不更新的问题。
列表可以使用ui:readonly进入“只读”模式
This commit is contained in:
FateRiddle
2019-12-04 19:51:37 +08:00
committed by GitHub
10 changed files with 150 additions and 107 deletions

View File

@@ -1,5 +1,10 @@
# Change Log
### 0.3.2
- [!] 列表支持`ui:readonly`进入只读模式,不允许对列表进行增、删和拖拽操作
- [!] fix 了日期类组件传入数据变化后显示日期无变化的 bug
### 0.3.1
- [+] 重磅schema 里大部分属性现在都支持函数表达式,方便表单组件间的关联(详见文档 UISchema、PropsSchema 部分)

View File

@@ -10,9 +10,9 @@
<body>
<div id="__render_content_"></div>
<script src="//unpkg.com/react@16.x/umd/react.production.min.js"></script>
<script src="//unpkg.com/react@16.x/umd/react.development.js"></script>
<script src="//unpkg.com/react-dom@16.x/umd/react-dom.production.min.js"></script>
<script src="//unpkg.com/react-dom@16.x/umd/react-dom.development.js"></script>
<script src="//unpkg.com/prop-types@15.x/prop-types.min.js"></script>

View File

@@ -63,7 +63,14 @@
### 控制表单项的 UI 展示(共通配置)
- `ui:disabled`: 可控制 input、number、date、checkbox、radio、select、switch 对于组件的 disabled 属性(变灰不可点击)
- `ui:readonly`:可控制 input、number 组件中的 readonly 属性(不可编辑,但不变灰)
- `ui:readonly`:可控制 input、number 组件中的 readonly 属性(不可编辑,但不变灰),列表也支持`readonly`,效果是列表的控件都会隐藏,导致列表不能增、删和拖拽,进入“只读”模式。但注意列表内的内容还是允许修改的,所以特别要注意如果列表套列表的场景,内部的列表也要 "ui:readonly": true
```js
"someList": {
"ui:readonly": true
}
```
- `ui:hidden`:可控制所有基础组件是否显示,可使用 true/false 或表达式,例如:
```json

View File

@@ -1,6 +1,6 @@
{
"name": "form-render",
"version": "0.3.1",
"version": "0.3.2",
"description": "通过 JSON Schema 生成标准 Form常用于自定义搭建配置界面生成",
"repository": {
"type": "git",

View File

@@ -9,21 +9,7 @@ function clone(data) {
// 获取当前字段默认值
function getDefaultValue({ default: def, enum: enums = [], type }) {
// 如果设置默认值,优先从默认值中获取
if (typeof def !== 'undefined') {
return def;
}
// array且enum的情况为多选框默认值[]
if (type === 'array' && enums.length > 0) {
return [];
}
// 如果enum是表达式不处理
// 如果设置枚举值,其次从枚举值中获取
if (Array.isArray(enums) && enums[0] && typeof enums[0] !== 'undefined') {
return enums[0];
}
// 最后使用对应基础类型的默认值
return {
const defaultValue = {
array: [],
boolean: false,
integer: '',
@@ -32,7 +18,45 @@ function getDefaultValue({ default: def, enum: enums = [], type }) {
object: {},
string: '',
range: null,
}[type];
};
const isFunction = func => {
if (typeof func === 'function') {
return true;
}
if (typeof func === 'string' && func.substring(0, 1) === '@') {
return true;
}
return false;
};
if (isFunction(def)) {
return defaultValue[type];
}
if (isFunction(enums)) {
if (type === 'array') {
return [];
}
if (type === 'string' || type === 'number') {
return '';
}
}
// 如果设置默认值,优先从默认值中获取
if (typeof def !== 'undefined') {
return def;
}
// array且enum的情况为多选框默认值[]
if (type === 'array' && enums.length) {
return [];
}
// 如果enum是表达式不处理
// 如果设置枚举值,其次从枚举值中获取
if (Array.isArray(enums) && enums[0] && typeof enums[0] !== 'undefined') {
return enums[0];
}
// 最后使用对应基础类型的默认值
return defaultValue[type];
}
function resolve(schema, data, options = {}) {
@@ -44,6 +68,7 @@ function resolve(schema, data, options = {}) {
// 数组子集
items,
// 必选值,对象的子集
default: def,
required = [],
} = schema;
const {
@@ -66,6 +91,9 @@ function resolve(schema, data, options = {}) {
return ret;
}
if (type === 'array') {
if (def) {
return value;
}
const subs = [].concat(items || []);
const ret = [];
value.forEach &&

View File

@@ -19,7 +19,7 @@ export default function date(p) {
let defaultObj = {};
if (p.value) {
defaultObj = {
defaultValue: moment(p.value, dateFormat),
value: moment(p.value, dateFormat),
};
}

View File

@@ -51,7 +51,7 @@ class ListItem extends React.Component {
render() {
const { item, p = {}, name, fold } = this.props;
const descProps = { ...p, index: name };
const { options = {} } = p;
const { options = {}, readonly } = p;
const { foldable: canFold, hideDelete } = options;
// 只有当items为object时才做收起fold处理
const isObj = p.schema.items && p.schema.items.type == 'object';
@@ -72,8 +72,8 @@ class ListItem extends React.Component {
style={{ position: 'absolute', top: 12, right: 36 }}
/>
)}
<DragHandle />
{!((canFold && fold) || hideDelete) && (
{!readonly && <DragHandle />}
{!((canFold && fold) || hideDelete || readonly) && (
<Button
className="self-end"
type="dashed"
@@ -105,6 +105,7 @@ class FieldList extends React.Component {
render() {
const { p, foldList = [], toggleFoldItem } = this.props;
const { readonly } = p;
const list = p.value || [];
return (
<ul className="pl0 ma0">
@@ -127,37 +128,39 @@ class FieldList extends React.Component {
})}
/>
))}
<div className="tr">
<Button className="" icon="file-add" onClick={this.handleAddClick}>
新增
</Button>
{p.extraButtons &&
p.extraButtons.length > 0 &&
p.extraButtons.map(item => (
<Button
className="ml2"
icon={item.icon}
onClick={() => {
if (item.callback === 'clearAll') {
p.onChange(p.name, []);
return;
}
if (item.callback === 'copyLast') {
const value = [...p.value];
const lastIndex = value.length - 1;
value.push(lastIndex > -1 ? value[lastIndex] : p.newItem);
p.onChange(p.name, value);
return;
}
if (typeof window[item.callback] === 'function') {
window[item.callback].call(); // eslint-disable-line
}
}}
>
{item.text}
</Button>
))}
</div>
{!readonly && (
<div className="tr">
<Button className="" icon="file-add" onClick={this.handleAddClick}>
新增
</Button>
{p.extraButtons &&
p.extraButtons.length > 0 &&
p.extraButtons.map(item => (
<Button
className="ml2"
icon={item.icon}
onClick={() => {
if (item.callback === 'clearAll') {
p.onChange(p.name, []);
return;
}
if (item.callback === 'copyLast') {
const value = [...p.value];
const lastIndex = value.length - 1;
value.push(lastIndex > -1 ? value[lastIndex] : p.newItem);
p.onChange(p.name, value);
return;
}
if (typeof window[item.callback] === 'function') {
window[item.callback].call(); // eslint-disable-line
}
}}
>
{item.text}
</Button>
))}
</div>
)}
</ul>
);
}

View File

@@ -19,7 +19,7 @@ export default function date(p) {
let defaultObj = {};
if (p.value) {
defaultObj = {
defaultValue: moment(p.value, dateFormat),
value: moment(p.value, dateFormat),
};
}

View File

@@ -48,7 +48,7 @@ class ListItem extends React.Component {
render() {
const { item, p = {}, name, fold } = this.props;
const descProps = { ...p, index: name };
const { options = {} } = p;
const { options = {}, readonly } = p;
const { foldable: canFold, hideDelete } = options;
// 只有当items为object时才做收起fold处理
const isObj = p.schema.items && p.schema.items.type == 'object';
@@ -69,8 +69,8 @@ class ListItem extends React.Component {
style={{ position: 'absolute', top: 12, right: 36 }}
/>
)}
<DragHandle />
{!((canFold && fold) || hideDelete) && (
{!readonly && <DragHandle />}
{!((canFold && fold) || hideDelete || readonly) && (
<Button
className="self-end"
onClick={() => {
@@ -100,6 +100,7 @@ class FieldList extends React.Component {
render() {
const { p, foldList = [], toggleFoldItem } = this.props;
const { readonly } = p;
const list = p.value || [];
return (
<ul className="pl0 ma0">
@@ -122,38 +123,40 @@ class FieldList extends React.Component {
})}
/>
))}
<div className="tr">
<Button className="" onClick={this.handleAddClick}>
<Icon type="add" />
新增
</Button>
{p.extraButtons &&
p.extraButtons.length > 0 &&
p.extraButtons.map(item => (
<Button
className="ml2"
onClick={() => {
if (item.callback === 'clearAll') {
p.onChange(p.name, []);
return;
}
if (item.callback === 'copyLast') {
const value = [...p.value];
const lastIndex = value.length - 1;
value.push(lastIndex > -1 ? value[lastIndex] : p.newItem);
p.onChange(p.name, value);
return;
}
if (typeof window[item.callback] === 'function') {
window[item.callback].call(); // eslint-disable-line
}
}}
>
<Icon type={item.icon} />
{item.text}
</Button>
))}
</div>
{!readonly && (
<div className="tr">
<Button className="" onClick={this.handleAddClick}>
<Icon type="add" />
新增
</Button>
{p.extraButtons &&
p.extraButtons.length > 0 &&
p.extraButtons.map(item => (
<Button
className="ml2"
onClick={() => {
if (item.callback === 'clearAll') {
p.onChange(p.name, []);
return;
}
if (item.callback === 'copyLast') {
const value = [...p.value];
const lastIndex = value.length - 1;
value.push(lastIndex > -1 ? value[lastIndex] : p.newItem);
p.onChange(p.name, value);
return;
}
if (typeof window[item.callback] === 'function') {
window[item.callback].call(); // eslint-disable-line
}
}}
>
<Icon type={item.icon} />
{item.text}
</Button>
))}
</div>
)}
</ul>
);
}

View File

@@ -1,7 +1,7 @@
const { resolve } = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports = {
@@ -38,27 +38,24 @@ module.exports = {
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
],
use: ['style-loader', 'css-loader'],
},
],
},
plugins: [
new MonacoWebpackPlugin({
languages: ['json'],
features: ['snippets', 'suggest']
features: ['snippets', 'suggest'],
}),
new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
new HtmlWebpackPlugin({
resources: {
js: [
'react@16.x/umd/react.production.min.js',
'react-dom@16.x/umd/react-dom.production.min.js',
'react@16.x/umd/react.development.js',
'react-dom@16.x/umd/react-dom.development.js',
'prop-types@15.x/prop-types.min.js',
'moment@2.24.0/min/moment.min.js',
'@alifd/next@1.x/dist/next.min.js'
'@alifd/next@1.x/dist/next.min.js',
],
css: ['@alifd/next@1.x/dist/next.min.css'],
},
@@ -73,14 +70,14 @@ module.exports = {
uglifyOptions: {
compress: {
unused: true,
drop_debugger: true
drop_debugger: true,
},
warnings: false,
output: {
comments: false
}
}
})
comments: false,
},
},
}),
],
externals: {
react: 'var window.React',
@@ -101,7 +98,7 @@ module.exports = {
resolve: {
extensions: ['.js', '.jsx'],
alias: {
'monaco-editor': 'monaco-editor/esm/vs/editor/editor.api'
}
'monaco-editor': 'monaco-editor/esm/vs/editor/editor.api',
},
},
};