未验证 提交 ad74ee37 编写于 作者: F FateRiddle 提交者: GitHub

Dev (#432)

* 一些造成hooks提示的报错fix。默认值生成不再赋值false

* table-render 内部打包优化

* v1.1.1

* 旧schema转新的转化器

* 改成default export,打包接babel-plugin-import

* 失败的tree shaking

* lazy load 一部分的组件

* import 方式改变

* 文档修改

* 避免formData注入的非schema的key的值被去掉

* 改用babel打包,因为用到了tree shaking, rollup不知道咋配

* 文档fix, range被bind的情况下,校验错误

* changelog

* v1.5.0

* 入口文件变了

* v1.5.1

* suspense移动到真正异步加载的部分

* html支持range等的只读展示 #431

* 去掉一些很小的包的懒加载,只分包最大的

* 切换到babel模式,需要开less

* table类list的props和itemProps扩展透传

* changelog

* v1.5.2
上级 4c552690
......@@ -50,13 +50,16 @@ npm publish
发布后记得到 [releases 页](https://github.com/alibaba/x-render/releases/) 补上最新发布日志
#### 6. 发布 beta 版本
#### 6. 实际装包测试(本地 or 发布 beta 版本)
beta 版本的版本号规范为 x.x.x-beta.x,一般用于大功能上线前的真实测试,不会被正常 npm i 安装。发布流程如下:
- 本地测试
1. 进入 package/form-render 文件夹
推荐一下 yalc。是一个完全可以当做 yarn 来使用,但是发包和装包都在本地的工具,个人调试强烈推荐
2. 执行
- 发 beta 包
beta 版本的版本号规范为 x.x.x-beta.x,一般用于大功能上线前的真实测试,不会被正常 npm i 安装。
进入 package/form-render 文件夹,执行
```sh
# 换版本号、打tag。注意tag要打,便于release note的维护
......@@ -68,3 +71,10 @@ npm run beta
#### 7. 分支管理
外部同学请 fork,内部同学请在 dev 分支开发,然后都发 pull-request 到 master 分支,由负责同学审核后合并,master 分支请勿人为去动
#### 8. 如何检验一个 pull request
```
# ID 为 pr 的 id
git fetch origin pull/ID/head && git checkout FETCH_HEAD
```
......@@ -365,6 +365,9 @@ const schema = {
description: '对象数组嵌套功能',
type: 'array',
widget: 'list1',
props: {
hideMove: true,
},
items: {
type: 'object',
properties: {
......
......@@ -189,6 +189,9 @@ const schema = {
description: '对象数组嵌套功能',
type: 'array',
widget: 'list1',
props: {
hideMove: true,
},
items: {
type: 'object',
properties: {
......
......@@ -24,3 +24,7 @@ FormRender 1.0 与之前一样,对组件库的口子都是开着的,外部
### 5、 只读模式下,默认的渲染不能满足要求我想定制怎么办?
参见[只读模式下的自定义组件](/advanced/widget#%E5%8F%AA%E8%AF%BB%E6%A8%A1%E5%BC%8F%E4%B8%8B%E7%9A%84%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6)
### 6、 我试着使用 form.setValues, 但是被 set 的值还是空的?
form-render 有生命周期的概念,请在 onMount 这个钩子里 set。
......@@ -181,6 +181,77 @@ const MyWidget = props => {
}
```
## 旧版 schema 转换器:
```jsx
import React, { useState } from 'react';
import { Input, Button } from 'antd';
import { updateSchemaToNewVersion } from '../../packages/form-render/src/utils.js';
const TextArea = Input.TextArea;
const old = {
type: 'object',
properties: {
number: {
title: '数字输入框',
type: 'number',
'ui:disabled': true,
},
checkbox: {
title: '是否选择',
type: 'boolean',
'ui:widget': 'switch',
},
},
required: ['number'],
};
const Translator = () => {
const [oldSchema, setOld] = useState(JSON.stringify(old));
const [newSchema, setNew] = useState({});
const handleClick = () => {
try {
const _newSchema = updateSchemaToNewVersion(JSON.parse(oldSchema));
setNew(_newSchema);
} catch (err) {
console.log(err);
}
};
const onOldChange = e => {
setOld(e.target.value);
};
const formatOld = () => {
setOld(JSON.stringify(JSON.parse(oldSchema), null, 2));
};
return (
<div>
<div>填入旧版schema:</div>
<TextArea
style={{ minHeight: 400, marginTop: 12, marginBottom: 12 }}
value={oldSchema}
onChange={onOldChange}
/>
<Button style={{ marginRight: 12 }} onClick={formatOld}>
格式化旧schema
</Button>
<Button type="primary" onClick={handleClick}>
生成新版schema
</Button>
<TextArea
style={{ minHeight: 400, marginTop: 12 }}
value={JSON.stringify(newSchema, null, 2)}
/>
</div>
);
};
export default Translator;
```
## Changelog 思考
在最后罗列一下细节上 FormRender 0.x -> 1.0 细节上的改动 & 思考
......
......@@ -37,12 +37,30 @@ toc: content
| hideMove | boolean | 隐藏上下移动 item 的按钮 |
| buttons | array | 下详 (注 1) |
注:对于展示是 table 类型的 list(list2、list3、list4),所有 antd table 支持的 props 都可以透传,例如
```json
"props": {
"scrollX": 2000
}
```
**itemProps**
| props | 类型 | 说明 |
| ------- | :---: | :-----------: |
| buttons | array | 下详 (注 2) |
注:对于展示是 table 类型的 list(list2、list3、list4),所有 columns 的单个配置都可以透传,会作用到 clumns 的所有 item,例如
```json
"itemProps": {
"width": 200
}
```
则 table 的所有单元格(除了“操作”那一列)都会宽度 200 px
**注 1:**
列表默认只展示“新增一条”按钮。`buttons` 用于添加更多列表操作按钮
......@@ -127,7 +145,7 @@ window.copyMe = ({ value, index, schema }) => {
### upload 上传
**props**
**uploadProps**
upload 组件的主体 props,参考 [antd/upload 文档](https://ant.design/components/upload/)
......
import copy from 'rollup-plugin-copy';
export default {
cjs: 'rollup',
// cjs: 'rollup',
cjs: 'babel',
// esm: {
// type: 'rollup',
// importLibToEs: true,
// },
esm: {
type: 'rollup',
type: 'babel',
importLibToEs: true,
},
lessInBabelMode: true,
extraRollupPlugins: [
copy({
targets: [{ src: 'src/index.d.ts', dest: 'dist/' }],
......
# Changelog
### 1.5.2
- [+] 展示是 table 的 list 类组件(list2,list3,list4)的 props 和 itemProps 分别透传 table 的 props 和 columns 的 props 到 antd,便于用户定制化配置
- [!] 优化了异步加载,只有需要异步加载的部分 Suspense 渲染,且分析并只异步加载体积大的组件
- [!] 修复了只读模式下 range 组件数据未展示的问题
### 1.5.1
- [!] 修复了 1.5.0 切换打包方式到 babel 引入的安装报错问题,以及 less -> css 转换未开启的问题
### 1.5.0 (这个版本有问题,请勿安装)
- [+] 内部使用了 tree-shaking,减少首次加载,避免未用到的组件被加载
- [+] 全新的 playground,类似 vscode 的更好的体验,以及代码输入提示等
- [!] 修复了 hideDelete 会隐藏整个操作栏的问题(list2)
- [!] 解决了 schema 不存在的字段会在初始化中被去掉的问题
- [!] 添加了旧版 schema 到新 schema 的转换器
- [!] list 的复制按钮可以单独隐藏,使用 `hideCopy`
### 1.4.4
- [!] 修复了 hidden = true 是做了多余的对类型的判断的问题
......
{
"name": "form-render",
"version": "1.4.5-beta.0",
"version": "1.5.2",
"description": "通过 JSON Schema 生成标准 Form,常用于自定义搭建配置界面生成",
"repository": {
"type": "git",
......@@ -30,8 +30,8 @@
"test": "umi-test",
"test:coverage": "umi-test --coverage"
},
"main": "dist/index.js",
"module": "dist/index.esm.js",
"main": "lib/index.js",
"module": "es/index.js",
"gitHooks": {
"pre-commit": "lint-staged"
},
......
import React from 'react';
import { useForm } from './useForm';
import useForm from './useForm';
export const connectForm = Component => {
const connectForm = Component => {
return props => {
const form = useForm();
return <Component {...props} form={form} />;
};
};
export default connectForm;
......@@ -25,7 +25,16 @@ const DrawerList = ({
changeList,
listData,
}) => {
const { props = {}, itemProps } = schema;
const { props = {}, itemProps = {} } = schema;
const { buttons, ...columnProps } = itemProps;
const { pagination = {}, ...rest } = props;
const paginationConfig = pagination && {
size: 'small',
hideOnSinglePage: true,
...pagination,
};
const currentIndex = useRef(-1);
const [state, setState] = useSet({
showDrawer: false,
......@@ -66,6 +75,7 @@ const DrawerList = ({
</div>
);
},
...columnProps,
};
});
......@@ -193,7 +203,8 @@ const DrawerList = ({
}}
rowKey="$idx"
size="small"
pagination={{ size: 'small', hideOnSinglePage: true }}
pagination={paginationConfig}
{...rest}
/>
</>
);
......
......@@ -19,11 +19,11 @@ const TableList = ({
flatten,
schema,
listData,
changeList,
}) => {
const { props = {}, itemProps } = schema;
const { pagination = {} } = props;
const { props = {}, itemProps = {} } = schema;
const { buttons, ...columnProps } = itemProps;
const { pagination = {}, ...rest } = props;
const paginationConfig = pagination && {
size: 'small',
......@@ -62,10 +62,16 @@ const TableList = ({
/>
);
},
...columnProps,
};
});
if (!props.hideDelete || !props.hideAdd || !props.hideCopy || !props.hideMove) {
if (
!props.hideDelete ||
!props.hideAdd ||
!props.hideCopy ||
!props.hideMove
) {
columns.push({
title: '操作',
key: '$action',
......@@ -152,6 +158,7 @@ const TableList = ({
rowKey="index"
size="small"
pagination={paginationConfig}
{...rest}
/>
</>
);
......
......@@ -21,7 +21,7 @@ const VirtualList = ({
changeList,
}) => {
const { props = {}, itemProps = {} } = schema;
const { scrollY = 600 } = props;
const { scrollY = 600, ...rest } = props;
const [vt, set_components] = useVT(() => ({ scroll: { y: scrollY } }), []);
......@@ -167,6 +167,7 @@ const VirtualList = ({
columns={columns}
dataSource={dataSource}
pagination={false}
{...rest}
/>
</>
);
......
import React, { useMemo } from 'react';
import React, { Suspense } from 'react';
import { getWidgetName, extraSchemaList } from '../../mapping';
import { defaultWidgetNameList } from '../../widgets/antd';
import { useTools } from '../../hooks';
import { transformProps } from '../../createWidget';
import { isObjType, isListType, isObject } from '../../utils';
// import { Input } from 'antd';
// import Map from '../../widgets/antd/map';
const ErrorSchema = schema => {
return (
......@@ -71,7 +68,6 @@ const ExtendedWidget = ({
...schema.props,
};
if (schema.type === 'string' && typeof schema.max === 'number') {
widgetProps.maxLength = schema.max;
}
......@@ -87,13 +83,12 @@ const ExtendedWidget = ({
}
// 支持 addonAfter 为自定义组件的情况
if(isObject(widgetProps.addonAfter) && widgetProps.addonAfter.widget) {
if (isObject(widgetProps.addonAfter) && widgetProps.addonAfter.widget) {
const AddonAfterWidget = widgets[widgetProps.addonAfter.widget];
widgetProps.addonAfter = <AddonAfterWidget {...schema}/>;
widgetProps.addonAfter = <AddonAfterWidget {...schema} />;
}
// 避免传组件不接受的props,按情况传多余的props
// const isExternalWidget = defaultWidgetNameList.indexOf(widgetName) === -1; // 是否是外部组件
widgetProps.addons = {
onItemChange,
setValue: onItemChange,
......@@ -105,7 +100,11 @@ const ExtendedWidget = ({
const finalProps = transformProps(widgetProps);
return <Widget {...finalProps} />;
return (
<Suspense fallback={<div></div>}>
<Widget {...finalProps} />
</Suspense>
);
};
const areEqual = (prev, current) => {
......
......@@ -4,11 +4,11 @@ import './Extra.less';
const Extra = ({ schema }) => {
const { extra } = schema;
const { widgets } = useTools();
if (!extra) return null;
// widget 这个api也可以不对外
const { widgets } = useTools();
const widgetName = extra.widget;
const Widget = widgets[widgetName];
if (Widget) return <Widget schema={schema} />;
......
......@@ -20,8 +20,8 @@ const defaultFinish = (data, errors) => {
export { defaultWidgets as widgets, defaultMapping as mapping };
export { useForm } from './useForm';
export { connectForm } from './connectForm';
export { default as useForm } from './useForm';
export { default as connectForm } from './connectForm';
function App({
widgets,
......
......@@ -6,7 +6,7 @@ import { set, sortedUniqBy } from 'lodash-es';
import { processData, transformDataWithBind2 } from './processData';
import { generateDataSkeleton, flattenSchema, clone } from './utils';
export const useForm = props => {
const useForm = props => {
const {
// 为了更平滑兼容 0.x,如果外部传入状态,那么使用外部的状态
formData: _formData,
......@@ -365,3 +365,5 @@ export const useForm = props => {
return form;
};
export default useForm;
......@@ -750,10 +750,12 @@ export const getDescriptorFromSchema = ({ schema, isRequired = true }) => {
if (!value) return true;
if (Array.isArray(value)) {
// range组件点击clear,会变成 ['','']
if (
typeof value[0] === 'string' &&
typeof value[1] === 'string'
) {
// range组件对应的值bind的时候,会变成 [null,null]
const validValue =
typeof value[0] === 'string' && typeof value[1] === 'string';
const validValue2 =
value[0] === undefined && value[1] === undefined;
if (validValue || validValue2) {
return true;
}
return false;
......@@ -907,9 +909,11 @@ export const isPathRequired = (path, schema) => {
export const generateDataSkeleton = (schema, formData, _path = '') => {
let result = {};
let _formData = clone(formData);
result = _formData;
if (isObjType(schema)) {
if (_formData === undefined || typeof _formData !== 'object') {
_formData = {};
result = {};
}
Object.keys(schema.properties).forEach(key => {
const childSchema = schema.properties[key];
......@@ -922,12 +926,13 @@ export const generateDataSkeleton = (schema, formData, _path = '') => {
result[key] = childResult;
});
} else if (_formData !== undefined) {
result = clone(_formData);
// result = _formData;
} else {
if (schema.default !== undefined) {
result = clone(schema.default);
} else if (schema.type === 'boolean') {
result = false;
// result = false;
result = undefined;
} else {
result = undefined;
}
......@@ -1079,6 +1084,18 @@ const updateSingleSchema = schema => {
schema.width = schema['ui:width'];
delete schema['ui:width'];
}
if (schema['ui:displayType']) {
schema.displayType = schema['ui:displayType'];
delete schema['ui:displayType'];
}
if (schema['ui:column']) {
schema.column = schema['ui:column'];
delete schema['ui:column'];
}
if (schema['ui:widget']) {
schema.widget = schema['ui:widget'];
delete schema['ui:widget'];
}
if (schema['ui:labelWidth']) {
schema.labelWidth = schema['ui:labelWidth'];
delete schema['ui:labelWidth'];
......@@ -1086,9 +1103,12 @@ const updateSingleSchema = schema => {
if (schema.rules && schema.rules.length === 0) {
delete schema.rules;
}
if (JSON.stringify(schema.props) === '{}') {
delete schema.props;
}
return schema;
} catch (error) {
console.error('schema转换失败!', error);
console.error('schema转换失败!', error);
return schema;
}
};
......
import React from 'react';
export default function html({ value, schema }) {
export default function html({ value, schema = {} }) {
let __html = '-';
if (schema.type === 'boolean') {
__html = value === true ? '' : '';
......@@ -23,6 +23,15 @@ export default function html({ value, schema }) {
__html = String(value);
} else if (typeof value === 'string') {
__html = value;
} else if (
schema.type === 'range' &&
Array.isArray(value) &&
value[0] &&
value[1]
) {
__html = `${value[0]} - ${value[1]}`;
} else if (value && ['number', 'string'].indexOf(value) === -1) {
__html = JSON.stringify(value);
}
return (
......
import checkboxes from './checkboxes';
import color from './color';
import date from './date';
import time from './time';
import dateRange from './dateRange';
import timeRange from './timeRange';
import list from './list';
import map from './map';
import multiSelect from './multiSelect';
import radio from './radio';
import select from './select';
import slider from './slider';
import upload from './upload';
import { InputNumber, Checkbox, Input, Switch, Rate } from 'antd';
import { createWidget } from '../../createWidget';
import ImageInput from './imageInput';
import urlInput from './urlInput';
import Html from './html';
import {
InputNumber,
Checkbox,
Switch,
Input,
Rate,
TreeSelect,
Cascader,
} from 'antd';
import { createWidget } from '../../createWidget';
import select from './select';
import checkboxes from './checkboxes';
import multiSelect from './multiSelect';
import radio from './radio';
import time from './time';
import date from './date';
import dateRange from './dateRange';
import timeRange from './timeRange';
const TreeSelect = React.lazy(() => import('antd/es/tree-select'));
const Cascader = React.lazy(() => import('antd/es/cascader'));
const color = React.lazy(() => import('./color'));
const slider = React.lazy(() => import('./slider'));
const upload = React.lazy(() => import('./upload'));
const { TextArea } = Input;
......
......@@ -3,7 +3,7 @@ import { Collapse } from 'antd';
import { useStore2 } from '../../hooks';
const { Panel } = Collapse;
export default function map({ children, title, ...rest }) {
export default function Map({ children, title, ...rest }) {
const { theme, displayType, allCollapsed } = useStore2();
const [collapsed, setCollapsed] = useState(false);
......
......@@ -6,12 +6,22 @@ export default {
},
extraBabelPlugins: [
[
'babel-plugin-import',
'import',
{
libraryName: 'antd',
libraryDirectory: 'es',
style: true,
},
'antd',
],
[
'import',
{
libraryName: '@ant-design/icons',
libraryDirectory: 'lib/icons',
camel2DashComponentName: false,
},
'@ant-design/icons',
],
],
};
# Changelog
### 1.1.1
- [!] 内部打包配置优化
### 1.1.0
- [!] 重构了 search,正式接入最新的 form-render 特性
......
{
"name": "table-render",
"version": "1.1.0",
"version": "1.1.1",
"description": "中后台表格解决方案",
"scripts": {
"start": "dumi dev",
......@@ -31,7 +31,6 @@
},
"peerDependencies": {
"antd": "4.x",
"form-render": "1.x",
"react": ">=16.8.0"
},
"dependencies": {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册