packages/form-render/src/render-core/FieldItem/module.tsx (280 lines of code) (raw):
import React from 'react';
import { _get, isObject, getArray, isArray, isNumber } from '../../utils';
const filterHiddenData = (list: any[]) => {
if (!isArray(list)) {
return list;
}
let result = [];
for (let i = 0; i < list.length; i++) {
const item = list[i];
if (!item.hidden) {
let node = {
...item,
};
if (item.children) {
let children = filterHiddenData(item.children);
if (children.length > 0) {
node.children = children;
}
}
if (item.items) {
let items = filterHiddenData(item.items);
if (items.length > 0) {
node.items = items;
}
}
result.push(node);
}
}
return result;
}
// return dataIndex、dataPath、schemaPath
const getPathObj = ({ rootPath = [], path }) => {
const pathList = (path || '').split('.');
const dataIndex: any[] = [];
const schemaIndex: any[] = [];
const dataPathList: any[] = [];
// dataIndex
rootPath.forEach((item: any, index: number) => {
if (isNumber(item)) {
dataIndex.push(item);
return;
}
if (isNumber(rootPath[index+1])) {
schemaIndex.push(`${item}[]`);
} else {
schemaIndex.push(item);
}
});
// dataPath
let list: any[] = [...rootPath];
list.pop();
list = [...list, ...pathList];
list.forEach((item: any, index: number) => {
if (isNumber(item)) {
dataPathList.push(`[${item}]`)
} else {
dataPathList.push(item)
}
});
const dataPath = dataPathList.join('.');
// schemaPath
const _path = pathList;
if (_path[0] && isNumber(_path[0])) {
_path.splice(0, 1);
}
const schemaPath = [...schemaIndex, _path].join('.');
// console.log(path, rootPath, '-------', dataIndex, dataPath, schemaPath);
return {
dataIndex,
dataPath,
schemaPath
};
};
export const getPath = (path: any) => {
if (!path) {
return null;
}
if (isArray(path)) {
return path.join('.');
}
return path;
};
export const getLabel = (schema: any, displayType: string, widgets: any, addons: any) => {
const { title, description, descWidget, labelWidget } = schema;
const LabelNode = widgets[labelWidget];
if (LabelNode) {
return <LabelNode schema={schema} addons={addons} />
}
if ((!description && !descWidget)) {
return title;
}
const RenderDesc = () => {
const Widget = widgets[descWidget];
if (Widget) {
return <Widget schema={schema} addons={addons} />;
}
if (description) {
return (
<span className='fr-desc'>
({description})
</span>
)
}
return null;
};
// if (displayType === 'inline') {
// return title;
// }
return (
<>
{title}
<RenderDesc />
</>
)
};
export const getTooltip = (schema: any, displayType: string) => {
const { descType, description, tooltip } = schema;
if (tooltip) {
if (typeof tooltip === 'string') {
return { title: <span dangerouslySetInnerHTML={{ __html: tooltip }} /> };
}
return {
...tooltip,
title: <span dangerouslySetInnerHTML={{ __html: tooltip.title }} />,
};
}
if (descType === 'widget' || !description) {
return null;
}
if (displayType === 'column' && descType === 'icon') {
return {
title: description
}
}
return null;
};
export const getExtraView = (extraKey: string, schema: any, widgets: any, addons: any) => {
const extra = schema[extraKey];
if (!extra) {
return;
}
// extra 自定义
const widgetName = extra?.widget || extra;
const Widget = widgets[widgetName];
if (!!Widget) {
return <Widget schema={schema} addons={addons} />;
} if (!Widget && extra?.widget) {
return;
}
let __html = '';
if (typeof extra === 'string') {
__html = extra;
}
// 内部BU使用的口子,这个api不对外,也没有必要
if (extra?.text) {
__html = extra.text;
}
if (!__html) {
return;
}
return (
<div
className='fr-form-item-extra'
dangerouslySetInnerHTML={{ __html }}
/>
)
}
export const getColSpan = (formCtx: any, parentCtx: any, schema: any) => {
let span = 24;
const column = getParamValue(formCtx, parentCtx, schema)('column');
if (column) {
span = 24 / column;
}
// 兼容 1.0 逻辑
if (schema.width) {
if (schema.width === '100%') {
span = 24;
} else if (schema.width === '50%') {
span = 12;
} else if (schema.width === '20%') {
span = 5;
} else if (schema.width < '50%') {
span = 8;
}
}
if (schema.cellSpan) {
span = schema.cellSpan * span;
}
if (schema.span) {
span = schema.span;
}
return span > 24 ? 24 : span;
};
export const getParamValue = (formCtx: any, upperCtx: any, schema: any) => (valueKey: string, isTop = true) => {
if (isTop) {
return schema[valueKey] ?? upperCtx[valueKey] ?? formCtx[valueKey];
}
return schema[valueKey] ?? upperCtx[valueKey];
};
export const getFieldProps = (widgetName: string, schema: any, { widgets, methods, form, dependValues, globalProps, path, rootPath, fieldRef }) => {
const pathObj = getPathObj({ path, rootPath });
let fieldProps = {
...schema.props,
addons: {
...form,
globalProps,
dependValues,
fieldRef,
...pathObj
}
};
if (dependValues?.length > 0) {
fieldProps.dependValues = dependValues;
}
['placeholder', 'disabled', 'format', 'onStatusChange'].forEach(key => {
if (schema[key]) {
fieldProps[key] = schema[key];
}
});
// 兼容 1.0 版本逻辑 enum => options
if (schema.enum && !schema.props?.options) {
const { enum: enums, enumNames } = schema;
fieldProps.options = getArray(enums).map((item: any, index: number) => {
let label = enumNames && Array.isArray(enumNames) ? enumNames[index] : item;
const isHtml = typeof label === 'string' && label[0] === '<';
if (isHtml) {
label = <span dangerouslySetInnerHTML={{ __html: label }} />;
}
return { label, value: item };
});
}
if (isArray(fieldProps.options)) {
fieldProps = {
...fieldProps,
options: filterHiddenData(fieldProps.options)
}
}
// 以 props 结尾的属性,直接透传
Object.keys(schema).forEach(key => {
if (
typeof key === 'string' &&
key.toLowerCase().indexOf('props') > -1 &&
key.length > 5
) {
fieldProps[key] = schema[key];
}
});
// 支持 addonAfter 为自定义组件的情况
if (isObject(fieldProps.addonAfter) && fieldProps.addonAfter.widget) {
const AddonAfterWidget = widgets[fieldProps.addonAfter.widget];
fieldProps.addonAfter = <AddonAfterWidget {...schema} />;
}
if (['treeSelect', 'inputNumber', 'multiSelect', 'select'].includes(widgetName)) {
fieldProps.style = {
width: '100%',
...fieldProps.style
}
}
if (widgetName === 'multiSelect') {
fieldProps.mode = 'multiple';
}
// Dynamic Mapping of Methods
if (isObject(schema.methods)) {
Object.keys(schema.methods).forEach(key => {
const name = schema.methods[key];
fieldProps[key] = methods[name];
});
}
fieldProps.schema = schema;
return fieldProps;
};
/*
* Get depend values
*
* 1. normal path
* Just get value of path in formData
*
* 2. list path
* Like `list[].foo`.`[]` means the same index as the current item.
* You can pass `[index]` to get specific item at the index of list, such as `list[1].foo`.
* Support more complex path like `list[].foo[].bar`
*/
export const getDependValues = (formData: any, dependPath: string, props: any, dependencieItem: any[]) => {
const indexReg =/\[[0-9]*\]/;
if (indexReg.test(dependPath)) {
const currentIndex = _get(props, 'path.0')
const dependIndex = dependPath
.match(indexReg)[0]
.replace('[', '')
.replace(']', '')
const listPath = dependPath.split(indexReg)[0];
const itemIndex = dependIndex || currentIndex;
const itemPath = dependPath.replace(`${listPath}[${dependIndex}].`, '')
const listData = _get(formData, `${listPath}[${itemIndex}]`);
dependencieItem.push(listPath, itemIndex);
return getDependValues(listData, itemPath, props, dependencieItem);
}
dependencieItem.push(...dependPath.split('.'));
return _get(formData, dependPath);
}