mirror of
https://github.com/zhigang1992/form-render.git
synced 2026-01-12 09:04:06 +08:00
Merge pull request #57 from alibaba/dev
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
# Change Log
|
||||
|
||||
### 0.4.5
|
||||
|
||||
- [+] 添加了“复杂联动”的相关 Demo 例子
|
||||
- [+] 现在 labelWidth 允许输入 “20%”,“2.5rem” 等
|
||||
- [!] 修复了一个类型为对象的自定义组件初始值始终为空的问题
|
||||
|
||||
### 0.4.4
|
||||
|
||||
- [+] 发布了 schema 书写利器 `form-render snippets`(vscode 插件),在 vscode 商店中搜索“formrender”
|
||||
|
||||
32
README.md
32
README.md
@@ -108,17 +108,17 @@ ReactDOM.render(<Demo />, rootElement);
|
||||
|
||||
### API
|
||||
|
||||
| Prop | Type | Required | Default | Description |
|
||||
| ----------------- | :--------: | :------: | :--------: | :---------------------------------------------: |
|
||||
| **`name`** | `String` | `Y` | `$form` | 表单的名称 |
|
||||
| **`propsSchema`** | `Object` | `Y` | `{}` | 表单属性配置 json |
|
||||
| **`uiSchema`** | `Object` | `N` | `{}` | 表单 UI 配置 json |
|
||||
| **`formData`** | `Object` | `N` | `{}` | 配置数据 |
|
||||
| **`onChange`** | `Function` | `Y` | `() => {}` | 数据更改回调函数 |
|
||||
| **`onValidate`** | `Function` | `N` | `() => {}` | 表单输入校验回调 |
|
||||
| **`displayType`** | `String` | `N` | `column` | 设置表单横向排列或者纵向排序`column`/ `row` |
|
||||
| **`readOnly`** | `Boolean` | `N` | `false` | 预览模式/可编辑模式 |
|
||||
| **`labelWidth`** | `Number` | `N` | `120` | label 的长度,指明 label 的长度(px),默认 120 |
|
||||
| Prop | Type | Required | Default | Description |
|
||||
| ------------------ | :---------------: | :------: | :--------: | :-------------------------------------------------------------------: |
|
||||
| **`propsSchema`** | `Object` | ✓ | `{}` | 表单属性配置 json |
|
||||
| **`uiSchema`** | `Object` | | `{}` | 表单 UI 配置 json(可以合并到 propsSchema) |
|
||||
| **`formData`** | `Object` | | `{}` | 配置数据 |
|
||||
| **`onChange`** | `Function` | ✓ | `() => {}` | 数据更改回调函数 |
|
||||
| **`onValidate`** | `Function` | | `() => {}` | 表单输入校验回调 |
|
||||
| **`displayType`** | `String` | | `column` | 设置表单横向排列或者纵向排序`column`/ `row` |
|
||||
| **`showDescIcon`** | `Boolean` | | `false` | 描述是否用 tooltip 展示。`displayType`为 `row`时建议设为 `true` |
|
||||
| **`readOnly`** | `Boolean` | | `false` | 预览模式/可编辑模式 |
|
||||
| **`labelWidth`** | `Number`/`String` | | `110` | label 的长度,默认 110。如为数字则单位是 px,也可以使用'20%'/'2rem'等 |
|
||||
|
||||
**注 1:** 设置表单 `displayType` 为 row 时候,请设置 `showDescIcon` 为 `true`,隐藏说明,效果会更好
|
||||
**注 2:** **onChange** 方法会用于初始化表单 data,如果不写会造成没有初始值的表单元素无法渲染(出现不报错也不显示的情况)
|
||||
@@ -128,11 +128,11 @@ ReactDOM.render(<Demo />, rootElement);
|
||||
|
||||
| Prop | Type | Required | Default | Description |
|
||||
| ------------------ | :-------: | :------: | :-----: | :-----------------------------------------------: |
|
||||
| **`column`** | `Number` | `1` | `N` | 整体布局 1 排 N,局部的 1 排 N 一般使用`ui:width` |
|
||||
| **`showValidate`** | `Boolean` | `N` | `true` | 是否展示校验信息 |
|
||||
| **`showDescIcon`** | `Boolean` | `N` | `false` | 是否将文字形式说明显示成描述 tooltip 形式 |
|
||||
| **`widgets`** | `Object` | `N` | `{}` | 自定义组件 |
|
||||
| **`mapping`** | `Object` | `N` | `{}` | 用于修改默认组件映射表 |
|
||||
| **`column`** | `Number` | | `1` | 整体布局 1 排 N,局部的 1 排 N 一般使用`ui:width` |
|
||||
| **`widgets`** | `Object` | | `{}` | 自定义组件 |
|
||||
| **`name`** | `String` | | `$form` | 表单的名称 |
|
||||
| **`showValidate`** | `Boolean` | | `true` | 是否展示校验信息 |
|
||||
| **`mapping`** | `Object` | | `{}` | 用于修改默认组件映射表 |
|
||||
|
||||
## 快速书写 schema
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class Root extends Component {
|
||||
displayType: 'column',
|
||||
showDescIcon: false,
|
||||
readOnly: false,
|
||||
labelWidth: 120,
|
||||
labelWidth: 110,
|
||||
};
|
||||
|
||||
onThemeChange = e => {
|
||||
@@ -72,6 +72,7 @@ class Root extends Component {
|
||||
>
|
||||
<Radio value="simplest">最简样例</Radio>
|
||||
<Radio value="basic">基础控件</Radio>
|
||||
<Radio value="function">复杂联动</Radio>
|
||||
<Radio value="input">个性输入框</Radio>
|
||||
<Radio value="select">个性选择框</Radio>
|
||||
<Radio value="date">日期</Radio>
|
||||
|
||||
92
demo/json/function.json
Normal file
92
demo/json/function.json
Normal file
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"propsSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"case1": {
|
||||
"title": "整体隐藏",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showMore": {
|
||||
"title": "显示更多",
|
||||
"type": "boolean"
|
||||
},
|
||||
"x1": {
|
||||
"title": "输入框1",
|
||||
"type": "string",
|
||||
"ui:hidden": "{{rootValue.showMore === false}}"
|
||||
},
|
||||
"x2": {
|
||||
"title": "输入框2",
|
||||
"type": "string",
|
||||
"ui:hidden": "{{rootValue.showMore === false}}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"case2": {
|
||||
"title": "选项联动",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bi": {
|
||||
"title": "汇款币种",
|
||||
"type": "string",
|
||||
"enum": ["rmb", "dollar"],
|
||||
"enumNames": ["人民币", "美元"]
|
||||
},
|
||||
"inputName": {
|
||||
"title": "金额",
|
||||
"description": "{{rootValue.bi === 'dollar' ? '一次汇款不超过150美元':'一次汇款不超过1000元'}}",
|
||||
"type": "string",
|
||||
"ui:options": "{{{addonBefore: rootValue.bi === 'rmb'? '¥':'$', addonAfter: rootValue.bi === 'rmb'? '元':'美元'}}}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"case3": {
|
||||
"title": "列表/显示不同组件",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ruleList": {
|
||||
"title": "球员筛选",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"attr": {
|
||||
"title": "标准",
|
||||
"type": "string",
|
||||
"enum": ["goal", "league"],
|
||||
"enumNames": ["入球数", "所在联盟"],
|
||||
"ui:width": "40%"
|
||||
},
|
||||
"relation": {
|
||||
"title": "-",
|
||||
"type": "string",
|
||||
"enum": [">", "<", "="],
|
||||
"ui:hidden": "{{rootValue.attr === 'league'}}",
|
||||
"ui:width": "20%"
|
||||
},
|
||||
"goal": {
|
||||
"title": "入球数",
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+$",
|
||||
"message": {
|
||||
"pattern": "输入正确得分"
|
||||
},
|
||||
"ui:hidden": "{{rootValue.attr !== 'goal'}}",
|
||||
"ui:width": "40%"
|
||||
},
|
||||
"league": {
|
||||
"title": "名称",
|
||||
"type": "string",
|
||||
"enum": ["a", "b", "c"],
|
||||
"enumNames": ["西甲", "英超", "中超"],
|
||||
"ui:hidden": "{{rootValue.attr !== 'league'}}",
|
||||
"ui:width": "40%"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -63,7 +63,7 @@ fs.writeFileSync(path.join(__dirname, 'schema.json'), JSON.stringify(apiInfo, nu
|
||||
|
||||
<table><tr><td>React 输入</td><td>标准Json schema 输出</td><td>带 Ui 配置的 schema 输出</td></tr><tr><td><img src="https://img.alicdn.com/tfs/TB1jVQFtuuSBuNjy1XcXXcYjFXa-1004-1310.png" width="420"></td><td><img src="https://gw.alicdn.com/tfs/TB14I0Rzx1YBuNjy1zcXXbNcXXa-862-1538.png" width="310"></td><td><img src="https://gw.alicdn.com/tfs/TB1r9QFwXmWBuNjSspdXXbugXXa-898-1460.png" width="340"></td></tr></table>
|
||||
|
||||
可以参考 [Demo](http://github.com/tw93/proptypes-to-json-schema/tree/master/demo) 中的使用
|
||||
可以参考 [Demo](http://github.com/form-render/proptypes-to-json-schema/tree/master/demo) 中的使用
|
||||
|
||||
## Changelog
|
||||
|
||||
|
||||
@@ -21,32 +21,21 @@ Could not find a declaration file for module 'form-render/lib/antd'. '/Users/nas
|
||||
因为 form-render 是以 JavaScript 书写,缺少 typescript 的声明文件。
|
||||
目前在 ts 项目中使用 form-render 只需自己创建一个 `index.d.ts`,具体如下:
|
||||
|
||||
1. 创建一个 `types` 目录,用来存放所有声明文件。可将 `form-render` 的声明文件放到 `types/form-render/index.d.ts` 中。
|
||||
1. 在 `src` 中创建一个 `types` 目录,用来存放所有声明文件 `index.d.ts`。
|
||||
|
||||
目录结构如下:
|
||||
|
||||
```
|
||||
/path/to/project
|
||||
├── src
|
||||
| └── index.ts
|
||||
├── types
|
||||
| └── form-render
|
||||
| └── index.d.ts
|
||||
| ├── index.ts
|
||||
| └── types
|
||||
| └── index.d.ts
|
||||
|
|
||||
└── tsconfig.json
|
||||
```
|
||||
|
||||
2. 注意在 `tsconfig.json` 中需要 `include` types 文件夹
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
...
|
||||
},
|
||||
"include": ["src", "types"]
|
||||
}
|
||||
```
|
||||
|
||||
3. `form-render/index.d.ts` 文件如下书写。注意使用 fusion 的同学 module 名称变为`'form-render/lib/fusion'`
|
||||
2. `index.d.ts` 文件如下书写。注意使用 fusion 的同学 module 名称变为`'form-render/lib/fusion'`
|
||||
|
||||
```js
|
||||
declare module 'form-render/lib/antd' {
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
}
|
||||
|
||||
.fr-validate {
|
||||
margin: 3px 0 0 12px;
|
||||
margin-left: 12px;
|
||||
font-size: 12px;
|
||||
word-break: break-all;
|
||||
color: #f5222d;
|
||||
@@ -69,7 +69,7 @@
|
||||
/* Row */
|
||||
|
||||
.fr-validate-row {
|
||||
margin-left: 0;
|
||||
margin: 3px 0 0 0;
|
||||
}
|
||||
|
||||
.fr-label-row {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "form-render",
|
||||
"version": "0.4.4",
|
||||
"version": "0.4.5",
|
||||
"description": "通过 JSON Schema 生成标准 Form,常用于自定义搭建配置界面生成",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -2,7 +2,12 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getValidateText } from './validate';
|
||||
import { isHidden, isDependShow } from './isHidden';
|
||||
import { evaluateString, isLooselyNumber, isFunction } from './utils';
|
||||
import {
|
||||
evaluateString,
|
||||
isLooselyNumber,
|
||||
isCssLength,
|
||||
isFunction,
|
||||
} from './utils';
|
||||
|
||||
// asField拆分成逻辑组件和展示组件,从而可替换展示组件的方式完全插拔fr的样式
|
||||
export const asField = ({ FieldUI, Widget }) => {
|
||||
@@ -218,7 +223,11 @@ export const DefaultFieldUI = ({
|
||||
contentClass += ' flex justify-end pr2';
|
||||
}
|
||||
|
||||
const _labelWidth = isLooselyNumber(labelWidth) ? Number(labelWidth) : 120; // 默认是 25% 的长度
|
||||
const _labelWidth = isLooselyNumber(labelWidth)
|
||||
? Number(labelWidth)
|
||||
: isCssLength(labelWidth)
|
||||
? labelWidth
|
||||
: 110; // 默认是 110px 的长度
|
||||
let labelStyle = { width: _labelWidth };
|
||||
if (type === 'boolean') {
|
||||
labelStyle = { flexGrow: 1 };
|
||||
|
||||
@@ -53,6 +53,7 @@ function resolve(schema, data, options = {}) {
|
||||
// 必选值,对象的子集
|
||||
default: def,
|
||||
required = [],
|
||||
'ui:widget': widget,
|
||||
} = schema;
|
||||
const {
|
||||
// 按照required规则做数据补全
|
||||
@@ -62,6 +63,13 @@ function resolve(schema, data, options = {}) {
|
||||
const value =
|
||||
typeof data === 'undefined' ? getDefaultValue(schema) : clone(data);
|
||||
if (type === 'object') {
|
||||
// 如果自定义组件
|
||||
if (widget) {
|
||||
if (def && typeof def === 'object') {
|
||||
return def;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
const subs = properties || {};
|
||||
const ret = {};
|
||||
Object.keys(subs).forEach(name => {
|
||||
@@ -74,6 +82,8 @@ function resolve(schema, data, options = {}) {
|
||||
return ret;
|
||||
}
|
||||
if (type === 'array') {
|
||||
// 如果自定义组件
|
||||
if (widget) return value;
|
||||
if (def && Array.isArray(def)) {
|
||||
return def;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ export function isLooselyNumber(num) {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isCssLength(str) {
|
||||
if (typeof str !== 'string') return false;
|
||||
return str.match(/^([0-9])*(%|px|rem|em)$/i);
|
||||
}
|
||||
|
||||
// 深度对比
|
||||
export function isDeepEqual(param1, param2) {
|
||||
if (param1 === undefined && param2 === undefined) return true;
|
||||
@@ -182,3 +187,18 @@ export function isFunction(func) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 判断schema中是否有属性值是函数表达式
|
||||
export function isFunctionSchema(schema) {
|
||||
return Object.keys(schema).some(key => {
|
||||
if (typeof schema[key] === 'function') {
|
||||
return true;
|
||||
} else if (typeof schema[key] === 'string') {
|
||||
return isFunction(schema[key]);
|
||||
} else if (typeof schema[key] === 'object') {
|
||||
return isFunctionSchema(schema[key]);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class FormRender extends React.Component {
|
||||
onChange: () => {},
|
||||
onValidate: () => {},
|
||||
readOnly: false,
|
||||
labelWidth: 120,
|
||||
labelWidth: 110,
|
||||
};
|
||||
|
||||
constructor() {
|
||||
|
||||
@@ -9,7 +9,7 @@ module.exports = {
|
||||
antd: './antd.js',
|
||||
fusion: './fusion.js',
|
||||
},
|
||||
devtool: 'inline-source-map',
|
||||
devtool: 'source-map', // smaller bundle size: https://webpack.js.org/configuration/devtool/
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
path: resolve(__dirname, 'dist'),
|
||||
|
||||
Reference in New Issue
Block a user