feat: improve code

This commit is contained in:
chenos 2021-08-17 00:18:42 +08:00
parent 24d5d3cfad
commit 20c839bfa9
18 changed files with 775 additions and 234 deletions

View File

@ -80,6 +80,12 @@ const schema: ISchema = {
// accept: 'jpg,png'
},
},
showLogoOnly: {
type: 'boolean',
'x-decorator': 'FormItem',
'x-component': 'Checkbox',
'x-content': '只显示 LOGO',
},
},
},
},

View File

@ -38,7 +38,7 @@ export const SiteTitle = () => {
{!loading && data?.logo?.url && (
<img className={'site-logo'} src={data?.logo?.url} />
)}
{!loading && data.title && (
{!loading && !data.showLogoOnly && data.title && (
<div className={'site-title'}>{data.title}</div>
)}
</div>

View File

@ -63,8 +63,14 @@
}
}
.site-info {
display: flex;
align-items: center;
.site-logo {
height: 20px;
padding: 0 16px;
}
.site-title {
color: #fff;
font-size: 20px;
}
}

View File

@ -40,6 +40,7 @@ import {
import { DatePicker } from '../../schemas/date-picker';
import { Filter } from '../../schemas/filter';
import { Form } from '../../schemas/form';
import { ActionLogs } from '../../schemas/action-logs';
import { Grid } from '../../schemas/grid';
import { IconPicker } from '../../schemas/icon-picker';
import { Input } from '../../schemas/input';
@ -192,6 +193,8 @@ export const SchemaField = createSchemaField({
Tabs,
TimePicker,
Upload,
ActionLogs,
},
});

View File

@ -0,0 +1,55 @@
import { Field } from '@formily/core';
import {
ISchema,
observer,
RecursionField,
Schema,
useField,
} from '@formily/react';
import React from 'react';
import { SchemaRenderer } from '../../components/schema-renderer';
import ArrayTable from '../array-table';
export const ActionLogs = () => null;
ActionLogs.Field = observer((props: any) => {
const { value } = props;
return <div>{value?.uiSchema?.title || value?.name}</div>;
});
ActionLogs.FieldValue = observer((props: any) => {
const field = useField<Field>();
const array = ArrayTable.useArray();
const index = ArrayTable.useIndex();
const collectionField = array.field.value[index]?.field;
console.log('ffffff', collectionField?.uiSchema, field.value);
if (!collectionField.uiSchema) {
if (!field.value) {
return <div>N/A</div>;
}
if (typeof field.value === 'boolean') {
return <div>{field.value ? 'true' : 'false'}</div>;
}
if (typeof field.value === 'string' || typeof field.value === 'number') {
return <div>{field.value}</div>;
}
return <pre>{JSON.stringify(field.value, null, 2)}</pre>;
}
return (
<SchemaRenderer
schema={{
type: 'object',
properties: {
[collectionField.name as string]: {
...collectionField.uiSchema,
default: field.value,
'x-decorator': null,
'x-read-pretty': true,
},
},
}}
/>
);
});

View File

@ -264,7 +264,7 @@ Action.Dropdown = observer((props: any) => {
document.body,
)}
<Dropdown
trigger={['click']}
trigger={['hover']}
{...props}
overlay={
<Menu>

View File

@ -397,6 +397,7 @@ function generateCardItemSchema(component) {
rowKey: 'id',
// dragSort: true,
showIndex: true,
defaultSort: ['-id'],
defaultAppends: ['user', 'collection'],
refreshRequestOnChange: true,
pagination: {
@ -459,9 +460,9 @@ function generateCardItemSchema(component) {
type: 'string',
'x-component': 'Select',
enum: [
{ label: '新增数据', value: 'create' },
{ label: '更新数据', value: 'update' },
{ label: '删除数据', value: 'destroy' },
{ label: '新增', value: 'create', color: 'green' },
{ label: '更新', value: 'update', color: 'blue' },
{ label: '删除', value: 'destroy', color: 'red' },
],
},
},
@ -521,7 +522,7 @@ function generateCardItemSchema(component) {
properties: {
created_at: {
type: 'string',
title: '创建时间',
title: '操作时间',
'x-decorator': 'FormItem',
'x-component': 'DatePicker',
'x-read-pretty': true,
@ -529,6 +530,20 @@ function generateCardItemSchema(component) {
format: 'YYYY-MM-DD HH:mm:ss',
},
},
'user.nickname': {
type: 'string',
title: '操作用户',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-read-pretty': true,
},
'collection.title': {
type: 'string',
title: '所属数据表',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-read-pretty': true,
},
type: {
type: 'string',
title: '操作类型',
@ -536,9 +551,21 @@ function generateCardItemSchema(component) {
'x-component': 'Select',
'x-read-pretty': true,
enum: [
{ label: '新增数据', value: 'create' },
{ label: '更新数据', value: 'update' },
{ label: '删除数据', value: 'destroy' },
{
label: '新增',
value: 'create',
color: 'green',
},
{
label: '更新',
value: 'update',
color: 'blue',
},
{
label: '删除',
value: 'destroy',
color: 'red',
},
],
},
changes: {
@ -550,18 +577,33 @@ function generateCardItemSchema(component) {
pagination: false,
// scroll: { x: '100%' },
},
// 'x-reactions': ['{{ filterActionLogs }}'],
items: {
type: 'object',
properties: {
column0: {
type: 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': {
width: 80,
align: 'center',
},
properties: {
index: {
type: 'void',
'x-component': 'ArrayTable.Index',
},
},
},
column1: {
type: 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': { title: '字段名称' },
properties: {
'field.uiSchema.title': {
field: {
type: 'string',
'x-decorator': 'FormilyFormItem',
'x-component': 'Input',
'x-component': 'ActionLogs.Field',
},
},
},
@ -573,7 +615,7 @@ function generateCardItemSchema(component) {
before: {
type: 'string',
'x-decorator': 'FormilyFormItem',
'x-component': 'Values',
'x-component': 'ActionLogs.FieldValue',
},
},
},
@ -585,7 +627,7 @@ function generateCardItemSchema(component) {
after: {
type: 'string',
'x-decorator': 'FormilyFormItem',
'x-component': 'Values',
'x-component': 'ActionLogs.FieldValue',
},
},
},
@ -604,7 +646,7 @@ function generateCardItemSchema(component) {
},
column1: {
type: 'void',
title: '创建时间',
title: '操作时间',
'x-component': 'Table.Column',
properties: {
created_at: {
@ -618,6 +660,30 @@ function generateCardItemSchema(component) {
},
},
column2: {
type: 'void',
title: '操作用户',
'x-component': 'Table.Column',
properties: {
'user.nickname': {
type: 'string',
'x-component': 'Input',
'x-read-pretty': true,
},
},
},
column3: {
type: 'void',
title: '所属数据表',
'x-component': 'Table.Column',
properties: {
'collection.title': {
type: 'string',
'x-component': 'Input',
'x-read-pretty': true,
},
},
},
column4: {
type: 'void',
title: '操作类型',
'x-component': 'Table.Column',
@ -627,9 +693,9 @@ function generateCardItemSchema(component) {
'x-component': 'Select',
'x-read-pretty': true,
enum: [
{ label: '新增数据', value: 'create' },
{ label: '更新数据', value: 'update' },
{ label: '删除数据', value: 'destroy' },
{ label: '新增', value: 'create', color: 'green' },
{ label: '更新', value: 'update', color: 'blue' },
{ label: '删除', value: 'destroy', color: 'red' },
],
},
},

View File

@ -1,13 +1,370 @@
import React from 'react';
import { ArrayTable as Table } from '@formily/antd';
import { useField, Schema } from '@formily/react';
import { Button } from 'antd';
import cls from 'classnames';
import { isValid, uid } from '@formily/shared';
import { PlusOutlined } from '@ant-design/icons';
import { usePrefixCls } from '@formily/antd/lib/__builtins__';
// import React from 'react';
// import { ArrayTable as Table } from '@formily/antd';
// import { useField, Schema } from '@formily/react';
// import { Button } from 'antd';
// import cls from 'classnames';
// import { isValid, uid } from '@formily/shared';
// import { PlusOutlined } from '@ant-design/icons';
// import { usePrefixCls } from '@formily/antd/lib/__builtins__';
export const ArrayTable = Table;
import React, { Fragment, useState, useRef, useEffect } from 'react';
import { Table, Pagination, Space, Select, Badge, Button } from 'antd';
import { PaginationProps } from 'antd/lib/pagination';
import { TableProps, ColumnProps } from 'antd/lib/table';
import { SelectProps } from 'antd/lib/select';
import cls from 'classnames';
import { SortableContainer, SortableElement } from 'react-sortable-hoc';
import { GeneralField, FieldDisplayTypes, ArrayField } from '@formily/core';
import {
useForm,
useField,
observer,
useFieldSchema,
RecursionField,
} from '@formily/react';
import { FormPath, isArr, isBool, uid, isValid } from '@formily/shared';
import { Schema } from '@formily/json-schema';
import { usePrefixCls } from '@formily/antd/lib/__builtins__';
import { ArrayBase, ArrayBaseMixins } from '@formily/antd/lib/array-base';
import { PlusOutlined } from '@ant-design/icons';
interface ObservableColumnSource {
field: GeneralField;
columnProps: ColumnProps<any>;
schema: Schema;
display: FieldDisplayTypes;
name: string;
}
interface IArrayTablePaginationProps extends PaginationProps {
dataSource?: any[];
paging?: boolean;
children?: (
dataSource: any[],
pagination: React.ReactNode,
) => React.ReactElement;
}
interface IStatusSelectProps extends SelectProps<any> {
pageSize?: number;
}
type ComposedArrayTable = React.FC<TableProps<any>> &
ArrayBaseMixins & {
Column?: React.FC<ColumnProps<any>>;
};
const SortableRow = SortableElement((props: any) => <tr {...props} />);
const SortableBody = SortableContainer((props: any) => <tbody {...props} />);
const isColumnComponent = (schema: Schema) => {
return schema['x-component']?.indexOf('Column') > -1;
};
const isOperationsComponent = (schema: Schema) => {
return schema['x-component']?.indexOf('Operations') > -1;
};
const isAdditionComponent = (schema: Schema) => {
return schema['x-component']?.indexOf('Addition') > -1;
};
const useArrayTableSources = () => {
const arrayField = useField();
const schema = useFieldSchema();
const parseSources = (schema: Schema): ObservableColumnSource[] => {
if (
isColumnComponent(schema) ||
isOperationsComponent(schema) ||
isAdditionComponent(schema)
) {
if (!schema['x-component-props']?.['dataIndex'] && !schema['name'])
return [];
const name = schema['x-component-props']?.['dataIndex'] || schema['name'];
const field = arrayField.query(arrayField.address.concat(name)).take();
const columnProps =
field?.component?.[1] || schema['x-component-props'] || {};
const display = field?.display || schema['x-display'];
return [
{
name,
display,
field,
schema,
columnProps,
},
];
} else if (schema.properties) {
return schema.reduceProperties((buf, schema) => {
return buf.concat(parseSources(schema));
}, []);
}
};
const parseArrayItems = (schema: Schema['items']) => {
const sources: ObservableColumnSource[] = [];
const items = isArr(schema) ? schema : [schema];
return items.reduce((columns, schema) => {
const item = parseSources(schema);
if (item) {
return columns.concat(item);
}
return columns;
}, sources);
};
if (!schema) throw new Error('can not found schema object');
return parseArrayItems(schema.items);
};
const useArrayTableColumns = (
dataSource: any[],
sources: ObservableColumnSource[],
): TableProps<any>['columns'] => {
return sources.reduce((buf, { name, columnProps, schema, display }, key) => {
if (display !== 'visible') return buf;
if (!isColumnComponent(schema)) return buf;
return buf.concat({
...columnProps,
key,
dataIndex: name,
render: (value: any, record: any) => {
const index = dataSource.indexOf(record);
const children = (
<ArrayBase.Item index={index}>
<RecursionField schema={schema} name={index} onlyRenderProperties />
</ArrayBase.Item>
);
return children;
},
});
}, []);
};
const useAddition = () => {
const schema = useFieldSchema();
return schema.reduceProperties((addition, schema, key) => {
if (isAdditionComponent(schema)) {
return <RecursionField schema={schema} name={key} />;
}
return addition;
}, null);
};
const StatusSelect: React.FC<IStatusSelectProps> = observer((props) => {
const form = useForm();
const field = useField<ArrayField>();
const prefixCls = usePrefixCls('formily-array-table');
const errors = form.queryFeedbacks({
type: 'error',
address: `${field.address}.*`,
});
const createIndexPattern = (page: number) => {
const pattern = `${field.address}.*[${(page - 1) * props.pageSize}:${
page * props.pageSize
}].*`;
return FormPath.parse(pattern);
};
const options = props.options?.map(({ label, value }) => {
const hasError = errors.some(({ address }) => {
return createIndexPattern(value).match(address);
});
return {
label: hasError ? <Badge dot>{label}</Badge> : label,
value,
};
});
const width = String(options?.length).length * 15;
return (
<Select
value={props.value}
onChange={props.onChange}
options={options}
virtual
style={{
width: width < 60 ? 60 : width,
}}
className={cls(`${prefixCls}-status-select`, {
'has-error': errors?.length,
})}
/>
);
});
const ArrayTablePagination: React.FC<IArrayTablePaginationProps> = (props) => {
const [current, setCurrent] = useState(1);
const prefixCls = usePrefixCls('formily-array-table');
const pageSize = props.pageSize || 10;
const size = props.size || 'default';
const dataSource = props.dataSource || [];
const startIndex = (current - 1) * pageSize;
const endIndex = startIndex + pageSize - 1;
const total = dataSource?.length || 0;
const totalPage = Math.ceil(total / pageSize);
const pages = Array.from(new Array(totalPage)).map((_, index) => {
const page = index + 1;
return {
label: page,
value: page,
};
});
const handleChange = (current: number) => {
setCurrent(current);
};
useEffect(() => {
if (totalPage > 0 && totalPage < current) {
handleChange(totalPage);
}
}, [totalPage, current]);
const renderPagination = () => {
if (totalPage <= 1) return;
return (
<div className={`${prefixCls}-pagination`}>
<Space>
<StatusSelect
value={current}
pageSize={pageSize}
onChange={handleChange}
options={pages}
notFoundContent={false}
/>
<Pagination
{...props}
pageSize={pageSize}
current={current}
total={dataSource.length}
size={size}
showSizeChanger={false}
onChange={handleChange}
/>
</Space>
</div>
);
};
return (
<Fragment>
{props.paging
? props.children?.(
dataSource?.slice(startIndex, endIndex + 1),
renderPagination(),
)
: props.children?.(dataSource, null)}
</Fragment>
);
};
export const ArrayTable: ComposedArrayTable = observer(
(props: TableProps<any>) => {
const ref = useRef<HTMLDivElement>();
const field = useField<ArrayField>();
const prefixCls = usePrefixCls('formily-array-table');
const dataSource = Array.isArray(field.value) ? field.value.slice() : [];
const sources = useArrayTableSources();
const columns = useArrayTableColumns(dataSource, sources);
const pagination = isBool(props.pagination) ? {} : props.pagination;
const addition = useAddition();
const defaultRowKey = (record: any) => {
return dataSource.indexOf(record);
};
const addTdStyles = (node: HTMLElement) => {
const helper = document.body.querySelector(`.${prefixCls}-sort-helper`);
if (helper) {
const tds = node.querySelectorAll('td');
requestAnimationFrame(() => {
helper.querySelectorAll('td').forEach((td, index) => {
if (tds[index]) {
td.style.width = getComputedStyle(tds[index]).width;
}
});
});
}
};
return (
<ArrayTablePagination
paging={isBool(props.pagination) ? false : true}
{...pagination}
dataSource={dataSource}
>
{(dataSource, pager) => (
<div ref={ref} className={prefixCls}>
<ArrayBase>
<Table
size="small"
bordered
rowKey={defaultRowKey}
{...props}
onChange={() => {}}
pagination={false}
columns={columns}
dataSource={dataSource}
components={{
body: {
wrapper: (props: any) => (
<SortableBody
useDragHandle
lockAxis="y"
helperClass={`${prefixCls}-sort-helper`}
helperContainer={() => {
return ref.current?.querySelector('tbody');
}}
onSortStart={({ node }) => {
addTdStyles(node);
}}
onSortEnd={({ oldIndex, newIndex }) => {
field.move(oldIndex, newIndex);
}}
{...props}
/>
),
row: (props: any) => {
return (
<SortableRow
index={props['data-row-key'] || 0}
{...props}
/>
);
},
},
}}
/>
<div style={{ marginTop: 5, marginBottom: 5 }}>{pager}</div>
{sources.map((column, key) => {
//专门用来承接对Column的状态管理
if (!isColumnComponent(column.schema)) return;
return React.createElement(RecursionField, {
name: column.name,
schema: column.schema,
onlyRenderSelf: true,
key,
});
})}
{addition}
</ArrayBase>
</div>
)}
</ArrayTablePagination>
);
},
);
ArrayTable.displayName = 'ArrayTable';
ArrayTable.Column = () => {
return <Fragment />;
};
ArrayBase.mixin(ArrayTable);
ArrayTable.Index = (props) => {
const index = ArrayBase.useIndex();
return <span {...props}>{index + 1}</span>;
};
const getDefaultValue = (defaultValue: any, schema: Schema) => {
if (isValid(defaultValue)) return defaultValue;
@ -26,7 +383,7 @@ const getDefaultValue = (defaultValue: any, schema: Schema) => {
ArrayTable.Addition = (props: any) => {
const { randomValue } = props;
const self = useField();
const array = Table.useArray();
const array = ArrayBase.useArray();
const prefixCls = usePrefixCls('formily-array-base');
if (!array) return null;
if (array.field?.pattern !== 'editable') return null;

View File

@ -37,6 +37,7 @@ const schema = {
loadData: '{{ loadData }}',
useDataSource: '{{ useDataSource }}',
labelInValue: true,
maxLevel: 3,
fieldNames: {
label: 'name',
value: 'code',

View File

@ -32,6 +32,7 @@ export const Cascader = connect(
fieldNames = defaultFieldNames,
...others
} = props;
console.log('Cascader', props)
// 兼容值为 object[] 的情况
const toValue = () => {
return toArr(value).map((item) => {
@ -73,7 +74,7 @@ export const Cascader = connect(
});
};
// 这里没有用 x-reactions 是因为 readyPretty=true 时不需要
if (useDataSource) {
if (typeof useDataSource === 'function') {
useDataSource({
onSuccess: (data) => {
field.dataSource = data;

View File

@ -4,6 +4,7 @@ import { Checkbox as AntdCheckbox, Tag } from 'antd';
import { CheckboxProps, CheckboxGroupProps } from 'antd/lib/checkbox';
import uniq from 'lodash/uniq';
import { CheckOutlined, CloseOutlined } from '@ant-design/icons';
import { isValid } from '@formily/shared';
type ComposedCheckbox = React.FC<CheckboxProps> & {
Group?: React.FC<CheckboxGroupProps>;
@ -25,6 +26,9 @@ export const Checkbox: ComposedCheckbox = connect(
},
),
mapReadPretty((props) => {
if (!isValid(props.value)) {
return <div>N/A</div>;
}
return props.value ? (
<CheckOutlined style={{ color: '#52c41a' }} />
) : (
@ -41,6 +45,9 @@ Checkbox.Group = connect(
dataSource: 'options',
}),
mapReadPretty((props) => {
if (!isValid(props.value)) {
return <div>N/A</div>;
}
const { options = [] } = props;
const field = useField<any>();
const dataSource = field.dataSource || [];
@ -50,7 +57,9 @@ Checkbox.Group = connect(
{dataSource
.filter((option) => value.includes(option.value))
.map((option, key) => (
<Tag key={key} color={option.color}>{option.label}</Tag>
<Tag key={key} color={option.color}>
{option.label}
</Tag>
))}
</div>
);

View File

@ -1,33 +1,36 @@
import React, { createContext, useContext } from 'react'
import { isArr, isValid } from '@formily/shared'
import { observer, useField } from '@formily/react'
import { InputProps } from 'antd/lib/input'
import { InputNumberProps } from 'antd/lib/input-number'
import { SelectProps } from 'antd/lib/select'
import { TreeSelectProps } from 'antd/lib/tree-select'
import { CascaderProps } from 'antd/lib/cascader'
import React, { createContext, useContext } from 'react';
import { isArr, isValid } from '@formily/shared';
import { observer, useField } from '@formily/react';
import { InputProps } from 'antd/lib/input';
import { InputNumberProps } from 'antd/lib/input-number';
import { SelectProps } from 'antd/lib/select';
import { TreeSelectProps } from 'antd/lib/tree-select';
import { CascaderProps } from 'antd/lib/cascader';
import {
DatePickerProps,
RangePickerProps as DateRangePickerProps,
} from 'antd/lib/date-picker'
import { TimePickerProps, TimeRangePickerProps } from 'antd/lib/time-picker'
import { Tag, Space, Popover } from 'antd'
import cls from 'classnames'
import { formatMomentValue, usePrefixCls } from '@formily/antd/esm/__builtins__'
} from 'antd/lib/date-picker';
import { TimePickerProps, TimeRangePickerProps } from 'antd/lib/time-picker';
import { Tag, Space, Popover } from 'antd';
import cls from 'classnames';
import {
formatMomentValue,
usePrefixCls,
} from '@formily/antd/esm/__builtins__';
import { FullscreenOutlined } from '@ant-design/icons';
import moment from 'moment';
const PlaceholderContext = createContext<string>('N/A')
const PlaceholderContext = createContext<string>('N/A');
const Placeholder = PlaceholderContext.Provider
const Placeholder = PlaceholderContext.Provider;
const usePlaceholder = (value?: any) => {
const placeholder = useContext(PlaceholderContext) || 'N/A'
return isValid(value) && value !== '' ? value : placeholder
}
const placeholder = useContext(PlaceholderContext) || 'N/A';
return isValid(value) && value !== '' ? value : placeholder;
};
const Input: React.FC<InputProps> = (props) => {
const prefixCls = usePrefixCls('description-text', props)
const prefixCls = usePrefixCls('description-text', props);
return (
<Space className={cls(prefixCls, props.className)} style={props.style}>
{props.addonBefore}
@ -36,14 +39,16 @@ const Input: React.FC<InputProps> = (props) => {
{props.suffix}
{props.addonAfter}
</Space>
)
}
);
};
const URL: React.FC<InputProps> = (props) => {
const prefixCls = usePrefixCls('description-text', props)
const prefixCls = usePrefixCls('description-text', props);
const content = props.value && (
<a target={'_blank'} href={props.value as any}>{props.value}</a>
)
<a target={'_blank'} href={props.value as any}>
{props.value}
</a>
);
return (
<Space className={cls(prefixCls, props.className)} style={props.style}>
{props.addonBefore}
@ -52,11 +57,11 @@ const URL: React.FC<InputProps> = (props) => {
{props.suffix}
{props.addonAfter}
</Space>
)
}
);
};
const InputNumber: React.FC<InputProps & InputNumberProps> = (props) => {
const prefixCls = usePrefixCls('description-text', props)
const prefixCls = usePrefixCls('description-text', props);
const value = usePlaceholder(props.value);
return (
<Space className={cls(prefixCls, props.className)} style={props.style}>
@ -66,30 +71,34 @@ const InputNumber: React.FC<InputProps & InputNumberProps> = (props) => {
{props.suffix}
{props.addonAfter}
</Space>
)
}
);
};
const TextArea: React.FC<any> = (props) => {
const prefixCls = usePrefixCls('description-text', props)
const prefixCls = usePrefixCls('description-text', props);
const ellipsis = props.ellipsis === true ? {} : props.ellipsis;
const content = props.ellipsis ? (
<div>
<Popover content={usePlaceholder(props.value)}>
<div style={{
display: 'inline-block',
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
width: 100,
verticalAlign: 'middle',
marginRight: 10,
...ellipsis,
}}>
{usePlaceholder(props.text||props.value)}
<div
style={{
display: 'inline-block',
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
width: 100,
verticalAlign: 'middle',
marginRight: 10,
...ellipsis,
}}
>
{usePlaceholder(props.text || props.value)}
</div>
</Popover>
</div>
) : usePlaceholder(props.value)
) : (
usePlaceholder(props.value)
);
return (
<Space className={cls(prefixCls, props.className)} style={props.style}>
{props.addonBefore}
@ -98,199 +107,202 @@ const TextArea: React.FC<any> = (props) => {
{props.suffix}
{props.addonAfter}
</Space>
)
}
);
};
const Select: React.FC<SelectProps<any>> = observer((props) => {
const field = useField<Formily.Core.Models.Field>()
const prefixCls = usePrefixCls('description-text', props)
const field = useField<Formily.Core.Models.Field>();
const prefixCls = usePrefixCls('description-text', props);
const dataSource: any[] = field?.dataSource?.length
? field.dataSource
: props?.options?.length
? props.options
: []
const placeholder = usePlaceholder()
: [];
const placeholder = usePlaceholder();
const getSelected = () => {
const value = props.value
const value = props.value;
if (props.mode === 'multiple' || props.mode === 'tags') {
if (props.labelInValue) {
return isArr(value) ? value : []
return isArr(value) ? value : [];
} else {
return isArr(value)
? value.map((val) => ({ label: val, value: val }))
: []
: [];
}
} else {
if (props.labelInValue) {
return isValid(value) ? [value] : []
return isValid(value) ? [value] : [];
} else {
return isValid(value) ? [{ label: value, value }] : []
return isValid(value) ? [{ label: value, value }] : [];
}
}
}
};
const getLabels = () => {
const selected = getSelected()
if (!selected.length) return <Tag>{placeholder}</Tag>
const selected = getSelected();
if (!selected.length) return <Tag>{placeholder}</Tag>;
return selected.map(({ value, label }, key) => {
const text =
dataSource?.find((item) => item.value == value)?.label || label
return <Tag key={key}>{text || placeholder}</Tag>
})
}
dataSource?.find((item) => item.value == value)?.label || label;
return <Tag key={key}>{text || placeholder}</Tag>;
});
};
return (
<div className={cls(prefixCls, props.className)} style={props.style}>
{getLabels()}
</div>
)
})
);
});
const ObjectSelect: React.FC<SelectProps<any>> = observer((props) => {
const field = useField<Formily.Core.Models.Field>()
const prefixCls = usePrefixCls('description-text', props)
const field = useField<Formily.Core.Models.Field>();
const prefixCls = usePrefixCls('description-text', props);
const dataSource: any[] = field?.dataSource?.length
? field.dataSource
: props?.options?.length
? props.options
: []
const placeholder = usePlaceholder()
: [];
const placeholder = usePlaceholder();
const getSelected = () => {
const value = props.value
const value = props.value;
if (props.mode === 'multiple' || props.mode === 'tags') {
if (props.labelInValue) {
return isArr(value) ? value : []
return isArr(value) ? value : [];
} else {
return isArr(value)
? value.map((val) => ({ label: val, value: val }))
: []
: [];
}
} else {
if (props.labelInValue) {
return isValid(value) ? [value] : []
return isValid(value) ? [value] : [];
} else {
return isValid(value) ? [{ label: value, value }] : []
return isValid(value) ? [{ label: value, value }] : [];
}
}
}
};
const getLabels = () => {
const selected = getSelected()
if (!selected.length) return <Tag>{placeholder}</Tag>
const selected = getSelected();
if (!selected.length) return <Tag>{placeholder}</Tag>;
return selected.map(({ value, label }, key) => {
const text =
dataSource?.find((item) => item.value == value)?.label || label
return <Tag key={key}>{text || placeholder}</Tag>
})
}
dataSource?.find((item) => item.value == value)?.label || label;
return <Tag key={key}>{text || placeholder}</Tag>;
});
};
return (
<div className={cls(prefixCls, props.className)} style={props.style}>
{getLabels()}
</div>
)
})
);
});
const TreeSelect: React.FC<TreeSelectProps<any>> = observer((props) => {
const field = useField<Formily.Core.Models.Field>()
const placeholder = usePlaceholder()
const prefixCls = usePrefixCls('description-text', props)
const field = useField<Formily.Core.Models.Field>();
const placeholder = usePlaceholder();
const prefixCls = usePrefixCls('description-text', props);
const dataSource = field?.dataSource?.length
? field.dataSource
: props?.options?.length
? props.options
: []
: [];
const getSelected = () => {
const value = props.value
const value = props.value;
if (props.multiple) {
if (props.labelInValue) {
return isArr(value) ? value : []
return isArr(value) ? value : [];
} else {
return isArr(value)
? value.map((val) => ({ label: val, value: val }))
: []
: [];
}
} else {
if (props.labelInValue) {
return value ? [value] : []
return value ? [value] : [];
} else {
return value ? [{ label: value, value }] : []
return value ? [{ label: value, value }] : [];
}
}
}
};
const findLabel = (value: any, dataSource: any[]) => {
for (let i = 0; i < dataSource?.length; i++) {
const item = dataSource[i]
const item = dataSource[i];
if (item?.value === value) {
return item?.label
return item?.label;
} else {
const childLabel = findLabel(value, item?.children)
if (childLabel) return childLabel
const childLabel = findLabel(value, item?.children);
if (childLabel) return childLabel;
}
}
}
};
const getLabels = () => {
const selected = getSelected()
if (!selected?.length) return <Tag>{placeholder}</Tag>
const selected = getSelected();
if (!selected?.length) return <Tag>{placeholder}</Tag>;
return selected.map(({ value, label }, key) => {
return (
<Tag key={key}>
{findLabel(value, dataSource) || label || placeholder}
</Tag>
)
})
}
);
});
};
return (
<div className={cls(prefixCls, props.className)} style={props.style}>
{getLabels()}
</div>
)
})
);
});
const Cascader: React.FC<CascaderProps> = observer((props) => {
const field = useField<Formily.Core.Models.Field>()
const placeholder = usePlaceholder()
const prefixCls = usePrefixCls('description-text', props)
const field = useField<Formily.Core.Models.Field>();
const placeholder = usePlaceholder();
const prefixCls = usePrefixCls('description-text', props);
const dataSource: any[] = field?.dataSource?.length
? field.dataSource
: props?.options?.length
? props.options
: []
: [];
const getSelected = () => {
return isArr(props.value) ? props.value : []
}
return isArr(props.value) ? props.value : [];
};
const findLabel = (value: any, dataSource: any[]) => {
for (let i = 0; i < dataSource?.length; i++) {
const item = dataSource[i]
const item = dataSource[i];
if (item?.value === value) {
return item?.label
return item?.label;
} else {
const childLabel = findLabel(value, item?.children)
if (childLabel) return childLabel
const childLabel = findLabel(value, item?.children);
if (childLabel) return childLabel;
}
}
}
};
const getLabels = () => {
const selected = getSelected()
const selected = getSelected();
if (!selected?.length) {
return placeholder
return placeholder;
}
return selected
.map((value) => {
return findLabel(value, dataSource) || placeholder
return findLabel(value, dataSource) || placeholder;
})
.join('/')
}
.join('/');
};
return (
<div className={cls(prefixCls, props.className)} style={props.style}>
{getLabels()}
</div>
)
})
);
});
const DatePicker: React.FC<DatePickerProps> = (props: any) => {
const placeholder = usePlaceholder()
const prefixCls = usePrefixCls('description-text', props)
if (!props.value) {
return <div>N/A</div>;
}
const placeholder = usePlaceholder();
const prefixCls = usePrefixCls('description-text', props);
const getDefaultFormat = () => {
const { dateFormat, showTime, timeFormat } = props;
let format = dateFormat;
@ -298,56 +310,60 @@ const DatePicker: React.FC<DatePickerProps> = (props: any) => {
format += ` ${timeFormat}`;
}
return format || props.format;
}
};
const getLabels = () => {
const d = moment(props.value);
const labels = formatMomentValue(d.isValid() ? d : null, getDefaultFormat(), placeholder)
return isArr(labels) ? labels.join('~') : labels
}
return <div className={cls(prefixCls, props.className)}>{getLabels()}</div>
}
const labels = formatMomentValue(
d.isValid() ? d : null,
getDefaultFormat(),
placeholder,
);
return isArr(labels) ? labels.join('~') : labels;
};
return <div className={cls(prefixCls, props.className)}>{getLabels()}</div>;
};
const DateRangePicker: React.FC<DateRangePickerProps> = (props) => {
const placeholder = usePlaceholder()
const prefixCls = usePrefixCls('description-text', props)
const placeholder = usePlaceholder();
const prefixCls = usePrefixCls('description-text', props);
const getLabels = () => {
const labels = formatMomentValue(props.value, props.format, placeholder)
return isArr(labels) ? labels.join('~') : labels
}
const labels = formatMomentValue(props.value, props.format, placeholder);
return isArr(labels) ? labels.join('~') : labels;
};
return (
<div className={cls(prefixCls, props.className)} style={props.style}>
{getLabels()}
</div>
)
}
);
};
const TimePicker: React.FC<TimePickerProps> = (props) => {
const placeholder = usePlaceholder()
const prefixCls = usePrefixCls('description-text', props)
const placeholder = usePlaceholder();
const prefixCls = usePrefixCls('description-text', props);
const getLabels = () => {
const labels = formatMomentValue(props.value, props.format, placeholder)
return isArr(labels) ? labels.join('~') : labels
}
const labels = formatMomentValue(props.value, props.format, placeholder);
return isArr(labels) ? labels.join('~') : labels;
};
return (
<div className={cls(prefixCls, props.className)} style={props.style}>
{getLabels()}
</div>
)
}
);
};
const TimeRangePicker: React.FC<TimeRangePickerProps> = (props) => {
const placeholder = usePlaceholder()
const prefixCls = usePrefixCls('description-text', props)
const placeholder = usePlaceholder();
const prefixCls = usePrefixCls('description-text', props);
const getLabels = () => {
const labels = formatMomentValue(props.value, props.format, placeholder)
return isArr(labels) ? labels.join('~') : labels
}
const labels = formatMomentValue(props.value, props.format, placeholder);
return isArr(labels) ? labels.join('~') : labels;
};
return (
<div className={cls(prefixCls, props.className)} style={props.style}>
{getLabels()}
</div>
)
}
);
};
export const Display = {
Input,
@ -364,6 +380,6 @@ export const Display = {
Placeholder,
InputNumber,
usePlaceholder,
}
};
export default Display;

View File

@ -7,6 +7,7 @@ import {
hasIcon,
IconPicker as Icon,
} from '../../components/icon-picker';
import { isValid } from '@formily/shared';
function IconField(props: any) {
const { value, onChange } = props;
@ -72,6 +73,9 @@ export const IconPicker = connect(
};
}),
mapReadPretty((props) => {
if (!isValid(props.value)) {
return <div>N/A</div>;
}
return <Icon type={props.value} />;
}),
);

View File

@ -34,8 +34,8 @@ export const InputNumber: any = connect(
},
mapReadPretty((props: any) => {
const { step, value, addonBefore, addonAfter } = props;
if (!isValid(value)) {
return null;
if (!isValid(props.value)) {
return <div>N/A</div>;
}
const precision = Math.max(
getNumberPrecision(String(value)),
@ -55,8 +55,8 @@ InputNumber.Percent = connect(
AntdNumber,
mapReadPretty((props: any) => {
const { step, value } = props;
if (!isValid(value)) {
return null;
if (!isValid(props.value)) {
return <div>N/A</div>;
}
return toFixed(
String(value),

View File

@ -3,63 +3,68 @@ import { connect, mapReadPretty } from '@formily/react';
import { Input } from 'antd';
import { PasswordProps } from 'antd/lib/input';
import { PasswordStrength } from './PasswordStrength';
import { isValid } from '@formily/shared';
export interface IPasswordProps extends PasswordProps {
checkStrength: boolean;
}
export const Password = connect((props: IPasswordProps) => {
const { value, className, checkStrength, ...others } = props;
const blockStyle: React.CSSProperties = {
position: 'absolute',
zIndex: 1,
height: 8,
top: 0,
background: '#fff',
width: 1,
transform: 'translate(-50%, 0)',
};
return (
<span className={className}>
<Input.Password {...others} value={value} />
{checkStrength && (
<PasswordStrength value={String(value)}>
{(score) => {
return (
<div
style={{
background: '#e0e0e0',
marginBottom: 3,
position: 'relative',
}}
>
<div style={{ ...blockStyle, left: '20%' }} />
<div style={{ ...blockStyle, left: '40%' }} />
<div style={{ ...blockStyle, left: '60%' }} />
<div style={{ ...blockStyle, left: '80%' }} />
export const Password = connect(
(props: IPasswordProps) => {
const { value, className, checkStrength, ...others } = props;
const blockStyle: React.CSSProperties = {
position: 'absolute',
zIndex: 1,
height: 8,
top: 0,
background: '#fff',
width: 1,
transform: 'translate(-50%, 0)',
};
return (
<span className={className}>
<Input.Password {...others} value={value} />
{checkStrength && (
<PasswordStrength value={String(value)}>
{(score) => {
return (
<div
style={{
background: '#e0e0e0',
marginBottom: 3,
position: 'relative',
backgroundImage:
'-webkit-linear-gradient(left, #ff5500, #ff9300)',
transition: 'all 0.35s ease-in-out',
height: 8,
width: '100%',
marginTop: 5,
clipPath: `polygon(0 0,${score}% 0,${score}% 100%,0 100%)`,
}}
/>
</div>
);
}}
</PasswordStrength>
)}
</span>
);
}, mapReadPretty(() => {
return (
<div>********</div>
)
}));
>
<div style={{ ...blockStyle, left: '20%' }} />
<div style={{ ...blockStyle, left: '40%' }} />
<div style={{ ...blockStyle, left: '60%' }} />
<div style={{ ...blockStyle, left: '80%' }} />
<div
style={{
position: 'relative',
backgroundImage:
'-webkit-linear-gradient(left, #ff5500, #ff9300)',
transition: 'all 0.35s ease-in-out',
height: 8,
width: '100%',
marginTop: 5,
clipPath: `polygon(0 0,${score}% 0,${score}% 100%,0 100%)`,
}}
/>
</div>
);
}}
</PasswordStrength>
)}
</span>
);
},
mapReadPretty((props) => {
if (!isValid(props.value)) {
return <div>N/A</div>;
}
return <div>********</div>;
}),
);
export default Password;

View File

@ -2,6 +2,7 @@ import React from 'react';
import { connect, mapProps, mapReadPretty, SchemaOptionsContext, useField } from '@formily/react'
import { Radio as AntdRadio, Tag } from 'antd'
import { RadioProps, RadioGroupProps } from 'antd/lib/radio'
import { isValid } from '@formily/shared';
type ComposedRadio = React.FC<RadioProps> & {
Group?: React.FC<RadioGroupProps>
@ -24,6 +25,9 @@ Radio.Group = connect(
dataSource: 'options',
}),
mapReadPretty((props) => {
if (!isValid(props.value)) {
return <div>N/A</div>;
}
const { options = [], value } = props;
const field = useField<any>();
const dataSource = field.dataSource || [];

View File

@ -18,7 +18,7 @@ import { useDesignable } from '../';
import { createContext } from 'react';
import { useContext } from 'react';
import { isEmpty } from 'lodash';
import { isField } from '@formily/core';
import { Field, isArrayField, isField } from '@formily/core';
export const Select: any = connect(
(props) => {
@ -65,7 +65,15 @@ export const Select: any = connect(
},
),
mapReadPretty((props) => {
// console.log('mapReadPretty', props.value)
// return <div>N/A</div>;
if (!isValid(props.value)) {
return <div>N/A</div>;
}
const field = useField<any>();
if (isArrayField(field) && field?.value?.length === 0) {
return <div>N/A</div>;
}
const dataSource = field.dataSource || [];
console.log('field.value', field.value, dataSource);
const values = toArr(field.value);

View File

@ -2244,7 +2244,7 @@ Table.useActionLogDetailsResource = ({ onSuccess }) => {
});
const service = useRequest(
(params?: any) => {
return resource.get({ ...params, appends: 'changes' });
return resource.get({ ...params, appends: ['changes', 'user', 'collection'] });
},
{
formatResult: (result) => result?.data,