UNPKG

sunmao-sdk

Version:

榫卯-开箱即用赋能-sdk

217 lines (198 loc) 5.41 kB
import React, { useRef, useEffect, useMemo, useImperativeHandle, useState } from "react"; import { useDebouncedCallback } from "use-debounce"; import { usePrevious } from "./hooks"; import PropTypes from "prop-types"; import { isDeepEqual, combineSchema } from "./base/utils"; import { asField, DefaultFieldUI } from "./base/asField"; import parse from "./base/parser"; import resolve from "./base/resolve"; import { getValidateList } from "./base/validate"; import fetcher from "./HOC/fetcher"; import "./atom.css"; import "./index.css"; function RenderField({ fields, onChange, ...settings }) { const { Field, props } = parse(settings, fields); if (!Field) { return null; } return ( <Field isRoot {...props} value={settings.data} onChange={onChange} formData={settings.formData} /> ); } // 在顶层将 propsSchema 和 uiSchema 合并,便于后续处理。 也可直接传入合并的 schema const Wrapper = ({ schema, propsSchema = {}, uiSchema = {}, readOnly, showValidate, ...rest }) => { let _schema = {}; const jsonSchema = schema || propsSchema; // 兼容schema字段和propsSchema字段 // 将uiSchema和schema合并(推荐不写uiSchema) _schema = combineSchema(jsonSchema, uiSchema); return ( <FormRender readOnly={readOnly} showValidate={!readOnly && showValidate} // 预览模式下不展示校验 {...rest} schema={_schema} /> ); }; function FormRender({ name = "$form", column = 1, className, schema = {}, formData = {}, widgets = {}, FieldUI = DefaultFieldUI, fields = {}, mapping = {}, showDescIcon = false, showValidate = true, displayType = "column", onChange = () => {}, onValidate = () => {}, onMount = () => {}, readOnly = false, labelWidth = 110, useLogger = false, forwardedRef }) { const isUserInput = useRef(false); // 状态改变是否来自于用户操作 const originWidgets = useRef(); const generatedFields = useRef({}); const firstRender = useRef(true); const previousSchema = usePrevious(schema); const previousData = usePrevious(formData); const [isEditing, setEditing] = useState(false); const debouncedSetEditing = useDebouncedCallback(setEditing, 300); const data = useMemo(() => resolve(schema, formData), [schema, formData]); useEffect(() => { onChange(data); updateValidation(); }, []); useEffect(() => { if (isUserInput.current) { isUserInput.current = false; return; } if (!isDeepEqual(previousSchema, schema)) { onChange(data); updateValidation(); } else if (!isDeepEqual(previousData, formData)) { if (firstRender.current) { onMount(); firstRender.current = false; } updateValidation(); } }, [schema, formData]); // data修改比较常用,所以放第一位 const resetData = (newData, newSchema) => { const _schema = newSchema || schema; const _formData = newData || formData; const res = resolve(_schema, _formData); return new Promise(resolve => { onChange(res); updateValidation(res, _schema); resolve(res); }); }; useImperativeHandle(forwardedRef, () => ({ resetData })); // 用户输入都是调用这个函数 const handleChange = (key, val) => { isUserInput.current = true; // 开始编辑,节流 setEditing(true); debouncedSetEditing.callback(false); onChange(val); onValidate(getValidateList(val, schema)); }; const updateValidation = (outData, outSchema) => { const _data = outData || data; const _schema = outSchema || schema; onValidate(getValidateList(_data, _schema)); }; const generated = {}; if (!originWidgets.current) { originWidgets.current = widgets; } Object.keys(widgets).forEach(key => { const oWidget = originWidgets.current[key]; const nWidget = widgets[key]; let gField = generatedFields.current[key]; if (!gField || oWidget !== nWidget) { if (oWidget !== nWidget) { originWidgets.current[key] = nWidget; } gField = asField({ FieldUI, Widget: nWidget }); generatedFields.current[key] = gField; } generated[key] = gField; }); const settings = { schema, data, name, column, showDescIcon, showValidate, displayType, readOnly, labelWidth, useLogger, formData: data, isEditing }; const _fields = { // 根据 Widget 生成的 Field generated, // 自定义的 Field customized: fields, // 字段 type 与 widgetName 的映射关系 mapping }; return ( <div className={`${className} fr-wrapper`}> <RenderField {...settings} fields={_fields} onChange={handleChange} /> </div> ); } FormRender.propTypes = { name: PropTypes.string, column: PropTypes.number, schema: PropTypes.object, formData: PropTypes.object, widgets: PropTypes.objectOf(PropTypes.func), FieldUI: PropTypes.elementType, fields: PropTypes.objectOf(PropTypes.element), mapping: PropTypes.object, showDescIcon: PropTypes.bool, showValidate: PropTypes.bool, displayType: PropTypes.string, onChange: PropTypes.func, onMount: PropTypes.func, onValidate: PropTypes.func, readOnly: PropTypes.bool, labelWidth: PropTypes.number, useLogger: PropTypes.bool }; export default fetcher(Wrapper);